@anuma/sdk 6.19.2 → 6.20.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/index.cjs +84 -32
- package/dist/expo/index.d.mts +108 -1
- package/dist/expo/index.d.ts +108 -1
- package/dist/expo/index.mjs +84 -32
- package/dist/react/index.cjs +84 -32
- package/dist/react/index.d.mts +108 -1
- package/dist/react/index.d.ts +108 -1
- package/dist/react/index.mjs +84 -32
- package/dist/server/index.cjs +84 -32
- package/dist/server/index.d.mts +108 -1
- package/dist/server/index.d.ts +108 -1
- package/dist/server/index.mjs +84 -32
- package/package.json +1 -1
package/dist/react/index.mjs
CHANGED
|
@@ -7464,6 +7464,7 @@ async function retain(content, ctx, options = {}) {
|
|
|
7464
7464
|
const threshold = options.autoMergeThreshold ?? DEFAULT_AUTO_MERGE_THRESHOLD;
|
|
7465
7465
|
const resolvedScope = options.scope ?? DEFAULT_SCOPE;
|
|
7466
7466
|
let supersedeTargetIds = [];
|
|
7467
|
+
let consolidationDecidedCreate = false;
|
|
7467
7468
|
let supersedeContent;
|
|
7468
7469
|
let prepared;
|
|
7469
7470
|
if (enableAutoMerge) {
|
|
@@ -7491,8 +7492,12 @@ async function retain(content, ctx, options = {}) {
|
|
|
7491
7492
|
const outcome = await tryConsolidate(trimmed, ctx, options, prepared);
|
|
7492
7493
|
if (outcome) {
|
|
7493
7494
|
if ("done" in outcome) return outcome.done;
|
|
7494
|
-
|
|
7495
|
-
|
|
7495
|
+
if ("create" in outcome) {
|
|
7496
|
+
consolidationDecidedCreate = true;
|
|
7497
|
+
} else {
|
|
7498
|
+
supersedeTargetIds = outcome.supersede;
|
|
7499
|
+
supersedeContent = outcome.content;
|
|
7500
|
+
}
|
|
7496
7501
|
}
|
|
7497
7502
|
}
|
|
7498
7503
|
if (supersedeTargetIds.length === 0) {
|
|
@@ -7542,7 +7547,14 @@ async function retain(content, ctx, options = {}) {
|
|
|
7542
7547
|
action: "merge",
|
|
7543
7548
|
memoryId: targetId,
|
|
7544
7549
|
targetId,
|
|
7545
|
-
proofCount: updated.proofCount ?? (existing.proofCount ?? 1) + 1
|
|
7550
|
+
proofCount: updated.proofCount ?? (existing.proofCount ?? 1) + 1,
|
|
7551
|
+
similarity: matches[0].similarity,
|
|
7552
|
+
// Stage 1 may have run and explicitly said `create`, and Stage 2
|
|
7553
|
+
// then found a strict-cosine match anyway. Reporting the decision
|
|
7554
|
+
// here is what makes that DISAGREEMENT visible — without it the
|
|
7555
|
+
// model's create silently vanished from the distribution whenever
|
|
7556
|
+
// the cosine stage won.
|
|
7557
|
+
...consolidationDecidedCreate && { consolidation: "create" }
|
|
7546
7558
|
};
|
|
7547
7559
|
}
|
|
7548
7560
|
await assertMergeTargetGoneOrThrow(ctx, targetId);
|
|
@@ -7555,12 +7567,18 @@ async function retain(content, ctx, options = {}) {
|
|
|
7555
7567
|
const embedding = reusableQueryEmbedding ?? await generateEmbedding(contentToWrite, ctx.embeddingOptions);
|
|
7556
7568
|
const embeddingModel = ctx.embeddingOptions.model ?? DEFAULT_API_EMBEDDING_MODEL;
|
|
7557
7569
|
if (options.respectTombstones) {
|
|
7558
|
-
const
|
|
7570
|
+
const tombstone = await findTombstoneMatch(embedding, embeddingModel, resolvedScope, ctx, {
|
|
7559
7571
|
threshold,
|
|
7560
7572
|
folderId: options.folderId
|
|
7561
7573
|
});
|
|
7562
|
-
if (
|
|
7563
|
-
return {
|
|
7574
|
+
if (tombstone) {
|
|
7575
|
+
return {
|
|
7576
|
+
action: "suppressed",
|
|
7577
|
+
memoryId: tombstone.id,
|
|
7578
|
+
tombstoneId: tombstone.id,
|
|
7579
|
+
proofCount: 0,
|
|
7580
|
+
similarity: tombstone.similarity
|
|
7581
|
+
};
|
|
7564
7582
|
}
|
|
7565
7583
|
}
|
|
7566
7584
|
const createOpts = {
|
|
@@ -7627,7 +7645,8 @@ async function retain(content, ctx, options = {}) {
|
|
|
7627
7645
|
action: "supersede",
|
|
7628
7646
|
memoryId: created2.uniqueId,
|
|
7629
7647
|
targetId: primaryTargetId,
|
|
7630
|
-
proofCount: 1
|
|
7648
|
+
proofCount: 1,
|
|
7649
|
+
consolidation: "supersede"
|
|
7631
7650
|
};
|
|
7632
7651
|
}
|
|
7633
7652
|
notifyConsolidationFallback(
|
|
@@ -7641,7 +7660,8 @@ async function retain(content, ctx, options = {}) {
|
|
|
7641
7660
|
return {
|
|
7642
7661
|
action: "create",
|
|
7643
7662
|
memoryId: created.uniqueId,
|
|
7644
|
-
proofCount: 1
|
|
7663
|
+
proofCount: 1,
|
|
7664
|
+
...consolidationDecidedCreate && { consolidation: "create" }
|
|
7645
7665
|
};
|
|
7646
7666
|
}
|
|
7647
7667
|
function unionStrings(a, b) {
|
|
@@ -7671,7 +7691,7 @@ async function findTombstoneMatch(embedding, embeddingModel, scope, ctx, opts) {
|
|
|
7671
7691
|
bestId = row.uniqueId;
|
|
7672
7692
|
}
|
|
7673
7693
|
}
|
|
7674
|
-
return bestId;
|
|
7694
|
+
return bestId === null ? null : { id: bestId, similarity: bestSim };
|
|
7675
7695
|
}
|
|
7676
7696
|
function pickEventTimeUpdate(existing, incoming) {
|
|
7677
7697
|
if (incoming === void 0 || incoming === null) return void 0;
|
|
@@ -7713,7 +7733,7 @@ async function tryConsolidate(trimmed, ctx, options, prepared) {
|
|
|
7713
7733
|
}));
|
|
7714
7734
|
const { consolidateMemory: doConsolidate } = await import("./consolidate-URHGJT7I.mjs");
|
|
7715
7735
|
const decision = await doConsolidate(trimmed, candidates, consolidateOptions);
|
|
7716
|
-
if (decision.action === "create") return null;
|
|
7736
|
+
if (decision.action === "create") return decision.fallbackReason ? null : { create: true };
|
|
7717
7737
|
if (decision.action === "supersede" && decision.content) {
|
|
7718
7738
|
const requestedIds = decision.targetIds?.length ? decision.targetIds : decision.targetId ? [decision.targetId] : [];
|
|
7719
7739
|
if (requestedIds.length === 0) return null;
|
|
@@ -7772,7 +7792,8 @@ async function tryConsolidate(trimmed, ctx, options, prepared) {
|
|
|
7772
7792
|
action: "merge",
|
|
7773
7793
|
memoryId: decision.targetId,
|
|
7774
7794
|
targetId: decision.targetId,
|
|
7775
|
-
proofCount: updated.proofCount ?? (existing.proofCount ?? 1) + 1
|
|
7795
|
+
proofCount: updated.proofCount ?? (existing.proofCount ?? 1) + 1,
|
|
7796
|
+
consolidation: "noop"
|
|
7776
7797
|
}
|
|
7777
7798
|
};
|
|
7778
7799
|
}
|
|
@@ -7825,7 +7846,8 @@ async function tryConsolidate(trimmed, ctx, options, prepared) {
|
|
|
7825
7846
|
action: "update",
|
|
7826
7847
|
memoryId: decision.targetId,
|
|
7827
7848
|
targetId: decision.targetId,
|
|
7828
|
-
proofCount: updated.proofCount ?? (existing.proofCount ?? 1) + 1
|
|
7849
|
+
proofCount: updated.proofCount ?? (existing.proofCount ?? 1) + 1,
|
|
7850
|
+
consolidation: "update"
|
|
7829
7851
|
}
|
|
7830
7852
|
};
|
|
7831
7853
|
}
|
|
@@ -7967,6 +7989,11 @@ Extract durable user facts.`,
|
|
|
7967
7989
|
fallbackSourceId,
|
|
7968
7990
|
options.userIdentity ?? []
|
|
7969
7991
|
);
|
|
7992
|
+
const rawCandidates = parsed.candidates;
|
|
7993
|
+
options.onCandidatesParsed?.({
|
|
7994
|
+
rawCount: Array.isArray(rawCandidates) ? rawCandidates.length : 0,
|
|
7995
|
+
validCount: candidates.length
|
|
7996
|
+
});
|
|
7970
7997
|
if (!redactor) return candidates;
|
|
7971
7998
|
const restored = restoreCandidates(candidates, redactor, options.userIdentity ?? []);
|
|
7972
7999
|
if (candidates.length > 0 && restored.length === 0) {
|
|
@@ -8003,8 +8030,12 @@ async function extractAndRetain(messages, retainCtx, options) {
|
|
|
8003
8030
|
let exhaustedEmpty = false;
|
|
8004
8031
|
let exhaustedFailure;
|
|
8005
8032
|
let droppedAfterRedaction = false;
|
|
8033
|
+
let rawCandidateCount = 0;
|
|
8034
|
+
let validCandidateCount = 0;
|
|
8006
8035
|
const callerOnExhaustedEmpty = options.extract.onExhaustedEmpty;
|
|
8007
8036
|
const callerOnCandidatesDropped = options.extract.onCandidatesDropped;
|
|
8037
|
+
const callerOnCandidatesParsed = options.extract.onCandidatesParsed;
|
|
8038
|
+
const tExtract = Date.now();
|
|
8008
8039
|
const candidates = await extractFacts(messages, {
|
|
8009
8040
|
...options.extract,
|
|
8010
8041
|
onExhaustedEmpty: (failure) => {
|
|
@@ -8015,8 +8046,14 @@ async function extractAndRetain(messages, retainCtx, options) {
|
|
|
8015
8046
|
onCandidatesDropped: () => {
|
|
8016
8047
|
droppedAfterRedaction = true;
|
|
8017
8048
|
callerOnCandidatesDropped?.();
|
|
8049
|
+
},
|
|
8050
|
+
onCandidatesParsed: (stats) => {
|
|
8051
|
+
rawCandidateCount = stats.rawCount;
|
|
8052
|
+
validCandidateCount = stats.validCount;
|
|
8053
|
+
callerOnCandidatesParsed?.(stats);
|
|
8018
8054
|
}
|
|
8019
8055
|
});
|
|
8056
|
+
const extractMs = Date.now() - tExtract;
|
|
8020
8057
|
const filtered = candidates.filter((c) => c.confidence >= minConfidence);
|
|
8021
8058
|
const log = getLogger();
|
|
8022
8059
|
const screened = screenCandidatesForInjection(filtered);
|
|
@@ -8064,6 +8101,7 @@ async function extractAndRetain(messages, retainCtx, options) {
|
|
|
8064
8101
|
const results = [];
|
|
8065
8102
|
const quarantinedInfo = [];
|
|
8066
8103
|
let failedWrites = 0;
|
|
8104
|
+
const tRetain = Date.now();
|
|
8067
8105
|
for (const { candidate, isQuarantined, reason, signature } of toRetain) {
|
|
8068
8106
|
try {
|
|
8069
8107
|
const result = await retain(candidate.content, retainCtx, {
|
|
@@ -8130,6 +8168,21 @@ async function extractAndRetain(messages, retainCtx, options) {
|
|
|
8130
8168
|
failedCount: failedWrites,
|
|
8131
8169
|
outcome,
|
|
8132
8170
|
quarantined: quarantinedInfo,
|
|
8171
|
+
funnel: {
|
|
8172
|
+
rawCandidateCount,
|
|
8173
|
+
validCandidateCount,
|
|
8174
|
+
afterRedactionCount: candidates.length,
|
|
8175
|
+
aboveConfidenceCount: filtered.length,
|
|
8176
|
+
// The PERSISTED quarantines (`quarantinedInfo`), not the screened list: a
|
|
8177
|
+
// screened candidate whose retain() throws is counted in `failedCount`,
|
|
8178
|
+
// and counting it here too would double it and break the identity below.
|
|
8179
|
+
// This also keeps the count equal to `quarantined.length` on the result.
|
|
8180
|
+
quarantinedCount: quarantinedInfo.length,
|
|
8181
|
+
retainedCount: results.length,
|
|
8182
|
+
failedCount: failedWrites
|
|
8183
|
+
},
|
|
8184
|
+
timings: { extractMs, retainMs: Date.now() - tRetain },
|
|
8185
|
+
model: options.extract.model ?? DEFAULT_EXTRACTION_MODEL,
|
|
8133
8186
|
// Only meaningful alongside `outcome: "empty-after-retry"`. Returned as well
|
|
8134
8187
|
// as pushed through the callback so a consumer that only inspects the result
|
|
8135
8188
|
// (rather than wiring a hook) can still report WHY.
|
|
@@ -8383,27 +8436,23 @@ function createAutoExtractor(options) {
|
|
|
8383
8436
|
const t0 = Date.now();
|
|
8384
8437
|
void (async () => {
|
|
8385
8438
|
try {
|
|
8386
|
-
const { candidates, results, failedCount, outcome, failure } = await extractAndRetain(
|
|
8387
|
-
|
|
8388
|
-
|
|
8389
|
-
{
|
|
8390
|
-
|
|
8391
|
-
|
|
8392
|
-
|
|
8393
|
-
|
|
8394
|
-
|
|
8395
|
-
|
|
8396
|
-
|
|
8397
|
-
|
|
8398
|
-
|
|
8399
|
-
|
|
8400
|
-
|
|
8401
|
-
},
|
|
8402
|
-
...options.onMemoryQuarantined && {
|
|
8403
|
-
onQuarantined: (info) => options.onMemoryQuarantined?.({ ...info, conversationId })
|
|
8404
|
-
}
|
|
8439
|
+
const { candidates, results, failedCount, outcome, failure, funnel, timings, model } = await extractAndRetain(window2, retainCtx, {
|
|
8440
|
+
extract,
|
|
8441
|
+
...options.minConfidence !== void 0 && { minConfidence: options.minConfidence },
|
|
8442
|
+
...options.entityCtx !== void 0 && { entityCtx: options.entityCtx },
|
|
8443
|
+
...options.scope !== void 0 && { scope: options.scope },
|
|
8444
|
+
...options.folderId !== void 0 && { folderId: options.folderId },
|
|
8445
|
+
...consolidateOptions !== void 0 && { consolidateOptions },
|
|
8446
|
+
...injectionClassifierOptions !== void 0 && {
|
|
8447
|
+
injectionClassifier: injectionClassifierOptions
|
|
8448
|
+
},
|
|
8449
|
+
...options.onCandidateFailed && {
|
|
8450
|
+
onCandidateFailed: (candidate, error) => options.onCandidateFailed?.({ candidate, error, conversationId })
|
|
8451
|
+
},
|
|
8452
|
+
...options.onMemoryQuarantined && {
|
|
8453
|
+
onQuarantined: (info) => options.onMemoryQuarantined?.({ ...info, conversationId })
|
|
8405
8454
|
}
|
|
8406
|
-
);
|
|
8455
|
+
});
|
|
8407
8456
|
if (outcome !== "empty-after-retry") {
|
|
8408
8457
|
const advancedTo = window2[window2.length - 1].id;
|
|
8409
8458
|
stateFor(conversationId).watermark = advancedTo;
|
|
@@ -8424,6 +8473,9 @@ function createAutoExtractor(options) {
|
|
|
8424
8473
|
durationMs: Date.now() - t0,
|
|
8425
8474
|
conversationId,
|
|
8426
8475
|
outcome,
|
|
8476
|
+
funnel,
|
|
8477
|
+
timings,
|
|
8478
|
+
model,
|
|
8427
8479
|
// Only set alongside `outcome: "empty-after-retry"`. Spread so the key
|
|
8428
8480
|
// is absent rather than explicitly undefined on a healthy turn —
|
|
8429
8481
|
// analytics backends store an explicit undefined as a real value.
|
package/dist/server/index.cjs
CHANGED
|
@@ -29296,6 +29296,7 @@ async function retain(content, ctx, options = {}) {
|
|
|
29296
29296
|
const threshold = options.autoMergeThreshold ?? DEFAULT_AUTO_MERGE_THRESHOLD;
|
|
29297
29297
|
const resolvedScope = options.scope ?? DEFAULT_SCOPE;
|
|
29298
29298
|
let supersedeTargetIds = [];
|
|
29299
|
+
let consolidationDecidedCreate = false;
|
|
29299
29300
|
let supersedeContent;
|
|
29300
29301
|
let prepared;
|
|
29301
29302
|
if (enableAutoMerge) {
|
|
@@ -29323,8 +29324,12 @@ async function retain(content, ctx, options = {}) {
|
|
|
29323
29324
|
const outcome = await tryConsolidate(trimmed, ctx, options, prepared);
|
|
29324
29325
|
if (outcome) {
|
|
29325
29326
|
if ("done" in outcome) return outcome.done;
|
|
29326
|
-
|
|
29327
|
-
|
|
29327
|
+
if ("create" in outcome) {
|
|
29328
|
+
consolidationDecidedCreate = true;
|
|
29329
|
+
} else {
|
|
29330
|
+
supersedeTargetIds = outcome.supersede;
|
|
29331
|
+
supersedeContent = outcome.content;
|
|
29332
|
+
}
|
|
29328
29333
|
}
|
|
29329
29334
|
}
|
|
29330
29335
|
if (supersedeTargetIds.length === 0) {
|
|
@@ -29374,7 +29379,14 @@ async function retain(content, ctx, options = {}) {
|
|
|
29374
29379
|
action: "merge",
|
|
29375
29380
|
memoryId: targetId,
|
|
29376
29381
|
targetId,
|
|
29377
|
-
proofCount: updated.proofCount ?? (existing.proofCount ?? 1) + 1
|
|
29382
|
+
proofCount: updated.proofCount ?? (existing.proofCount ?? 1) + 1,
|
|
29383
|
+
similarity: matches[0].similarity,
|
|
29384
|
+
// Stage 1 may have run and explicitly said `create`, and Stage 2
|
|
29385
|
+
// then found a strict-cosine match anyway. Reporting the decision
|
|
29386
|
+
// here is what makes that DISAGREEMENT visible — without it the
|
|
29387
|
+
// model's create silently vanished from the distribution whenever
|
|
29388
|
+
// the cosine stage won.
|
|
29389
|
+
...consolidationDecidedCreate && { consolidation: "create" }
|
|
29378
29390
|
};
|
|
29379
29391
|
}
|
|
29380
29392
|
await assertMergeTargetGoneOrThrow(ctx, targetId);
|
|
@@ -29387,12 +29399,18 @@ async function retain(content, ctx, options = {}) {
|
|
|
29387
29399
|
const embedding = reusableQueryEmbedding ?? await generateEmbedding(contentToWrite, ctx.embeddingOptions);
|
|
29388
29400
|
const embeddingModel = ctx.embeddingOptions.model ?? DEFAULT_API_EMBEDDING_MODEL;
|
|
29389
29401
|
if (options.respectTombstones) {
|
|
29390
|
-
const
|
|
29402
|
+
const tombstone = await findTombstoneMatch(embedding, embeddingModel, resolvedScope, ctx, {
|
|
29391
29403
|
threshold,
|
|
29392
29404
|
folderId: options.folderId
|
|
29393
29405
|
});
|
|
29394
|
-
if (
|
|
29395
|
-
return {
|
|
29406
|
+
if (tombstone) {
|
|
29407
|
+
return {
|
|
29408
|
+
action: "suppressed",
|
|
29409
|
+
memoryId: tombstone.id,
|
|
29410
|
+
tombstoneId: tombstone.id,
|
|
29411
|
+
proofCount: 0,
|
|
29412
|
+
similarity: tombstone.similarity
|
|
29413
|
+
};
|
|
29396
29414
|
}
|
|
29397
29415
|
}
|
|
29398
29416
|
const createOpts = {
|
|
@@ -29459,7 +29477,8 @@ async function retain(content, ctx, options = {}) {
|
|
|
29459
29477
|
action: "supersede",
|
|
29460
29478
|
memoryId: created2.uniqueId,
|
|
29461
29479
|
targetId: primaryTargetId,
|
|
29462
|
-
proofCount: 1
|
|
29480
|
+
proofCount: 1,
|
|
29481
|
+
consolidation: "supersede"
|
|
29463
29482
|
};
|
|
29464
29483
|
}
|
|
29465
29484
|
notifyConsolidationFallback(
|
|
@@ -29473,7 +29492,8 @@ async function retain(content, ctx, options = {}) {
|
|
|
29473
29492
|
return {
|
|
29474
29493
|
action: "create",
|
|
29475
29494
|
memoryId: created.uniqueId,
|
|
29476
|
-
proofCount: 1
|
|
29495
|
+
proofCount: 1,
|
|
29496
|
+
...consolidationDecidedCreate && { consolidation: "create" }
|
|
29477
29497
|
};
|
|
29478
29498
|
}
|
|
29479
29499
|
function unionStrings(a, b) {
|
|
@@ -29503,7 +29523,7 @@ async function findTombstoneMatch(embedding, embeddingModel, scope, ctx, opts) {
|
|
|
29503
29523
|
bestId = row.uniqueId;
|
|
29504
29524
|
}
|
|
29505
29525
|
}
|
|
29506
|
-
return bestId;
|
|
29526
|
+
return bestId === null ? null : { id: bestId, similarity: bestSim };
|
|
29507
29527
|
}
|
|
29508
29528
|
function pickEventTimeUpdate(existing, incoming) {
|
|
29509
29529
|
if (incoming === void 0 || incoming === null) return void 0;
|
|
@@ -29545,7 +29565,7 @@ async function tryConsolidate(trimmed, ctx, options, prepared) {
|
|
|
29545
29565
|
}));
|
|
29546
29566
|
const { consolidateMemory: doConsolidate } = await Promise.resolve().then(() => (init_consolidate(), consolidate_exports));
|
|
29547
29567
|
const decision = await doConsolidate(trimmed, candidates, consolidateOptions);
|
|
29548
|
-
if (decision.action === "create") return null;
|
|
29568
|
+
if (decision.action === "create") return decision.fallbackReason ? null : { create: true };
|
|
29549
29569
|
if (decision.action === "supersede" && decision.content) {
|
|
29550
29570
|
const requestedIds = decision.targetIds?.length ? decision.targetIds : decision.targetId ? [decision.targetId] : [];
|
|
29551
29571
|
if (requestedIds.length === 0) return null;
|
|
@@ -29604,7 +29624,8 @@ async function tryConsolidate(trimmed, ctx, options, prepared) {
|
|
|
29604
29624
|
action: "merge",
|
|
29605
29625
|
memoryId: decision.targetId,
|
|
29606
29626
|
targetId: decision.targetId,
|
|
29607
|
-
proofCount: updated.proofCount ?? (existing.proofCount ?? 1) + 1
|
|
29627
|
+
proofCount: updated.proofCount ?? (existing.proofCount ?? 1) + 1,
|
|
29628
|
+
consolidation: "noop"
|
|
29608
29629
|
}
|
|
29609
29630
|
};
|
|
29610
29631
|
}
|
|
@@ -29657,7 +29678,8 @@ async function tryConsolidate(trimmed, ctx, options, prepared) {
|
|
|
29657
29678
|
action: "update",
|
|
29658
29679
|
memoryId: decision.targetId,
|
|
29659
29680
|
targetId: decision.targetId,
|
|
29660
|
-
proofCount: updated.proofCount ?? (existing.proofCount ?? 1) + 1
|
|
29681
|
+
proofCount: updated.proofCount ?? (existing.proofCount ?? 1) + 1,
|
|
29682
|
+
consolidation: "update"
|
|
29661
29683
|
}
|
|
29662
29684
|
};
|
|
29663
29685
|
}
|
|
@@ -29799,6 +29821,11 @@ Extract durable user facts.`,
|
|
|
29799
29821
|
fallbackSourceId,
|
|
29800
29822
|
options.userIdentity ?? []
|
|
29801
29823
|
);
|
|
29824
|
+
const rawCandidates = parsed.candidates;
|
|
29825
|
+
options.onCandidatesParsed?.({
|
|
29826
|
+
rawCount: Array.isArray(rawCandidates) ? rawCandidates.length : 0,
|
|
29827
|
+
validCount: candidates.length
|
|
29828
|
+
});
|
|
29802
29829
|
if (!redactor) return candidates;
|
|
29803
29830
|
const restored = restoreCandidates(candidates, redactor, options.userIdentity ?? []);
|
|
29804
29831
|
if (candidates.length > 0 && restored.length === 0) {
|
|
@@ -29835,8 +29862,12 @@ async function extractAndRetain(messages, retainCtx, options) {
|
|
|
29835
29862
|
let exhaustedEmpty = false;
|
|
29836
29863
|
let exhaustedFailure;
|
|
29837
29864
|
let droppedAfterRedaction = false;
|
|
29865
|
+
let rawCandidateCount = 0;
|
|
29866
|
+
let validCandidateCount = 0;
|
|
29838
29867
|
const callerOnExhaustedEmpty = options.extract.onExhaustedEmpty;
|
|
29839
29868
|
const callerOnCandidatesDropped = options.extract.onCandidatesDropped;
|
|
29869
|
+
const callerOnCandidatesParsed = options.extract.onCandidatesParsed;
|
|
29870
|
+
const tExtract = Date.now();
|
|
29840
29871
|
const candidates = await extractFacts(messages, {
|
|
29841
29872
|
...options.extract,
|
|
29842
29873
|
onExhaustedEmpty: (failure) => {
|
|
@@ -29847,8 +29878,14 @@ async function extractAndRetain(messages, retainCtx, options) {
|
|
|
29847
29878
|
onCandidatesDropped: () => {
|
|
29848
29879
|
droppedAfterRedaction = true;
|
|
29849
29880
|
callerOnCandidatesDropped?.();
|
|
29881
|
+
},
|
|
29882
|
+
onCandidatesParsed: (stats) => {
|
|
29883
|
+
rawCandidateCount = stats.rawCount;
|
|
29884
|
+
validCandidateCount = stats.validCount;
|
|
29885
|
+
callerOnCandidatesParsed?.(stats);
|
|
29850
29886
|
}
|
|
29851
29887
|
});
|
|
29888
|
+
const extractMs = Date.now() - tExtract;
|
|
29852
29889
|
const filtered = candidates.filter((c) => c.confidence >= minConfidence);
|
|
29853
29890
|
const log = getLogger();
|
|
29854
29891
|
const screened = screenCandidatesForInjection(filtered);
|
|
@@ -29896,6 +29933,7 @@ async function extractAndRetain(messages, retainCtx, options) {
|
|
|
29896
29933
|
const results = [];
|
|
29897
29934
|
const quarantinedInfo = [];
|
|
29898
29935
|
let failedWrites = 0;
|
|
29936
|
+
const tRetain = Date.now();
|
|
29899
29937
|
for (const { candidate, isQuarantined, reason, signature } of toRetain) {
|
|
29900
29938
|
try {
|
|
29901
29939
|
const result = await retain(candidate.content, retainCtx, {
|
|
@@ -29962,6 +30000,21 @@ async function extractAndRetain(messages, retainCtx, options) {
|
|
|
29962
30000
|
failedCount: failedWrites,
|
|
29963
30001
|
outcome,
|
|
29964
30002
|
quarantined: quarantinedInfo,
|
|
30003
|
+
funnel: {
|
|
30004
|
+
rawCandidateCount,
|
|
30005
|
+
validCandidateCount,
|
|
30006
|
+
afterRedactionCount: candidates.length,
|
|
30007
|
+
aboveConfidenceCount: filtered.length,
|
|
30008
|
+
// The PERSISTED quarantines (`quarantinedInfo`), not the screened list: a
|
|
30009
|
+
// screened candidate whose retain() throws is counted in `failedCount`,
|
|
30010
|
+
// and counting it here too would double it and break the identity below.
|
|
30011
|
+
// This also keeps the count equal to `quarantined.length` on the result.
|
|
30012
|
+
quarantinedCount: quarantinedInfo.length,
|
|
30013
|
+
retainedCount: results.length,
|
|
30014
|
+
failedCount: failedWrites
|
|
30015
|
+
},
|
|
30016
|
+
timings: { extractMs, retainMs: Date.now() - tRetain },
|
|
30017
|
+
model: options.extract.model ?? DEFAULT_EXTRACTION_MODEL,
|
|
29965
30018
|
// Only meaningful alongside `outcome: "empty-after-retry"`. Returned as well
|
|
29966
30019
|
// as pushed through the callback so a consumer that only inspects the result
|
|
29967
30020
|
// (rather than wiring a hook) can still report WHY.
|
|
@@ -30217,27 +30270,23 @@ function createAutoExtractor(options) {
|
|
|
30217
30270
|
const t0 = Date.now();
|
|
30218
30271
|
void (async () => {
|
|
30219
30272
|
try {
|
|
30220
|
-
const { candidates, results, failedCount, outcome, failure } = await extractAndRetain(
|
|
30221
|
-
|
|
30222
|
-
|
|
30223
|
-
{
|
|
30224
|
-
|
|
30225
|
-
|
|
30226
|
-
|
|
30227
|
-
|
|
30228
|
-
|
|
30229
|
-
|
|
30230
|
-
|
|
30231
|
-
|
|
30232
|
-
|
|
30233
|
-
|
|
30234
|
-
|
|
30235
|
-
},
|
|
30236
|
-
...options.onMemoryQuarantined && {
|
|
30237
|
-
onQuarantined: (info) => options.onMemoryQuarantined?.({ ...info, conversationId })
|
|
30238
|
-
}
|
|
30273
|
+
const { candidates, results, failedCount, outcome, failure, funnel, timings, model } = await extractAndRetain(window2, retainCtx, {
|
|
30274
|
+
extract,
|
|
30275
|
+
...options.minConfidence !== void 0 && { minConfidence: options.minConfidence },
|
|
30276
|
+
...options.entityCtx !== void 0 && { entityCtx: options.entityCtx },
|
|
30277
|
+
...options.scope !== void 0 && { scope: options.scope },
|
|
30278
|
+
...options.folderId !== void 0 && { folderId: options.folderId },
|
|
30279
|
+
...consolidateOptions !== void 0 && { consolidateOptions },
|
|
30280
|
+
...injectionClassifierOptions !== void 0 && {
|
|
30281
|
+
injectionClassifier: injectionClassifierOptions
|
|
30282
|
+
},
|
|
30283
|
+
...options.onCandidateFailed && {
|
|
30284
|
+
onCandidateFailed: (candidate, error) => options.onCandidateFailed?.({ candidate, error, conversationId })
|
|
30285
|
+
},
|
|
30286
|
+
...options.onMemoryQuarantined && {
|
|
30287
|
+
onQuarantined: (info) => options.onMemoryQuarantined?.({ ...info, conversationId })
|
|
30239
30288
|
}
|
|
30240
|
-
);
|
|
30289
|
+
});
|
|
30241
30290
|
if (outcome !== "empty-after-retry") {
|
|
30242
30291
|
const advancedTo = window2[window2.length - 1].id;
|
|
30243
30292
|
stateFor(conversationId).watermark = advancedTo;
|
|
@@ -30258,6 +30307,9 @@ function createAutoExtractor(options) {
|
|
|
30258
30307
|
durationMs: Date.now() - t0,
|
|
30259
30308
|
conversationId,
|
|
30260
30309
|
outcome,
|
|
30310
|
+
funnel,
|
|
30311
|
+
timings,
|
|
30312
|
+
model,
|
|
30261
30313
|
// Only set alongside `outcome: "empty-after-retry"`. Spread so the key
|
|
30262
30314
|
// is absent rather than explicitly undefined on a healthy turn —
|
|
30263
30315
|
// analytics backends store an explicit undefined as a real value.
|