@anuma/sdk 6.10.0 → 6.12.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/expo/{chunk-4M56MKBV.mjs → chunk-732ZLOLA.mjs} +89 -32
- package/dist/expo/index.cjs +112 -38
- package/dist/expo/index.d.mts +109 -0
- package/dist/expo/index.d.ts +109 -0
- package/dist/expo/index.mjs +34 -18
- package/dist/expo/{recall-WIB7RQQZ.mjs → recall-PIATJ336.mjs} +1 -1
- package/dist/index.d.mts +15 -1
- package/dist/index.d.ts +15 -1
- package/dist/react/{chunk-EJ65X4GI.mjs → chunk-6IBFFWCE.mjs} +89 -32
- package/dist/react/index.cjs +112 -38
- package/dist/react/index.d.mts +109 -0
- package/dist/react/index.d.ts +109 -0
- package/dist/react/index.mjs +34 -18
- package/dist/react/{recall-FWILZSFH.mjs → recall-QUWN7DJ7.mjs} +1 -1
- package/dist/server/{chunk-I56324FR.mjs → chunk-7YTEE6JT.mjs} +89 -32
- package/dist/server/index.cjs +112 -38
- package/dist/server/index.d.mts +109 -0
- package/dist/server/index.d.ts +109 -0
- package/dist/server/index.mjs +34 -18
- package/dist/server/{recall-T7CNXTQ3.mjs → recall-HOVWMT4Q.mjs} +1 -1
- package/package.json +1 -1
package/dist/react/index.d.ts
CHANGED
|
@@ -6140,6 +6140,51 @@ declare function getMemoriesByEntityNamesOp(ctx: EntityOperationsContext, entity
|
|
|
6140
6140
|
*/
|
|
6141
6141
|
declare function getEntitiesByMemoryIdsOp(ctx: EntityOperationsContext, memoryIds: readonly string[]): Promise<Map<string, Set<string>>>;
|
|
6142
6142
|
|
|
6143
|
+
/**
|
|
6144
|
+
* Class-B task types — the vocabulary of the `X-Anuma-Task-Type` header.
|
|
6145
|
+
*
|
|
6146
|
+
* A Class-B flow has ONE fixed purpose and no conversation: extract memories
|
|
6147
|
+
* from a snippet, pick a topic, consolidate duplicates, classify a query. Today
|
|
6148
|
+
* each of those builds its own system prompt on the client and sends it as a
|
|
6149
|
+
* `role:system` message, which makes the prompt a suggestion rather than a
|
|
6150
|
+
* contract — a caller can reword it into "you are a general assistant" and get
|
|
6151
|
+
* general chat at internal-endpoint prices.
|
|
6152
|
+
*
|
|
6153
|
+
* The fix is that the SERVER owns the text and the client only names the task.
|
|
6154
|
+
* This header is the name. The portal allowlists it against its own copy of this
|
|
6155
|
+
* enum (ai-portal `internal/systemprompt/tasks.go`) and DROPS anything it does
|
|
6156
|
+
* not recognise, so an unknown value degrades to "no task declared" rather than
|
|
6157
|
+
* failing the request.
|
|
6158
|
+
*
|
|
6159
|
+
* ⚠ These string values are a wire contract with ai-portal. Adding one here
|
|
6160
|
+
* before the portal knows it is harmless (it is dropped); renaming or removing
|
|
6161
|
+
* one silently stops that flow being counted. Keep them in step with
|
|
6162
|
+
* `AllTaskTypes` on the portal side, and see ai-portal
|
|
6163
|
+
* `docs/BACKEND_OWNED_TASK_PROMPTS.md` for the rollout.
|
|
6164
|
+
*
|
|
6165
|
+
* ⚠ RELEASE ORDER. This union is a PUBLIC type, so a split is breaking in both
|
|
6166
|
+
* directions and has to be sequenced:
|
|
6167
|
+
*
|
|
6168
|
+
* 1. merge + publish this package;
|
|
6169
|
+
* 2. bump `@anuma/sdk` in the consumer and switch its call sites to the new
|
|
6170
|
+
* names in the same commit.
|
|
6171
|
+
*
|
|
6172
|
+
* A consumer on the OLD version cannot compile the new names, and a consumer on
|
|
6173
|
+
* the NEW version cannot compile the retired ones (`app_inspiration` is gone
|
|
6174
|
+
* here, replaced by the three `app_inspiration_*` names). Both halves of that
|
|
6175
|
+
* are type-only — the wire itself always degrades safely, because the portal
|
|
6176
|
+
* drops any value it does not recognise. Concretely: ai-memoryless-client#5782
|
|
6177
|
+
* is blocked on this publish for exactly that reason.
|
|
6178
|
+
*
|
|
6179
|
+
* Deliberately ABSENT: agent personas, `reflect()` when it answers the user, and
|
|
6180
|
+
* the app-idea chat. Those carry an open conversation with the user, so a fixed
|
|
6181
|
+
* server-owned prompt would be wrong rather than merely premature — the same
|
|
6182
|
+
* boundary {@link INTERNAL_FLOW_MARKER} draws.
|
|
6183
|
+
*
|
|
6184
|
+
* @public
|
|
6185
|
+
*/
|
|
6186
|
+
type TaskType = "title" | "commit_message" | "memory_extract" | "memory_dedup" | "memory_decay" | "memory_injection_check" | "memory_verify_support" | "memory_autosort" | "memory_topic" | "memory_consolidate" | "memory_decompose" | "memory_profile_synth" | "memory_graph" | "folder_context" | "selection_memory" | "summarize" | "style_analysis" | "classify_search" | "app_inspiration_generate" | "app_inspiration_trends" | "app_inspiration_remix" | "spotlight_image" | "slide_image" | "slide_video" | "slide_music" | "slide_sfx" | "media_video" | "media_audio" | "media_bg_removal" | "media_image_preset" | "tool_app_builder" | "tool_document_builder" | "tool_slides" | "tool_slides_image_gen" | "connector_guidance" | "cf_task" | "xbot_intent" | "xbot_council" | "scheduled_task";
|
|
6187
|
+
|
|
6143
6188
|
/**
|
|
6144
6189
|
* Shared portal LLM call for the memory pipeline.
|
|
6145
6190
|
*
|
|
@@ -6992,6 +7037,17 @@ interface RecallDiagnostics {
|
|
|
6992
7037
|
* never the cost.
|
|
6993
7038
|
*/
|
|
6994
7039
|
vaultRowsDecrypted?: number;
|
|
7040
|
+
/**
|
|
7041
|
+
* Rows the fact lane had to re-embed through the portal because their stored
|
|
7042
|
+
* vector was unusable — stale `embedding_model`, wrong dimension, or
|
|
7043
|
+
* unparseable. Absent when the fact lane didn't run.
|
|
7044
|
+
*
|
|
7045
|
+
* Expected to be 0 on a healthy vault: rows are embedded at write time and the
|
|
7046
|
+
* re-embed writes the current model back, so a persistently non-zero value
|
|
7047
|
+
* means the writeback is not sticking and every turn is paying for it. On the
|
|
7048
|
+
* LEGACY read path this batch is uncapped, so it can be the whole vault.
|
|
7049
|
+
*/
|
|
7050
|
+
vaultRowsEmbedded?: number;
|
|
6995
7051
|
/** Facts the fact lane returned (post-dedupe, pre-fusion). */
|
|
6996
7052
|
factCount: number;
|
|
6997
7053
|
/** Chunks the chunk lane returned (post-dedupe, pre-fusion). */
|
|
@@ -7004,6 +7060,37 @@ interface RecallDiagnostics {
|
|
|
7004
7060
|
prep: number;
|
|
7005
7061
|
/** Vault fact-lane search (`searchVaultMemoriesWithSize`). */
|
|
7006
7062
|
factLane: number;
|
|
7063
|
+
/**
|
|
7064
|
+
* The cross-encoder's share of {@link factLane} — wall-clock spent inside
|
|
7065
|
+
* `rerankPairs`. Billed even when the CE threw partway through, because a
|
|
7066
|
+
* rerank that burned three seconds and then failed still cost them.
|
|
7067
|
+
*
|
|
7068
|
+
* A SUBSET of `factLane`, not a sibling: read `factLane - rerank` for
|
|
7069
|
+
* everything else the lane did (query embed, vault read, fused ranking).
|
|
7070
|
+
* 0 when the CE did not run; `reranked` is what distinguishes that from a
|
|
7071
|
+
* rerank that cost nothing.
|
|
7072
|
+
*
|
|
7073
|
+
* Exists because #845 spent three rounds arguing about which stage inside
|
|
7074
|
+
* the fact lane dominated — whole-vault read, then admission window, then
|
|
7075
|
+
* the CE — with one aggregate number for all of them. Every hypothesis was
|
|
7076
|
+
* an inference; this makes the question a query.
|
|
7077
|
+
*/
|
|
7078
|
+
rerank: number;
|
|
7079
|
+
/**
|
|
7080
|
+
* The query embed's share of {@link factLane} — one portal round trip.
|
|
7081
|
+
*
|
|
7082
|
+
* Also a SUBSET of `factLane`. Together with `rerank` this makes the lane
|
|
7083
|
+
* decomposable: `factLane - queryEmbed - rerank` is the local work (vault
|
|
7084
|
+
* read, decrypt, BM25/cosine fusion), and each of the three implies a
|
|
7085
|
+
* completely different fix.
|
|
7086
|
+
*
|
|
7087
|
+
* 0 when the lane returned before embedding, which includes every empty
|
|
7088
|
+
* vault — `prepareVaultCandidates` short-circuits first. That is why the
|
|
7089
|
+
* fast `vault_size = 0` population never established a baseline for this
|
|
7090
|
+
* cost, and why the ~850ms floor on the smallest NON-empty vaults had no
|
|
7091
|
+
* attributable owner.
|
|
7092
|
+
*/
|
|
7093
|
+
queryEmbed: number;
|
|
7007
7094
|
/** Chunk-lane search (`searchChunksOp`). */
|
|
7008
7095
|
chunkLane: number;
|
|
7009
7096
|
/** Cross-lane RRF fusion + provenance dedup after both lanes. */
|
|
@@ -9348,6 +9435,28 @@ interface ReflectOptions extends RecallOptions, PortalLlmAuth {
|
|
|
9348
9435
|
maxTokens?: number;
|
|
9349
9436
|
/** Override the grounding system prompt. */
|
|
9350
9437
|
systemPrompt?: string;
|
|
9438
|
+
/**
|
|
9439
|
+
* Extra caller instruction to carry on the USER turn, between the question and
|
|
9440
|
+
* the evidence block (see the `userMessage` assembly below). This is the slot a
|
|
9441
|
+
* background caller uses to keep its per-request data OUT of the system message
|
|
9442
|
+
* without colliding with the numbered evidence list — profile-facet synthesis
|
|
9443
|
+
* puts its section label, guidance and response-field hint here so its system
|
|
9444
|
+
* half can stay fixed and server-ownable.
|
|
9445
|
+
*/
|
|
9446
|
+
userInstructions?: string;
|
|
9447
|
+
/**
|
|
9448
|
+
* Class-B task name for the `X-Anuma-Task-Type` header, or nothing.
|
|
9449
|
+
*
|
|
9450
|
+
* Deliberately OPTIONAL and unset by default. reflect() also answers the user's
|
|
9451
|
+
* OWN question, and that traffic is chat, not an internal flow — declaring a
|
|
9452
|
+
* task type unconditionally here would put an internal-flow name on real
|
|
9453
|
+
* conversation, which is the same boundary `INTERNAL_FLOW_MARKER` draws
|
|
9454
|
+
* (reflect is deliberately unmarked; its background caller marks its own
|
|
9455
|
+
* prompt — see ../internalFlowMarker.ts). So the name is per call: only a
|
|
9456
|
+
* caller with ONE fixed purpose passes
|
|
9457
|
+
* one, and today that is profile-facet synthesis (`memory_profile_synth`).
|
|
9458
|
+
*/
|
|
9459
|
+
taskType?: TaskType;
|
|
9351
9460
|
/** Endpoint for the answer LLM. */
|
|
9352
9461
|
baseUrl?: string;
|
|
9353
9462
|
/** Override fetch (for tests). */
|
package/dist/react/index.mjs
CHANGED
|
@@ -178,7 +178,7 @@ import {
|
|
|
178
178
|
updateVaultMemoryOp,
|
|
179
179
|
useEncryption,
|
|
180
180
|
vaultMemoryToStored
|
|
181
|
-
} from "./chunk-
|
|
181
|
+
} from "./chunk-6IBFFWCE.mjs";
|
|
182
182
|
import {
|
|
183
183
|
PiiRedactor,
|
|
184
184
|
createStreamingDeAnonymizer,
|
|
@@ -8949,11 +8949,13 @@ Respond with ONLY a single JSON object conforming to this JSON Schema, with no p
|
|
|
8949
8949
|
${JSON.stringify(
|
|
8950
8950
|
options.responseSchema
|
|
8951
8951
|
)}` : basePrompt;
|
|
8952
|
-
const userMessage =
|
|
8953
|
-
|
|
8954
|
-
|
|
8955
|
-
|
|
8956
|
-
|
|
8952
|
+
const userMessage = [
|
|
8953
|
+
`Question:
|
|
8954
|
+
${trimmed}`,
|
|
8955
|
+
...options.userInstructions ? [options.userInstructions] : [],
|
|
8956
|
+
`Memories (use only these as evidence):
|
|
8957
|
+
${evidence}`
|
|
8958
|
+
].join("\n\n");
|
|
8957
8959
|
const maxTokens = options.maxTokens ?? DEFAULT_MAX_TOKENS;
|
|
8958
8960
|
const fetchImpl = options.fetchFn ?? fetch;
|
|
8959
8961
|
const log = getLogger();
|
|
@@ -8967,7 +8969,13 @@ ${evidence}`;
|
|
|
8967
8969
|
try {
|
|
8968
8970
|
response = await fetchImpl(`${baseUrl}/api/v1/chat/completions`, {
|
|
8969
8971
|
method: "POST",
|
|
8970
|
-
|
|
8972
|
+
// No task type unless the caller named one — an undeclared task adds no
|
|
8973
|
+
// header at all, which is what keeps user-facing reflect() unlabelled.
|
|
8974
|
+
headers: {
|
|
8975
|
+
...authHeaders,
|
|
8976
|
+
...taskTypeHeader(options.taskType),
|
|
8977
|
+
"Content-Type": "application/json"
|
|
8978
|
+
},
|
|
8971
8979
|
body: JSON.stringify({
|
|
8972
8980
|
model,
|
|
8973
8981
|
// Modern OpenAI field; the portal reads only `max_completion_tokens`
|
|
@@ -9407,10 +9415,15 @@ async function synthesizeFacet(facet, ctx, options, prior) {
|
|
|
9407
9415
|
limit,
|
|
9408
9416
|
types: ["fact"],
|
|
9409
9417
|
maxTokens: DEFAULT_FACET_MAX_TOKENS,
|
|
9410
|
-
// Profile-facet synthesis is a background op, so it marks its own prompt
|
|
9411
|
-
// reflect() itself must
|
|
9412
|
-
// and that traffic is chat, not an internal flow. See
|
|
9413
|
-
|
|
9418
|
+
// Profile-facet synthesis is a background op, so it marks its own prompt AND
|
|
9419
|
+
// names its own task. reflect() itself must do neither — it also serves the
|
|
9420
|
+
// user's own questions, and that traffic is chat, not an internal flow. See
|
|
9421
|
+
// ../internalFlowMarker and ReflectOptions.taskType.
|
|
9422
|
+
taskType: "memory_profile_synth",
|
|
9423
|
+
systemPrompt: withInternalFlowMarker(FACET_SYSTEM_PROMPT),
|
|
9424
|
+
// The facet's label/guidance/response-fields, on the user turn — what keeps
|
|
9425
|
+
// the system half fixed and therefore server-ownable.
|
|
9426
|
+
userInstructions: buildFacetUserInstructions(facet),
|
|
9414
9427
|
responseSchema: facetResponseSchema(facet.key),
|
|
9415
9428
|
memories
|
|
9416
9429
|
});
|
|
@@ -9468,19 +9481,22 @@ function fallbackSection(facet, prior) {
|
|
|
9468
9481
|
stale: true
|
|
9469
9482
|
};
|
|
9470
9483
|
}
|
|
9471
|
-
|
|
9472
|
-
const structuredField = FACET_STRUCTURED_RESPONSE_HINT[facet.key] ?? "";
|
|
9473
|
-
return `You are writing the "${facet.label}" section of a person's shareable profile, using their private memories (supplied as evidence) as the only source of truth.
|
|
9474
|
-
|
|
9475
|
-
Task for this section:
|
|
9476
|
-
${facet.guidance}
|
|
9484
|
+
var FACET_SYSTEM_PROMPT = `You are writing one section of a person's shareable profile, using their private memories (supplied as evidence) as the only source of truth. The user turn names the section, states the task for it, and gives the exact JSON shape to respond in.
|
|
9477
9485
|
|
|
9478
9486
|
Rules:
|
|
9479
9487
|
- Ground every claim in the supplied memories \u2014 never invent, infer beyond, or embellish what they support.
|
|
9480
9488
|
- If the memories don't cover this section, return an empty summary with hasEvidence=false. Do not pad or guess.
|
|
9481
9489
|
- Write in third person about the person, in a natural voice suitable for a public profile (no "I"/"you", no name repetition).
|
|
9482
9490
|
- Be concise and specific; no preamble, hedging, or meta-commentary.
|
|
9483
|
-
- Respond as JSON
|
|
9491
|
+
- Respond as JSON in exactly the shape the user turn states, with no extra fields.`;
|
|
9492
|
+
function buildFacetUserInstructions(facet) {
|
|
9493
|
+
const structuredField = FACET_STRUCTURED_RESPONSE_HINT[facet.key] ?? "";
|
|
9494
|
+
return `Section: "${facet.label}"
|
|
9495
|
+
|
|
9496
|
+
Task for this section:
|
|
9497
|
+
${facet.guidance}
|
|
9498
|
+
|
|
9499
|
+
Respond as JSON: { "summary": <the section text, or "">, "hasEvidence": <true|false>${structuredField} }.`;
|
|
9484
9500
|
}
|
|
9485
9501
|
function extractFacetText(structured) {
|
|
9486
9502
|
if (structured && typeof structured === "object") {
|
|
@@ -19193,6 +19193,9 @@ async function decomposeQuery(query, options) {
|
|
|
19193
19193
|
...options.baseUrl !== void 0 && { baseUrl: options.baseUrl },
|
|
19194
19194
|
model: options.model ?? DEFAULT_MODEL,
|
|
19195
19195
|
systemPrompt: SYSTEM_PROMPT,
|
|
19196
|
+
// The query itself rides the user turn below, so SYSTEM_PROMPT is the whole
|
|
19197
|
+
// fixed instruction — which is what lets the portal own it outright.
|
|
19198
|
+
taskType: "memory_decompose",
|
|
19196
19199
|
// Wrap the bare query in a task framing so the model treats it as
|
|
19197
19200
|
// input to classify, not as a question to answer conversationally.
|
|
19198
19201
|
// Bare-query inputs caused Anthropic models to respond with prose
|
|
@@ -19307,9 +19310,9 @@ function recencyMultiplier(updatedAt, opts = {}) {
|
|
|
19307
19310
|
const updatedMs = updatedAt.getTime();
|
|
19308
19311
|
if (!Number.isFinite(updatedMs)) return noDate;
|
|
19309
19312
|
const now = opts.now ?? /* @__PURE__ */ new Date();
|
|
19310
|
-
const
|
|
19311
|
-
if (!Number.isFinite(
|
|
19312
|
-
const ageDays = Math.max(0, (
|
|
19313
|
+
const nowMs3 = now.getTime();
|
|
19314
|
+
if (!Number.isFinite(nowMs3)) return noDate;
|
|
19315
|
+
const ageDays = Math.max(0, (nowMs3 - updatedMs) / DAY_MS);
|
|
19313
19316
|
const perYearDecay = opts.perYearDecay ?? DEFAULT_PER_YEAR_DECAY;
|
|
19314
19317
|
const floor = opts.floor ?? DEFAULT_FLOOR;
|
|
19315
19318
|
const decay = perYearDecay * (ageDays / 365);
|
|
@@ -19655,6 +19658,7 @@ function createVaultEmbeddingCache(maxSize = DEFAULT_VAULT_CACHE_SIZE) {
|
|
|
19655
19658
|
}
|
|
19656
19659
|
|
|
19657
19660
|
// src/lib/memoryVault/searchTool.ts
|
|
19661
|
+
var nowMs = () => typeof performance !== "undefined" && typeof performance.now === "function" ? performance.now() : Date.now();
|
|
19658
19662
|
var warnedProgrammaticDecomposeIgnored = false;
|
|
19659
19663
|
function normalizeEventTimeKind(kind) {
|
|
19660
19664
|
return kind === "point" || kind === "range" || kind === "ongoing" ? kind : null;
|
|
@@ -19887,6 +19891,7 @@ async function rankFusedVaultMemoriesAsync(query, queryEmbedding, items, options
|
|
|
19887
19891
|
const rerankTopN = options.rerankTopN ?? 30;
|
|
19888
19892
|
const ceWeight = options.ceWeight ?? 0.1;
|
|
19889
19893
|
const headSlice = v2Ranked.slice(0, rerankTopN);
|
|
19894
|
+
const ceStart = nowMs();
|
|
19890
19895
|
try {
|
|
19891
19896
|
const reranked = await rerankPairs(
|
|
19892
19897
|
query,
|
|
@@ -19914,6 +19919,8 @@ async function rankFusedVaultMemoriesAsync(query, queryEmbedding, items, options
|
|
|
19914
19919
|
getLogger().warn("[memory/search] cross-encoder rerank failed; using V2 ranking", err);
|
|
19915
19920
|
}
|
|
19916
19921
|
combined = v2Ranked;
|
|
19922
|
+
} finally {
|
|
19923
|
+
if (options.rerankStats) options.rerankStats.ms += nowMs() - ceStart;
|
|
19917
19924
|
}
|
|
19918
19925
|
} else {
|
|
19919
19926
|
combined = v2Ranked;
|
|
@@ -20080,6 +20087,7 @@ async function rankComposite(originalQuery, originalQueryEmbedding, subQueries,
|
|
|
20080
20087
|
const ceWeight = options.ceWeight ?? 0.1;
|
|
20081
20088
|
const headSlice = combined.slice(0, rerankTopN);
|
|
20082
20089
|
const tailSlice = combined.slice(rerankTopN);
|
|
20090
|
+
const ceStart = nowMs();
|
|
20083
20091
|
try {
|
|
20084
20092
|
const reranked = await rerankPairs(
|
|
20085
20093
|
originalQuery,
|
|
@@ -20107,6 +20115,8 @@ async function rankComposite(originalQuery, originalQueryEmbedding, subQueries,
|
|
|
20107
20115
|
err
|
|
20108
20116
|
);
|
|
20109
20117
|
}
|
|
20118
|
+
} finally {
|
|
20119
|
+
if (options.rerankStats) options.rerankStats.ms += nowMs() - ceStart;
|
|
20110
20120
|
}
|
|
20111
20121
|
}
|
|
20112
20122
|
if (options?.mmr) {
|
|
@@ -20233,11 +20243,13 @@ async function buildProjectedCorpus(query, vaultCtx, embeddingOptions, cache, qu
|
|
|
20233
20243
|
rowsDecrypted: 0
|
|
20234
20244
|
};
|
|
20235
20245
|
}
|
|
20246
|
+
const queryEmbedStart = nowMs();
|
|
20236
20247
|
const queryEmbedding = await embedQueryOrDegrade(
|
|
20237
20248
|
query,
|
|
20238
20249
|
embeddingOptions,
|
|
20239
20250
|
opts.onEmbeddingDegraded
|
|
20240
20251
|
);
|
|
20252
|
+
if (opts.embedStats) opts.embedStats.queryMs += nowMs() - queryEmbedStart;
|
|
20241
20253
|
const embeddingsDegraded = queryEmbedding.length === 0;
|
|
20242
20254
|
const currentModel = embeddingOptions.model ?? DEFAULT_API_EMBEDDING_MODEL;
|
|
20243
20255
|
const vectored = [];
|
|
@@ -20290,6 +20302,7 @@ async function buildProjectedCorpus(query, vaultCtx, embeddingOptions, cache, qu
|
|
|
20290
20302
|
const laneRows = laneFetched.filter((m) => !isEncrypted(m.content));
|
|
20291
20303
|
if (laneRows.length > 0) {
|
|
20292
20304
|
let laneVecs;
|
|
20305
|
+
if (opts.embedStats) opts.embedStats.rowsEmbedded += laneRows.length;
|
|
20293
20306
|
try {
|
|
20294
20307
|
laneVecs = await generateEmbeddings(
|
|
20295
20308
|
laneRows.map((m) => m.content),
|
|
@@ -20368,6 +20381,10 @@ async function buildProjectedCorpus(query, vaultCtx, embeddingOptions, cache, qu
|
|
|
20368
20381
|
async function prepareVaultCandidates(query, vaultCtx, embeddingOptions, cache, searchOptions) {
|
|
20369
20382
|
if (!query || typeof query !== "string") {
|
|
20370
20383
|
return {
|
|
20384
|
+
// Nothing was read and nothing was embedded — the short-circuit fires
|
|
20385
|
+
// before either.
|
|
20386
|
+
queryEmbedMs: 0,
|
|
20387
|
+
rowsEmbedded: 0,
|
|
20371
20388
|
memories: [],
|
|
20372
20389
|
embeddedItems: [],
|
|
20373
20390
|
queryEmbedding: [],
|
|
@@ -20400,6 +20417,7 @@ async function prepareVaultCandidates(query, vaultCtx, embeddingOptions, cache,
|
|
|
20400
20417
|
embeddingsUnavailable = true;
|
|
20401
20418
|
embeddingFailure = true;
|
|
20402
20419
|
};
|
|
20420
|
+
const embedStats = { queryMs: 0, rowsEmbedded: 0 };
|
|
20403
20421
|
if (searchOptions?.decryptLast) {
|
|
20404
20422
|
const forceIncludeIds = [
|
|
20405
20423
|
...searchOptions.entityRanking ?? [],
|
|
@@ -20411,7 +20429,8 @@ async function prepareVaultCandidates(query, vaultCtx, embeddingOptions, cache,
|
|
|
20411
20429
|
admitFloor: ADMIT_FLOOR,
|
|
20412
20430
|
unembeddedCap: UNEMBEDDED_CAP,
|
|
20413
20431
|
...forceIncludeIds.length > 0 && { forceIncludeIds },
|
|
20414
|
-
onEmbeddingDegraded
|
|
20432
|
+
onEmbeddingDegraded,
|
|
20433
|
+
embedStats
|
|
20415
20434
|
});
|
|
20416
20435
|
if (corpus.vaultSize === 0) {
|
|
20417
20436
|
return {
|
|
@@ -20422,7 +20441,9 @@ async function prepareVaultCandidates(query, vaultCtx, embeddingOptions, cache,
|
|
|
20422
20441
|
embeddingsUnavailable: false,
|
|
20423
20442
|
embeddingFailure: false,
|
|
20424
20443
|
decryptLast: true,
|
|
20425
|
-
rowsDecrypted: corpus.rowsDecrypted
|
|
20444
|
+
rowsDecrypted: corpus.rowsDecrypted,
|
|
20445
|
+
queryEmbedMs: embedStats.queryMs,
|
|
20446
|
+
rowsEmbedded: embedStats.rowsEmbedded
|
|
20426
20447
|
};
|
|
20427
20448
|
}
|
|
20428
20449
|
({ memories, embeddedItems, queryEmbedding, vaultSize } = corpus);
|
|
@@ -20439,7 +20460,9 @@ async function prepareVaultCandidates(query, vaultCtx, embeddingOptions, cache,
|
|
|
20439
20460
|
decryptLast: true,
|
|
20440
20461
|
// Rows WERE decrypt-attempted even though none came back readable — that
|
|
20441
20462
|
// is the whole point of reporting attempts rather than successes.
|
|
20442
|
-
rowsDecrypted: corpus.rowsDecrypted
|
|
20463
|
+
rowsDecrypted: corpus.rowsDecrypted,
|
|
20464
|
+
queryEmbedMs: embedStats.queryMs,
|
|
20465
|
+
rowsEmbedded: embedStats.rowsEmbedded
|
|
20443
20466
|
};
|
|
20444
20467
|
}
|
|
20445
20468
|
} else {
|
|
@@ -20464,10 +20487,14 @@ async function prepareVaultCandidates(query, vaultCtx, embeddingOptions, cache,
|
|
|
20464
20487
|
embeddingsUnavailable: false,
|
|
20465
20488
|
embeddingFailure: false,
|
|
20466
20489
|
decryptLast: false,
|
|
20467
|
-
rowsDecrypted: loaded.length
|
|
20490
|
+
rowsDecrypted: loaded.length,
|
|
20491
|
+
queryEmbedMs: embedStats.queryMs,
|
|
20492
|
+
rowsEmbedded: embedStats.rowsEmbedded
|
|
20468
20493
|
};
|
|
20469
20494
|
}
|
|
20495
|
+
const legacyEmbedStart = nowMs();
|
|
20470
20496
|
queryEmbedding = await embedQueryOrDegrade(query, embeddingOptions, onEmbeddingDegraded);
|
|
20497
|
+
embedStats.queryMs += nowMs() - legacyEmbedStart;
|
|
20471
20498
|
const currentModel = embeddingOptions.model ?? DEFAULT_API_EMBEDDING_MODEL;
|
|
20472
20499
|
const uncachedTexts = [];
|
|
20473
20500
|
const uncachedIndices = [];
|
|
@@ -20501,6 +20528,7 @@ async function prepareVaultCandidates(query, vaultCtx, embeddingOptions, cache,
|
|
|
20501
20528
|
}
|
|
20502
20529
|
if (uncachedTexts.length > 0) {
|
|
20503
20530
|
let newEmbeddings;
|
|
20531
|
+
embedStats.rowsEmbedded += uncachedTexts.length;
|
|
20504
20532
|
try {
|
|
20505
20533
|
newEmbeddings = await generateEmbeddings(uncachedTexts, embeddingOptions);
|
|
20506
20534
|
} catch (err) {
|
|
@@ -20544,6 +20572,8 @@ async function prepareVaultCandidates(query, vaultCtx, embeddingOptions, cache,
|
|
|
20544
20572
|
embeddingsUnavailable = true;
|
|
20545
20573
|
}
|
|
20546
20574
|
return {
|
|
20575
|
+
queryEmbedMs: embedStats.queryMs,
|
|
20576
|
+
rowsEmbedded: embedStats.rowsEmbedded,
|
|
20547
20577
|
memories,
|
|
20548
20578
|
embeddedItems,
|
|
20549
20579
|
queryEmbedding,
|
|
@@ -20594,11 +20624,15 @@ async function rankPreparedVaultCandidates(query, prepared, embeddingOptions, se
|
|
|
20594
20624
|
results: results2,
|
|
20595
20625
|
vaultSize: out.vaultSize,
|
|
20596
20626
|
reranked: out.reranked ?? false,
|
|
20627
|
+
// Read straight off the shared stats object rather than being passed in at
|
|
20628
|
+
// each call site: every return in this function funnels through here, so a
|
|
20629
|
+
// path added later reports the CE bill without having to remember to.
|
|
20630
|
+
rerankMs: rerankStats.ms,
|
|
20597
20631
|
hadV2Head: out.hadV2Head ?? false,
|
|
20598
20632
|
embeddingsUnavailable
|
|
20599
20633
|
};
|
|
20600
20634
|
};
|
|
20601
|
-
const rerankStats = { applied: false };
|
|
20635
|
+
const rerankStats = { applied: false, ms: 0 };
|
|
20602
20636
|
const useFusion = searchOptions?.useFusion ?? true;
|
|
20603
20637
|
const tuning = {
|
|
20604
20638
|
...searchOptions?.recencyAlpha !== void 0 && { recencyAlpha: searchOptions.recencyAlpha },
|
|
@@ -20717,13 +20751,16 @@ async function searchVaultMemoriesWithSize(query, vaultCtx, embeddingOptions, ca
|
|
|
20717
20751
|
results: [],
|
|
20718
20752
|
vaultSize: 0,
|
|
20719
20753
|
reranked: false,
|
|
20754
|
+
rerankMs: 0,
|
|
20720
20755
|
hadV2Head: false,
|
|
20721
20756
|
embeddingsUnavailable: false,
|
|
20722
20757
|
rankedOnCosine: false,
|
|
20723
20758
|
// No storage read happened, so nothing was decrypted; report the path the
|
|
20724
20759
|
// caller asked for so this turn is still attributable.
|
|
20725
20760
|
decryptLast: !!searchOptions?.decryptLast,
|
|
20726
|
-
rowsDecrypted: 0
|
|
20761
|
+
rowsDecrypted: 0,
|
|
20762
|
+
queryEmbedMs: 0,
|
|
20763
|
+
rowsEmbedded: 0
|
|
20727
20764
|
};
|
|
20728
20765
|
}
|
|
20729
20766
|
if (searchOptions?.decompose === "llm" && normalizeSubQueries(searchOptions.subQueries).length < 2 && !warnedProgrammaticDecomposeIgnored) {
|
|
@@ -20744,11 +20781,14 @@ async function searchVaultMemoriesWithSize(query, vaultCtx, embeddingOptions, ca
|
|
|
20744
20781
|
results: [],
|
|
20745
20782
|
vaultSize: 0,
|
|
20746
20783
|
reranked: false,
|
|
20784
|
+
rerankMs: 0,
|
|
20747
20785
|
hadV2Head: false,
|
|
20748
20786
|
embeddingsUnavailable: prepared.embeddingsUnavailable,
|
|
20749
20787
|
rankedOnCosine: false,
|
|
20750
20788
|
decryptLast: prepared.decryptLast,
|
|
20751
|
-
rowsDecrypted: prepared.rowsDecrypted
|
|
20789
|
+
rowsDecrypted: prepared.rowsDecrypted,
|
|
20790
|
+
queryEmbedMs: prepared.queryEmbedMs,
|
|
20791
|
+
rowsEmbedded: prepared.rowsEmbedded
|
|
20752
20792
|
};
|
|
20753
20793
|
}
|
|
20754
20794
|
if (prepared.memories.length === 0) {
|
|
@@ -20756,11 +20796,14 @@ async function searchVaultMemoriesWithSize(query, vaultCtx, embeddingOptions, ca
|
|
|
20756
20796
|
results: [],
|
|
20757
20797
|
vaultSize: prepared.vaultSize,
|
|
20758
20798
|
reranked: false,
|
|
20799
|
+
rerankMs: 0,
|
|
20759
20800
|
hadV2Head: false,
|
|
20760
20801
|
embeddingsUnavailable: prepared.embeddingsUnavailable,
|
|
20761
20802
|
rankedOnCosine: false,
|
|
20762
20803
|
decryptLast: prepared.decryptLast,
|
|
20763
|
-
rowsDecrypted: prepared.rowsDecrypted
|
|
20804
|
+
rowsDecrypted: prepared.rowsDecrypted,
|
|
20805
|
+
queryEmbedMs: prepared.queryEmbedMs,
|
|
20806
|
+
rowsEmbedded: prepared.rowsEmbedded
|
|
20764
20807
|
};
|
|
20765
20808
|
}
|
|
20766
20809
|
const ranked = await rankPreparedVaultCandidates(
|
|
@@ -20773,7 +20816,9 @@ async function searchVaultMemoriesWithSize(query, vaultCtx, embeddingOptions, ca
|
|
|
20773
20816
|
...ranked,
|
|
20774
20817
|
rankedOnCosine: !ranked.embeddingsUnavailable,
|
|
20775
20818
|
decryptLast: prepared.decryptLast,
|
|
20776
|
-
rowsDecrypted: prepared.rowsDecrypted
|
|
20819
|
+
rowsDecrypted: prepared.rowsDecrypted,
|
|
20820
|
+
queryEmbedMs: prepared.queryEmbedMs,
|
|
20821
|
+
rowsEmbedded: prepared.rowsEmbedded
|
|
20777
20822
|
};
|
|
20778
20823
|
}
|
|
20779
20824
|
async function searchVaultMemories(query, vaultCtx, embeddingOptions, cache, searchOptions) {
|
|
@@ -20878,7 +20923,7 @@ function createMemoryVaultSearchTool(vaultCtx, embeddingOptions, cache, searchOp
|
|
|
20878
20923
|
subQueries = decomp.subQueries;
|
|
20879
20924
|
}
|
|
20880
20925
|
}
|
|
20881
|
-
const { recall: recall2 } = await import("./recall-
|
|
20926
|
+
const { recall: recall2 } = await import("./recall-HOVWMT4Q.mjs");
|
|
20882
20927
|
let recallDegraded = [];
|
|
20883
20928
|
const result = await recall2(
|
|
20884
20929
|
query,
|
|
@@ -21131,29 +21176,29 @@ async function traverseGraphLane(query, entityCtx, options = {}) {
|
|
|
21131
21176
|
var DEFAULT_REFINER_MODEL = "inclusionai/ling-2.6-flash";
|
|
21132
21177
|
var DEFAULT_REFINER_ATTEMPTS = 1;
|
|
21133
21178
|
var DEFAULT_REFINER_TOTAL_TIMEOUT_MS = 8e3;
|
|
21179
|
+
var REFINER_SYSTEM_PROMPT = `You help a memory-retrieval system decide which related topics to explore.
|
|
21180
|
+
|
|
21181
|
+
Given a user's question and a numbered list of candidate topics/entities linked to memories found so far, pick the ones most likely to lead to memories that help ANSWER the question. Never return more than the maximum the user turn asks for. Prefer topics semantically related to the question; ignore incidental ones.
|
|
21182
|
+
|
|
21183
|
+
Output strict JSON, no prose: { "expand": [<entity names to expand, verbatim from the list>] }`;
|
|
21134
21184
|
function createLlmNeighborRefiner(options) {
|
|
21135
21185
|
return {
|
|
21136
21186
|
async refine(query, candidates, limit) {
|
|
21137
21187
|
if (candidates.length === 0) return [];
|
|
21138
21188
|
const numbered = candidates.map((name, i) => `[${i + 1}] ${name}`).join("\n");
|
|
21139
|
-
const systemPrompt = `You help a memory-retrieval system decide which related topics to explore.
|
|
21140
|
-
|
|
21141
|
-
Given a user's question and a numbered list of candidate topics/entities linked to memories found so far, pick the ones most likely to lead to memories that help ANSWER the question. Choose at most ${limit}. Prefer topics semantically related to the question; ignore incidental ones.
|
|
21142
|
-
|
|
21143
|
-
Output strict JSON, no prose: { "expand": [<entity names to expand, verbatim from the list>] }`;
|
|
21144
21189
|
const parsed = await callPortalJsonCompletion({
|
|
21145
21190
|
...options.apiKey !== void 0 && { apiKey: options.apiKey },
|
|
21146
21191
|
...options.getToken !== void 0 && { getToken: options.getToken },
|
|
21147
21192
|
...options.baseUrl !== void 0 && { baseUrl: options.baseUrl },
|
|
21148
21193
|
taskType: "memory_graph",
|
|
21149
21194
|
model: options.model ?? DEFAULT_REFINER_MODEL,
|
|
21150
|
-
systemPrompt,
|
|
21195
|
+
systemPrompt: REFINER_SYSTEM_PROMPT,
|
|
21151
21196
|
userMessage: `Question: ${query}
|
|
21152
21197
|
|
|
21153
21198
|
Candidate topics:
|
|
21154
21199
|
${numbered}
|
|
21155
21200
|
|
|
21156
|
-
Which should be expanded
|
|
21201
|
+
Which should be expanded? Choose at most ${limit}.`,
|
|
21157
21202
|
tag: "memory/graph-refine",
|
|
21158
21203
|
maxAttempts: options.maxAttempts ?? DEFAULT_REFINER_ATTEMPTS,
|
|
21159
21204
|
totalTimeoutMs: options.totalTimeoutMs ?? DEFAULT_REFINER_TOTAL_TIMEOUT_MS,
|
|
@@ -21418,7 +21463,7 @@ function scoreEventTimeOverlap(memoryStart, memoryEnd, memoryKind, window2) {
|
|
|
21418
21463
|
var DEFAULT_LIMIT = 8;
|
|
21419
21464
|
var DEFAULT_BUDGET = "low";
|
|
21420
21465
|
var DEFAULT_FACT_MIN_SCORE = 0.1;
|
|
21421
|
-
var
|
|
21466
|
+
var nowMs2 = () => typeof performance !== "undefined" && typeof performance.now === "function" ? performance.now() : Date.now();
|
|
21422
21467
|
var DEFAULT_CHUNK_MIN_SCORE = 0.5;
|
|
21423
21468
|
function flagsForBudget(budget) {
|
|
21424
21469
|
switch (budget) {
|
|
@@ -21451,7 +21496,7 @@ async function recall(query, ctx, options = {}) {
|
|
|
21451
21496
|
const flags = flagsForBudget(usedBudget);
|
|
21452
21497
|
const normalizedFacets = normalizeSubQueries(options.subQueries);
|
|
21453
21498
|
const subQueries = normalizedFacets.length >= 2 ? normalizedFacets : void 0;
|
|
21454
|
-
const t0 =
|
|
21499
|
+
const t0 = nowMs2();
|
|
21455
21500
|
let prepMs = 0;
|
|
21456
21501
|
let factLaneMs = 0;
|
|
21457
21502
|
let chunkLaneMs = 0;
|
|
@@ -21460,6 +21505,9 @@ async function recall(query, ctx, options = {}) {
|
|
|
21460
21505
|
const chunkResults = [];
|
|
21461
21506
|
let vaultSize;
|
|
21462
21507
|
let didRerank = false;
|
|
21508
|
+
let rerankMs = 0;
|
|
21509
|
+
let queryEmbedMs = 0;
|
|
21510
|
+
let vaultRowsEmbedded;
|
|
21463
21511
|
let hadV2Head = false;
|
|
21464
21512
|
let embeddingsUnavailable = false;
|
|
21465
21513
|
let chunkEmbedFailed = false;
|
|
@@ -21484,12 +21532,15 @@ async function recall(query, ctx, options = {}) {
|
|
|
21484
21532
|
...vaultSize !== void 0 && { vaultSize },
|
|
21485
21533
|
...decryptLastRan !== void 0 && { decryptLast: decryptLastRan },
|
|
21486
21534
|
...vaultRowsDecrypted !== void 0 && { vaultRowsDecrypted },
|
|
21535
|
+
...vaultRowsEmbedded !== void 0 && { vaultRowsEmbedded },
|
|
21487
21536
|
factCount: factResults.length,
|
|
21488
21537
|
chunkCount: chunkResults.length,
|
|
21489
21538
|
timings: {
|
|
21490
|
-
total:
|
|
21539
|
+
total: nowMs2() - t0,
|
|
21491
21540
|
prep: prepMs,
|
|
21492
21541
|
factLane: factLaneMs,
|
|
21542
|
+
rerank: rerankMs,
|
|
21543
|
+
queryEmbed: queryEmbedMs,
|
|
21493
21544
|
chunkLane: chunkLaneMs,
|
|
21494
21545
|
fuse: fuseMs
|
|
21495
21546
|
},
|
|
@@ -21507,7 +21558,7 @@ async function recall(query, ctx, options = {}) {
|
|
|
21507
21558
|
const needsChunkEmbedding = types.includes("chunk") && ctx.storageCtx;
|
|
21508
21559
|
const wantsTemporal = types.includes("fact") && ctx.vaultCtx;
|
|
21509
21560
|
const graphRefiner = flags.traverse && options.graphRefine && options.decomposeOptions ? createLlmNeighborRefiner(options.decomposeOptions) : void 0;
|
|
21510
|
-
const prepStart =
|
|
21561
|
+
const prepStart = nowMs2();
|
|
21511
21562
|
const [queryEmbedding, entityRanking, temporalRanking] = await Promise.all([
|
|
21512
21563
|
// The chunk lane is cosine-only — `searchChunksOp` needs a real vector, and
|
|
21513
21564
|
// there is no lexical fallback for it — so an embeddings outage must SKIP the
|
|
@@ -21545,14 +21596,17 @@ async function recall(query, ctx, options = {}) {
|
|
|
21545
21596
|
),
|
|
21546
21597
|
wantsTemporal ? safeLane("temporal", () => buildTemporalLaneRanking(query, ctx.vaultCtx, options.now)) : Promise.resolve([])
|
|
21547
21598
|
]);
|
|
21548
|
-
prepMs =
|
|
21599
|
+
prepMs = nowMs2() - prepStart;
|
|
21549
21600
|
if (types.includes("fact") && ctx.vaultCtx && ctx.vaultCache) {
|
|
21550
|
-
const factStart =
|
|
21601
|
+
const factStart = nowMs2();
|
|
21551
21602
|
const vaultMinScore = options.minScore ?? DEFAULT_FACT_MIN_SCORE;
|
|
21552
21603
|
const {
|
|
21553
21604
|
results,
|
|
21554
21605
|
vaultSize: size,
|
|
21555
21606
|
reranked,
|
|
21607
|
+
rerankMs: factRerankMs,
|
|
21608
|
+
queryEmbedMs: factQueryEmbedMs,
|
|
21609
|
+
rowsEmbedded: factRowsEmbedded,
|
|
21556
21610
|
hadV2Head: v2Head,
|
|
21557
21611
|
embeddingsUnavailable: factEmbeddingsUnavailable,
|
|
21558
21612
|
rankedOnCosine: factRankedOnCosine,
|
|
@@ -21611,16 +21665,19 @@ async function recall(query, ctx, options = {}) {
|
|
|
21611
21665
|
);
|
|
21612
21666
|
vaultSize = size;
|
|
21613
21667
|
didRerank = reranked;
|
|
21668
|
+
rerankMs = factRerankMs;
|
|
21669
|
+
queryEmbedMs = factQueryEmbedMs;
|
|
21670
|
+
vaultRowsEmbedded = factRowsEmbedded;
|
|
21614
21671
|
hadV2Head = v2Head;
|
|
21615
21672
|
if (factEmbeddingsUnavailable) embeddingsUnavailable = true;
|
|
21616
21673
|
factLaneRankedOnCosine = factRankedOnCosine;
|
|
21617
21674
|
decryptLastRan = factDecryptLast;
|
|
21618
21675
|
vaultRowsDecrypted = factRowsDecrypted;
|
|
21619
|
-
factLaneMs =
|
|
21676
|
+
factLaneMs = nowMs2() - factStart;
|
|
21620
21677
|
}
|
|
21621
21678
|
if (chunkEmbedFailed && !factLaneRankedOnCosine) embeddingsUnavailable = true;
|
|
21622
21679
|
if (types.includes("chunk") && ctx.storageCtx && queryEmbedding) {
|
|
21623
|
-
const chunkStart =
|
|
21680
|
+
const chunkStart = nowMs2();
|
|
21624
21681
|
const chunkMinScore = options.minScore ?? DEFAULT_CHUNK_MIN_SCORE;
|
|
21625
21682
|
const results = await searchChunksOp(ctx.storageCtx, queryEmbedding, {
|
|
21626
21683
|
limit: types.includes("fact") ? Math.max(limit * 2, 16) : limit,
|
|
@@ -21637,9 +21694,9 @@ async function recall(query, ctx, options = {}) {
|
|
|
21637
21694
|
(r) => r.chunkText.trim()
|
|
21638
21695
|
)
|
|
21639
21696
|
);
|
|
21640
|
-
chunkLaneMs =
|
|
21697
|
+
chunkLaneMs = nowMs2() - chunkStart;
|
|
21641
21698
|
}
|
|
21642
|
-
const fuseStart =
|
|
21699
|
+
const fuseStart = nowMs2();
|
|
21643
21700
|
if (types.length === 1 || factResults.length === 0 || chunkResults.length === 0) {
|
|
21644
21701
|
const memories2 = [
|
|
21645
21702
|
...factResults.map((r) => toFactMemory(r, options.now)),
|
|
@@ -21647,7 +21704,7 @@ async function recall(query, ctx, options = {}) {
|
|
|
21647
21704
|
];
|
|
21648
21705
|
memories2.sort((a, b) => b.score - a.score);
|
|
21649
21706
|
const candidateCount = factResults.length + chunkResults.length;
|
|
21650
|
-
fuseMs =
|
|
21707
|
+
fuseMs = nowMs2() - fuseStart;
|
|
21651
21708
|
emitDiagnostics(candidateCount);
|
|
21652
21709
|
return {
|
|
21653
21710
|
memories: memories2.slice(0, limit),
|
|
@@ -21702,7 +21759,7 @@ async function recall(query, ctx, options = {}) {
|
|
|
21702
21759
|
suppressed = next;
|
|
21703
21760
|
memories = selectWith(suppressed);
|
|
21704
21761
|
}
|
|
21705
|
-
fuseMs =
|
|
21762
|
+
fuseMs = nowMs2() - fuseStart;
|
|
21706
21763
|
emitDiagnostics(byId.size);
|
|
21707
21764
|
return {
|
|
21708
21765
|
memories,
|