Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion bun.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion packages/tools/package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "@supermemory/tools",
"type": "module",
"version": "2.1.1",
"version": "2.2.0",
"description": "Memory tools for AI SDK, OpenAI, Voltagent and Mastra with supermemory",
"scripts": {
"build": "tsdown",
Expand Down
8 changes: 7 additions & 1 deletion packages/tools/src/ai-sdk.ts
Original file line number Diff line number Diff line change
Expand Up @@ -372,4 +372,10 @@ export function supermemoryTools(
}
}

export { withSupermemory } from "./vercel"
// `./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"
8 changes: 4 additions & 4 deletions packages/tools/src/openai/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import type OpenAI from "openai"
import { validateApiKey } from "../shared"
import {
createOpenAIMiddleware,
type OpenAIMiddlewareOptions,
Expand All @@ -21,6 +22,7 @@ import {
* @param options.verbose - Optional flag to enable detailed logging of memory search and injection process (default: false)
* @param options.mode - Optional mode for memory search: "profile" (default), "query", or "full"
* @param options.addMemory - Optional mode for memory addition: "always" (default), "never"
* @param options.apiKey - Optional Supermemory API key to use instead of the SUPERMEMORY_API_KEY environment variable
*
* @returns An OpenAI client with SuperMemory middleware injected for both Chat Completions and Responses APIs
*
Expand Down Expand Up @@ -56,16 +58,14 @@ import {
* })
* ```
*
* @throws {Error} When SUPERMEMORY_API_KEY environment variable is not set
* @throws {Error} When neither `options.apiKey` nor `process.env.SUPERMEMORY_API_KEY` are set
* @throws {Error} When supermemory API request fails
*/
export function withSupermemory(
openaiClient: OpenAI,
options: OpenAIMiddlewareOptions,
) {
if (!process.env.SUPERMEMORY_API_KEY) {
throw new Error("SUPERMEMORY_API_KEY is not set")
}
validateApiKey(options.apiKey)

if (!options.containerTag) {
throw new Error(
Expand Down
30 changes: 22 additions & 8 deletions packages/tools/src/openai/middleware.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import type OpenAI from "openai"
import Supermemory from "supermemory"
import { addConversation } from "../conversations-client"
import { validateApiKey } from "../shared"
import { deduplicateMemoriesForMode } from "../tools-shared"
import { createLogger, type Logger } from "../vercel/logger"
import { convertProfileToMarkdown } from "../vercel/util"
Expand All @@ -20,6 +21,7 @@ export interface OpenAIMiddlewareOptions {
mode?: "profile" | "query" | "full"
addMemory?: "always" | "never"
baseUrl?: string
apiKey?: string
}

interface SupermemoryProfileSearch {
Expand Down Expand Up @@ -75,22 +77,25 @@ const getLastUserMessage = (
*
* @param containerTag - The container tag/identifier for memory search (e.g., user ID, project ID)
* @param queryText - Optional query text to search for specific memories. If empty, returns all profile memories
* @param baseUrl - The Supermemory API base URL
* @param apiKey - The Supermemory API key used to authenticate the request
* @returns Promise that resolves to the SuperMemory profile search response
* @throws {Error} When the API request fails or returns an error status
*
* @example
* ```typescript
* // Search with query
* const results = await supermemoryProfileSearch("user-123", "favorite programming language")
* const results = await supermemoryProfileSearch("user-123", "favorite programming language", baseUrl, apiKey)
*
* // Get all profile memories
* const profile = await supermemoryProfileSearch("user-123", "")
* const profile = await supermemoryProfileSearch("user-123", "", baseUrl, apiKey)
* ```
*/
const supermemoryProfileSearch = async (
containerTag: string,
queryText: string,
baseUrl: string,
apiKey: string,
): Promise<SupermemoryProfileSearch> => {
const payload = queryText
? JSON.stringify({
Expand All @@ -106,7 +111,7 @@ const supermemoryProfileSearch = async (
method: "POST",
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${process.env.SUPERMEMORY_API_KEY}`,
Authorization: `Bearer ${apiKey}`,
},
body: payload,
})
Expand Down Expand Up @@ -138,6 +143,8 @@ const supermemoryProfileSearch = async (
* @param containerTag - The container tag/identifier for memory search
* @param logger - Logger instance for debugging and info output
* @param mode - Memory search mode: "profile" (all memories), "query" (search-based), or "full" (both)
* @param baseUrl - The Supermemory API base URL
* @param apiKey - The Supermemory API key used to authenticate the request
* @returns Promise that resolves to enhanced messages with memory-injected system prompt
*
* @example
Expand All @@ -150,7 +157,9 @@ const supermemoryProfileSearch = async (
* messages,
* "user-123",
* logger,
* "full"
* "full",
* baseUrl,
* apiKey
* )
* // Returns messages with system prompt containing relevant memories
* ```
Expand All @@ -161,6 +170,7 @@ const addSystemPrompt = async (
logger: Logger,
mode: "profile" | "query" | "full",
baseUrl: string,
apiKey: string,
) => {
const systemPromptExists = messages.some((msg) => msg.role === "system")

Expand All @@ -170,6 +180,7 @@ const addSystemPrompt = async (
containerTag,
queryText,
baseUrl,
apiKey,
)

const memoryCountStatic = memoriesResponse.profile.static?.length || 0
Expand Down Expand Up @@ -400,8 +411,9 @@ const addMemoryTool = async (
* @param options.verbose - Enable detailed logging of memory operations (default: false)
* @param options.mode - Memory search mode: "profile" (all memories), "query" (search-based), or "full" (both) (default: "profile")
* @param options.addMemory - Automatic memory storage mode: "always" or "never" (default: "always")
* @param options.apiKey - Supermemory API key to use instead of the SUPERMEMORY_API_KEY environment variable
* @returns Object with `wrapClient` and `createClient` methods
* @throws {Error} When SUPERMEMORY_API_KEY environment variable is not set
* @throws {Error} When neither `options.apiKey` nor `process.env.SUPERMEMORY_API_KEY` are set
*
* @example
* ```typescript
Expand All @@ -421,8 +433,9 @@ export function createOpenAIMiddleware(
) {
const logger = createLogger(options?.verbose ?? false)
const baseUrl = normalizeBaseUrl(options?.baseUrl)
const apiKey = validateApiKey(options?.apiKey)
const client = new Supermemory({
apiKey: process.env.SUPERMEMORY_API_KEY,
apiKey,
...(baseUrl !== "https://api.supermemory.ai" ? { baseURL: baseUrl } : {}),
})

Expand Down Expand Up @@ -457,6 +470,7 @@ export function createOpenAIMiddleware(
containerTag,
queryText,
baseUrl,
apiKey,
)

const memoryCountStatic = memoriesResponse.profile.static?.length || 0
Expand Down Expand Up @@ -615,15 +629,15 @@ export function createOpenAIMiddleware(
memoryCustomId,
logger,
messages,
process.env.SUPERMEMORY_API_KEY,
apiKey,
baseUrl,
),
)
}
}

operations.push(
addSystemPrompt(messages, containerTag, logger, mode, baseUrl),
addSystemPrompt(messages, containerTag, logger, mode, baseUrl, apiKey),
)

const results = await Promise.all(operations)
Expand Down
17 changes: 16 additions & 1 deletion packages/tools/src/openai/tools.ts
Original file line number Diff line number Diff line change
Expand Up @@ -552,6 +552,14 @@ export function getToolDefinitions(): OpenAI.Chat.Completions.ChatCompletionTool
]
}

function parseToolArguments(argumentsJson: string) {
try {
return { success: true as const, value: JSON.parse(argumentsJson) }
} catch {
return { success: false as const }
}
}

/**
* Execute a tool call based on the function name and arguments
*/
Expand All @@ -565,7 +573,14 @@ export function createToolCallExecutor(
toolCall: OpenAI.Chat.Completions.ChatCompletionMessageToolCall,
): Promise<string> {
const functionName = toolCall.function.name
const args = JSON.parse(toolCall.function.arguments)
const parsed = parseToolArguments(toolCall.function.arguments)
if (!parsed.success) {
return JSON.stringify({
success: false,
error: `Invalid JSON arguments for ${functionName}`,
})
}
const args = parsed.value

switch (functionName) {
case "searchMemories":
Expand Down
6 changes: 1 addition & 5 deletions packages/tools/src/voltagent/hooks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -129,11 +129,7 @@ export function createSupermemoryHooks(
return
}

saveConversation(messages, ctx).catch((error) => {
ctx.logger.error("Background conversation save failed", {
error: error instanceof Error ? error.message : "Unknown error",
})
})
await saveConversation(messages, ctx)
} catch (error) {
ctx.logger.error("Error in onEnd", {
error: error instanceof Error ? error.message : "Unknown error",
Expand Down
2 changes: 1 addition & 1 deletion packages/tools/src/voltagent/middleware.ts
Original file line number Diff line number Diff line change
Expand Up @@ -448,7 +448,7 @@ const convertToConversationMessages = (
}

/**
* Saves conversation to Supermemory (fire-and-forget).
* Saves conversation to Supermemory.
*/
export const saveConversation = async (
messages: VoltAgentMessage[],
Expand Down
Loading