@duckcodeailabs/dql-agent 1.9.5 → 1.10.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/agent-run-engine.d.ts +13 -0
- package/dist/agent-run-engine.d.ts.map +1 -1
- package/dist/agent-run-engine.js +59 -1
- package/dist/agent-run-engine.js.map +1 -1
- package/dist/answer-loop.d.ts +34 -1
- package/dist/answer-loop.d.ts.map +1 -1
- package/dist/answer-loop.js +295 -35
- package/dist/answer-loop.js.map +1 -1
- package/dist/embeddings/provider.d.ts +1 -1
- package/dist/embeddings/provider.d.ts.map +1 -1
- package/dist/embeddings/provider.js.map +1 -1
- package/dist/governed-relational-compiler.d.ts +138 -0
- package/dist/governed-relational-compiler.d.ts.map +1 -0
- package/dist/governed-relational-compiler.js +315 -0
- package/dist/governed-relational-compiler.js.map +1 -0
- package/dist/index.d.ts +13 -5
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +8 -4
- package/dist/index.js.map +1 -1
- package/dist/intent-controller.d.ts +5 -0
- package/dist/intent-controller.d.ts.map +1 -1
- package/dist/intent-controller.js.map +1 -1
- package/dist/kg/build.d.ts.map +1 -1
- package/dist/kg/build.js +81 -1
- package/dist/kg/build.js.map +1 -1
- package/dist/meaning-resolution.d.ts +4 -0
- package/dist/meaning-resolution.d.ts.map +1 -1
- package/dist/meaning-resolution.js +14 -4
- package/dist/meaning-resolution.js.map +1 -1
- package/dist/metadata/analysis-planner.d.ts.map +1 -1
- package/dist/metadata/analysis-planner.js +34 -6
- package/dist/metadata/analysis-planner.js.map +1 -1
- package/dist/metadata/catalog.d.ts +83 -2
- package/dist/metadata/catalog.d.ts.map +1 -1
- package/dist/metadata/catalog.js +381 -28
- package/dist/metadata/catalog.js.map +1 -1
- package/dist/metadata/meaning-evidence.d.ts +10 -1
- package/dist/metadata/meaning-evidence.d.ts.map +1 -1
- package/dist/metadata/meaning-evidence.js +78 -1
- package/dist/metadata/meaning-evidence.js.map +1 -1
- package/dist/plan-execution-adapter.d.ts +60 -0
- package/dist/plan-execution-adapter.d.ts.map +1 -0
- package/dist/plan-execution-adapter.js +239 -0
- package/dist/plan-execution-adapter.js.map +1 -0
- package/dist/research-governance.d.ts +56 -0
- package/dist/research-governance.d.ts.map +1 -0
- package/dist/research-governance.js +68 -0
- package/dist/research-governance.js.map +1 -0
- package/dist/research-loop.d.ts +14 -0
- package/dist/research-loop.d.ts.map +1 -1
- package/dist/research-loop.js +77 -23
- package/dist/research-loop.js.map +1 -1
- package/dist/resolved-analytical-plan.d.ts +112 -0
- package/dist/resolved-analytical-plan.d.ts.map +1 -0
- package/dist/resolved-analytical-plan.js +346 -0
- package/dist/resolved-analytical-plan.js.map +1 -0
- package/dist/router.d.ts +3 -0
- package/dist/router.d.ts.map +1 -1
- package/dist/router.js +34 -10
- package/dist/router.js.map +1 -1
- package/package.json +4 -4
package/dist/answer-loop.js
CHANGED
|
@@ -41,6 +41,9 @@ import { questionTypeFromText } from './meaning-resolution.js';
|
|
|
41
41
|
import { cascadeTraceToEvidenceRouteSteps, createCascadeAnswerResult, createCascadeTrace, } from './cascade/cascade.js';
|
|
42
42
|
import { shouldClarifyBeforeGeneration } from './cascade/triage.js';
|
|
43
43
|
import { stampTrustLabel } from './trust/stamp.js';
|
|
44
|
+
import { deriveResolvedAnalyticalPlan } from './resolved-analytical-plan.js';
|
|
45
|
+
import { adaptResolvedAnalyticalPlan, buildPlanExecutionRegistry, } from './plan-execution-adapter.js';
|
|
46
|
+
import { buildGovernedRelationalRegistry, compileGovernedRelationalPlan, finalizeGovernedCompilationReceipt, renderGovernedRelationalDqlArtifact, } from './governed-relational-compiler.js';
|
|
44
47
|
import { QUICK_PROMPT_CONTEXT_BUDGET, canUseLaneRepair, cascadeBudgetTrace, createCascadeBudgetState, deepAlternativeCountForQuestion, promptContextBudgetForQuestion, proposalToolBudgetForQuestion, recordLaneRepair, } from './cascade/budgets.js';
|
|
45
48
|
/**
|
|
46
49
|
* Physical column names whose sampled runtime values include `value`. Used by the
|
|
@@ -460,13 +463,126 @@ function tryCrossResultAnswer(input) {
|
|
|
460
463
|
}
|
|
461
464
|
return answer;
|
|
462
465
|
}
|
|
466
|
+
/**
|
|
467
|
+
* Materialize exactly the Skill IDs/hashes selected in the immutable context
|
|
468
|
+
* pack. No downstream answer route may rerun trigger/domain selection or add a
|
|
469
|
+
* Skill that was not recorded in the KnowledgeLens.
|
|
470
|
+
*
|
|
471
|
+
* Acceptance: SKILL-003, AGT-013.
|
|
472
|
+
*/
|
|
473
|
+
export function materializeKnowledgeLensSkills(contextPack, available) {
|
|
474
|
+
const byIdentity = new Map();
|
|
475
|
+
for (const skill of available) {
|
|
476
|
+
byIdentity.set(skill.qualifiedId ?? skill.id, skill);
|
|
477
|
+
if (!byIdentity.has(skill.id))
|
|
478
|
+
byIdentity.set(skill.id, skill);
|
|
479
|
+
}
|
|
480
|
+
return (contextPack.skills ?? []).map((selected) => {
|
|
481
|
+
const identity = selected.qualifiedId ?? selected.id;
|
|
482
|
+
const source = byIdentity.get(identity) ?? byIdentity.get(selected.id);
|
|
483
|
+
if (source) {
|
|
484
|
+
// Guidance is the immutable, bounded snapshot body. Retain structured
|
|
485
|
+
// source fields but never reread or inject a newer disk body mid-run.
|
|
486
|
+
return { ...source, body: selected.guidance };
|
|
487
|
+
}
|
|
488
|
+
return {
|
|
489
|
+
id: selected.id,
|
|
490
|
+
localId: selected.id,
|
|
491
|
+
qualifiedId: selected.qualifiedId,
|
|
492
|
+
scope: 'project',
|
|
493
|
+
domain: selected.domain,
|
|
494
|
+
domains: selected.domains,
|
|
495
|
+
modelAreaRefs: selected.modelAreaRefs,
|
|
496
|
+
kind: selected.kind,
|
|
497
|
+
status: selected.status,
|
|
498
|
+
owner: selected.owner,
|
|
499
|
+
triggers: selected.triggers,
|
|
500
|
+
exclusions: selected.exclusions,
|
|
501
|
+
description: selected.description,
|
|
502
|
+
preferredMetrics: selected.preferredMetrics,
|
|
503
|
+
preferredBlocks: selected.preferredBlocks,
|
|
504
|
+
preferredDimensions: selected.preferredDimensions,
|
|
505
|
+
requiredFilters: selected.requiredFilters,
|
|
506
|
+
clarifyWhen: selected.clarifyWhen,
|
|
507
|
+
examples: [],
|
|
508
|
+
sourceRefs: selected.sourceRefs,
|
|
509
|
+
vocabulary: selected.vocabulary,
|
|
510
|
+
body: selected.guidance,
|
|
511
|
+
sourcePath: selected.sourcePath ?? `snapshot:${selected.objectKey}`,
|
|
512
|
+
};
|
|
513
|
+
});
|
|
514
|
+
}
|
|
463
515
|
export async function answer(input) {
|
|
516
|
+
const inheritedPlan = !input.resolvedAnalyticalPlan
|
|
517
|
+
&& input.followUp?.priorResolvedAnalyticalPlan
|
|
518
|
+
&& input.followUp.resolvedAnalyticalPlanDelta
|
|
519
|
+
? deriveResolvedAnalyticalPlan(input.followUp.priorResolvedAnalyticalPlan, input.followUp.resolvedAnalyticalPlanDelta)
|
|
520
|
+
: undefined;
|
|
521
|
+
const normalizedInput = inheritedPlan
|
|
522
|
+
? { ...input, resolvedAnalyticalPlan: inheritedPlan }
|
|
523
|
+
: input;
|
|
524
|
+
const resolvedPlanExecutionBinding = normalizedInput.resolvedAnalyticalPlan
|
|
525
|
+
? adaptResolvedAnalyticalPlan({
|
|
526
|
+
plan: normalizedInput.resolvedAnalyticalPlan,
|
|
527
|
+
registry: buildPlanExecutionRegistry({
|
|
528
|
+
nodes: [
|
|
529
|
+
...normalizedInput.kg.getNodesByKind('block', 100_000),
|
|
530
|
+
...normalizedInput.kg.getNodesByKind('metric', 100_000),
|
|
531
|
+
...normalizedInput.kg.getNodesByKind('dimension', 100_000),
|
|
532
|
+
],
|
|
533
|
+
objects: normalizedInput.contextPack?.objects,
|
|
534
|
+
}),
|
|
535
|
+
semanticLayer: normalizedInput.semanticLayer,
|
|
536
|
+
expectedSnapshotId: normalizedInput.contextPack?.knowledgeLens.snapshotId,
|
|
537
|
+
})
|
|
538
|
+
: undefined;
|
|
539
|
+
const planBoundInput = resolvedPlanExecutionBinding
|
|
540
|
+
? { ...normalizedInput, resolvedPlanExecutionBinding }
|
|
541
|
+
: normalizedInput;
|
|
542
|
+
const relationalSchemaContext = planBoundInput.schemaContext?.length
|
|
543
|
+
? planBoundInput.schemaContext
|
|
544
|
+
: (planBoundInput.contextPack?.allowedSqlContext.relations ?? []).map((relation) => ({
|
|
545
|
+
relation: relation.relation,
|
|
546
|
+
name: relation.name,
|
|
547
|
+
columns: relation.columns.map((column) => ({
|
|
548
|
+
name: column.name,
|
|
549
|
+
type: column.type,
|
|
550
|
+
description: column.description,
|
|
551
|
+
})),
|
|
552
|
+
}));
|
|
553
|
+
const governedRelationalCompilation = planBoundInput.resolvedAnalyticalPlan?.mode === 'authoritative'
|
|
554
|
+
&& planBoundInput.resolvedAnalyticalPlan.capability === 'governed_relational'
|
|
555
|
+
? compileGovernedRelationalPlan({
|
|
556
|
+
plan: planBoundInput.resolvedAnalyticalPlan,
|
|
557
|
+
registry: buildGovernedRelationalRegistry({
|
|
558
|
+
snapshotId: planBoundInput.resolvedAnalyticalPlan.snapshotId,
|
|
559
|
+
schemaContext: relationalSchemaContext,
|
|
560
|
+
manifest: planBoundInput.manifest,
|
|
561
|
+
}),
|
|
562
|
+
driver: planBoundInput.semanticDriver,
|
|
563
|
+
})
|
|
564
|
+
: undefined;
|
|
565
|
+
const compiledInput = governedRelationalCompilation
|
|
566
|
+
? { ...planBoundInput, governedRelationalCompilation }
|
|
567
|
+
: planBoundInput;
|
|
568
|
+
const executionInput = compiledInput.contextPack
|
|
569
|
+
? {
|
|
570
|
+
...compiledInput,
|
|
571
|
+
skills: materializeKnowledgeLensSkills(compiledInput.contextPack, compiledInput.skills ?? []),
|
|
572
|
+
skillsSelectionLocked: true,
|
|
573
|
+
}
|
|
574
|
+
: compiledInput;
|
|
464
575
|
// Cross-result follow-up ("of these, the average") — computed from the prior
|
|
465
576
|
// rows, before the cascade, so it never re-queries or times out.
|
|
466
|
-
const crossResult = tryCrossResultAnswer(
|
|
467
|
-
if (crossResult)
|
|
468
|
-
return
|
|
469
|
-
|
|
577
|
+
const crossResult = tryCrossResultAnswer(executionInput);
|
|
578
|
+
if (crossResult) {
|
|
579
|
+
return {
|
|
580
|
+
...crossResult,
|
|
581
|
+
resolvedAnalyticalPlan: executionInput.resolvedAnalyticalPlan,
|
|
582
|
+
executablePlan: executionInput.resolvedPlanExecutionBinding,
|
|
583
|
+
};
|
|
584
|
+
}
|
|
585
|
+
const result = applyHollowAnswerGate(await runAnswerLoop(executionInput));
|
|
470
586
|
// Attach the canonical trust label once, at the single exit point, so every
|
|
471
587
|
// return site inside runAnswerLoop stays untouched and backward compatible.
|
|
472
588
|
// Freshness-aware trust: for a certified answer, fold the source block's data
|
|
@@ -495,6 +611,8 @@ export async function answer(input) {
|
|
|
495
611
|
...publicResult,
|
|
496
612
|
domainContext: input.domainContext,
|
|
497
613
|
intentDecision,
|
|
614
|
+
resolvedAnalyticalPlan: executionInput.resolvedAnalyticalPlan,
|
|
615
|
+
executablePlan: executionInput.resolvedPlanExecutionBinding,
|
|
498
616
|
trustLabelInfo,
|
|
499
617
|
provenanceFooter: buildProvenanceFooter(result, trustLabelInfo),
|
|
500
618
|
cascade: publicResult.cascade ?? createCascadeAnswerResult({
|
|
@@ -514,15 +632,17 @@ export async function answer(input) {
|
|
|
514
632
|
// Stamp the SELECTED skills that shaped the answer (transparency). Computed
|
|
515
633
|
// here so every return site inside runAnswerLoop stays untouched.
|
|
516
634
|
appliedSkills: result.appliedSkills ??
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
635
|
+
(executionInput.skillsSelectionLocked
|
|
636
|
+
? executionInput.skills ?? []
|
|
637
|
+
: selectRelevantSkills(executionInput.skills ?? [], executionInput.question, {
|
|
638
|
+
userId: executionInput.userId ?? null,
|
|
639
|
+
modelAreaIds: executionInput.domainContext?.modelAreaId ? [executionInput.domainContext.modelAreaId] : [],
|
|
640
|
+
domains: Array.from(new Set([
|
|
641
|
+
...domainContextSearchDomains(executionInput.domainContext),
|
|
642
|
+
...(executionInput.domain ? [executionInput.domain] : []),
|
|
643
|
+
...(executionInput.contextPack?.objects ?? []).slice(0, 20).flatMap((object) => object.domain ? [object.domain] : []),
|
|
644
|
+
])),
|
|
645
|
+
})).map((s) => ({
|
|
526
646
|
id: s.id,
|
|
527
647
|
description: s.description,
|
|
528
648
|
})),
|
|
@@ -614,11 +734,13 @@ async function runAnswerLoop(input) {
|
|
|
614
734
|
? (input.contextPack?.objects ?? []).slice(0, 20).flatMap((object) => object.domain ? [object.domain] : [])
|
|
615
735
|
: []),
|
|
616
736
|
]));
|
|
617
|
-
const selectedSkills =
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
737
|
+
const selectedSkills = input.skillsSelectionLocked
|
|
738
|
+
? skills
|
|
739
|
+
: selectRelevantSkills(skills, question, {
|
|
740
|
+
userId: userId ?? null,
|
|
741
|
+
domains: inferredDomains,
|
|
742
|
+
modelAreaIds: input.domainContext?.modelAreaId ? [input.domainContext.modelAreaId] : [],
|
|
743
|
+
});
|
|
622
744
|
const effectiveBlockHints = Array.from(new Set([
|
|
623
745
|
...blockHints,
|
|
624
746
|
// Only selected skills may influence block ranking. Previously a preferred
|
|
@@ -654,6 +776,85 @@ async function runAnswerLoop(input) {
|
|
|
654
776
|
// columns—not copy a customer-grain worked example into a product-grain ask.
|
|
655
777
|
const promptContextPack = contextPackForRequestedShape(scopedContextPack, question, questionPlan, kg);
|
|
656
778
|
const repairBudgetState = createCascadeBudgetState(input.cascadeBudgetModel);
|
|
779
|
+
const authoritativePlanBinding = input.resolvedAnalyticalPlan?.mode === 'authoritative'
|
|
780
|
+
? input.resolvedPlanExecutionBinding
|
|
781
|
+
: undefined;
|
|
782
|
+
const governedRelationalCompilation = input.governedRelationalCompilation;
|
|
783
|
+
if (governedRelationalCompilation?.status === 'blocked') {
|
|
784
|
+
const text = `The governed relational plan cannot compile safely: ${governedRelationalCompilation.reason}`;
|
|
785
|
+
return {
|
|
786
|
+
kind: 'no_answer',
|
|
787
|
+
sourceTier: 'no_answer',
|
|
788
|
+
certification: 'analyst_review_required',
|
|
789
|
+
reviewStatus: 'none',
|
|
790
|
+
confidence: 0,
|
|
791
|
+
text,
|
|
792
|
+
answer: text,
|
|
793
|
+
refusalCode: governedRelationalCompilation.code.startsWith('RELATIONSHIP_') ? 'modeling_gap' : 'grounding_gap',
|
|
794
|
+
refusalDetails: { code: 'grounding_gap', message: `${governedRelationalCompilation.code}: ${governedRelationalCompilation.reason}` },
|
|
795
|
+
citations: contextPackCitations(input.contextPack, 8),
|
|
796
|
+
considered,
|
|
797
|
+
contextPack: input.contextPack,
|
|
798
|
+
providerUsed: provider.name,
|
|
799
|
+
};
|
|
800
|
+
}
|
|
801
|
+
if (governedRelationalCompilation?.status === 'compiled') {
|
|
802
|
+
const dqlArtifact = renderGovernedRelationalDqlArtifact(governedRelationalCompilation);
|
|
803
|
+
let result;
|
|
804
|
+
let executionError;
|
|
805
|
+
let receipt = governedRelationalCompilation.receipt;
|
|
806
|
+
if (input.executeDqlArtifact) {
|
|
807
|
+
try {
|
|
808
|
+
result = await input.executeDqlArtifact(dqlArtifact);
|
|
809
|
+
receipt = finalizeGovernedCompilationReceipt(receipt, result);
|
|
810
|
+
}
|
|
811
|
+
catch (error) {
|
|
812
|
+
executionError = error instanceof Error ? error.message : String(error);
|
|
813
|
+
}
|
|
814
|
+
}
|
|
815
|
+
const text = executionError
|
|
816
|
+
? `The governed relational query compiled, but execution failed: ${executionError}`
|
|
817
|
+
: result
|
|
818
|
+
? `Compiled and executed the snapshot-bound governed relational plan. Returned ${result.rowCount} row${result.rowCount === 1 ? '' : 's'}.`
|
|
819
|
+
: 'Compiled the snapshot-bound governed relational plan. Execution was not requested.';
|
|
820
|
+
return {
|
|
821
|
+
kind: executionError ? 'no_answer' : 'uncertified',
|
|
822
|
+
sourceTier: executionError ? 'no_answer' : 'dbt_manifest',
|
|
823
|
+
certification: executionError ? 'analyst_review_required' : 'governed',
|
|
824
|
+
reviewStatus: executionError ? 'analyst_review_required' : 'governed',
|
|
825
|
+
confidence: executionError ? 0 : 0.9,
|
|
826
|
+
text,
|
|
827
|
+
answer: text,
|
|
828
|
+
...(executionError ? { executionError, refusalCode: 'grounding_gap' } : {}),
|
|
829
|
+
proposedSql: governedRelationalCompilation.sql,
|
|
830
|
+
sql: governedRelationalCompilation.sql,
|
|
831
|
+
dqlArtifact,
|
|
832
|
+
result,
|
|
833
|
+
governedCompilationReceipt: receipt,
|
|
834
|
+
citations: schemaCitations(schemaContext, 8),
|
|
835
|
+
considered,
|
|
836
|
+
contextPack: input.contextPack,
|
|
837
|
+
providerUsed: provider.name,
|
|
838
|
+
};
|
|
839
|
+
}
|
|
840
|
+
if (authoritativePlanBinding?.status === 'blocked') {
|
|
841
|
+
const text = `The resolved analytical plan cannot execute safely: ${authoritativePlanBinding.reason}`;
|
|
842
|
+
return {
|
|
843
|
+
kind: 'no_answer',
|
|
844
|
+
sourceTier: 'no_answer',
|
|
845
|
+
certification: 'analyst_review_required',
|
|
846
|
+
reviewStatus: 'none',
|
|
847
|
+
confidence: 0,
|
|
848
|
+
text,
|
|
849
|
+
answer: text,
|
|
850
|
+
refusalCode: authoritativePlanBinding.code === 'PLAN_BLOCKED' ? 'ambiguous' : 'grounding_gap',
|
|
851
|
+
refusalDetails: { code: authoritativePlanBinding.code, message: authoritativePlanBinding.reason },
|
|
852
|
+
citations: contextPackCitations(input.contextPack, 8),
|
|
853
|
+
considered,
|
|
854
|
+
contextPack: input.contextPack,
|
|
855
|
+
providerUsed: provider.name,
|
|
856
|
+
};
|
|
857
|
+
}
|
|
657
858
|
const fallbackIntent = classifyAgentIntent({
|
|
658
859
|
question,
|
|
659
860
|
followUp: input.followUp,
|
|
@@ -673,16 +874,25 @@ async function runAnswerLoop(input) {
|
|
|
673
874
|
if (node && !semanticMetricNodes.some((candidate) => candidate.nodeId === node.nodeId))
|
|
674
875
|
semanticMetricNodes.push(node);
|
|
675
876
|
}
|
|
676
|
-
const
|
|
877
|
+
const authoritativeSemanticBinding = authoritativePlanBinding?.status === 'ready'
|
|
878
|
+
&& authoritativePlanBinding.kind === 'semantic'
|
|
879
|
+
? authoritativePlanBinding
|
|
880
|
+
: undefined;
|
|
881
|
+
const preferredSemanticMetric = authoritativeSemanticBinding?.metricNode
|
|
882
|
+
?? (input.resolvedAnalyticalPlan?.mode === 'authoritative'
|
|
883
|
+
? undefined
|
|
884
|
+
: resolvePreferredSemanticMetric([input.preferredExecutionId, ...(input.preferredEvidenceIds ?? [])], semanticMetricNodes, kg));
|
|
677
885
|
const semanticLayerForExec = input.semanticLayer;
|
|
678
886
|
const canExecuteSemanticMetricForMatch = input.canExecuteSemanticMetric
|
|
679
887
|
?? (semanticLayerForExec ? (name) => semanticLayerForExec.canComposeMetric(name) : undefined);
|
|
680
888
|
let semanticMetricMatch = preferredSemanticMetric
|
|
681
889
|
? { metric: preferredSemanticMetric, score: 1, basis: 'name' }
|
|
682
|
-
:
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
890
|
+
: input.resolvedAnalyticalPlan?.mode === 'authoritative'
|
|
891
|
+
? null
|
|
892
|
+
: await matchSemanticMetric(semanticQuestion, semanticMetricNodes, {
|
|
893
|
+
measureTerms: [...questionPlan.requestedShape.measures, ...questionPlan.metricTerms],
|
|
894
|
+
...(canExecuteSemanticMetricForMatch ? { canExecute: canExecuteSemanticMetricForMatch } : {}),
|
|
895
|
+
}).catch(() => null);
|
|
686
896
|
// Stage 1: certified artifact match. Blocks can be executed; dashboards,
|
|
687
897
|
// Apps, and notebooks are returned as governed citations/navigation targets.
|
|
688
898
|
const drilldownCertifiedHit = input.followUp?.kind === 'drilldown'
|
|
@@ -726,8 +936,16 @@ async function runAnswerLoop(input) {
|
|
|
726
936
|
excludedArtifactIds,
|
|
727
937
|
kg,
|
|
728
938
|
}) : null;
|
|
729
|
-
|
|
730
|
-
|
|
939
|
+
const authoritativeCertifiedBinding = authoritativePlanBinding?.status === 'ready'
|
|
940
|
+
&& authoritativePlanBinding.kind === 'certified'
|
|
941
|
+
? authoritativePlanBinding
|
|
942
|
+
: undefined;
|
|
943
|
+
let artifactHit = authoritativeCertifiedBinding
|
|
944
|
+
? { node: authoritativeCertifiedBinding.node, score: 1 }
|
|
945
|
+
: input.resolvedAnalyticalPlan?.mode === 'authoritative'
|
|
946
|
+
? null
|
|
947
|
+
: drilldownCertifiedHit ?? unsafeCatalogCertifiedHit
|
|
948
|
+
?? (catalogCertifiedHit ? null : fallbackCertifiedHit);
|
|
731
949
|
let certifiedExecutionFallback;
|
|
732
950
|
// Certified remains first when it actually covers the question. If the
|
|
733
951
|
// retrieved block does not fit but a governed semantic metric does, never
|
|
@@ -875,8 +1093,10 @@ async function runAnswerLoop(input) {
|
|
|
875
1093
|
const dqlArtifact = buildCertifiedBlockDqlArtifact(artifactHit.node, result, questionPlan.requestedShape.topN?.scope === 'per_group'
|
|
876
1094
|
? undefined
|
|
877
1095
|
: questionPlan.requestedShape.topN?.n);
|
|
1096
|
+
const authoritativeCertifiedFailure = Boolean(authoritativeCertifiedBinding && executionError);
|
|
878
1097
|
const recoverableCertifiedFailure = artifactHit.node.kind === 'block'
|
|
879
1098
|
&& executionError !== undefined
|
|
1099
|
+
&& !authoritativeCertifiedFailure
|
|
880
1100
|
&& isRetryableCertifiedExecutionError(executionError);
|
|
881
1101
|
if (recoverableCertifiedFailure) {
|
|
882
1102
|
// A certified artifact is trusted evidence, not an obligation to return a
|
|
@@ -889,16 +1109,17 @@ async function runAnswerLoop(input) {
|
|
|
889
1109
|
}
|
|
890
1110
|
else {
|
|
891
1111
|
return {
|
|
892
|
-
kind: certifiedShapePassed ? 'certified' : 'uncertified',
|
|
893
|
-
sourceTier,
|
|
1112
|
+
kind: authoritativeCertifiedFailure ? 'no_answer' : certifiedShapePassed ? 'certified' : 'uncertified',
|
|
1113
|
+
sourceTier: authoritativeCertifiedFailure ? 'no_answer' : sourceTier,
|
|
894
1114
|
certification: certifiedShapePassed ? 'certified' : 'analyst_review_required',
|
|
895
|
-
reviewStatus: certifiedShapePassed ? 'certified' : 'analyst_review_required',
|
|
1115
|
+
reviewStatus: authoritativeCertifiedFailure ? 'none' : certifiedShapePassed ? 'certified' : 'analyst_review_required',
|
|
896
1116
|
confidence: certifiedShapePassed ? 0.95 : 0.45,
|
|
897
1117
|
text,
|
|
898
1118
|
answer: text,
|
|
899
1119
|
block: artifactHit.node.kind === 'block' ? artifactHit.node : undefined,
|
|
900
1120
|
result,
|
|
901
1121
|
executionError,
|
|
1122
|
+
...(authoritativeCertifiedFailure ? { refusalCode: 'grounding_gap' } : {}),
|
|
902
1123
|
sql: result?.sql,
|
|
903
1124
|
dqlArtifact,
|
|
904
1125
|
trustLabel: certifiedShapePassed ? input.contextPack?.trustLabel ?? 'certified' : 'mixed',
|
|
@@ -1095,7 +1316,44 @@ async function runAnswerLoop(input) {
|
|
|
1095
1316
|
let semanticBridgeAnswer;
|
|
1096
1317
|
let semanticRuntimeFailure;
|
|
1097
1318
|
let semanticRuntimeCompiledAnswer = false;
|
|
1098
|
-
if (input.semanticLayer
|
|
1319
|
+
if (authoritativeSemanticBinding && input.semanticLayer) {
|
|
1320
|
+
const selection = authoritativeSemanticBinding.selection;
|
|
1321
|
+
semanticBridgeAnswer = composeSemanticQueryFromMembers({
|
|
1322
|
+
semanticLayer: input.semanticLayer,
|
|
1323
|
+
question,
|
|
1324
|
+
selection,
|
|
1325
|
+
...(input.semanticDriver ? { driver: input.semanticDriver } : {}),
|
|
1326
|
+
...(input.semanticTableMapping ? { tableMapping: input.semanticTableMapping } : {}),
|
|
1327
|
+
});
|
|
1328
|
+
if (!semanticBridgeAnswer && input.semanticQueryCompiler) {
|
|
1329
|
+
try {
|
|
1330
|
+
const compiled = await input.semanticQueryCompiler(selection);
|
|
1331
|
+
semanticBridgeAnswer = composeSemanticQueryFromCompiledMembers({
|
|
1332
|
+
semanticLayer: input.semanticLayer,
|
|
1333
|
+
question,
|
|
1334
|
+
selection: compiled.selection ?? selection,
|
|
1335
|
+
sql: compiled.sql,
|
|
1336
|
+
});
|
|
1337
|
+
semanticRuntimeCompiledAnswer = Boolean(semanticBridgeAnswer && compiled.engine !== 'native');
|
|
1338
|
+
}
|
|
1339
|
+
catch (error) {
|
|
1340
|
+
semanticRuntimeFailure = error instanceof Error ? error.message : String(error);
|
|
1341
|
+
}
|
|
1342
|
+
}
|
|
1343
|
+
if (semanticBridgeAnswer) {
|
|
1344
|
+
semanticBridgeToolCalls.push({
|
|
1345
|
+
name: 'compile_resolved_analytical_plan',
|
|
1346
|
+
status: 'checked',
|
|
1347
|
+
inputSummary: `plan: ${authoritativeSemanticBinding.planId}; metric: ${selection.metrics.join(', ')}`,
|
|
1348
|
+
outputSummary: 'Compiled the exact snapshot-bound member selection without rematching.',
|
|
1349
|
+
order: 1,
|
|
1350
|
+
});
|
|
1351
|
+
}
|
|
1352
|
+
else if (!semanticRuntimeFailure) {
|
|
1353
|
+
semanticRuntimeFailure = `The exact plan ${authoritativeSemanticBinding.planId} is not composable by the pinned semantic runtime.`;
|
|
1354
|
+
}
|
|
1355
|
+
}
|
|
1356
|
+
if (!authoritativeSemanticBinding && input.semanticLayer && semanticMetricMatch) {
|
|
1099
1357
|
semanticBridgeAnswer = composeSemanticQueryForQuestion({
|
|
1100
1358
|
semanticLayer: input.semanticLayer,
|
|
1101
1359
|
question,
|
|
@@ -2146,7 +2404,7 @@ async function runAnswerLoop(input) {
|
|
|
2146
2404
|
catch (err) {
|
|
2147
2405
|
executionError = err instanceof Error ? err.message : String(err);
|
|
2148
2406
|
}
|
|
2149
|
-
if (executionError && !fanoutContradiction) {
|
|
2407
|
+
if (executionError && !fanoutContradiction && !authoritativeSemanticBinding) {
|
|
2150
2408
|
if (isRetryableGeneratedSqlError(executionError)) {
|
|
2151
2409
|
const localRepairSql = repairGeneratedSqlLocally(parsed.sql, executionError, schemaContext);
|
|
2152
2410
|
if (localRepairSql && canUseLaneRepair(repairBudgetState, 'execution')) {
|
|
@@ -2368,19 +2626,21 @@ async function runAnswerLoop(input) {
|
|
|
2368
2626
|
? semanticMetricMatch?.metric.certification
|
|
2369
2627
|
: undefined;
|
|
2370
2628
|
const certifiedMetricAnswer = semanticMetricCertification === 'certified' || semanticMetricCertification === 'reviewed';
|
|
2629
|
+
const governedMetricExecutionFailure = governedMetricAnswer && Boolean(executionError);
|
|
2371
2630
|
return {
|
|
2372
|
-
kind: 'uncertified',
|
|
2373
|
-
sourceTier: governedMetricAnswer ? 'semantic_layer' : activeTier,
|
|
2374
|
-
certification: governedMetricAnswer ? 'governed' : 'ai_generated',
|
|
2375
|
-
reviewStatus: governedMetricAnswer ? 'governed' : 'draft_ready',
|
|
2631
|
+
kind: governedMetricExecutionFailure ? 'no_answer' : 'uncertified',
|
|
2632
|
+
sourceTier: governedMetricExecutionFailure ? 'no_answer' : governedMetricAnswer ? 'semantic_layer' : activeTier,
|
|
2633
|
+
certification: governedMetricExecutionFailure ? 'analyst_review_required' : governedMetricAnswer ? 'governed' : 'ai_generated',
|
|
2634
|
+
reviewStatus: governedMetricExecutionFailure ? 'none' : governedMetricAnswer ? 'governed' : 'draft_ready',
|
|
2376
2635
|
semanticMetricCertification,
|
|
2377
|
-
confidence: certifiedMetricAnswer ? 0.8 : governedMetricAnswer ? 0.72 : 0.55,
|
|
2636
|
+
confidence: governedMetricExecutionFailure ? 0 : certifiedMetricAnswer ? 0.8 : governedMetricAnswer ? 0.72 : 0.55,
|
|
2378
2637
|
text: generatedText,
|
|
2379
2638
|
answer: generatedText,
|
|
2380
2639
|
proposedSql: parsed.sql,
|
|
2381
2640
|
sql: parsed.sql,
|
|
2382
2641
|
result,
|
|
2383
2642
|
executionError,
|
|
2643
|
+
...(governedMetricExecutionFailure ? { refusalCode: 'grounding_gap' } : {}),
|
|
2384
2644
|
suggestedViz: parsed.viz ?? 'table',
|
|
2385
2645
|
dqlArtifact: answerDqlArtifact,
|
|
2386
2646
|
draftBlock,
|