@duckcodeailabs/dql-agent 1.6.3 → 1.6.5
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/answer-loop.d.ts +4 -2
- package/dist/answer-loop.d.ts.map +1 -1
- package/dist/answer-loop.js +212 -25
- package/dist/answer-loop.js.map +1 -1
- package/dist/app-builder.d.ts +31 -1
- package/dist/app-builder.d.ts.map +1 -1
- package/dist/app-builder.js +299 -27
- package/dist/app-builder.js.map +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/skills/loader.d.ts +2 -0
- package/dist/skills/loader.d.ts.map +1 -1
- package/dist/skills/loader.js +39 -3
- package/dist/skills/loader.js.map +1 -1
- package/package.json +4 -4
package/dist/answer-loop.d.ts
CHANGED
|
@@ -26,7 +26,7 @@ export type AnswerReviewStatus = 'none' | 'draft_ready' | 'analyst_review_requir
|
|
|
26
26
|
export type AgentIntent = 'exact_certified_lookup' | 'ad_hoc_analysis' | 'drillthrough' | 'clarify';
|
|
27
27
|
export interface AgentCitation {
|
|
28
28
|
nodeId: string;
|
|
29
|
-
kind: KGNode['kind'] | 'memory';
|
|
29
|
+
kind: KGNode['kind'] | 'memory' | 'runtime_schema';
|
|
30
30
|
name: string;
|
|
31
31
|
/** Frozen-in-time SHA at the moment of indexing. */
|
|
32
32
|
gitSha?: string;
|
|
@@ -43,7 +43,7 @@ export interface AgentEvidenceRouteStep {
|
|
|
43
43
|
}
|
|
44
44
|
export interface AgentEvidenceAsset {
|
|
45
45
|
nodeId: string;
|
|
46
|
-
kind: KGNode['kind'] | 'memory' | 'question';
|
|
46
|
+
kind: KGNode['kind'] | 'memory' | 'question' | 'runtime_schema';
|
|
47
47
|
name: string;
|
|
48
48
|
description?: string;
|
|
49
49
|
sourceTier?: AnswerSourceTier | 'memory' | 'project';
|
|
@@ -73,6 +73,8 @@ export interface AgentSchemaColumn {
|
|
|
73
73
|
name: string;
|
|
74
74
|
type?: string;
|
|
75
75
|
description?: string;
|
|
76
|
+
/** Bounded runtime values that matched the user's question, used only as SQL-generation hints. */
|
|
77
|
+
sampleValues?: string[];
|
|
76
78
|
}
|
|
77
79
|
export interface AgentSchemaTable {
|
|
78
80
|
relation: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"answer-loop.d.ts","sourceRoot":"","sources":["../src/answer-loop.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,oBAAoB,CAAC;AAClD,OAAO,KAAK,EAAE,MAAM,EAAc,WAAW,EAAE,MAAM,eAAe,CAAC;AACrE,OAAO,KAAK,EAAE,aAAa,EAAgB,MAAM,sBAAsB,CAAC;AACxE,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,oBAAoB,CAAC;AAEhD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,2BAA2B,CAAC;AAE7D,MAAM,MAAM,UAAU,GAAG,WAAW,GAAG,aAAa,GAAG,WAAW,CAAC;AACnE,MAAM,MAAM,gBAAgB,GAAG,oBAAoB,GAAG,kBAAkB,GAAG,gBAAgB,GAAG,cAAc,GAAG,WAAW,CAAC;AAC3H,MAAM,MAAM,mBAAmB,GAAG,WAAW,GAAG,cAAc,GAAG,yBAAyB,CAAC;AAC3F,MAAM,MAAM,kBAAkB,GAAG,MAAM,GAAG,aAAa,GAAG,yBAAyB,GAAG,WAAW,CAAC;AAClG,MAAM,MAAM,WAAW,GAAG,wBAAwB,GAAG,iBAAiB,GAAG,cAAc,GAAG,SAAS,CAAC;AAEpG,MAAM,WAAW,aAAa;IAC5B,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC,MAAM,CAAC,GAAG,QAAQ,CAAC;
|
|
1
|
+
{"version":3,"file":"answer-loop.d.ts","sourceRoot":"","sources":["../src/answer-loop.ts"],"names":[],"mappings":"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"}
|
package/dist/answer-loop.js
CHANGED
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
* step. Tests can mock the provider with a canned response and exercise the
|
|
15
15
|
* full pipeline.
|
|
16
16
|
*/
|
|
17
|
-
import { buildSkillsPrompt } from './skills/loader.js';
|
|
17
|
+
import { buildSkillBlockHints, buildSkillsPrompt } from './skills/loader.js';
|
|
18
18
|
const CERTIFIED_HIT_THRESHOLD = 0.18;
|
|
19
19
|
const HARD_NEGATIVE_RATIO = 0.5;
|
|
20
20
|
const EXECUTABLE_ARTIFACT_KINDS = ['block', 'dashboard', 'app', 'notebook'];
|
|
@@ -24,6 +24,10 @@ const SEMANTIC_KINDS = ['metric', 'dimension', 'measure', 'entity', 'semantic_mo
|
|
|
24
24
|
const MANIFEST_KINDS = ['dbt_model', 'dbt_source'];
|
|
25
25
|
export async function answer(input) {
|
|
26
26
|
const { question, userId, domain, provider, kg, skills = [], blockHints = [] } = input;
|
|
27
|
+
const effectiveBlockHints = Array.from(new Set([
|
|
28
|
+
...blockHints,
|
|
29
|
+
...buildSkillBlockHints(skills, userId ?? null),
|
|
30
|
+
]));
|
|
27
31
|
const followUpSourceBlock = input.followUp?.sourceBlockName
|
|
28
32
|
? kg.getNode(`block:${input.followUp.sourceBlockName}`)
|
|
29
33
|
: null;
|
|
@@ -52,7 +56,7 @@ export async function answer(input) {
|
|
|
52
56
|
executableArtifactHits,
|
|
53
57
|
businessHits,
|
|
54
58
|
question,
|
|
55
|
-
blockHints: input.followUp?.kind === 'drilldown' ? [] :
|
|
59
|
+
blockHints: input.followUp?.kind === 'drilldown' ? [] : effectiveBlockHints,
|
|
56
60
|
excludedArtifactIds,
|
|
57
61
|
kg,
|
|
58
62
|
})
|
|
@@ -170,7 +174,7 @@ export async function answer(input) {
|
|
|
170
174
|
const reviewRequiredArtifactHits = artifactHits
|
|
171
175
|
.filter((hit) => hit.score >= CERTIFIED_HIT_THRESHOLD && !isCertifiedHit(hit, kg))
|
|
172
176
|
.slice(0, 4);
|
|
173
|
-
const trustedArtifactContext = executableArtifactHits
|
|
177
|
+
const trustedArtifactContext = rankGeneratedContextHits(executableArtifactHits.filter((hit) => !excludedArtifactIds?.has(hit.node.nodeId)), input.schemaContext ?? [], question)
|
|
174
178
|
.filter((hit) => !excludedArtifactIds?.has(hit.node.nodeId))
|
|
175
179
|
.slice(0, 5);
|
|
176
180
|
const contextHits = activeTier === 'semantic_layer'
|
|
@@ -275,6 +279,7 @@ export async function answer(input) {
|
|
|
275
279
|
sourceTier: 'memory',
|
|
276
280
|
provenance: m.source,
|
|
277
281
|
})),
|
|
282
|
+
...schemaCitations(input.schemaContext ?? [], Math.max(0, 4 - contextNodes.length)),
|
|
278
283
|
];
|
|
279
284
|
let result;
|
|
280
285
|
let executionError;
|
|
@@ -364,6 +369,7 @@ export async function answer(input) {
|
|
|
364
369
|
activeTier,
|
|
365
370
|
intent,
|
|
366
371
|
contextNodes,
|
|
372
|
+
schemaContext: input.schemaContext ?? [],
|
|
367
373
|
followUp: input.followUp,
|
|
368
374
|
businessHits,
|
|
369
375
|
semanticHits,
|
|
@@ -383,18 +389,27 @@ export async function answer(input) {
|
|
|
383
389
|
const SYSTEM_PROMPT = `You are the DQL Analytics Agent.
|
|
384
390
|
|
|
385
391
|
Rules:
|
|
386
|
-
1.
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
392
|
+
1. First classify the question: exact saved artifact/direct KPI, entity-specific
|
|
393
|
+
lookup, ad hoc ranking/breakdown/comparison/custom grain, drill-through
|
|
394
|
+
follow-up, or insufficient context.
|
|
395
|
+
2. Use certified DQL blocks only when the user's question exactly asks for that
|
|
396
|
+
saved block, direct KPI, or definition. For single-user/customer/account,
|
|
397
|
+
custom filters, rankings, breakdowns, comparisons, drill-throughs, or custom
|
|
398
|
+
grains, generate review-required SQL from supplied metadata and cite
|
|
399
|
+
certified context as evidence.
|
|
400
|
+
3. If you must generate SQL, return it inside a single \`\`\`sql code block.
|
|
401
|
+
4. Provide a one-paragraph natural-language summary BEFORE the SQL block.
|
|
402
|
+
5. Suggest a visualization type from this list, on a line starting with "Viz:":
|
|
393
403
|
line, bar, area, pie, single_value, table, pivot, kpi.
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
7.
|
|
404
|
+
6. NEVER fabricate column names that are not present in the supplied schema context.
|
|
405
|
+
If a requested filter value is supplied as a matched value, prefer the table
|
|
406
|
+
and column that matched that value.
|
|
407
|
+
7. Return one read-only SELECT or WITH query for the local warehouse/runtime.
|
|
408
|
+
Do NOT use dbt/Jinja macros such as {{ ref(...) }} or {{ source(...) }} in
|
|
409
|
+
proposed SQL. Do not emit INSERT, UPDATE, DELETE, CREATE, DROP, ALTER, COPY,
|
|
410
|
+
PRAGMA, SET, or multiple statements.
|
|
411
|
+
8. If the schema is insufficient to answer, say so explicitly and ask a
|
|
412
|
+
clarifying question instead of guessing.`;
|
|
398
413
|
function renderContextPrompt(blocks, businessContext, others, activeTier, memoryContext, extraContext, followUp, schemaContext = [], intent = 'ad_hoc_analysis') {
|
|
399
414
|
const intentSection = `## Routing intent\n\nintent: ${intent}\n${intent === 'exact_certified_lookup'
|
|
400
415
|
? 'Use a certified artifact only if it exactly answers the question.'
|
|
@@ -420,7 +435,12 @@ function renderContextPrompt(blocks, businessContext, others, activeTier, memory
|
|
|
420
435
|
.map((table) => {
|
|
421
436
|
const cols = table.columns
|
|
422
437
|
.slice(0, 50)
|
|
423
|
-
.map((col) =>
|
|
438
|
+
.map((col) => {
|
|
439
|
+
const sampleValues = col.sampleValues?.length
|
|
440
|
+
? `; matched values: ${col.sampleValues.slice(0, 4).map(formatPromptValue).join(', ')}`
|
|
441
|
+
: '';
|
|
442
|
+
return `${col.name}${col.type ? ` ${col.type}` : ''}${col.description ? ` (${col.description})` : ''}${sampleValues}`;
|
|
443
|
+
})
|
|
424
444
|
.join(', ');
|
|
425
445
|
return `- ${table.relation}${table.description ? ` — ${table.description}` : ''}\n columns: ${cols}`;
|
|
426
446
|
})
|
|
@@ -473,6 +493,8 @@ export function parseProposal(raw) {
|
|
|
473
493
|
function buildSchemaAwareProposal(input) {
|
|
474
494
|
if (input.intent !== 'ad_hoc_analysis' && input.intent !== 'drillthrough')
|
|
475
495
|
return undefined;
|
|
496
|
+
if (isFilteredEntityQuestion(input.question))
|
|
497
|
+
return undefined;
|
|
476
498
|
const lower = input.question.toLowerCase();
|
|
477
499
|
const asksForCustomerPerformance = /\bcustomers?\b/.test(lower)
|
|
478
500
|
&& /\border|orders|spend|revenue|perform|performed|better|top|best|rank|ranking\b/.test(lower)
|
|
@@ -570,12 +592,15 @@ function pickCertifiedArtifact(input) {
|
|
|
570
592
|
return { node, score: 1, snippet: undefined };
|
|
571
593
|
}
|
|
572
594
|
}
|
|
595
|
+
const executableHit = pickFirstCertifiedHit(input.executableArtifactHits, input.kg, input.excludedArtifactIds, input.question);
|
|
573
596
|
if (isBusinessDefinitionQuestion(input.question)) {
|
|
597
|
+
if (executableHit && hasExactExecutableArtifactSignal(input.question, executableHit.node)) {
|
|
598
|
+
return executableHit;
|
|
599
|
+
}
|
|
574
600
|
const businessHit = pickFirstCertifiedHit(input.businessHits, input.kg);
|
|
575
601
|
if (businessHit)
|
|
576
602
|
return businessHit;
|
|
577
603
|
}
|
|
578
|
-
const executableHit = pickFirstCertifiedHit(input.executableArtifactHits, input.kg, input.excludedArtifactIds, input.question);
|
|
579
604
|
if (executableHit && shouldDeferCertifiedArtifactForReviewPath({
|
|
580
605
|
hits: input.executableArtifactHits,
|
|
581
606
|
selected: executableHit,
|
|
@@ -686,6 +711,97 @@ function hasMeaningfulCertifiedBlockSignal(question, node) {
|
|
|
686
711
|
}
|
|
687
712
|
return false;
|
|
688
713
|
}
|
|
714
|
+
function hasExactExecutableArtifactSignal(question, node) {
|
|
715
|
+
if (!EXECUTABLE_ARTIFACT_KINDS.includes(node.kind))
|
|
716
|
+
return false;
|
|
717
|
+
const questionTokens = exactMatchTokens(question);
|
|
718
|
+
const nameTokens = exactMatchTokens(node.name);
|
|
719
|
+
if (nameTokens.size === 0)
|
|
720
|
+
return false;
|
|
721
|
+
for (const token of nameTokens) {
|
|
722
|
+
if (!questionTokens.has(token))
|
|
723
|
+
return false;
|
|
724
|
+
}
|
|
725
|
+
return true;
|
|
726
|
+
}
|
|
727
|
+
function rankGeneratedContextHits(hits, schemaContext, question) {
|
|
728
|
+
const schemaTokens = schemaEntityTokens(schemaContext, question);
|
|
729
|
+
if (schemaTokens.size === 0)
|
|
730
|
+
return hits;
|
|
731
|
+
const filteredEntityQuestion = isFilteredEntityQuestion(question);
|
|
732
|
+
return [...hits].sort((a, b) => {
|
|
733
|
+
const aScore = generatedContextScore(a, schemaTokens, filteredEntityQuestion);
|
|
734
|
+
const bScore = generatedContextScore(b, schemaTokens, filteredEntityQuestion);
|
|
735
|
+
return bScore - aScore;
|
|
736
|
+
});
|
|
737
|
+
}
|
|
738
|
+
function generatedContextScore(hit, schemaTokens, filteredEntityQuestion) {
|
|
739
|
+
const identityTokens = exactMatchTokens([
|
|
740
|
+
hit.node.name,
|
|
741
|
+
hit.node.domain ?? '',
|
|
742
|
+
...(hit.node.tags ?? []),
|
|
743
|
+
].join(' '));
|
|
744
|
+
const bodyTokens = exactMatchTokens([
|
|
745
|
+
hit.node.description ?? '',
|
|
746
|
+
hit.node.llmContext ?? '',
|
|
747
|
+
].join(' '));
|
|
748
|
+
let score = hit.score;
|
|
749
|
+
for (const token of schemaTokens) {
|
|
750
|
+
if (identityTokens.has(token)) {
|
|
751
|
+
score += filteredEntityQuestion ? 0.6 : 0.25;
|
|
752
|
+
}
|
|
753
|
+
else if (bodyTokens.has(token)) {
|
|
754
|
+
score += filteredEntityQuestion ? 0.15 : 0.05;
|
|
755
|
+
}
|
|
756
|
+
}
|
|
757
|
+
if (hit.node.kind === 'block')
|
|
758
|
+
score += 0.2;
|
|
759
|
+
return score;
|
|
760
|
+
}
|
|
761
|
+
function schemaEntityTokens(schemaContext, question) {
|
|
762
|
+
const tokens = new Set();
|
|
763
|
+
for (const table of schemaContext) {
|
|
764
|
+
const hasMatchedValues = table.columns.some((column) => column.sampleValues?.length);
|
|
765
|
+
if (!hasMatchedValues)
|
|
766
|
+
continue;
|
|
767
|
+
for (const token of exactMatchTokens([table.relation, table.name, table.description ?? ''].join(' '))) {
|
|
768
|
+
tokens.add(token);
|
|
769
|
+
}
|
|
770
|
+
for (const column of table.columns) {
|
|
771
|
+
if (!column.sampleValues?.length)
|
|
772
|
+
continue;
|
|
773
|
+
for (const token of exactMatchTokens(column.name))
|
|
774
|
+
tokens.add(token);
|
|
775
|
+
}
|
|
776
|
+
}
|
|
777
|
+
if (tokens.size > 0 || !isFilteredEntityQuestion(question))
|
|
778
|
+
return tokens;
|
|
779
|
+
for (const table of schemaContext) {
|
|
780
|
+
for (const token of exactMatchTokens([table.relation, table.name, table.description ?? ''].join(' '))) {
|
|
781
|
+
if (ENTITY_CONTEXT_TOKENS.has(token))
|
|
782
|
+
tokens.add(token);
|
|
783
|
+
}
|
|
784
|
+
for (const column of table.columns) {
|
|
785
|
+
for (const token of exactMatchTokens(column.name)) {
|
|
786
|
+
if (ENTITY_CONTEXT_TOKENS.has(token))
|
|
787
|
+
tokens.add(token);
|
|
788
|
+
}
|
|
789
|
+
}
|
|
790
|
+
}
|
|
791
|
+
return tokens;
|
|
792
|
+
}
|
|
793
|
+
const ENTITY_CONTEXT_TOKENS = new Set([
|
|
794
|
+
'account',
|
|
795
|
+
'customer',
|
|
796
|
+
'location',
|
|
797
|
+
'member',
|
|
798
|
+
'order',
|
|
799
|
+
'product',
|
|
800
|
+
'region',
|
|
801
|
+
'segment',
|
|
802
|
+
'subscriber',
|
|
803
|
+
'user',
|
|
804
|
+
]);
|
|
689
805
|
function meaningfulTokens(value) {
|
|
690
806
|
const tokens = new Set();
|
|
691
807
|
for (const raw of value.toLowerCase().match(/[a-z0-9_]+/g) ?? []) {
|
|
@@ -698,6 +814,18 @@ function meaningfulTokens(value) {
|
|
|
698
814
|
}
|
|
699
815
|
return tokens;
|
|
700
816
|
}
|
|
817
|
+
function exactMatchTokens(value) {
|
|
818
|
+
const tokens = new Set();
|
|
819
|
+
for (const raw of value.toLowerCase().match(/[a-z0-9_]+/g) ?? []) {
|
|
820
|
+
for (const part of raw.split('_')) {
|
|
821
|
+
const normalized = normalizeToken(part);
|
|
822
|
+
if (!normalized || normalized.length < 3)
|
|
823
|
+
continue;
|
|
824
|
+
tokens.add(normalized);
|
|
825
|
+
}
|
|
826
|
+
}
|
|
827
|
+
return tokens;
|
|
828
|
+
}
|
|
701
829
|
function normalizeToken(token) {
|
|
702
830
|
if (token === 'skus')
|
|
703
831
|
return 'sku';
|
|
@@ -716,14 +844,16 @@ function normalizeToken(token) {
|
|
|
716
844
|
function classifyAgentIntent(input) {
|
|
717
845
|
if (input.followUp?.kind === 'drilldown')
|
|
718
846
|
return 'drillthrough';
|
|
719
|
-
if (isBusinessDefinitionQuestion(input.question))
|
|
720
|
-
return 'exact_certified_lookup';
|
|
721
847
|
if (isExplicitSavedArtifactQuestion(input.question, input.artifactHits))
|
|
722
848
|
return 'exact_certified_lookup';
|
|
723
849
|
const hasContext = input.artifactHits.some((hit) => hit.score >= CERTIFIED_HIT_THRESHOLD) ||
|
|
724
850
|
input.semanticHits.some((hit) => hit.score >= CERTIFIED_HIT_THRESHOLD) ||
|
|
725
851
|
input.manifestHits.some((hit) => hit.score >= CERTIFIED_HIT_THRESHOLD) ||
|
|
726
852
|
input.schemaContext.length > 0;
|
|
853
|
+
if (isFilteredEntityQuestion(input.question))
|
|
854
|
+
return hasContext ? 'ad_hoc_analysis' : 'clarify';
|
|
855
|
+
if (isBusinessDefinitionQuestion(input.question))
|
|
856
|
+
return 'exact_certified_lookup';
|
|
727
857
|
if (isAdHocAnalysisQuestion(input.question))
|
|
728
858
|
return hasContext ? 'ad_hoc_analysis' : 'clarify';
|
|
729
859
|
if (looksLikeDataQuestion(input.question) && !hasContext)
|
|
@@ -746,11 +876,27 @@ function isAdHocAnalysisQuestion(question) {
|
|
|
746
876
|
const lower = question.toLowerCase();
|
|
747
877
|
if (isBusinessDefinitionQuestion(question))
|
|
748
878
|
return false;
|
|
749
|
-
return /\b(break\s*down|breakdown|drill\s*(?:down|into)|slice|segment|split|compare|versus|vs\.?|trend|over time|top|bottom|best|worst|highest|lowest|rank|ranking|performed better|better performing|by\s+[a-z][\w\s-]{1,40})\b/i.test(lower)
|
|
750
|
-
|| /\b(show|list|find|give)\b.+\b(customer|customers|product|products|order|orders|region|location|month|week|day)\b/i.test(lower);
|
|
879
|
+
return /\b(break\s*down|breakdown|drill\s*(?:down|into)|slice|segment|split|compare|versus|vs\.?|trend|over time|top|bottom|best|worst|highest|lowest|rank|ranking|performed better|better performing|why|what drove|driver|drivers|top movers?|changed?|change|dropped?|drop|decreased?|decrease|declined?|decline|increased?|increase|anomal(?:y|ies)|exceptions?|root cause|contribut(?:e|ed|ion)|variance|delta|by\s+[a-z][\w\s-]{1,40})\b/i.test(lower)
|
|
880
|
+
|| /\b(show|list|find|give)\b.+\b(account|accounts|customer|customers|product|products|order|orders|region|location|month|week|day|user|users)\b/i.test(lower);
|
|
881
|
+
}
|
|
882
|
+
function isFilteredEntityQuestion(question) {
|
|
883
|
+
const lower = question.toLowerCase();
|
|
884
|
+
if (!looksLikeDataQuestion(question))
|
|
885
|
+
return false;
|
|
886
|
+
if (/\b(for|where|only|specific|single|individual|named|called)\b.+\b(account|accounts|customer|customers|product|products|sku|user|users)\b/i.test(lower)) {
|
|
887
|
+
return true;
|
|
888
|
+
}
|
|
889
|
+
if (/\b(account|customer|product|sku|user)\s+(?:id|name|email)\b/i.test(lower))
|
|
890
|
+
return true;
|
|
891
|
+
if (/[A-Z][a-z]+(?:\s+[A-Z][a-z]+){1,3}/.test(question) && /\b(revenue|sales|order|orders|spend|value|churn|usage|activity|performance|performed|metric|kpi)\b/i.test(lower)) {
|
|
892
|
+
return true;
|
|
893
|
+
}
|
|
894
|
+
if (/\b[\w.%+-]+@[\w.-]+\.[A-Za-z]{2,}\b/.test(question))
|
|
895
|
+
return true;
|
|
896
|
+
return false;
|
|
751
897
|
}
|
|
752
898
|
function looksLikeDataQuestion(question) {
|
|
753
|
-
return /\b(show|list|find|what|which|how many|how much|compare|trend|revenue|customer|customers|order|orders|product|products|sales|metric|kpi|dashboard|performance|performed)\b/i.test(question);
|
|
899
|
+
return /\b(show|list|find|what|which|how many|how much|compare|trend|revenue|account|accounts|customer|customers|order|orders|product|products|sales|metric|kpi|dashboard|performance|performed|user|users)\b/i.test(question);
|
|
754
900
|
}
|
|
755
901
|
function citationSourceTier(node, fallback) {
|
|
756
902
|
if (node.sourceTier === 'certified_artifact')
|
|
@@ -871,6 +1017,11 @@ function chartNameFromConfig(config) {
|
|
|
871
1017
|
}
|
|
872
1018
|
return undefined;
|
|
873
1019
|
}
|
|
1020
|
+
function formatPromptValue(value) {
|
|
1021
|
+
const compact = value.replace(/\s+/g, ' ').trim();
|
|
1022
|
+
const shown = compact.length > 80 ? `${compact.slice(0, 77)}...` : compact;
|
|
1023
|
+
return JSON.stringify(shown);
|
|
1024
|
+
}
|
|
874
1025
|
function composeClarificationText(question, considered, schemaContext) {
|
|
875
1026
|
const context = considered.slice(0, 3).map((hit) => hit.node.name).join(', ');
|
|
876
1027
|
const tables = schemaContext.slice(0, 3).map((table) => table.relation).join(', ');
|
|
@@ -1082,9 +1233,12 @@ function buildGeneratedEvidence(input) {
|
|
|
1082
1233
|
const semanticObjects = uniqueAssets([...input.contextNodes, ...input.semanticHits.map((hit) => hit.node)]
|
|
1083
1234
|
.filter((node) => SEMANTIC_KINDS.includes(node.kind))
|
|
1084
1235
|
.map(assetFromNode)).slice(0, 6);
|
|
1085
|
-
const sourceTables = uniqueAssets([
|
|
1086
|
-
.
|
|
1087
|
-
|
|
1236
|
+
const sourceTables = uniqueAssets([
|
|
1237
|
+
...[...input.contextNodes, ...input.manifestHits.map((hit) => hit.node)]
|
|
1238
|
+
.filter((node) => MANIFEST_KINDS.includes(node.kind))
|
|
1239
|
+
.map(assetFromNode),
|
|
1240
|
+
...schemaContextAssets(input.schemaContext),
|
|
1241
|
+
]).slice(0, 6);
|
|
1088
1242
|
const selectedAssets = uniqueAssets(selectedNodes.map(assetFromNode)).slice(0, 4);
|
|
1089
1243
|
const selectedSemantic = input.activeTier === 'semantic_layer' && semanticObjects.length > 0;
|
|
1090
1244
|
return {
|
|
@@ -1122,7 +1276,17 @@ function buildGeneratedEvidence(input) {
|
|
|
1122
1276
|
{
|
|
1123
1277
|
tool: input.activeTier === 'semantic_layer' ? 'compose_semantic_query' : 'search_dbt_manifest',
|
|
1124
1278
|
status: 'selected',
|
|
1125
|
-
label: input.activeTier === 'semantic_layer'
|
|
1279
|
+
label: input.activeTier === 'semantic_layer'
|
|
1280
|
+
? 'Composed SQL from semantic context'
|
|
1281
|
+
: input.schemaContext.length > 0
|
|
1282
|
+
? 'Composed SQL from runtime schema and project metadata'
|
|
1283
|
+
: 'Composed SQL from dbt manifest context',
|
|
1284
|
+
},
|
|
1285
|
+
{
|
|
1286
|
+
tool: 'inspect_runtime_schema',
|
|
1287
|
+
status: input.schemaContext.length > 0 ? 'checked' : 'skipped',
|
|
1288
|
+
label: input.schemaContext.length > 0 ? 'Runtime tables and columns attached' : 'No runtime schema context available',
|
|
1289
|
+
detail: input.schemaContext.slice(0, 3).map((table) => table.relation).join(', ') || undefined,
|
|
1126
1290
|
},
|
|
1127
1291
|
{
|
|
1128
1292
|
tool: 'validate_sql',
|
|
@@ -1284,6 +1448,29 @@ function assetFromNode(node) {
|
|
|
1284
1448
|
status: node.status,
|
|
1285
1449
|
};
|
|
1286
1450
|
}
|
|
1451
|
+
function schemaContextAssets(schemaContext) {
|
|
1452
|
+
return schemaContext.slice(0, 6).map((table) => ({
|
|
1453
|
+
nodeId: `runtime_schema:${table.relation}`,
|
|
1454
|
+
kind: 'runtime_schema',
|
|
1455
|
+
name: table.relation,
|
|
1456
|
+
description: table.description ?? `${table.columns.length} runtime column${table.columns.length === 1 ? '' : 's'} available for generated SQL.`,
|
|
1457
|
+
sourceTier: 'project',
|
|
1458
|
+
certification: 'ai_generated',
|
|
1459
|
+
provenance: table.source ?? 'runtime information_schema',
|
|
1460
|
+
sourcePath: table.relation,
|
|
1461
|
+
}));
|
|
1462
|
+
}
|
|
1463
|
+
function schemaCitations(schemaContext, limit) {
|
|
1464
|
+
if (limit <= 0)
|
|
1465
|
+
return [];
|
|
1466
|
+
return schemaContext.slice(0, limit).map((table) => ({
|
|
1467
|
+
nodeId: `runtime_schema:${table.relation}`,
|
|
1468
|
+
kind: 'runtime_schema',
|
|
1469
|
+
name: table.relation,
|
|
1470
|
+
sourceTier: 'dbt_manifest',
|
|
1471
|
+
provenance: table.source ?? 'runtime information_schema',
|
|
1472
|
+
}));
|
|
1473
|
+
}
|
|
1287
1474
|
function certificationForNode(node) {
|
|
1288
1475
|
if (node.status === 'certified' || node.certification === 'certified')
|
|
1289
1476
|
return 'certified';
|