feat(sf): /sf memory search — embedding-ranked memory query
New subcommand: /sf memory search "<query>". Routes through getRelevantMemoriesRanked, so when SF_LLM_GATEWAY_KEY is set the gateway embeds the query and ranks memories by cosine + static blend; without the key, gracefully degrades to static ranking. Header text indicates which path was taken so users know whether embeddings are live. This makes the embedding pipeline operator-discoverable — previously the only consumer was the silent execute-task injection path. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
eb5f7ef7b6
commit
e5787794f3
1 changed files with 39 additions and 0 deletions
|
|
@ -26,6 +26,7 @@ import {
|
|||
enforceMemoryCap,
|
||||
getActiveMemories,
|
||||
getActiveMemoriesRanked,
|
||||
getRelevantMemoriesRanked,
|
||||
supersedeMemory,
|
||||
} from "./memory-store.js";
|
||||
import { _getAdapter, isDbAvailable } from "./sf-db.js";
|
||||
|
|
@ -117,6 +118,9 @@ export async function handleMemory(
|
|||
case "list":
|
||||
handleList(ctx);
|
||||
return;
|
||||
case "search":
|
||||
await handleSearch(ctx, parsed);
|
||||
return;
|
||||
case "show":
|
||||
handleShow(ctx, parsed.positional[0]);
|
||||
return;
|
||||
|
|
@ -160,6 +164,7 @@ function usage(): string {
|
|||
return [
|
||||
"Usage: /sf memory <subcommand>",
|
||||
" list list recent active memories",
|
||||
' search "<query>" embedding-ranked search (gateway-aware; static fallback)',
|
||||
" show <MEM###> print one memory",
|
||||
" forget <MEM###> supersede a memory",
|
||||
" stats counts by category / sources / edges",
|
||||
|
|
@ -199,6 +204,40 @@ function handleList(ctx: ExtensionCommandContext): void {
|
|||
ctx.ui.notify(lines.join("\n"), "info");
|
||||
}
|
||||
|
||||
async function handleSearch(
|
||||
ctx: ExtensionCommandContext,
|
||||
parsed: MemoryCmdArgs,
|
||||
): Promise<void> {
|
||||
if (!isDbAvailable()) {
|
||||
ctx.ui.notify("No SF database available.", "warning");
|
||||
return;
|
||||
}
|
||||
const query = parsed.positional.join(" ").trim();
|
||||
if (!query) {
|
||||
ctx.ui.notify(
|
||||
'Usage: /sf memory search "<query>" (uses embeddings when SF_LLM_GATEWAY_KEY is set; static fallback otherwise)',
|
||||
"warning",
|
||||
);
|
||||
return;
|
||||
}
|
||||
const memories = await getRelevantMemoriesRanked(query, 10);
|
||||
if (memories.length === 0) {
|
||||
ctx.ui.notify("No matches.", "info");
|
||||
return;
|
||||
}
|
||||
const usingEmbeddings = !!process.env.SF_LLM_GATEWAY_KEY;
|
||||
const header = usingEmbeddings
|
||||
? `Top ${memories.length} memories for "${truncate(query, 60)}" (embedding-ranked):`
|
||||
: `Top ${memories.length} memories for "${truncate(query, 60)}" (static rank — set SF_LLM_GATEWAY_KEY for embeddings):`;
|
||||
const lines = [header];
|
||||
for (const m of memories) {
|
||||
lines.push(
|
||||
` [${m.id}] (${m.category}, conf ${m.confidence.toFixed(2)}) ${truncate(m.content, 100)}`,
|
||||
);
|
||||
}
|
||||
ctx.ui.notify(lines.join("\n"), "info");
|
||||
}
|
||||
|
||||
function handleShow(ctx: ExtensionCommandContext, id: string | undefined): void {
|
||||
if (!id) {
|
||||
ctx.ui.notify("Usage: /sf memory show <MEM###>", "warning");
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue