@duckcodeailabs/dql-cli 1.8.1 → 1.8.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/assets/dql-notebook/assets/{index-siDRLswd.js → index-DGbXmDU6.js} +325 -324
- package/dist/assets/dql-notebook/index.html +1 -1
- package/dist/llm/providers/dql-agent-provider.d.ts.map +1 -1
- package/dist/llm/providers/dql-agent-provider.js +33 -7
- package/dist/llm/providers/dql-agent-provider.js.map +1 -1
- package/dist/local-runtime.d.ts +3 -1
- package/dist/local-runtime.d.ts.map +1 -1
- package/dist/local-runtime.js +114 -39
- package/dist/local-runtime.js.map +1 -1
- package/dist/package.json +10 -10
- package/package.json +10 -10
package/dist/local-runtime.js
CHANGED
|
@@ -145,6 +145,7 @@ export function parseAgentRunRequestBody(body) {
|
|
|
145
145
|
return {
|
|
146
146
|
request: {
|
|
147
147
|
question,
|
|
148
|
+
selectedEvidenceId: agentRunString(record.selectedEvidenceId),
|
|
148
149
|
requestedMode,
|
|
149
150
|
audience,
|
|
150
151
|
intent: agentRunString(record.intent),
|
|
@@ -197,6 +198,10 @@ export function shouldSynthesizeAgentRunAnswer(governedAnswer) {
|
|
|
197
198
|
return false;
|
|
198
199
|
return true;
|
|
199
200
|
}
|
|
201
|
+
export function agentAnswerHasExecutionFailure(governedAnswer) {
|
|
202
|
+
return typeof governedAnswer.executionError === 'string'
|
|
203
|
+
&& governedAnswer.executionError.trim().length > 0;
|
|
204
|
+
}
|
|
200
205
|
function businessNarrativeGaps(warnings) {
|
|
201
206
|
const businessRelevant = (warnings ?? []).filter((warning) => !(/missing requested output column/i.test(warning)
|
|
202
207
|
|| /\bquery plan\b/i.test(warning)
|
|
@@ -280,16 +285,20 @@ function recordConversationTurn(store, threadId, run) {
|
|
|
280
285
|
// Conversation persistence is additive; a failed write must not fail the run.
|
|
281
286
|
}
|
|
282
287
|
}
|
|
283
|
-
function conversationTurnInputFromRun(run) {
|
|
288
|
+
export function conversationTurnInputFromRun(run) {
|
|
284
289
|
const artifact = run.artifacts.find((candidate) => candidate.kind === 'answer')
|
|
285
290
|
?? run.artifacts.find((candidate) => candidate.kind === 'research_run')
|
|
286
291
|
?? run.artifacts[0];
|
|
287
292
|
const payload = agentRunRecord(artifact?.payload);
|
|
288
293
|
const result = agentRunRecord(payload?.result);
|
|
289
294
|
const columns = conversationResultColumns(result?.columns);
|
|
290
|
-
|
|
291
|
-
|
|
295
|
+
// The visual preview stays tiny, but member resolution needs a wider bounded
|
|
296
|
+
// value window. Deriving dimensions from only the eight preview rows caused a
|
|
297
|
+
// valid row 9/10 member to disappear before the next turn (AGT-012/E2E-010).
|
|
298
|
+
const memberRows = Array.isArray(result?.rows)
|
|
299
|
+
? result.rows.filter((row) => Boolean(row && typeof row === 'object' && !Array.isArray(row))).slice(0, 24)
|
|
292
300
|
: [];
|
|
301
|
+
const rows = memberRows.slice(0, 8);
|
|
293
302
|
const rowsSample = rows.map((row) => columns.map((column) => row[column]));
|
|
294
303
|
const contextPack = agentRunRecord(payload?.contextPack);
|
|
295
304
|
const questionPlan = agentRunRecord(contextPack?.questionPlan);
|
|
@@ -313,7 +322,7 @@ function conversationTurnInputFromRun(run) {
|
|
|
313
322
|
? {
|
|
314
323
|
columns,
|
|
315
324
|
rowsSample,
|
|
316
|
-
dimensionValues: conversationDimensionValues(columns,
|
|
325
|
+
dimensionValues: conversationDimensionValues(columns, memberRows),
|
|
317
326
|
measureColumns,
|
|
318
327
|
rowCount: typeof rowCountRaw === 'number' ? rowCountRaw : rows.length || undefined,
|
|
319
328
|
}
|
|
@@ -1445,6 +1454,7 @@ export async function startLocalServer(opts) {
|
|
|
1445
1454
|
|| governedAnswer.sourceCertifiedBlock
|
|
1446
1455
|
|| governedAnswer.dqlArtifact?.kind === 'certified_block'));
|
|
1447
1456
|
const isExploratory = Boolean(governedAnswer.exploratoryCandidate);
|
|
1457
|
+
const isExecutionFailure = agentAnswerHasExecutionFailure(governedAnswer);
|
|
1448
1458
|
const isGroundingGap = governedAnswer.kind === 'no_answer'
|
|
1449
1459
|
&& governedAnswer.refusalCode === 'grounding_gap'
|
|
1450
1460
|
&& !isExploratory;
|
|
@@ -1465,7 +1475,7 @@ export async function startLocalServer(opts) {
|
|
|
1465
1475
|
const needsClarification = governedAnswer.kind === 'no_answer'
|
|
1466
1476
|
&& !isGroundingGap && !isProviderError && !isModelDeclined && !isPolicyBlocked;
|
|
1467
1477
|
const sql = governedAnswer.proposedSql ?? governedAnswer.sql;
|
|
1468
|
-
const runnableSql = governedAnswer.kind === 'no_answer' || (isExploratory && !governedAnswer.result)
|
|
1478
|
+
const runnableSql = governedAnswer.kind === 'no_answer' || isExecutionFailure || (isExploratory && !governedAnswer.result)
|
|
1469
1479
|
? undefined
|
|
1470
1480
|
: sql;
|
|
1471
1481
|
// Render executed rows deterministically for ordinary lookups. A second LLM
|
|
@@ -1504,14 +1514,14 @@ export async function startLocalServer(opts) {
|
|
|
1504
1514
|
synthesizedAnswer = undefined;
|
|
1505
1515
|
}
|
|
1506
1516
|
}
|
|
1507
|
-
const status = isProviderError
|
|
1517
|
+
const status = isProviderError || isExecutionFailure
|
|
1508
1518
|
? 'blocked'
|
|
1509
1519
|
: needsClarification
|
|
1510
1520
|
? 'needs_clarification'
|
|
1511
1521
|
: isCertified || isSemantic
|
|
1512
1522
|
? 'completed'
|
|
1513
1523
|
: 'needs_review';
|
|
1514
|
-
const trustState = isProviderError
|
|
1524
|
+
const trustState = isProviderError || isExecutionFailure
|
|
1515
1525
|
? 'blocked'
|
|
1516
1526
|
: needsClarification
|
|
1517
1527
|
? 'not_applicable'
|
|
@@ -1520,7 +1530,7 @@ export async function startLocalServer(opts) {
|
|
|
1520
1530
|
: isSemantic
|
|
1521
1531
|
? 'governed'
|
|
1522
1532
|
: 'review_required';
|
|
1523
|
-
const stopReason = isProviderError
|
|
1533
|
+
const stopReason = isProviderError || isExecutionFailure
|
|
1524
1534
|
? 'blocked'
|
|
1525
1535
|
: needsClarification
|
|
1526
1536
|
? 'needs_clarification'
|
|
@@ -1531,33 +1541,41 @@ export async function startLocalServer(opts) {
|
|
|
1531
1541
|
: 'human_review_required';
|
|
1532
1542
|
const nextActions = needsClarification
|
|
1533
1543
|
? [{ id: 'clarify', label: 'Clarify question', route: 'generated_answer' }]
|
|
1534
|
-
:
|
|
1535
|
-
{ id: '
|
|
1536
|
-
|
|
1537
|
-
|
|
1538
|
-
|
|
1539
|
-
label: governedAnswer.dqlArtifact ? '
|
|
1540
|
-
route: '
|
|
1541
|
-
|
|
1542
|
-
|
|
1543
|
-
|
|
1544
|
+
: isExecutionFailure
|
|
1545
|
+
? [{ id: 'retry-after-connection', label: 'Retry after fixing the database connection', route: 'generated_answer' }]
|
|
1546
|
+
: isGroundingGap
|
|
1547
|
+
? [{ id: 'research-gap', label: 'Research missing metadata coverage', route: 'research', artifactKind: 'research_run' }]
|
|
1548
|
+
: [
|
|
1549
|
+
{ id: 'create-block', label: governedAnswer.dqlArtifact ? 'Review DQL draft' : 'Create DQL draft', route: 'dql_block_draft', artifactKind: 'dql_block_draft' },
|
|
1550
|
+
{ id: 'research-gap', label: 'Research deeper', route: 'research' },
|
|
1551
|
+
...(runnableSql ? [{
|
|
1552
|
+
id: 'insert-sql',
|
|
1553
|
+
label: governedAnswer.dqlArtifact ? 'Insert as DQL cell' : 'Insert SQL preview',
|
|
1554
|
+
route: 'sql_cell',
|
|
1555
|
+
artifactKind: 'sql_cell',
|
|
1556
|
+
}] : []),
|
|
1557
|
+
];
|
|
1544
1558
|
return {
|
|
1545
1559
|
resolvedRoute,
|
|
1546
1560
|
answerRefusalCode: governedAnswer.kind === 'no_answer' ? governedAnswer.refusalCode : undefined,
|
|
1547
1561
|
answerTier: governedAnswer.route?.tier,
|
|
1548
|
-
summary:
|
|
1562
|
+
summary: isExecutionFailure
|
|
1563
|
+
? 'The governed query could not be executed.'
|
|
1564
|
+
: governedAnswer.route?.label ?? (isCertified ? 'Answered from certified DQL context.' : isExploratory ? 'Exploratory DBT-grounded analysis requires review.' : 'Answered with review-required generated analysis.'),
|
|
1549
1565
|
answer: synthesizedAnswer ?? governedAnswer.answer ?? governedAnswer.text,
|
|
1550
1566
|
status,
|
|
1551
1567
|
trustState,
|
|
1552
1568
|
stopReason,
|
|
1553
|
-
artifacts:
|
|
1554
|
-
|
|
1555
|
-
|
|
1556
|
-
|
|
1557
|
-
|
|
1558
|
-
|
|
1559
|
-
|
|
1560
|
-
|
|
1569
|
+
artifacts: isExecutionFailure
|
|
1570
|
+
? []
|
|
1571
|
+
: governedAnswer.kind === 'no_answer'
|
|
1572
|
+
// A refusal still keeps the DQL draft the answer loop produced (when any),
|
|
1573
|
+
// so the "Review DQL draft" next-action isn't a dead link and the user can
|
|
1574
|
+
// see the SQL that was about to run. Provider outages carry no draft.
|
|
1575
|
+
? (governedAnswer.dqlArtifact && !isProviderError && !isGroundingGap && !isModelDeclined && !isPolicyBlocked
|
|
1576
|
+
? [agentRunArtifact('dql_block_draft', 'DQL draft (review required)', governedAnswer.dqlArtifact, undefined, 'review_required')]
|
|
1577
|
+
: [])
|
|
1578
|
+
: [agentRunArtifact('answer', isCertified ? 'Certified answer' : isSemantic ? 'Governed semantic answer' : isExploratory ? 'Exploratory DBT-grounded answer' : 'Review-required answer', governedAnswer, governedAnswer.sourceCertifiedBlock ?? governedAnswer.block?.name, isCertified ? 'certified' : isSemantic ? 'governed' : 'review_required')],
|
|
1561
1579
|
evaluations: [
|
|
1562
1580
|
agentRunEvaluation('route-decision', 'Route decision', true, 'info', routeDecision?.reason ?? 'Routed request to governed answer.', {
|
|
1563
1581
|
plannedRoute: route,
|
|
@@ -1586,6 +1604,7 @@ export async function startLocalServer(opts) {
|
|
|
1586
1604
|
: isExploratory
|
|
1587
1605
|
? 'The answer used bounded DBT/schema evidence because no certified modeled relationship path covered the request. It is review-required and was not certified.'
|
|
1588
1606
|
: 'The answer is generated or semantic-layer backed and remains review-required.', governedAnswer.route),
|
|
1607
|
+
...(isExecutionFailure ? [agentRunEvaluation('query-execution', 'Query execution', false, 'blocking', `The governed query failed before it produced a result: ${governedAnswer.executionError}`)] : []),
|
|
1589
1608
|
...(isGroundingGap ? [
|
|
1590
1609
|
{
|
|
1591
1610
|
...agentRunEvaluation('grounding-gap', 'Metadata grounding', false, 'warning', 'The bounded lookup could not prove the required metadata grounding. No automatic retry or Research escalation was started.', {
|
|
@@ -2171,6 +2190,7 @@ export async function startLocalServer(opts) {
|
|
|
2171
2190
|
: undefined;
|
|
2172
2191
|
const task = buildLocalContextPack(projectRoot, {
|
|
2173
2192
|
question: request.question,
|
|
2193
|
+
focusObjectKey: request.selectedEvidenceId,
|
|
2174
2194
|
followUp,
|
|
2175
2195
|
priorContextPackId: agentRunString(request.conversationContext?.contextPackId),
|
|
2176
2196
|
conversationTopicRelation: topicRelation === 'continuation'
|
|
@@ -2246,9 +2266,9 @@ export async function startLocalServer(opts) {
|
|
|
2246
2266
|
};
|
|
2247
2267
|
}
|
|
2248
2268
|
if ((candidate.kind === 'semantic_metric' || candidate.kind === 'semantic_member')
|
|
2249
|
-
&& semanticEvidence.has(candidate.id)
|
|
2250
|
-
&&
|
|
2251
|
-
|
|
2269
|
+
&& (semanticEvidence.has(candidate.id) || request.selectedEvidenceId === candidate.id)
|
|
2270
|
+
&& (request.selectedEvidenceId === candidate.id
|
|
2271
|
+
|| (pack.routeDecision.route !== 'clarify' && pack.routeDecision.route !== 'conflict'))) {
|
|
2252
2272
|
const requestedDimensions = pack.questionPlan.requestedShape.dimensions.map((dimension) => dimension.toLowerCase());
|
|
2253
2273
|
const availableDimensions = (candidate.dimensions ?? []).map((dimension) => dimension.toLowerCase());
|
|
2254
2274
|
const dimensionsFit = requestedDimensions.length === 0 || requestedDimensions.every((requested) => availableDimensions.some((available) => available === requested || available.endsWith(`.${requested}`)));
|
|
@@ -8722,6 +8742,12 @@ export async function startLocalServer(opts) {
|
|
|
8722
8742
|
}));
|
|
8723
8743
|
return;
|
|
8724
8744
|
}
|
|
8745
|
+
const provider = semanticConfig?.provider ?? semanticDetectedProvider ?? 'dql';
|
|
8746
|
+
const dbtManifestReady = provider === 'dbt'
|
|
8747
|
+
? hasDbtSemanticManifest(projectRoot, semanticConfig?.projectPath)
|
|
8748
|
+
: false;
|
|
8749
|
+
const metricFlowReady = provider === 'dbt' ? hasMetricFlowCli() : false;
|
|
8750
|
+
const dbtExecutionReady = dbtManifestReady && metricFlowReady;
|
|
8725
8751
|
const metrics = semanticLayer.listMetrics().map((m) => ({
|
|
8726
8752
|
name: m.name,
|
|
8727
8753
|
label: m.label,
|
|
@@ -8736,6 +8762,7 @@ export async function startLocalServer(opts) {
|
|
|
8736
8762
|
typeParams: m.typeParams ?? null,
|
|
8737
8763
|
filter: m.filter ?? null,
|
|
8738
8764
|
source: m.source ?? null,
|
|
8765
|
+
execution: semanticMetricExecutionCapability(m.name, semanticLayer, provider, metricFlowReady, connection?.driver),
|
|
8739
8766
|
}));
|
|
8740
8767
|
const measures = semanticLayer.listMeasures().map((m) => ({
|
|
8741
8768
|
name: m.name,
|
|
@@ -8833,12 +8860,6 @@ export async function startLocalServer(opts) {
|
|
|
8833
8860
|
owner: q.owner ?? null,
|
|
8834
8861
|
source: q.source ?? null,
|
|
8835
8862
|
}));
|
|
8836
|
-
const provider = semanticConfig?.provider ?? semanticDetectedProvider ?? 'dql';
|
|
8837
|
-
const dbtManifestReady = provider === 'dbt'
|
|
8838
|
-
? hasDbtSemanticManifest(projectRoot, semanticConfig?.projectPath)
|
|
8839
|
-
: false;
|
|
8840
|
-
const metricFlowReady = provider === 'dbt' ? hasMetricFlowCli() : false;
|
|
8841
|
-
const dbtExecutionReady = dbtManifestReady && metricFlowReady;
|
|
8842
8863
|
const dbtExecutionSetup = dbtExecutionReady
|
|
8843
8864
|
? null
|
|
8844
8865
|
: !dbtManifestReady && !metricFlowReady
|
|
@@ -9753,8 +9774,20 @@ export async function startLocalServer(opts) {
|
|
|
9753
9774
|
tableMapping,
|
|
9754
9775
|
});
|
|
9755
9776
|
if (!composed) {
|
|
9777
|
+
const provider = semanticConfig?.provider ?? semanticDetectedProvider ?? 'dql';
|
|
9778
|
+
const metricFlowReady = provider === 'dbt' && hasMetricFlowCli();
|
|
9779
|
+
const blocked = metrics.map((metricName) => ({
|
|
9780
|
+
metric: metricName,
|
|
9781
|
+
...semanticMetricExecutionCapability(metricName, semanticLayer, provider, metricFlowReady, driver),
|
|
9782
|
+
})).filter((capability) => capability.status !== 'ready');
|
|
9756
9783
|
res.writeHead(400, { 'Content-Type': 'application/json; charset=utf-8' });
|
|
9757
|
-
res.end(serializeJSON({
|
|
9784
|
+
res.end(serializeJSON({
|
|
9785
|
+
error: blocked.length > 0
|
|
9786
|
+
? blocked.map((capability) => `${capability.metric}: ${capability.reason}`).join(' ')
|
|
9787
|
+
: 'The selected dimensions do not share a governed join path with every selected metric.',
|
|
9788
|
+
code: blocked.length > 0 ? 'SEMANTIC_RUNTIME_REQUIRED' : 'SEMANTIC_FIELDS_INCOMPATIBLE',
|
|
9789
|
+
details: { metrics, dimensions, blocked },
|
|
9790
|
+
}));
|
|
9758
9791
|
return;
|
|
9759
9792
|
}
|
|
9760
9793
|
// Execute the composed SQL against the resolved connection
|
|
@@ -9839,8 +9872,20 @@ export async function startLocalServer(opts) {
|
|
|
9839
9872
|
tableMapping,
|
|
9840
9873
|
});
|
|
9841
9874
|
if (!composed) {
|
|
9875
|
+
const provider = semanticConfig?.provider ?? semanticDetectedProvider ?? 'dql';
|
|
9876
|
+
const metricFlowReady = provider === 'dbt' && hasMetricFlowCli();
|
|
9877
|
+
const blocked = metrics.map((metricName) => ({
|
|
9878
|
+
metric: metricName,
|
|
9879
|
+
...semanticMetricExecutionCapability(metricName, semanticLayer, provider, metricFlowReady, driver),
|
|
9880
|
+
})).filter((capability) => capability.status !== 'ready');
|
|
9842
9881
|
res.writeHead(400, { 'Content-Type': 'application/json; charset=utf-8' });
|
|
9843
|
-
res.end(serializeJSON({
|
|
9882
|
+
res.end(serializeJSON({
|
|
9883
|
+
error: blocked.length > 0
|
|
9884
|
+
? blocked.map((capability) => `${capability.metric}: ${capability.reason}`).join(' ')
|
|
9885
|
+
: 'The selected dimensions do not share a governed join path with every selected metric.',
|
|
9886
|
+
code: blocked.length > 0 ? 'SEMANTIC_RUNTIME_REQUIRED' : 'SEMANTIC_FIELDS_INCOMPATIBLE',
|
|
9887
|
+
details: { metrics, dimensions, blocked },
|
|
9888
|
+
}));
|
|
9844
9889
|
return;
|
|
9845
9890
|
}
|
|
9846
9891
|
const prepared = prepareLocalExecution(composed.sql, targetConnection, projectRoot, projectConfig);
|
|
@@ -13671,6 +13716,28 @@ export function openBlockStudioDocument(projectRoot, relativePath, semanticLayer
|
|
|
13671
13716
|
validation: validateBlockStudioSource(source, semanticLayer),
|
|
13672
13717
|
};
|
|
13673
13718
|
}
|
|
13719
|
+
function semanticMetricExecutionCapability(metricName, semanticLayer, provider, metricFlowReady, driver) {
|
|
13720
|
+
if (provider === 'dbt' && metricFlowReady) {
|
|
13721
|
+
return { status: 'ready', engine: 'metricflow', reason: null };
|
|
13722
|
+
}
|
|
13723
|
+
const native = semanticLayer.composeQuery({ metrics: [metricName], dimensions: [], driver });
|
|
13724
|
+
if (native)
|
|
13725
|
+
return { status: 'ready', engine: 'native', reason: null };
|
|
13726
|
+
const metric = semanticLayer.getMetric(metricName);
|
|
13727
|
+
if (provider === 'dbt') {
|
|
13728
|
+
const kind = metric?.metricType || metric?.aggregation || metric?.type || 'metric';
|
|
13729
|
+
return {
|
|
13730
|
+
status: 'requires_setup',
|
|
13731
|
+
engine: null,
|
|
13732
|
+
reason: `${kind} metric requires MetricFlow execution. Install/configure the MetricFlow CLI, then refresh the semantic layer.`,
|
|
13733
|
+
};
|
|
13734
|
+
}
|
|
13735
|
+
return {
|
|
13736
|
+
status: 'unsupported',
|
|
13737
|
+
engine: null,
|
|
13738
|
+
reason: 'The metric does not have enough composable measure and relation metadata.',
|
|
13739
|
+
};
|
|
13740
|
+
}
|
|
13674
13741
|
function parseBlockStudioArrayField(source, key) {
|
|
13675
13742
|
const match = source.match(new RegExp(`\\b${key}\\s*=\\s*\\[([\\s\\S]*?)\\]`, 'i'));
|
|
13676
13743
|
if (!match)
|
|
@@ -13905,10 +13972,18 @@ function composeSemanticBlockSql(source, semanticLayer, options) {
|
|
|
13905
13972
|
return { sql: null, diagnostics, semanticRefs };
|
|
13906
13973
|
}
|
|
13907
13974
|
if (!composed) {
|
|
13975
|
+
const provider = options?.projectConfig && isDbtSemanticRuntime(options.projectConfig, options.detectedProvider, semanticLayer) ? 'dbt' : (options?.detectedProvider ?? 'dql');
|
|
13976
|
+
const metricFlowReady = provider === 'dbt' && hasMetricFlowCli();
|
|
13977
|
+
const reasons = metrics.map((metricName) => {
|
|
13978
|
+
const capability = semanticMetricExecutionCapability(metricName, semanticLayer, provider, metricFlowReady, options?.driver);
|
|
13979
|
+
return capability.status === 'ready' ? null : `${metricName}: ${capability.reason}`;
|
|
13980
|
+
}).filter((reason) => Boolean(reason));
|
|
13908
13981
|
diagnostics.push({
|
|
13909
13982
|
severity: 'error',
|
|
13910
13983
|
code: 'semantic_compose_failed',
|
|
13911
|
-
message:
|
|
13984
|
+
message: reasons.length > 0
|
|
13985
|
+
? `Could not compose SQL for semantic block metrics. ${reasons.join(' ')}`
|
|
13986
|
+
: `Could not compose SQL for semantic block metrics: [${metrics.join(', ')}]. Check that the selected dimensions share a governed join path.`,
|
|
13912
13987
|
});
|
|
13913
13988
|
return { sql: null, diagnostics, semanticRefs };
|
|
13914
13989
|
}
|