@querypanel/node-sdk 1.3.0 → 1.3.3
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 +1 -1
- package/dist/index.cjs +230 -65
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +19 -13
- package/dist/index.d.ts +19 -13
- package/dist/index.js +230 -65
- package/dist/index.js.map +1 -1
- package/package.json +3 -2
package/dist/index.d.cts
CHANGED
|
@@ -731,12 +731,11 @@ interface AskOptions {
|
|
|
731
731
|
*/
|
|
732
732
|
querypanelSessionId?: string;
|
|
733
733
|
/**
|
|
734
|
-
*
|
|
735
|
-
*
|
|
736
|
-
*
|
|
737
|
-
* - `"v1"` (**deprecated**): legacy `POST /query` pipeline — prefer omitting or `"v2"`
|
|
734
|
+
* Query pipeline. Only `"v2"` is accepted (`POST /v2/query`).
|
|
735
|
+
* `"v1"` was removed: passing it fails typecheck and throws at runtime.
|
|
736
|
+
* Omit this field; asks always use v2.
|
|
738
737
|
*/
|
|
739
|
-
pipeline?: "
|
|
738
|
+
pipeline?: "v2";
|
|
740
739
|
/**
|
|
741
740
|
* Optional model id for SQL generation (e.g. OpenAI or `gemini-*` when the API uses Gemini). Omit for server default.
|
|
742
741
|
*/
|
|
@@ -750,7 +749,7 @@ interface AskOptions {
|
|
|
750
749
|
* Use this for tenant-specific policies (e.g. retention windows) that must
|
|
751
750
|
* be enforced on every query.
|
|
752
751
|
*
|
|
753
|
-
* Sent
|
|
752
|
+
* Sent on every ask (`POST /v2/query`).
|
|
754
753
|
*/
|
|
755
754
|
systemPrompt?: string;
|
|
756
755
|
/**
|
|
@@ -828,9 +827,10 @@ interface AskResponse {
|
|
|
828
827
|
trace?: PipelineTrace;
|
|
829
828
|
}
|
|
830
829
|
/**
|
|
831
|
-
* Resolve ask/modify pipeline.
|
|
830
|
+
* Resolve ask/modify pipeline. Always `"v2"` (`POST /v2/query`).
|
|
831
|
+
* Throws if a removed pipeline such as `"v1"` is passed.
|
|
832
832
|
*/
|
|
833
|
-
declare function resolveQueryPipeline(pipeline?: "
|
|
833
|
+
declare function resolveQueryPipeline(pipeline?: "v2"): "v2";
|
|
834
834
|
declare function anonymizeResults(rows: Array<Record<string, unknown>>): Array<Record<string, string>>;
|
|
835
835
|
|
|
836
836
|
/**
|
|
@@ -985,12 +985,18 @@ interface ChartModifyOptions {
|
|
|
985
985
|
/** Chart generation method: 'vega-lite' or 'vizspec' */
|
|
986
986
|
chartType?: "vega-lite" | "vizspec";
|
|
987
987
|
/**
|
|
988
|
-
*
|
|
989
|
-
*
|
|
990
|
-
*
|
|
991
|
-
* - `"v1"` (**deprecated**): legacy `POST /query` pipeline — prefer omitting or `"v2"`
|
|
988
|
+
* Query pipeline. Only `"v2"` is accepted (`POST /v2/query`).
|
|
989
|
+
* `"v1"` was removed: passing it fails typecheck and throws at runtime.
|
|
990
|
+
* Omit this field; chart modifications always use v2.
|
|
992
991
|
*/
|
|
993
|
-
pipeline?: "
|
|
992
|
+
pipeline?: "v2";
|
|
993
|
+
/**
|
|
994
|
+
* Fast path for structured controls (pinned charts): date-only changes
|
|
995
|
+
* substitute params or call `/v2/rewrite-datefilter` instead of `/v2/query`,
|
|
996
|
+
* and chart generation is skipped when the caller keeps its own encoding
|
|
997
|
+
* (`chart.vizSpec` / `vegaLiteSpec` are then omitted). Default: false.
|
|
998
|
+
*/
|
|
999
|
+
lightweight?: boolean;
|
|
994
1000
|
/**
|
|
995
1001
|
* QueryPanel session ID for context-aware follow-ups.
|
|
996
1002
|
* Pass the querypanelSessionId from a previous ask() response
|
package/dist/index.d.ts
CHANGED
|
@@ -731,12 +731,11 @@ interface AskOptions {
|
|
|
731
731
|
*/
|
|
732
732
|
querypanelSessionId?: string;
|
|
733
733
|
/**
|
|
734
|
-
*
|
|
735
|
-
*
|
|
736
|
-
*
|
|
737
|
-
* - `"v1"` (**deprecated**): legacy `POST /query` pipeline — prefer omitting or `"v2"`
|
|
734
|
+
* Query pipeline. Only `"v2"` is accepted (`POST /v2/query`).
|
|
735
|
+
* `"v1"` was removed: passing it fails typecheck and throws at runtime.
|
|
736
|
+
* Omit this field; asks always use v2.
|
|
738
737
|
*/
|
|
739
|
-
pipeline?: "
|
|
738
|
+
pipeline?: "v2";
|
|
740
739
|
/**
|
|
741
740
|
* Optional model id for SQL generation (e.g. OpenAI or `gemini-*` when the API uses Gemini). Omit for server default.
|
|
742
741
|
*/
|
|
@@ -750,7 +749,7 @@ interface AskOptions {
|
|
|
750
749
|
* Use this for tenant-specific policies (e.g. retention windows) that must
|
|
751
750
|
* be enforced on every query.
|
|
752
751
|
*
|
|
753
|
-
* Sent
|
|
752
|
+
* Sent on every ask (`POST /v2/query`).
|
|
754
753
|
*/
|
|
755
754
|
systemPrompt?: string;
|
|
756
755
|
/**
|
|
@@ -828,9 +827,10 @@ interface AskResponse {
|
|
|
828
827
|
trace?: PipelineTrace;
|
|
829
828
|
}
|
|
830
829
|
/**
|
|
831
|
-
* Resolve ask/modify pipeline.
|
|
830
|
+
* Resolve ask/modify pipeline. Always `"v2"` (`POST /v2/query`).
|
|
831
|
+
* Throws if a removed pipeline such as `"v1"` is passed.
|
|
832
832
|
*/
|
|
833
|
-
declare function resolveQueryPipeline(pipeline?: "
|
|
833
|
+
declare function resolveQueryPipeline(pipeline?: "v2"): "v2";
|
|
834
834
|
declare function anonymizeResults(rows: Array<Record<string, unknown>>): Array<Record<string, string>>;
|
|
835
835
|
|
|
836
836
|
/**
|
|
@@ -985,12 +985,18 @@ interface ChartModifyOptions {
|
|
|
985
985
|
/** Chart generation method: 'vega-lite' or 'vizspec' */
|
|
986
986
|
chartType?: "vega-lite" | "vizspec";
|
|
987
987
|
/**
|
|
988
|
-
*
|
|
989
|
-
*
|
|
990
|
-
*
|
|
991
|
-
* - `"v1"` (**deprecated**): legacy `POST /query` pipeline — prefer omitting or `"v2"`
|
|
988
|
+
* Query pipeline. Only `"v2"` is accepted (`POST /v2/query`).
|
|
989
|
+
* `"v1"` was removed: passing it fails typecheck and throws at runtime.
|
|
990
|
+
* Omit this field; chart modifications always use v2.
|
|
992
991
|
*/
|
|
993
|
-
pipeline?: "
|
|
992
|
+
pipeline?: "v2";
|
|
993
|
+
/**
|
|
994
|
+
* Fast path for structured controls (pinned charts): date-only changes
|
|
995
|
+
* substitute params or call `/v2/rewrite-datefilter` instead of `/v2/query`,
|
|
996
|
+
* and chart generation is skipped when the caller keeps its own encoding
|
|
997
|
+
* (`chart.vizSpec` / `vegaLiteSpec` are then omitted). Default: false.
|
|
998
|
+
*/
|
|
999
|
+
lightweight?: boolean;
|
|
994
1000
|
/**
|
|
995
1001
|
* QueryPanel session ID for context-aware follow-ups.
|
|
996
1002
|
* Pass the querypanelSessionId from a previous ask() response
|
package/dist/index.js
CHANGED
|
@@ -14,6 +14,13 @@ var BigQueryAdapter = class {
|
|
|
14
14
|
);
|
|
15
15
|
}
|
|
16
16
|
}
|
|
17
|
+
clientFn;
|
|
18
|
+
projectId;
|
|
19
|
+
datasetProjectId;
|
|
20
|
+
defaultDataset;
|
|
21
|
+
databaseName;
|
|
22
|
+
kind;
|
|
23
|
+
allowedTables;
|
|
17
24
|
async execute(sql, params) {
|
|
18
25
|
if (this.allowedTables) {
|
|
19
26
|
this.validateQueryTables(sql);
|
|
@@ -244,6 +251,11 @@ var ClickHouseAdapter = class {
|
|
|
244
251
|
this.allowedTables = normalizeTableFilter2(options.allowedTables);
|
|
245
252
|
}
|
|
246
253
|
}
|
|
254
|
+
clientFn;
|
|
255
|
+
databaseName;
|
|
256
|
+
defaultFormat;
|
|
257
|
+
kind;
|
|
258
|
+
allowedTables;
|
|
247
259
|
async execute(sql, params) {
|
|
248
260
|
if (this.allowedTables) {
|
|
249
261
|
this.validateQueryTables(sql);
|
|
@@ -497,6 +509,11 @@ var PostgresAdapter = class {
|
|
|
497
509
|
);
|
|
498
510
|
}
|
|
499
511
|
}
|
|
512
|
+
clientFn;
|
|
513
|
+
databaseName;
|
|
514
|
+
defaultSchema;
|
|
515
|
+
kind;
|
|
516
|
+
allowedTables;
|
|
500
517
|
async execute(sql, params) {
|
|
501
518
|
if (this.allowedTables) {
|
|
502
519
|
this.validateQueryTables(sql);
|
|
@@ -805,6 +822,9 @@ var QueryPipelineError = class extends Error {
|
|
|
805
822
|
this.status = status;
|
|
806
823
|
this.name = "QueryPipelineError";
|
|
807
824
|
}
|
|
825
|
+
code;
|
|
826
|
+
details;
|
|
827
|
+
status;
|
|
808
828
|
/**
|
|
809
829
|
* Check if this is a moderation error
|
|
810
830
|
*/
|
|
@@ -857,8 +877,14 @@ var QueryPipelineError = class extends Error {
|
|
|
857
877
|
|
|
858
878
|
// src/core/client.ts
|
|
859
879
|
var ApiClient = class {
|
|
880
|
+
baseUrl;
|
|
881
|
+
privateKey;
|
|
882
|
+
organizationId;
|
|
883
|
+
defaultTenantId;
|
|
884
|
+
additionalHeaders;
|
|
885
|
+
fetchImpl;
|
|
886
|
+
cryptoKey = null;
|
|
860
887
|
constructor(baseUrl, privateKey, organizationId, options) {
|
|
861
|
-
this.cryptoKey = null;
|
|
862
888
|
if (!baseUrl) {
|
|
863
889
|
throw new Error("Base URL is required");
|
|
864
890
|
}
|
|
@@ -1112,6 +1138,8 @@ var ApiClient = class {
|
|
|
1112
1138
|
|
|
1113
1139
|
// src/core/callback-client.ts
|
|
1114
1140
|
var CallbackApiClient = class {
|
|
1141
|
+
requestHandler;
|
|
1142
|
+
defaultTenantId;
|
|
1115
1143
|
constructor(requestHandler, options) {
|
|
1116
1144
|
this.requestHandler = requestHandler;
|
|
1117
1145
|
this.defaultTenantId = options?.defaultTenantId;
|
|
@@ -1254,10 +1282,9 @@ function normalizeSqlParamsInput(params) {
|
|
|
1254
1282
|
|
|
1255
1283
|
// src/core/query-engine.ts
|
|
1256
1284
|
var QueryEngine = class {
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
}
|
|
1285
|
+
databases = /* @__PURE__ */ new Map();
|
|
1286
|
+
databaseMetadata = /* @__PURE__ */ new Map();
|
|
1287
|
+
defaultDatabase;
|
|
1261
1288
|
attachDatabase(name, adapter, metadata) {
|
|
1262
1289
|
this.databases.set(name, adapter);
|
|
1263
1290
|
this.databaseMetadata.set(name, metadata);
|
|
@@ -1943,7 +1970,13 @@ import crypto4 from "crypto";
|
|
|
1943
1970
|
// src/routes/query.ts
|
|
1944
1971
|
import crypto3 from "crypto";
|
|
1945
1972
|
function resolveQueryPipeline(pipeline) {
|
|
1946
|
-
|
|
1973
|
+
const requested = pipeline;
|
|
1974
|
+
if (requested != null && requested !== "v2") {
|
|
1975
|
+
throw new Error(
|
|
1976
|
+
`Query pipeline "${requested}" was removed. ask() and modifyChart() always use POST /v2/query.`
|
|
1977
|
+
);
|
|
1978
|
+
}
|
|
1979
|
+
return "v2";
|
|
1947
1980
|
}
|
|
1948
1981
|
async function ask(client, queryEngine, question, options, signal) {
|
|
1949
1982
|
const tenantId = resolveTenantId5(client, options.tenantId);
|
|
@@ -1953,8 +1986,9 @@ async function ask(client, queryEngine, question, options, signal) {
|
|
|
1953
1986
|
let attempt = 0;
|
|
1954
1987
|
let lastError = options.lastError;
|
|
1955
1988
|
let previousSql = options.previousSql;
|
|
1956
|
-
|
|
1957
|
-
|
|
1989
|
+
let parentQueryId;
|
|
1990
|
+
resolveQueryPipeline(options.pipeline);
|
|
1991
|
+
const queryEndpoint = "/v2/query";
|
|
1958
1992
|
while (attempt <= maxRetry) {
|
|
1959
1993
|
const databaseName = options.database ?? queryEngine.getDefaultDatabase();
|
|
1960
1994
|
const metadata = databaseName ? queryEngine.getDatabaseMetadata(databaseName) : void 0;
|
|
@@ -1984,7 +2018,8 @@ async function ask(client, queryEngine, question, options, signal) {
|
|
|
1984
2018
|
...databaseName ? { database: databaseName } : {},
|
|
1985
2019
|
...metadata?.dialect ? { dialect: metadata.dialect } : {},
|
|
1986
2020
|
...options.model?.trim() ? { model: options.model.trim() } : {},
|
|
1987
|
-
...
|
|
2021
|
+
...options.systemPrompt?.trim() ? { system_prompt: options.systemPrompt.trim() } : {},
|
|
2022
|
+
...parentQueryId ? { parent_query_id: parentQueryId } : {},
|
|
1988
2023
|
debug: options.debug ?? false
|
|
1989
2024
|
},
|
|
1990
2025
|
tenantId,
|
|
@@ -2006,6 +2041,9 @@ async function ask(client, queryEngine, question, options, signal) {
|
|
|
2006
2041
|
if (!sql || !dialect) {
|
|
2007
2042
|
throw new Error("Query response missing required SQL or dialect");
|
|
2008
2043
|
}
|
|
2044
|
+
if (queryResponse.data.queryId) {
|
|
2045
|
+
parentQueryId = queryResponse.data.queryId;
|
|
2046
|
+
}
|
|
2009
2047
|
const dbName = queryResponse.data.database ?? options.database ?? queryEngine.getDefaultDatabase();
|
|
2010
2048
|
if (!dbName) {
|
|
2011
2049
|
throw new Error(
|
|
@@ -2165,35 +2203,35 @@ async function resolveQuery(client, queryEngine, queryId, options, signal) {
|
|
|
2165
2203
|
dialect: response.dialect
|
|
2166
2204
|
};
|
|
2167
2205
|
}
|
|
2206
|
+
async function markQueryExecuted(client, queryId, options, signal) {
|
|
2207
|
+
try {
|
|
2208
|
+
await client.post(
|
|
2209
|
+
`/v2/queries/${encodeURIComponent(queryId)}/executed`,
|
|
2210
|
+
{},
|
|
2211
|
+
options.tenantId,
|
|
2212
|
+
options.userId,
|
|
2213
|
+
options.scopes,
|
|
2214
|
+
signal
|
|
2215
|
+
);
|
|
2216
|
+
} catch {
|
|
2217
|
+
}
|
|
2218
|
+
}
|
|
2168
2219
|
|
|
2169
2220
|
// src/routes/modify.ts
|
|
2170
|
-
function buildModifiedQuestion(originalQuestion, modifications,
|
|
2221
|
+
function buildModifiedQuestion(originalQuestion, modifications, hasSessionContext = false) {
|
|
2171
2222
|
const hints = [];
|
|
2172
2223
|
if (modifications.timeGranularity) {
|
|
2173
2224
|
hints.push(`group results by ${modifications.timeGranularity}`);
|
|
2174
2225
|
}
|
|
2175
2226
|
if (modifications.dateRange) {
|
|
2176
|
-
|
|
2177
|
-
|
|
2178
|
-
|
|
2179
|
-
|
|
2180
|
-
|
|
2181
|
-
|
|
2182
|
-
|
|
2183
|
-
|
|
2184
|
-
hints.push(`change end date to ${to}`);
|
|
2185
|
-
}
|
|
2186
|
-
} else {
|
|
2187
|
-
const parts = [];
|
|
2188
|
-
if (modifications.dateRange.from) {
|
|
2189
|
-
parts.push(`from ${modifications.dateRange.from}`);
|
|
2190
|
-
}
|
|
2191
|
-
if (modifications.dateRange.to) {
|
|
2192
|
-
parts.push(`to ${modifications.dateRange.to}`);
|
|
2193
|
-
}
|
|
2194
|
-
if (parts.length > 0) {
|
|
2195
|
-
hints.push(`filter date range ${parts.join(" ")}`);
|
|
2196
|
-
}
|
|
2227
|
+
const from = normalizeDateInput(modifications.dateRange.from);
|
|
2228
|
+
const to = normalizeDateInput(modifications.dateRange.to);
|
|
2229
|
+
if (from && to) {
|
|
2230
|
+
hints.push(`change date range to ${from} through ${to}`);
|
|
2231
|
+
} else if (from) {
|
|
2232
|
+
hints.push(`change start date to ${from}`);
|
|
2233
|
+
} else if (to) {
|
|
2234
|
+
hints.push(`change end date to ${to}`);
|
|
2197
2235
|
}
|
|
2198
2236
|
}
|
|
2199
2237
|
if (modifications.additionalInstructions) {
|
|
@@ -2202,11 +2240,12 @@ function buildModifiedQuestion(originalQuestion, modifications, pipeline, hasSes
|
|
|
2202
2240
|
if (hints.length === 0) {
|
|
2203
2241
|
return originalQuestion;
|
|
2204
2242
|
}
|
|
2205
|
-
if (
|
|
2243
|
+
if (hasSessionContext) {
|
|
2206
2244
|
return hints.join(", ");
|
|
2207
2245
|
}
|
|
2208
2246
|
return `${originalQuestion} (${hints.join(", ")})`;
|
|
2209
2247
|
}
|
|
2248
|
+
var REWRITE_DATEFILTER_ENDPOINT = "/v2/rewrite-datefilter";
|
|
2210
2249
|
var START_PARAM_KEY_REGEX = /(^|_)(start|from)(_|$)/i;
|
|
2211
2250
|
var END_PARAM_KEY_REGEX = /(^|_)(end|to)(_|$)/i;
|
|
2212
2251
|
var ISO_DATETIME_RE = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(\.\d+)?Z?$/;
|
|
@@ -2243,31 +2282,118 @@ function normalizeGeneratedParamKey(param, index) {
|
|
|
2243
2282
|
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);
|
|
2244
2283
|
return nameCandidate.replace(/[{}]/g, "").replace(/(.+):.*$/, "$1").replace(/^[:$]/, "").trim();
|
|
2245
2284
|
}
|
|
2246
|
-
function
|
|
2285
|
+
function collectParamKeys(params, paramMetadata) {
|
|
2286
|
+
const keys = new Set(Object.keys(params));
|
|
2287
|
+
for (let i = 0; i < paramMetadata.length; i++) {
|
|
2288
|
+
const param = paramMetadata[i];
|
|
2289
|
+
if (!param) continue;
|
|
2290
|
+
keys.add(normalizeGeneratedParamKey(param, i));
|
|
2291
|
+
}
|
|
2292
|
+
return [...keys];
|
|
2293
|
+
}
|
|
2294
|
+
function placeholderKey(token) {
|
|
2295
|
+
if (token.startsWith("$") || token.startsWith("@")) return token.slice(1);
|
|
2296
|
+
return token.slice(1, -1).replace(/:.+$/, "").trim();
|
|
2297
|
+
}
|
|
2298
|
+
function isDateLike(value) {
|
|
2299
|
+
return typeof value === "string" && /^\d{4}-\d{2}-\d{2}/.test(value.trim());
|
|
2300
|
+
}
|
|
2301
|
+
function dateBoundKeysFromSql(sql) {
|
|
2302
|
+
const start = /* @__PURE__ */ new Set();
|
|
2303
|
+
const end = /* @__PURE__ */ new Set();
|
|
2304
|
+
const comparison = /(>=|<=|>|<)\s*(\$\d+|\{[^}]+\}|@[A-Za-z_][\w]*)/g;
|
|
2305
|
+
const between = /BETWEEN\s+(\$\d+|\{[^}]+\}|@[A-Za-z_][\w]*)\s+AND\s+(\$\d+|\{[^}]+\}|@[A-Za-z_][\w]*)/gi;
|
|
2306
|
+
for (const match of sql.matchAll(comparison)) {
|
|
2307
|
+
const key = placeholderKey(match[2] ?? "");
|
|
2308
|
+
if (!key) continue;
|
|
2309
|
+
if (match[1] === ">=" || match[1] === ">") start.add(key);
|
|
2310
|
+
else end.add(key);
|
|
2311
|
+
}
|
|
2312
|
+
for (const match of sql.matchAll(between)) {
|
|
2313
|
+
const startKey = placeholderKey(match[1] ?? "");
|
|
2314
|
+
const endKey = placeholderKey(match[2] ?? "");
|
|
2315
|
+
if (startKey) start.add(startKey);
|
|
2316
|
+
if (endKey) end.add(endKey);
|
|
2317
|
+
}
|
|
2318
|
+
return { start, end };
|
|
2319
|
+
}
|
|
2320
|
+
function paramValueForKey(key, params, paramMetadata) {
|
|
2321
|
+
if (key in params) return params[key];
|
|
2322
|
+
for (let i = 0; i < paramMetadata.length; i++) {
|
|
2323
|
+
const param = paramMetadata[i];
|
|
2324
|
+
if (!param) continue;
|
|
2325
|
+
if (normalizeGeneratedParamKey(param, i) === key) return param.value;
|
|
2326
|
+
}
|
|
2327
|
+
return void 0;
|
|
2328
|
+
}
|
|
2329
|
+
function dateBoundKeys(sql, params, paramMetadata, boundary) {
|
|
2330
|
+
const fromSql = dateBoundKeysFromSql(sql)[boundary];
|
|
2331
|
+
const keys = [];
|
|
2332
|
+
for (const key of collectParamKeys(params, paramMetadata)) {
|
|
2333
|
+
const named = keyLooksLikeDateBoundary(key, boundary);
|
|
2334
|
+
if (!named && !fromSql.has(key)) continue;
|
|
2335
|
+
if (!isDateLike(paramValueForKey(key, params, paramMetadata))) continue;
|
|
2336
|
+
keys.push(key);
|
|
2337
|
+
}
|
|
2338
|
+
return keys;
|
|
2339
|
+
}
|
|
2340
|
+
function canSubstituteDateRangeParams(dateRange, sql, params, paramMetadata = []) {
|
|
2341
|
+
if (!dateRange) return false;
|
|
2342
|
+
const from = normalizeDateInput(dateRange.from);
|
|
2343
|
+
const to = normalizeDateInput(dateRange.to);
|
|
2344
|
+
if (!from && !to) return false;
|
|
2345
|
+
if (from && dateBoundKeys(sql, params, paramMetadata, "start").length === 0) {
|
|
2346
|
+
return false;
|
|
2347
|
+
}
|
|
2348
|
+
if (to && dateBoundKeys(sql, params, paramMetadata, "end").length === 0) {
|
|
2349
|
+
return false;
|
|
2350
|
+
}
|
|
2351
|
+
return true;
|
|
2352
|
+
}
|
|
2353
|
+
function isDateRangeOnlySqlModification(modifications) {
|
|
2354
|
+
if (!modifications?.dateRange) return false;
|
|
2355
|
+
if (modifications.customSql) return false;
|
|
2356
|
+
if (modifications.timeGranularity) return false;
|
|
2357
|
+
if (typeof modifications.additionalInstructions === "string" && modifications.additionalInstructions.trim().length > 0) {
|
|
2358
|
+
return false;
|
|
2359
|
+
}
|
|
2360
|
+
return true;
|
|
2361
|
+
}
|
|
2362
|
+
function applyDateRangeOverrides(dateRange, sql, params, paramMetadata) {
|
|
2247
2363
|
if (!dateRange) return;
|
|
2248
2364
|
const from = normalizeDateInput(dateRange.from);
|
|
2249
2365
|
const to = normalizeDateInput(dateRange.to);
|
|
2250
2366
|
if (!from && !to) return;
|
|
2367
|
+
const startKeys = new Set(
|
|
2368
|
+
from ? dateBoundKeys(sql, params, paramMetadata, "start") : []
|
|
2369
|
+
);
|
|
2370
|
+
const endKeys = new Set(
|
|
2371
|
+
to ? dateBoundKeys(sql, params, paramMetadata, "end") : []
|
|
2372
|
+
);
|
|
2251
2373
|
for (const [key, value] of Object.entries(params)) {
|
|
2252
|
-
if (
|
|
2253
|
-
params[key] = formatDateOverride(from, "start", value);
|
|
2374
|
+
if (startKeys.has(key)) {
|
|
2375
|
+
params[key] = formatDateOverride(from ?? "", "start", value);
|
|
2254
2376
|
}
|
|
2255
|
-
if (
|
|
2256
|
-
params[key] = formatDateOverride(to, "end", value);
|
|
2377
|
+
if (endKeys.has(key)) {
|
|
2378
|
+
params[key] = formatDateOverride(to ?? "", "end", value);
|
|
2257
2379
|
}
|
|
2258
2380
|
}
|
|
2259
2381
|
for (let i = 0; i < paramMetadata.length; i++) {
|
|
2260
2382
|
const param = paramMetadata[i];
|
|
2261
2383
|
if (!param) continue;
|
|
2262
2384
|
const key = normalizeGeneratedParamKey(param, i);
|
|
2263
|
-
if (
|
|
2264
|
-
param.value = formatDateOverride(from, "start", param.value);
|
|
2385
|
+
if (startKeys.has(key)) {
|
|
2386
|
+
param.value = formatDateOverride(from ?? "", "start", param.value);
|
|
2265
2387
|
}
|
|
2266
|
-
if (
|
|
2267
|
-
param.value = formatDateOverride(to, "end", param.value);
|
|
2388
|
+
if (endKeys.has(key)) {
|
|
2389
|
+
param.value = formatDateOverride(to ?? "", "end", param.value);
|
|
2268
2390
|
}
|
|
2269
2391
|
}
|
|
2270
2392
|
}
|
|
2393
|
+
function buildPreviousParams(params, paramMetadata) {
|
|
2394
|
+
if (paramMetadata.length > 0) return paramMetadata;
|
|
2395
|
+
return Object.entries(params).map(([name, value]) => ({ name, value }));
|
|
2396
|
+
}
|
|
2271
2397
|
function buildVizHints(modifications) {
|
|
2272
2398
|
const hints = {};
|
|
2273
2399
|
if (modifications.kind) {
|
|
@@ -2318,8 +2444,11 @@ async function modifyChart(client, queryEngine, input, options, signal) {
|
|
|
2318
2444
|
const hasSqlMods = !!input.sqlModifications;
|
|
2319
2445
|
const hasVizMods = !!input.vizModifications;
|
|
2320
2446
|
const hasCustomSql = !!input.sqlModifications?.customSql;
|
|
2321
|
-
const
|
|
2322
|
-
const
|
|
2447
|
+
const lightweight = options?.lightweight === true;
|
|
2448
|
+
const dateOnly = lightweight && isDateRangeOnlySqlModification(input.sqlModifications);
|
|
2449
|
+
const skipChartLlm = lightweight && !hasCustomSql && (dateOnly || hasVizMods && !hasSqlMods);
|
|
2450
|
+
resolveQueryPipeline(options?.pipeline);
|
|
2451
|
+
const queryEndpoint = "/v2/query";
|
|
2323
2452
|
let currentSql = input.sql;
|
|
2324
2453
|
let finalParams = input.params ?? {};
|
|
2325
2454
|
let paramMetadata = [];
|
|
@@ -2371,28 +2500,57 @@ async function modifyChart(client, queryEngine, input, options, signal) {
|
|
|
2371
2500
|
paramMetadata = [];
|
|
2372
2501
|
queryId = void 0;
|
|
2373
2502
|
sqlChanged = true;
|
|
2503
|
+
} else if (dateOnly && canSubstituteDateRangeParams(
|
|
2504
|
+
input.sqlModifications?.dateRange,
|
|
2505
|
+
finalSql,
|
|
2506
|
+
finalParams,
|
|
2507
|
+
paramMetadata
|
|
2508
|
+
)) {
|
|
2509
|
+
finalParams = { ...finalParams };
|
|
2510
|
+
applyDateRangeOverrides(
|
|
2511
|
+
input.sqlModifications?.dateRange,
|
|
2512
|
+
finalSql,
|
|
2513
|
+
finalParams,
|
|
2514
|
+
paramMetadata
|
|
2515
|
+
);
|
|
2516
|
+
sqlChanged = false;
|
|
2374
2517
|
} else if (hasSqlMods && !hasCustomSql) {
|
|
2375
2518
|
const modifiedQuestion = buildModifiedQuestion(
|
|
2376
2519
|
input.question,
|
|
2377
2520
|
input.sqlModifications,
|
|
2378
|
-
pipeline,
|
|
2379
2521
|
hasQuerypanelSessionContext
|
|
2380
2522
|
);
|
|
2381
|
-
|
|
2382
|
-
|
|
2383
|
-
|
|
2384
|
-
|
|
2523
|
+
finalQuestion = modifiedQuestion;
|
|
2524
|
+
const sharedBody = {
|
|
2525
|
+
session_id: querypanelSessionId,
|
|
2526
|
+
previous_sql: currentSql,
|
|
2527
|
+
...tenantSettings ? { tenant_settings: tenantSettings } : {},
|
|
2528
|
+
...databaseName ? { database: databaseName } : {},
|
|
2529
|
+
...metadata?.dialect ? { dialect: metadata.dialect } : {},
|
|
2530
|
+
debug: options?.debug ?? false
|
|
2531
|
+
};
|
|
2532
|
+
const [endpoint, body] = dateOnly ? [
|
|
2533
|
+
REWRITE_DATEFILTER_ENDPOINT,
|
|
2534
|
+
{
|
|
2535
|
+
...sharedBody,
|
|
2536
|
+
question: input.question,
|
|
2537
|
+
previous_params: buildPreviousParams(finalParams, paramMetadata),
|
|
2538
|
+
date_range: {
|
|
2539
|
+
from: normalizeDateInput(input.sqlModifications?.dateRange?.from),
|
|
2540
|
+
to: normalizeDateInput(input.sqlModifications?.dateRange?.to)
|
|
2541
|
+
}
|
|
2542
|
+
}
|
|
2543
|
+
] : [
|
|
2385
2544
|
queryEndpoint,
|
|
2386
2545
|
{
|
|
2546
|
+
...sharedBody,
|
|
2387
2547
|
question: modifiedQuestion,
|
|
2388
|
-
|
|
2389
|
-
|
|
2390
|
-
|
|
2391
|
-
|
|
2392
|
-
|
|
2393
|
-
|
|
2394
|
-
debug: options?.debug ?? false
|
|
2395
|
-
},
|
|
2548
|
+
...options?.maxRetry ? { max_retry: options.maxRetry } : {}
|
|
2549
|
+
}
|
|
2550
|
+
];
|
|
2551
|
+
const queryResponse = await client.post(
|
|
2552
|
+
endpoint,
|
|
2553
|
+
body,
|
|
2396
2554
|
tenantId,
|
|
2397
2555
|
options?.userId,
|
|
2398
2556
|
options?.scopes,
|
|
@@ -2402,13 +2560,12 @@ async function modifyChart(client, queryEngine, input, options, signal) {
|
|
|
2402
2560
|
finalSql = queryResponse.sql;
|
|
2403
2561
|
paramMetadata = Array.isArray(queryResponse.params) ? queryResponse.params : [];
|
|
2404
2562
|
finalParams = queryEngine.mapGeneratedParams(paramMetadata);
|
|
2405
|
-
|
|
2406
|
-
|
|
2407
|
-
|
|
2408
|
-
|
|
2409
|
-
|
|
2410
|
-
|
|
2411
|
-
}
|
|
2563
|
+
applyDateRangeOverrides(
|
|
2564
|
+
input.sqlModifications?.dateRange,
|
|
2565
|
+
finalSql,
|
|
2566
|
+
finalParams,
|
|
2567
|
+
paramMetadata
|
|
2568
|
+
);
|
|
2412
2569
|
rationale = queryResponse.rationale;
|
|
2413
2570
|
queryId = queryResponse.queryId;
|
|
2414
2571
|
sqlChanged = finalSql !== currentSql;
|
|
@@ -2426,7 +2583,7 @@ async function modifyChart(client, queryEngine, input, options, signal) {
|
|
|
2426
2583
|
};
|
|
2427
2584
|
const vizHints = hasVizMods ? buildVizHints(input.vizModifications) : {};
|
|
2428
2585
|
const vizHintsForChart = chartType === "vizspec" ? vizHints : stripVizSpecOnlyHints(vizHints);
|
|
2429
|
-
if (chartType === "vizspec") {
|
|
2586
|
+
if (!skipChartLlm && chartType === "vizspec") {
|
|
2430
2587
|
const vizspecResponse = await client.post(
|
|
2431
2588
|
"/vizspec",
|
|
2432
2589
|
{
|
|
@@ -2453,7 +2610,7 @@ async function modifyChart(client, queryEngine, input, options, signal) {
|
|
|
2453
2610
|
specType: "vizspec",
|
|
2454
2611
|
notes: rows.length === 0 ? vizspecResponse.notes ?? "Query returned no rows." : vizspecResponse.notes
|
|
2455
2612
|
};
|
|
2456
|
-
} else {
|
|
2613
|
+
} else if (!skipChartLlm) {
|
|
2457
2614
|
const chartResponse = await client.post(
|
|
2458
2615
|
"/chart",
|
|
2459
2616
|
{
|
|
@@ -2626,6 +2783,9 @@ function resolveAttachEnforceTenantIsolation(tenantFieldName, enforceTenantIsola
|
|
|
2626
2783
|
return tenantFieldName ? true : void 0;
|
|
2627
2784
|
}
|
|
2628
2785
|
var QueryPanelSdkAPI = class _QueryPanelSdkAPI {
|
|
2786
|
+
client;
|
|
2787
|
+
queryEngine;
|
|
2788
|
+
supportedChartTypes;
|
|
2629
2789
|
/**
|
|
2630
2790
|
* @param workspaceId - Workspace UUID (same value as your org in the dashboard). Passed to the API as JWT claim `organizationId`.
|
|
2631
2791
|
*/
|
|
@@ -2877,6 +3037,11 @@ var QueryPanelSdkAPI = class _QueryPanelSdkAPI {
|
|
|
2877
3037
|
databaseName,
|
|
2878
3038
|
tenantId
|
|
2879
3039
|
);
|
|
3040
|
+
if (input.queryId) {
|
|
3041
|
+
await markQueryExecuted(this.client, input.queryId, {
|
|
3042
|
+
tenantId
|
|
3043
|
+
});
|
|
3044
|
+
}
|
|
2880
3045
|
return { rows: result.rows, fields: result.fields };
|
|
2881
3046
|
}
|
|
2882
3047
|
// VizSpec generation
|