diff --git a/apps/web/components/integrations/chrome-detail.tsx b/apps/web/components/integrations/chrome-detail.tsx index 03762e9a3..c419e4ff6 100644 --- a/apps/web/components/integrations/chrome-detail.tsx +++ b/apps/web/components/integrations/chrome-detail.tsx @@ -1,5 +1,6 @@ "use client" +import { CHROME_EXTENSION_URL } from "@repo/lib/constants" import { cn } from "@lib/utils" import { dmSans125ClassName } from "@/lib/fonts" import { ChromeIcon } from "@/components/integration-icons" @@ -33,11 +34,7 @@ function PillButton({ export function ChromeDetail() { const handleInstall = () => { - window.open( - "https://chromewebstore.google.com/detail/supermemory/afpgkkipfdpeaflnpoaffkcankadgjfc", - "_blank", - "noopener,noreferrer", - ) + window.open(CHROME_EXTENSION_URL, "_blank", "noopener,noreferrer") analytics.onboardingChromeExtensionClicked({ source: "integrations" }) } diff --git a/apps/web/components/onboarding/setup/integrations-step.tsx b/apps/web/components/onboarding/setup/integrations-step.tsx index 32e420054..d7f16e517 100644 --- a/apps/web/components/onboarding/setup/integrations-step.tsx +++ b/apps/web/components/onboarding/setup/integrations-step.tsx @@ -1,5 +1,6 @@ "use client" +import { CHROME_EXTENSION_URL } from "@repo/lib/constants" import { useState } from "react" import { Button } from "@ui/components/button" import { MCPDetailView } from "@/components/mcp-modal/mcp-detail-view" @@ -113,10 +114,7 @@ export function IntegrationsStep() { analytics.onboardingChromeExtensionClicked({ source: "onboarding", }) - window.open( - "https://chromewebstore.google.com/detail/supermemory/afpgkkipfdpeaflnpoaffkcankadgjfc", - "_blank", - ) + window.open(CHROME_EXTENSION_URL, "_blank") } else { analytics.onboardingIntegrationClicked({ integration: card.title, diff --git a/apps/web/components/onboarding/x-bookmarks-detail-view.tsx b/apps/web/components/onboarding/x-bookmarks-detail-view.tsx index acd4ebf8d..9a6b37c37 100644 --- a/apps/web/components/onboarding/x-bookmarks-detail-view.tsx +++ b/apps/web/components/onboarding/x-bookmarks-detail-view.tsx @@ -4,6 +4,7 @@ import { Button } from "@ui/components/button" import { cn } from "@lib/utils" import { dmSansClassName } from "@/lib/fonts" import Image from "next/image" +import { CHROME_EXTENSION_URL } from "@repo/lib/constants" interface XBookmarksDetailViewProps { onBack: () => void @@ -29,10 +30,7 @@ const steps = [ export function XBookmarksDetailView({ onBack }: XBookmarksDetailViewProps) { const handleInstall = () => { - window.open( - "https://chromewebstore.google.com/detail/supermemory/afpgkkipfdpeaflnangednailhoegogi", - "_blank", - ) + window.open(CHROME_EXTENSION_URL, "_blank") } return ( diff --git a/apps/web/components/settings/integrations.tsx b/apps/web/components/settings/integrations.tsx index f9b6786fc..72893eefc 100644 --- a/apps/web/components/settings/integrations.tsx +++ b/apps/web/components/settings/integrations.tsx @@ -8,6 +8,7 @@ import { useAuth } from "@lib/auth-context" import { generateId } from "@lib/generate-id" import { ADD_MEMORY_SHORTCUT_URL, + CHROME_EXTENSION_URL, RAYCAST_EXTENSION_URL, SEARCH_MEMORY_SHORTCUT_URL, } from "@lib/constants" @@ -215,11 +216,7 @@ export default function Integrations() { }, [searchParams, hasTriggeredRaycast, createRaycastApiKeyMutation, org]) const handleChromeInstall = () => { - window.open( - "https://chromewebstore.google.com/detail/supermemory/afpgkkipfdpeaflnpoaffkcankadgjfc", - "_blank", - "noopener,noreferrer", - ) + window.open(CHROME_EXTENSION_URL, "_blank", "noopener,noreferrer") analytics.onboardingChromeExtensionClicked({ source: "settings" }) analytics.extensionInstallClicked() } diff --git a/packages/lib/constants.ts b/packages/lib/constants.ts index bdeb91604..6762ec1b0 100644 --- a/packages/lib/constants.ts +++ b/packages/lib/constants.ts @@ -5,6 +5,7 @@ const SEARCH_MEMORY_SHORTCUT_URL = const ADD_MEMORY_SHORTCUT_URL = "https://www.icloud.com/shortcuts/0fd3e855be444845b457f94c78c2c8d9" const RAYCAST_EXTENSION_URL = "https://www.raycast.com/supermemory/supermemory" +const CHROME_EXTENSION_URL = "https://chromewebstore.google.com/detail/supermemory/afpgkkipfdpeaflnpoaffkcankadgjfc" export { BIG_DIMENSIONS_NEW, @@ -12,4 +13,5 @@ export { SEARCH_MEMORY_SHORTCUT_URL, ADD_MEMORY_SHORTCUT_URL, RAYCAST_EXTENSION_URL, + CHROME_EXTENSION_URL, } diff --git a/packages/tools/src/ai-sdk.ts b/packages/tools/src/ai-sdk.ts index 7e10523ed..6cd2d6129 100644 --- a/packages/tools/src/ai-sdk.ts +++ b/packages/tools/src/ai-sdk.ts @@ -118,6 +118,259 @@ export const addMemoryTool = ( }) } +export const getProfileTool = ( + apiKey: string, + config?: SupermemoryToolsConfig, +) => { + const client = new Supermemory({ + apiKey, + ...(config?.baseUrl ? { baseURL: config.baseUrl } : {}), + }) + + const containerTags = getContainerTags(config) + const strict = config?.strict ?? false + + return tool({ + description: TOOL_DESCRIPTIONS.getProfile, + inputSchema: z.object({ + containerTag: strict + ? z.string().describe(PARAMETER_DESCRIPTIONS.containerTag) + : z + .string() + .optional() + .describe(PARAMETER_DESCRIPTIONS.containerTag), + query: z + .string() + .optional() + .describe(PARAMETER_DESCRIPTIONS.query), + }), + execute: async ({ containerTag, query }) => { + try { + const tag = containerTag || containerTags[0] + + const response = await client.profile({ + containerTag: tag, + ...(query && { q: query }), + }) + + return { + success: true, + profile: response.profile, + searchResults: response.searchResults, + } + } catch (error) { + return { + success: false, + error: error instanceof Error ? error.message : "Unknown error", + } + } + }, + }) +} + +export const documentListTool = ( + apiKey: string, + config?: SupermemoryToolsConfig, +) => { + const client = new Supermemory({ + apiKey, + ...(config?.baseUrl ? { baseURL: config.baseUrl } : {}), + }) + + const containerTags = getContainerTags(config) + const strict = config?.strict ?? false + + return tool({ + description: TOOL_DESCRIPTIONS.documentList, + inputSchema: z.object({ + containerTag: z + .string() + .optional() + .describe(PARAMETER_DESCRIPTIONS.containerTag), + limit: strict + ? z + .number() + .default(DEFAULT_VALUES.limit) + .describe(PARAMETER_DESCRIPTIONS.limit) + : z + .number() + .optional() + .default(DEFAULT_VALUES.limit) + .describe(PARAMETER_DESCRIPTIONS.limit), + offset: z + .number() + .optional() + .describe(PARAMETER_DESCRIPTIONS.offset), + status: z + .string() + .optional() + .describe(PARAMETER_DESCRIPTIONS.status), + }), + execute: async ({ containerTag, limit, offset, status }) => { + try { + const tag = containerTag || containerTags[0] + + const response = await client.documents.list({ + containerTags: [tag], + limit: limit || DEFAULT_VALUES.limit, + ...(offset !== undefined && { offset }), + ...(status && { status }), + }) + + return { + success: true, + documents: response.documents, + pagination: response.pagination, + } + } catch (error) { + return { + success: false, + error: error instanceof Error ? error.message : "Unknown error", + } + } + }, + }) +} + +export const documentDeleteTool = ( + apiKey: string, + config?: SupermemoryToolsConfig, +) => { + const client = new Supermemory({ + apiKey, + ...(config?.baseUrl ? { baseURL: config.baseUrl } : {}), + }) + + return tool({ + description: TOOL_DESCRIPTIONS.documentDelete, + inputSchema: z.object({ + documentId: z.string().describe(PARAMETER_DESCRIPTIONS.documentId), + }), + execute: async ({ documentId }) => { + try { + await client.documents.delete({ docId: documentId }) + + return { + success: true, + message: `Document ${documentId} deleted successfully`, + } + } catch (error) { + return { + success: false, + error: error instanceof Error ? error.message : "Unknown error", + } + } + }, + }) +} + +export const documentAddTool = ( + apiKey: string, + config?: SupermemoryToolsConfig, +) => { + const client = new Supermemory({ + apiKey, + ...(config?.baseUrl ? { baseURL: config.baseUrl } : {}), + }) + + const containerTags = getContainerTags(config) + + return tool({ + description: TOOL_DESCRIPTIONS.documentAdd, + inputSchema: z.object({ + content: z.string().describe(PARAMETER_DESCRIPTIONS.content), + title: z.string().optional().describe(PARAMETER_DESCRIPTIONS.title), + description: z + .string() + .optional() + .describe(PARAMETER_DESCRIPTIONS.description), + }), + execute: async ({ content, title, description }) => { + try { + const metadata: Record = {} + if (title) metadata.title = title + if (description) metadata.description = description + + const response = await client.documents.add({ + content, + containerTags, + ...(Object.keys(metadata).length > 0 && { metadata }), + }) + + return { + success: true, + document: response, + } + } catch (error) { + return { + success: false, + error: error instanceof Error ? error.message : "Unknown error", + } + } + }, + }) +} + +export const memoryForgetTool = ( + apiKey: string, + config?: SupermemoryToolsConfig, +) => { + const client = new Supermemory({ + apiKey, + ...(config?.baseUrl ? { baseURL: config.baseUrl } : {}), + }) + + const containerTags = getContainerTags(config) + + return tool({ + description: TOOL_DESCRIPTIONS.memoryForget, + inputSchema: z.object({ + containerTag: z + .string() + .optional() + .describe(PARAMETER_DESCRIPTIONS.containerTag), + memoryId: z + .string() + .optional() + .describe(PARAMETER_DESCRIPTIONS.memoryId), + memoryContent: z + .string() + .optional() + .describe(PARAMETER_DESCRIPTIONS.memoryContent), + reason: z.string().optional().describe(PARAMETER_DESCRIPTIONS.reason), + }), + execute: async ({ containerTag, memoryId, memoryContent, reason }) => { + try { + if (!memoryId && !memoryContent) { + return { + success: false, + error: "Either memoryId or memoryContent must be provided", + } + } + + const tag = containerTag || containerTags[0] + + await client.memories.forget({ + containerTag: tag, + ...(memoryId && { id: memoryId }), + ...(memoryContent && { content: memoryContent }), + ...(reason && { reason }), + }) + + return { + success: true, + message: "Memory forgotten successfully", + } + } catch (error) { + return { + success: false, + error: error instanceof Error ? error.message : "Unknown error", + } + } + }, + }) +} + /** * Create Supermemory tools for AI SDK */ @@ -128,6 +381,11 @@ export function supermemoryTools( return { searchMemories: searchMemoriesTool(apiKey, config), addMemory: addMemoryTool(apiKey, config), + getProfile: getProfileTool(apiKey, config), + documentList: documentListTool(apiKey, config), + documentDelete: documentDeleteTool(apiKey, config), + documentAdd: documentAddTool(apiKey, config), + memoryForget: memoryForgetTool(apiKey, config), } } diff --git a/packages/tools/src/openai/index.ts b/packages/tools/src/openai/index.ts index 6200d5b16..17a37a9cb 100644 --- a/packages/tools/src/openai/index.ts +++ b/packages/tools/src/openai/index.ts @@ -89,15 +89,33 @@ export function withSupermemory( } export type { OpenAIMiddlewareOptions } -export type { MemorySearchResult, MemoryAddResult } from "./tools" +export type { + MemorySearchResult, + MemoryAddResult, + ProfileResult, + DocumentListResult, + DocumentDeleteResult, + DocumentAddResult, + MemoryForgetResult, +} from "./tools" export { createSearchMemoriesFunction, createAddMemoryFunction, + createGetProfileFunction, + createDocumentListFunction, + createDocumentDeleteFunction, + createDocumentAddFunction, + createMemoryForgetFunction, supermemoryTools, getToolDefinitions, createToolCallExecutor, createToolCallsExecutor, createSearchMemoriesTool, createAddMemoryTool, + createGetProfileTool, + createDocumentListTool, + createDocumentDeleteTool, + createDocumentAddTool, + createMemoryForgetTool, memoryToolSchemas, } from "./tools" diff --git a/packages/tools/src/openai/tools.ts b/packages/tools/src/openai/tools.ts index 1ae21f147..aa1f3114d 100644 --- a/packages/tools/src/openai/tools.ts +++ b/packages/tools/src/openai/tools.ts @@ -24,6 +24,41 @@ export interface MemoryAddResult { error?: string } +export interface ProfileResult { + success: boolean + profile?: { + static: string[] + dynamic: string[] + } + searchResults?: Awaited> + error?: string +} + +export interface DocumentListResult { + success: boolean + documents?: Awaited>["documents"] + pagination?: Awaited>["pagination"] + error?: string +} + +export interface DocumentDeleteResult { + success: boolean + message?: string + error?: string +} + +export interface DocumentAddResult { + success: boolean + document?: Awaited> + error?: string +} + +export interface MemoryForgetResult { + success: boolean + message?: string + error?: string +} + /** * Function schemas for OpenAI function calling */ @@ -67,6 +102,118 @@ export const memoryToolSchemas = { required: ["memory"], }, } satisfies OpenAI.FunctionDefinition, + + getProfile: { + name: "getProfile", + description: TOOL_DESCRIPTIONS.getProfile, + parameters: { + type: "object", + properties: { + containerTag: { + type: "string", + description: PARAMETER_DESCRIPTIONS.containerTag, + }, + query: { + type: "string", + description: PARAMETER_DESCRIPTIONS.query, + }, + }, + required: [], + }, + } satisfies OpenAI.FunctionDefinition, + + documentList: { + name: "documentList", + description: TOOL_DESCRIPTIONS.documentList, + parameters: { + type: "object", + properties: { + containerTag: { + type: "string", + description: PARAMETER_DESCRIPTIONS.containerTag, + }, + limit: { + type: "number", + description: PARAMETER_DESCRIPTIONS.limit, + default: DEFAULT_VALUES.limit, + }, + offset: { + type: "number", + description: PARAMETER_DESCRIPTIONS.offset, + }, + status: { + type: "string", + description: PARAMETER_DESCRIPTIONS.status, + }, + }, + required: [], + }, + } satisfies OpenAI.FunctionDefinition, + + documentDelete: { + name: "documentDelete", + description: TOOL_DESCRIPTIONS.documentDelete, + parameters: { + type: "object", + properties: { + documentId: { + type: "string", + description: PARAMETER_DESCRIPTIONS.documentId, + }, + }, + required: ["documentId"], + }, + } satisfies OpenAI.FunctionDefinition, + + documentAdd: { + name: "documentAdd", + description: TOOL_DESCRIPTIONS.documentAdd, + parameters: { + type: "object", + properties: { + content: { + type: "string", + description: PARAMETER_DESCRIPTIONS.content, + }, + title: { + type: "string", + description: PARAMETER_DESCRIPTIONS.title, + }, + description: { + type: "string", + description: PARAMETER_DESCRIPTIONS.description, + }, + }, + required: ["content"], + }, + } satisfies OpenAI.FunctionDefinition, + + memoryForget: { + name: "memoryForget", + description: TOOL_DESCRIPTIONS.memoryForget, + parameters: { + type: "object", + properties: { + containerTag: { + type: "string", + description: PARAMETER_DESCRIPTIONS.containerTag, + }, + memoryId: { + type: "string", + description: PARAMETER_DESCRIPTIONS.memoryId, + }, + memoryContent: { + type: "string", + description: PARAMETER_DESCRIPTIONS.memoryContent, + }, + reason: { + type: "string", + description: PARAMETER_DESCRIPTIONS.reason, + }, + }, + required: [], + }, + } satisfies OpenAI.FunctionDefinition, } as const /** @@ -160,6 +307,210 @@ export function createAddMemoryFunction( } } +/** + * Get profile function + */ +export function createGetProfileFunction( + apiKey: string, + config?: SupermemoryToolsConfig, +) { + const { client, containerTags } = createClient(apiKey, config) + + return async function getProfile({ + containerTag, + query, + }: { + containerTag?: string + query?: string + }): Promise { + try { + const tag = containerTag || containerTags[0] + + const response = await client.profile({ + containerTag: tag, + ...(query && { q: query }), + }) + + return { + success: true, + profile: response.profile, + searchResults: response.searchResults, + } + } catch (error) { + return { + success: false, + error: error instanceof Error ? error.message : "Unknown error", + } + } + } +} + +/** + * List documents function + */ +export function createDocumentListFunction( + apiKey: string, + config?: SupermemoryToolsConfig, +) { + const { client, containerTags } = createClient(apiKey, config) + + return async function documentList({ + containerTag, + limit, + offset, + status, + }: { + containerTag?: string + limit?: number + offset?: number + status?: string + }): Promise { + try { + const tag = containerTag || containerTags[0] + + const response = await client.documents.list({ + containerTags: [tag], + limit: limit || DEFAULT_VALUES.limit, + ...(offset !== undefined && { offset }), + ...(status && { status }), + }) + + return { + success: true, + documents: response.documents, + pagination: response.pagination, + } + } catch (error) { + return { + success: false, + error: error instanceof Error ? error.message : "Unknown error", + } + } + } +} + +/** + * Delete document function + */ +export function createDocumentDeleteFunction( + apiKey: string, + config?: SupermemoryToolsConfig, +) { + const { client } = createClient(apiKey, config) + + return async function documentDelete({ + documentId, + }: { + documentId: string + }): Promise { + try { + await client.documents.delete(documentId) + + return { + success: true, + message: `Document ${documentId} deleted successfully`, + } + } catch (error) { + return { + success: false, + error: error instanceof Error ? error.message : "Unknown error", + } + } + } +} + +/** + * Add document function + */ +export function createDocumentAddFunction( + apiKey: string, + config?: SupermemoryToolsConfig, +) { + const { client, containerTags } = createClient(apiKey, config) + + return async function documentAdd({ + content, + title, + description, + }: { + content: string + title?: string + description?: string + }): Promise { + try { + const metadata: Record = {} + if (title) metadata.title = title + if (description) metadata.description = description + + const response = await client.documents.add({ + content, + containerTags, + ...(Object.keys(metadata).length > 0 && { metadata }), + }) + + return { + success: true, + document: response, + } + } catch (error) { + return { + success: false, + error: error instanceof Error ? error.message : "Unknown error", + } + } + } +} + +/** + * Forget memory function + */ +export function createMemoryForgetFunction( + apiKey: string, + config?: SupermemoryToolsConfig, +) { + const { client, containerTags } = createClient(apiKey, config) + + return async function memoryForget({ + containerTag, + memoryId, + memoryContent, + reason, + }: { + containerTag?: string + memoryId?: string + memoryContent?: string + reason?: string + }): Promise { + try { + if (!memoryId && !memoryContent) { + return { + success: false, + error: "Either memoryId or memoryContent must be provided", + } + } + + const tag = containerTag || containerTags[0] + + await client.memories.forget({ + containerTag: tag, + ...(memoryId && { id: memoryId }), + ...(memoryContent && { content: memoryContent }), + ...(reason && { reason }), + }) + + return { + success: true, + message: "Memory forgotten successfully", + } + } catch (error) { + return { + success: false, + error: error instanceof Error ? error.message : "Unknown error", + } + } + } +} + /** * Create all memory tools functions */ @@ -169,10 +520,20 @@ export function supermemoryTools( ) { const searchMemories = createSearchMemoriesFunction(apiKey, config) const addMemory = createAddMemoryFunction(apiKey, config) + const getProfile = createGetProfileFunction(apiKey, config) + const documentList = createDocumentListFunction(apiKey, config) + const documentDelete = createDocumentDeleteFunction(apiKey, config) + const documentAdd = createDocumentAddFunction(apiKey, config) + const memoryForget = createMemoryForgetFunction(apiKey, config) return { searchMemories, addMemory, + getProfile, + documentList, + documentDelete, + documentAdd, + memoryForget, } } @@ -183,6 +544,11 @@ export function getToolDefinitions(): OpenAI.Chat.Completions.ChatCompletionTool return [ { type: "function", function: memoryToolSchemas.searchMemories }, { type: "function", function: memoryToolSchemas.addMemory }, + { type: "function", function: memoryToolSchemas.getProfile }, + { type: "function", function: memoryToolSchemas.documentList }, + { type: "function", function: memoryToolSchemas.documentDelete }, + { type: "function", function: memoryToolSchemas.documentAdd }, + { type: "function", function: memoryToolSchemas.memoryForget }, ] } @@ -206,6 +572,16 @@ export function createToolCallExecutor( return JSON.stringify(await tools.searchMemories(args)) case "addMemory": return JSON.stringify(await tools.addMemory(args)) + case "getProfile": + return JSON.stringify(await tools.getProfile(args)) + case "documentList": + return JSON.stringify(await tools.documentList(args)) + case "documentDelete": + return JSON.stringify(await tools.documentDelete(args)) + case "documentAdd": + return JSON.stringify(await tools.documentAdd(args)) + case "memoryForget": + return JSON.stringify(await tools.memoryForget(args)) default: return JSON.stringify({ success: false, @@ -274,3 +650,78 @@ export function createAddMemoryTool( execute: addMemory, } } + +export function createGetProfileTool( + apiKey: string, + config?: SupermemoryToolsConfig, +) { + const getProfile = createGetProfileFunction(apiKey, config) + + return { + definition: { + type: "function" as const, + function: memoryToolSchemas.getProfile, + }, + execute: getProfile, + } +} + +export function createDocumentListTool( + apiKey: string, + config?: SupermemoryToolsConfig, +) { + const documentList = createDocumentListFunction(apiKey, config) + + return { + definition: { + type: "function" as const, + function: memoryToolSchemas.documentList, + }, + execute: documentList, + } +} + +export function createDocumentDeleteTool( + apiKey: string, + config?: SupermemoryToolsConfig, +) { + const documentDelete = createDocumentDeleteFunction(apiKey, config) + + return { + definition: { + type: "function" as const, + function: memoryToolSchemas.documentDelete, + }, + execute: documentDelete, + } +} + +export function createDocumentAddTool( + apiKey: string, + config?: SupermemoryToolsConfig, +) { + const documentAdd = createDocumentAddFunction(apiKey, config) + + return { + definition: { + type: "function" as const, + function: memoryToolSchemas.documentAdd, + }, + execute: documentAdd, + } +} + +export function createMemoryForgetTool( + apiKey: string, + config?: SupermemoryToolsConfig, +) { + const memoryForget = createMemoryForgetFunction(apiKey, config) + + return { + definition: { + type: "function" as const, + function: memoryToolSchemas.memoryForget, + }, + execute: memoryForget, + } +} diff --git a/packages/tools/src/tools-shared.ts b/packages/tools/src/tools-shared.ts index b40000424..1ea20db56 100644 --- a/packages/tools/src/tools-shared.ts +++ b/packages/tools/src/tools-shared.ts @@ -8,6 +8,16 @@ export const TOOL_DESCRIPTIONS = { "Search (recall) memories/details/information about the user or other facts or entities. Run when explicitly asked or when context about user's past choices would be helpful.", addMemory: "Add (remember) memories/details/information about the user or other facts or entities. Run when explicitly asked or when the user mentions any information generalizable beyond the context of the current conversation.", + getProfile: + "Get user profile containing static memories (permanent facts) and dynamic memories (recent context). Optionally include search results by providing a query.", + documentList: + "List stored documents with optional filtering by container tag, status, and pagination. Useful for browsing or managing saved content.", + documentDelete: + "Delete a document and its associated memories by document ID or customId. Deletes are permanent. Use when user wants to remove saved content.", + documentAdd: + "Add a new document (URL, text, or content) to memory. The content is queued for processing, and memories will be extracted automatically.", + memoryForget: + "Forget (soft delete) a specific memory by ID or content match. The memory is marked as forgotten but not permanently deleted. Use when user wants to remove specific information from their profile.", } as const // Parameter descriptions @@ -18,6 +28,17 @@ export const PARAMETER_DESCRIPTIONS = { limit: "Maximum number of results to return", memory: "The text content of the memory to add. This should be a single sentence or a short paragraph.", + containerTag: "Tag to filter/scope the operation (e.g., user ID, project ID)", + query: "Optional search query to include relevant search results", + offset: "Number of items to skip for pagination (default: 0)", + status: "Filter documents by processing status (e.g., 'completed', 'processing', 'failed')", + documentId: "The unique identifier of the document to operate on", + content: "The content to add - can be text, URL, or other supported formats", + title: "Optional title for the document", + description: "Optional description for the document", + memoryId: "The unique identifier of the memory entry", + memoryContent: "Exact content match of the memory entry to operate on (alternative to ID)", + reason: "Optional reason for forgetting this memory", } as const // Default values diff --git a/packages/tools/src/tools.test.ts b/packages/tools/src/tools.test.ts index ab6f9b848..56ef7b62f 100644 --- a/packages/tools/src/tools.test.ts +++ b/packages/tools/src/tools.test.ts @@ -34,6 +34,11 @@ describe("@supermemory/tools", () => { expect(tools).toBeDefined() expect(tools.searchMemories).toBeDefined() expect(tools.addMemory).toBeDefined() + expect(tools.getProfile).toBeDefined() + expect(tools.documentList).toBeDefined() + expect(tools.documentDelete).toBeDefined() + expect(tools.documentAdd).toBeDefined() + expect(tools.memoryForget).toBeDefined() }) it("should create tools with custom baseUrl", () => { @@ -45,6 +50,11 @@ describe("@supermemory/tools", () => { expect(tools).toBeDefined() expect(tools.searchMemories).toBeDefined() expect(tools.addMemory).toBeDefined() + expect(tools.getProfile).toBeDefined() + expect(tools.documentList).toBeDefined() + expect(tools.documentDelete).toBeDefined() + expect(tools.documentAdd).toBeDefined() + expect(tools.memoryForget).toBeDefined() }) it("should create individual tools", () => { @@ -54,9 +64,29 @@ describe("@supermemory/tools", () => { const addTool = aiSdk.addMemoryTool(testApiKey, { projectId: "test-project-123", }) + const profileTool = aiSdk.getProfileTool(testApiKey, { + projectId: "test-project-123", + }) + const listTool = aiSdk.documentListTool(testApiKey, { + projectId: "test-project-123", + }) + const deleteTool = aiSdk.documentDeleteTool(testApiKey, { + projectId: "test-project-123", + }) + const addDocTool = aiSdk.documentAddTool(testApiKey, { + projectId: "test-project-123", + }) + const forgetTool = aiSdk.memoryForgetTool(testApiKey, { + projectId: "test-project-123", + }) expect(searchTool).toBeDefined() expect(addTool).toBeDefined() + expect(profileTool).toBeDefined() + expect(listTool).toBeDefined() + expect(deleteTool).toBeDefined() + expect(addDocTool).toBeDefined() + expect(forgetTool).toBeDefined() }) }) @@ -123,6 +153,128 @@ describe("@supermemory/tools", () => { expect(result).toBeDefined() expect(result.text).toBeDefined() }) + + it("should work with new profile tool", async () => { + const openai = createOpenAI({ + apiKey: testOpenAIKey, + }) + + const tools = aiSdk.supermemoryTools(testApiKey, { + projectId: "test-profile-tool", + baseUrl: testBaseUrl, + }) + + const result = await generateText({ + model: openai(testModelName), + messages: [ + { + role: "system", + content: + "You are a helpful assistant. When asked about user profile or preferences, use the getProfile tool.", + }, + { + role: "user", + content: "What do you know about me?", + }, + ], + tools: { + getProfile: tools.getProfile, + }, + }) + + expect(result).toBeDefined() + expect(result.text).toBeDefined() + }) + + it("should work with new document tools", async () => { + const openai = createOpenAI({ + apiKey: testOpenAIKey, + }) + + const tools = aiSdk.supermemoryTools(testApiKey, { + projectId: "test-document-tools", + baseUrl: testBaseUrl, + }) + + const result = await generateText({ + model: openai(testModelName), + messages: [ + { + role: "system", + content: + "You are a helpful assistant. When asked to list documents, use the documentList tool.", + }, + { + role: "user", + content: "Show me my saved documents", + }, + ], + tools: { + documentList: tools.documentList, + }, + }) + + expect(result).toBeDefined() + expect(result.text).toBeDefined() + }) + }) + + describe("new tool operations", () => { + it("should get profile with getProfileTool", async () => { + const profileTool = aiSdk.getProfileTool(testApiKey, { + projectId: "test-profile", + baseUrl: testBaseUrl, + }) + + // Verify tool is a valid CoreTool from AI SDK + expect(profileTool).toBeDefined() + expect(profileTool.description).toBeDefined() + expect(typeof profileTool.description).toBe("string") + }) + + it("should list documents with documentListTool", async () => { + const listTool = aiSdk.documentListTool(testApiKey, { + projectId: "test-list", + baseUrl: testBaseUrl, + }) + + expect(listTool).toBeDefined() + expect(listTool.description).toBeDefined() + expect(typeof listTool.description).toBe("string") + }) + + it("should create documentDeleteTool", async () => { + const deleteTool = aiSdk.documentDeleteTool(testApiKey, { + projectId: "test-delete", + baseUrl: testBaseUrl, + }) + + expect(deleteTool).toBeDefined() + expect(deleteTool.description).toBeDefined() + expect(typeof deleteTool.description).toBe("string") + }) + + it("should create documentAddTool", async () => { + const addDocTool = aiSdk.documentAddTool(testApiKey, { + projectId: "test-add-doc", + baseUrl: testBaseUrl, + }) + + expect(addDocTool).toBeDefined() + expect(addDocTool.description).toBeDefined() + expect(typeof addDocTool.description).toBe("string") + }) + + it("should create memoryForgetTool", async () => { + const forgetTool = aiSdk.memoryForgetTool(testApiKey, { + projectId: "test-forget", + baseUrl: testBaseUrl, + }) + + expect(forgetTool).toBeDefined() + expect(forgetTool.description).toBeDefined() + expect(typeof forgetTool.description).toBe("string") + }) }) })