From 9263c1560eb8ca85059685ba441039b118415b70 Mon Sep 17 00:00:00 2001 From: ved015 <122012786+ved015@users.noreply.github.com> Date: Sun, 23 Aug 2026 20:07:39 +0530 Subject: [PATCH] fix(docs): align Supermemory skill with current SDKs --- skills/supermemory/SKILL.md | 71 +++++--- skills/supermemory/references/sdk-guide.md | 199 ++++++++++++--------- 2 files changed, 159 insertions(+), 111 deletions(-) diff --git a/skills/supermemory/SKILL.md b/skills/supermemory/SKILL.md index 9b097732..b92b365c 100644 --- a/skills/supermemory/SKILL.md +++ b/skills/supermemory/SKILL.md @@ -38,7 +38,7 @@ Provides semantic search with: ## Quick Integration Examples -### TypeScript (Vercel AI SDK) +### TypeScript ```typescript import { Supermemory } from 'supermemory'; @@ -49,14 +49,19 @@ const client = new Supermemory({ // 1. Retrieve personalized context const context = await client.profile({ containerTag: "user_123", - query: "What are my preferences?" + q: "What are my preferences?" }); // 2. Enrich your prompt with context -const systemMessage = `User Profile: ${context.profile} -Relevant Memories: ${context.memories.join('\n')}`; +const profileText = [ + ...context.profile.static, + ...context.profile.dynamic, +].join('\n'); +const relevantMemories = JSON.stringify(context.searchResults?.results ?? []); +const systemMessage = `User Profile:\n${profileText}\n\nRelevant Memories:\n${relevantMemories}`; // 3. Store new memories after conversation +const conversationText = "User: I prefer dark mode.\nAssistant: I'll remember that."; await client.add({ content: conversationText, containerTag: "user_123", @@ -66,6 +71,8 @@ await client.add({ ### Python ```python +import os + from supermemory import Supermemory client = Supermemory(api_key=os.environ["SUPERMEMORY_API_KEY"]) @@ -73,10 +80,20 @@ client = Supermemory(api_key=os.environ["SUPERMEMORY_API_KEY"]) # Retrieve context context = client.profile( container_tag="user_123", - query="What are my preferences?" + q="What are my preferences?" +) + +profile_text = "\n".join( + (context.profile.static or []) + (context.profile.dynamic or []) +) +relevant_memories = "\n".join( + result.memory + for result in (context.search_results.results if context.search_results else []) + if result.memory ) # Add memories +conversation_text = "User: I prefer dark mode.\nAssistant: I'll remember that." client.add( content=conversation_text, container_tag="user_123", @@ -101,14 +118,14 @@ Supermemory builds a **living knowledge graph** rather than static document stor 3. **Graph Construction**: Builds relationships between memories (updates, extends, derives) 4. **Semantic Retrieval**: Returns contextually relevant information, not just keyword matches -Processing times: PDFs take 1-2 minutes, videos 5-10 minutes for 100 pages. - ## Getting Started 1. **Get API Key**: Sign up at [console.supermemory.ai](https://console.supermemory.ai) 2. **Install SDK**: Supermemory works with the following SDKs natively: - **TypeScript/JavaScript**: `npm install supermemory` ([npm](https://www.npmjs.com/package/supermemory)) - **Python**: `pip install supermemory` ([PyPI](https://pypi.org/project/supermemory/)) + - **TypeScript agent tools/middleware**: `npm install @supermemory/tools` + - **Python OpenAI tools/middleware**: `pip install supermemory-openai-sdk` Discover all available SDKs and community integrations at [supermemory.ai/docs](https://supermemory.ai/docs) 3. **Set Environment Variable**: `export SUPERMEMORY_API_KEY="your_key"` @@ -123,19 +140,19 @@ Supermemory supports two complementary integration styles: | Path | When to use | Packages | |------|-------------|----------| -| **Tools** | Model explicitly decides when to search, add, list, or forget | `@supermemory/tools` (TypeScript), `supermemory-openai-sdk` (Python) | -| **Middleware** | Auto-inject profile context before each request and save conversations after | `@supermemory/tools` `withSupermemory`, `supermemory-openai-sdk` `with_supermemory` | +| **Tools** | Model explicitly decides when to search, add, list, or forget | `@supermemory/tools/ai-sdk` or `@supermemory/ai-sdk` (TypeScript), `supermemory-openai-sdk` (Python) | +| **Middleware** | Auto-inject profile context before each request and save conversations after | `@supermemory/tools/ai-sdk` (Vercel AI SDK), `@supermemory/tools/openai` (OpenAI), `supermemory-openai-sdk` (Python) | **Tools (7 canonical operations):** | Tool | Use when | |------|----------| -| `searchMemories` / `search_memories` | Proactive hybrid recall before answering when user-specific context could help — not only when explicitly asked. Best for targeted lookups; returns memory IDs for `memoryForget`. | +| `searchMemories` / `search_memories` | Proactive hybrid recall before answering when user-specific context could help — not only when explicitly asked. Hybrid returns both memory entries and source-document chunks. | | `addMemory` / `add_memory` | Store a single generalizable fact the user stated | | `getProfile` / `get_profile` | Load static + dynamic profile; pass `query` to scope search results to the current topic | | `documentList` / `document_list` | Browse stored **source documents** (conversations, URLs, files); returns **document IDs** | | `documentAdd` / `document_add` | Ingest raw content (text blob, conversation transcript, URL, notes) for **background processing** — memories are extracted automatically; use for substantial content, not single facts (`addMemory`) | -| `documentDelete` / `document_delete` | **Hard delete** a document + all memories extracted from it (permanent) | +| `documentDelete` / `document_delete` | Permanently delete a source document and soft-forget memories extracted from it | | `memoryForget` / `memory_forget` | **Soft delete** one learned profile fact by memory ID or exact content match | ### Removing information — three different mechanisms @@ -144,18 +161,20 @@ Agents must pick the right removal path: | User intent | Tool | What it removes | ID source | |-------------|------|-----------------|-----------| -| "Forget that I like tea" / correct a wrong fact | `memoryForget` | One extracted profile memory (soft delete) | `memoryId` from `searchMemories` or `getProfile` | -| "Delete that conversation" / remove a whole file or URL | `documentDelete` | Entire source document + all extracted memories (permanent) | `documentId` from `documentList` | +| "Forget that I like tea" / correct a wrong fact | `memoryForget` | One extracted profile memory (soft delete) | Memory ID from a search result that contains `memory`, or query-backed `getProfile.searchResults` / `get_profile.search_results` | +| "Delete that conversation" / remove a whole file or URL | `documentDelete` | Source document permanently; extracted memories are soft-forgotten | `documentId` from `documentList` | | User is vague ("forget what you know about my job") | `searchMemories` first → then `memoryForget` | Same as memoryForget | Search first, then use `memoryId` | -**Do not confuse IDs:** `memoryId` ≠ `documentId`. Profile memory IDs come from search/profile; document IDs come from document list. +**Do not confuse IDs:** `memoryId` ≠ `documentId`. In hybrid results, only an item containing `memory` has a forgettable memory ID; an item containing `chunk` has a chunk ID. Static/dynamic profile entries are plain text, so use query-backed profile search results when you need an ID. -**Soft vs hard delete:** `memoryForget` hides a fact from profile/search but leaves source documents. `documentDelete` permanently removes the underlying stored content. +**Soft vs hard delete:** `memoryForget` hides a fact from profile/search but leaves source documents. `documentDelete` permanently removes the underlying source and soft-forgets its extracted memories. **When to use profile vs search vs documents:** - **`profile()` / `getProfile`**: Broad user context (static facts + recent dynamic memories). Use before responses when you want a holistic view of the user. -- **`search()` / `searchMemories`**: Targeted recall — use proactively before answering when memory could improve the response, not only when the user says "search" or "what do you remember". Hybrid mode combines semantic + keyword search. -- **`documents.*`**: Raw content management — list, add, or delete source documents before memory extraction runs. +- **`search()` / `searchMemories`**: Targeted recall — use proactively before answering when memory could improve the response, not only when the user says "search" or "what do you remember". Hybrid mode returns both extracted memories and source-document chunks. +- **`documents.*`**: Source management — list, add, or delete documents throughout their lifecycle. + +With multiple configured container tags, `searchMemories`, `getProfile`, and `memoryForget` use the first tag because v4 memory operations are single-space. Add, list, and delete operations use the broader configured scope where supported. **TypeScript (Vercel AI SDK):** ```typescript @@ -167,17 +186,26 @@ const tools = supermemoryTools(process.env.SUPERMEMORY_API_KEY!, { }) ``` +The aggregate includes destructive tools. Select only the operations the agent needs, and expose `documentDelete` or `memoryForget` only when the agent is authorized to remove data. + **Python (OpenAI function calling):** ```python +import os + from supermemory_openai import SupermemoryTools -tools = SupermemoryTools(api_key, {"container_tags": ["user_123"]}) +tools = SupermemoryTools( + os.environ["SUPERMEMORY_API_KEY"], + {"container_tags": ["user_123"]}, +) definitions = tools.get_tool_definitions() # all 7 tools ``` +Filter `definitions` before passing them to a model if it should not be able to call `document_delete` or `memory_forget`. + **For Chatbots**: Use middleware (`withSupermemory` / `with_supermemory`) for automatic context injection, or pass tools to the model for explicit memory control -**For Knowledge Bases (RAG)**: Use `add()` / `documentAdd` for ingestion, then `searchMemories` with hybrid mode for retrieval +**For Knowledge Bases (RAG)**: Use `add()` / `documentAdd` for text or URL ingestion, the SDK file-upload method for local files, then `searchMemories` with hybrid mode for retrieval **For Task Assistants**: Combine `getProfile` with `searchMemories` for context-aware task completion @@ -195,9 +223,8 @@ definitions = tools.get_tool_definitions() # all 7 tools 1. **Container Tags**: Use consistent user/project IDs as containerTags for proper isolation 2. **Metadata**: Add custom metadata for advanced filtering (source, type, timestamp) -3. **Thresholds**: Start with `threshold: 0.3` for balanced precision/recall -4. **Static Memories**: Mark permanent facts as `isStatic: true` for better performance -5. **Batch Operations**: Use bulk endpoints for multiple documents +3. **Thresholds**: The v4 search default is `0.6`; tune it only after checking retrieval quality +4. **Batch Operations**: Use bulk endpoints for multiple documents ## Integration Ecosystem diff --git a/skills/supermemory/references/sdk-guide.md b/skills/supermemory/references/sdk-guide.md index a4376410..0f511fc3 100644 --- a/skills/supermemory/references/sdk-guide.md +++ b/skills/supermemory/references/sdk-guide.md @@ -13,6 +13,9 @@ npm install supermemory yarn add supermemory # or pnpm add supermemory + +# Agent tools and Vercel AI SDK middleware +npm install @supermemory/tools ``` 📦 View on npm: [https://www.npmjs.com/package/supermemory](https://www.npmjs.com/package/supermemory) @@ -22,6 +25,9 @@ pnpm add supermemory pip install supermemory # Or for async support with aiohttp pip install 'supermemory[aiohttp]' + +# OpenAI function tools and middleware +pip install supermemory-openai-sdk ``` 📦 View on PyPI: [https://pypi.org/project/supermemory/](https://pypi.org/project/supermemory/) @@ -44,6 +50,8 @@ const client = new Supermemory({ ### Python ```python +import os + from supermemory import Supermemory # Synchronous client @@ -69,7 +77,7 @@ Add content to Supermemory for processing and memory extraction. #### TypeScript ```typescript await client.add({ - content: string | URL, // Required: text, URL, or file path + content: string, // Required: plaintext or a URL string containerTag?: string, // Optional: isolation identifier entityContext?: string, // Optional: context for memory extraction customId?: string, // Optional: your custom identifier @@ -80,7 +88,7 @@ await client.add({ #### Python ```python client.add( - content=str | url, # Required: text, URL, or file path + content=str, # Required: plaintext or a URL string container_tag=str, # Optional: isolation identifier entity_context=str, # Optional: context for memory extraction custom_id=str, # Optional: your custom identifier @@ -88,6 +96,8 @@ client.add( ) ``` +`add()` does not read a local file path. Upload local files with `client.documents.uploadFile({ file })` in TypeScript or `client.documents.upload_file(file=...)` in Python; `filepath` is metadata, not a file upload. + #### Examples **Add text content:** @@ -137,7 +147,7 @@ const response = await client.profile({ // Returns: // { // profile: { -// static: string[], // Array of static memories (permanent facts) +// static: string[], // Array of long-lived profile facts // dynamic: string[] // Array of dynamic memories (recent context) // }, // searchResults?: { // Only included if q parameter was provided @@ -161,18 +171,9 @@ response = client.profile( threshold=float # Optional: relevance threshold (0-1, default 0.5) ) -# Returns dict: -# { -# "profile": { -# "static": List[str], # Array of static memories (permanent facts) -# "dynamic": List[str] # Array of dynamic memories (recent context) -# }, -# "searchResults": { # Only included if q parameter was provided -# "results": List[dict], # Search results -# "total": int, -# "timing": int -# } -# } +# Returns a ProfileResponse model: +# response.profile.static / response.profile.dynamic +# response.search_results.results # only when q was provided ``` #### Examples @@ -203,15 +204,15 @@ console.log(response.profile.dynamic); // Recent dynamic memories ### `search()` - Semantic Search -Search across memories using semantic understanding, not just keywords. `client.search.memories()` and `client.search.documents()` still work (deprecated) but `client.search()` is the current, recommended call — Python keeps `client.search.memories()`. +Search across memories using semantic retrieval. `client.search()` is the current TypeScript v4 call. Python uses `client.search.memories()` for the same v4 endpoint; the TypeScript `client.search.documents()` method is the legacy v3 document response. #### TypeScript ```typescript const response = await client.search({ q: string, // Required: search query containerTag?: string, // Optional: filter by container tag - limit?: number, // Optional: max results (default 10) - threshold?: number, // Optional: similarity threshold (0-1, default 0.5) + limit?: number, // Optional: max results (default 10, max 100) + threshold?: number, // Optional: similarity threshold (0-1, default 0.6) searchMode?: "memories" | "hybrid" | "documents", // Optional: "memories" (default), "hybrid" (memories + document chunks), or "documents" (chunks only) filters?: FilterObject // Optional: advanced filtering }); @@ -237,18 +238,14 @@ const response = await client.search({ response = client.search.memories( q=str, # Required: search query container_tag=str, # Optional: filter by container tag - threshold=float, # Optional: similarity threshold (0-1, default 0.5) - limit=int, # Optional: max results (default 50) + threshold=float, # Optional: similarity threshold (0-1, default 0.6) + limit=int, # Optional: max results (default 10, max 100) search_mode=str, # Optional: "memories" (default), "hybrid", or "documents" filters=dict # Optional: advanced filtering ) -# Returns dict: -# { -# "results": List[dict], # Array of search results -# "total": int, -# "timing": int # Search time in milliseconds -# } +# Returns a SearchMemoriesResponse model: +# response.results, response.total, response.timing ``` #### Examples @@ -262,17 +259,17 @@ const response = await client.search({ }); response.results.forEach(result => { - console.log(`Score: ${result.score}`); - console.log(`Content: ${result.content}`); + console.log(`Similarity: ${result.similarity}`); + console.log(`Content: ${result.memory ?? result.chunk}`); }); ``` -**Hybrid search for RAG (semantic + keyword):** +**Hybrid search for RAG (memories + source chunks):** ```typescript const response = await client.search({ q: "authentication methods", containerTag: "docs", - searchMode: "hybrid", // Combines semantic and keyword search for better RAG accuracy + searchMode: "hybrid", // Returns both extracted memories and document chunks threshold: 0.3, limit: 10 }); @@ -285,19 +282,20 @@ const response = await client.search({ containerTag: "docs", threshold: 0.3, filters: { - metadata: { - type: "tutorial", - category: "security" - } + AND: [ + { key: "type", value: "tutorial" }, + { key: "category", value: "security" } + ] } }); ``` -**Search within specific document:** +**Search within a filepath:** ```typescript const response = await client.search({ q: "rate limiting configuration", - containerTag: "specific_project" + containerTag: "specific_project", + filepath: "/docs/api.md" }); ``` @@ -308,32 +306,34 @@ Retrieve stored documents with optional filtering and pagination. #### TypeScript ```typescript const docs = await client.documents.list({ - containerTag?: string, // Optional: filter by container - limit?: number, // Optional: number of results (default 20) - offset?: number, // Optional: pagination offset - status?: string // Optional: filter by processing status + containerTags?: string[], // Optional: filter by one or more containers + limit?: number, // Optional: items per page (default 10) + page?: number, // Optional: 1-based page number (default 1) + includeContent?: boolean, // Optional: include source content (default false) + sort?: "createdAt" | "updatedAt", + order?: "asc" | "desc" }); // Returns: // { -// documents: Array<{ +// memories: Array<{ // id: string, -// content: string, // status: string, // metadata: object, -// createdAt: string +// createdAt: string, +// content?: string // only when includeContent=true // }>, -// total: number +// pagination: { currentPage, totalItems, totalPages, limit? } // } ``` #### Python ```python docs = client.documents.list( - container_tag=str, # Optional: filter by container - limit=int, # Optional: number of results (default 20) - offset=int, # Optional: pagination offset - status=str # Optional: filter by processing status + container_tags=[str], # Optional: filter by one or more containers + limit=int, # Optional: items per page (default 10) + page=int, # Optional: 1-based page number (default 1) + include_content=bool # Optional: include source content (default False) ) ``` @@ -342,53 +342,39 @@ docs = client.documents.list( **List all documents for a user:** ```typescript const docs = await client.documents.list({ - containerTag: "user_123", + containerTags: ["user_123"], limit: 50 }); -docs.documents.forEach(doc => { +docs.memories.forEach(doc => { console.log(`${doc.id}: ${doc.status}`); }); ``` **Paginated listing:** ```typescript -const page1 = await client.documents.list({ limit: 20, offset: 0 }); -const page2 = await client.documents.list({ limit: 20, offset: 20 }); -``` - -**Filter by status:** -```typescript -const processing = await client.documents.list({ - containerTag: "project_abc", - status: "processing" -}); +const page1 = await client.documents.list({ limit: 20, page: 1 }); +const page2 = await client.documents.list({ limit: 20, page: 2 }); ``` ### `documents.delete()` - Delete Document -Remove a document and its associated memories. +Permanently remove a source document. Memories extracted from that source are soft-forgotten so they no longer appear in profile or search. #### TypeScript ```typescript -await client.documents.delete({ - docId: string // Required: document ID -}); +await client.documents.delete(documentId); ``` #### Python ```python -client.documents.delete( - doc_id=str # Required: document ID -) +client.documents.delete(document_id) ``` #### Example ```typescript -await client.documents.delete({ - docId: "doc_abc123" -}); +await client.documents.delete("doc_abc123"); ``` ## Advanced Features @@ -414,11 +400,11 @@ const results = await client.search({ q: "phone reviews", containerTag: "reviews", filters: { - metadata: { - rating: { $gte: 4.0 }, // Rating >= 4.0 - verified: true, - tags: { $contains: "apple" } - } + AND: [ + { key: "rating", value: "4.0", filterType: "numeric", numericOperator: ">=" }, + { key: "verified", value: "true" }, + { key: "tags", value: "apple", filterType: "array_contains" } + ] } }); ``` @@ -472,33 +458,57 @@ await client.add({ ### Vercel AI SDK -#### Agent tools (`@supermemory/tools` / `@supermemory/ai-sdk`) +#### Agent tools (`@supermemory/tools/ai-sdk` / `@supermemory/ai-sdk`) For models that call memory operations explicitly, use the 7-tool set instead of hand-rolling SDK calls: ```typescript -import { generateText } from "ai" +import { generateText, stepCountIs } from "ai" import { openai } from "@ai-sdk/openai" import { supermemoryTools } from "@supermemory/tools/ai-sdk" -const tools = supermemoryTools(process.env.SUPERMEMORY_API_KEY!, { +const allTools = supermemoryTools(process.env.SUPERMEMORY_API_KEY!, { containerTags: ["user_123"], }) +// Select the operations this agent is allowed to call. +const tools = { + searchMemories: allTools.searchMemories, + addMemory: allTools.addMemory, + getProfile: allTools.getProfile, + documentList: allTools.documentList, + documentAdd: allTools.documentAdd, +} + const { text } = await generateText({ model: openai("gpt-4o"), tools, + stopWhen: stepCountIs(5), prompt: "What do you remember about my coffee preferences?", }) ``` Tools: `searchMemories`, `addMemory`, `getProfile`, `documentList`, `documentAdd`, `documentDelete`, `memoryForget`. -Use `searchMemories` for targeted hybrid recall; `getProfile` for broad static/dynamic user context; `documents.*` for raw content management. +Use `searchMemories` for targeted hybrid recall; `getProfile` for broad static/dynamic user context; `documentList`, `documentAdd`, and `documentDelete` for source management. Hybrid search returns both extracted memories and source-document chunks. + +If you configure multiple container tags, `searchMemories`, `getProfile`, and `memoryForget` use the first tag because v4 memory operations are single-space. Add, list, and delete operations use the broader configured scope where supported. + +`supermemoryTools()` includes destructive operations. Expose `documentDelete` and `memoryForget` only when the agent is authorized to remove data, and require user confirmation when appropriate. `stopWhen` allows the model to consume tool results and produce a final answer instead of stopping immediately after the first tool call. #### Middleware (`withSupermemory`) -For automatic profile injection and conversation saving without tool calls, use `withSupermemory` from `@supermemory/tools` (see package docs). +For automatic profile injection and conversation saving without tool calls, import `withSupermemory` from `@supermemory/tools/ai-sdk`: + +```typescript +import { withSupermemory } from "@supermemory/tools/ai-sdk" +import { openai } from "@ai-sdk/openai" + +const modelWithMemory = withSupermemory(openai("gpt-4o"), { + containerTag: "user_123", + customId: "conversation_456", +}) +``` #### Manual SDK integration @@ -515,11 +525,16 @@ async function chat(userId: string, message: string) { containerTag: userId, q: message }); + const profileText = [ + ...context.profile.static, + ...context.profile.dynamic, + ].join('\n'); + const searchText = JSON.stringify(context.searchResults?.results ?? []); // 2. Generate response with context const { text } = await generateText({ model: openai('gpt-4'), - system: `User Profile: ${context.profile}\n\nRelevant Context:\n${context.memories.map(m => m.content).join('\n')}`, + system: `User Profile:\n${profileText}\n\nRelevant Context:\n${searchText}`, prompt: message }); @@ -580,13 +595,22 @@ def create_memory_enhanced_agent(user_id: str): # Get user context context = memory.profile( container_tag=user_id, - query="user preferences and history" + q="user preferences and history" + ) + + profile_text = "\n".join( + (context.profile.static or []) + (context.profile.dynamic or []) + ) + search_text = "\n".join( + result.memory + for result in (context.search_results.results if context.search_results else []) + if result.memory ) agent = Agent( role="Personal Assistant", goal="Help the user with personalized assistance", - backstory=f"User Context: {context['profile']}\n\nRecent interactions:\n{context['memories']}", + backstory=f"User Context:\n{profile_text}\n\nRelevant memories:\n{search_text}", verbose=True ) @@ -632,9 +656,9 @@ await client.add({ ``` ### 4. Appropriate Thresholds -Start with default (0.5) and adjust based on results: +Start with the v4 search default (`0.6`) and adjust based on results: - **0.3-0.5**: Broader recall, good for discovery -- **0.5-0.7**: Balanced precision and recall +- **0.5-0.7**: Balanced precision and recall; `0.6` is the default - **0.7-1.0**: High precision, fewer but more relevant results ### 5. Error Handling @@ -660,7 +684,6 @@ try { - `entityContext` - `customId` - `threshold` -- `docId` - `q` ### Python (snake_case) @@ -668,15 +691,13 @@ try { - `entity_context` - `custom_id` - `threshold` -- `doc_id` ## Performance Tips 1. **Batch Operations**: Add multiple documents in quick succession if needed 2. **Async/Await**: Always use async operations to avoid blocking -3. **Pagination**: Use `limit` and `offset` for large result sets +3. **Pagination**: Use `limit` and 1-based `page` for large document lists 4. **Caching**: Cache profile() results for short periods if making multiple calls -5. **Processing Time**: Allow 1-2 minutes for PDFs, 5-10 minutes for videos ## Support