@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/expo/index.mjs
CHANGED
|
@@ -6646,6 +6646,7 @@ async function retain(content, ctx, options = {}) {
|
|
|
6646
6646
|
const threshold = options.autoMergeThreshold ?? DEFAULT_AUTO_MERGE_THRESHOLD;
|
|
6647
6647
|
const resolvedScope = options.scope ?? DEFAULT_SCOPE;
|
|
6648
6648
|
let supersedeTargetIds = [];
|
|
6649
|
+
let consolidationDecidedCreate = false;
|
|
6649
6650
|
let supersedeContent;
|
|
6650
6651
|
let prepared;
|
|
6651
6652
|
if (enableAutoMerge) {
|
|
@@ -6673,8 +6674,12 @@ async function retain(content, ctx, options = {}) {
|
|
|
6673
6674
|
const outcome = await tryConsolidate(trimmed, ctx, options, prepared);
|
|
6674
6675
|
if (outcome) {
|
|
6675
6676
|
if ("done" in outcome) return outcome.done;
|
|
6676
|
-
|
|
6677
|
-
|
|
6677
|
+
if ("create" in outcome) {
|
|
6678
|
+
consolidationDecidedCreate = true;
|
|
6679
|
+
} else {
|
|
6680
|
+
supersedeTargetIds = outcome.supersede;
|
|
6681
|
+
supersedeContent = outcome.content;
|
|
6682
|
+
}
|
|
6678
6683
|
}
|
|
6679
6684
|
}
|
|
6680
6685
|
if (supersedeTargetIds.length === 0) {
|
|
@@ -6724,7 +6729,14 @@ async function retain(content, ctx, options = {}) {
|
|
|
6724
6729
|
action: "merge",
|
|
6725
6730
|
memoryId: targetId,
|
|
6726
6731
|
targetId,
|
|
6727
|
-
proofCount: updated.proofCount ?? (existing.proofCount ?? 1) + 1
|
|
6732
|
+
proofCount: updated.proofCount ?? (existing.proofCount ?? 1) + 1,
|
|
6733
|
+
similarity: matches[0].similarity,
|
|
6734
|
+
// Stage 1 may have run and explicitly said `create`, and Stage 2
|
|
6735
|
+
// then found a strict-cosine match anyway. Reporting the decision
|
|
6736
|
+
// here is what makes that DISAGREEMENT visible — without it the
|
|
6737
|
+
// model's create silently vanished from the distribution whenever
|
|
6738
|
+
// the cosine stage won.
|
|
6739
|
+
...consolidationDecidedCreate && { consolidation: "create" }
|
|
6728
6740
|
};
|
|
6729
6741
|
}
|
|
6730
6742
|
await assertMergeTargetGoneOrThrow(ctx, targetId);
|
|
@@ -6737,12 +6749,18 @@ async function retain(content, ctx, options = {}) {
|
|
|
6737
6749
|
const embedding = reusableQueryEmbedding ?? await generateEmbedding(contentToWrite, ctx.embeddingOptions);
|
|
6738
6750
|
const embeddingModel = ctx.embeddingOptions.model ?? DEFAULT_API_EMBEDDING_MODEL;
|
|
6739
6751
|
if (options.respectTombstones) {
|
|
6740
|
-
const
|
|
6752
|
+
const tombstone = await findTombstoneMatch(embedding, embeddingModel, resolvedScope, ctx, {
|
|
6741
6753
|
threshold,
|
|
6742
6754
|
folderId: options.folderId
|
|
6743
6755
|
});
|
|
6744
|
-
if (
|
|
6745
|
-
return {
|
|
6756
|
+
if (tombstone) {
|
|
6757
|
+
return {
|
|
6758
|
+
action: "suppressed",
|
|
6759
|
+
memoryId: tombstone.id,
|
|
6760
|
+
tombstoneId: tombstone.id,
|
|
6761
|
+
proofCount: 0,
|
|
6762
|
+
similarity: tombstone.similarity
|
|
6763
|
+
};
|
|
6746
6764
|
}
|
|
6747
6765
|
}
|
|
6748
6766
|
const createOpts = {
|
|
@@ -6809,7 +6827,8 @@ async function retain(content, ctx, options = {}) {
|
|
|
6809
6827
|
action: "supersede",
|
|
6810
6828
|
memoryId: created2.uniqueId,
|
|
6811
6829
|
targetId: primaryTargetId,
|
|
6812
|
-
proofCount: 1
|
|
6830
|
+
proofCount: 1,
|
|
6831
|
+
consolidation: "supersede"
|
|
6813
6832
|
};
|
|
6814
6833
|
}
|
|
6815
6834
|
notifyConsolidationFallback(
|
|
@@ -6823,7 +6842,8 @@ async function retain(content, ctx, options = {}) {
|
|
|
6823
6842
|
return {
|
|
6824
6843
|
action: "create",
|
|
6825
6844
|
memoryId: created.uniqueId,
|
|
6826
|
-
proofCount: 1
|
|
6845
|
+
proofCount: 1,
|
|
6846
|
+
...consolidationDecidedCreate && { consolidation: "create" }
|
|
6827
6847
|
};
|
|
6828
6848
|
}
|
|
6829
6849
|
function unionStrings(a, b) {
|
|
@@ -6853,7 +6873,7 @@ async function findTombstoneMatch(embedding, embeddingModel, scope, ctx, opts) {
|
|
|
6853
6873
|
bestId = row.uniqueId;
|
|
6854
6874
|
}
|
|
6855
6875
|
}
|
|
6856
|
-
return bestId;
|
|
6876
|
+
return bestId === null ? null : { id: bestId, similarity: bestSim };
|
|
6857
6877
|
}
|
|
6858
6878
|
function pickEventTimeUpdate(existing, incoming) {
|
|
6859
6879
|
if (incoming === void 0 || incoming === null) return void 0;
|
|
@@ -6895,7 +6915,7 @@ async function tryConsolidate(trimmed, ctx, options, prepared) {
|
|
|
6895
6915
|
}));
|
|
6896
6916
|
const { consolidateMemory: doConsolidate } = await import("./consolidate-XBYKAFKT.mjs");
|
|
6897
6917
|
const decision = await doConsolidate(trimmed, candidates, consolidateOptions);
|
|
6898
|
-
if (decision.action === "create") return null;
|
|
6918
|
+
if (decision.action === "create") return decision.fallbackReason ? null : { create: true };
|
|
6899
6919
|
if (decision.action === "supersede" && decision.content) {
|
|
6900
6920
|
const requestedIds = decision.targetIds?.length ? decision.targetIds : decision.targetId ? [decision.targetId] : [];
|
|
6901
6921
|
if (requestedIds.length === 0) return null;
|
|
@@ -6954,7 +6974,8 @@ async function tryConsolidate(trimmed, ctx, options, prepared) {
|
|
|
6954
6974
|
action: "merge",
|
|
6955
6975
|
memoryId: decision.targetId,
|
|
6956
6976
|
targetId: decision.targetId,
|
|
6957
|
-
proofCount: updated.proofCount ?? (existing.proofCount ?? 1) + 1
|
|
6977
|
+
proofCount: updated.proofCount ?? (existing.proofCount ?? 1) + 1,
|
|
6978
|
+
consolidation: "noop"
|
|
6958
6979
|
}
|
|
6959
6980
|
};
|
|
6960
6981
|
}
|
|
@@ -7007,7 +7028,8 @@ async function tryConsolidate(trimmed, ctx, options, prepared) {
|
|
|
7007
7028
|
action: "update",
|
|
7008
7029
|
memoryId: decision.targetId,
|
|
7009
7030
|
targetId: decision.targetId,
|
|
7010
|
-
proofCount: updated.proofCount ?? (existing.proofCount ?? 1) + 1
|
|
7031
|
+
proofCount: updated.proofCount ?? (existing.proofCount ?? 1) + 1,
|
|
7032
|
+
consolidation: "update"
|
|
7011
7033
|
}
|
|
7012
7034
|
};
|
|
7013
7035
|
}
|
|
@@ -7149,6 +7171,11 @@ Extract durable user facts.`,
|
|
|
7149
7171
|
fallbackSourceId,
|
|
7150
7172
|
options.userIdentity ?? []
|
|
7151
7173
|
);
|
|
7174
|
+
const rawCandidates = parsed.candidates;
|
|
7175
|
+
options.onCandidatesParsed?.({
|
|
7176
|
+
rawCount: Array.isArray(rawCandidates) ? rawCandidates.length : 0,
|
|
7177
|
+
validCount: candidates.length
|
|
7178
|
+
});
|
|
7152
7179
|
if (!redactor) return candidates;
|
|
7153
7180
|
const restored = restoreCandidates(candidates, redactor, options.userIdentity ?? []);
|
|
7154
7181
|
if (candidates.length > 0 && restored.length === 0) {
|
|
@@ -7185,8 +7212,12 @@ async function extractAndRetain(messages, retainCtx, options) {
|
|
|
7185
7212
|
let exhaustedEmpty = false;
|
|
7186
7213
|
let exhaustedFailure;
|
|
7187
7214
|
let droppedAfterRedaction = false;
|
|
7215
|
+
let rawCandidateCount = 0;
|
|
7216
|
+
let validCandidateCount = 0;
|
|
7188
7217
|
const callerOnExhaustedEmpty = options.extract.onExhaustedEmpty;
|
|
7189
7218
|
const callerOnCandidatesDropped = options.extract.onCandidatesDropped;
|
|
7219
|
+
const callerOnCandidatesParsed = options.extract.onCandidatesParsed;
|
|
7220
|
+
const tExtract = Date.now();
|
|
7190
7221
|
const candidates = await extractFacts(messages, {
|
|
7191
7222
|
...options.extract,
|
|
7192
7223
|
onExhaustedEmpty: (failure) => {
|
|
@@ -7197,8 +7228,14 @@ async function extractAndRetain(messages, retainCtx, options) {
|
|
|
7197
7228
|
onCandidatesDropped: () => {
|
|
7198
7229
|
droppedAfterRedaction = true;
|
|
7199
7230
|
callerOnCandidatesDropped?.();
|
|
7231
|
+
},
|
|
7232
|
+
onCandidatesParsed: (stats) => {
|
|
7233
|
+
rawCandidateCount = stats.rawCount;
|
|
7234
|
+
validCandidateCount = stats.validCount;
|
|
7235
|
+
callerOnCandidatesParsed?.(stats);
|
|
7200
7236
|
}
|
|
7201
7237
|
});
|
|
7238
|
+
const extractMs = Date.now() - tExtract;
|
|
7202
7239
|
const filtered = candidates.filter((c) => c.confidence >= minConfidence);
|
|
7203
7240
|
const log = getLogger();
|
|
7204
7241
|
const screened = screenCandidatesForInjection(filtered);
|
|
@@ -7246,6 +7283,7 @@ async function extractAndRetain(messages, retainCtx, options) {
|
|
|
7246
7283
|
const results = [];
|
|
7247
7284
|
const quarantinedInfo = [];
|
|
7248
7285
|
let failedWrites = 0;
|
|
7286
|
+
const tRetain = Date.now();
|
|
7249
7287
|
for (const { candidate, isQuarantined, reason, signature } of toRetain) {
|
|
7250
7288
|
try {
|
|
7251
7289
|
const result = await retain(candidate.content, retainCtx, {
|
|
@@ -7312,6 +7350,21 @@ async function extractAndRetain(messages, retainCtx, options) {
|
|
|
7312
7350
|
failedCount: failedWrites,
|
|
7313
7351
|
outcome,
|
|
7314
7352
|
quarantined: quarantinedInfo,
|
|
7353
|
+
funnel: {
|
|
7354
|
+
rawCandidateCount,
|
|
7355
|
+
validCandidateCount,
|
|
7356
|
+
afterRedactionCount: candidates.length,
|
|
7357
|
+
aboveConfidenceCount: filtered.length,
|
|
7358
|
+
// The PERSISTED quarantines (`quarantinedInfo`), not the screened list: a
|
|
7359
|
+
// screened candidate whose retain() throws is counted in `failedCount`,
|
|
7360
|
+
// and counting it here too would double it and break the identity below.
|
|
7361
|
+
// This also keeps the count equal to `quarantined.length` on the result.
|
|
7362
|
+
quarantinedCount: quarantinedInfo.length,
|
|
7363
|
+
retainedCount: results.length,
|
|
7364
|
+
failedCount: failedWrites
|
|
7365
|
+
},
|
|
7366
|
+
timings: { extractMs, retainMs: Date.now() - tRetain },
|
|
7367
|
+
model: options.extract.model ?? DEFAULT_EXTRACTION_MODEL,
|
|
7315
7368
|
// Only meaningful alongside `outcome: "empty-after-retry"`. Returned as well
|
|
7316
7369
|
// as pushed through the callback so a consumer that only inspects the result
|
|
7317
7370
|
// (rather than wiring a hook) can still report WHY.
|
|
@@ -7565,27 +7618,23 @@ function createAutoExtractor(options) {
|
|
|
7565
7618
|
const t0 = Date.now();
|
|
7566
7619
|
void (async () => {
|
|
7567
7620
|
try {
|
|
7568
|
-
const { candidates, results, failedCount, outcome, failure } = await extractAndRetain(
|
|
7569
|
-
|
|
7570
|
-
|
|
7571
|
-
{
|
|
7572
|
-
|
|
7573
|
-
|
|
7574
|
-
|
|
7575
|
-
|
|
7576
|
-
|
|
7577
|
-
|
|
7578
|
-
|
|
7579
|
-
|
|
7580
|
-
|
|
7581
|
-
|
|
7582
|
-
|
|
7583
|
-
},
|
|
7584
|
-
...options.onMemoryQuarantined && {
|
|
7585
|
-
onQuarantined: (info) => options.onMemoryQuarantined?.({ ...info, conversationId })
|
|
7586
|
-
}
|
|
7621
|
+
const { candidates, results, failedCount, outcome, failure, funnel, timings, model } = await extractAndRetain(window, retainCtx, {
|
|
7622
|
+
extract,
|
|
7623
|
+
...options.minConfidence !== void 0 && { minConfidence: options.minConfidence },
|
|
7624
|
+
...options.entityCtx !== void 0 && { entityCtx: options.entityCtx },
|
|
7625
|
+
...options.scope !== void 0 && { scope: options.scope },
|
|
7626
|
+
...options.folderId !== void 0 && { folderId: options.folderId },
|
|
7627
|
+
...consolidateOptions !== void 0 && { consolidateOptions },
|
|
7628
|
+
...injectionClassifierOptions !== void 0 && {
|
|
7629
|
+
injectionClassifier: injectionClassifierOptions
|
|
7630
|
+
},
|
|
7631
|
+
...options.onCandidateFailed && {
|
|
7632
|
+
onCandidateFailed: (candidate, error) => options.onCandidateFailed?.({ candidate, error, conversationId })
|
|
7633
|
+
},
|
|
7634
|
+
...options.onMemoryQuarantined && {
|
|
7635
|
+
onQuarantined: (info) => options.onMemoryQuarantined?.({ ...info, conversationId })
|
|
7587
7636
|
}
|
|
7588
|
-
);
|
|
7637
|
+
});
|
|
7589
7638
|
if (outcome !== "empty-after-retry") {
|
|
7590
7639
|
const advancedTo = window[window.length - 1].id;
|
|
7591
7640
|
stateFor(conversationId).watermark = advancedTo;
|
|
@@ -7606,6 +7655,9 @@ function createAutoExtractor(options) {
|
|
|
7606
7655
|
durationMs: Date.now() - t0,
|
|
7607
7656
|
conversationId,
|
|
7608
7657
|
outcome,
|
|
7658
|
+
funnel,
|
|
7659
|
+
timings,
|
|
7660
|
+
model,
|
|
7609
7661
|
// Only set alongside `outcome: "empty-after-retry"`. Spread so the key
|
|
7610
7662
|
// is absent rather than explicitly undefined on a healthy turn —
|
|
7611
7663
|
// analytics backends store an explicit undefined as a real value.
|
package/dist/react/index.cjs
CHANGED
|
@@ -17683,6 +17683,7 @@ async function retain(content, ctx, options = {}) {
|
|
|
17683
17683
|
const threshold = options.autoMergeThreshold ?? DEFAULT_AUTO_MERGE_THRESHOLD;
|
|
17684
17684
|
const resolvedScope = options.scope ?? DEFAULT_SCOPE;
|
|
17685
17685
|
let supersedeTargetIds = [];
|
|
17686
|
+
let consolidationDecidedCreate = false;
|
|
17686
17687
|
let supersedeContent;
|
|
17687
17688
|
let prepared;
|
|
17688
17689
|
if (enableAutoMerge) {
|
|
@@ -17710,8 +17711,12 @@ async function retain(content, ctx, options = {}) {
|
|
|
17710
17711
|
const outcome = await tryConsolidate(trimmed, ctx, options, prepared);
|
|
17711
17712
|
if (outcome) {
|
|
17712
17713
|
if ("done" in outcome) return outcome.done;
|
|
17713
|
-
|
|
17714
|
-
|
|
17714
|
+
if ("create" in outcome) {
|
|
17715
|
+
consolidationDecidedCreate = true;
|
|
17716
|
+
} else {
|
|
17717
|
+
supersedeTargetIds = outcome.supersede;
|
|
17718
|
+
supersedeContent = outcome.content;
|
|
17719
|
+
}
|
|
17715
17720
|
}
|
|
17716
17721
|
}
|
|
17717
17722
|
if (supersedeTargetIds.length === 0) {
|
|
@@ -17761,7 +17766,14 @@ async function retain(content, ctx, options = {}) {
|
|
|
17761
17766
|
action: "merge",
|
|
17762
17767
|
memoryId: targetId,
|
|
17763
17768
|
targetId,
|
|
17764
|
-
proofCount: updated.proofCount ?? (existing.proofCount ?? 1) + 1
|
|
17769
|
+
proofCount: updated.proofCount ?? (existing.proofCount ?? 1) + 1,
|
|
17770
|
+
similarity: matches[0].similarity,
|
|
17771
|
+
// Stage 1 may have run and explicitly said `create`, and Stage 2
|
|
17772
|
+
// then found a strict-cosine match anyway. Reporting the decision
|
|
17773
|
+
// here is what makes that DISAGREEMENT visible — without it the
|
|
17774
|
+
// model's create silently vanished from the distribution whenever
|
|
17775
|
+
// the cosine stage won.
|
|
17776
|
+
...consolidationDecidedCreate && { consolidation: "create" }
|
|
17765
17777
|
};
|
|
17766
17778
|
}
|
|
17767
17779
|
await assertMergeTargetGoneOrThrow(ctx, targetId);
|
|
@@ -17774,12 +17786,18 @@ async function retain(content, ctx, options = {}) {
|
|
|
17774
17786
|
const embedding = reusableQueryEmbedding ?? await generateEmbedding(contentToWrite, ctx.embeddingOptions);
|
|
17775
17787
|
const embeddingModel = ctx.embeddingOptions.model ?? DEFAULT_API_EMBEDDING_MODEL;
|
|
17776
17788
|
if (options.respectTombstones) {
|
|
17777
|
-
const
|
|
17789
|
+
const tombstone = await findTombstoneMatch(embedding, embeddingModel, resolvedScope, ctx, {
|
|
17778
17790
|
threshold,
|
|
17779
17791
|
folderId: options.folderId
|
|
17780
17792
|
});
|
|
17781
|
-
if (
|
|
17782
|
-
return {
|
|
17793
|
+
if (tombstone) {
|
|
17794
|
+
return {
|
|
17795
|
+
action: "suppressed",
|
|
17796
|
+
memoryId: tombstone.id,
|
|
17797
|
+
tombstoneId: tombstone.id,
|
|
17798
|
+
proofCount: 0,
|
|
17799
|
+
similarity: tombstone.similarity
|
|
17800
|
+
};
|
|
17783
17801
|
}
|
|
17784
17802
|
}
|
|
17785
17803
|
const createOpts = {
|
|
@@ -17846,7 +17864,8 @@ async function retain(content, ctx, options = {}) {
|
|
|
17846
17864
|
action: "supersede",
|
|
17847
17865
|
memoryId: created2.uniqueId,
|
|
17848
17866
|
targetId: primaryTargetId,
|
|
17849
|
-
proofCount: 1
|
|
17867
|
+
proofCount: 1,
|
|
17868
|
+
consolidation: "supersede"
|
|
17850
17869
|
};
|
|
17851
17870
|
}
|
|
17852
17871
|
notifyConsolidationFallback(
|
|
@@ -17860,7 +17879,8 @@ async function retain(content, ctx, options = {}) {
|
|
|
17860
17879
|
return {
|
|
17861
17880
|
action: "create",
|
|
17862
17881
|
memoryId: created.uniqueId,
|
|
17863
|
-
proofCount: 1
|
|
17882
|
+
proofCount: 1,
|
|
17883
|
+
...consolidationDecidedCreate && { consolidation: "create" }
|
|
17864
17884
|
};
|
|
17865
17885
|
}
|
|
17866
17886
|
function unionStrings(a, b) {
|
|
@@ -17890,7 +17910,7 @@ async function findTombstoneMatch(embedding, embeddingModel, scope, ctx, opts) {
|
|
|
17890
17910
|
bestId = row.uniqueId;
|
|
17891
17911
|
}
|
|
17892
17912
|
}
|
|
17893
|
-
return bestId;
|
|
17913
|
+
return bestId === null ? null : { id: bestId, similarity: bestSim };
|
|
17894
17914
|
}
|
|
17895
17915
|
function pickEventTimeUpdate(existing, incoming) {
|
|
17896
17916
|
if (incoming === void 0 || incoming === null) return void 0;
|
|
@@ -17932,7 +17952,7 @@ async function tryConsolidate(trimmed, ctx, options, prepared) {
|
|
|
17932
17952
|
}));
|
|
17933
17953
|
const { consolidateMemory: doConsolidate } = await Promise.resolve().then(() => (init_consolidate(), consolidate_exports));
|
|
17934
17954
|
const decision = await doConsolidate(trimmed, candidates, consolidateOptions);
|
|
17935
|
-
if (decision.action === "create") return null;
|
|
17955
|
+
if (decision.action === "create") return decision.fallbackReason ? null : { create: true };
|
|
17936
17956
|
if (decision.action === "supersede" && decision.content) {
|
|
17937
17957
|
const requestedIds = decision.targetIds?.length ? decision.targetIds : decision.targetId ? [decision.targetId] : [];
|
|
17938
17958
|
if (requestedIds.length === 0) return null;
|
|
@@ -17991,7 +18011,8 @@ async function tryConsolidate(trimmed, ctx, options, prepared) {
|
|
|
17991
18011
|
action: "merge",
|
|
17992
18012
|
memoryId: decision.targetId,
|
|
17993
18013
|
targetId: decision.targetId,
|
|
17994
|
-
proofCount: updated.proofCount ?? (existing.proofCount ?? 1) + 1
|
|
18014
|
+
proofCount: updated.proofCount ?? (existing.proofCount ?? 1) + 1,
|
|
18015
|
+
consolidation: "noop"
|
|
17995
18016
|
}
|
|
17996
18017
|
};
|
|
17997
18018
|
}
|
|
@@ -18044,7 +18065,8 @@ async function tryConsolidate(trimmed, ctx, options, prepared) {
|
|
|
18044
18065
|
action: "update",
|
|
18045
18066
|
memoryId: decision.targetId,
|
|
18046
18067
|
targetId: decision.targetId,
|
|
18047
|
-
proofCount: updated.proofCount ?? (existing.proofCount ?? 1) + 1
|
|
18068
|
+
proofCount: updated.proofCount ?? (existing.proofCount ?? 1) + 1,
|
|
18069
|
+
consolidation: "update"
|
|
18048
18070
|
}
|
|
18049
18071
|
};
|
|
18050
18072
|
}
|
|
@@ -18186,6 +18208,11 @@ Extract durable user facts.`,
|
|
|
18186
18208
|
fallbackSourceId,
|
|
18187
18209
|
options.userIdentity ?? []
|
|
18188
18210
|
);
|
|
18211
|
+
const rawCandidates = parsed.candidates;
|
|
18212
|
+
options.onCandidatesParsed?.({
|
|
18213
|
+
rawCount: Array.isArray(rawCandidates) ? rawCandidates.length : 0,
|
|
18214
|
+
validCount: candidates.length
|
|
18215
|
+
});
|
|
18189
18216
|
if (!redactor) return candidates;
|
|
18190
18217
|
const restored = restoreCandidates(candidates, redactor, options.userIdentity ?? []);
|
|
18191
18218
|
if (candidates.length > 0 && restored.length === 0) {
|
|
@@ -18222,8 +18249,12 @@ async function extractAndRetain(messages, retainCtx, options) {
|
|
|
18222
18249
|
let exhaustedEmpty = false;
|
|
18223
18250
|
let exhaustedFailure;
|
|
18224
18251
|
let droppedAfterRedaction = false;
|
|
18252
|
+
let rawCandidateCount = 0;
|
|
18253
|
+
let validCandidateCount = 0;
|
|
18225
18254
|
const callerOnExhaustedEmpty = options.extract.onExhaustedEmpty;
|
|
18226
18255
|
const callerOnCandidatesDropped = options.extract.onCandidatesDropped;
|
|
18256
|
+
const callerOnCandidatesParsed = options.extract.onCandidatesParsed;
|
|
18257
|
+
const tExtract = Date.now();
|
|
18227
18258
|
const candidates = await extractFacts(messages, {
|
|
18228
18259
|
...options.extract,
|
|
18229
18260
|
onExhaustedEmpty: (failure) => {
|
|
@@ -18234,8 +18265,14 @@ async function extractAndRetain(messages, retainCtx, options) {
|
|
|
18234
18265
|
onCandidatesDropped: () => {
|
|
18235
18266
|
droppedAfterRedaction = true;
|
|
18236
18267
|
callerOnCandidatesDropped?.();
|
|
18268
|
+
},
|
|
18269
|
+
onCandidatesParsed: (stats) => {
|
|
18270
|
+
rawCandidateCount = stats.rawCount;
|
|
18271
|
+
validCandidateCount = stats.validCount;
|
|
18272
|
+
callerOnCandidatesParsed?.(stats);
|
|
18237
18273
|
}
|
|
18238
18274
|
});
|
|
18275
|
+
const extractMs = Date.now() - tExtract;
|
|
18239
18276
|
const filtered = candidates.filter((c) => c.confidence >= minConfidence);
|
|
18240
18277
|
const log = getLogger();
|
|
18241
18278
|
const screened = screenCandidatesForInjection(filtered);
|
|
@@ -18283,6 +18320,7 @@ async function extractAndRetain(messages, retainCtx, options) {
|
|
|
18283
18320
|
const results = [];
|
|
18284
18321
|
const quarantinedInfo = [];
|
|
18285
18322
|
let failedWrites = 0;
|
|
18323
|
+
const tRetain = Date.now();
|
|
18286
18324
|
for (const { candidate, isQuarantined, reason, signature } of toRetain) {
|
|
18287
18325
|
try {
|
|
18288
18326
|
const result = await retain(candidate.content, retainCtx, {
|
|
@@ -18349,6 +18387,21 @@ async function extractAndRetain(messages, retainCtx, options) {
|
|
|
18349
18387
|
failedCount: failedWrites,
|
|
18350
18388
|
outcome,
|
|
18351
18389
|
quarantined: quarantinedInfo,
|
|
18390
|
+
funnel: {
|
|
18391
|
+
rawCandidateCount,
|
|
18392
|
+
validCandidateCount,
|
|
18393
|
+
afterRedactionCount: candidates.length,
|
|
18394
|
+
aboveConfidenceCount: filtered.length,
|
|
18395
|
+
// The PERSISTED quarantines (`quarantinedInfo`), not the screened list: a
|
|
18396
|
+
// screened candidate whose retain() throws is counted in `failedCount`,
|
|
18397
|
+
// and counting it here too would double it and break the identity below.
|
|
18398
|
+
// This also keeps the count equal to `quarantined.length` on the result.
|
|
18399
|
+
quarantinedCount: quarantinedInfo.length,
|
|
18400
|
+
retainedCount: results.length,
|
|
18401
|
+
failedCount: failedWrites
|
|
18402
|
+
},
|
|
18403
|
+
timings: { extractMs, retainMs: Date.now() - tRetain },
|
|
18404
|
+
model: options.extract.model ?? DEFAULT_EXTRACTION_MODEL,
|
|
18352
18405
|
// Only meaningful alongside `outcome: "empty-after-retry"`. Returned as well
|
|
18353
18406
|
// as pushed through the callback so a consumer that only inspects the result
|
|
18354
18407
|
// (rather than wiring a hook) can still report WHY.
|
|
@@ -18604,27 +18657,23 @@ function createAutoExtractor(options) {
|
|
|
18604
18657
|
const t0 = Date.now();
|
|
18605
18658
|
void (async () => {
|
|
18606
18659
|
try {
|
|
18607
|
-
const { candidates, results, failedCount, outcome, failure } = await extractAndRetain(
|
|
18608
|
-
|
|
18609
|
-
|
|
18610
|
-
{
|
|
18611
|
-
|
|
18612
|
-
|
|
18613
|
-
|
|
18614
|
-
|
|
18615
|
-
|
|
18616
|
-
|
|
18617
|
-
|
|
18618
|
-
|
|
18619
|
-
|
|
18620
|
-
|
|
18621
|
-
|
|
18622
|
-
},
|
|
18623
|
-
...options.onMemoryQuarantined && {
|
|
18624
|
-
onQuarantined: (info) => options.onMemoryQuarantined?.({ ...info, conversationId })
|
|
18625
|
-
}
|
|
18660
|
+
const { candidates, results, failedCount, outcome, failure, funnel, timings, model } = await extractAndRetain(window2, retainCtx, {
|
|
18661
|
+
extract,
|
|
18662
|
+
...options.minConfidence !== void 0 && { minConfidence: options.minConfidence },
|
|
18663
|
+
...options.entityCtx !== void 0 && { entityCtx: options.entityCtx },
|
|
18664
|
+
...options.scope !== void 0 && { scope: options.scope },
|
|
18665
|
+
...options.folderId !== void 0 && { folderId: options.folderId },
|
|
18666
|
+
...consolidateOptions !== void 0 && { consolidateOptions },
|
|
18667
|
+
...injectionClassifierOptions !== void 0 && {
|
|
18668
|
+
injectionClassifier: injectionClassifierOptions
|
|
18669
|
+
},
|
|
18670
|
+
...options.onCandidateFailed && {
|
|
18671
|
+
onCandidateFailed: (candidate, error) => options.onCandidateFailed?.({ candidate, error, conversationId })
|
|
18672
|
+
},
|
|
18673
|
+
...options.onMemoryQuarantined && {
|
|
18674
|
+
onQuarantined: (info) => options.onMemoryQuarantined?.({ ...info, conversationId })
|
|
18626
18675
|
}
|
|
18627
|
-
);
|
|
18676
|
+
});
|
|
18628
18677
|
if (outcome !== "empty-after-retry") {
|
|
18629
18678
|
const advancedTo = window2[window2.length - 1].id;
|
|
18630
18679
|
stateFor(conversationId).watermark = advancedTo;
|
|
@@ -18645,6 +18694,9 @@ function createAutoExtractor(options) {
|
|
|
18645
18694
|
durationMs: Date.now() - t0,
|
|
18646
18695
|
conversationId,
|
|
18647
18696
|
outcome,
|
|
18697
|
+
funnel,
|
|
18698
|
+
timings,
|
|
18699
|
+
model,
|
|
18648
18700
|
// Only set alongside `outcome: "empty-after-retry"`. Spread so the key
|
|
18649
18701
|
// is absent rather than explicitly undefined on a healthy turn —
|
|
18650
18702
|
// analytics backends store an explicit undefined as a real value.
|