@duckcodeailabs/dql-agent 1.14.2 → 1.14.3-rc.1
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/agent-run-engine.d.ts +63 -2
- package/dist/agent-run-engine.d.ts.map +1 -1
- package/dist/agent-run-engine.js +864 -29
- package/dist/agent-run-engine.js.map +1 -1
- package/dist/agent-run-planner.d.ts +6 -0
- package/dist/agent-run-planner.d.ts.map +1 -1
- package/dist/agent-run-planner.js +2 -0
- package/dist/agent-run-planner.js.map +1 -1
- package/dist/agent-run-store.d.ts.map +1 -1
- package/dist/agent-run-store.js +48 -3
- package/dist/agent-run-store.js.map +1 -1
- package/dist/agentic/analyst-loop.d.ts +7 -1
- package/dist/agentic/analyst-loop.d.ts.map +1 -1
- package/dist/agentic/analyst-loop.js +163 -0
- package/dist/agentic/analyst-loop.js.map +1 -1
- package/dist/agentic/assumptions.d.ts +1 -1
- package/dist/agentic/assumptions.d.ts.map +1 -1
- package/dist/agentic/assumptions.js.map +1 -1
- package/dist/agentic/research-agent.d.ts.map +1 -1
- package/dist/agentic/research-agent.js +10 -1
- package/dist/agentic/research-agent.js.map +1 -1
- package/dist/agentic/sql-authorization.d.ts +8 -0
- package/dist/agentic/sql-authorization.d.ts.map +1 -1
- package/dist/agentic/sql-authorization.js +3 -0
- package/dist/agentic/sql-authorization.js.map +1 -1
- package/dist/analytical-compatibility.d.ts.map +1 -1
- package/dist/analytical-compatibility.js +8 -1
- package/dist/analytical-compatibility.js.map +1 -1
- package/dist/analytical-failure-repair.d.ts.map +1 -1
- package/dist/analytical-failure-repair.js +6 -2
- package/dist/analytical-failure-repair.js.map +1 -1
- package/dist/analytical-frame.d.ts +23 -1
- package/dist/analytical-frame.d.ts.map +1 -1
- package/dist/analytical-frame.js +322 -16
- package/dist/analytical-frame.js.map +1 -1
- package/dist/analytical-orchestration.d.ts +314 -3
- package/dist/analytical-orchestration.d.ts.map +1 -1
- package/dist/analytical-orchestration.js +486 -43
- package/dist/analytical-orchestration.js.map +1 -1
- package/dist/answer-loop.d.ts +64 -8
- package/dist/answer-loop.d.ts.map +1 -1
- package/dist/answer-loop.js +528 -67
- package/dist/answer-loop.js.map +1 -1
- package/dist/ask-observability/index.d.ts +11 -0
- package/dist/ask-observability/index.d.ts.map +1 -0
- package/dist/ask-observability/index.js +7 -0
- package/dist/ask-observability/index.js.map +1 -0
- package/dist/ask-observability/instrumentation.d.ts +35 -0
- package/dist/ask-observability/instrumentation.d.ts.map +1 -0
- package/dist/ask-observability/instrumentation.js +472 -0
- package/dist/ask-observability/instrumentation.js.map +1 -0
- package/dist/ask-observability/observer.d.ts +62 -0
- package/dist/ask-observability/observer.d.ts.map +1 -0
- package/dist/ask-observability/observer.js +537 -0
- package/dist/ask-observability/observer.js.map +1 -0
- package/dist/ask-observability/portable.d.ts +98 -0
- package/dist/ask-observability/portable.d.ts.map +1 -0
- package/dist/ask-observability/portable.js +527 -0
- package/dist/ask-observability/portable.js.map +1 -0
- package/dist/ask-observability/store.d.ts +130 -0
- package/dist/ask-observability/store.d.ts.map +1 -0
- package/dist/ask-observability/store.js +1114 -0
- package/dist/ask-observability/store.js.map +1 -0
- package/dist/ask-observability/types.d.ts +357 -0
- package/dist/ask-observability/types.d.ts.map +1 -0
- package/dist/ask-observability/types.js +2 -0
- package/dist/ask-observability/types.js.map +1 -0
- package/dist/ask-observability/utils.d.ts +21 -0
- package/dist/ask-observability/utils.d.ts.map +1 -0
- package/dist/ask-observability/utils.js +102 -0
- package/dist/ask-observability/utils.js.map +1 -0
- package/dist/generated-analytical-proposal.d.ts +37 -1
- package/dist/generated-analytical-proposal.d.ts.map +1 -1
- package/dist/generated-analytical-proposal.js +211 -3
- package/dist/generated-analytical-proposal.js.map +1 -1
- package/dist/governed-relational-compiler.d.ts +15 -0
- package/dist/governed-relational-compiler.d.ts.map +1 -1
- package/dist/governed-relational-compiler.js +99 -1
- package/dist/governed-relational-compiler.js.map +1 -1
- package/dist/index.d.ts +9 -7
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +6 -4
- package/dist/index.js.map +1 -1
- package/dist/intent-controller.d.ts +10 -0
- package/dist/intent-controller.d.ts.map +1 -1
- package/dist/intent-controller.js +1 -1
- package/dist/intent-controller.js.map +1 -1
- package/dist/meaning-resolution.d.ts +78 -6
- package/dist/meaning-resolution.d.ts.map +1 -1
- package/dist/meaning-resolution.js +331 -38
- package/dist/meaning-resolution.js.map +1 -1
- package/dist/metadata/analysis-planner.d.ts.map +1 -1
- package/dist/metadata/analysis-planner.js +21 -1
- package/dist/metadata/analysis-planner.js.map +1 -1
- package/dist/metadata/catalog.d.ts.map +1 -1
- package/dist/metadata/catalog.js +1 -1
- package/dist/metadata/catalog.js.map +1 -1
- package/dist/metadata/meaning-evidence.d.ts +9 -2
- package/dist/metadata/meaning-evidence.d.ts.map +1 -1
- package/dist/metadata/meaning-evidence.js +261 -11
- package/dist/metadata/meaning-evidence.js.map +1 -1
- package/dist/plan-execution-adapter.d.ts +9 -1
- package/dist/plan-execution-adapter.d.ts.map +1 -1
- package/dist/plan-execution-adapter.js +93 -13
- package/dist/plan-execution-adapter.js.map +1 -1
- package/dist/provider-egress.d.ts +24 -10
- package/dist/provider-egress.d.ts.map +1 -1
- package/dist/provider-egress.js +22 -17
- package/dist/provider-egress.js.map +1 -1
- package/dist/providers/claude.d.ts.map +1 -1
- package/dist/providers/claude.js +10 -9
- package/dist/providers/claude.js.map +1 -1
- package/dist/providers/dispatch.d.ts +27 -1
- package/dist/providers/dispatch.d.ts.map +1 -1
- package/dist/providers/dispatch.js +63 -1
- package/dist/providers/dispatch.js.map +1 -1
- package/dist/providers/gemini.js +10 -10
- package/dist/providers/gemini.js.map +1 -1
- package/dist/providers/index.d.ts +2 -2
- package/dist/providers/index.d.ts.map +1 -1
- package/dist/providers/index.js +1 -1
- package/dist/providers/index.js.map +1 -1
- package/dist/providers/ollama.js +15 -15
- package/dist/providers/ollama.js.map +1 -1
- package/dist/providers/openai.js +22 -22
- package/dist/providers/openai.js.map +1 -1
- package/dist/providers/types.d.ts +52 -0
- package/dist/providers/types.d.ts.map +1 -1
- package/dist/providers/types.js.map +1 -1
- package/dist/relationship-proof.d.ts +81 -0
- package/dist/relationship-proof.d.ts.map +1 -1
- package/dist/relationship-proof.js +228 -4
- package/dist/relationship-proof.js.map +1 -1
- package/dist/resolved-analytical-plan.d.ts +18 -4
- package/dist/resolved-analytical-plan.d.ts.map +1 -1
- package/dist/resolved-analytical-plan.js +764 -47
- package/dist/resolved-analytical-plan.js.map +1 -1
- package/dist/router.d.ts +10 -1
- package/dist/router.d.ts.map +1 -1
- package/dist/router.js +1785 -213
- package/dist/router.js.map +1 -1
- package/dist/semantic-bridge/compose.d.ts.map +1 -1
- package/dist/semantic-bridge/compose.js +23 -4
- package/dist/semantic-bridge/compose.js.map +1 -1
- package/package.json +4 -4
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
*/
|
|
8
8
|
import type { AnalyticalPolicyContract, AnalyticalQuestionFrameV2, MetricCapabilityContract } from '@duckcodeailabs/dql-core';
|
|
9
9
|
import type { KnowledgeLens } from './domain-context.js';
|
|
10
|
-
import { type ContextSourceCoverageV1 } from './analytical-orchestration.js';
|
|
10
|
+
import { type AnalyticalRequirementSetV1, type AnalyticalRequirementSeedV1, type ContextSourceCoverageV1, type SameSnapshotRoleExtensionV1 } from './analytical-orchestration.js';
|
|
11
11
|
export type AgentEvidenceKind = "certified_block" | "semantic_metric" | "semantic_member" | "dql_modeling" | "dbt_model" | "dbt_source" | "sql_table" | "sql_column";
|
|
12
12
|
export type AgentEvidenceTrustTier = "certified" | "semantic" | "governed_sql" | "exploratory";
|
|
13
13
|
export type AgentEvidenceCompatibility = "compatible" | "partial" | "incompatible" | "unknown";
|
|
@@ -78,6 +78,12 @@ export interface AgentEvidenceCandidate {
|
|
|
78
78
|
matchReasons: string[];
|
|
79
79
|
compatibility: AgentEvidenceCompatibility;
|
|
80
80
|
compatibilityFacts?: string[];
|
|
81
|
+
/**
|
|
82
|
+
* Host-authored role admission from one immutable semantic capability
|
|
83
|
+
* snapshot. A resolver may select this supplied card, but cannot mint or
|
|
84
|
+
* alter it; the selected metric capability remains the execution authority.
|
|
85
|
+
*/
|
|
86
|
+
sameSnapshotRoleExtension?: SameSnapshotRoleExtensionV1;
|
|
81
87
|
/** Normalized executable capability. Names alone never supply missing facts. */
|
|
82
88
|
analyticalCapability?: MetricCapabilityContract;
|
|
83
89
|
/** Deterministic certified-asset fit; ignored for non-certified evidence. */
|
|
@@ -86,15 +92,36 @@ export interface AgentEvidenceCandidate {
|
|
|
86
92
|
eligible?: boolean;
|
|
87
93
|
/** True only for an exact qualified/name/approved-alias match. */
|
|
88
94
|
exactMatch?: boolean;
|
|
95
|
+
/**
|
|
96
|
+
* Real membership in the immutable retrieval snapshot. Multiple lanes are
|
|
97
|
+
* retained because fusion must not erase whether a card came from vector,
|
|
98
|
+
* graph, lexical, exact, or the conversation continuation boundary.
|
|
99
|
+
*/
|
|
100
|
+
retrievalLanes?: Array<{
|
|
101
|
+
lane: 'exact' | 'lexical' | 'vector' | 'graph' | 'conversation';
|
|
102
|
+
rank?: number;
|
|
103
|
+
}>;
|
|
89
104
|
}
|
|
90
105
|
/**
|
|
91
106
|
* Return the authored output identity that proves a certified block can answer
|
|
92
|
-
* one requested measure.
|
|
93
|
-
* which are populated from declared/output-contract fields at indexing
|
|
94
|
-
*
|
|
95
|
-
*
|
|
107
|
+
* one requested measure. This normally reads only the block's own `output:`
|
|
108
|
+
* facts, which are populated from declared/output-contract fields at indexing
|
|
109
|
+
* time. An exact block-title request may additionally carry a
|
|
110
|
+
* `catalog-proven-output:` fact from the snapshot-local catalog fit. That fact
|
|
111
|
+
* is minted only after the catalog has already bound this exact title to a
|
|
112
|
+
* high-confidence certified answer contract; names, tags, examples,
|
|
113
|
+
* definitions, and unrelated retrieved metrics are intentionally absent.
|
|
96
114
|
*/
|
|
97
115
|
export declare function certifiedCandidateDeclaredMeasureOutput(candidate: AgentEvidenceCandidate, requested: string): string | undefined;
|
|
116
|
+
/**
|
|
117
|
+
* Return the block output that proves a requested display/grouping dimension.
|
|
118
|
+
* This is intentionally narrower than retrieval matching: only a declared
|
|
119
|
+
* output or the catalog-proven output bridge for an exact certified title may
|
|
120
|
+
* bind a dimension to the block executor. `customer_name` is a valid output
|
|
121
|
+
* for the business term `customer`; arbitrary attributes such as owner or
|
|
122
|
+
* sentiment are not.
|
|
123
|
+
*/
|
|
124
|
+
export declare function certifiedCandidateDeclaredDimensionOutput(candidate: AgentEvidenceCandidate, requested: string): string | undefined;
|
|
98
125
|
/**
|
|
99
126
|
* A certified tier can freeze only when the selected block itself declares
|
|
100
127
|
* every requested measure. This is deliberately stricter than retrieval
|
|
@@ -183,6 +210,32 @@ export interface MeaningResolution {
|
|
|
183
210
|
message: string;
|
|
184
211
|
candidateIds: string[];
|
|
185
212
|
}>;
|
|
213
|
+
/** Immutable host tuple supplied before the one bounded meaning call. */
|
|
214
|
+
hostRequirementSeed?: AnalyticalRequirementSeedV1;
|
|
215
|
+
/**
|
|
216
|
+
* Canonical metric-capability dimensions restored from a validated,
|
|
217
|
+
* server-issued clarification selection. This is host continuation state,
|
|
218
|
+
* not a model-provided query-intent field; RAP revalidates each ID against
|
|
219
|
+
* the selected metric capability before it can enter the frozen plan.
|
|
220
|
+
*/
|
|
221
|
+
structuredDimensionIds?: string[];
|
|
222
|
+
/** Content-safe record of model fields the host deliberately did not grant authority. */
|
|
223
|
+
overrideReceipts?: MeaningResolutionOverrideReceiptV1[];
|
|
224
|
+
/**
|
|
225
|
+
* Parse-only distinction for the bounded candidate-ID protocol. A provider
|
|
226
|
+
* may return a syntactically valid object with no selected IDs while omitting
|
|
227
|
+
* every clarification instruction. That is not itself a business
|
|
228
|
+
* clarification: the router may use a host-proven frozen plan, if one exists.
|
|
229
|
+
* An explicit provider clarification is intentionally never marked here.
|
|
230
|
+
*/
|
|
231
|
+
emptyCandidateBinding?: true;
|
|
232
|
+
}
|
|
233
|
+
export interface MeaningResolutionOverrideReceiptV1 {
|
|
234
|
+
version: 1;
|
|
235
|
+
field: 'interpreted_question' | 'question_type' | 'query_intent' | 'recommended_route' | 'analytical_frame' | 'candidate_selection' | 'member_binding';
|
|
236
|
+
action: 'host_preserved' | 'selection_accepted';
|
|
237
|
+
reason: string;
|
|
238
|
+
candidateIds?: string[];
|
|
186
239
|
}
|
|
187
240
|
export interface MeaningResolutionInput {
|
|
188
241
|
question: string;
|
|
@@ -192,6 +245,8 @@ export interface MeaningResolutionInput {
|
|
|
192
245
|
}>;
|
|
193
246
|
evidence: AgentRetrievalEvidence;
|
|
194
247
|
candidates: AgentEvidenceCandidate[];
|
|
248
|
+
/** Host-owned typed request tuple; never model-authored. */
|
|
249
|
+
requirementSeed?: AnalyticalRequirementSeedV1;
|
|
195
250
|
signal?: AbortSignal;
|
|
196
251
|
}
|
|
197
252
|
export type AgentMeaningResolver = (input: MeaningResolutionInput) => Promise<MeaningResolution>;
|
|
@@ -211,13 +266,30 @@ export declare function canonicalizeMetricMeasureCandidates(candidates: AgentEvi
|
|
|
211
266
|
/** Match only explicit qualified references; ordinary names still use resolution. */
|
|
212
267
|
export declare function findExplicitEvidenceReference(question: string, candidates: AgentEvidenceCandidate[]): AgentEvidenceCandidate | undefined;
|
|
213
268
|
/** Validate that a resolver cannot invent or select ineligible evidence. */
|
|
214
|
-
export declare function validateMeaningResolution(value: MeaningResolution, candidates: AgentEvidenceCandidate[], requestedMeasures?: string[]
|
|
269
|
+
export declare function validateMeaningResolution(value: MeaningResolution, candidates: AgentEvidenceCandidate[], requestedMeasures?: string[], options?: {
|
|
270
|
+
requirements?: AnalyticalRequirementSetV1;
|
|
271
|
+
}): {
|
|
215
272
|
ok: true;
|
|
216
273
|
resolution: MeaningResolution;
|
|
217
274
|
} | {
|
|
218
275
|
ok: false;
|
|
219
276
|
reason: string;
|
|
220
277
|
};
|
|
278
|
+
/**
|
|
279
|
+
* Merge the one model meaning response into the host-owned request seed.
|
|
280
|
+
*
|
|
281
|
+
* Candidate IDs are the only model-controlled execution-adjacent values, and
|
|
282
|
+
* they are still validated against the exact supplied package afterwards. The
|
|
283
|
+
* model's route, SQL-adjacent frame, query intent, and reworded question are
|
|
284
|
+
* presentation suggestions at most; accepting them as authority was how an
|
|
285
|
+
* omitted product category/order ID or an inherited prior filter silently
|
|
286
|
+
* changed the answer tuple after retrieval.
|
|
287
|
+
*/
|
|
288
|
+
export declare function mergeMeaningResolutionWithRequirementSeed(input: {
|
|
289
|
+
seed: AnalyticalRequirementSeedV1;
|
|
290
|
+
resolution: MeaningResolution;
|
|
291
|
+
candidates: AgentEvidenceCandidate[];
|
|
292
|
+
}): MeaningResolution;
|
|
221
293
|
export declare function routeForEvidenceCandidate(candidate: AgentEvidenceCandidate): MeaningExecutionRoute;
|
|
222
294
|
export declare function questionTypeFromText(question: string): MeaningQuestionType;
|
|
223
295
|
export declare function defaultQueryIntent(evidence: AgentRetrievalEvidence): MeaningQueryIntent;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"meaning-resolution.d.ts","sourceRoot":"","sources":["../src/meaning-resolution.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EACV,wBAAwB,EACxB,yBAAyB,EACzB,wBAAwB,EACzB,MAAM,0BAA0B,CAAC;AAClC,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACzD,OAAO,
|
|
1
|
+
{"version":3,"file":"meaning-resolution.d.ts","sourceRoot":"","sources":["../src/meaning-resolution.ts"],"names":[],"mappings":"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"}
|
|
@@ -5,13 +5,16 @@
|
|
|
5
5
|
* package and may reference only IDs contained in it. Execution remains owned by
|
|
6
6
|
* the host after it validates the selected candidates and route.
|
|
7
7
|
*/
|
|
8
|
-
import { buildAnalyticalRequirementSet, evidenceCandidateRoles, selectRoleBalancedMeaningCandidates, } from './analytical-orchestration.js';
|
|
8
|
+
import { buildAnalyticalRequirementSet, candidateConflictsWithExplicitRankingMeasure, evidenceCandidateRoles, hasEntityAttributeTerm, isEntityAttributeCandidate, selectRoleBalancedMeaningCandidates, } from './analytical-orchestration.js';
|
|
9
9
|
/**
|
|
10
10
|
* Return the authored output identity that proves a certified block can answer
|
|
11
|
-
* one requested measure.
|
|
12
|
-
* which are populated from declared/output-contract fields at indexing
|
|
13
|
-
*
|
|
14
|
-
*
|
|
11
|
+
* one requested measure. This normally reads only the block's own `output:`
|
|
12
|
+
* facts, which are populated from declared/output-contract fields at indexing
|
|
13
|
+
* time. An exact block-title request may additionally carry a
|
|
14
|
+
* `catalog-proven-output:` fact from the snapshot-local catalog fit. That fact
|
|
15
|
+
* is minted only after the catalog has already bound this exact title to a
|
|
16
|
+
* high-confidence certified answer contract; names, tags, examples,
|
|
17
|
+
* definitions, and unrelated retrieved metrics are intentionally absent.
|
|
15
18
|
*/
|
|
16
19
|
export function certifiedCandidateDeclaredMeasureOutput(candidate, requested) {
|
|
17
20
|
if (candidate.kind !== 'certified_block')
|
|
@@ -20,11 +23,31 @@ export function certifiedCandidateDeclaredMeasureOutput(candidate, requested) {
|
|
|
20
23
|
if (!requestedIdentity)
|
|
21
24
|
return undefined;
|
|
22
25
|
const declared = (candidate.compatibilityFacts ?? [])
|
|
23
|
-
.flatMap((fact) => /^output:\s*(.+)$/i.exec(fact)?.[1] ?? [])
|
|
26
|
+
.flatMap((fact) => /^(?:output|catalog-proven-output):\s*(.+)$/i.exec(fact)?.[1] ?? [])
|
|
24
27
|
.map((output) => output.trim())
|
|
25
28
|
.filter(Boolean);
|
|
26
29
|
return declared.find((output) => canonicalCertifiedOutputMetricIdentity(output) === requestedIdentity);
|
|
27
30
|
}
|
|
31
|
+
/**
|
|
32
|
+
* Return the block output that proves a requested display/grouping dimension.
|
|
33
|
+
* This is intentionally narrower than retrieval matching: only a declared
|
|
34
|
+
* output or the catalog-proven output bridge for an exact certified title may
|
|
35
|
+
* bind a dimension to the block executor. `customer_name` is a valid output
|
|
36
|
+
* for the business term `customer`; arbitrary attributes such as owner or
|
|
37
|
+
* sentiment are not.
|
|
38
|
+
*/
|
|
39
|
+
export function certifiedCandidateDeclaredDimensionOutput(candidate, requested) {
|
|
40
|
+
if (candidate.kind !== 'certified_block')
|
|
41
|
+
return undefined;
|
|
42
|
+
const requestedIdentity = canonicalCertifiedOutputDimensionIdentity(requested);
|
|
43
|
+
if (!requestedIdentity)
|
|
44
|
+
return undefined;
|
|
45
|
+
const declared = (candidate.compatibilityFacts ?? [])
|
|
46
|
+
.flatMap((fact) => /^(?:output|catalog-proven-output):\s*(.+)$/i.exec(fact)?.[1] ?? [])
|
|
47
|
+
.map((output) => output.trim())
|
|
48
|
+
.filter(Boolean);
|
|
49
|
+
return declared.find((output) => canonicalCertifiedOutputDimensionIdentity(output) === requestedIdentity);
|
|
50
|
+
}
|
|
28
51
|
/**
|
|
29
52
|
* A certified tier can freeze only when the selected block itself declares
|
|
30
53
|
* every requested measure. This is deliberately stricter than retrieval
|
|
@@ -85,6 +108,19 @@ function canonicalCertifiedOutputMetricIdentity(value) {
|
|
|
85
108
|
tokens.pop();
|
|
86
109
|
return tokens.join('_');
|
|
87
110
|
}
|
|
111
|
+
function canonicalCertifiedOutputDimensionIdentity(value) {
|
|
112
|
+
const tokens = value
|
|
113
|
+
.toLowerCase()
|
|
114
|
+
.replace(/[_./:-]+/g, ' ')
|
|
115
|
+
.replace(/[^a-z0-9 ]+/g, ' ')
|
|
116
|
+
.split(/\s+/)
|
|
117
|
+
.map((token) => token.endsWith('s') && token.length > 3 ? token.slice(0, -1) : token)
|
|
118
|
+
.filter(Boolean);
|
|
119
|
+
const displaySuffixes = new Set(['display', 'id', 'key', 'label', 'name', 'title']);
|
|
120
|
+
while (tokens.length > 1 && displaySuffixes.has(tokens.at(-1)))
|
|
121
|
+
tokens.pop();
|
|
122
|
+
return tokens.join('_');
|
|
123
|
+
}
|
|
88
124
|
const TRUST_ORDER = {
|
|
89
125
|
certified: 4,
|
|
90
126
|
semantic: 3,
|
|
@@ -112,6 +148,11 @@ export function buildMeaningEvidencePackage(evidence, maxCandidates = 16, questi
|
|
|
112
148
|
// role-aware admission code ever saw them.
|
|
113
149
|
const canonicalEligible = canonicalizeMetricMeasureCandidates(evidence.candidates)
|
|
114
150
|
.filter((candidate) => candidate.eligible !== false)
|
|
151
|
+
// Preserve the retrieval result for lifecycle/diagnostics, but do not put
|
|
152
|
+
// a correlated non-requested metric in the bounded meaning package. An
|
|
153
|
+
// explicit ranking measure is a typed requirement, not a prompt hint the
|
|
154
|
+
// resolver may replace with a more highly scored BCM/run-rate card.
|
|
155
|
+
.filter((candidate) => !candidateConflictsWithExplicitRankingMeasure(candidate, requirements))
|
|
115
156
|
.sort(compareCandidates);
|
|
116
157
|
const rawPinned = selectRoleBalancedMeaningCandidates({
|
|
117
158
|
candidates: canonicalEligible,
|
|
@@ -124,10 +165,10 @@ export function buildMeaningEvidencePackage(evidence, maxCandidates = 16, questi
|
|
|
124
165
|
// categorical-dimension parser also saw the token "account". An explicit
|
|
125
166
|
// attribute request remains eligible.
|
|
126
167
|
const requestedEntityDisplay = requirements.entityDisplayTerms.length > 0;
|
|
127
|
-
const explicitlyRequestsAttribute =
|
|
168
|
+
const explicitlyRequestsAttribute = hasEntityAttributeTerm(question);
|
|
128
169
|
const hasRawPinnedEntityLabel = rawPinned.some((candidate) => evidenceCandidateRoles(candidate).includes('entity_label'));
|
|
129
170
|
const pinned = requestedEntityDisplay && hasRawPinnedEntityLabel && !explicitlyRequestsAttribute
|
|
130
|
-
? rawPinned.filter((candidate) =>
|
|
171
|
+
? rawPinned.filter((candidate) => !isEntityAttributeCandidate(candidate))
|
|
131
172
|
: rawPinned;
|
|
132
173
|
const kindCounts = new Map();
|
|
133
174
|
const perKindQualified = canonicalEligible
|
|
@@ -152,7 +193,7 @@ export function buildMeaningEvidencePackage(evidence, maxCandidates = 16, questi
|
|
|
152
193
|
// An account owner/e-mail/sentiment is an attribute, never a substitute
|
|
153
194
|
// for the requested account/customer display entity. Do not let it consume
|
|
154
195
|
// the remaining meaning cards after that display role was successfully pinned.
|
|
155
|
-
return !hasPinnedEntityLabel ||
|
|
196
|
+
return !hasPinnedEntityLabel || !isEntityAttributeCandidate(candidate);
|
|
156
197
|
});
|
|
157
198
|
return [...pinned, ...safeFill]
|
|
158
199
|
.slice(0, limit);
|
|
@@ -208,69 +249,92 @@ function normalizeRef(value) {
|
|
|
208
249
|
return value.trim().toLowerCase().replace(/[`"']/g, "");
|
|
209
250
|
}
|
|
210
251
|
/** Validate that a resolver cannot invent or select ineligible evidence. */
|
|
211
|
-
export function validateMeaningResolution(value, candidates, requestedMeasures = value.queryIntent.measures) {
|
|
252
|
+
export function validateMeaningResolution(value, candidates, requestedMeasures = value.queryIntent.measures, options = {}) {
|
|
212
253
|
const byId = new Map(candidates.map((candidate) => [candidate.id, candidate]));
|
|
213
254
|
const selectedConceptIds = value.selectedConceptIds.length > 0
|
|
214
255
|
? value.selectedConceptIds
|
|
215
256
|
: value.recommendedExecutionId
|
|
216
257
|
? [value.recommendedExecutionId]
|
|
217
258
|
: [];
|
|
218
|
-
|
|
259
|
+
// Selected/recommended IDs are execution authority and must be present in
|
|
260
|
+
// the bounded package the resolver received. Rejections are explanatory
|
|
261
|
+
// only: hosts may retain a full-snapshot rejection ledger even when its
|
|
262
|
+
// card was correctly pruned from the 16-card meaning package. Do not let a
|
|
263
|
+
// non-authoritative rejected ID invalidate an otherwise valid frozen plan.
|
|
264
|
+
const authoritativeIds = [
|
|
219
265
|
...value.selectedConceptIds,
|
|
220
266
|
...(value.recommendedExecutionId ? [value.recommendedExecutionId] : []),
|
|
221
|
-
...value.rejectedCandidates.map((candidate) => candidate.id),
|
|
222
267
|
];
|
|
223
|
-
const invented =
|
|
268
|
+
const invented = authoritativeIds.find((id) => !byId.has(id));
|
|
224
269
|
if (invented)
|
|
225
270
|
return { ok: false, reason: `The resolver referenced evidence that was not retrieved: ${invented}` };
|
|
226
|
-
|
|
271
|
+
const packageRejectedCandidates = value.rejectedCandidates.filter((candidate) => byId.has(candidate.id));
|
|
272
|
+
const normalizedValue = packageRejectedCandidates.length === value.rejectedCandidates.length
|
|
273
|
+
? value
|
|
274
|
+
: { ...value, rejectedCandidates: packageRejectedCandidates };
|
|
275
|
+
if (normalizedValue.confidence !== "low" && selectedConceptIds.length === 0) {
|
|
227
276
|
return { ok: false, reason: "A medium/high-confidence resolution must select at least one retrieved concept." };
|
|
228
277
|
}
|
|
229
278
|
const selected = selectedConceptIds.map((id) => byId.get(id));
|
|
230
279
|
if (selected.some((candidate) => candidate.eligible === false || candidate.compatibility === "incompatible")) {
|
|
231
280
|
return { ok: false, reason: "The resolver selected ineligible or incompatible evidence." };
|
|
232
281
|
}
|
|
233
|
-
|
|
282
|
+
// The evidence package normally excludes a competing metric before the
|
|
283
|
+
// resolver sees it. Keep the same check at the model-output boundary so an
|
|
284
|
+
// extension, legacy caller, or malformed resolver response cannot turn an
|
|
285
|
+
// explicitly named ranking measure into a correlated alternative.
|
|
286
|
+
const explicitRankingConflict = options.requirements
|
|
287
|
+
? selected.find((candidate) => candidateConflictsWithExplicitRankingMeasure(candidate, options.requirements))
|
|
288
|
+
: undefined;
|
|
289
|
+
if (explicitRankingConflict) {
|
|
290
|
+
return {
|
|
291
|
+
ok: false,
|
|
292
|
+
reason: `The resolver selected a metric that conflicts with the explicit ranking measure: ${explicitRankingConflict.id}`,
|
|
293
|
+
};
|
|
294
|
+
}
|
|
295
|
+
const rejectedIds = new Set(normalizedValue.rejectedCandidates.map((candidate) => candidate.id));
|
|
234
296
|
if (selectedConceptIds.some((id) => rejectedIds.has(id))) {
|
|
235
297
|
return { ok: false, reason: "The resolver both selected and rejected the same evidence." };
|
|
236
298
|
}
|
|
237
|
-
const executionId =
|
|
299
|
+
const executionId = normalizedValue.recommendedExecutionId ?? normalizedValue.selectedConceptIds[0];
|
|
238
300
|
if (executionId) {
|
|
239
301
|
const execution = byId.get(executionId);
|
|
240
|
-
if (
|
|
241
|
-
return { ok: false, reason: "The recommended execution evidence is ineligible or incompatible." };
|
|
242
|
-
}
|
|
243
|
-
if (value.recommendedRoute === "certified" && execution.kind !== "certified_block") {
|
|
244
|
-
return { ok: false, reason: "A certified route must reference a certified block." };
|
|
245
|
-
}
|
|
246
|
-
if (value.recommendedRoute === "certified" && execution.compatibility !== "compatible") {
|
|
247
|
-
return { ok: false, reason: "A certified route requires a deterministically compatible block fit." };
|
|
248
|
-
}
|
|
249
|
-
if (value.recommendedRoute === 'certified'
|
|
250
|
-
&& !certifiedCandidateExplicitlyCoversMeasures(execution, requestedMeasures)) {
|
|
302
|
+
if (options.requirements && candidateConflictsWithExplicitRankingMeasure(execution, options.requirements)) {
|
|
251
303
|
return {
|
|
252
304
|
ok: false,
|
|
253
|
-
reason:
|
|
305
|
+
reason: `The resolver selected a metric that conflicts with the explicit ranking measure: ${execution.id}`,
|
|
254
306
|
};
|
|
255
307
|
}
|
|
256
|
-
if (
|
|
257
|
-
return { ok: false, reason: "
|
|
258
|
-
}
|
|
259
|
-
if (value.recommendedRoute === "semantic" && execution.compatibility !== "compatible") {
|
|
260
|
-
return { ok: false, reason: "A semantic route requires deterministic measure, grain, and dimension compatibility." };
|
|
308
|
+
if (execution.eligible === false || execution.compatibility === "incompatible") {
|
|
309
|
+
return { ok: false, reason: "The recommended execution evidence is ineligible or incompatible." };
|
|
261
310
|
}
|
|
311
|
+
// `recommendedRoute` is deliberately not validated here. The bounded
|
|
312
|
+
// meaning call can nominate only supplied IDs; the authoritative cascade
|
|
313
|
+
// subsequently evaluates the full host-owned requirement seed against
|
|
314
|
+
// those IDs. Rejecting a partial selected semantic metric at this boundary
|
|
315
|
+
// used the model-adjacent nomination as route authority and stopped the
|
|
316
|
+
// valid same-snapshot metric + grouping extension before the semantic
|
|
317
|
+
// compatibility solver could prove it. The same rule applies to a
|
|
318
|
+
// selected certified block: its output contract is checked by the
|
|
319
|
+
// certified cascade before freeze, where an incomplete block advances to
|
|
320
|
+
// semantic/exploratory rather than becoming a false terminal failure.
|
|
321
|
+
//
|
|
322
|
+
// Package membership, eligibility, incompatible evidence, and explicit
|
|
323
|
+
// ranking conflicts above remain hard boundaries; this only defers tier
|
|
324
|
+
// fitness to the host-owned planner.
|
|
325
|
+
void execution;
|
|
262
326
|
}
|
|
263
|
-
if (
|
|
264
|
-
const invalidFrameReference = firstInvalidAnalyticalFrameReference(
|
|
327
|
+
if (normalizedValue.analyticalFrame) {
|
|
328
|
+
const invalidFrameReference = firstInvalidAnalyticalFrameReference(normalizedValue.analyticalFrame, candidates, executionId);
|
|
265
329
|
if (invalidFrameReference) {
|
|
266
330
|
return { ok: false, reason: `The analytical frame referenced evidence that was not retrieved: ${invalidFrameReference}` };
|
|
267
331
|
}
|
|
268
332
|
}
|
|
269
333
|
return {
|
|
270
334
|
ok: true,
|
|
271
|
-
resolution: selectedConceptIds ===
|
|
272
|
-
?
|
|
273
|
-
: { ...
|
|
335
|
+
resolution: selectedConceptIds === normalizedValue.selectedConceptIds
|
|
336
|
+
? normalizedValue
|
|
337
|
+
: { ...normalizedValue, selectedConceptIds },
|
|
274
338
|
};
|
|
275
339
|
}
|
|
276
340
|
function firstInvalidAnalyticalFrameReference(frame, candidates, executionId) {
|
|
@@ -362,6 +426,235 @@ function firstInvalidAnalyticalFrameReference(frame, candidates, executionId) {
|
|
|
362
426
|
}
|
|
363
427
|
return undefined;
|
|
364
428
|
}
|
|
429
|
+
/**
|
|
430
|
+
* Merge the one model meaning response into the host-owned request seed.
|
|
431
|
+
*
|
|
432
|
+
* Candidate IDs are the only model-controlled execution-adjacent values, and
|
|
433
|
+
* they are still validated against the exact supplied package afterwards. The
|
|
434
|
+
* model's route, SQL-adjacent frame, query intent, and reworded question are
|
|
435
|
+
* presentation suggestions at most; accepting them as authority was how an
|
|
436
|
+
* omitted product category/order ID or an inherited prior filter silently
|
|
437
|
+
* changed the answer tuple after retrieval.
|
|
438
|
+
*/
|
|
439
|
+
export function mergeMeaningResolutionWithRequirementSeed(input) {
|
|
440
|
+
const { seed, resolution, candidates } = input;
|
|
441
|
+
const modelSelectedConceptIds = [...new Set([
|
|
442
|
+
...resolution.selectedConceptIds,
|
|
443
|
+
...(resolution.recommendedExecutionId ? [resolution.recommendedExecutionId] : []),
|
|
444
|
+
])];
|
|
445
|
+
// A model can bind only supplied IDs, but supplied is not synonymous with
|
|
446
|
+
// grounded in the current request. Discard a known semantic metric that
|
|
447
|
+
// would add a qualifier not present in the immutable seed. Keep unknown IDs
|
|
448
|
+
// in the result so `validateMeaningResolution` still rejects inventions at
|
|
449
|
+
// the boundary rather than silently laundering them away.
|
|
450
|
+
const omittedUngroundedMetricIds = modelSelectedConceptIds.filter((id) => {
|
|
451
|
+
const candidate = candidates.find((item) => item.id === id || item.qualifiedId === id);
|
|
452
|
+
return candidate?.kind === 'semantic_metric'
|
|
453
|
+
&& !metricCandidateExactlyMatchesSeed(candidate, seed);
|
|
454
|
+
});
|
|
455
|
+
const selectedConceptIds = modelSelectedConceptIds.filter((id) => !omittedUngroundedMetricIds.includes(id));
|
|
456
|
+
// A minimal candidate-ID-only response is intentionally allowed for the
|
|
457
|
+
// one bounded meaning call. Do not let the order of a role-targeted member
|
|
458
|
+
// extension turn that member into the execution target merely because the
|
|
459
|
+
// model omitted the optional recommendation. A selected metric is the
|
|
460
|
+
// host-safe default; a member only supplies a dimension binding beside it.
|
|
461
|
+
const selectedMetricId = selectedConceptIds.find((id) => {
|
|
462
|
+
const candidate = candidates.find((item) => item.id === id || item.qualifiedId === id);
|
|
463
|
+
return candidate?.kind === 'semantic_metric'
|
|
464
|
+
// A selected metric can supersede a legacy recommendation only when it
|
|
465
|
+
// proves the host-owned metric wording. Token-overlap retrieval is not
|
|
466
|
+
// enough here: otherwise `rollover amount` silently became
|
|
467
|
+
// `rollover_balance_amount` merely because a pooled semantic card had
|
|
468
|
+
// been selected alongside a certified nomination. The cascade can
|
|
469
|
+
// still evaluate a partial candidate later; this guard only prevents a
|
|
470
|
+
// model selection from adding an unspoken metric qualifier.
|
|
471
|
+
&& metricCandidateExactlyMatchesSeed(candidate, seed);
|
|
472
|
+
});
|
|
473
|
+
// A model may nominate a certified block in its legacy recommendation while
|
|
474
|
+
// selecting a supplied semantic metric. The recommendation is not route
|
|
475
|
+
// authority; when one selected metric exists, it is the only safe host
|
|
476
|
+
// execution nomination. This preserves the candidate-ID boundary without
|
|
477
|
+
// letting an unproved block displace the selected complete semantic tuple.
|
|
478
|
+
const recommendedExecutionId = selectedMetricId
|
|
479
|
+
?? resolution.recommendedExecutionId
|
|
480
|
+
?? selectedConceptIds[0];
|
|
481
|
+
const recommendedCandidate = recommendedExecutionId
|
|
482
|
+
? candidates.find((candidate) => candidate.id === recommendedExecutionId || candidate.qualifiedId === recommendedExecutionId)
|
|
483
|
+
: undefined;
|
|
484
|
+
const rejectedCandidates = resolution.rejectedCandidates.filter((candidate) => candidates.some((known) => known.id === candidate.id || known.qualifiedId === candidate.id));
|
|
485
|
+
const queryIntent = bindSelectedMemberValuesToSeed({
|
|
486
|
+
seed,
|
|
487
|
+
selectedConceptIds,
|
|
488
|
+
candidates,
|
|
489
|
+
});
|
|
490
|
+
const receipts = [];
|
|
491
|
+
const recordHostPreserved = (field, differs, reason) => {
|
|
492
|
+
if (differs)
|
|
493
|
+
receipts.push({ version: 1, field, action: 'host_preserved', reason });
|
|
494
|
+
};
|
|
495
|
+
recordHostPreserved('interpreted_question', normalizeMeaningText(resolution.interpretedQuestion) !== normalizeMeaningText(seed.sourceQuestion), 'The source question remains host-owned; model rephrasing cannot add context.');
|
|
496
|
+
recordHostPreserved('question_type', resolution.questionType !== questionTypeFromText(seed.sourceQuestion), 'The host classifies the request mode before meaning resolution.');
|
|
497
|
+
recordHostPreserved('query_intent', !sameQueryIntent(resolution.queryIntent, seed.queryIntent), 'Explicit measures, entity/grain, dimensions, filters, ranking, outputs, and time requirements remain host-owned.');
|
|
498
|
+
recordHostPreserved('recommended_route', resolution.recommendedRoute !== (recommendedCandidate ? cascadeNominationForCandidate(recommendedCandidate) : 'clarify'), 'Tier selection is deterministic cascade authority, not a model recommendation.');
|
|
499
|
+
recordHostPreserved('analytical_frame', resolution.analyticalFrame !== undefined, 'A model frame cannot introduce identifiers, SQL semantics, trust, or a replacement route.');
|
|
500
|
+
if (selectedConceptIds.length > 0) {
|
|
501
|
+
receipts.push({
|
|
502
|
+
version: 1,
|
|
503
|
+
field: 'candidate_selection',
|
|
504
|
+
action: 'selection_accepted',
|
|
505
|
+
reason: 'The model selected supplied candidate IDs; host validation still verifies package membership and compatibility.',
|
|
506
|
+
candidateIds: selectedConceptIds,
|
|
507
|
+
});
|
|
508
|
+
}
|
|
509
|
+
if (omittedUngroundedMetricIds.length > 0) {
|
|
510
|
+
receipts.push({
|
|
511
|
+
version: 1,
|
|
512
|
+
field: 'candidate_selection',
|
|
513
|
+
action: 'host_preserved',
|
|
514
|
+
reason: 'A selected semantic metric would add an unspoken qualifier to the host-owned measure requirement.',
|
|
515
|
+
candidateIds: omittedUngroundedMetricIds,
|
|
516
|
+
});
|
|
517
|
+
}
|
|
518
|
+
if (!sameQueryIntent(queryIntent, seed.queryIntent)) {
|
|
519
|
+
receipts.push({
|
|
520
|
+
version: 1,
|
|
521
|
+
field: 'member_binding',
|
|
522
|
+
action: 'selection_accepted',
|
|
523
|
+
reason: 'A selected, supplied semantic member canonically bound an existing host filter value; no filter field or scope was added.',
|
|
524
|
+
candidateIds: selectedConceptIds,
|
|
525
|
+
});
|
|
526
|
+
}
|
|
527
|
+
return {
|
|
528
|
+
interpretedQuestion: seed.sourceQuestion,
|
|
529
|
+
questionType: questionTypeFromText(seed.sourceQuestion),
|
|
530
|
+
selectedConceptIds,
|
|
531
|
+
...(recommendedExecutionId ? { recommendedExecutionId } : {}),
|
|
532
|
+
queryIntent,
|
|
533
|
+
rejectedCandidates,
|
|
534
|
+
confidence: resolution.confidence,
|
|
535
|
+
missingInformation: [...new Set(resolution.missingInformation)],
|
|
536
|
+
recommendedRoute: recommendedCandidate ? cascadeNominationForCandidate(recommendedCandidate) : 'clarify',
|
|
537
|
+
...(resolution.clarifyingQuestion ? { clarifyingQuestion: resolution.clarifyingQuestion } : {}),
|
|
538
|
+
hostRequirementSeed: seed,
|
|
539
|
+
...(receipts.length > 0 ? { overrideReceipts: receipts } : {}),
|
|
540
|
+
};
|
|
541
|
+
}
|
|
542
|
+
/**
|
|
543
|
+
* Keep the host's literal metric requirement authoritative when choosing a
|
|
544
|
+
* primary nominated metric. This intentionally uses exact canonical identity
|
|
545
|
+
* rather than retrieval-style token matching: aliases are normalized into the
|
|
546
|
+
* requirement seed before this boundary, so a legitimate `sales` ->
|
|
547
|
+
* `revenue` binding still matches while a related `rollover balance amount`
|
|
548
|
+
* card cannot add `balance` to an unspoken request for `rollover amount`.
|
|
549
|
+
*/
|
|
550
|
+
function metricCandidateExactlyMatchesSeed(candidate, seed) {
|
|
551
|
+
if (seed.requirements.measures.length === 0)
|
|
552
|
+
return true;
|
|
553
|
+
const identities = [candidate.name, ...(candidate.aliases ?? []), candidate.qualifiedId ?? candidate.id]
|
|
554
|
+
.map((value) => canonicalMetricIdentity(value))
|
|
555
|
+
.filter(Boolean);
|
|
556
|
+
// A multi-metric tuple legitimately selects one candidate per requested
|
|
557
|
+
// measure. Each candidate must prove at least one host metric; requiring a
|
|
558
|
+
// single candidate to prove the entire tuple would drop `beverage revenue`
|
|
559
|
+
// beside `total revenue` before the semantic compiler can combine them.
|
|
560
|
+
return seed.requirements.measures.some((measure) => {
|
|
561
|
+
const requested = canonicalMetricIdentity(measure);
|
|
562
|
+
const genericBareTerm = /^(?:amount|value|count|number|total|rate|percentage|percent)$/.test(requested);
|
|
563
|
+
return identities.some((identity) => identity === requested
|
|
564
|
+
// A governed semantic metric may carry a leading accounting qualifier
|
|
565
|
+
// (`net revenue`) for the reader's base term (`revenue`). Keep that
|
|
566
|
+
// narrow suffix form for the established semantic contract, but never
|
|
567
|
+
// permit an inserted qualifier: `rollover balance amount` must not
|
|
568
|
+
// satisfy the distinct request `rollover amount`.
|
|
569
|
+
|| (!genericBareTerm && identity.endsWith(` ${requested}`)));
|
|
570
|
+
});
|
|
571
|
+
}
|
|
572
|
+
function canonicalMetricIdentity(value) {
|
|
573
|
+
return value
|
|
574
|
+
.trim()
|
|
575
|
+
.toLowerCase()
|
|
576
|
+
.replace(/([a-z])([A-Z])/g, '$1 $2')
|
|
577
|
+
.replace(/[._:/-]+/g, ' ')
|
|
578
|
+
.replace(/\s+/g, ' ')
|
|
579
|
+
.trim();
|
|
580
|
+
}
|
|
581
|
+
/**
|
|
582
|
+
* A selected semantic member can canonically correct an already-host-owned
|
|
583
|
+
* filter value (for example a retrieved alias/typo), but it cannot introduce
|
|
584
|
+
* a new filter field, member, or prior-result scope. Ambiguous member cards
|
|
585
|
+
* deliberately leave the seed value untouched for later clarification.
|
|
586
|
+
*/
|
|
587
|
+
function bindSelectedMemberValuesToSeed(input) {
|
|
588
|
+
const selectedMembers = input.selectedConceptIds.flatMap((id) => {
|
|
589
|
+
const candidate = input.candidates.find((item) => item.id === id || item.qualifiedId === id);
|
|
590
|
+
return candidate?.kind === 'semantic_member' ? [candidate] : [];
|
|
591
|
+
});
|
|
592
|
+
const queryIntent = cloneSeedQueryIntent(input.seed);
|
|
593
|
+
return {
|
|
594
|
+
...queryIntent,
|
|
595
|
+
filters: queryIntent.filters.map((filter) => {
|
|
596
|
+
const matches = selectedMembers.filter((candidate) => [candidate.name, ...(candidate.aliases ?? [])]
|
|
597
|
+
.map(normalizeMemberBindingText)
|
|
598
|
+
.includes(normalizeMemberBindingText(filter.value)));
|
|
599
|
+
return matches.length === 1 ? { ...filter, value: matches[0].name } : filter;
|
|
600
|
+
}),
|
|
601
|
+
};
|
|
602
|
+
}
|
|
603
|
+
function normalizeMemberBindingText(value) {
|
|
604
|
+
return value.trim().replace(/\s+/g, ' ').toLowerCase();
|
|
605
|
+
}
|
|
606
|
+
/**
|
|
607
|
+
* The model's selected candidate is a nomination for the cascade, not the
|
|
608
|
+
* final eligibility verdict. A partial certified card must therefore enter
|
|
609
|
+
* the certified tier so the deterministic output-contract check can continue
|
|
610
|
+
* to semantic/exploratory evidence instead of pretending it is raw SQL.
|
|
611
|
+
*/
|
|
612
|
+
function cascadeNominationForCandidate(candidate) {
|
|
613
|
+
if (candidate.kind === 'certified_block')
|
|
614
|
+
return 'certified';
|
|
615
|
+
if (candidate.kind === 'semantic_metric' || candidate.kind === 'semantic_member')
|
|
616
|
+
return 'semantic';
|
|
617
|
+
return candidate.trustTier === 'governed_sql' ? 'governed_sql' : 'exploratory';
|
|
618
|
+
}
|
|
619
|
+
function cloneSeedQueryIntent(seed) {
|
|
620
|
+
return {
|
|
621
|
+
measures: [...seed.queryIntent.measures],
|
|
622
|
+
dimensions: [...seed.queryIntent.dimensions],
|
|
623
|
+
filters: seed.queryIntent.filters.map((filter) => ({ field: filter.field, value: filter.value })),
|
|
624
|
+
...(seed.queryIntent.timeRange ? { timeRange: seed.queryIntent.timeRange } : {}),
|
|
625
|
+
...(seed.queryIntent.timeGrain ? { timeGrain: seed.queryIntent.timeGrain } : {}),
|
|
626
|
+
...(seed.queryIntent.order ? { order: seed.queryIntent.order } : {}),
|
|
627
|
+
...(seed.queryIntent.limit !== undefined ? { limit: seed.queryIntent.limit } : {}),
|
|
628
|
+
...(seed.queryIntent.fiscalCalendarId ? { fiscalCalendarId: seed.queryIntent.fiscalCalendarId } : {}),
|
|
629
|
+
...(seed.queryIntent.fiscalDateRoleId ? { fiscalDateRoleId: seed.queryIntent.fiscalDateRoleId } : {}),
|
|
630
|
+
};
|
|
631
|
+
}
|
|
632
|
+
function sameQueryIntent(left, right) {
|
|
633
|
+
return JSON.stringify({
|
|
634
|
+
measures: left.measures,
|
|
635
|
+
dimensions: left.dimensions,
|
|
636
|
+
filters: left.filters,
|
|
637
|
+
timeRange: left.timeRange,
|
|
638
|
+
timeGrain: left.timeGrain,
|
|
639
|
+
order: left.order,
|
|
640
|
+
limit: left.limit,
|
|
641
|
+
fiscalCalendarId: left.fiscalCalendarId,
|
|
642
|
+
fiscalDateRoleId: left.fiscalDateRoleId,
|
|
643
|
+
}) === JSON.stringify({
|
|
644
|
+
measures: right.measures,
|
|
645
|
+
dimensions: right.dimensions,
|
|
646
|
+
filters: right.filters,
|
|
647
|
+
timeRange: right.timeRange,
|
|
648
|
+
timeGrain: right.timeGrain,
|
|
649
|
+
order: right.order,
|
|
650
|
+
limit: right.limit,
|
|
651
|
+
fiscalCalendarId: right.fiscalCalendarId,
|
|
652
|
+
fiscalDateRoleId: right.fiscalDateRoleId,
|
|
653
|
+
});
|
|
654
|
+
}
|
|
655
|
+
function normalizeMeaningText(value) {
|
|
656
|
+
return value.trim().replace(/\s+/g, ' ').toLowerCase();
|
|
657
|
+
}
|
|
365
658
|
export function routeForEvidenceCandidate(candidate) {
|
|
366
659
|
if (candidate.kind === "certified_block" && candidate.compatibility === "compatible")
|
|
367
660
|
return "certified";
|