diff --git a/packages/ai-sdk/package.json b/packages/ai-sdk/package.json index 30ff931b..d4923c5a 100644 --- a/packages/ai-sdk/package.json +++ b/packages/ai-sdk/package.json @@ -1,19 +1,21 @@ { "name": "@supermemory/ai-sdk", "type": "module", - "version": "1.0.8", + "version": "1.0.9", "scripts": { "build": "tsdown", "dev": "tsdown --watch --ignore-watch .turbo", "check-types": "tsc --noEmit", "test": "vitest", + "test:unit": "vitest run src/tools.unit.test.ts", "test:watch": "vitest --watch" }, "dependencies": { "@ai-sdk/openai": "^2.0.22", "@ai-sdk/provider": "^2.0.0", + "@supermemory/tools": "workspace:*", "ai": "^5.0.113", - "supermemory": "^3.0.0-alpha.26" + "supermemory": "^4.25.4" }, "devDependencies": { "@total-typescript/tsconfig": "^1.0.4", @@ -24,7 +26,7 @@ }, "main": "./dist/index.js", "module": "./dist/index.js", - "types": "./dist/index-Dk1U5LBS.d.ts", + "types": "./dist/index-B8qmWxBg.d.ts", "exports": { ".": "./dist/index.js", "./package.json": "./package.json" diff --git a/packages/ai-sdk/src/tools.test.ts b/packages/ai-sdk/src/tools.test.ts index 0acfc7ed..83e397ca 100644 --- a/packages/ai-sdk/src/tools.test.ts +++ b/packages/ai-sdk/src/tools.test.ts @@ -5,19 +5,12 @@ import { type SupermemoryToolsConfig, supermemoryTools } from "./tools" import "dotenv/config" -describe("supermemoryTools", () => { - // Required API keys - tests will fail if not provided - const testApiKey = process.env.SUPERMEMORY_API_KEY - const testOpenAIKey = process.env.OPENAI_API_KEY - - if (!testApiKey) { - throw new Error( - "SUPERMEMORY_API_KEY environment variable is required for tests", - ) - } - if (!testOpenAIKey) { - throw new Error("OPENAI_API_KEY environment variable is required for tests") - } +describe.skipIf( + !process.env.SUPERMEMORY_API_KEY || !process.env.OPENAI_API_KEY, +)("supermemoryTools", () => { + // Required API keys — suite is skipped in CI without them + const testApiKey = process.env.SUPERMEMORY_API_KEY as string + const testOpenAIKey = process.env.OPENAI_API_KEY as string // Optional configuration with defaults const testBaseUrl = process.env.SUPERMEMORY_BASE_URL ?? undefined @@ -39,6 +32,11 @@ describe("supermemoryTools", () => { 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", () => { diff --git a/packages/ai-sdk/src/tools.ts b/packages/ai-sdk/src/tools.ts index 7d0b837c..b0bafb08 100644 --- a/packages/ai-sdk/src/tools.ts +++ b/packages/ai-sdk/src/tools.ts @@ -1,150 +1,17 @@ -import { jsonSchema, tool } from "ai" -import Supermemory from "supermemory" - /** - * Supermemory configuration - * Only one of `projectId` or `containerTags` can be provided. + * Re-export the canonical Supermemory AI SDK tools from @supermemory/tools. + * Prefer @supermemory/tools for middleware, OpenAI, Mastra, and VoltAgent integrations. */ -export interface SupermemoryToolsConfig { - baseUrl?: string - containerTags?: string[] - projectId?: string -} +export { + supermemoryTools, + searchMemoriesTool, + addMemoryTool, + getProfileTool, + documentListTool, + documentDeleteTool, + documentAddTool, + memoryForgetTool, + getContainerTags, +} from "@supermemory/tools/ai-sdk" -type SearchMemoriesInput = { - informationToGet: string - includeFullDocs: boolean - limit: number -} - -type AddMemoryInput = { - memory: string -} - -/** - * Create Supermemory tools for AI SDK - */ -export function supermemoryTools( - apiKey: string, - config?: SupermemoryToolsConfig, -) { - const client = new Supermemory({ - apiKey, - ...(config?.baseUrl ? { baseURL: config.baseUrl } : {}), - }) - - const containerTags = config?.projectId - ? [`sm_project_${config?.projectId}`] - : config?.containerTags - - const searchMemories = tool({ - description: - "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.", - inputSchema: jsonSchema({ - type: "object", - properties: { - informationToGet: { - type: "string", - description: "Terms to search for in the user's memories", - }, - includeFullDocs: { - type: "boolean", - description: - "Whether to include the full document content in the response. Defaults to true for better AI context.", - default: true, - }, - limit: { - type: "number", - description: "Maximum number of results to return", - default: 10, - }, - }, - required: ["informationToGet"], - }), - execute: async ({ - informationToGet, - includeFullDocs = true, - limit = 10, - }) => { - try { - const response = await client.search.execute({ - q: informationToGet, - containerTags, - limit, - chunkThreshold: 0.6, - includeFullDocs, - }) - - return { - success: true, - results: response.results, - count: response.results?.length || 0, - } - } catch (error) { - return { - success: false, - error: error instanceof Error ? error.message : "Unknown error", - } - } - }, - }) - - const addMemory = tool({ - description: - "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.", - inputSchema: jsonSchema({ - type: "object", - properties: { - memory: { - type: "string", - description: - "The text content of the memory to add. This should be a single sentence or a short paragraph.", - }, - }, - required: ["memory"], - }), - execute: async ({ memory }) => { - try { - const metadata: Record = {} - - const response = await client.add({ - content: memory, - containerTags, - ...(Object.keys(metadata).length > 0 && { metadata }), - }) - - return { - success: true, - memory: response, - } - } catch (error) { - return { - success: false, - error: error instanceof Error ? error.message : "Unknown error", - } - } - }, - }) - - return { - searchMemories, - addMemory, - } -} - -// Export individual tool creators for more flexibility -export const searchMemoriesTool = ( - apiKey: string, - config?: SupermemoryToolsConfig, -) => { - const { searchMemories } = supermemoryTools(apiKey, config) - return searchMemories -} - -export const addMemoryTool = ( - apiKey: string, - config?: SupermemoryToolsConfig, -) => { - const { addMemory } = supermemoryTools(apiKey, config) - return addMemory -} +export type { SupermemoryToolsConfig } from "@supermemory/tools" diff --git a/packages/ai-sdk/src/tools.unit.test.ts b/packages/ai-sdk/src/tools.unit.test.ts new file mode 100644 index 00000000..e4f58b7a --- /dev/null +++ b/packages/ai-sdk/src/tools.unit.test.ts @@ -0,0 +1,28 @@ +import { describe, expect, it } from "vitest" +import { getContainerTags } from "./tools" + +describe("getContainerTags", () => { + it("defaults to the default project when no config is provided", () => { + expect(getContainerTags()).toEqual(["sm_project_default"]) + }) + + it("converts projectId into a project container tag", () => { + expect(getContainerTags({ projectId: "abc" })).toEqual(["sm_project_abc"]) + }) + + it("uses explicit container tags", () => { + expect(getContainerTags({ containerTags: ["tag-a", "tag-b"] })).toEqual([ + "tag-a", + "tag-b", + ]) + }) + + it("rejects config with both projectId and containerTags", () => { + expect(() => + getContainerTags({ + projectId: "abc", + containerTags: ["tag-a"], + }), + ).toThrow("either projectId or containerTags") + }) +})