mirror of
https://github.com/supermemoryai/supermemory.git
synced 2026-09-07 08:26:15 +00:00
removed cache
This commit is contained in:
parent
76a32dfd00
commit
c1aa4b19cd
9 changed files with 12 additions and 395 deletions
|
|
@ -4,7 +4,7 @@ import type { FunctionReference } from "convex/server"
|
|||
/**
|
||||
* Supermemory AI SDK Middleware for Convex
|
||||
*
|
||||
* Wraps AI models to automatically inject user context from Convex-cached memories.
|
||||
* Wraps AI models to automatically inject user context from Supermemory.
|
||||
*/
|
||||
|
||||
/**
|
||||
|
|
@ -182,7 +182,7 @@ export function withSupermemory<T extends WrappableLanguageModel>(
|
|||
|
||||
if (verbose) {
|
||||
console.log(
|
||||
`[Supermemory] Found ${searchResults.length} relevant memories (cached: ${searchResult.cached})`,
|
||||
`[Supermemory] Found ${searchResults.length} relevant memories`,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -200,15 +200,15 @@ export function withSupermemory<T extends WrappableLanguageModel>(
|
|||
...callOptions.prompt,
|
||||
]
|
||||
|
||||
// Auto-save user message if enabled
|
||||
// Auto-save user message if enabled (fire-and-forget to avoid blocking)
|
||||
if (addMemory === "always" && userQuery) {
|
||||
if (verbose) console.log("[Supermemory] Auto-saving user message...")
|
||||
|
||||
await convexClient.action(addAction, {
|
||||
convexClient.action(addAction, {
|
||||
content: userQuery,
|
||||
containerTag,
|
||||
metadata: { source: "ai-middleware", auto: true },
|
||||
})
|
||||
}).catch((e) => console.error("[Supermemory] Failed to auto-save:", e))
|
||||
}
|
||||
|
||||
// Call original model with enhanced context
|
||||
|
|
@ -281,11 +281,11 @@ export function withSupermemory<T extends WrappableLanguageModel>(
|
|||
]
|
||||
|
||||
if (addMemory === "always" && userQuery) {
|
||||
await convexClient.action(addAction, {
|
||||
convexClient.action(addAction, {
|
||||
content: userQuery,
|
||||
containerTag,
|
||||
metadata: { source: "ai-middleware", auto: true },
|
||||
})
|
||||
}).catch((e) => console.error("[Supermemory] Failed to auto-save:", e))
|
||||
}
|
||||
|
||||
return await model.doStream({
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ import type { FunctionReference } from "convex/server"
|
|||
* Supermemory AI SDK Tools for Convex
|
||||
*
|
||||
* Provides AI agent tools that use Convex actions for memory operations.
|
||||
* All operations are cached and tracked in the Convex dashboard.
|
||||
* All operations are tracked in the Convex dashboard.
|
||||
*/
|
||||
|
||||
/**
|
||||
|
|
@ -95,7 +95,6 @@ export function supermemoryConvexTools(
|
|||
metadata: r.metadata,
|
||||
})),
|
||||
count: result.total,
|
||||
cached: result.cached,
|
||||
}
|
||||
} catch (error) {
|
||||
return {
|
||||
|
|
|
|||
|
|
@ -44,7 +44,6 @@ export interface SearchResponse {
|
|||
results: SearchResult[]
|
||||
timing: number
|
||||
total: number
|
||||
cached: boolean
|
||||
}
|
||||
|
||||
export interface ProfileResponse {
|
||||
|
|
@ -61,7 +60,6 @@ export interface ProfileResponse {
|
|||
metadata?: Record<string, any>
|
||||
}>
|
||||
}
|
||||
cached: boolean
|
||||
}
|
||||
|
||||
export interface Document {
|
||||
|
|
@ -206,26 +204,6 @@ export function createSupermemoryClient(
|
|||
return await client.query(query("getApiStats"), args || {})
|
||||
},
|
||||
|
||||
/**
|
||||
* Search cached documents locally
|
||||
* Fast text search across cached content
|
||||
*/
|
||||
searchCached: async (args: {
|
||||
searchText: string
|
||||
containerTag?: string
|
||||
limit?: number
|
||||
}): Promise<Document[]> => {
|
||||
return await client.query(query("searchCachedDocuments"), args)
|
||||
},
|
||||
|
||||
/**
|
||||
* Clean expired cache entries
|
||||
* Removes old search and profile caches
|
||||
*/
|
||||
cleanCache: async () => {
|
||||
return await client.mutation(mutation("cleanExpiredCache"), {})
|
||||
},
|
||||
|
||||
/**
|
||||
* Update document status
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import { action } from "./_generated/server"
|
||||
import { v } from "convex/values"
|
||||
import Supermemory from "supermemory"
|
||||
import { api, internal } from "./_generated/api"
|
||||
import { internal } from "./_generated/api"
|
||||
|
||||
/**
|
||||
* Supermemory Actions
|
||||
|
|
@ -123,22 +123,6 @@ export const search = action({
|
|||
const startTime = Date.now()
|
||||
|
||||
try {
|
||||
// Check cache first
|
||||
const cached = await ctx.runQuery(api.queries.getSearchCache, {
|
||||
query: args.q,
|
||||
containerTag: args.containerTag,
|
||||
searchMode: args.searchMode,
|
||||
})
|
||||
|
||||
if (cached) {
|
||||
return {
|
||||
results: cached.results,
|
||||
timing: cached.timing,
|
||||
total: cached.total,
|
||||
cached: true,
|
||||
}
|
||||
}
|
||||
|
||||
// Get API key from config
|
||||
const apiKey = await ctx.runQuery(internal.lib.getApiKey)
|
||||
const client = new Supermemory({ apiKey })
|
||||
|
|
@ -156,17 +140,6 @@ export const search = action({
|
|||
|
||||
const responseTime = Date.now() - startTime
|
||||
|
||||
// Cache results (expires in 5 minutes)
|
||||
await ctx.runMutation(internal.mutations.cacheSearchResults, {
|
||||
query: args.q,
|
||||
containerTag: args.containerTag,
|
||||
searchMode: args.searchMode,
|
||||
results: result.results,
|
||||
timing: result.timing,
|
||||
total: result.total,
|
||||
ttl: 300, // 5 minutes
|
||||
})
|
||||
|
||||
// Update analytics
|
||||
await ctx.runMutation(internal.mutations.updateAnalytics, {
|
||||
containerTag: args.containerTag,
|
||||
|
|
@ -184,10 +157,7 @@ export const search = action({
|
|||
responseTime,
|
||||
})
|
||||
|
||||
return {
|
||||
...result,
|
||||
cached: false,
|
||||
}
|
||||
return result
|
||||
} catch (error) {
|
||||
const responseTime = Date.now() - startTime
|
||||
|
||||
|
|
@ -224,24 +194,6 @@ export const profile = action({
|
|||
const startTime = Date.now()
|
||||
|
||||
try {
|
||||
// Check cache first
|
||||
const cached = await ctx.runQuery(api.queries.getProfileCache, {
|
||||
containerTag: args.containerTag,
|
||||
})
|
||||
|
||||
if (cached) {
|
||||
return {
|
||||
profile: {
|
||||
static: cached.staticProfile,
|
||||
dynamic: cached.dynamicProfile,
|
||||
},
|
||||
searchResults: cached.searchResults
|
||||
? { results: cached.searchResults }
|
||||
: undefined,
|
||||
cached: true,
|
||||
}
|
||||
}
|
||||
|
||||
// Get API key from config
|
||||
const apiKey = await ctx.runQuery(internal.lib.getApiKey)
|
||||
const client = new Supermemory({ apiKey })
|
||||
|
|
@ -254,15 +206,6 @@ export const profile = action({
|
|||
|
||||
const responseTime = Date.now() - startTime
|
||||
|
||||
// Cache profile (expires in 2 minutes for freshness)
|
||||
await ctx.runMutation(internal.mutations.cacheProfile, {
|
||||
containerTag: args.containerTag,
|
||||
staticProfile: result.profile.static,
|
||||
dynamicProfile: result.profile.dynamic,
|
||||
searchResults: result.searchResults?.results,
|
||||
ttl: 120, // 2 minutes
|
||||
})
|
||||
|
||||
// Log API call
|
||||
await ctx.runMutation(internal.mutations.logApiCall, {
|
||||
endpoint: "profile",
|
||||
|
|
@ -272,10 +215,7 @@ export const profile = action({
|
|||
responseTime,
|
||||
})
|
||||
|
||||
return {
|
||||
...result,
|
||||
cached: false,
|
||||
}
|
||||
return result
|
||||
} catch (error) {
|
||||
const responseTime = Date.now() - startTime
|
||||
|
||||
|
|
|
|||
|
|
@ -18,7 +18,6 @@ export {
|
|||
getDashboardOverview,
|
||||
} from "./queries"
|
||||
export {
|
||||
cleanExpiredCache,
|
||||
updateDocumentStatus,
|
||||
trackChatMessage,
|
||||
} from "./mutations"
|
||||
|
|
|
|||
|
|
@ -8,113 +8,6 @@ import { v } from "convex/values"
|
|||
* These functions update the Convex cache with Supermemory data.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Cache search results
|
||||
* Stores search results from Supermemory API for reactive access
|
||||
*/
|
||||
export const cacheSearchResults = internalMutation({
|
||||
args: {
|
||||
query: v.string(),
|
||||
containerTag: v.string(),
|
||||
searchMode: v.optional(
|
||||
v.union(
|
||||
v.literal("hybrid"),
|
||||
v.literal("memories"),
|
||||
v.literal("documents"),
|
||||
),
|
||||
),
|
||||
results: v.array(v.any()), // Accept any shape from Supermemory API
|
||||
timing: v.number(),
|
||||
total: v.number(),
|
||||
ttl: v.number(), // Time to live in seconds
|
||||
},
|
||||
handler: async (ctx, args) => {
|
||||
const expiresAt = Date.now() + args.ttl * 1000
|
||||
|
||||
// Check if cache already exists
|
||||
const existing = await ctx.db
|
||||
.query("searchCache")
|
||||
.withIndex("by_query_container", (q) =>
|
||||
q.eq("query", args.query).eq("containerTag", args.containerTag),
|
||||
)
|
||||
.first()
|
||||
|
||||
if (existing) {
|
||||
// Update existing cache
|
||||
await ctx.db.patch(existing._id, {
|
||||
results: args.results,
|
||||
timing: args.timing,
|
||||
total: args.total,
|
||||
expiresAt,
|
||||
searchMode: args.searchMode,
|
||||
})
|
||||
} else {
|
||||
// Create new cache entry
|
||||
await ctx.db.insert("searchCache", {
|
||||
query: args.query,
|
||||
containerTag: args.containerTag,
|
||||
searchMode: args.searchMode,
|
||||
results: args.results,
|
||||
timing: args.timing,
|
||||
total: args.total,
|
||||
expiresAt,
|
||||
})
|
||||
}
|
||||
},
|
||||
})
|
||||
|
||||
/**
|
||||
* Cache user profile
|
||||
* Stores user profile from Supermemory API for reactive access
|
||||
*/
|
||||
export const cacheProfile = internalMutation({
|
||||
args: {
|
||||
containerTag: v.string(),
|
||||
staticProfile: v.array(v.string()),
|
||||
dynamicProfile: v.array(v.string()),
|
||||
searchResults: v.optional(
|
||||
v.array(
|
||||
v.object({
|
||||
id: v.string(),
|
||||
memory: v.optional(v.string()),
|
||||
chunk: v.optional(v.string()),
|
||||
similarity: v.number(),
|
||||
metadata: v.optional(v.any()),
|
||||
}),
|
||||
),
|
||||
),
|
||||
ttl: v.number(),
|
||||
},
|
||||
handler: async (ctx, args) => {
|
||||
const expiresAt = Date.now() + args.ttl * 1000
|
||||
|
||||
// Check if profile cache exists
|
||||
const existing = await ctx.db
|
||||
.query("profileCache")
|
||||
.withIndex("by_container", (q) => q.eq("containerTag", args.containerTag))
|
||||
.first()
|
||||
|
||||
if (existing) {
|
||||
// Update existing cache
|
||||
await ctx.db.patch(existing._id, {
|
||||
staticProfile: args.staticProfile,
|
||||
dynamicProfile: args.dynamicProfile,
|
||||
searchResults: args.searchResults,
|
||||
expiresAt,
|
||||
})
|
||||
} else {
|
||||
// Create new cache entry
|
||||
await ctx.db.insert("profileCache", {
|
||||
containerTag: args.containerTag,
|
||||
staticProfile: args.staticProfile,
|
||||
dynamicProfile: args.dynamicProfile,
|
||||
searchResults: args.searchResults,
|
||||
expiresAt,
|
||||
})
|
||||
}
|
||||
},
|
||||
})
|
||||
|
||||
/**
|
||||
* Store document metadata
|
||||
* Tracks documents/memories added to Supermemory
|
||||
|
|
@ -202,42 +95,6 @@ export const logApiCall = internalMutation({
|
|||
},
|
||||
})
|
||||
|
||||
/**
|
||||
* Clean expired cache entries
|
||||
* Removes expired search and profile caches
|
||||
*/
|
||||
export const cleanExpiredCache = mutation({
|
||||
args: {},
|
||||
handler: async (ctx) => {
|
||||
const now = Date.now()
|
||||
|
||||
// Clean expired search caches
|
||||
const expiredSearches = await ctx.db
|
||||
.query("searchCache")
|
||||
.withIndex("by_expires", (q) => q.lt("expiresAt", now))
|
||||
.collect()
|
||||
|
||||
for (const cache of expiredSearches) {
|
||||
await ctx.db.delete(cache._id)
|
||||
}
|
||||
|
||||
// Clean expired profile caches
|
||||
const expiredProfiles = await ctx.db
|
||||
.query("profileCache")
|
||||
.withIndex("by_expires", (q) => q.lt("expiresAt", now))
|
||||
.collect()
|
||||
|
||||
for (const cache of expiredProfiles) {
|
||||
await ctx.db.delete(cache._id)
|
||||
}
|
||||
|
||||
return {
|
||||
cleanedSearchCaches: expiredSearches.length,
|
||||
cleanedProfileCaches: expiredProfiles.length,
|
||||
}
|
||||
},
|
||||
})
|
||||
|
||||
/**
|
||||
* Update document status
|
||||
* Updates the processing status of a document
|
||||
|
|
|
|||
|
|
@ -4,74 +4,10 @@ import { v } from "convex/values"
|
|||
/**
|
||||
* Supermemory Queries
|
||||
*
|
||||
* Queries provide reactive, read-only access to cached Supermemory data.
|
||||
* Queries provide reactive, read-only access to Supermemory data.
|
||||
* Components using these queries will automatically re-render when data changes.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Get cached search results
|
||||
* Returns cached search results if available and not expired
|
||||
*/
|
||||
export const getSearchCache = query({
|
||||
args: {
|
||||
query: v.string(),
|
||||
containerTag: v.string(),
|
||||
searchMode: v.optional(
|
||||
v.union(
|
||||
v.literal("hybrid"),
|
||||
v.literal("memories"),
|
||||
v.literal("documents"),
|
||||
),
|
||||
),
|
||||
},
|
||||
handler: async (ctx, args) => {
|
||||
const now = Date.now()
|
||||
|
||||
const cached = await ctx.db
|
||||
.query("searchCache")
|
||||
.withIndex("by_query_container", (q) =>
|
||||
q.eq("query", args.query).eq("containerTag", args.containerTag),
|
||||
)
|
||||
.first()
|
||||
|
||||
// Return null if cache expired or searchMode doesn't match
|
||||
if (!cached || cached.expiresAt < now) {
|
||||
return null
|
||||
}
|
||||
const effectiveMode = args.searchMode || "hybrid"
|
||||
if (cached.searchMode !== effectiveMode) {
|
||||
return null
|
||||
}
|
||||
|
||||
return cached
|
||||
},
|
||||
})
|
||||
|
||||
/**
|
||||
* Get cached user profile
|
||||
* Returns cached profile if available and not expired
|
||||
*/
|
||||
export const getProfileCache = query({
|
||||
args: {
|
||||
containerTag: v.string(),
|
||||
},
|
||||
handler: async (ctx, args) => {
|
||||
const now = Date.now()
|
||||
|
||||
const cached = await ctx.db
|
||||
.query("profileCache")
|
||||
.withIndex("by_container", (q) => q.eq("containerTag", args.containerTag))
|
||||
.first()
|
||||
|
||||
// Return null if cache expired
|
||||
if (!cached || cached.expiresAt < now) {
|
||||
return null
|
||||
}
|
||||
|
||||
return cached
|
||||
},
|
||||
})
|
||||
|
||||
/**
|
||||
* List documents added to Supermemory
|
||||
* Provides visibility into what content has been indexed
|
||||
|
|
@ -191,37 +127,6 @@ export const getApiStats = query({
|
|||
},
|
||||
})
|
||||
|
||||
/**
|
||||
* Search documents locally (in Convex cache)
|
||||
* Fast text search across cached content previews
|
||||
*/
|
||||
export const searchCachedDocuments = query({
|
||||
args: {
|
||||
searchText: v.string(),
|
||||
containerTag: v.optional(v.string()),
|
||||
limit: v.optional(v.number()),
|
||||
},
|
||||
handler: async (ctx, args) => {
|
||||
const limit = args.limit || 20
|
||||
const searchLower = args.searchText.toLowerCase()
|
||||
|
||||
const query = args.containerTag
|
||||
? ctx.db
|
||||
.query("documents")
|
||||
.withIndex("by_container", (q) =>
|
||||
q.eq("containerTag", args.containerTag),
|
||||
)
|
||||
: ctx.db.query("documents")
|
||||
|
||||
const allDocs = await query.collect()
|
||||
|
||||
// Simple text matching on content preview
|
||||
return allDocs
|
||||
.filter((doc) => doc.contentPreview.toLowerCase().includes(searchLower))
|
||||
.slice(0, limit)
|
||||
},
|
||||
})
|
||||
|
||||
/**
|
||||
* List memories for a user
|
||||
* View all memories saved through Supermemory
|
||||
|
|
|
|||
|
|
@ -8,52 +8,6 @@ import { v } from "convex/values"
|
|||
* enabling reactive queries and reducing API calls.
|
||||
*/
|
||||
export default defineSchema({
|
||||
/**
|
||||
* Cached search results from Supermemory
|
||||
* Stores recent search queries and their results for fast reactive access
|
||||
*/
|
||||
searchCache: defineTable({
|
||||
query: v.string(),
|
||||
containerTag: v.string(),
|
||||
searchMode: v.optional(
|
||||
v.union(
|
||||
v.literal("hybrid"),
|
||||
v.literal("memories"),
|
||||
v.literal("documents"),
|
||||
),
|
||||
),
|
||||
results: v.any(), // Accept any shape from Supermemory API
|
||||
timing: v.number(),
|
||||
total: v.number(),
|
||||
expiresAt: v.number(), // Timestamp when cache expires
|
||||
})
|
||||
.index("by_query_container", ["query", "containerTag"])
|
||||
.index("by_expires", ["expiresAt"]),
|
||||
|
||||
/**
|
||||
* Cached user profiles from Supermemory
|
||||
* Stores user context (static + dynamic facts) for fast access
|
||||
*/
|
||||
profileCache: defineTable({
|
||||
containerTag: v.string(),
|
||||
staticProfile: v.array(v.string()),
|
||||
dynamicProfile: v.array(v.string()),
|
||||
searchResults: v.optional(
|
||||
v.array(
|
||||
v.object({
|
||||
id: v.string(),
|
||||
memory: v.optional(v.string()),
|
||||
chunk: v.optional(v.string()),
|
||||
similarity: v.number(),
|
||||
metadata: v.optional(v.any()),
|
||||
}),
|
||||
),
|
||||
),
|
||||
expiresAt: v.number(),
|
||||
})
|
||||
.index("by_container", ["containerTag"])
|
||||
.index("by_expires", ["expiresAt"]),
|
||||
|
||||
/**
|
||||
* Metadata about documents/memories added to Supermemory
|
||||
* Tracks what content has been sent to Supermemory for analytics
|
||||
|
|
|
|||
|
|
@ -298,21 +298,6 @@ export function useApiStats(
|
|||
return useQuery(query, args || {}) as ApiStats | undefined
|
||||
}
|
||||
|
||||
/**
|
||||
* Hook to clean expired cache
|
||||
*
|
||||
* @param componentPath - Path to the component (default: "supermemory")
|
||||
*/
|
||||
export function useCleanCache(componentPath = "supermemory") {
|
||||
const mutation =
|
||||
`${componentPath}:mutations.cleanExpiredCache` as unknown as FunctionReference<"mutation">
|
||||
const cleanMutation = useMutation(mutation)
|
||||
|
||||
return useCallback(async () => {
|
||||
return await cleanMutation({})
|
||||
}, [cleanMutation])
|
||||
}
|
||||
|
||||
/**
|
||||
* Hook to update document status
|
||||
*
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue