@querypanel/node-sdk 1.0.42 → 1.0.44
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/README.md +127 -0
- package/dist/index.cjs +637 -23
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +518 -3
- package/dist/index.d.ts +518 -3
- package/dist/index.js +635 -23
- package/dist/index.js.map +1 -1
- package/package.json +71 -71
package/dist/index.js
CHANGED
|
@@ -842,6 +842,57 @@ var QueryEngine = class {
|
|
|
842
842
|
}
|
|
843
843
|
};
|
|
844
844
|
|
|
845
|
+
// src/errors.ts
|
|
846
|
+
var QueryErrorCode = {
|
|
847
|
+
// Moderation errors
|
|
848
|
+
MODERATION_FAILED: "MODERATION_FAILED",
|
|
849
|
+
// Guardrail errors
|
|
850
|
+
RELEVANCE_CHECK_FAILED: "RELEVANCE_CHECK_FAILED",
|
|
851
|
+
SECURITY_CHECK_FAILED: "SECURITY_CHECK_FAILED",
|
|
852
|
+
// SQL generation errors
|
|
853
|
+
SQL_GENERATION_FAILED: "SQL_GENERATION_FAILED",
|
|
854
|
+
// SQL validation errors
|
|
855
|
+
SQL_VALIDATION_FAILED: "SQL_VALIDATION_FAILED",
|
|
856
|
+
// Context retrieval errors
|
|
857
|
+
CONTEXT_RETRIEVAL_FAILED: "CONTEXT_RETRIEVAL_FAILED",
|
|
858
|
+
// General errors
|
|
859
|
+
INTERNAL_ERROR: "INTERNAL_ERROR",
|
|
860
|
+
AUTHENTICATION_REQUIRED: "AUTHENTICATION_REQUIRED",
|
|
861
|
+
VALIDATION_ERROR: "VALIDATION_ERROR"
|
|
862
|
+
};
|
|
863
|
+
var QueryPipelineError = class extends Error {
|
|
864
|
+
constructor(message, code, details) {
|
|
865
|
+
super(message);
|
|
866
|
+
this.code = code;
|
|
867
|
+
this.details = details;
|
|
868
|
+
this.name = "QueryPipelineError";
|
|
869
|
+
}
|
|
870
|
+
/**
|
|
871
|
+
* Check if this is a moderation error
|
|
872
|
+
*/
|
|
873
|
+
isModeration() {
|
|
874
|
+
return this.code === QueryErrorCode.MODERATION_FAILED;
|
|
875
|
+
}
|
|
876
|
+
/**
|
|
877
|
+
* Check if this is a relevance error (question not related to database)
|
|
878
|
+
*/
|
|
879
|
+
isRelevanceError() {
|
|
880
|
+
return this.code === QueryErrorCode.RELEVANCE_CHECK_FAILED;
|
|
881
|
+
}
|
|
882
|
+
/**
|
|
883
|
+
* Check if this is a security error (SQL injection, prompt injection, etc.)
|
|
884
|
+
*/
|
|
885
|
+
isSecurityError() {
|
|
886
|
+
return this.code === QueryErrorCode.SECURITY_CHECK_FAILED;
|
|
887
|
+
}
|
|
888
|
+
/**
|
|
889
|
+
* Check if this is any guardrail error (relevance or security)
|
|
890
|
+
*/
|
|
891
|
+
isGuardrailError() {
|
|
892
|
+
return this.isRelevanceError() || this.isSecurityError();
|
|
893
|
+
}
|
|
894
|
+
};
|
|
895
|
+
|
|
845
896
|
// src/routes/charts.ts
|
|
846
897
|
async function createChart(client, body, options, signal) {
|
|
847
898
|
const tenantId = resolveTenantId(client, options?.tenantId);
|
|
@@ -878,15 +929,10 @@ async function listCharts(client, queryEngine, options, signal) {
|
|
|
878
929
|
);
|
|
879
930
|
if (options?.includeData) {
|
|
880
931
|
response.data = await Promise.all(
|
|
881
|
-
response.data.map(async (chart) =>
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
data: {
|
|
886
|
-
values: await executeChartQuery(queryEngine, chart, tenantId)
|
|
887
|
-
}
|
|
888
|
-
}
|
|
889
|
-
}))
|
|
932
|
+
response.data.map(async (chart) => {
|
|
933
|
+
const rows = await executeChartQuery(queryEngine, chart, tenantId);
|
|
934
|
+
return hydrateChartWithData(chart, rows);
|
|
935
|
+
})
|
|
890
936
|
);
|
|
891
937
|
}
|
|
892
938
|
return response;
|
|
@@ -900,15 +946,8 @@ async function getChart(client, queryEngine, id, options, signal) {
|
|
|
900
946
|
options?.scopes,
|
|
901
947
|
signal
|
|
902
948
|
);
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
vega_lite_spec: {
|
|
906
|
-
...chart.vega_lite_spec,
|
|
907
|
-
data: {
|
|
908
|
-
values: await executeChartQuery(queryEngine, chart, tenantId)
|
|
909
|
-
}
|
|
910
|
-
}
|
|
911
|
-
};
|
|
949
|
+
const rows = await executeChartQuery(queryEngine, chart, tenantId);
|
|
950
|
+
return hydrateChartWithData(chart, rows);
|
|
912
951
|
}
|
|
913
952
|
async function updateChart(client, id, body, options, signal) {
|
|
914
953
|
const tenantId = resolveTenantId(client, options?.tenantId);
|
|
@@ -959,6 +998,31 @@ async function executeChartQuery(queryEngine, chart, tenantId) {
|
|
|
959
998
|
return [];
|
|
960
999
|
}
|
|
961
1000
|
}
|
|
1001
|
+
function hydrateChartWithData(chart, rows) {
|
|
1002
|
+
const spec = chart.vega_lite_spec;
|
|
1003
|
+
if (chart.spec_type === "vizspec") {
|
|
1004
|
+
const existingData = spec.data ?? {};
|
|
1005
|
+
return {
|
|
1006
|
+
...chart,
|
|
1007
|
+
vega_lite_spec: {
|
|
1008
|
+
...spec,
|
|
1009
|
+
data: {
|
|
1010
|
+
...existingData,
|
|
1011
|
+
values: rows
|
|
1012
|
+
}
|
|
1013
|
+
}
|
|
1014
|
+
};
|
|
1015
|
+
}
|
|
1016
|
+
return {
|
|
1017
|
+
...chart,
|
|
1018
|
+
vega_lite_spec: {
|
|
1019
|
+
...spec,
|
|
1020
|
+
data: {
|
|
1021
|
+
values: rows
|
|
1022
|
+
}
|
|
1023
|
+
}
|
|
1024
|
+
};
|
|
1025
|
+
}
|
|
962
1026
|
|
|
963
1027
|
// src/routes/active-charts.ts
|
|
964
1028
|
async function createActiveChart(client, body, options, signal) {
|
|
@@ -1125,6 +1189,9 @@ function buildSchemaRequest(databaseName, adapter, introspection, metadata) {
|
|
|
1125
1189
|
return request;
|
|
1126
1190
|
}
|
|
1127
1191
|
|
|
1192
|
+
// src/routes/modify.ts
|
|
1193
|
+
import crypto4 from "crypto";
|
|
1194
|
+
|
|
1128
1195
|
// src/routes/query.ts
|
|
1129
1196
|
import crypto3 from "crypto";
|
|
1130
1197
|
async function ask(client, queryEngine, question, options, signal) {
|
|
@@ -1163,6 +1230,13 @@ async function ask(client, queryEngine, question, options, signal) {
|
|
|
1163
1230
|
signal,
|
|
1164
1231
|
sessionId
|
|
1165
1232
|
);
|
|
1233
|
+
if (!queryResponse.success) {
|
|
1234
|
+
throw new QueryPipelineError(
|
|
1235
|
+
queryResponse.error || "Query generation failed",
|
|
1236
|
+
queryResponse.code || "INTERNAL_ERROR",
|
|
1237
|
+
queryResponse.details
|
|
1238
|
+
);
|
|
1239
|
+
}
|
|
1166
1240
|
const dbName = queryResponse.database ?? options.database ?? queryEngine.getDefaultDatabase();
|
|
1167
1241
|
if (!dbName) {
|
|
1168
1242
|
throw new Error(
|
|
@@ -1286,11 +1360,216 @@ function anonymizeResults(rows) {
|
|
|
1286
1360
|
});
|
|
1287
1361
|
}
|
|
1288
1362
|
|
|
1289
|
-
// src/routes/
|
|
1290
|
-
|
|
1291
|
-
|
|
1363
|
+
// src/routes/modify.ts
|
|
1364
|
+
function buildModifiedQuestion(originalQuestion, modifications) {
|
|
1365
|
+
const hints = [];
|
|
1366
|
+
if (modifications.timeGranularity) {
|
|
1367
|
+
hints.push(`group results by ${modifications.timeGranularity}`);
|
|
1368
|
+
}
|
|
1369
|
+
if (modifications.dateRange) {
|
|
1370
|
+
const parts = [];
|
|
1371
|
+
if (modifications.dateRange.from) {
|
|
1372
|
+
parts.push(`from ${modifications.dateRange.from}`);
|
|
1373
|
+
}
|
|
1374
|
+
if (modifications.dateRange.to) {
|
|
1375
|
+
parts.push(`to ${modifications.dateRange.to}`);
|
|
1376
|
+
}
|
|
1377
|
+
if (parts.length > 0) {
|
|
1378
|
+
hints.push(`filter date range ${parts.join(" ")}`);
|
|
1379
|
+
}
|
|
1380
|
+
}
|
|
1381
|
+
if (modifications.additionalInstructions) {
|
|
1382
|
+
hints.push(modifications.additionalInstructions);
|
|
1383
|
+
}
|
|
1384
|
+
if (hints.length === 0) {
|
|
1385
|
+
return originalQuestion;
|
|
1386
|
+
}
|
|
1387
|
+
return `${originalQuestion} (${hints.join(", ")})`;
|
|
1388
|
+
}
|
|
1389
|
+
function buildVizHints(modifications) {
|
|
1390
|
+
const hints = {};
|
|
1391
|
+
if (modifications.chartType) {
|
|
1392
|
+
hints.chartType = modifications.chartType;
|
|
1393
|
+
}
|
|
1394
|
+
if (modifications.xAxis) {
|
|
1395
|
+
hints.xAxis = modifications.xAxis;
|
|
1396
|
+
}
|
|
1397
|
+
if (modifications.yAxis) {
|
|
1398
|
+
hints.yAxis = modifications.yAxis;
|
|
1399
|
+
}
|
|
1400
|
+
if (modifications.series) {
|
|
1401
|
+
hints.series = modifications.series;
|
|
1402
|
+
}
|
|
1403
|
+
if (modifications.stacking) {
|
|
1404
|
+
hints.stacking = modifications.stacking;
|
|
1405
|
+
}
|
|
1406
|
+
if (modifications.limit !== void 0) {
|
|
1407
|
+
hints.limit = modifications.limit;
|
|
1408
|
+
}
|
|
1409
|
+
return hints;
|
|
1410
|
+
}
|
|
1411
|
+
function resolveTenantId5(client, tenantId) {
|
|
1412
|
+
const resolved = tenantId ?? client.getDefaultTenantId();
|
|
1413
|
+
if (!resolved) {
|
|
1414
|
+
throw new Error(
|
|
1415
|
+
"tenantId is required. Provide it per request or via defaultTenantId option."
|
|
1416
|
+
);
|
|
1417
|
+
}
|
|
1418
|
+
return resolved;
|
|
1419
|
+
}
|
|
1420
|
+
async function modifyChart(client, queryEngine, input, options, signal) {
|
|
1292
1421
|
const tenantId = resolveTenantId5(client, options?.tenantId);
|
|
1293
1422
|
const sessionId = crypto4.randomUUID();
|
|
1423
|
+
const chartType = options?.chartType ?? "vega-lite";
|
|
1424
|
+
const hasSqlMods = !!input.sqlModifications;
|
|
1425
|
+
const hasVizMods = !!input.vizModifications;
|
|
1426
|
+
const hasCustomSql = !!input.sqlModifications?.customSql;
|
|
1427
|
+
let finalSql = input.sql;
|
|
1428
|
+
let finalParams = input.params ?? {};
|
|
1429
|
+
let paramMetadata = [];
|
|
1430
|
+
let rationale;
|
|
1431
|
+
let queryId;
|
|
1432
|
+
let sqlChanged = false;
|
|
1433
|
+
const databaseName = input.database ?? queryEngine.getDefaultDatabase();
|
|
1434
|
+
if (!databaseName) {
|
|
1435
|
+
throw new Error(
|
|
1436
|
+
"No database specified. Provide database in input or attach a default database."
|
|
1437
|
+
);
|
|
1438
|
+
}
|
|
1439
|
+
const metadata = queryEngine.getDatabaseMetadata(databaseName);
|
|
1440
|
+
let tenantSettings;
|
|
1441
|
+
if (metadata?.tenantFieldName) {
|
|
1442
|
+
tenantSettings = {
|
|
1443
|
+
tenantFieldName: metadata.tenantFieldName,
|
|
1444
|
+
tenantFieldType: metadata.tenantFieldType,
|
|
1445
|
+
enforceTenantIsolation: metadata.enforceTenantIsolation
|
|
1446
|
+
};
|
|
1447
|
+
}
|
|
1448
|
+
if (hasCustomSql) {
|
|
1449
|
+
finalSql = input.sqlModifications.customSql;
|
|
1450
|
+
finalParams = {};
|
|
1451
|
+
paramMetadata = [];
|
|
1452
|
+
sqlChanged = true;
|
|
1453
|
+
} else if (hasSqlMods && !hasCustomSql) {
|
|
1454
|
+
const modifiedQuestion = buildModifiedQuestion(
|
|
1455
|
+
input.question,
|
|
1456
|
+
input.sqlModifications
|
|
1457
|
+
);
|
|
1458
|
+
const queryResponse = await client.post(
|
|
1459
|
+
"/query",
|
|
1460
|
+
{
|
|
1461
|
+
question: modifiedQuestion,
|
|
1462
|
+
previous_sql: input.sql,
|
|
1463
|
+
...options?.maxRetry ? { max_retry: options.maxRetry } : {},
|
|
1464
|
+
...tenantSettings ? { tenant_settings: tenantSettings } : {},
|
|
1465
|
+
...databaseName ? { database: databaseName } : {},
|
|
1466
|
+
...metadata?.dialect ? { dialect: metadata.dialect } : {}
|
|
1467
|
+
},
|
|
1468
|
+
tenantId,
|
|
1469
|
+
options?.userId,
|
|
1470
|
+
options?.scopes,
|
|
1471
|
+
signal,
|
|
1472
|
+
sessionId
|
|
1473
|
+
);
|
|
1474
|
+
finalSql = queryResponse.sql;
|
|
1475
|
+
paramMetadata = Array.isArray(queryResponse.params) ? queryResponse.params : [];
|
|
1476
|
+
finalParams = queryEngine.mapGeneratedParams(paramMetadata);
|
|
1477
|
+
rationale = queryResponse.rationale;
|
|
1478
|
+
queryId = queryResponse.queryId;
|
|
1479
|
+
sqlChanged = finalSql !== input.sql;
|
|
1480
|
+
}
|
|
1481
|
+
const execution = await queryEngine.validateAndExecute(
|
|
1482
|
+
finalSql,
|
|
1483
|
+
finalParams,
|
|
1484
|
+
databaseName,
|
|
1485
|
+
tenantId
|
|
1486
|
+
);
|
|
1487
|
+
const rows = execution.rows ?? [];
|
|
1488
|
+
let chart = {
|
|
1489
|
+
specType: chartType,
|
|
1490
|
+
notes: rows.length === 0 ? "Query returned no rows." : null
|
|
1491
|
+
};
|
|
1492
|
+
if (rows.length > 0) {
|
|
1493
|
+
const vizHints = hasVizMods ? buildVizHints(input.vizModifications) : {};
|
|
1494
|
+
if (chartType === "vizspec") {
|
|
1495
|
+
const vizspecResponse = await client.post(
|
|
1496
|
+
"/vizspec",
|
|
1497
|
+
{
|
|
1498
|
+
question: input.question,
|
|
1499
|
+
sql: finalSql,
|
|
1500
|
+
rationale,
|
|
1501
|
+
fields: execution.fields,
|
|
1502
|
+
rows: anonymizeResults(rows),
|
|
1503
|
+
max_retries: options?.chartMaxRetries ?? 3,
|
|
1504
|
+
query_id: queryId,
|
|
1505
|
+
// Include viz hints for the chart generator
|
|
1506
|
+
...hasVizMods ? { encoding_hints: vizHints } : {}
|
|
1507
|
+
},
|
|
1508
|
+
tenantId,
|
|
1509
|
+
options?.userId,
|
|
1510
|
+
options?.scopes,
|
|
1511
|
+
signal,
|
|
1512
|
+
sessionId
|
|
1513
|
+
);
|
|
1514
|
+
chart = {
|
|
1515
|
+
vizSpec: vizspecResponse.spec,
|
|
1516
|
+
specType: "vizspec",
|
|
1517
|
+
notes: vizspecResponse.notes
|
|
1518
|
+
};
|
|
1519
|
+
} else {
|
|
1520
|
+
const chartResponse = await client.post(
|
|
1521
|
+
"/chart",
|
|
1522
|
+
{
|
|
1523
|
+
question: input.question,
|
|
1524
|
+
sql: finalSql,
|
|
1525
|
+
rationale,
|
|
1526
|
+
fields: execution.fields,
|
|
1527
|
+
rows: anonymizeResults(rows),
|
|
1528
|
+
max_retries: options?.chartMaxRetries ?? 3,
|
|
1529
|
+
query_id: queryId,
|
|
1530
|
+
// Include viz hints for the chart generator
|
|
1531
|
+
...hasVizMods ? { encoding_hints: vizHints } : {}
|
|
1532
|
+
},
|
|
1533
|
+
tenantId,
|
|
1534
|
+
options?.userId,
|
|
1535
|
+
options?.scopes,
|
|
1536
|
+
signal,
|
|
1537
|
+
sessionId
|
|
1538
|
+
);
|
|
1539
|
+
chart = {
|
|
1540
|
+
vegaLiteSpec: chartResponse.chart ? {
|
|
1541
|
+
...chartResponse.chart,
|
|
1542
|
+
data: { values: rows }
|
|
1543
|
+
} : null,
|
|
1544
|
+
specType: "vega-lite",
|
|
1545
|
+
notes: chartResponse.notes
|
|
1546
|
+
};
|
|
1547
|
+
}
|
|
1548
|
+
}
|
|
1549
|
+
return {
|
|
1550
|
+
sql: finalSql,
|
|
1551
|
+
params: finalParams,
|
|
1552
|
+
paramMetadata,
|
|
1553
|
+
rationale,
|
|
1554
|
+
dialect: metadata?.dialect ?? "unknown",
|
|
1555
|
+
queryId,
|
|
1556
|
+
rows,
|
|
1557
|
+
fields: execution.fields,
|
|
1558
|
+
chart,
|
|
1559
|
+
attempts: 1,
|
|
1560
|
+
target_db: databaseName,
|
|
1561
|
+
modified: {
|
|
1562
|
+
sqlChanged,
|
|
1563
|
+
vizChanged: hasVizMods
|
|
1564
|
+
}
|
|
1565
|
+
};
|
|
1566
|
+
}
|
|
1567
|
+
|
|
1568
|
+
// src/routes/vizspec.ts
|
|
1569
|
+
import crypto5 from "crypto";
|
|
1570
|
+
async function generateVizSpec(client, input, options, signal) {
|
|
1571
|
+
const tenantId = resolveTenantId6(client, options?.tenantId);
|
|
1572
|
+
const sessionId = crypto5.randomUUID();
|
|
1294
1573
|
const response = await client.post(
|
|
1295
1574
|
"/vizspec",
|
|
1296
1575
|
{
|
|
@@ -1310,7 +1589,7 @@ async function generateVizSpec(client, input, options, signal) {
|
|
|
1310
1589
|
);
|
|
1311
1590
|
return response;
|
|
1312
1591
|
}
|
|
1313
|
-
function
|
|
1592
|
+
function resolveTenantId6(client, tenantId) {
|
|
1314
1593
|
const resolved = tenantId ?? client.getDefaultTenantId();
|
|
1315
1594
|
if (!resolved) {
|
|
1316
1595
|
throw new Error(
|
|
@@ -1367,6 +1646,30 @@ var QueryPanelSdkAPI = class {
|
|
|
1367
1646
|
const adapter = this.queryEngine.getDatabase(databaseName);
|
|
1368
1647
|
return await adapter.introspect(tables ? { tables } : void 0);
|
|
1369
1648
|
}
|
|
1649
|
+
/**
|
|
1650
|
+
* Syncs the database schema to QueryPanel for natural language query generation.
|
|
1651
|
+
*
|
|
1652
|
+
* This method introspects your database schema and uploads it to QueryPanel's
|
|
1653
|
+
* vector store. The schema is used by the LLM to generate accurate SQL queries.
|
|
1654
|
+
* Schema embedding is skipped if no changes are detected (drift detection).
|
|
1655
|
+
*
|
|
1656
|
+
* @param databaseName - Name of the attached database to sync
|
|
1657
|
+
* @param options - Sync options including tenantId and forceReindex
|
|
1658
|
+
* @param signal - Optional AbortSignal for cancellation
|
|
1659
|
+
* @returns Response with sync status and chunk counts
|
|
1660
|
+
*
|
|
1661
|
+
* @example
|
|
1662
|
+
* ```typescript
|
|
1663
|
+
* // Basic schema sync (skips if no changes)
|
|
1664
|
+
* await qp.syncSchema("analytics", { tenantId: "tenant_123" });
|
|
1665
|
+
*
|
|
1666
|
+
* // Force re-embedding even if schema hasn't changed
|
|
1667
|
+
* await qp.syncSchema("analytics", {
|
|
1668
|
+
* tenantId: "tenant_123",
|
|
1669
|
+
* forceReindex: true,
|
|
1670
|
+
* });
|
|
1671
|
+
* ```
|
|
1672
|
+
*/
|
|
1370
1673
|
async syncSchema(databaseName, options, signal) {
|
|
1371
1674
|
return await syncSchema(
|
|
1372
1675
|
this.client,
|
|
@@ -1377,6 +1680,36 @@ var QueryPanelSdkAPI = class {
|
|
|
1377
1680
|
);
|
|
1378
1681
|
}
|
|
1379
1682
|
// Natural language query
|
|
1683
|
+
/**
|
|
1684
|
+
* Generates SQL from a natural language question and executes it.
|
|
1685
|
+
*
|
|
1686
|
+
* This is the primary method for converting user questions into data.
|
|
1687
|
+
* It handles the complete flow: SQL generation → validation → execution → chart generation.
|
|
1688
|
+
*
|
|
1689
|
+
* @param question - Natural language question (e.g., "Show revenue by country")
|
|
1690
|
+
* @param options - Query options including tenantId, database, and retry settings
|
|
1691
|
+
* @param signal - Optional AbortSignal for cancellation
|
|
1692
|
+
* @returns Response with SQL, executed data rows, and generated chart
|
|
1693
|
+
* @throws {Error} When SQL generation or execution fails after all retries
|
|
1694
|
+
*
|
|
1695
|
+
* @example
|
|
1696
|
+
* ```typescript
|
|
1697
|
+
* // Basic query
|
|
1698
|
+
* const result = await qp.ask("Top 10 customers by revenue", {
|
|
1699
|
+
* tenantId: "tenant_123",
|
|
1700
|
+
* database: "analytics",
|
|
1701
|
+
* });
|
|
1702
|
+
* console.log(result.sql); // Generated SQL
|
|
1703
|
+
* console.log(result.rows); // Query results
|
|
1704
|
+
* console.log(result.chart); // Vega-Lite chart spec
|
|
1705
|
+
*
|
|
1706
|
+
* // With automatic SQL repair on failure
|
|
1707
|
+
* const result = await qp.ask("Show monthly trends", {
|
|
1708
|
+
* tenantId: "tenant_123",
|
|
1709
|
+
* maxRetry: 3, // Retry up to 3 times if SQL fails
|
|
1710
|
+
* });
|
|
1711
|
+
* ```
|
|
1712
|
+
*/
|
|
1380
1713
|
async ask(question, options, signal) {
|
|
1381
1714
|
return await ask(
|
|
1382
1715
|
this.client,
|
|
@@ -1387,6 +1720,28 @@ var QueryPanelSdkAPI = class {
|
|
|
1387
1720
|
);
|
|
1388
1721
|
}
|
|
1389
1722
|
// VizSpec generation
|
|
1723
|
+
/**
|
|
1724
|
+
* Generates a VizSpec visualization specification from query results.
|
|
1725
|
+
*
|
|
1726
|
+
* Use this when you have raw SQL results and want to generate a chart
|
|
1727
|
+
* specification without going through the full ask() flow. Useful for
|
|
1728
|
+
* re-generating charts with different settings.
|
|
1729
|
+
*
|
|
1730
|
+
* @param input - VizSpec generation input with question, SQL, and result data
|
|
1731
|
+
* @param options - Optional settings for tenant and retries
|
|
1732
|
+
* @param signal - Optional AbortSignal for cancellation
|
|
1733
|
+
* @returns VizSpec specification for chart, table, or metric visualization
|
|
1734
|
+
*
|
|
1735
|
+
* @example
|
|
1736
|
+
* ```typescript
|
|
1737
|
+
* const vizspec = await qp.generateVizSpec({
|
|
1738
|
+
* question: "Revenue by country",
|
|
1739
|
+
* sql: "SELECT country, SUM(revenue) FROM orders GROUP BY country",
|
|
1740
|
+
* fields: ["country", "revenue"],
|
|
1741
|
+
* rows: queryResults,
|
|
1742
|
+
* }, { tenantId: "tenant_123" });
|
|
1743
|
+
* ```
|
|
1744
|
+
*/
|
|
1390
1745
|
async generateVizSpec(input, options, signal) {
|
|
1391
1746
|
return await generateVizSpec(
|
|
1392
1747
|
this.client,
|
|
@@ -1395,10 +1750,125 @@ var QueryPanelSdkAPI = class {
|
|
|
1395
1750
|
signal
|
|
1396
1751
|
);
|
|
1397
1752
|
}
|
|
1753
|
+
// Chart modification
|
|
1754
|
+
/**
|
|
1755
|
+
* Modifies a chart by regenerating SQL and/or applying visualization changes.
|
|
1756
|
+
*
|
|
1757
|
+
* This method supports three modes of operation:
|
|
1758
|
+
*
|
|
1759
|
+
* 1. **SQL Modifications**: When `sqlModifications` is provided, the SQL is
|
|
1760
|
+
* regenerated using the query endpoint with modification hints. If `customSql`
|
|
1761
|
+
* is set, it's used directly without regeneration.
|
|
1762
|
+
*
|
|
1763
|
+
* 2. **Visualization Modifications**: When only `vizModifications` is provided,
|
|
1764
|
+
* the existing SQL is re-executed and a new chart is generated with the
|
|
1765
|
+
* specified encoding preferences.
|
|
1766
|
+
*
|
|
1767
|
+
* 3. **Combined**: Both SQL and visualization modifications can be applied
|
|
1768
|
+
* together. SQL is regenerated first, then viz modifications are applied.
|
|
1769
|
+
*
|
|
1770
|
+
* @param input - Chart modification input with source data and modifications
|
|
1771
|
+
* @param options - Optional settings for tenant, user, and chart generation
|
|
1772
|
+
* @param signal - Optional AbortSignal for cancellation
|
|
1773
|
+
* @returns Modified chart response with SQL, data, and chart specification
|
|
1774
|
+
*
|
|
1775
|
+
* @example
|
|
1776
|
+
* ```typescript
|
|
1777
|
+
* // Change chart type and axis from an ask() response
|
|
1778
|
+
* const modified = await qp.modifyChart({
|
|
1779
|
+
* sql: response.sql,
|
|
1780
|
+
* question: "revenue by country",
|
|
1781
|
+
* database: "analytics",
|
|
1782
|
+
* vizModifications: {
|
|
1783
|
+
* chartType: "bar",
|
|
1784
|
+
* xAxis: { field: "country" },
|
|
1785
|
+
* yAxis: { field: "revenue", aggregate: "sum" },
|
|
1786
|
+
* },
|
|
1787
|
+
* }, { tenantId: "tenant_123" });
|
|
1788
|
+
*
|
|
1789
|
+
* // Change time granularity (triggers SQL regeneration)
|
|
1790
|
+
* const monthly = await qp.modifyChart({
|
|
1791
|
+
* sql: response.sql,
|
|
1792
|
+
* question: "revenue over time",
|
|
1793
|
+
* database: "analytics",
|
|
1794
|
+
* sqlModifications: {
|
|
1795
|
+
* timeGranularity: "month",
|
|
1796
|
+
* dateRange: { from: "2024-01-01", to: "2024-12-31" },
|
|
1797
|
+
* },
|
|
1798
|
+
* }, { tenantId: "tenant_123" });
|
|
1799
|
+
*
|
|
1800
|
+
* // Direct SQL edit with chart regeneration
|
|
1801
|
+
* const customized = await qp.modifyChart({
|
|
1802
|
+
* sql: response.sql,
|
|
1803
|
+
* question: "revenue by country",
|
|
1804
|
+
* database: "analytics",
|
|
1805
|
+
* sqlModifications: {
|
|
1806
|
+
* customSql: "SELECT country, SUM(revenue) FROM orders GROUP BY country",
|
|
1807
|
+
* },
|
|
1808
|
+
* }, { tenantId: "tenant_123" });
|
|
1809
|
+
* ```
|
|
1810
|
+
*/
|
|
1811
|
+
async modifyChart(input, options, signal) {
|
|
1812
|
+
return await modifyChart(
|
|
1813
|
+
this.client,
|
|
1814
|
+
this.queryEngine,
|
|
1815
|
+
input,
|
|
1816
|
+
options,
|
|
1817
|
+
signal
|
|
1818
|
+
);
|
|
1819
|
+
}
|
|
1398
1820
|
// Chart CRUD operations
|
|
1821
|
+
/**
|
|
1822
|
+
* Saves a chart to the QueryPanel system for later retrieval.
|
|
1823
|
+
*
|
|
1824
|
+
* Charts store the SQL query, parameters, and visualization spec - never the actual data.
|
|
1825
|
+
* Data is fetched live when the chart is rendered or refreshed.
|
|
1826
|
+
*
|
|
1827
|
+
* @param body - Chart data including title, SQL, and Vega-Lite spec
|
|
1828
|
+
* @param options - Tenant, user, and scope options
|
|
1829
|
+
* @param signal - Optional AbortSignal for cancellation
|
|
1830
|
+
* @returns The saved chart with its generated ID
|
|
1831
|
+
*
|
|
1832
|
+
* @example
|
|
1833
|
+
* ```typescript
|
|
1834
|
+
* const savedChart = await qp.createChart({
|
|
1835
|
+
* title: "Revenue by Country",
|
|
1836
|
+
* sql: response.sql,
|
|
1837
|
+
* sql_params: response.params,
|
|
1838
|
+
* vega_lite_spec: response.chart.vegaLiteSpec,
|
|
1839
|
+
* target_db: "analytics",
|
|
1840
|
+
* }, { tenantId: "tenant_123", userId: "user_456" });
|
|
1841
|
+
* ```
|
|
1842
|
+
*/
|
|
1399
1843
|
async createChart(body, options, signal) {
|
|
1400
1844
|
return await createChart(this.client, body, options, signal);
|
|
1401
1845
|
}
|
|
1846
|
+
/**
|
|
1847
|
+
* Lists saved charts with optional filtering and pagination.
|
|
1848
|
+
*
|
|
1849
|
+
* Use `includeData: true` to execute each chart's SQL and include live data.
|
|
1850
|
+
*
|
|
1851
|
+
* @param options - Filtering, pagination, and data options
|
|
1852
|
+
* @param signal - Optional AbortSignal for cancellation
|
|
1853
|
+
* @returns Paginated list of charts
|
|
1854
|
+
*
|
|
1855
|
+
* @example
|
|
1856
|
+
* ```typescript
|
|
1857
|
+
* // List charts with pagination
|
|
1858
|
+
* const charts = await qp.listCharts({
|
|
1859
|
+
* tenantId: "tenant_123",
|
|
1860
|
+
* pagination: { page: 1, limit: 10 },
|
|
1861
|
+
* sortBy: "created_at",
|
|
1862
|
+
* sortDir: "desc",
|
|
1863
|
+
* });
|
|
1864
|
+
*
|
|
1865
|
+
* // List with live data
|
|
1866
|
+
* const chartsWithData = await qp.listCharts({
|
|
1867
|
+
* tenantId: "tenant_123",
|
|
1868
|
+
* includeData: true,
|
|
1869
|
+
* });
|
|
1870
|
+
* ```
|
|
1871
|
+
*/
|
|
1402
1872
|
async listCharts(options, signal) {
|
|
1403
1873
|
return await listCharts(
|
|
1404
1874
|
this.client,
|
|
@@ -1407,6 +1877,24 @@ var QueryPanelSdkAPI = class {
|
|
|
1407
1877
|
signal
|
|
1408
1878
|
);
|
|
1409
1879
|
}
|
|
1880
|
+
/**
|
|
1881
|
+
* Retrieves a single chart by ID with live data.
|
|
1882
|
+
*
|
|
1883
|
+
* The chart's SQL is automatically executed and data is included in the response.
|
|
1884
|
+
*
|
|
1885
|
+
* @param id - Chart ID
|
|
1886
|
+
* @param options - Tenant, user, and scope options
|
|
1887
|
+
* @param signal - Optional AbortSignal for cancellation
|
|
1888
|
+
* @returns Chart with live data populated
|
|
1889
|
+
*
|
|
1890
|
+
* @example
|
|
1891
|
+
* ```typescript
|
|
1892
|
+
* const chart = await qp.getChart("chart_123", {
|
|
1893
|
+
* tenantId: "tenant_123",
|
|
1894
|
+
* });
|
|
1895
|
+
* console.log(chart.vega_lite_spec.data.values); // Live data
|
|
1896
|
+
* ```
|
|
1897
|
+
*/
|
|
1410
1898
|
async getChart(id, options, signal) {
|
|
1411
1899
|
return await getChart(
|
|
1412
1900
|
this.client,
|
|
@@ -1416,6 +1904,23 @@ var QueryPanelSdkAPI = class {
|
|
|
1416
1904
|
signal
|
|
1417
1905
|
);
|
|
1418
1906
|
}
|
|
1907
|
+
/**
|
|
1908
|
+
* Updates an existing chart's metadata or configuration.
|
|
1909
|
+
*
|
|
1910
|
+
* @param id - Chart ID to update
|
|
1911
|
+
* @param body - Fields to update (partial update supported)
|
|
1912
|
+
* @param options - Tenant, user, and scope options
|
|
1913
|
+
* @param signal - Optional AbortSignal for cancellation
|
|
1914
|
+
* @returns Updated chart
|
|
1915
|
+
*
|
|
1916
|
+
* @example
|
|
1917
|
+
* ```typescript
|
|
1918
|
+
* const updated = await qp.updateChart("chart_123", {
|
|
1919
|
+
* title: "Updated Chart Title",
|
|
1920
|
+
* description: "New description",
|
|
1921
|
+
* }, { tenantId: "tenant_123" });
|
|
1922
|
+
* ```
|
|
1923
|
+
*/
|
|
1419
1924
|
async updateChart(id, body, options, signal) {
|
|
1420
1925
|
return await updateChart(
|
|
1421
1926
|
this.client,
|
|
@@ -1425,10 +1930,42 @@ var QueryPanelSdkAPI = class {
|
|
|
1425
1930
|
signal
|
|
1426
1931
|
);
|
|
1427
1932
|
}
|
|
1933
|
+
/**
|
|
1934
|
+
* Deletes a chart permanently.
|
|
1935
|
+
*
|
|
1936
|
+
* @param id - Chart ID to delete
|
|
1937
|
+
* @param options - Tenant, user, and scope options
|
|
1938
|
+
* @param signal - Optional AbortSignal for cancellation
|
|
1939
|
+
*
|
|
1940
|
+
* @example
|
|
1941
|
+
* ```typescript
|
|
1942
|
+
* await qp.deleteChart("chart_123", { tenantId: "tenant_123" });
|
|
1943
|
+
* ```
|
|
1944
|
+
*/
|
|
1428
1945
|
async deleteChart(id, options, signal) {
|
|
1429
1946
|
await deleteChart(this.client, id, options, signal);
|
|
1430
1947
|
}
|
|
1431
|
-
// Active Chart CRUD operations
|
|
1948
|
+
// Active Chart CRUD operations (Dashboard)
|
|
1949
|
+
/**
|
|
1950
|
+
* Pins a saved chart to the dashboard (Active Charts).
|
|
1951
|
+
*
|
|
1952
|
+
* Active Charts are used for building dashboards. Unlike the chart history,
|
|
1953
|
+
* active charts are meant to be displayed together with layout metadata.
|
|
1954
|
+
*
|
|
1955
|
+
* @param body - Active chart config with chart_id, order, and optional meta
|
|
1956
|
+
* @param options - Tenant, user, and scope options
|
|
1957
|
+
* @param signal - Optional AbortSignal for cancellation
|
|
1958
|
+
* @returns Created active chart entry
|
|
1959
|
+
*
|
|
1960
|
+
* @example
|
|
1961
|
+
* ```typescript
|
|
1962
|
+
* const pinned = await qp.createActiveChart({
|
|
1963
|
+
* chart_id: savedChart.id,
|
|
1964
|
+
* order: 1,
|
|
1965
|
+
* meta: { width: "full", variant: "dark" },
|
|
1966
|
+
* }, { tenantId: "tenant_123" });
|
|
1967
|
+
* ```
|
|
1968
|
+
*/
|
|
1432
1969
|
async createActiveChart(body, options, signal) {
|
|
1433
1970
|
return await createActiveChart(
|
|
1434
1971
|
this.client,
|
|
@@ -1437,6 +1974,30 @@ var QueryPanelSdkAPI = class {
|
|
|
1437
1974
|
signal
|
|
1438
1975
|
);
|
|
1439
1976
|
}
|
|
1977
|
+
/**
|
|
1978
|
+
* Lists all active charts (dashboard items) with optional live data.
|
|
1979
|
+
*
|
|
1980
|
+
* Use `withData: true` to execute each chart's SQL and include results.
|
|
1981
|
+
* This is the primary method for loading a complete dashboard.
|
|
1982
|
+
*
|
|
1983
|
+
* @param options - Filtering and data options including withData
|
|
1984
|
+
* @param signal - Optional AbortSignal for cancellation
|
|
1985
|
+
* @returns Paginated list of active charts with optional live data
|
|
1986
|
+
*
|
|
1987
|
+
* @example
|
|
1988
|
+
* ```typescript
|
|
1989
|
+
* // Load dashboard with live data
|
|
1990
|
+
* const dashboard = await qp.listActiveCharts({
|
|
1991
|
+
* tenantId: "tenant_123",
|
|
1992
|
+
* withData: true,
|
|
1993
|
+
* });
|
|
1994
|
+
*
|
|
1995
|
+
* dashboard.data.forEach(item => {
|
|
1996
|
+
* console.log(item.chart?.title);
|
|
1997
|
+
* console.log(item.chart?.vega_lite_spec.data.values);
|
|
1998
|
+
* });
|
|
1999
|
+
* ```
|
|
2000
|
+
*/
|
|
1440
2001
|
async listActiveCharts(options, signal) {
|
|
1441
2002
|
return await listActiveCharts(
|
|
1442
2003
|
this.client,
|
|
@@ -1445,6 +2006,22 @@ var QueryPanelSdkAPI = class {
|
|
|
1445
2006
|
signal
|
|
1446
2007
|
);
|
|
1447
2008
|
}
|
|
2009
|
+
/**
|
|
2010
|
+
* Retrieves a single active chart by ID.
|
|
2011
|
+
*
|
|
2012
|
+
* @param id - Active chart ID
|
|
2013
|
+
* @param options - Options including withData for live data
|
|
2014
|
+
* @param signal - Optional AbortSignal for cancellation
|
|
2015
|
+
* @returns Active chart with associated chart data
|
|
2016
|
+
*
|
|
2017
|
+
* @example
|
|
2018
|
+
* ```typescript
|
|
2019
|
+
* const activeChart = await qp.getActiveChart("active_123", {
|
|
2020
|
+
* tenantId: "tenant_123",
|
|
2021
|
+
* withData: true,
|
|
2022
|
+
* });
|
|
2023
|
+
* ```
|
|
2024
|
+
*/
|
|
1448
2025
|
async getActiveChart(id, options, signal) {
|
|
1449
2026
|
return await getActiveChart(
|
|
1450
2027
|
this.client,
|
|
@@ -1454,6 +2031,25 @@ var QueryPanelSdkAPI = class {
|
|
|
1454
2031
|
signal
|
|
1455
2032
|
);
|
|
1456
2033
|
}
|
|
2034
|
+
/**
|
|
2035
|
+
* Updates an active chart's order or metadata.
|
|
2036
|
+
*
|
|
2037
|
+
* Use this to reorder dashboard items or update layout hints.
|
|
2038
|
+
*
|
|
2039
|
+
* @param id - Active chart ID to update
|
|
2040
|
+
* @param body - Fields to update (order, meta)
|
|
2041
|
+
* @param options - Tenant, user, and scope options
|
|
2042
|
+
* @param signal - Optional AbortSignal for cancellation
|
|
2043
|
+
* @returns Updated active chart
|
|
2044
|
+
*
|
|
2045
|
+
* @example
|
|
2046
|
+
* ```typescript
|
|
2047
|
+
* const updated = await qp.updateActiveChart("active_123", {
|
|
2048
|
+
* order: 5,
|
|
2049
|
+
* meta: { width: "half" },
|
|
2050
|
+
* }, { tenantId: "tenant_123" });
|
|
2051
|
+
* ```
|
|
2052
|
+
*/
|
|
1457
2053
|
async updateActiveChart(id, body, options, signal) {
|
|
1458
2054
|
return await updateActiveChart(
|
|
1459
2055
|
this.client,
|
|
@@ -1463,6 +2059,20 @@ var QueryPanelSdkAPI = class {
|
|
|
1463
2059
|
signal
|
|
1464
2060
|
);
|
|
1465
2061
|
}
|
|
2062
|
+
/**
|
|
2063
|
+
* Removes a chart from the dashboard (unpins it).
|
|
2064
|
+
*
|
|
2065
|
+
* This only removes the active chart entry, not the underlying saved chart.
|
|
2066
|
+
*
|
|
2067
|
+
* @param id - Active chart ID to delete
|
|
2068
|
+
* @param options - Tenant, user, and scope options
|
|
2069
|
+
* @param signal - Optional AbortSignal for cancellation
|
|
2070
|
+
*
|
|
2071
|
+
* @example
|
|
2072
|
+
* ```typescript
|
|
2073
|
+
* await qp.deleteActiveChart("active_123", { tenantId: "tenant_123" });
|
|
2074
|
+
* ```
|
|
2075
|
+
*/
|
|
1466
2076
|
async deleteActiveChart(id, options, signal) {
|
|
1467
2077
|
await deleteActiveChart(this.client, id, options, signal);
|
|
1468
2078
|
}
|
|
@@ -1470,7 +2080,9 @@ var QueryPanelSdkAPI = class {
|
|
|
1470
2080
|
export {
|
|
1471
2081
|
ClickHouseAdapter,
|
|
1472
2082
|
PostgresAdapter,
|
|
2083
|
+
QueryErrorCode,
|
|
1473
2084
|
QueryPanelSdkAPI,
|
|
2085
|
+
QueryPipelineError,
|
|
1474
2086
|
anonymizeResults
|
|
1475
2087
|
};
|
|
1476
2088
|
//# sourceMappingURL=index.js.map
|