Skip to content
Open
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
26 changes: 26 additions & 0 deletions apps/docs/ai-sdk/user-profiles.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,32 @@ const result = await generateText({
// Uses both profile (user's expertise) AND search (previous debugging sessions)
```

### Hybrid Search Mode

Use `searchMode: "hybrid"` to search both memories AND document chunks.

```typescript
const model = withSupermemory(openai("gpt-4"), "user-123", {
mode: "full",
searchMode: "hybrid", // Search memories + document chunks
searchLimit: 15 // Max results (default: 10)
})

const result = await generateText({
model,
messages: [{
role: "user",
content: "What's in my documents about quarterly goals?"
}]
})
// Searches both extracted memories AND raw document content
```

**Search Mode Options:**
- `"memories"` (default) - Search only memory entries
- `"hybrid"` - Search memories + document chunks
- `"documents"` - Search only document chunks

## Custom Prompt Templates

Customize how memories are formatted and injected into the system prompt using the `promptTemplate` option. This is useful for:
Expand Down
27 changes: 27 additions & 0 deletions packages/tools/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -184,6 +184,29 @@ const result = await generateText({
})
```

**Hybrid Search Mode (RAG)** - Search both memories AND document chunks:
```typescript
import { generateText } from "ai"
import { withSupermemory } from "@supermemory/tools/ai-sdk"
import { openai } from "@ai-sdk/openai"

const modelWithHybrid = withSupermemory(openai("gpt-4"), "user-123", {
mode: "full",
searchMode: "hybrid", // Search memories + document chunks
searchLimit: 15 // Max results (default: 10)
})

const result = await generateText({
model: modelWithHybrid,
messages: [{ role: "user", content: "What's in my documents about quarterly goals?" }],
})
```

Search mode options:
- `"memories"` (default) - Search only memory entries
- `"hybrid"` - Search memories + document chunks (recommended for RAG)
- `"documents"` - Search only document chunks

#### Automatic Memory Capture

The middleware can automatically save user messages as memories:
Expand Down Expand Up @@ -652,6 +675,8 @@ interface WithSupermemoryOptions {
conversationId?: string
verbose?: boolean
mode?: "profile" | "query" | "full"
searchMode?: "memories" | "hybrid" | "documents"
searchLimit?: number
addMemory?: "always" | "never"
/** Optional Supermemory API key. Use this in browser environments. */
apiKey?: string
Expand All @@ -661,6 +686,8 @@ interface WithSupermemoryOptions {
- **conversationId**: Optional conversation ID to group messages into a single document for contextual memory generation
- **verbose**: Enable detailed logging of memory search and injection process (default: false)
- **mode**: Memory search mode - "profile" (default), "query", or "full"
- **searchMode**: Search mode - "memories" (default), "hybrid", or "documents". Use "hybrid" for RAG applications
- **searchLimit**: Maximum number of search results when using hybrid/documents mode (default: 10)
- **addMemory**: Automatic memory storage mode - "always" or "never" (default: "never")

## Available Tools
Expand Down
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": "1.4.00",
"version": "1.5.0",
"description": "Memory tools for AI SDK and OpenAI function calling with supermemory",
"scripts": {
"build": "tsdown",
Expand Down
225 changes: 225 additions & 0 deletions packages/tools/src/ai-sdk.ts
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,227 @@ 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 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
*/
Expand All @@ -128,6 +349,10 @@ export function supermemoryTools(
return {
searchMemories: searchMemoriesTool(apiKey, config),
addMemory: addMemoryTool(apiKey, config),
getProfile: getProfileTool(apiKey, config),
documentList: documentListTool(apiKey, config),
documentAdd: documentAddTool(apiKey, config),
memoryForget: memoryForgetTool(apiKey, config),
}
}

Expand Down
4 changes: 4 additions & 0 deletions packages/tools/src/shared/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ export type {
MemoryPromptData,
PromptTemplate,
MemoryMode,
SearchMode,
AddMemoryMode,
Logger,
ProfileStructure,
Expand Down Expand Up @@ -34,9 +35,12 @@ export {
// Memory client
export {
supermemoryProfileSearch,
supermemoryHybridSearch,
buildMemoriesText,
extractQueryText,
getLastUserMessageText,
type BuildMemoriesTextOptions,
type GenericMessage,
type SearchResultItem,
type SearchResponse,
} from "./memory-client"
Loading
Loading