mirror of
https://github.com/supermemoryai/supermemory.git
synced 2026-09-07 08:26:15 +00:00
fixed bugs
This commit is contained in:
parent
039f5fc34d
commit
e8252b1048
1 changed files with 193 additions and 25 deletions
|
|
@ -1,3 +1,4 @@
|
|||
import Supermemory from "supermemory"
|
||||
import { deduplicateMemories } from "../tools-shared"
|
||||
import type {
|
||||
Logger,
|
||||
|
|
@ -11,6 +12,7 @@ import {
|
|||
convertProfileToMarkdown,
|
||||
defaultPromptTemplate,
|
||||
} from "./prompt-builder"
|
||||
import { createSupermemoryClient } from "./context"
|
||||
|
||||
/**
|
||||
* Fetches profile and search results from the Supermemory API.
|
||||
|
|
@ -84,10 +86,142 @@ export interface BuildMemoriesTextOptions {
|
|||
searchLimit?: number
|
||||
}
|
||||
|
||||
/**
|
||||
* Search result structure for unified handling.
|
||||
*/
|
||||
interface SearchResult {
|
||||
content: string
|
||||
metadata?: Record<string, unknown>
|
||||
}
|
||||
|
||||
/**
|
||||
* Searches for memories using the Supermemory SDK.
|
||||
*
|
||||
* @param client - Supermemory client instance
|
||||
* @param containerTag - Container tag for scoping
|
||||
* @param query - Search query text
|
||||
* @param limit - Maximum number of results
|
||||
* @returns Array of search results
|
||||
*/
|
||||
async function searchMemoriesSDK(
|
||||
client: Supermemory,
|
||||
containerTag: string,
|
||||
query: string,
|
||||
limit: number,
|
||||
): Promise<SearchResult[]> {
|
||||
const response = await client.search.memories({
|
||||
q: query,
|
||||
containerTag,
|
||||
limit,
|
||||
include: { chunks: false },
|
||||
})
|
||||
return (response.results || []).map((r) => ({
|
||||
content: r.memory || "",
|
||||
metadata: r.metadata ?? undefined,
|
||||
}))
|
||||
}
|
||||
|
||||
/**
|
||||
* Searches for document chunks using the Supermemory SDK.
|
||||
* Only includes chunks marked as relevant (isRelevant: true).
|
||||
*
|
||||
* @param client - Supermemory client instance
|
||||
* @param containerTag - Container tag for scoping
|
||||
* @param query - Search query text
|
||||
* @param limit - Maximum number of results
|
||||
* @returns Array of search results containing only relevant chunks
|
||||
*/
|
||||
async function searchDocumentsSDK(
|
||||
client: Supermemory,
|
||||
containerTag: string,
|
||||
query: string,
|
||||
limit: number,
|
||||
): Promise<SearchResult[]> {
|
||||
const response = await client.search.documents({
|
||||
q: query,
|
||||
containerTags: [containerTag],
|
||||
limit,
|
||||
})
|
||||
// Extract only relevant chunks from each document result
|
||||
const results: SearchResult[] = []
|
||||
for (const doc of response.results || []) {
|
||||
for (const chunk of doc.chunks || []) {
|
||||
// Only include chunks marked as relevant
|
||||
if (chunk.isRelevant) {
|
||||
results.push({
|
||||
content: chunk.content || "",
|
||||
metadata: doc.metadata ?? undefined,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
return results
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs search based on the specified search mode.
|
||||
*
|
||||
* @param client - Supermemory client instance
|
||||
* @param containerTag - Container tag for scoping
|
||||
* @param query - Search query text
|
||||
* @param searchMode - Search mode: "memories", "hybrid", or "documents"
|
||||
* @param limit - Maximum number of results per search type
|
||||
* @param logger - Logger instance
|
||||
* @returns Combined array of search results
|
||||
*/
|
||||
async function performSearch(
|
||||
client: Supermemory,
|
||||
containerTag: string,
|
||||
query: string,
|
||||
searchMode: SearchMode,
|
||||
limit: number,
|
||||
logger: Logger,
|
||||
): Promise<SearchResult[]> {
|
||||
logger.debug("Performing search", { searchMode, containerTag, limit })
|
||||
|
||||
switch (searchMode) {
|
||||
case "memories":
|
||||
return searchMemoriesSDK(client, containerTag, query, limit)
|
||||
|
||||
case "documents":
|
||||
return searchDocumentsSDK(client, containerTag, query, limit)
|
||||
|
||||
case "hybrid": {
|
||||
// Run both searches in parallel
|
||||
const [memoriesResults, documentsResults] = await Promise.all([
|
||||
searchMemoriesSDK(client, containerTag, query, limit),
|
||||
searchDocumentsSDK(client, containerTag, query, limit),
|
||||
])
|
||||
|
||||
logger.debug("Hybrid search completed", {
|
||||
memoriesCount: memoriesResults.length,
|
||||
documentsCount: documentsResults.length,
|
||||
})
|
||||
|
||||
// Combine results, memories first
|
||||
return [...memoriesResults, ...documentsResults]
|
||||
}
|
||||
|
||||
default:
|
||||
logger.warn(`Unknown search mode: ${searchMode}, defaulting to memories`)
|
||||
return searchMemoriesSDK(client, containerTag, query, limit)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetches memories from the API, deduplicates them, and formats them into
|
||||
* the final string to be injected into the system prompt.
|
||||
*
|
||||
* When searchMode is specified, uses the Supermemory SDK search endpoints:
|
||||
* - "memories": Uses search.memories() for memory entries
|
||||
* - "documents": Uses search.documents() for document chunks
|
||||
* - "hybrid": Uses both endpoints in parallel
|
||||
*
|
||||
* The mode option controls whether profile data is included:
|
||||
* - "profile": Only profile data (static/dynamic), no search
|
||||
* - "query": Only search results, no profile data
|
||||
* - "full": Both profile data and search results
|
||||
*
|
||||
* @param options - Configuration for building memories text
|
||||
* @returns The final formatted memories string ready for injection
|
||||
*/
|
||||
|
|
@ -102,48 +236,77 @@ export const buildMemoriesText = async (
|
|||
apiKey,
|
||||
logger,
|
||||
promptTemplate = defaultPromptTemplate,
|
||||
searchMode = "memories",
|
||||
searchLimit = 10,
|
||||
} = options
|
||||
|
||||
const memoriesResponse = await supermemoryProfileSearch(
|
||||
containerTag,
|
||||
queryText,
|
||||
baseUrl,
|
||||
apiKey,
|
||||
)
|
||||
// Fetch profile data when mode includes profile (profile or full)
|
||||
let profileData: ProfileStructure | null = null
|
||||
if (mode !== "query") {
|
||||
profileData = await supermemoryProfileSearch(
|
||||
containerTag,
|
||||
mode === "profile" ? "" : queryText, // Only send query for full mode
|
||||
baseUrl,
|
||||
apiKey,
|
||||
)
|
||||
|
||||
const memoryCountStatic = memoriesResponse.profile.static?.length || 0
|
||||
const memoryCountDynamic = memoriesResponse.profile.dynamic?.length || 0
|
||||
const memoryCountStatic = profileData.profile.static?.length || 0
|
||||
const memoryCountDynamic = profileData.profile.dynamic?.length || 0
|
||||
|
||||
logger.info("Memory search completed", {
|
||||
containerTag,
|
||||
memoryCountStatic,
|
||||
memoryCountDynamic,
|
||||
queryText:
|
||||
queryText.substring(0, 100) + (queryText.length > 100 ? "..." : ""),
|
||||
mode,
|
||||
})
|
||||
logger.info("Profile search completed", {
|
||||
containerTag,
|
||||
memoryCountStatic,
|
||||
memoryCountDynamic,
|
||||
mode,
|
||||
})
|
||||
}
|
||||
|
||||
// Perform SDK-based search when mode includes query (query or full)
|
||||
let searchResults: SearchResult[] = []
|
||||
if (mode !== "profile" && queryText) {
|
||||
const client = createSupermemoryClient({ apiKey, baseUrl })
|
||||
searchResults = await performSearch(
|
||||
client,
|
||||
containerTag,
|
||||
queryText,
|
||||
searchMode,
|
||||
searchLimit,
|
||||
logger,
|
||||
)
|
||||
|
||||
logger.info("Search completed", {
|
||||
containerTag,
|
||||
searchMode,
|
||||
searchLimit,
|
||||
resultCount: searchResults.length,
|
||||
queryText:
|
||||
queryText.substring(0, 100) + (queryText.length > 100 ? "..." : ""),
|
||||
})
|
||||
}
|
||||
|
||||
// Deduplicate profile memories
|
||||
const deduplicated = deduplicateMemories({
|
||||
static: memoriesResponse.profile.static,
|
||||
dynamic: memoriesResponse.profile.dynamic,
|
||||
searchResults: memoriesResponse.searchResults?.results,
|
||||
static: profileData?.profile.static,
|
||||
dynamic: profileData?.profile.dynamic,
|
||||
searchResults: searchResults.map((r) => ({ memory: r.content })),
|
||||
})
|
||||
|
||||
logger.debug("Memory deduplication completed", {
|
||||
static: {
|
||||
original: memoryCountStatic,
|
||||
original: profileData?.profile.static?.length || 0,
|
||||
deduplicated: deduplicated.static.length,
|
||||
},
|
||||
dynamic: {
|
||||
original: memoryCountDynamic,
|
||||
original: profileData?.profile.dynamic?.length || 0,
|
||||
deduplicated: deduplicated.dynamic.length,
|
||||
},
|
||||
searchResults: {
|
||||
original: memoriesResponse.searchResults?.results?.length,
|
||||
original: searchResults.length,
|
||||
deduplicated: deduplicated.searchResults?.length,
|
||||
},
|
||||
})
|
||||
|
||||
// Build user memories from profile (static + dynamic)
|
||||
const userMemories =
|
||||
mode !== "query"
|
||||
? convertProfileToMarkdown({
|
||||
|
|
@ -154,9 +317,11 @@ export const buildMemoriesText = async (
|
|||
searchResults: { results: [] },
|
||||
})
|
||||
: ""
|
||||
|
||||
// Build search results text
|
||||
const generalSearchMemories =
|
||||
mode !== "profile"
|
||||
? `Search results for user's recent message: \n${deduplicated.searchResults
|
||||
mode !== "profile" && deduplicated.searchResults.length > 0
|
||||
? `Search results for user's recent message:\n${deduplicated.searchResults
|
||||
.map((memory) => `- ${memory}`)
|
||||
.join("\n")}`
|
||||
: ""
|
||||
|
|
@ -164,7 +329,10 @@ export const buildMemoriesText = async (
|
|||
const promptData: MemoryPromptData = {
|
||||
userMemories,
|
||||
generalSearchMemories,
|
||||
searchResults: memoriesResponse.searchResults?.results ?? [],
|
||||
searchResults: searchResults.map((r) => ({
|
||||
memory: r.content,
|
||||
metadata: r.metadata,
|
||||
})),
|
||||
}
|
||||
|
||||
const memories = promptTemplate(promptData)
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue