supermemory/packages/tools/src/ai-sdk.ts
MaheshtheDev e4afc770be
Some checks failed
Publish Tools / publish (push) Has been cancelled
feat(tools): apiKey option, type re-exports, and two reliability fixes (#1594)
Cherry-picks four contributor PRs for `@supermemory/tools` onto one branch, and bumps the package to 2.2.0.

- #1244 (@rajarshidattapy): `withSupermemory` accepts `options.apiKey` instead of only reading `SUPERMEMORY_API_KEY`, matching the Vercel, Mastra and Voltagent integrations. Unblocks secrets managers, edge runtimes and per-request keys.
- #1574 (@Agnik47): re-exports `PromptTemplate`, `MemoryPromptData` and `WithSupermemoryOptions` from `ai-sdk`. `./vercel` is not a published subpath, so the documented custom-template example did not compile.
- #1488 (@abhinav7x94): malformed tool-call JSON returns an error result instead of throwing out of the request.
- #1507 (@abhinav7x94): VoltAgent `onEnd` awaits the conversation save, which was fire-and-forget and could be dropped when a serverless runtime tore down.

Dropped the `middleware.test.ts` added by #1244. Note that editing `packages/tools/package.json` triggers the npm publish workflow on merge.

Co-Authored-By: rajarshidattapy <138959719+rajarshidattapy@users.noreply.github.com>
Co-Authored-By: Agnik47 <140933190+Agnik47@users.noreply.github.com>
Co-Authored-By: abhinav7x94 <204053250+abhinav7x94@users.noreply.github.com>
2026-08-24 22:39:55 +00:00

381 lines
9.1 KiB
TypeScript

import Supermemory from "supermemory"
import { tool } from "ai"
import { z } from "zod"
import {
DEFAULT_VALUES,
PARAMETER_DESCRIPTIONS,
TOOL_DESCRIPTIONS,
getContainerTags,
} from "./tools-shared"
import { forgetMemoryRequest } from "./shared/forget-memory"
import type { SupermemoryToolsConfig } from "./types"
// Export individual tool creators
export const searchMemoriesTool = (
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.searchMemories,
inputSchema: z.object({
informationToGet: z
.string()
.describe(PARAMETER_DESCRIPTIONS.informationToGet),
includeFullDocs: strict
? z
.boolean()
.default(DEFAULT_VALUES.includeFullDocs)
.describe(PARAMETER_DESCRIPTIONS.includeFullDocs)
: z
.boolean()
.optional()
.default(DEFAULT_VALUES.includeFullDocs)
.describe(PARAMETER_DESCRIPTIONS.includeFullDocs),
limit: strict
? z.coerce
.number()
.default(DEFAULT_VALUES.limit)
.describe(PARAMETER_DESCRIPTIONS.limit)
: z.coerce
.number()
.optional()
.default(DEFAULT_VALUES.limit)
.describe(PARAMETER_DESCRIPTIONS.limit),
}),
execute: async ({
informationToGet,
includeFullDocs = DEFAULT_VALUES.includeFullDocs,
limit = DEFAULT_VALUES.limit,
}) => {
try {
const response = await client.search.execute({
q: informationToGet,
containerTags,
limit,
chunkThreshold: DEFAULT_VALUES.chunkThreshold,
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",
}
}
},
})
}
export const addMemoryTool = (
apiKey: string,
config?: SupermemoryToolsConfig,
) => {
const client = new Supermemory({
apiKey,
...(config?.baseUrl ? { baseURL: config.baseUrl } : {}),
})
const containerTags = getContainerTags(config)
return tool({
description: TOOL_DESCRIPTIONS.addMemory,
inputSchema: z.object({
memory: z.string().describe(PARAMETER_DESCRIPTIONS.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",
}
}
},
})
}
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.coerce
.number()
.default(DEFAULT_VALUES.limit)
.describe(PARAMETER_DESCRIPTIONS.limit)
: z.coerce
.number()
.optional()
.default(DEFAULT_VALUES.limit)
.describe(PARAMETER_DESCRIPTIONS.limit),
page: z.coerce.number().optional().describe(PARAMETER_DESCRIPTIONS.page),
}),
execute: async ({ containerTag, limit, page }) => {
try {
const tag = containerTag || containerTags[0]
const response = await client.documents.list({
containerTags: [tag],
limit: limit || DEFAULT_VALUES.limit,
...(page !== undefined && { page }),
})
return {
success: true,
documents: response.memories,
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(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 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 forgetMemoryRequest(
apiKey,
{
containerTag: tag as string,
...(memoryId && { id: memoryId }),
...(memoryContent && { content: memoryContent }),
...(reason && { reason }),
},
config?.baseUrl,
)
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
*/
export function supermemoryTools(
apiKey: string,
config?: SupermemoryToolsConfig,
) {
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),
}
}
// `./vercel` is not a published subpath, so this is the only way consumers reach the middleware types.
export {
withSupermemory,
type WithSupermemoryOptions,
type PromptTemplate,
type MemoryPromptData,
} from "./vercel"