@querypanel/node-sdk 1.0.51 → 1.0.53
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/index.cjs +194 -16
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +50 -1
- package/dist/index.d.ts +50 -1
- package/dist/index.js +194 -16
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -929,6 +929,8 @@ var QueryErrorCode = {
|
|
|
929
929
|
SQL_VALIDATION_FAILED: "SQL_VALIDATION_FAILED",
|
|
930
930
|
// Context retrieval errors
|
|
931
931
|
CONTEXT_RETRIEVAL_FAILED: "CONTEXT_RETRIEVAL_FAILED",
|
|
932
|
+
// Clarification errors (v2)
|
|
933
|
+
CLARIFICATION_NEEDED: "CLARIFICATION_NEEDED",
|
|
932
934
|
// General errors
|
|
933
935
|
INTERNAL_ERROR: "INTERNAL_ERROR",
|
|
934
936
|
AUTHENTICATION_REQUIRED: "AUTHENTICATION_REQUIRED",
|
|
@@ -965,6 +967,12 @@ var QueryPipelineError = class extends Error {
|
|
|
965
967
|
isGuardrailError() {
|
|
966
968
|
return this.isRelevanceError() || this.isSecurityError();
|
|
967
969
|
}
|
|
970
|
+
/**
|
|
971
|
+
* Check if this is a clarification needed error (v2 pipeline)
|
|
972
|
+
*/
|
|
973
|
+
isClarificationNeeded() {
|
|
974
|
+
return this.code === QueryErrorCode.CLARIFICATION_NEEDED;
|
|
975
|
+
}
|
|
968
976
|
};
|
|
969
977
|
|
|
970
978
|
// src/routes/charts.ts
|
|
@@ -1276,6 +1284,7 @@ async function ask(client, queryEngine, question, options, signal) {
|
|
|
1276
1284
|
let attempt = 0;
|
|
1277
1285
|
let lastError = options.lastError;
|
|
1278
1286
|
let previousSql = options.previousSql;
|
|
1287
|
+
const queryEndpoint = options.pipeline === "v2" ? "/v2/query" : "/query";
|
|
1279
1288
|
while (attempt <= maxRetry) {
|
|
1280
1289
|
console.log({ lastError, previousSql });
|
|
1281
1290
|
const databaseName = options.database ?? queryEngine.getDefaultDatabase();
|
|
@@ -1289,7 +1298,7 @@ async function ask(client, queryEngine, question, options, signal) {
|
|
|
1289
1298
|
};
|
|
1290
1299
|
}
|
|
1291
1300
|
const queryResponse = await client.postWithHeaders(
|
|
1292
|
-
|
|
1301
|
+
queryEndpoint,
|
|
1293
1302
|
{
|
|
1294
1303
|
question,
|
|
1295
1304
|
...querypanelSessionId ? { session_id: querypanelSessionId } : {},
|
|
@@ -1405,7 +1414,9 @@ async function ask(client, queryEngine, question, options, signal) {
|
|
|
1405
1414
|
context: queryResponse.data.context,
|
|
1406
1415
|
attempts: attempt + 1,
|
|
1407
1416
|
target_db: dbName,
|
|
1408
|
-
querypanelSessionId: responseSessionId ?? void 0
|
|
1417
|
+
querypanelSessionId: responseSessionId ?? void 0,
|
|
1418
|
+
intent: queryResponse.data.intent,
|
|
1419
|
+
trace: queryResponse.data.trace
|
|
1409
1420
|
};
|
|
1410
1421
|
} catch (error) {
|
|
1411
1422
|
attempt++;
|
|
@@ -1444,21 +1455,39 @@ function anonymizeResults(rows) {
|
|
|
1444
1455
|
}
|
|
1445
1456
|
|
|
1446
1457
|
// src/routes/modify.ts
|
|
1447
|
-
function buildModifiedQuestion(originalQuestion, modifications) {
|
|
1458
|
+
function buildModifiedQuestion(originalQuestion, modifications, pipeline) {
|
|
1448
1459
|
const hints = [];
|
|
1449
1460
|
if (modifications.timeGranularity) {
|
|
1450
1461
|
hints.push(`group results by ${modifications.timeGranularity}`);
|
|
1451
1462
|
}
|
|
1452
1463
|
if (modifications.dateRange) {
|
|
1453
|
-
|
|
1454
|
-
|
|
1455
|
-
|
|
1456
|
-
|
|
1457
|
-
|
|
1458
|
-
|
|
1459
|
-
|
|
1460
|
-
|
|
1461
|
-
|
|
1464
|
+
if (pipeline === "v2") {
|
|
1465
|
+
const from = normalizeDateInput(modifications.dateRange.from);
|
|
1466
|
+
const to = normalizeDateInput(modifications.dateRange.to);
|
|
1467
|
+
if (from && to) {
|
|
1468
|
+
hints.push(
|
|
1469
|
+
`replace any existing date filters with exact date range from ${from} to ${to} (inclusive, do not add extra days)`
|
|
1470
|
+
);
|
|
1471
|
+
} else if (from) {
|
|
1472
|
+
hints.push(
|
|
1473
|
+
`replace any existing date filters with exact start date ${from} (do not shift this date)`
|
|
1474
|
+
);
|
|
1475
|
+
} else if (to) {
|
|
1476
|
+
hints.push(
|
|
1477
|
+
`replace any existing date filters with exact end date ${to} (inclusive, do not add extra days)`
|
|
1478
|
+
);
|
|
1479
|
+
}
|
|
1480
|
+
} else {
|
|
1481
|
+
const parts = [];
|
|
1482
|
+
if (modifications.dateRange.from) {
|
|
1483
|
+
parts.push(`from ${modifications.dateRange.from}`);
|
|
1484
|
+
}
|
|
1485
|
+
if (modifications.dateRange.to) {
|
|
1486
|
+
parts.push(`to ${modifications.dateRange.to}`);
|
|
1487
|
+
}
|
|
1488
|
+
if (parts.length > 0) {
|
|
1489
|
+
hints.push(`filter date range ${parts.join(" ")}`);
|
|
1490
|
+
}
|
|
1462
1491
|
}
|
|
1463
1492
|
}
|
|
1464
1493
|
if (modifications.additionalInstructions) {
|
|
@@ -1469,6 +1498,67 @@ function buildModifiedQuestion(originalQuestion, modifications) {
|
|
|
1469
1498
|
}
|
|
1470
1499
|
return `${originalQuestion} (${hints.join(", ")})`;
|
|
1471
1500
|
}
|
|
1501
|
+
var START_PARAM_KEY_REGEX = /(^|_)(start|from)(_|$)/i;
|
|
1502
|
+
var END_PARAM_KEY_REGEX = /(^|_)(end|to)(_|$)/i;
|
|
1503
|
+
var ISO_DATETIME_RE = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(\.\d+)?Z?$/;
|
|
1504
|
+
var SQL_DATE_RE = /^\d{4}-\d{2}-\d{2}$/;
|
|
1505
|
+
var SQL_DATETIME_RE = /^\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}$/;
|
|
1506
|
+
function normalizeDateInput(value) {
|
|
1507
|
+
if (!value) return void 0;
|
|
1508
|
+
const trimmed = value.trim();
|
|
1509
|
+
if (!trimmed) return void 0;
|
|
1510
|
+
if (ISO_DATETIME_RE.test(trimmed)) {
|
|
1511
|
+
return trimmed.replace("T", " ").replace(/\.\d+Z?$/, "").replace(/Z$/, "");
|
|
1512
|
+
}
|
|
1513
|
+
return trimmed;
|
|
1514
|
+
}
|
|
1515
|
+
function keyLooksLikeDateBoundary(key, boundary) {
|
|
1516
|
+
return boundary === "start" ? START_PARAM_KEY_REGEX.test(key) : END_PARAM_KEY_REGEX.test(key);
|
|
1517
|
+
}
|
|
1518
|
+
function hasTimeComponent(value) {
|
|
1519
|
+
return typeof value === "string" && /\d{2}:\d{2}:\d{2}/.test(value);
|
|
1520
|
+
}
|
|
1521
|
+
function formatDateOverride(dateValue, boundary, existingValue) {
|
|
1522
|
+
if (SQL_DATE_RE.test(dateValue) && !hasTimeComponent(existingValue)) {
|
|
1523
|
+
return dateValue;
|
|
1524
|
+
}
|
|
1525
|
+
if (SQL_DATE_RE.test(dateValue)) {
|
|
1526
|
+
return `${dateValue} ${boundary === "start" ? "00:00:00" : "23:59:59"}`;
|
|
1527
|
+
}
|
|
1528
|
+
if (SQL_DATETIME_RE.test(dateValue)) {
|
|
1529
|
+
return dateValue;
|
|
1530
|
+
}
|
|
1531
|
+
return dateValue;
|
|
1532
|
+
}
|
|
1533
|
+
function normalizeGeneratedParamKey(param, index) {
|
|
1534
|
+
const nameCandidate = typeof param.name === "string" && param.name.trim() || typeof param.placeholder === "string" && param.placeholder.trim() || typeof param.position === "number" && String(param.position) || String(index + 1);
|
|
1535
|
+
return nameCandidate.replace(/[{}]/g, "").replace(/(.+):.*$/, "$1").replace(/^[:$]/, "").trim();
|
|
1536
|
+
}
|
|
1537
|
+
function applyDateRangeOverrides(dateRange, params, paramMetadata) {
|
|
1538
|
+
if (!dateRange) return;
|
|
1539
|
+
const from = normalizeDateInput(dateRange.from);
|
|
1540
|
+
const to = normalizeDateInput(dateRange.to);
|
|
1541
|
+
if (!from && !to) return;
|
|
1542
|
+
for (const [key, value] of Object.entries(params)) {
|
|
1543
|
+
if (from && keyLooksLikeDateBoundary(key, "start")) {
|
|
1544
|
+
params[key] = formatDateOverride(from, "start", value);
|
|
1545
|
+
}
|
|
1546
|
+
if (to && keyLooksLikeDateBoundary(key, "end")) {
|
|
1547
|
+
params[key] = formatDateOverride(to, "end", value);
|
|
1548
|
+
}
|
|
1549
|
+
}
|
|
1550
|
+
for (let i = 0; i < paramMetadata.length; i++) {
|
|
1551
|
+
const param = paramMetadata[i];
|
|
1552
|
+
if (!param) continue;
|
|
1553
|
+
const key = normalizeGeneratedParamKey(param, i);
|
|
1554
|
+
if (from && keyLooksLikeDateBoundary(key, "start")) {
|
|
1555
|
+
param.value = formatDateOverride(from, "start", param.value);
|
|
1556
|
+
}
|
|
1557
|
+
if (to && keyLooksLikeDateBoundary(key, "end")) {
|
|
1558
|
+
param.value = formatDateOverride(to, "end", param.value);
|
|
1559
|
+
}
|
|
1560
|
+
}
|
|
1561
|
+
}
|
|
1472
1562
|
function buildVizHints(modifications) {
|
|
1473
1563
|
const hints = {};
|
|
1474
1564
|
if (modifications.kind) {
|
|
@@ -1501,6 +1591,9 @@ function stripVizSpecOnlyHints(hints) {
|
|
|
1501
1591
|
const { kind: _kind, ...rest } = hints;
|
|
1502
1592
|
return rest;
|
|
1503
1593
|
}
|
|
1594
|
+
function isDateRangeOnly(mods) {
|
|
1595
|
+
return !!mods.dateRange && !mods.timeGranularity && !mods.additionalInstructions && !mods.customSql;
|
|
1596
|
+
}
|
|
1504
1597
|
function resolveTenantId5(client, tenantId) {
|
|
1505
1598
|
const resolved = tenantId ?? client.getDefaultTenantId();
|
|
1506
1599
|
if (!resolved) {
|
|
@@ -1517,12 +1610,14 @@ async function modifyChart(client, queryEngine, input, options, signal) {
|
|
|
1517
1610
|
const hasSqlMods = !!input.sqlModifications;
|
|
1518
1611
|
const hasVizMods = !!input.vizModifications;
|
|
1519
1612
|
const hasCustomSql = !!input.sqlModifications?.customSql;
|
|
1613
|
+
const queryEndpoint = options?.pipeline === "v2" ? "/v2/query" : "/query";
|
|
1520
1614
|
let finalSql = input.sql;
|
|
1521
1615
|
let finalParams = input.params ?? {};
|
|
1522
1616
|
let paramMetadata = [];
|
|
1523
1617
|
let rationale;
|
|
1524
1618
|
let queryId;
|
|
1525
1619
|
let sqlChanged = false;
|
|
1620
|
+
let finalQuestion = input.question;
|
|
1526
1621
|
const databaseName = input.database ?? queryEngine.getDefaultDatabase();
|
|
1527
1622
|
if (!databaseName) {
|
|
1528
1623
|
throw new Error(
|
|
@@ -1543,13 +1638,89 @@ async function modifyChart(client, queryEngine, input, options, signal) {
|
|
|
1543
1638
|
finalParams = {};
|
|
1544
1639
|
paramMetadata = [];
|
|
1545
1640
|
sqlChanged = true;
|
|
1641
|
+
} else if (hasSqlMods && options?.pipeline === "v2" && isDateRangeOnly(input.sqlModifications)) {
|
|
1642
|
+
let usedFastPath = false;
|
|
1643
|
+
try {
|
|
1644
|
+
const rewriteResponse = await client.post(
|
|
1645
|
+
"/v2/rewrite-datefilter",
|
|
1646
|
+
{
|
|
1647
|
+
previous_sql: input.sql,
|
|
1648
|
+
previous_params: input.params ? Object.entries(input.params).map(([name, value]) => ({
|
|
1649
|
+
name,
|
|
1650
|
+
value
|
|
1651
|
+
})) : [],
|
|
1652
|
+
date_range: input.sqlModifications.dateRange,
|
|
1653
|
+
question: input.question,
|
|
1654
|
+
...tenantSettings ? { tenant_settings: tenantSettings } : {},
|
|
1655
|
+
...databaseName ? { database: databaseName } : {},
|
|
1656
|
+
...metadata?.dialect ? { dialect: metadata.dialect } : {}
|
|
1657
|
+
},
|
|
1658
|
+
tenantId,
|
|
1659
|
+
options?.userId,
|
|
1660
|
+
options?.scopes,
|
|
1661
|
+
signal,
|
|
1662
|
+
sessionId
|
|
1663
|
+
);
|
|
1664
|
+
finalSql = rewriteResponse.sql;
|
|
1665
|
+
paramMetadata = Array.isArray(rewriteResponse.params) ? rewriteResponse.params : [];
|
|
1666
|
+
finalParams = queryEngine.mapGeneratedParams(paramMetadata);
|
|
1667
|
+
applyDateRangeOverrides(
|
|
1668
|
+
input.sqlModifications?.dateRange,
|
|
1669
|
+
finalParams,
|
|
1670
|
+
paramMetadata
|
|
1671
|
+
);
|
|
1672
|
+
rationale = rewriteResponse.rationale;
|
|
1673
|
+
queryId = rewriteResponse.queryId;
|
|
1674
|
+
sqlChanged = finalSql !== input.sql;
|
|
1675
|
+
usedFastPath = true;
|
|
1676
|
+
} catch {
|
|
1677
|
+
}
|
|
1678
|
+
if (!usedFastPath) {
|
|
1679
|
+
const modifiedQuestion = buildModifiedQuestion(
|
|
1680
|
+
input.question,
|
|
1681
|
+
input.sqlModifications,
|
|
1682
|
+
"v2"
|
|
1683
|
+
);
|
|
1684
|
+
finalQuestion = modifiedQuestion;
|
|
1685
|
+
const queryResponse = await client.post(
|
|
1686
|
+
queryEndpoint,
|
|
1687
|
+
{
|
|
1688
|
+
question: modifiedQuestion,
|
|
1689
|
+
previous_sql: input.sql,
|
|
1690
|
+
...options?.maxRetry ? { max_retry: options.maxRetry } : {},
|
|
1691
|
+
...tenantSettings ? { tenant_settings: tenantSettings } : {},
|
|
1692
|
+
...databaseName ? { database: databaseName } : {},
|
|
1693
|
+
...metadata?.dialect ? { dialect: metadata.dialect } : {}
|
|
1694
|
+
},
|
|
1695
|
+
tenantId,
|
|
1696
|
+
options?.userId,
|
|
1697
|
+
options?.scopes,
|
|
1698
|
+
signal,
|
|
1699
|
+
sessionId
|
|
1700
|
+
);
|
|
1701
|
+
finalSql = queryResponse.sql;
|
|
1702
|
+
paramMetadata = Array.isArray(queryResponse.params) ? queryResponse.params : [];
|
|
1703
|
+
finalParams = queryEngine.mapGeneratedParams(paramMetadata);
|
|
1704
|
+
applyDateRangeOverrides(
|
|
1705
|
+
input.sqlModifications?.dateRange,
|
|
1706
|
+
finalParams,
|
|
1707
|
+
paramMetadata
|
|
1708
|
+
);
|
|
1709
|
+
rationale = queryResponse.rationale;
|
|
1710
|
+
queryId = queryResponse.queryId;
|
|
1711
|
+
sqlChanged = finalSql !== input.sql;
|
|
1712
|
+
}
|
|
1546
1713
|
} else if (hasSqlMods && !hasCustomSql) {
|
|
1547
1714
|
const modifiedQuestion = buildModifiedQuestion(
|
|
1548
1715
|
input.question,
|
|
1549
|
-
input.sqlModifications
|
|
1716
|
+
input.sqlModifications,
|
|
1717
|
+
options?.pipeline
|
|
1550
1718
|
);
|
|
1719
|
+
if (options?.pipeline === "v2") {
|
|
1720
|
+
finalQuestion = modifiedQuestion;
|
|
1721
|
+
}
|
|
1551
1722
|
const queryResponse = await client.post(
|
|
1552
|
-
|
|
1723
|
+
queryEndpoint,
|
|
1553
1724
|
{
|
|
1554
1725
|
question: modifiedQuestion,
|
|
1555
1726
|
previous_sql: input.sql,
|
|
@@ -1567,6 +1738,13 @@ async function modifyChart(client, queryEngine, input, options, signal) {
|
|
|
1567
1738
|
finalSql = queryResponse.sql;
|
|
1568
1739
|
paramMetadata = Array.isArray(queryResponse.params) ? queryResponse.params : [];
|
|
1569
1740
|
finalParams = queryEngine.mapGeneratedParams(paramMetadata);
|
|
1741
|
+
if (options?.pipeline === "v2") {
|
|
1742
|
+
applyDateRangeOverrides(
|
|
1743
|
+
input.sqlModifications?.dateRange,
|
|
1744
|
+
finalParams,
|
|
1745
|
+
paramMetadata
|
|
1746
|
+
);
|
|
1747
|
+
}
|
|
1570
1748
|
rationale = queryResponse.rationale;
|
|
1571
1749
|
queryId = queryResponse.queryId;
|
|
1572
1750
|
sqlChanged = finalSql !== input.sql;
|
|
@@ -1589,7 +1767,7 @@ async function modifyChart(client, queryEngine, input, options, signal) {
|
|
|
1589
1767
|
const vizspecResponse = await client.post(
|
|
1590
1768
|
"/vizspec",
|
|
1591
1769
|
{
|
|
1592
|
-
question:
|
|
1770
|
+
question: finalQuestion,
|
|
1593
1771
|
sql: finalSql,
|
|
1594
1772
|
rationale,
|
|
1595
1773
|
fields: execution.fields,
|
|
@@ -1614,7 +1792,7 @@ async function modifyChart(client, queryEngine, input, options, signal) {
|
|
|
1614
1792
|
const chartResponse = await client.post(
|
|
1615
1793
|
"/chart",
|
|
1616
1794
|
{
|
|
1617
|
-
question:
|
|
1795
|
+
question: finalQuestion,
|
|
1618
1796
|
sql: finalSql,
|
|
1619
1797
|
rationale,
|
|
1620
1798
|
fields: execution.fields,
|