mirror of
https://github.com/supermemoryai/supermemory.git
synced 2026-10-20 05:06:31 +00:00
refactor(ai-sdk)!: re-export @supermemory/tools/ai-sdk (#1247) — the ! marks the breaking default-scope change.
This commit is contained in:
parent
2cebe81512
commit
b73b4cb53e
9 changed files with 39 additions and 352 deletions
3
.github/workflows/publish-ai-sdk.yml
vendored
3
.github/workflows/publish-ai-sdk.yml
vendored
|
|
@ -56,7 +56,8 @@ jobs:
|
|||
|
||||
- name: Build
|
||||
if: steps.version-check.outputs.changed == 'true'
|
||||
run: bun run build
|
||||
working-directory: .
|
||||
run: bunx turbo run build --filter='@supermemory/ai-sdk'
|
||||
|
||||
- name: Publish
|
||||
if: steps.version-check.outputs.changed == 'true'
|
||||
|
|
|
|||
9
bun.lock
9
bun.lock
|
|
@ -242,19 +242,16 @@
|
|||
},
|
||||
"packages/ai-sdk": {
|
||||
"name": "@supermemory/ai-sdk",
|
||||
"version": "1.0.8",
|
||||
"version": "2.0.0",
|
||||
"dependencies": {
|
||||
"@ai-sdk/openai": "^2.0.22",
|
||||
"@ai-sdk/provider": "^2.0.0",
|
||||
"ai": "^5.0.113",
|
||||
"supermemory": "^3.0.0-alpha.26",
|
||||
"@supermemory/tools": "^2.1.0",
|
||||
},
|
||||
"devDependencies": {
|
||||
"@total-typescript/tsconfig": "^1.0.4",
|
||||
"@types/bun": "^1.2.21",
|
||||
"tsdown": "^0.14.2",
|
||||
"typescript": "^5.9.2",
|
||||
"vitest": "^3.2.4",
|
||||
},
|
||||
},
|
||||
"packages/docs-test": {
|
||||
|
|
@ -320,7 +317,7 @@
|
|||
},
|
||||
"packages/tools": {
|
||||
"name": "@supermemory/tools",
|
||||
"version": "2.0.0",
|
||||
"version": "2.1.0",
|
||||
"dependencies": {
|
||||
"@ai-sdk/anthropic": "^2.0.25",
|
||||
"@ai-sdk/openai": "^2.0.23",
|
||||
|
|
|
|||
|
|
@ -1,5 +1,18 @@
|
|||
# supermemory AI SDK Utilities
|
||||
|
||||
> **Deprecated.** Use [`@supermemory/tools`](../tools) instead:
|
||||
>
|
||||
> ```ts
|
||||
> import { supermemoryTools } from "@supermemory/tools/ai-sdk"
|
||||
> ```
|
||||
>
|
||||
> As of 2.0.0 this package is a thin re-export of `@supermemory/tools/ai-sdk`, so it
|
||||
> inherits that package's container-scoping semantics. **This is a behavior change:**
|
||||
>
|
||||
> - Passing both `projectId` and `containerTags` now throws instead of silently ignoring `containerTags`.
|
||||
> - With no config, calls are scoped to `["sm_project_default"]`. Previously they were **unscoped**, i.e. searching and writing across every project. If you relied on that, pass `containerTags` explicitly.
|
||||
> - `limit` is coerced, so a model emitting `"5"` is accepted rather than rejected.
|
||||
|
||||
Vercel AI SDK utilities for supermemory
|
||||
|
||||
## Installation
|
||||
|
|
|
|||
|
|
@ -1,30 +1,26 @@
|
|||
{
|
||||
"name": "@supermemory/ai-sdk",
|
||||
"type": "module",
|
||||
"version": "1.0.8",
|
||||
"version": "2.0.0",
|
||||
"description": "Deprecated: re-exports @supermemory/tools/ai-sdk",
|
||||
"scripts": {
|
||||
"build": "tsdown",
|
||||
"dev": "tsdown --watch --ignore-watch .turbo",
|
||||
"check-types": "tsc --noEmit",
|
||||
"test": "vitest",
|
||||
"test:watch": "vitest --watch"
|
||||
"check-types": "tsc --noEmit"
|
||||
},
|
||||
"dependencies": {
|
||||
"@ai-sdk/openai": "^2.0.22",
|
||||
"@ai-sdk/provider": "^2.0.0",
|
||||
"ai": "^5.0.113",
|
||||
"supermemory": "^3.0.0-alpha.26"
|
||||
"@supermemory/tools": "^2.1.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@total-typescript/tsconfig": "^1.0.4",
|
||||
"@types/bun": "^1.2.21",
|
||||
"tsdown": "^0.14.2",
|
||||
"typescript": "^5.9.2",
|
||||
"vitest": "^3.2.4"
|
||||
"typescript": "^5.9.2"
|
||||
},
|
||||
"main": "./dist/index.js",
|
||||
"module": "./dist/index.js",
|
||||
"types": "./dist/index-Dk1U5LBS.d.ts",
|
||||
"types": "./dist/index-iQkOjAt3.d.ts",
|
||||
"exports": {
|
||||
".": "./dist/index.js",
|
||||
"./package.json": "./package.json"
|
||||
|
|
|
|||
|
|
@ -1 +1,12 @@
|
|||
export * from "./tools"
|
||||
/**
|
||||
* @deprecated Use `@supermemory/tools/ai-sdk` instead. This package is now a
|
||||
* thin re-export of it and will not receive further changes.
|
||||
*
|
||||
* Behavior change in 2.0.0 (inherited from `@supermemory/tools`):
|
||||
* - Passing both `projectId` and `containerTags` now throws instead of silently
|
||||
* ignoring `containerTags`.
|
||||
* - With no config, calls are scoped to `["sm_project_default"]` instead of being
|
||||
* unscoped across every project.
|
||||
* - `limit`/`offset` are coerced, so a model emitting `"5"` is accepted.
|
||||
*/
|
||||
export * from "@supermemory/tools/ai-sdk"
|
||||
|
|
|
|||
|
|
@ -1,181 +0,0 @@
|
|||
import { createOpenAI } from "@ai-sdk/openai"
|
||||
import { generateText } from "ai"
|
||||
import { describe, expect, it } from "vitest"
|
||||
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")
|
||||
}
|
||||
|
||||
// Optional configuration with defaults
|
||||
const testBaseUrl = process.env.SUPERMEMORY_BASE_URL ?? undefined
|
||||
const testModelName = process.env.MODEL_NAME || "gpt-5-nano"
|
||||
|
||||
const testPrompts = [
|
||||
"What do you remember about my preferences?",
|
||||
"Help me plan my day based on what you know about me",
|
||||
"What are my current projects?",
|
||||
"Remind me of my interests and hobbies",
|
||||
"What should I focus on today?",
|
||||
]
|
||||
|
||||
describe("client initialization", () => {
|
||||
it("should create tools with default configuration", () => {
|
||||
const config: SupermemoryToolsConfig = {}
|
||||
const tools = supermemoryTools(testApiKey, config)
|
||||
|
||||
expect(tools).toBeDefined()
|
||||
expect(tools.searchMemories).toBeDefined()
|
||||
expect(tools.addMemory).toBeDefined()
|
||||
})
|
||||
|
||||
it("should create tools with custom baseUrl", () => {
|
||||
const config: SupermemoryToolsConfig = {
|
||||
baseUrl: testBaseUrl,
|
||||
}
|
||||
const tools = supermemoryTools(testApiKey, config)
|
||||
|
||||
expect(tools).toBeDefined()
|
||||
expect(tools.searchMemories).toBeDefined()
|
||||
expect(tools.addMemory).toBeDefined()
|
||||
})
|
||||
|
||||
it("should create tools with projectId configuration", () => {
|
||||
const config: SupermemoryToolsConfig = {
|
||||
projectId: "test-project-123",
|
||||
}
|
||||
const tools = supermemoryTools(testApiKey, config)
|
||||
|
||||
expect(tools).toBeDefined()
|
||||
expect(tools.searchMemories).toBeDefined()
|
||||
expect(tools.addMemory).toBeDefined()
|
||||
})
|
||||
|
||||
it("should create tools with custom container tags", () => {
|
||||
const config: SupermemoryToolsConfig = {
|
||||
containerTags: ["custom-tag-1", "custom-tag-2"],
|
||||
}
|
||||
const tools = supermemoryTools(testApiKey, config)
|
||||
|
||||
expect(tools).toBeDefined()
|
||||
expect(tools.searchMemories).toBeDefined()
|
||||
expect(tools.addMemory).toBeDefined()
|
||||
})
|
||||
})
|
||||
|
||||
describe("AI SDK integration", () => {
|
||||
it("should work with AI SDK generateText", async () => {
|
||||
const openai = createOpenAI({
|
||||
apiKey: testOpenAIKey,
|
||||
})
|
||||
|
||||
const result = await generateText({
|
||||
model: openai(testModelName),
|
||||
messages: [
|
||||
{
|
||||
role: "system",
|
||||
content:
|
||||
"You are a helpful assistant with access to user memories. Use the search tool when the user asks about preferences or past information.",
|
||||
},
|
||||
{
|
||||
role: "user",
|
||||
content: testPrompts[0]!,
|
||||
},
|
||||
],
|
||||
tools: {
|
||||
...supermemoryTools(testApiKey, {
|
||||
projectId: "test-ai-integration",
|
||||
baseUrl: testBaseUrl,
|
||||
}),
|
||||
},
|
||||
})
|
||||
|
||||
expect(result).toBeDefined()
|
||||
expect(result.text).toBeDefined()
|
||||
expect(typeof result.text).toBe("string")
|
||||
})
|
||||
|
||||
it("should use tools when prompted", async () => {
|
||||
const openai = createOpenAI({
|
||||
apiKey: testOpenAIKey,
|
||||
})
|
||||
|
||||
const tools = supermemoryTools(testApiKey, {
|
||||
projectId: "test-tool-usage",
|
||||
baseUrl: testBaseUrl,
|
||||
})
|
||||
|
||||
const result = await generateText({
|
||||
model: openai(testModelName),
|
||||
messages: [
|
||||
{
|
||||
role: "system",
|
||||
content:
|
||||
"You are a helpful assistant. When the user asks you to remember something, use the addMemory tool.",
|
||||
},
|
||||
{
|
||||
role: "user",
|
||||
content: "Please remember that I prefer dark roast coffee",
|
||||
},
|
||||
],
|
||||
tools: {
|
||||
addMemory: tools.addMemory,
|
||||
},
|
||||
})
|
||||
|
||||
expect(result).toBeDefined()
|
||||
expect(result.text).toBeDefined()
|
||||
expect(result.toolCalls).toBeDefined()
|
||||
|
||||
if (result.toolCalls && result.toolCalls.length > 0) {
|
||||
const addMemoryCall = result.toolCalls.find(
|
||||
(call) => call.toolName === "addMemory",
|
||||
)
|
||||
expect(addMemoryCall).toBeDefined()
|
||||
}
|
||||
})
|
||||
|
||||
it("should handle multiple tool types", async () => {
|
||||
const openai = createOpenAI({
|
||||
apiKey: testOpenAIKey,
|
||||
})
|
||||
|
||||
const result = await generateText({
|
||||
model: openai(testModelName),
|
||||
messages: [
|
||||
{
|
||||
role: "system",
|
||||
content:
|
||||
"You are a helpful assistant with access to user memories. Use search when asked about preferences, and addMemory when told to remember something.",
|
||||
},
|
||||
{
|
||||
role: "user",
|
||||
content:
|
||||
"Search for my preferences and then remember that I also like green tea",
|
||||
},
|
||||
],
|
||||
tools: {
|
||||
...supermemoryTools(testApiKey, {
|
||||
containerTags: ["test-multi-tools"],
|
||||
}),
|
||||
},
|
||||
})
|
||||
|
||||
expect(result).toBeDefined()
|
||||
expect(result.text).toBeDefined()
|
||||
expect(typeof result.text).toBe("string")
|
||||
})
|
||||
})
|
||||
})
|
||||
|
|
@ -1,150 +0,0 @@
|
|||
import { jsonSchema, tool } from "ai"
|
||||
import Supermemory from "supermemory"
|
||||
|
||||
/**
|
||||
* Supermemory configuration
|
||||
* Only one of `projectId` or `containerTags` can be provided.
|
||||
*/
|
||||
export interface SupermemoryToolsConfig {
|
||||
baseUrl?: string
|
||||
containerTags?: string[]
|
||||
projectId?: string
|
||||
}
|
||||
|
||||
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<SearchMemoriesInput>({
|
||||
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<AddMemoryInput>({
|
||||
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<string, string | number | boolean> = {}
|
||||
|
||||
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
|
||||
}
|
||||
|
|
@ -51,8 +51,8 @@
|
|||
"./ai-sdk": "./dist/ai-sdk.js",
|
||||
"./claude-memory": "./dist/claude-memory.js",
|
||||
"./mastra": "./dist/mastra.js",
|
||||
"./openai": "./dist/openai/index.js",
|
||||
"./voltagent": "./dist/voltagent/index.js",
|
||||
"./openai\\index": "./dist/openai\\index.js",
|
||||
"./voltagent\\index": "./dist/voltagent\\index.js",
|
||||
"./package.json": "./package.json"
|
||||
},
|
||||
"repository": {
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@
|
|||
"dependsOn": ["^lint"]
|
||||
},
|
||||
"check-types": {
|
||||
"dependsOn": ["^check-types"]
|
||||
"dependsOn": ["^build"]
|
||||
},
|
||||
"dev": {
|
||||
"cache": false,
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue