OpenAI SDK Backfill (#771)

This commit is contained in:
sreedharsreeram 2026-03-06 23:57:10 +00:00
parent 5d12a59bc1
commit 6d93cd083d
5 changed files with 901 additions and 1 deletions

View file

@ -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<string, string> = {}
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),
}
}

View file

@ -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"

View file

@ -24,6 +24,41 @@ export interface MemoryAddResult {
error?: string
}
export interface ProfileResult {
success: boolean
profile?: {
static: string[]
dynamic: string[]
}
searchResults?: Awaited<ReturnType<Supermemory["search"]["execute"]>>
error?: string
}
export interface DocumentListResult {
success: boolean
documents?: Awaited<ReturnType<Supermemory["documents"]["list"]>>["documents"]
pagination?: Awaited<ReturnType<Supermemory["documents"]["list"]>>["pagination"]
error?: string
}
export interface DocumentDeleteResult {
success: boolean
message?: string
error?: string
}
export interface DocumentAddResult {
success: boolean
document?: Awaited<ReturnType<Supermemory["documents"]["add"]>>
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<ProfileResult> {
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<DocumentListResult> {
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<DocumentDeleteResult> {
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<DocumentAddResult> {
try {
const metadata: Record<string, string> = {}
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<MemoryForgetResult> {
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,
}
}

View file

@ -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

View file

@ -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")
})
})
})