From 244f924c04ddea2427ae798c80f774d25206365a Mon Sep 17 00:00:00 2001 From: Sreeram Sreedhar Date: Tue, 21 Apr 2026 15:16:22 -0700 Subject: [PATCH] fixes and improvements --- apps/docs/integrations/convex.mdx | 199 +----- packages/tools/src/convex-component/README.md | 155 ++--- .../tools/src/convex-component/SHIPPING.md | 255 -------- .../tools/src/convex-component/USAGE_GUIDE.md | 587 ------------------ .../src/convex-component/src/client/index.ts | 57 +- .../convex-component/src/component/actions.ts | 53 +- .../convex-component/src/component/install.ts | 7 +- .../src/component/mutations.ts | 126 +--- .../convex-component/src/component/queries.ts | 65 +- .../convex-component/src/component/schema.ts | 43 +- .../src/convex-component/src/react/index.tsx | 381 +----------- 11 files changed, 147 insertions(+), 1781 deletions(-) delete mode 100644 packages/tools/src/convex-component/SHIPPING.md delete mode 100644 packages/tools/src/convex-component/USAGE_GUIDE.md diff --git a/apps/docs/integrations/convex.mdx b/apps/docs/integrations/convex.mdx index 556b2475..28ce7fcd 100644 --- a/apps/docs/integrations/convex.mdx +++ b/apps/docs/integrations/convex.mdx @@ -1,11 +1,11 @@ --- title: "Convex" sidebarTitle: "Convex" -description: "Add semantic memory and RAG to your Convex apps with reactive queries and smart caching" +description: "Add semantic memory and RAG to your Convex apps with reactive queries" icon: "database" --- -Supermemory integrates with [Convex](https://convex.dev) as a native component, giving you AI-powered semantic memory with reactive real-time queries, smart caching, and full dashboard visibility. +Supermemory integrates with [Convex](https://convex.dev) as a native component, giving you AI-powered semantic memory with reactive real-time queries and full dashboard visibility. Check out the NPM page for more details @@ -14,7 +14,7 @@ Supermemory integrates with [Convex](https://convex.dev) as a native component, ## Installation ```bash -npm install @supermemory/convex-component convex +npm install @supermemory/tools ``` ## Quick Start @@ -46,18 +46,18 @@ npx convex env set SUPERMEMORY_API_KEY your-api-key ```tsx - import { useAddMemory, useSupermemorySearch } from "@supermemory/convex-component/react"; + import { addMemory, searchMemories } from "@supermemory/convex-component/react"; function ChatApp() { - const addMemory = useAddMemory(); - const { results, isLoading, search } = useSupermemorySearch({ + const add = addMemory(); + const { results, isLoading, search } = searchMemories({ q: "user preferences", containerTag: "user_123", searchMode: "hybrid" }); const handleSendMessage = async (message: string) => { - await addMemory({ + await add({ content: message, containerTag: "user_123" }); @@ -112,14 +112,14 @@ npx convex env set SUPERMEMORY_API_KEY your-api-key ## React Hooks -### `useAddMemory()` +### `addMemory()` Add memories to Supermemory through a Convex action. ```tsx -const addMemory = useAddMemory(); +const add = addMemory(); -await addMemory({ +await add({ content: "Meeting notes from Q1 planning", containerTag: "user_123", customId: "meeting_2024_q1", @@ -127,12 +127,12 @@ await addMemory({ }); ``` -### `useSupermemorySearch(args)` +### `searchMemories(args)` Reactive semantic search with loading and error states. ```tsx -const { results, isLoading, error, search } = useSupermemorySearch({ +const { results, isLoading, error, search } = searchMemories({ q: "project updates", containerTag: "user_123", searchMode: "hybrid", @@ -143,12 +143,12 @@ const { results, isLoading, error, search } = useSupermemorySearch({ await search({ q: "new query", containerTag: "user_123" }); ``` -### `useSupermemoryProfile(args)` +### `getProfile(args)` Get user profile with static and dynamic facts. ```tsx -const { profile, isLoading, refresh } = useSupermemoryProfile({ +const { profile, isLoading, refresh } = getProfile({ containerTag: "user_123", q: "recent preferences" }); @@ -157,68 +157,18 @@ const { profile, isLoading, refresh } = useSupermemoryProfile({ await refresh(); ``` -### `useDocumentList(args?)` - -Reactively list all documents added to Supermemory. - -```tsx -const documents = useDocumentList({ containerTag: "user_123", limit: 20 }); -``` - -### `useMemories(args)` +### `listMemories(args)` List memories by container tag and source. ```tsx -const memories = useMemories({ +const memories = listMemories({ containerTag: "user_123", source: "manual", // "chat" | "document" | "manual" limit: 50 }); ``` -### `useApiStats(args?)` - -Get API call statistics for dashboard visibility. - -```tsx -const stats = useApiStats({ containerTag: "user_123" }); - -// stats.totalCalls, stats.successfulCalls, stats.averageResponseTime -``` - -### `useApiLogs(args?)` - -View recent API call logs for debugging. - -```tsx -const logs = useApiLogs({ endpoint: "search", limit: 50 }); -``` - -## Client SDK - -For non-React environments or server-side code: - -```typescript -import { ConvexHttpClient } from "convex/browser"; -import { createSupermemoryClient } from "@supermemory/convex-component"; - -const convex = new ConvexHttpClient(process.env.CONVEX_URL!); -const client = createSupermemoryClient(convex); -``` - -| Method | Description | -|--------|-------------| -| `client.add(args)` | Add content to Supermemory | -| `client.search(args)` | Semantic search across memories | -| `client.profile(args)` | Get user profile (static + dynamic facts) | -| `client.listDocuments(args?)` | List indexed documents | -| `client.getDocumentByCustomId(id)` | Get document by custom ID | -| `client.getApiLogs(args?)` | View API call logs | -| `client.getApiStats(args?)` | Get aggregated statistics | -| `client.searchCached(args)` | Local text search in Convex cache | -| `client.cleanCache()` | Remove expired cache entries | - ## AI SDK Integration Use Supermemory with Vercel AI SDK through the Convex backend. @@ -253,120 +203,3 @@ const result = await generateText({ | `promptTemplate` | function | — | Custom memory formatting function | | `verbose` | boolean | `false` | Enable debug logging | -### Tools (Agent-Based Memory) - -```typescript -import { streamText } from "ai"; -import { openai } from "@ai-sdk/openai"; -import { supermemoryConvexTools } from "@supermemory/convex-component/ai-sdk"; -import { ConvexHttpClient } from "convex/browser"; - -const convex = new ConvexHttpClient(process.env.CONVEX_URL!); - -const result = await streamText({ - model: openai("gpt-4"), - prompt: "Remember that I love TypeScript", - tools: supermemoryConvexTools(convex, "user_123") -}); -``` - -The tools include: -- **`searchMemories`** — Semantic search through user's memories and past conversations -- **`addMemory`** — Store new information about the user for future recall - -## Advanced Usage - -### Container Tags - -Use container tags to isolate memories by user, session, or project: - -```typescript -// Per user -await client.add({ content: "...", containerTag: "user_alice" }); -await client.add({ content: "...", containerTag: "user_bob" }); - -// Per project -await client.add({ content: "...", containerTag: "project_123" }); -``` - -### Metadata and Custom IDs - -```typescript -// Add with metadata -await client.add({ - content: "Design doc for feature X", - containerTag: "user_123", - customId: "design_doc_v2", - metadata: { type: "document", priority: "high" } -}); - -// Update existing content using the same customId -await client.add({ - content: "Updated design doc for feature X", - containerTag: "user_123", - customId: "design_doc_v2" // Same ID = update -}); -``` - -### Search with Filters - -```typescript -const results = await client.search({ - q: "design documents", - containerTag: "user_123", - searchMode: "hybrid", - threshold: 0.3, - rerank: true, - filters: { - AND: [ - { key: "type", value: "document" }, - { key: "priority", value: "high" } - ] - } -}); -``` - -## Smart Caching - -The component automatically caches API responses in Convex: - -| Cache | TTL | Purpose | -|-------|-----|---------| -| Search results | 5 minutes | Reduce redundant search API calls | -| User profiles | 2 minutes | Keep profiles fresh while limiting calls | - -Cached data is served reactively — all connected clients see updates in real-time. Clean expired entries manually: - -```typescript -const cleanCache = useCleanCache(); -await cleanCache(); -``` - -## Architecture - -``` -┌─────────────────────────────────────────────────────┐ -│ Your React / Next.js App │ -│ useSupermemorySearch, useAddMemory, useApiStats... │ -└──────────────────────┬──────────────────────────────┘ - │ - ▼ -┌─────────────────────────────────────────────────────┐ -│ Convex Backend │ -│ ┌──────────┐ ┌──────────┐ ┌──────────────────┐ │ -│ │ Queries │ │Mutations │ │ Actions │ │ -│ │(Reactive)│ │ (Cache) │ │ (Supermemory API) │ │ -│ └─────┬────┘ └────┬─────┘ └────────┬─────────┘ │ -│ └────────────┬┘ │ │ -│ ┌──────────────────▼──────────────────┘ │ -│ │ Convex Tables: searchCache, profileCache, │ -│ │ documents, apiLogs, memories, analytics │ -│ └──────────────────────────────────────────────────│ -└──────────────────────┬──────────────────────────────┘ - │ - ▼ -┌─────────────────────────────────────────────────────┐ -│ Supermemory API (supermemory.ai) │ -│ Semantic memory · User profiles · Hybrid search │ -└─────────────────────────────────────────────────────┘ -``` diff --git a/packages/tools/src/convex-component/README.md b/packages/tools/src/convex-component/README.md index b8c9e323..4008f433 100644 --- a/packages/tools/src/convex-component/README.md +++ b/packages/tools/src/convex-component/README.md @@ -16,8 +16,7 @@ Supermemory Convex Component integrates [Supermemory](https://supermemory.ai)'s - **User Profiles**: Automatic extraction of static and dynamic user facts - **Hybrid Search**: Combine memory extraction with document chunk search - **Reactive Queries**: Auto-updating UI components via Convex -- **Smart Caching**: Reduce API calls with intelligent Convex-based caching -- **Dashboard Visibility**: See all Supermemory API calls in your Convex dashboard +- **Dashboard Visibility**: See all memories, API calls, chat sessions, and analytics in your Convex dashboard - **TypeScript First**: Fully typed SDK and React hooks ## Installation @@ -55,11 +54,11 @@ npx convex env set SUPERMEMORY_API_KEY your-api-key #### React/Next.js with Hooks ```tsx -import { useAddMemory, useSupermemorySearch } from "@supermemory/convex-component/react"; +import { addMemory, searchMemories } from "@supermemory/convex-component/react"; function ChatApp() { - const addMemory = useAddMemory(); - const { results, isLoading, search } = useSupermemorySearch({ + const add = addMemory(); + const { results, isLoading, search } = searchMemories({ q: "user preferences", containerTag: "user_123", searchMode: "hybrid" @@ -67,7 +66,7 @@ function ChatApp() { const handleSendMessage = async (message: string) => { // Add to memory - await addMemory({ + await add({ content: `User: ${message}`, containerTag: "user_123" }); @@ -128,14 +127,14 @@ console.log("Dynamic context:", profile.profile.dynamic); ### React Hooks -#### `useAddMemory(componentPath?)` +#### `addMemory(componentPath?)` -Hook to add memories to Supermemory. +Add memories to Supermemory. ```tsx -const addMemory = useAddMemory(); +const add = addMemory(); -await addMemory({ +await add({ content: "Meeting notes from Q1 planning", containerTag: "user_123", customId: "meeting_2024_q1", @@ -143,12 +142,12 @@ await addMemory({ }); ``` -#### `useSupermemorySearch(args, componentPath?)` +#### `searchMemories(args, componentPath?)` -Hook for reactive semantic search. +Search Supermemory with reactive results. ```tsx -const { results, isLoading, error, search } = useSupermemorySearch({ +const { results, isLoading, error, search } = searchMemories({ q: "project updates", containerTag: "user_123", searchMode: "hybrid", @@ -159,12 +158,12 @@ const { results, isLoading, error, search } = useSupermemorySearch({ await search({ q: "new query", containerTag: "user_123" }); ``` -#### `useSupermemoryProfile(args, componentPath?)` +#### `getProfile(args, componentPath?)` -Hook to get user profile with context. +Get user profile with context. ```tsx -const { profile, isLoading, refresh } = useSupermemoryProfile({ +const { profile, isLoading, refresh } = getProfile({ containerTag: "user_123", q: "recent preferences" }); @@ -173,29 +172,23 @@ const { profile, isLoading, refresh } = useSupermemoryProfile({ await refresh(); ``` -#### `useDocumentList(args?, componentPath?)` +#### `listMemories(args, componentPath?)` -Hook to list documents reactively. +List memories reactively. Includes content and extracted memories. ```tsx -const documents = useDocumentList({ - containerTag: "user_123", - limit: 20 -}); -``` - -#### `useApiStats(args?, componentPath?)` - -Hook to get API statistics for dashboard. - -```tsx -const stats = useApiStats({ containerTag: "user_123" }); +const memories = listMemories({ containerTag: "user_123", limit: 20 }); return (
-

Total Calls: {stats?.totalCalls}

-

Success Rate: {((stats?.successfulCalls / stats?.totalCalls) * 100).toFixed(1)}%

-

Avg Response: {stats?.averageResponseTime.toFixed(0)}ms

+ {memories?.map(m => ( +
+

{m.content}

+ {m.extractedMemories?.map((em, i) => ( + {em} + ))} +
+ ))}
); ``` @@ -218,17 +211,8 @@ const results = await client.search({ q: "...", containerTag: "user_123" }); // Get profile const profile = await client.profile({ containerTag: "user_123" }); -// List documents -const docs = await client.listDocuments({ containerTag: "user_123" }); - -// Get API logs -const logs = await client.getApiLogs({ limit: 50 }); - -// Get stats -const stats = await client.getApiStats(); - -// Clean cache -await client.cleanCache(); +// List memories +const memories = await client.listMemories({ containerTag: "user_123" }); ``` ## Advanced Usage @@ -294,22 +278,13 @@ await addMemory({ }); ``` -### Cache Management - -The component automatically caches search results (5 min) and profiles (2 min). Clean manually: - -```typescript -const cleanCache = useCleanCache(); -await cleanCache(); // Removes all expired entries -``` - ## Architecture ``` ┌─────────────────────────────────────────────────────────────┐ │ Your Next.js/React App │ │ ┌──────────────────────────────────────────────────────┐ │ -│ │ useSupermemorySearch, useAddMemory, etc. │ │ +│ │ searchMemories, addMemory, listMemories │ │ │ └────────────────────┬─────────────────────────────────┘ │ └───────────────────────┼──────────────────────────────────────┘ │ @@ -318,18 +293,19 @@ await cleanCache(); // Removes all expired entries │ Convex Backend │ │ ┌──────────────┐ ┌──────────────┐ ┌──────────────────┐ │ │ │ Queries │ │ Mutations │ │ Actions │ │ -│ │ (Reactive) │ │ (Tx Cache) │ │ (Supermemory │ │ +│ │ (Reactive) │ │ (Storage) │ │ (Supermemory │ │ │ │ │ │ │ │ API Calls) │ │ │ └──────┬───────┘ └──────┬───────┘ └──────┬───────────┘ │ │ │ │ │ │ │ └─────────────────┼─────────────────┘ │ │ │ │ │ ┌────────────────────────▼──────────────────────────────┐ │ -│ │ Convex Tables (Smart Cache) │ │ -│ │ - searchCache: Search results with TTL │ │ -│ │ - profileCache: User profiles with TTL │ │ -│ │ - documents: Document metadata │ │ -│ │ - apiLogs: API call logs for dashboard │ │ +│ │ Convex Tables │ │ +│ │ - memories: Content + extracted memories │ │ +│ │ - chatSessions: Conversation history │ │ +│ │ - analytics: Usage tracking │ │ +│ │ - apiLogs: API call logs for dashboard │ │ +│ │ - config: Component configuration │ │ │ └───────────────────────────────────────────────────────┘ │ └───────────────────────┬──────────────────────────────────────┘ │ @@ -346,62 +322,15 @@ await cleanCache(); // Removes all expired entries ## Why This Architecture? 1. **Best of Both Worlds**: Supermemory's AI-powered memory + Convex's reactive sync -2. **Dashboard Visibility**: See all API calls, cache hits, errors in Convex dashboard -3. **Performance**: Smart caching reduces Supermemory API calls by ~80% -4. **Real-time**: Search results update reactively across all connected clients -5. **DX**: Install one package, 3 lines of config, start building +2. **Dashboard Visibility**: See all API calls, memories, chat sessions, and analytics in Convex dashboard +3. **Real-time**: Memories and search results update reactively across all connected clients +4. **DX**: Install one package, 3 lines of config, start building ## Examples -Check out the `/example` directory for a complete Next.js chat app with: -- Real-time semantic search -- User profile extraction -- Conversation memory -- API analytics dashboard - -## AI SDK Integration - -Use Supermemory with Vercel AI SDK through the Convex backend: - -### Middleware (Automatic Context Injection) - -```typescript -import { generateText } from "ai"; -import { openai } from "@ai-sdk/openai"; -import { withSupermemory } from "@supermemory/convex-component/ai-sdk"; -import { ConvexHttpClient } from "convex/browser"; - -const convex = new ConvexHttpClient(process.env.CONVEX_URL!); - -const modelWithMemory = withSupermemory( - openai("gpt-4"), - convex, - "user_123", - { mode: "full", addMemory: "always" } -); - -const result = await generateText({ - model: modelWithMemory, - messages: [{ role: "user", content: "What do you know about me?" }] -}); -``` - -### Tools (Agent-Based Memory) - -```typescript -import { streamText } from "ai"; -import { openai } from "@ai-sdk/openai"; -import { supermemoryConvexTools } from "@supermemory/convex-component/ai-sdk"; -import { ConvexHttpClient } from "convex/browser"; - -const convex = new ConvexHttpClient(process.env.CONVEX_URL!); - -const result = await streamText({ - model: openai("gpt-4"), - prompt: "Remember that I love TypeScript", - tools: supermemoryConvexTools(convex, "user_123") -}); -``` +Check out the `/example` directory for complete examples: +- `basic-usage.ts` - Vanilla TypeScript client usage +- `react-example.tsx` - React hooks with reactive UI ## Contributing diff --git a/packages/tools/src/convex-component/SHIPPING.md b/packages/tools/src/convex-component/SHIPPING.md deleted file mode 100644 index 95675a4f..00000000 --- a/packages/tools/src/convex-component/SHIPPING.md +++ /dev/null @@ -1,255 +0,0 @@ -# 🚀 Supermemory Convex Component - Ready to Ship! - -## ✅ What's Complete - -### 📦 Package Structure -- **Location**: `packages/tools/src/convex-component/` -- **Package name**: `@supermemory/convex-component` -- **Version**: `0.1.0` - -### 🏗️ Components Built - -#### 1. Convex Backend (`src/component/`) -- ✅ `convex.config.ts` - Component definition -- ✅ `schema.ts` - 5 Convex tables (searchCache, profileCache, documents, apiLogs, config) -- ✅ `actions.ts` - 3 actions (add, search, profile) calling Supermemory API -- ✅ `queries.ts` - 7 reactive queries for cache access -- ✅ `mutations.ts` - 6 mutations for cache management -- ✅ `lib.ts` - Internal utilities (API key retrieval) - -#### 2. TypeScript Client SDK (`src/client/`) -- ✅ `index.ts` - Full typed client with 11 methods -- ✅ Type exports for all interfaces -- ✅ Works with any JS/TS project - -#### 3. React Hooks (`src/react/`) -- ✅ `index.tsx` - 10 React hooks - - `useAddMemory()` - Add memories - - `useSupermemorySearch()` - Reactive search - - `useSupermemoryProfile()` - User profiles - - `useDocumentList()` - List documents - - `useDocument()` - Get by custom ID - - `useApiLogs()` - View API logs - - `useApiStats()` - Dashboard stats - - `useCleanCache()` - Cache management - - `useUpdateDocumentStatus()` - Status updates - - `useSetApiKey()` - API key config - -#### 4. Documentation -- ✅ `README.md` - Complete API reference (12KB) -- ✅ `USAGE_GUIDE.md` - Step-by-step integration guide (13KB) -- ✅ `example/` - Code examples - - `basic-usage.ts` - Vanilla TypeScript - - `react-example.tsx` - React components - -#### 5. Test Application -- ✅ `test-app/` - Full React + Vite app - - Complete UI for testing all features - - Add memories, search, view profiles, see stats - - Ready to run with `npm run dev` - -### 📊 Architecture - -``` -┌─────────────────────────────────────────┐ -│ User's Next.js/React App │ -│ (useAddMemory, useSupermemorySearch) │ -└──────────────┬──────────────────────────┘ - │ - ▼ -┌─────────────────────────────────────────┐ -│ Convex Backend Component │ -│ ┌─────────┐ ┌──────────┐ ┌────────┐ │ -│ │ Queries │ │Mutations │ │Actions │ │ -│ │(reactive)│ │(cache) │ │(API) │ │ -│ └────┬────┘ └────┬─────┘ └───┬────┘ │ -│ │ │ │ │ -│ ┌────▼────────────▼────────────▼────┐ │ -│ │ Convex Tables (Smart Cache) │ │ -│ │ - searchCache, profileCache │ │ -│ │ - documents, apiLogs, config │ │ -│ └───────────────────────────────────┘ │ -└──────────────┬──────────────────────────┘ - │ - ▼ -┌─────────────────────────────────────────┐ -│ Supermemory API (supermemory.ai) │ -│ - Semantic search │ -│ - Memory extraction │ -│ - User profiles │ -└─────────────────────────────────────────┘ -``` - -### 🎯 Key Features - -1. **3-Line Setup** - Easiest Supermemory integration ever -2. **Smart Caching** - Reduces API calls by ~80% -3. **Reactive UI** - Auto-updates via Convex subscriptions -4. **Dashboard Visibility** - See everything in Convex dashboard -5. **Full TypeScript** - Completely typed, no any's -6. **Zero Backend** - No server setup needed - -## 🧪 Testing - -### Status -- ✅ TypeScript compilation passing -- ✅ Dependencies installed (`supermemory@4.21.1`, `convex@1.35.1`) -- ✅ Package structure validated -- ✅ Test app created -- ⚠️ **Needs live testing** - Run test-app with real Convex deployment - -### To Test - -```bash -cd packages/tools/src/convex-component/test-app -npm install -npx convex dev # Follow prompts to create deployment -npm run dev # Open http://localhost:5173 -``` - -**Test checklist**: -- [ ] Add memories -- [ ] Search and verify results -- [ ] Check profile extraction -- [ ] Verify stats update -- [ ] Confirm caching works -- [ ] Check Convex dashboard shows tables/logs - -## 📦 Publishing to npm - -### Prerequisites -1. **npm account** with access to `@supermemory` org -2. **Build the package**: - ```bash - cd packages/tools/src/convex-component - npm run build - ``` - -### Publish Steps - -```bash -# 1. Build -cd packages/tools/src/convex-component -npm run build - -# 2. Login to npm -npm login - -# 3. Publish -npm publish --access public - -# 4. Verify -npm info @supermemory/convex-component -``` - -### After Publishing - -Update installation docs to use: -```bash -npm install @supermemory/convex-component -``` - -## 📝 Next Steps - -### 1. Integration Testing (PRIORITY) -- [ ] Run test-app with real Convex deployment -- [ ] Verify all hooks work end-to-end -- [ ] Test error handling -- [ ] Validate cache expiration - -### 2. Documentation -- [ ] Add to main Supermemory docs site -- [ ] Create integration guide on docs.supermemory.ai -- [ ] Add to integrations page - -### 3. Marketing -- [ ] Create demo video (3-5 min) - - Show installation - - Demonstrate adding memory - - Show search with results - - Highlight dashboard visibility -- [ ] Twitter/X announcement -- [ ] Discord announcement -- [ ] Blog post on supermemory.ai - -### 4. GitHub PR -- [ ] Create PR to main branch -- [ ] Update monorepo README -- [ ] Add changelog entry -- [ ] Link from docs - -## 🎬 Demo Video Script - -**Title**: "Add AI Memory to Convex in 3 Lines of Code" - -**Script** (3 minutes): - -1. **Intro** (15s) - - "Want to add semantic memory to your Convex app?" - - "Supermemory Convex Component makes it dead simple" - -2. **Installation** (30s) - - Show: `npm install @supermemory/convex-component` - - Add to convex.config.ts (3 lines) - - Set API key - -3. **Add Memory** (45s) - - Use `useAddMemory()` hook - - Add conversation to memory - - Show it appear in Convex dashboard - -4. **Search** (60s) - - Use `useSupermemorySearch()` hook - - Search for "user preferences" - - Show results with similarity scores - - Highlight reactive updates - -5. **Dashboard** (30s) - - Open Convex dashboard - - Show tables: searchCache, documents, apiLogs - - Show API statistics - -6. **Outro** (15s) - - "That's it! Supermemory + Convex = AI memory made easy" - - "Link in description" - -## 💡 Marketing Angles - -### For Convex Users -> "Add state-of-the-art semantic memory to your Convex app. Zero backend setup. See every API call in your dashboard. Ships in 3 lines of code." - -### For Supermemory Users -> "Use Supermemory with Convex's reactive database. Get real-time UI updates, smart caching, and amazing dashboard visibility. Easiest integration ever." - -### For AI App Builders -> "Build AI apps with long-term memory. Convex handles sync, Supermemory handles intelligence. Just plug and play." - -## 📊 Success Metrics - -**Week 1 Targets**: -- 50+ npm downloads -- 5+ GitHub stars -- 3+ people testing in Discord - -**Month 1 Targets**: -- 500+ npm downloads -- 25+ GitHub stars -- 10+ production deployments - -## 🔗 Links - -- **Package**: `packages/tools/src/convex-component/` -- **Test App**: `packages/tools/src/convex-component/test-app/` -- **Supermemory Docs**: https://supermemory.ai/docs -- **Convex Docs**: https://docs.convex.dev/components -- **npm**: https://www.npmjs.com/package/@supermemory/convex-component (after publish) - ---- - -## 🎉 Ready to Ship! - -The Supermemory Convex Component is **production-ready**. All code is complete, tested for types, and documented. - -**Next action**: Run the test-app to validate everything works with a real Convex deployment, then publish to npm! - -**LFG! 🚀** diff --git a/packages/tools/src/convex-component/USAGE_GUIDE.md b/packages/tools/src/convex-component/USAGE_GUIDE.md deleted file mode 100644 index 2e0c8720..00000000 --- a/packages/tools/src/convex-component/USAGE_GUIDE.md +++ /dev/null @@ -1,587 +0,0 @@ -# Supermemory Convex Component - Complete Usage Guide - -This guide walks you through everything you need to know to use the Supermemory Convex Component in your application. - -## Table of Contents - -1. [Installation](#installation) -2. [Setup](#setup) -3. [Basic Usage](#basic-usage) -4. [React Hooks API](#react-hooks-api) -5. [Client SDK API](#client-sdk-api) -6. [Advanced Patterns](#advanced-patterns) -7. [Convex Dashboard](#convex-dashboard) -8. [Troubleshooting](#troubleshooting) - -## Installation - -```bash -npm install @supermemory/convex-component convex -# or -bun add @supermemory/convex-component convex -``` - -## Setup - -### Step 1: Initialize Convex (if you haven't already) - -```bash -npx convex dev -``` - -### Step 2: Configure the Component - -Create or update `convex/convex.config.ts`: - -```typescript -import { defineApp } from "convex/server"; -import supermemory from "@supermemory/convex-component/convex.config"; - -const app = defineApp(); -app.use(supermemory, { name: "supermemory" }); - -export default app; -``` - -### Step 3: Set Your Supermemory API Key - -You have two options: - -**Option A: Environment Variable (Recommended)** - -```bash -# Add to .env.local -SUPERMEMORY_API_KEY=your-api-key-here -``` - -**Option B: Store in Convex** - -```bash -npx convex run supermemory:mutations.setApiKey '{"apiKey": "your-api-key-here"}' -``` - -Get your API key from [supermemory.ai/dashboard](https://supermemory.ai/dashboard). - -## Basic Usage - -### Adding Memories - -```typescript -import { createSupermemoryClient } from "@supermemory/convex-component"; -import { ConvexHttpClient } from "convex/browser"; - -const convex = new ConvexHttpClient(process.env.NEXT_PUBLIC_CONVEX_URL!); -const supermemory = createSupermemoryClient(convex); - -// Add a simple memory -await supermemory.add({ - content: "User prefers dark mode and uses TypeScript", - containerTag: "user_alice", -}); - -// Add a conversation -await supermemory.add({ - content: `User: What's your favorite framework? -Assistant: I love Next.js for full-stack development. -User: Me too! The app router is amazing.`, - containerTag: "user_alice", - customId: "conversation_001", // For updates -}); -``` - -### Searching Memories - -```typescript -const results = await supermemory.search({ - q: "framework preferences", - containerTag: "user_alice", - searchMode: "hybrid", // or "memories" - limit: 5, -}); - -console.log(results.results); -// [ -// { -// id: "mem_xyz", -// memory: "User loves Next.js for full-stack development", -// similarity: 0.92, -// ... -// } -// ] -``` - -### Getting User Profiles - -```typescript -const profile = await supermemory.profile({ - containerTag: "user_alice", - q: "preferences", // Optional: context for search -}); - -console.log("Static facts:", profile.profile.static); -// ["User prefers dark mode", "User uses TypeScript"] - -console.log("Dynamic context:", profile.profile.dynamic); -// ["Recently discussed Next.js framework"] -``` - -## React Hooks API - -### useAddMemory - -Add memories with a simple hook: - -```tsx -import { useAddMemory } from "@supermemory/convex-component/react"; - -function ChatInput({ userId }) { - const addMemory = useAddMemory(); - const [message, setMessage] = useState(""); - - const handleSend = async () => { - await addMemory({ - content: message, - containerTag: userId, - metadata: { type: "chat" }, - }); - setMessage(""); - }; - - return ( -
- setMessage(e.target.value)} /> - -
- ); -} -``` - -### useSupermemorySearch - -Reactive search with automatic UI updates: - -```tsx -import { useSupermemorySearch } from "@supermemory/convex-component/react"; - -function SearchResults({ userId }) { - const [query, setQuery] = useState(""); - const { results, isLoading, error, search } = useSupermemorySearch(null); - - const handleSearch = () => { - search({ - q: query, - containerTag: userId, - searchMode: "hybrid", - }); - }; - - return ( -
- setQuery(e.target.value)} - placeholder="Search memories..." - /> - - - {error &&
Error: {error.message}
} - - {results && ( -
-

- Found {results.total} results in {results.timing}ms - {results.cached && " (cached)"} -

- {results.results.map((result) => ( -
-

{result.memory || result.chunk}

- Similarity: {(result.similarity * 100).toFixed(1)}% -
- ))} -
- )} -
- ); -} -``` - -### useSupermemoryProfile - -Get user profile with reactive updates: - -```tsx -import { useSupermemoryProfile } from "@supermemory/convex-component/react"; - -function UserContextPanel({ userId }) { - const { profile, isLoading, refresh } = useSupermemoryProfile({ - containerTag: userId, - }); - - if (isLoading) return
Loading profile...
; - if (!profile) return null; - - return ( -
-

User Context

- - -
-

Static Facts (Always True)

-
    - {profile.profile.static.map((fact, i) => ( -
  • {fact}
  • - ))} -
-
- -
-

Dynamic Context (Recent)

-
    - {profile.profile.dynamic.map((fact, i) => ( -
  • {fact}
  • - ))} -
-
-
- ); -} -``` - -### useDocumentList - -List documents reactively: - -```tsx -import { useDocumentList } from "@supermemory/convex-component/react"; - -function DocumentHistory({ userId }) { - const documents = useDocumentList({ containerTag: userId, limit: 20 }); - - return ( -
-

Memory History

- {documents?.map((doc) => ( -
-

{doc.contentPreview}

- - {doc.status} • {new Date(doc.addedAt).toLocaleString()} - -
- ))} -
- ); -} -``` - -### useApiStats - -Dashboard statistics: - -```tsx -import { useApiStats } from "@supermemory/convex-component/react"; - -function ApiDashboard({ userId }) { - const stats = useApiStats({ containerTag: userId }); - - if (!stats) return
Loading...
; - - const successRate = (stats.successfulCalls / stats.totalCalls) * 100; - - return ( -
-

API Statistics

-
-
- {stats.totalCalls} - Total Calls -
-
- {successRate.toFixed(1)}% - Success Rate -
-
- {stats.averageResponseTime.toFixed(0)}ms - Avg Response -
-
- -

Calls by Endpoint

- {Object.entries(stats.callsByEndpoint).map(([endpoint, count]) => ( -
- {endpoint}: {count} -
- ))} -
- ); -} -``` - -## Client SDK API - -### Complete API Reference - -```typescript -const supermemory = createSupermemoryClient(convex); - -// Add content -await supermemory.add({ - content: string, - containerTag: string, - customId?: string, - metadata?: Record -}); - -// Search -await supermemory.search({ - q: string, - containerTag: string, - searchMode?: "hybrid" | "memories", - limit?: number, - threshold?: number, - rerank?: boolean, - filters?: Record -}); - -// Get profile -await supermemory.profile({ - containerTag: string, - q?: string -}); - -// List documents -await supermemory.listDocuments({ - containerTag?: string, - limit?: number -}); - -// Get document by custom ID -await supermemory.getDocumentByCustomId(customId: string); - -// Get API logs -await supermemory.getApiLogs({ - endpoint?: string, - containerTag?: string, - limit?: number -}); - -// Get stats -await supermemory.getApiStats({ - containerTag?: string -}); - -// Search cached documents -await supermemory.searchCached({ - searchText: string, - containerTag?: string, - limit?: number -}); - -// Clean expired cache -await supermemory.cleanCache(); - -// Update document status -await supermemory.updateDocumentStatus({ - documentId: string, - status: "queued" | "processed" | "failed" -}); - -// Set API key -await supermemory.setApiKey(apiKey: string); -``` - -## Advanced Patterns - -### Multi-Tenant Applications - -Organize memories by user, session, or organization: - -```typescript -// Per user -await supermemory.add({ - content: "User data", - containerTag: `user_${userId}`, -}); - -// Per organization -await supermemory.add({ - content: "Company knowledge", - containerTag: `org_${orgId}`, -}); - -// Per session -await supermemory.add({ - content: "Chat session", - containerTag: `session_${sessionId}`, -}); -``` - -### Metadata Filtering - -Add rich metadata and filter searches: - -```typescript -// Add with metadata -await supermemory.add({ - content: "Product design doc for feature X", - containerTag: "user_123", - metadata: { - type: "document", - category: "design", - priority: "high", - team: "product", - tags: ["feature-x", "q1-2024"], - }, -}); - -// Search with filters -const results = await supermemory.search({ - q: "design documents", - containerTag: "user_123", - filters: { - AND: [ - { key: "type", value: "document" }, - { key: "priority", value: "high" }, - ], - }, -}); -``` - -### Updating Content with Custom IDs - -Use custom IDs to update existing memories: - -```typescript -// Initial message -await supermemory.add({ - content: "User: Hello\nAssistant: Hi!", - containerTag: "user_123", - customId: "conversation_abc", -}); - -// Add new messages (Supermemory handles the diff) -await supermemory.add({ - content: "User: How are you?\nAssistant: I'm great!", - containerTag: "user_123", - customId: "conversation_abc", // Same ID = update -}); -``` - -### Building a Chatbot with Memory - -```tsx -import { useAddMemory, useSupermemoryProfile } from "@supermemory/convex-component/react"; - -function AIChatbot({ userId }) { - const addMemory = useAddMemory(); - const { profile, refresh } = useSupermemoryProfile({ containerTag: userId }); - const [conversation, setConversation] = useState([]); - - const sendMessage = async (userMessage: string) => { - // Get user context - await refresh(); - - // Build context-aware prompt - const context = ` -Static profile: ${profile?.profile.static.join(", ")} -Dynamic context: ${profile?.profile.dynamic.join(", ")} - `; - - // Call your LLM with context - const aiResponse = await callLLM({ - systemPrompt: `User context:\n${context}`, - messages: conversation, - userMessage, - }); - - // Store conversation in memory - await addMemory({ - content: `User: ${userMessage}\nAssistant: ${aiResponse}`, - containerTag: userId, - customId: `conversation_${Date.now()}`, - }); - - // Update UI - setConversation([...conversation, { user: userMessage, ai: aiResponse }]); - }; - - return ; -} -``` - -## Convex Dashboard - -The Supermemory component gives you full visibility in your Convex dashboard: - -### Tables You'll See - -1. **searchCache**: Cached search results with TTL -2. **profileCache**: Cached user profiles -3. **documents**: All memories/documents added -4. **apiLogs**: Every API call made to Supermemory -5. **config**: Configuration (API key, etc.) - -### Viewing API Logs - -```typescript -// In Convex dashboard, run: -const logs = await ctx.db.query("apiLogs").order("desc").take(100); - -// Or use the client: -const logs = await supermemory.getApiLogs({ limit: 100 }); -``` - -### Cache Management - -Caches automatically expire: -- Search cache: 5 minutes -- Profile cache: 2 minutes - -Clean manually: - -```typescript -await supermemory.cleanCache(); -``` - -## Troubleshooting - -### "Cannot find module '@supermemory/convex-component/convex.config'" - -Make sure you've installed the package and it's in your `package.json`. - -### "Supermemory API key not configured" - -Set the `SUPERMEMORY_API_KEY` environment variable or use `setApiKey()`. - -### Search results are empty - -Check that: -1. You've added content with `add()` -2. The `containerTag` matches -3. Wait a few seconds for Supermemory to process the content - -### TypeScript errors in hooks - -Make sure you have the correct React version (18+ or 19+) and convex version (1.35+). - -### Cache not updating - -Caches expire automatically. To force refresh: -```typescript -// Clean all caches -await supermemory.cleanCache(); - -// Or refetch profile -await refresh(); // in useSupermemoryProfile -``` - -## Next Steps - -- Check out the `/example` directory for complete examples -- Read the main [README.md](./README.md) for API reference -- Join our [Discord](https://discord.gg/supermemory) for support - ---- - -Built with ❤️ by the Supermemory team diff --git a/packages/tools/src/convex-component/src/client/index.ts b/packages/tools/src/convex-component/src/client/index.ts index dfaacd1c..dee1f437 100644 --- a/packages/tools/src/convex-component/src/client/index.ts +++ b/packages/tools/src/convex-component/src/client/index.ts @@ -62,15 +62,15 @@ export interface ProfileResponse { } } -export interface Document { +export interface Memory { _id: string - documentId: string - customId?: string + content: string containerTag: string - contentPreview: string + source: "chat" | "document" | "manual" + supermemoryId?: string + extractedMemories?: string[] + createdAt: number metadata?: Record - status: "queued" | "processed" | "failed" - addedAt: number } export interface ApiLog { @@ -123,7 +123,6 @@ export function createSupermemoryClient( client: ConvexClient, componentPath = "supermemory", ) { - // Helper to construct function references const action = (name: string): FunctionReference<"action"> => { return `${componentPath}:actions.${name}` as any } @@ -132,21 +131,17 @@ export function createSupermemoryClient( return `${componentPath}:queries.${name}` as any } - const mutation = (name: string): FunctionReference<"mutation"> => { - return `${componentPath}:mutations.${name}` as any - } - return { /** * Add content to Supermemory - * Stores text, conversations, files, or URLs for semantic search + * Stores text and extracts memories from it */ add: async (args: AddMemoryArgs) => { return await client.action(action("add"), args) }, /** - * Search memories and documents + * Search memories * Performs semantic search across all content */ search: async (args: SearchMemoriesArgs): Promise => { @@ -155,35 +150,26 @@ export function createSupermemoryClient( /** * Get user profile with context - * Retrieves static/dynamic facts about a user plus relevant memories + * Retrieves static/dynamic facts about a user */ profile: async (args: ProfileArgs): Promise => { return await client.action(action("profile"), args) }, /** - * List documents added to Supermemory - * Query documents with optional filtering + * List memories for a user + * Includes content and extracted memories */ - listDocuments: async (args?: { - containerTag?: string + listMemories: async (args: { + containerTag: string + source?: "chat" | "document" | "manual" limit?: number - }): Promise => { - return await client.query(query("listDocuments"), args || {}) - }, - - /** - * Get a document by custom ID - */ - getDocumentByCustomId: async ( - customId: string, - ): Promise => { - return await client.query(query("getDocumentByCustomId"), { customId }) + }): Promise => { + return await client.query(query("listMemories"), args) }, /** * Get API call logs - * View recent Supermemory API calls for debugging */ getApiLogs: async (args?: { endpoint?: string @@ -195,7 +181,6 @@ export function createSupermemoryClient( /** * Get API statistics - * Aggregate stats for dashboard visibility */ getApiStats: async (args?: { containerTag?: string @@ -203,16 +188,6 @@ export function createSupermemoryClient( }): Promise => { return await client.query(query("getApiStats"), args || {}) }, - - /** - * Update document status - */ - updateDocumentStatus: async (args: { - documentId: string - status: "queued" | "processed" | "failed" - }) => { - return await client.mutation(mutation("updateDocumentStatus"), args) - }, } } diff --git a/packages/tools/src/convex-component/src/component/actions.ts b/packages/tools/src/convex-component/src/component/actions.ts index 79f9f202..48e81351 100644 --- a/packages/tools/src/convex-component/src/component/actions.ts +++ b/packages/tools/src/convex-component/src/component/actions.ts @@ -3,16 +3,9 @@ import { v } from "convex/values" import Supermemory from "supermemory" import { internal } from "./_generated/api" -/** - * Supermemory Actions - * - * Actions handle non-deterministic operations like calling external APIs. - * These functions call the Supermemory REST API and cache results in Convex. - */ - /** * Add content to Supermemory - * Stores text, conversations, files, or URLs in Supermemory for semantic search + * Stores text and then searches for the extracted memories from it */ export const add = action({ args: { @@ -25,11 +18,10 @@ export const add = action({ const startTime = Date.now() try { - // Get API key from config const apiKey = await ctx.runQuery(internal.lib.getApiKey) const client = new Supermemory({ apiKey }) - // Call Supermemory API + // Add content to Supermemory const result = await client.add({ content: args.content, containerTag: args.containerTag, @@ -39,22 +31,31 @@ export const add = action({ const responseTime = Date.now() - startTime - // Store document metadata in Convex - await ctx.runMutation(internal.mutations.storeDocument, { - documentId: result.id, - customId: args.customId, - containerTag: args.containerTag, - contentPreview: args.content.substring(0, 200), - metadata: args.metadata, - status: result.status === "queued" ? "queued" : "processed", - }) + // Search for extracted memories from this content + let extractedMemories: string[] = [] + try { + // Small delay to let Supermemory process + await new Promise((r) => setTimeout(r, 1000)) + const searchResult = await client.search.memories({ + q: args.content, + containerTag: args.containerTag, + searchMode: "memories", + limit: 10, + }) + extractedMemories = (searchResult.results || []) + .map((r: any) => r.memory || r.chunk || "") + .filter((m: string) => m.length > 0) + } catch { + // Extraction might not be ready yet, that's ok + } - // Store memory in dashboard table + // Store memory with extracted memories await ctx.runMutation(internal.mutations.storeMemory, { content: args.content, containerTag: args.containerTag, source: "manual", supermemoryId: result.id, + extractedMemories, metadata: args.metadata, }) @@ -64,7 +65,7 @@ export const add = action({ incrementMemories: 1, }) - // Log API call (truncate content to avoid 1MiB field limit) + // Log API call const logBody = { ...args, content: args.content.substring(0, 500) } await ctx.runMutation(internal.mutations.logApiCall, { endpoint: "add", @@ -78,7 +79,6 @@ export const add = action({ } catch (error) { const responseTime = Date.now() - startTime - // Log error (wrapped to prevent logging failure from masking the real error) try { const logBody = { ...args, content: args.content.substring(0, 500) } await ctx.runMutation(internal.mutations.logApiCall, { @@ -101,7 +101,6 @@ export const add = action({ /** * Search memories and documents - * Performs semantic search across all content in Supermemory */ export const search = action({ args: { @@ -123,11 +122,9 @@ export const search = action({ const startTime = Date.now() try { - // Get API key from config const apiKey = await ctx.runQuery(internal.lib.getApiKey) const client = new Supermemory({ apiKey }) - // Call Supermemory API const result = await client.search.memories({ q: args.q, containerTag: args.containerTag, @@ -147,7 +144,7 @@ export const search = action({ responseTime, }) - // Log API call (exclude large filters from log) + // Log API call const logBody = { q: args.q, containerTag: args.containerTag, searchMode: args.searchMode, limit: args.limit } await ctx.runMutation(internal.mutations.logApiCall, { endpoint: "search", @@ -183,7 +180,6 @@ export const search = action({ /** * Get user profile with context - * Retrieves static/dynamic facts about a user plus relevant memories */ export const profile = action({ args: { @@ -194,11 +190,9 @@ export const profile = action({ const startTime = Date.now() try { - // Get API key from config const apiKey = await ctx.runQuery(internal.lib.getApiKey) const client = new Supermemory({ apiKey }) - // Call Supermemory API const result = await client.profile({ containerTag: args.containerTag, q: args.q, @@ -206,7 +200,6 @@ export const profile = action({ const responseTime = Date.now() - startTime - // Log API call await ctx.runMutation(internal.mutations.logApiCall, { endpoint: "profile", containerTag: args.containerTag, diff --git a/packages/tools/src/convex-component/src/component/install.ts b/packages/tools/src/convex-component/src/component/install.ts index 7823c147..9d98d5cf 100644 --- a/packages/tools/src/convex-component/src/component/install.ts +++ b/packages/tools/src/convex-component/src/component/install.ts @@ -9,15 +9,10 @@ export { add, search, profile } from "./actions" export { getApiStats, getApiLogs, - listDocuments, - getDocumentByCustomId, listMemories, getChatSessions, getChatSession, getAnalytics, getDashboardOverview, } from "./queries" -export { - updateDocumentStatus, - trackChatMessage, -} from "./mutations" +export { trackChatMessage } from "./mutations" diff --git a/packages/tools/src/convex-component/src/component/mutations.ts b/packages/tools/src/convex-component/src/component/mutations.ts index 66c104be..87c06f82 100644 --- a/packages/tools/src/convex-component/src/component/mutations.ts +++ b/packages/tools/src/convex-component/src/component/mutations.ts @@ -1,73 +1,8 @@ import { internalMutation, mutation } from "./_generated/server" import { v } from "convex/values" -/** - * Supermemory Mutations - * - * Mutations handle all database writes in transactions. - * These functions update the Convex cache with Supermemory data. - */ - -/** - * Store document metadata - * Tracks documents/memories added to Supermemory - */ -export const storeDocument = internalMutation({ - args: { - documentId: v.string(), - customId: v.optional(v.string()), - containerTag: v.string(), - contentPreview: v.string(), - metadata: v.optional(v.any()), - status: v.union( - v.literal("queued"), - v.literal("processed"), - v.literal("failed"), - ), - }, - handler: async (ctx, args) => { - // Check if document with this customId or documentId exists - const existingByCustomId = args.customId - ? await ctx.db - .query("documents") - .withIndex("by_custom_id", (q) => q.eq("customId", args.customId)) - .first() - : null - - const existingByDocId = await ctx.db - .query("documents") - .withIndex("by_document_id", (q) => q.eq("documentId", args.documentId)) - .first() - - const existing = existingByCustomId || existingByDocId - - if (existing) { - // Update existing document - await ctx.db.patch(existing._id, { - documentId: args.documentId, - customId: args.customId, - contentPreview: args.contentPreview, - metadata: args.metadata, - status: args.status, - }) - } else { - // Create new document entry - await ctx.db.insert("documents", { - documentId: args.documentId, - customId: args.customId, - containerTag: args.containerTag, - contentPreview: args.contentPreview, - metadata: args.metadata, - status: args.status, - addedAt: Date.now(), - }) - } - }, -}) - /** * Log API call - * Records API calls for debugging and analytics */ export const logApiCall = internalMutation({ args: { @@ -95,39 +30,8 @@ export const logApiCall = internalMutation({ }, }) -/** - * Update document status - * Updates the processing status of a document - */ -export const updateDocumentStatus = mutation({ - args: { - documentId: v.string(), - status: v.union( - v.literal("queued"), - v.literal("processed"), - v.literal("failed"), - ), - }, - handler: async (ctx, args) => { - const doc = await ctx.db - .query("documents") - .withIndex("by_document_id", (q) => q.eq("documentId", args.documentId)) - .first() - - if (!doc) { - throw new Error(`Document ${args.documentId} not found`) - } - - await ctx.db.patch(doc._id, { status: args.status }) - }, -}) - /** * Initialize or update API key - * Stores the Supermemory API key in Convex - * - * SECURITY NOTE: This is an internal mutation. Use `npx convex env set SUPERMEMORY_API_KEY` - * for production, or call this from a server-side admin endpoint with proper auth checks. */ export const setApiKey = internalMutation({ args: { @@ -151,8 +55,7 @@ export const setApiKey = internalMutation({ }) /** - * Store a memory - * Tracks individual memories in the dashboard + * Store a memory with extracted memories */ export const storeMemory = internalMutation({ args: { @@ -164,6 +67,7 @@ export const storeMemory = internalMutation({ v.literal("manual"), ), supermemoryId: v.optional(v.string()), + extractedMemories: v.optional(v.array(v.string())), metadata: v.optional(v.any()), }, handler: async (ctx, args) => { @@ -172,15 +76,30 @@ export const storeMemory = internalMutation({ containerTag: args.containerTag, source: args.source, supermemoryId: args.supermemoryId, + extractedMemories: args.extractedMemories, createdAt: Date.now(), metadata: args.metadata, }) }, }) +/** + * Update extracted memories for an existing memory + */ +export const updateExtractedMemories = internalMutation({ + args: { + memoryId: v.id("memories"), + extractedMemories: v.array(v.string()), + }, + handler: async (ctx, args) => { + await ctx.db.patch(args.memoryId, { + extractedMemories: args.extractedMemories, + }) + }, +}) + /** * Create or update chat session - * Tracks conversation history with memory usage */ export const updateChatSession = internalMutation({ args: { @@ -196,7 +115,6 @@ export const updateChatSession = internalMutation({ handler: async (ctx, args) => { const MAX_MESSAGES = 500 if (args.sessionId) { - // Update existing session const session = await ctx.db.get(args.sessionId) if (session) { const messages = [...session.messages, args.newMessage].slice( @@ -216,7 +134,6 @@ export const updateChatSession = internalMutation({ } } - // Create new session const sessionId = await ctx.db.insert("chatSessions", { containerTag: args.containerTag, messages: [args.newMessage], @@ -230,7 +147,6 @@ export const updateChatSession = internalMutation({ /** * Public wrapper for updateChatSession - * Allows clients to track chat sessions */ export const trackChatMessage = mutation({ args: { @@ -246,7 +162,6 @@ export const trackChatMessage = mutation({ handler: async (ctx, args) => { const MAX_MESSAGES = 500 if (args.sessionId) { - // Update existing session const session = await ctx.db.get(args.sessionId) if (session) { const messages = [...session.messages, args.newMessage].slice( @@ -266,7 +181,6 @@ export const trackChatMessage = mutation({ } } - // Create new session const sessionId = await ctx.db.insert("chatSessions", { containerTag: args.containerTag, messages: [args.newMessage], @@ -280,7 +194,6 @@ export const trackChatMessage = mutation({ /** * Update analytics - * Updates usage statistics for a user */ export const updateAnalytics = internalMutation({ args: { @@ -297,7 +210,6 @@ export const updateAnalytics = internalMutation({ .first() if (existing) { - // Update existing analytics const updates: any = { lastActive: Date.now(), } @@ -312,7 +224,6 @@ export const updateAnalytics = internalMutation({ updates.totalSearches = existing.totalSearches + args.incrementSearches } if (args.incrementSearches && args.responseTime) { - // Only update average when we're also incrementing searches const totalTime = existing.avgResponseTime * existing.totalSearches const newTotal = totalTime + args.responseTime const newSearchCount = existing.totalSearches + args.incrementSearches @@ -321,7 +232,6 @@ export const updateAnalytics = internalMutation({ await ctx.db.patch(existing._id, updates) } else { - // Create new analytics entry await ctx.db.insert("analytics", { containerTag: args.containerTag, totalMemories: args.incrementMemories || 0, diff --git a/packages/tools/src/convex-component/src/component/queries.ts b/packages/tools/src/convex-component/src/component/queries.ts index a58f85fb..2fa9297a 100644 --- a/packages/tools/src/convex-component/src/component/queries.ts +++ b/packages/tools/src/convex-component/src/component/queries.ts @@ -1,58 +1,8 @@ import { query } from "./_generated/server" import { v } from "convex/values" -/** - * Supermemory Queries - * - * Queries provide reactive, read-only access to Supermemory data. - * Components using these queries will automatically re-render when data changes. - */ - -/** - * List documents added to Supermemory - * Provides visibility into what content has been indexed - */ -export const listDocuments = query({ - args: { - containerTag: v.optional(v.string()), - limit: v.optional(v.number()), - }, - handler: async (ctx, args) => { - const limit = args.limit || 50 - - if (args.containerTag) { - return await ctx.db - .query("documents") - .withIndex("by_container", (q) => - q.eq("containerTag", args.containerTag), - ) - .order("desc") - .take(limit) - } - - return await ctx.db.query("documents").order("desc").take(limit) - }, -}) - -/** - * Get document by custom ID - * Find a specific document using your custom identifier - */ -export const getDocumentByCustomId = query({ - args: { - customId: v.string(), - }, - handler: async (ctx, args) => { - return await ctx.db - .query("documents") - .withIndex("by_custom_id", (q) => q.eq("customId", args.customId)) - .first() - }, -}) - /** * Get API call logs - * View recent Supermemory API calls for debugging and analytics */ export const getApiLogs = query({ args: { @@ -87,7 +37,6 @@ export const getApiLogs = query({ /** * Get API statistics - * Aggregate stats for dashboard visibility */ export const getApiStats = query({ args: { @@ -117,7 +66,6 @@ export const getApiStats = query({ callsByEndpoint: {} as Record, } - // Count calls by endpoint for (const log of logs) { stats.callsByEndpoint[log.endpoint] = (stats.callsByEndpoint[log.endpoint] || 0) + 1 @@ -129,7 +77,7 @@ export const getApiStats = query({ /** * List memories for a user - * View all memories saved through Supermemory + * Includes content and extractedMemories */ export const listMemories = query({ args: { @@ -162,7 +110,6 @@ export const listMemories = query({ /** * Get chat sessions for a user - * View conversation history with memory usage */ export const getChatSessions = query({ args: { @@ -182,7 +129,6 @@ export const getChatSessions = query({ /** * Get a specific chat session - * View full conversation with memory usage */ export const getChatSession = query({ args: { @@ -195,7 +141,6 @@ export const getChatSession = query({ /** * Get analytics for a user - * View usage statistics and metrics */ export const getAnalytics = query({ args: { @@ -211,7 +156,6 @@ export const getAnalytics = query({ /** * Get dashboard overview - * Comprehensive view of user's memory usage */ export const getDashboardOverview = query({ args: { @@ -235,12 +179,6 @@ export const getDashboardOverview = query({ .order("desc") .take(5) - const recentDocuments = await ctx.db - .query("documents") - .withIndex("by_container", (q) => q.eq("containerTag", args.containerTag)) - .order("desc") - .take(10) - return { analytics: analytics || { totalMemories: 0, @@ -251,7 +189,6 @@ export const getDashboardOverview = query({ }, recentMemories, recentSessions, - recentDocuments, } }, }) diff --git a/packages/tools/src/convex-component/src/component/schema.ts b/packages/tools/src/convex-component/src/component/schema.ts index 5d891e62..93d53cf6 100644 --- a/packages/tools/src/convex-component/src/component/schema.ts +++ b/packages/tools/src/convex-component/src/component/schema.ts @@ -1,41 +1,12 @@ import { defineSchema, defineTable } from "convex/server" import { v } from "convex/values" -/** - * Convex schema for Supermemory component - * - * This schema defines tables for caching Supermemory API responses, - * enabling reactive queries and reducing API calls. - */ export default defineSchema({ - /** - * Metadata about documents/memories added to Supermemory - * Tracks what content has been sent to Supermemory for analytics - */ - documents: defineTable({ - documentId: v.string(), // Supermemory document ID - customId: v.optional(v.string()), - containerTag: v.string(), - contentPreview: v.string(), // First 200 chars for reference - metadata: v.optional(v.any()), - status: v.union( - v.literal("queued"), - v.literal("processed"), - v.literal("failed"), - ), - addedAt: v.number(), - }) - .index("by_container", ["containerTag"]) - .index("by_custom_id", ["customId"]) - .index("by_document_id", ["documentId"]) - .index("by_status", ["status"]), - /** * API call logs for dashboard visibility - * Tracks all Supermemory API calls for debugging and analytics */ apiLogs: defineTable({ - endpoint: v.string(), // "add", "search", "profile", etc. + endpoint: v.string(), containerTag: v.optional(v.string()), requestBody: v.optional(v.any()), responseStatus: v.union( @@ -43,7 +14,7 @@ export default defineSchema({ v.literal("error"), v.literal("pending"), ), - responseTime: v.optional(v.number()), // milliseconds + responseTime: v.optional(v.number()), errorMessage: v.optional(v.string()), timestamp: v.number(), }) @@ -54,7 +25,6 @@ export default defineSchema({ /** * Component configuration - * Stores API key and other settings */ config: defineTable({ key: v.string(), @@ -63,7 +33,7 @@ export default defineSchema({ /** * Memories - Core memory storage - * All user memories saved through Supermemory + * Stores original content and extracted memories from Supermemory */ memories: defineTable({ content: v.string(), @@ -73,7 +43,8 @@ export default defineSchema({ v.literal("document"), v.literal("manual"), ), - supermemoryId: v.optional(v.string()), // ID from Supermemory API + supermemoryId: v.optional(v.string()), + extractedMemories: v.optional(v.array(v.string())), createdAt: v.number(), metadata: v.optional(v.any()), }) @@ -84,7 +55,6 @@ export default defineSchema({ /** * Chat Sessions - Conversation history with memory usage - * Tracks full conversations and which memories were retrieved */ chatSessions: defineTable({ containerTag: v.string(), @@ -95,7 +65,7 @@ export default defineSchema({ timestamp: v.number(), }), ), - memoriesRetrieved: v.array(v.string()), // IDs of memories used in this session + memoriesRetrieved: v.array(v.string()), createdAt: v.number(), lastMessageAt: v.number(), }) @@ -104,7 +74,6 @@ export default defineSchema({ /** * Analytics - Usage statistics per user - * Dashboard metrics for monitoring */ analytics: defineTable({ containerTag: v.string(), diff --git a/packages/tools/src/convex-component/src/react/index.tsx b/packages/tools/src/convex-component/src/react/index.tsx index 39d3f0be..c55a70a0 100644 --- a/packages/tools/src/convex-component/src/react/index.tsx +++ b/packages/tools/src/convex-component/src/react/index.tsx @@ -1,4 +1,4 @@ -import { useAction, useQuery, useMutation } from "convex/react" +import { useAction, useQuery } from "convex/react" import { useState, useCallback } from "react" import type { FunctionReference } from "convex/server" import type { @@ -7,7 +7,7 @@ import type { ProfileArgs, SearchResponse, ProfileResponse, - Document, + Memory, ApiLog, ApiStats, } from "../client/index" @@ -20,25 +20,17 @@ import type { */ /** - * Hook to add memories to Supermemory - * - * @param componentPath - Path to the component (default: "supermemory") + * Add memories to Supermemory * * @example * ```tsx * function ChatApp() { - * const addMemory = useAddMemory(); - * - * const handleSend = async (message: string) => { - * await addMemory({ - * content: message, - * containerTag: userId - * }); - * }; + * const add = addMemory(); + * await add({ content: "Hello", containerTag: "user_123" }); * } * ``` */ -export function useAddMemory(componentPath = "supermemory") { +export function addMemory(componentPath = "supermemory") { const action = `${componentPath}:actions.add` as unknown as FunctionReference<"action"> const addAction = useAction(action) @@ -52,34 +44,16 @@ export function useAddMemory(componentPath = "supermemory") { } /** - * Hook to search Supermemory with reactive results - * - * @param args - Search arguments - * @param componentPath - Path to the component (default: "supermemory") + * Search Supermemory with reactive results * * @example * ```tsx - * function SearchResults({ query, userId }) { - * const { results, isLoading, error, search } = useSupermemorySearch({ - * q: query, - * containerTag: userId, - * searchMode: "hybrid" - * }); - * - * if (isLoading) return
Searching...
; - * if (error) return
Error: {error.message}
; - * - * return ( - *
- * {results?.results.map(r => ( - *
{r.memory || r.chunk}
- * ))} - *
- * ); - * } + * const { results, isLoading, search } = searchMemories({ + * q: "typescript", containerTag: "user_123" + * }); * ``` */ -export function useSupermemorySearch( +export function searchMemories( args: SearchMemoriesArgs | null, componentPath = "supermemory", ) { @@ -110,43 +84,20 @@ export function useSupermemorySearch( [searchAction, args], ) - return { - results, - isLoading, - error, - search, - } + return { results, isLoading, error, search } } /** - * Hook to get user profile with reactive updates - * - * @param args - Profile arguments - * @param componentPath - Path to the component (default: "supermemory") + * Get user profile * * @example * ```tsx - * function UserContext({ userId }) { - * const { profile, isLoading, refresh } = useSupermemoryProfile({ - * containerTag: userId, - * q: "recent preferences" - * }); - * - * if (!profile) return null; - * - * return ( - *
- *

Static Facts

- * {profile.profile.static.map(fact =>

{fact}

)} - * - *

Dynamic Context

- * {profile.profile.dynamic.map(fact =>

{fact}

)} - *
- * ); - * } + * const { profile, isLoading, refresh } = getProfile({ + * containerTag: "user_123" + * }); * ``` */ -export function useSupermemoryProfile( +export function getProfile( args: ProfileArgs | null, componentPath = "supermemory", ) { @@ -177,173 +128,19 @@ export function useSupermemoryProfile( [profileAction, args], ) - return { - profile, - isLoading, - error, - refresh, - } + return { profile, isLoading, error, refresh } } /** - * Hook to list documents reactively - * - * @param args - List arguments - * @param componentPath - Path to the component (default: "supermemory") + * List memories reactively * * @example * ```tsx - * function DocumentList({ userId }) { - * const documents = useDocumentList({ containerTag: userId, limit: 20 }); - * - * return ( - *
- * {documents?.map(doc => ( - *
- *

{doc.contentPreview}

- * Status: {doc.status} - *
- * ))} - *
- * ); - * } + * const memories = listMemories({ containerTag: "user_123" }); + * // memories includes content + extractedMemories for each entry * ``` */ -export function useDocumentList( - args?: { containerTag?: string; limit?: number }, - componentPath = "supermemory", -) { - const query = - `${componentPath}:queries.listDocuments` as unknown as FunctionReference<"query"> - return useQuery(query, args || {}) as Document[] | undefined -} - -/** - * Hook to get a document by custom ID - * - * @param customId - Custom document identifier - * @param componentPath - Path to the component (default: "supermemory") - */ -export function useDocument( - customId: string | null, - componentPath = "supermemory", -) { - const query = - `${componentPath}:queries.getDocumentByCustomId` as unknown as FunctionReference<"query"> - return useQuery(query, customId ? { customId } : "skip") as - | Document - | null - | undefined -} - -/** - * Hook to get API logs reactively - * - * @param args - Filter arguments - * @param componentPath - Path to the component (default: "supermemory") - * - * @example - * ```tsx - * function ApiLogs() { - * const logs = useApiLogs({ limit: 50 }); - * - * return ( - *
- * {logs?.map(log => ( - *
- * {log.endpoint} - {log.responseStatus} ({log.responseTime}ms) - *
- * ))} - *
- * ); - * } - * ``` - */ -export function useApiLogs( - args?: { endpoint?: string; containerTag?: string; limit?: number }, - componentPath = "supermemory", -) { - const query = - `${componentPath}:queries.getApiLogs` as unknown as FunctionReference<"query"> - return useQuery(query, args || {}) as ApiLog[] | undefined -} - -/** - * Hook to get API statistics reactively - * - * @param args - Filter arguments - * @param componentPath - Path to the component (default: "supermemory") - * - * @example - * ```tsx - * function Dashboard({ userId }) { - * const stats = useApiStats({ containerTag: userId }); - * - * return ( - *
- *

Total Calls: {stats?.totalCalls}

- *

Success Rate: {((stats?.successfulCalls / stats?.totalCalls) * 100).toFixed(1)}%

- *

Avg Response: {stats?.averageResponseTime.toFixed(0)}ms

- *
- * ); - * } - * ``` - */ -export function useApiStats( - args?: { containerTag?: string; limit?: number }, - componentPath = "supermemory", -) { - const query = - `${componentPath}:queries.getApiStats` as unknown as FunctionReference<"query"> - return useQuery(query, args || {}) as ApiStats | undefined -} - -/** - * Hook to update document status - * - * @param componentPath - Path to the component (default: "supermemory") - */ -export function useUpdateDocumentStatus(componentPath = "supermemory") { - const mutation = - `${componentPath}:mutations.updateDocumentStatus` as unknown as FunctionReference<"mutation"> - const updateMutation = useMutation(mutation) - - return useCallback( - async (args: { - documentId: string - status: "queued" | "processed" | "failed" - }) => { - return await updateMutation(args) - }, - [updateMutation], - ) -} - -/** - * Hook to list memories for a user - * - * @param args - Filter arguments - * @param componentPath - Path to the component (default: "supermemory") - * - * @example - * ```tsx - * function MemoryList({ userId }) { - * const memories = useMemories({ containerTag: userId, limit: 50 }); - * - * return ( - *
- * {memories?.map(memory => ( - *
- *

{memory.content}

- * Source: {memory.source} - *
- * ))} - *
- * ); - * } - * ``` - */ -export function useMemories( +export function listMemories( args: { containerTag: string source?: "chat" | "document" | "manual" @@ -353,137 +150,9 @@ export function useMemories( ) { const query = `${componentPath}:queries.listMemories` as unknown as FunctionReference<"query"> - return useQuery(query, args) as any[] | undefined + return useQuery(query, args) as Memory[] | undefined } -/** - * Hook to get chat sessions for a user - * - * @param args - Filter arguments - * @param componentPath - Path to the component (default: "supermemory") - * - * @example - * ```tsx - * function ChatHistory({ userId }) { - * const sessions = useChatSessions({ containerTag: userId, limit: 10 }); - * - * return ( - *
- * {sessions?.map(session => ( - *
- *

{session.messages.length} messages

- *

Last active: {new Date(session.lastMessageAt).toLocaleString()}

- *
- * ))} - *
- * ); - * } - * ``` - */ -export function useChatSessions( - args: { containerTag: string; limit?: number }, - componentPath = "supermemory", -) { - const query = - `${componentPath}:queries.getChatSessions` as unknown as FunctionReference<"query"> - return useQuery(query, args) as any[] | undefined -} - -/** - * Hook to get a specific chat session - * - * @param sessionId - Chat session ID - * @param componentPath - Path to the component (default: "supermemory") - */ -export function useChatSession( - sessionId: string | null, - componentPath = "supermemory", -) { - const query = - `${componentPath}:queries.getChatSession` as unknown as FunctionReference<"query"> - return useQuery(query, sessionId ? { sessionId } : "skip") as - | any - | null - | undefined -} - -/** - * Hook to get analytics for a user - * - * @param containerTag - User identifier - * @param componentPath - Path to the component (default: "supermemory") - * - * @example - * ```tsx - * function Analytics({ userId }) { - * const analytics = useAnalytics(userId); - * - * if (!analytics) return
Loading...
; - * - * return ( - *
- *

Total Memories: {analytics.totalMemories}

- *

Total Chats: {analytics.totalChats}

- *

Total Searches: {analytics.totalSearches}

- *

Avg Response Time: {analytics.avgResponseTime.toFixed(0)}ms

- *
- * ); - * } - * ``` - */ -export function useAnalytics( - containerTag: string | null, - componentPath = "supermemory", -) { - const query = - `${componentPath}:queries.getAnalytics` as unknown as FunctionReference<"query"> - return useQuery(query, containerTag ? { containerTag } : "skip") as - | any - | null - | undefined -} - -/** - * Hook to get dashboard overview - * - * @param containerTag - User identifier - * @param componentPath - Path to the component (default: "supermemory") - * - * @example - * ```tsx - * function Dashboard({ userId }) { - * const overview = useDashboardOverview(userId); - * - * if (!overview) return
Loading...
; - * - * return ( - *
- *

Analytics

- *

Total Memories: {overview.analytics.totalMemories}

- *

Total Chats: {overview.analytics.totalChats}

- * - *

Recent Memories

- * {overview.recentMemories.map(m =>

{m.content}

)} - * - *

Recent Sessions

- * {overview.recentSessions.map(s => ( - *

{s.messages.length} messages

- * ))} - *
- * ); - * } - * ``` - */ -export function useDashboardOverview( - containerTag: string | null, - componentPath = "supermemory", -) { - const query = - `${componentPath}:queries.getDashboardOverview` as unknown as FunctionReference<"query"> - return useQuery(query, containerTag ? { containerTag } : "skip") as - | any - | undefined -} // Export all types export type { @@ -492,7 +161,5 @@ export type { ProfileArgs, SearchResponse, ProfileResponse, - Document, - ApiLog, - ApiStats, + Memory, } from "../client/index"