@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 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
- "/query",
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
- const parts = [];
1454
- if (modifications.dateRange.from) {
1455
- parts.push(`from ${modifications.dateRange.from}`);
1456
- }
1457
- if (modifications.dateRange.to) {
1458
- parts.push(`to ${modifications.dateRange.to}`);
1459
- }
1460
- if (parts.length > 0) {
1461
- hints.push(`filter date range ${parts.join(" ")}`);
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
- "/query",
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: input.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: input.question,
1795
+ question: finalQuestion,
1618
1796
  sql: finalSql,
1619
1797
  rationale,
1620
1798
  fields: execution.fields,