@memberjunction/ai-vector-dupe 6.2.0-edge.1 → 6.2.0-edge.2

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.
Files changed (36) hide show
  1. package/README.md +7 -5
  2. package/dist/duplicateEntryCheckTypes.d.ts +80 -0
  3. package/dist/duplicateEntryCheckTypes.d.ts.map +1 -0
  4. package/dist/duplicateEntryCheckTypes.js +29 -0
  5. package/dist/duplicateEntryCheckTypes.js.map +1 -0
  6. package/dist/duplicateRecordDetector.d.ts +152 -12
  7. package/dist/duplicateRecordDetector.d.ts.map +1 -1
  8. package/dist/duplicateRecordDetector.js +434 -48
  9. package/dist/duplicateRecordDetector.js.map +1 -1
  10. package/dist/entryCheckDeadline.d.ts +73 -0
  11. package/dist/entryCheckDeadline.d.ts.map +1 -0
  12. package/dist/entryCheckDeadline.js +118 -0
  13. package/dist/entryCheckDeadline.js.map +1 -0
  14. package/dist/index.d.ts +6 -1
  15. package/dist/index.d.ts.map +1 -1
  16. package/dist/index.js +4 -1
  17. package/dist/index.js.map +1 -1
  18. package/dist/reasoning/DecisionReasoningProvider.d.ts +177 -0
  19. package/dist/reasoning/DecisionReasoningProvider.d.ts.map +1 -0
  20. package/dist/reasoning/DecisionReasoningProvider.js +306 -0
  21. package/dist/reasoning/DecisionReasoningProvider.js.map +1 -0
  22. package/dist/reasoning/DecisionThenPromptReasoningProvider.d.ts +88 -0
  23. package/dist/reasoning/DecisionThenPromptReasoningProvider.d.ts.map +1 -0
  24. package/dist/reasoning/DecisionThenPromptReasoningProvider.js +159 -0
  25. package/dist/reasoning/DecisionThenPromptReasoningProvider.js.map +1 -0
  26. package/dist/reasoning/DuplicateReasoningProvider.d.ts +12 -6
  27. package/dist/reasoning/DuplicateReasoningProvider.d.ts.map +1 -1
  28. package/dist/reasoning/DuplicateReasoningProvider.js +12 -6
  29. package/dist/reasoning/DuplicateReasoningProvider.js.map +1 -1
  30. package/dist/reasoning/DuplicateReasoningTypes.d.ts +21 -2
  31. package/dist/reasoning/DuplicateReasoningTypes.d.ts.map +1 -1
  32. package/dist/reasoning/MatchedSetDeltaBuilder.d.ts +17 -3
  33. package/dist/reasoning/MatchedSetDeltaBuilder.d.ts.map +1 -1
  34. package/dist/reasoning/MatchedSetDeltaBuilder.js +49 -2
  35. package/dist/reasoning/MatchedSetDeltaBuilder.js.map +1 -1
  36. package/package.json +14 -14
@@ -0,0 +1,177 @@
1
+ /**
2
+ * @fileoverview 'Decision' mode reasoning provider.
3
+ *
4
+ * Asks a typed decision model one Likelihood per candidate ("the candidate record is the same
5
+ * real-world entity as the new record") in a single `AIDecisionRunner` call per matched set. The
6
+ * state is the same fields the prompt provider renders, from the same helper, serialized compactly.
7
+ *
8
+ * It only **recommends**: a candidate whose probability is at or above
9
+ * {@link DecisionReasoningProvider.UncertainAbove} is `Uncertain` and flagged for a person; one below
10
+ * it is `NotDuplicate`. It never emits `Merge` and never proposes a survivor or a field map, so a
11
+ * `Decision`-mode set is never auto-merge eligible (typed-decision plan §3.6, rule 8). Survivor and
12
+ * field choices remain LLM work: see `DecisionThenPromptReasoningProvider`.
13
+ *
14
+ * A failed decision is a failed reasoning call, handled as the `Prompt` mode handles one: the
15
+ * detector saves the set's match rows with empty LLM columns, so every candidate stays `Pending`
16
+ * for review and none is auto-merge eligible.
17
+ *
18
+ * @module @memberjunction/ai-vector-dupe
19
+ */
20
+ import { AIDecisionParams } from '@memberjunction/ai-prompts';
21
+ import { type DecisionAnsweringModel, type DecisionModelCalibration, type MJAIPromptEntityExtended } from '@memberjunction/ai-core-plus';
22
+ import { type DecisionQuestion, type PlattCalibration } from '@memberjunction/ai';
23
+ import { DuplicateReasoningProvider } from './DuplicateReasoningProvider.js';
24
+ import { DuplicateReasoningInput, DuplicateReasoningOutput, DuplicateReasoningContext, DuplicateReasoningCandidateVerdict, ReasoningCandidate, ReasoningSourceRecord } from './DuplicateReasoningTypes.js';
25
+ /** One candidate's probability of being the same real-world entity as the source record. */
26
+ export interface DuplicateCandidateProbability {
27
+ /** The candidate record id (matches the input candidate's RecordID). */
28
+ RecordID: string;
29
+ /**
30
+ * The Likelihood's probability in [0, 1], **calibrated** for the model that answered (see
31
+ * {@link DUPLICATE_DECISION_CALIBRATION}). Null when the decision returned no answer for this
32
+ * candidate, or when the answering model has no calibration.
33
+ */
34
+ Probability: number | null;
35
+ /** The model's own probability, before calibration; null when there was no answer. */
36
+ RawProbability?: number | null;
37
+ }
38
+ /**
39
+ * Platt calibration of the duplicate Likelihood ("the candidate is the same real-world entity as
40
+ * the new record"), per decision model, each tied to the exact model it was fitted on (see
41
+ * `FindDecisionCalibration` in `@memberjunction/ai-core-plus`): the MJ decision model that answered
42
+ * (`ModelName`) and the model the driver reports behind it (`ResolvedModel`). A model's raw
43
+ * probabilities are not calibrated: Jev ranks candidates almost perfectly (AUC 0.99) but its raw
44
+ * probabilities run high for similar records that are not duplicates.
45
+ *
46
+ * Any other model, including Jev at another version or `LLM Decision` answered by another chat
47
+ * model, gives no calibrated probability, since its raw one can't be banded. The entry check then
48
+ * flags nothing, as it does for a failed decision; batch `Decision` mode bands its candidates
49
+ * `Uncertain` for review; and `DecisionThenPrompt` sends them all to the prompt. The provider logs
50
+ * the missing calibration once per model. An app that binds another decision model supplies its
51
+ * calibration by overriding {@link DecisionReasoningProvider.CalibrationFor}.
52
+ *
53
+ * Fitted on the duplicate-check measurement (plan Task 3.8, 2026-09-29), on every candidate: 160
54
+ * labelled new records for MJ: Actions (80 rewrites of an existing action, 80 similar actions that
55
+ * don't exist), five vector candidates each.
56
+ * - **Jev** at its pinned `APIName`, `typesafe/jev-1.13-20260917`, which OpenRouter reports back as
57
+ * the resolved model.
58
+ * - **LLM Decision** when its chat model is GPT-OSS-120B, its `LLM Decision` prompt's first choice.
59
+ * The stored runs don't record the resolved model; this is the prompt's binding on that day.
60
+ *
61
+ * Refit whenever a model's pinned version, its delegated chat model or the question changes.
62
+ */
63
+ export declare const DUPLICATE_DECISION_CALIBRATION: readonly DecisionModelCalibration<PlattCalibration>[];
64
+ /** The shipped calibration for the exact model that answered, or null when it has none. */
65
+ export declare function DuplicateDecisionCalibrationFor(answeredBy: DecisionAnsweringModel): PlattCalibration | null;
66
+ /**
67
+ * The duplicate Likelihood calibrated for the exact model that answered, or null when that model
68
+ * has no calibration.
69
+ */
70
+ export declare function CalibratedDuplicateProbability(probability: number, answeredBy: DecisionAnsweringModel): number | null;
71
+ /** The outcome of one decision call over a matched set. */
72
+ export interface DuplicateDecisionResult {
73
+ /** Whether the decision call succeeded. */
74
+ Success: boolean;
75
+ /** Why the call failed, when {@link Success} is false. */
76
+ ErrorMessage?: string;
77
+ /** One entry per input candidate, in input order. Empty when the call failed. */
78
+ Candidates: DuplicateCandidateProbability[];
79
+ /** The decision's `MJ: AI Prompt Runs` row id, when the runner wrote one. */
80
+ AIPromptRunID: string | null;
81
+ /**
82
+ * Set when the exact model that answered has no calibration: the model, as
83
+ * `DescribeAnsweringModel` names it (`Jev (typesafe/jev-1.14-...)`). Every candidate's
84
+ * `Probability` is then null and its `RawProbability` holds the model's own answer. The entry
85
+ * check treats such a result like a failed decision and flags nothing.
86
+ */
87
+ UncalibratedModel?: string;
88
+ }
89
+ /**
90
+ * Typed-decision reasoning provider. Registered under the 'Decision' `ReasoningMode`.
91
+ */
92
+ export declare class DecisionReasoningProvider extends DuplicateReasoningProvider {
93
+ /**
94
+ * The flagging threshold used when none is passed, as it is when the class factory builds the
95
+ * provider, on **calibrated** probabilities. A flag asks a person to look, so it favours precision:
96
+ * a missed duplicate is only today's behaviour, and needless flags teach people to ignore them.
97
+ * At a calibrated 0.7, scored out of fold, Jev flagged the true source at 96.6% precision, caught
98
+ * 70% of duplicates, and flagged 2.5% of new records; LLM Decision scored 74%, 36% and 12.5%. The
99
+ * vector threshold alone flagged every candidate. (Duplicate-check measurement, plan Task 3.8,
100
+ * 2026-09-29, re-scored 2026-09-30.)
101
+ */
102
+ static readonly DEFAULT_UNCERTAIN_ABOVE = 0.7;
103
+ /**
104
+ * The threshold for the decision stage of `DecisionThenPrompt`, on calibrated probabilities. There
105
+ * the decision only drops implausible candidates before the prompt reasons over the rest, so it
106
+ * keeps recall: at a calibrated 0.3, scored out of fold, Jev kept 98.8% of true duplicates and passed
107
+ * 14.8% of candidates.
108
+ */
109
+ static readonly PRE_FILTER_UNCERTAIN_ABOVE = 0.3;
110
+ /** The seeded decision prompt whose model bindings choose the decision model. */
111
+ static readonly DEFAULT_PROMPT_NAME = "Default Decision";
112
+ /** A candidate at or above this probability is `Uncertain` (flagged for a person); below it, `NotDuplicate`. */
113
+ readonly UncertainAbove: number;
114
+ /**
115
+ * @param uncertainAbove the flagging threshold in [0, 1]. Defaults to
116
+ * {@link DecisionReasoningProvider.DEFAULT_UNCERTAIN_ABOVE}.
117
+ */
118
+ constructor(uncertainAbove?: number);
119
+ /**
120
+ * Reason over a matched set with one decision call. A failed call returns `Success: false` with
121
+ * the error and the decision's run id, and no verdicts: the detector writes no LLM columns for a
122
+ * failed set, so no candidate is marked `NotDuplicate` and none is merged.
123
+ */
124
+ Reason(input: DuplicateReasoningInput, context: DuplicateReasoningContext): Promise<DuplicateReasoningOutput>;
125
+ /**
126
+ * Run the decision for a matched set: one `AIDecisionRunner` call, with one Likelihood per
127
+ * candidate. The context's `CancellationToken` and `TimeoutMS`, when set, bound the model call.
128
+ * Never throws. A set with no candidates succeeds without a call.
129
+ */
130
+ DecideCandidates(input: DuplicateReasoningInput, context: DuplicateReasoningContext): Promise<DuplicateDecisionResult>;
131
+ /**
132
+ * Whether a candidate stays in play: its probability is at or above {@link UncertainAbove}, or
133
+ * unknown. An unknown probability fails toward inclusion.
134
+ */
135
+ IsPlausible(probability: number | null): boolean;
136
+ /** Band one candidate's probability into `Uncertain` or `NotDuplicate`, carrying the probability as its confidence. */
137
+ BandCandidate(candidate: DuplicateCandidateProbability): DuplicateReasoningCandidateVerdict;
138
+ /**
139
+ * Turn a successful decision into the reasoning contract. Recommends only: no `Merge`, no
140
+ * survivor and no field choices.
141
+ */
142
+ RecommendFromDecision(decision: DuplicateDecisionResult): DuplicateReasoningOutput;
143
+ /** Resolve the decision prompt by name from the AIEngine cache (no DB query). */
144
+ protected ResolveDecisionPrompt(): MJAIPromptEntityExtended | null;
145
+ /**
146
+ * The decision's state: the fields the prompt provider renders, from the same helper
147
+ * ({@link DuplicateReasoningProvider.buildPromptData}), serialized as compact JSON.
148
+ */
149
+ protected BuildDecisionState(input: DuplicateReasoningInput): string;
150
+ /** One Likelihood per candidate, keyed by {@link QuestionKey}. */
151
+ protected BuildQuestions(input: DuplicateReasoningInput): Record<string, DecisionQuestion>;
152
+ /** The question key for the candidate at `index`. A label for code only: the model never reads it. */
153
+ protected QuestionKey(index: number): string;
154
+ /**
155
+ * The statement the model judges for one candidate. It names both records by label and record id,
156
+ * because the model reads only the instructions and the state, and duplicates often share a label.
157
+ */
158
+ protected BuildQuestionInstructions(source: ReasoningSourceRecord, candidate: ReasoningCandidate): string;
159
+ protected BuildDecisionParams(prompt: MJAIPromptEntityExtended, input: DuplicateReasoningInput, context: DuplicateReasoningContext): AIDecisionParams;
160
+ /**
161
+ * The calibration for the exact model that answered (the MJ decision model and the model the
162
+ * driver reports behind it), or null when it has none. Defaults to
163
+ * {@link DUPLICATE_DECISION_CALIBRATION}; override to calibrate another model.
164
+ */
165
+ protected CalibrationFor(answeredBy: DecisionAnsweringModel): PlattCalibration | null;
166
+ /** Read each candidate's probability from the run, in input order, calibrated for the model that answered. */
167
+ private readProbabilities;
168
+ /** Log a model with no calibration the first time it answers, not on every decision. */
169
+ private logUncalibratedOnce;
170
+ private likelihoodOf;
171
+ private failedDecision;
172
+ /** A failed decision: `Success: false`, with the error and the decision's run id. */
173
+ private failedDecisionOutput;
174
+ private describeBand;
175
+ private summarize;
176
+ }
177
+ //# sourceMappingURL=DecisionReasoningProvider.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"DecisionReasoningProvider.d.ts","sourceRoot":"","sources":["../../src/reasoning/DecisionReasoningProvider.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,306 @@
1
+ /**
2
+ * @fileoverview 'Decision' mode reasoning provider.
3
+ *
4
+ * Asks a typed decision model one Likelihood per candidate ("the candidate record is the same
5
+ * real-world entity as the new record") in a single `AIDecisionRunner` call per matched set. The
6
+ * state is the same fields the prompt provider renders, from the same helper, serialized compactly.
7
+ *
8
+ * It only **recommends**: a candidate whose probability is at or above
9
+ * {@link DecisionReasoningProvider.UncertainAbove} is `Uncertain` and flagged for a person; one below
10
+ * it is `NotDuplicate`. It never emits `Merge` and never proposes a survivor or a field map, so a
11
+ * `Decision`-mode set is never auto-merge eligible (typed-decision plan §3.6, rule 8). Survivor and
12
+ * field choices remain LLM work: see `DecisionThenPromptReasoningProvider`.
13
+ *
14
+ * A failed decision is a failed reasoning call, handled as the `Prompt` mode handles one: the
15
+ * detector saves the set's match rows with empty LLM columns, so every candidate stays `Pending`
16
+ * for review and none is auto-merge eligible.
17
+ *
18
+ * @module @memberjunction/ai-vector-dupe
19
+ */
20
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
21
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
22
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
23
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
24
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
25
+ };
26
+ var __metadata = (this && this.__metadata) || function (k, v) {
27
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
28
+ };
29
+ var DecisionReasoningProvider_1;
30
+ import { LogError } from '@memberjunction/core';
31
+ import { RegisterClass } from '@memberjunction/global';
32
+ import { AIEngine } from '@memberjunction/aiengine';
33
+ import { AIDecisionRunner, AIDecisionParams } from '@memberjunction/ai-prompts';
34
+ import { DescribeAnsweringModel, FindDecisionCalibration } from '@memberjunction/ai-core-plus';
35
+ import { ApplyPlattCalibration } from '@memberjunction/ai';
36
+ import { DuplicateReasoningProvider, DECISION_REASONING_PROVIDER_KEY } from './DuplicateReasoningProvider.js';
37
+ /**
38
+ * Platt calibration of the duplicate Likelihood ("the candidate is the same real-world entity as
39
+ * the new record"), per decision model, each tied to the exact model it was fitted on (see
40
+ * `FindDecisionCalibration` in `@memberjunction/ai-core-plus`): the MJ decision model that answered
41
+ * (`ModelName`) and the model the driver reports behind it (`ResolvedModel`). A model's raw
42
+ * probabilities are not calibrated: Jev ranks candidates almost perfectly (AUC 0.99) but its raw
43
+ * probabilities run high for similar records that are not duplicates.
44
+ *
45
+ * Any other model, including Jev at another version or `LLM Decision` answered by another chat
46
+ * model, gives no calibrated probability, since its raw one can't be banded. The entry check then
47
+ * flags nothing, as it does for a failed decision; batch `Decision` mode bands its candidates
48
+ * `Uncertain` for review; and `DecisionThenPrompt` sends them all to the prompt. The provider logs
49
+ * the missing calibration once per model. An app that binds another decision model supplies its
50
+ * calibration by overriding {@link DecisionReasoningProvider.CalibrationFor}.
51
+ *
52
+ * Fitted on the duplicate-check measurement (plan Task 3.8, 2026-09-29), on every candidate: 160
53
+ * labelled new records for MJ: Actions (80 rewrites of an existing action, 80 similar actions that
54
+ * don't exist), five vector candidates each.
55
+ * - **Jev** at its pinned `APIName`, `typesafe/jev-1.13-20260917`, which OpenRouter reports back as
56
+ * the resolved model.
57
+ * - **LLM Decision** when its chat model is GPT-OSS-120B, its `LLM Decision` prompt's first choice.
58
+ * The stored runs don't record the resolved model; this is the prompt's binding on that day.
59
+ *
60
+ * Refit whenever a model's pinned version, its delegated chat model or the question changes.
61
+ */
62
+ export const DUPLICATE_DECISION_CALIBRATION = Object.freeze([
63
+ Object.freeze({ ModelName: 'Jev', ResolvedModel: 'typesafe/jev-1.13-20260917', Calibration: Object.freeze({ A: 2.5855, B: -4.4485 }) }),
64
+ Object.freeze({ ModelName: 'LLM Decision', ResolvedModel: 'GPT-OSS-120B', Calibration: Object.freeze({ A: 0.9918, B: -1.4843 }) })
65
+ ]);
66
+ /** The shipped calibration for the exact model that answered, or null when it has none. */
67
+ export function DuplicateDecisionCalibrationFor(answeredBy) {
68
+ return FindDecisionCalibration(DUPLICATE_DECISION_CALIBRATION, answeredBy);
69
+ }
70
+ /**
71
+ * The duplicate Likelihood calibrated for the exact model that answered, or null when that model
72
+ * has no calibration.
73
+ */
74
+ export function CalibratedDuplicateProbability(probability, answeredBy) {
75
+ const calibration = DuplicateDecisionCalibrationFor(answeredBy);
76
+ return calibration ? ApplyPlattCalibration(probability, calibration) : null;
77
+ }
78
+ /** Models already logged as having no calibration, so each is logged once per process. */
79
+ const loggedUncalibratedModels = new Set();
80
+ /**
81
+ * Typed-decision reasoning provider. Registered under the 'Decision' `ReasoningMode`.
82
+ */
83
+ let DecisionReasoningProvider = class DecisionReasoningProvider extends DuplicateReasoningProvider {
84
+ static { DecisionReasoningProvider_1 = this; }
85
+ /**
86
+ * The flagging threshold used when none is passed, as it is when the class factory builds the
87
+ * provider, on **calibrated** probabilities. A flag asks a person to look, so it favours precision:
88
+ * a missed duplicate is only today's behaviour, and needless flags teach people to ignore them.
89
+ * At a calibrated 0.7, scored out of fold, Jev flagged the true source at 96.6% precision, caught
90
+ * 70% of duplicates, and flagged 2.5% of new records; LLM Decision scored 74%, 36% and 12.5%. The
91
+ * vector threshold alone flagged every candidate. (Duplicate-check measurement, plan Task 3.8,
92
+ * 2026-09-29, re-scored 2026-09-30.)
93
+ */
94
+ static { this.DEFAULT_UNCERTAIN_ABOVE = 0.7; }
95
+ /**
96
+ * The threshold for the decision stage of `DecisionThenPrompt`, on calibrated probabilities. There
97
+ * the decision only drops implausible candidates before the prompt reasons over the rest, so it
98
+ * keeps recall: at a calibrated 0.3, scored out of fold, Jev kept 98.8% of true duplicates and passed
99
+ * 14.8% of candidates.
100
+ */
101
+ static { this.PRE_FILTER_UNCERTAIN_ABOVE = 0.3; }
102
+ /** The seeded decision prompt whose model bindings choose the decision model. */
103
+ static { this.DEFAULT_PROMPT_NAME = 'Default Decision'; }
104
+ /**
105
+ * @param uncertainAbove the flagging threshold in [0, 1]. Defaults to
106
+ * {@link DecisionReasoningProvider.DEFAULT_UNCERTAIN_ABOVE}.
107
+ */
108
+ constructor(uncertainAbove = DecisionReasoningProvider_1.DEFAULT_UNCERTAIN_ABOVE) {
109
+ super();
110
+ this.UncertainAbove = uncertainAbove;
111
+ }
112
+ /**
113
+ * Reason over a matched set with one decision call. A failed call returns `Success: false` with
114
+ * the error and the decision's run id, and no verdicts: the detector writes no LLM columns for a
115
+ * failed set, so no candidate is marked `NotDuplicate` and none is merged.
116
+ */
117
+ async Reason(input, context) {
118
+ const decision = await this.DecideCandidates(input, context);
119
+ return decision.Success
120
+ ? this.RecommendFromDecision(decision)
121
+ : this.failedDecisionOutput(decision);
122
+ }
123
+ /**
124
+ * Run the decision for a matched set: one `AIDecisionRunner` call, with one Likelihood per
125
+ * candidate. The context's `CancellationToken` and `TimeoutMS`, when set, bound the model call.
126
+ * Never throws. A set with no candidates succeeds without a call.
127
+ */
128
+ async DecideCandidates(input, context) {
129
+ if (input.Candidates.length === 0) {
130
+ return { Success: true, Candidates: [], AIPromptRunID: null };
131
+ }
132
+ try {
133
+ await AIEngine.Instance.Config(false, context.ContextUser, context.Provider);
134
+ const prompt = this.ResolveDecisionPrompt();
135
+ if (!prompt) {
136
+ return this.failedDecision(`Decision prompt "${DecisionReasoningProvider_1.DEFAULT_PROMPT_NAME}" not found`, null);
137
+ }
138
+ const run = await new AIDecisionRunner().ExecuteDecision(this.BuildDecisionParams(prompt, input, context));
139
+ return this.readProbabilities(input, run);
140
+ }
141
+ catch (e) {
142
+ LogError(e);
143
+ return this.failedDecision(e instanceof Error ? e.message : String(e), null);
144
+ }
145
+ }
146
+ /**
147
+ * Whether a candidate stays in play: its probability is at or above {@link UncertainAbove}, or
148
+ * unknown. An unknown probability fails toward inclusion.
149
+ */
150
+ IsPlausible(probability) {
151
+ return probability == null || probability >= this.UncertainAbove;
152
+ }
153
+ /** Band one candidate's probability into `Uncertain` or `NotDuplicate`, carrying the probability as its confidence. */
154
+ BandCandidate(candidate) {
155
+ const flagged = this.IsPlausible(candidate.Probability);
156
+ return {
157
+ RecordID: candidate.RecordID,
158
+ Recommendation: flagged ? 'Uncertain' : 'NotDuplicate',
159
+ Confidence: candidate.Probability,
160
+ Reasoning: this.describeBand(candidate.Probability, flagged, candidate.RawProbability)
161
+ };
162
+ }
163
+ /**
164
+ * Turn a successful decision into the reasoning contract. Recommends only: no `Merge`, no
165
+ * survivor and no field choices.
166
+ */
167
+ RecommendFromDecision(decision) {
168
+ const verdicts = decision.Candidates.map(c => this.BandCandidate(c));
169
+ const overall = this.deriveOverall(verdicts);
170
+ return {
171
+ Success: true,
172
+ Recommendation: overall.Recommendation,
173
+ Confidence: overall.Confidence,
174
+ Reasoning: this.summarize(verdicts),
175
+ CandidateVerdicts: verdicts,
176
+ SurvivorRecordID: null,
177
+ FieldChoices: [],
178
+ AIPromptRunID: decision.AIPromptRunID
179
+ };
180
+ }
181
+ /** Resolve the decision prompt by name from the AIEngine cache (no DB query). */
182
+ ResolveDecisionPrompt() {
183
+ const target = DecisionReasoningProvider_1.DEFAULT_PROMPT_NAME.toLowerCase();
184
+ return AIEngine.Instance.Prompts.find(p => (p.Name ?? '').trim().toLowerCase() === target) ?? null;
185
+ }
186
+ /**
187
+ * The decision's state: the fields the prompt provider renders, from the same helper
188
+ * ({@link DuplicateReasoningProvider.buildPromptData}), serialized as compact JSON.
189
+ */
190
+ BuildDecisionState(input) {
191
+ return JSON.stringify(this.buildPromptData(input));
192
+ }
193
+ /** One Likelihood per candidate, keyed by {@link QuestionKey}. */
194
+ BuildQuestions(input) {
195
+ const questions = {};
196
+ input.Candidates.forEach((candidate, index) => {
197
+ questions[this.QuestionKey(index)] = {
198
+ Kind: 'Likelihood',
199
+ Instructions: this.BuildQuestionInstructions(input.SourceRecord, candidate)
200
+ };
201
+ });
202
+ return questions;
203
+ }
204
+ /** The question key for the candidate at `index`. A label for code only: the model never reads it. */
205
+ QuestionKey(index) {
206
+ return `candidate_${index + 1}`;
207
+ }
208
+ /**
209
+ * The statement the model judges for one candidate. It names both records by label and record id,
210
+ * because the model reads only the instructions and the state, and duplicates often share a label.
211
+ */
212
+ BuildQuestionInstructions(source, candidate) {
213
+ return `The candidate record "${candidate.Label}" (recordId ${candidate.RecordID}) is the same real-world entity ` +
214
+ `as the new record, the sourceRecord "${source.Label}" (recordId ${source.RecordID}).`;
215
+ }
216
+ BuildDecisionParams(prompt, input, context) {
217
+ const params = new AIDecisionParams();
218
+ params.prompt = prompt;
219
+ params.contextUser = context.ContextUser;
220
+ params.cancellationToken = context.CancellationToken;
221
+ params.timeoutMS = context.TimeoutMS;
222
+ params.State = this.BuildDecisionState(input);
223
+ params.Questions = this.BuildQuestions(input);
224
+ return params;
225
+ }
226
+ /**
227
+ * The calibration for the exact model that answered (the MJ decision model and the model the
228
+ * driver reports behind it), or null when it has none. Defaults to
229
+ * {@link DUPLICATE_DECISION_CALIBRATION}; override to calibrate another model.
230
+ */
231
+ CalibrationFor(answeredBy) {
232
+ return DuplicateDecisionCalibrationFor(answeredBy);
233
+ }
234
+ /** Read each candidate's probability from the run, in input order, calibrated for the model that answered. */
235
+ readProbabilities(input, run) {
236
+ const runID = run.promptRun?.ID ?? null;
237
+ if (!run.success) {
238
+ return this.failedDecision(run.errorMessage ?? 'Decision execution failed', runID);
239
+ }
240
+ const answeredBy = {
241
+ ModelName: run.modelInfo?.modelName,
242
+ ResolvedModel: run.DecisionResult?.ResolvedModel
243
+ };
244
+ const calibration = this.CalibrationFor(answeredBy);
245
+ const candidates = input.Candidates.map((candidate, index) => {
246
+ const raw = this.likelihoodOf(run.Answers[this.QuestionKey(index)]);
247
+ return {
248
+ RecordID: candidate.RecordID,
249
+ Probability: raw == null || !calibration ? null : ApplyPlattCalibration(raw, calibration),
250
+ RawProbability: raw
251
+ };
252
+ });
253
+ const result = { Success: true, Candidates: candidates, AIPromptRunID: runID };
254
+ if (!calibration) {
255
+ result.UncalibratedModel = DescribeAnsweringModel(answeredBy);
256
+ this.logUncalibratedOnce(result.UncalibratedModel);
257
+ }
258
+ return result;
259
+ }
260
+ /** Log a model with no calibration the first time it answers, not on every decision. */
261
+ logUncalibratedOnce(model) {
262
+ if (loggedUncalibratedModels.has(model)) {
263
+ return;
264
+ }
265
+ loggedUncalibratedModels.add(model);
266
+ LogError(`Duplicate decision: the model "${model}" has no calibration, so its probabilities can't be banded. ` +
267
+ 'The entry check flags nothing for it, and Decision mode flags its candidates for review. ' +
268
+ 'Add its calibration (DUPLICATE_DECISION_CALIBRATION, or override CalibrationFor).');
269
+ }
270
+ likelihoodOf(answer) {
271
+ return answer?.Kind === 'Likelihood' ? answer.Probability : null;
272
+ }
273
+ failedDecision(message, runID) {
274
+ return { Success: false, ErrorMessage: message, Candidates: [], AIPromptRunID: runID };
275
+ }
276
+ /** A failed decision: `Success: false`, with the error and the decision's run id. */
277
+ failedDecisionOutput(decision) {
278
+ const output = this.failedOutput(`Decision failed: ${decision.ErrorMessage ?? 'unknown error'}`);
279
+ output.AIPromptRunID = decision.AIPromptRunID;
280
+ return output;
281
+ }
282
+ describeBand(probability, flagged, rawProbability) {
283
+ if (probability == null && rawProbability != null) {
284
+ return `The decision model has no calibration (raw probability ${rawProbability.toFixed(2)}). Flagged for review.`;
285
+ }
286
+ if (probability == null) {
287
+ return 'The decision model returned no answer for this candidate. Flagged for review.';
288
+ }
289
+ const p = probability.toFixed(2);
290
+ const threshold = this.UncertainAbove.toFixed(2);
291
+ return flagged
292
+ ? `Decision model: ${p} probability this is the same entity, at or above ${threshold}. Flagged for review.`
293
+ : `Decision model: ${p} probability this is the same entity, below ${threshold}.`;
294
+ }
295
+ summarize(verdicts) {
296
+ const flagged = verdicts.filter(v => v.Recommendation === 'Uncertain').length;
297
+ return `Decision model flagged ${flagged} of ${verdicts.length} candidates for review ` +
298
+ `(probability at or above ${this.UncertainAbove.toFixed(2)}). Decision mode recommends only; it never merges.`;
299
+ }
300
+ };
301
+ DecisionReasoningProvider = DecisionReasoningProvider_1 = __decorate([
302
+ RegisterClass(DuplicateReasoningProvider, DECISION_REASONING_PROVIDER_KEY),
303
+ __metadata("design:paramtypes", [Number])
304
+ ], DecisionReasoningProvider);
305
+ export { DecisionReasoningProvider };
306
+ //# sourceMappingURL=DecisionReasoningProvider.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"DecisionReasoningProvider.js","sourceRoot":"","sources":["../../src/reasoning/DecisionReasoningProvider.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,88 @@
1
+ /**
2
+ * @fileoverview 'DecisionThenPrompt' mode reasoning provider: an explicit chain.
3
+ *
4
+ * The detector resolves exactly one provider per `ReasoningMode`, so a decision cannot feed the
5
+ * prompt provider through configuration alone. This provider chains them:
6
+ *
7
+ * 1. {@link DecisionReasoningProvider} runs one decision over the set and drops every candidate
8
+ * whose probability is below its threshold.
9
+ * 2. `PromptReasoningProvider` reasons over **only the survivors**, so auto-merge still works
10
+ * wherever the prompt provider allows it.
11
+ *
12
+ * Both stages resolve through the class factory under the 'Decision' and 'Prompt' modes, so an
13
+ * application's override of either mode applies inside the chain as well.
14
+ *
15
+ * With no survivors the set is `NotDuplicate` and the prompt never runs. If the decision fails,
16
+ * every candidate goes to the prompt provider, which is the `Prompt` mode's behaviour: narrowing is
17
+ * an optimisation, so its failure must never hide a candidate.
18
+ *
19
+ * Each match row points at the run that produced its verdict: a survivor's at the prompt run, a
20
+ * dropped candidate's at the decision run. A failed decision's run is named in the error log, since
21
+ * a match row holds only one prompt run id.
22
+ *
23
+ * @module @memberjunction/ai-vector-dupe
24
+ */
25
+ import { DuplicateReasoningProvider } from './DuplicateReasoningProvider.js';
26
+ import { DecisionReasoningProvider, DuplicateCandidateProbability } from './DecisionReasoningProvider.js';
27
+ import { DuplicateReasoningInput, DuplicateReasoningOutput, DuplicateReasoningContext } from './DuplicateReasoningTypes.js';
28
+ /**
29
+ * Chained reasoning provider: the decision filters, then the prompt reasons over the survivors.
30
+ * Registered under the 'DecisionThenPrompt' `ReasoningMode`.
31
+ */
32
+ export declare class DecisionThenPromptReasoningProvider extends DuplicateReasoningProvider {
33
+ /** The filter stage. Its `UncertainAbove` is the survival threshold. */
34
+ protected readonly DecisionStage: DecisionReasoningProvider;
35
+ /** The reasoning stage, which sees only the survivors. */
36
+ protected readonly PromptStage: DuplicateReasoningProvider;
37
+ /**
38
+ * The class factory passes no arguments, so both stages resolve through the class factory
39
+ * under the 'Decision' and 'Prompt' modes: an application's override of either mode applies
40
+ * inside the chain too.
41
+ *
42
+ * @param decisionStage the filter stage; defaults to {@link ResolveDecisionStage}, at the pre-filter
43
+ * threshold ({@link DecisionReasoningProvider.PRE_FILTER_UNCERTAIN_ABOVE}), which keeps recall
44
+ * @param promptStage the reasoning stage; defaults to {@link ResolvePromptStage}
45
+ */
46
+ constructor(decisionStage?: DecisionReasoningProvider, promptStage?: DuplicateReasoningProvider);
47
+ /**
48
+ * Filter the set with the decision, then reason over the survivors with the prompt.
49
+ */
50
+ Reason(input: DuplicateReasoningInput, context: DuplicateReasoningContext): Promise<DuplicateReasoningOutput>;
51
+ /**
52
+ * The input with the dropped candidates removed, from the candidate list and from the field
53
+ * deltas. A field whose remaining values no longer differ is dropped too, since the deltas carry
54
+ * differing fields only.
55
+ */
56
+ protected NarrowToSurvivors(input: DuplicateReasoningInput, dropped: DuplicateCandidateProbability[]): DuplicateReasoningInput;
57
+ /**
58
+ * The filter stage: the provider registered for the 'Decision' mode, built with the pre-filter
59
+ * threshold ({@link DecisionReasoningProvider.PRE_FILTER_UNCERTAIN_ABOVE}) as its `uncertainAbove`
60
+ * argument, since here the decision only drops implausible candidates and keeps recall. The chain
61
+ * needs its probability API, so a registration that is not a `DecisionReasoningProvider` falls
62
+ * back to the shipped one, at the same threshold.
63
+ */
64
+ protected ResolveDecisionStage(): DecisionReasoningProvider;
65
+ /** The reasoning stage: the provider registered for the 'Prompt' mode, else the shipped one. */
66
+ protected ResolvePromptStage(): DuplicateReasoningProvider;
67
+ /**
68
+ * The provider the class factory registers for a mode, built with `params` as its constructor
69
+ * arguments, or null when none is registered.
70
+ */
71
+ private resolveRegisteredProvider;
72
+ /** No candidate survived: `NotDuplicate` for the set, from the decision alone, with no prompt call. */
73
+ private noSurvivorsOutput;
74
+ /**
75
+ * Give each dropped candidate its own `NotDuplicate` verdict from the decision, carrying the
76
+ * decision's run id, so its match row neither falls back to the set-level recommendation nor
77
+ * points at a prompt run that never saw it. The prompt's verdicts and set-level fields are
78
+ * otherwise left as the prompt returned them.
79
+ */
80
+ private withDroppedVerdicts;
81
+ /** The failure for the log, naming the decision's run when the runner wrote one. */
82
+ private describeFailure;
83
+ private stillDiffers;
84
+ private recordIDSet;
85
+ /** Record ids compare case-insensitively (SQL Server and PostgreSQL case UUIDs differently). */
86
+ private normalizeRecordID;
87
+ }
88
+ //# sourceMappingURL=DecisionThenPromptReasoningProvider.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"DecisionThenPromptReasoningProvider.d.ts","sourceRoot":"","sources":["../../src/reasoning/DecisionThenPromptReasoningProvider.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAIH,OAAO,EACH,0BAA0B,EAI7B,MAAM,8BAA8B,CAAC;AAEtC,OAAO,EACH,yBAAyB,EACzB,6BAA6B,EAEhC,MAAM,6BAA6B,CAAC;AACrC,OAAO,EACH,uBAAuB,EACvB,wBAAwB,EACxB,yBAAyB,EAE5B,MAAM,2BAA2B,CAAC;AAEnC;;;GAGG;AACH,qBACa,mCAAoC,SAAQ,0BAA0B;IAC/E,wEAAwE;IACxE,SAAS,CAAC,QAAQ,CAAC,aAAa,EAAE,yBAAyB,CAAC;IAC5D,0DAA0D;IAC1D,SAAS,CAAC,QAAQ,CAAC,WAAW,EAAE,0BAA0B,CAAC;IAE3D;;;;;;;;OAQG;gBACS,aAAa,CAAC,EAAE,yBAAyB,EAAE,WAAW,CAAC,EAAE,0BAA0B;IAM/F;;OAEG;IACU,MAAM,CACf,KAAK,EAAE,uBAAuB,EAC9B,OAAO,EAAE,yBAAyB,GACnC,OAAO,CAAC,wBAAwB,CAAC;IAcpC;;;;OAIG;IACH,SAAS,CAAC,iBAAiB,CACvB,KAAK,EAAE,uBAAuB,EAC9B,OAAO,EAAE,6BAA6B,EAAE,GACzC,uBAAuB;IAY1B;;;;;;OAMG;IACH,SAAS,CAAC,oBAAoB,IAAI,yBAAyB;IAM3D,gGAAgG;IAChG,SAAS,CAAC,kBAAkB,IAAI,0BAA0B;IAI1D;;;OAGG;IACH,OAAO,CAAC,yBAAyB;IAKjC,uGAAuG;IACvG,OAAO,CAAC,iBAAiB;IAOzB;;;;;OAKG;IACH,OAAO,CAAC,mBAAmB;IAgB3B,oFAAoF;IACpF,OAAO,CAAC,eAAe;IAKvB,OAAO,CAAC,YAAY;IAIpB,OAAO,CAAC,WAAW;IAInB,gGAAgG;IAChG,OAAO,CAAC,iBAAiB;CAG5B"}