@thyn-ai/sqai-ai-sdk 0.1.0
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/LICENSE +202 -0
- package/dist/index.cjs +846 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +837 -0
- package/dist/index.d.ts +837 -0
- package/dist/index.js +794 -0
- package/dist/index.js.map +1 -0
- package/package.json +62 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,794 @@
|
|
|
1
|
+
// src/toolkit.ts
|
|
2
|
+
import { SQAI, SqaiError as SqaiError2 } from "@thyn-ai/sqai";
|
|
3
|
+
|
|
4
|
+
// src/tools.ts
|
|
5
|
+
import { tool } from "ai";
|
|
6
|
+
import {
|
|
7
|
+
ContractIndex,
|
|
8
|
+
SqaiError,
|
|
9
|
+
currentPlatformKey,
|
|
10
|
+
invocationHash,
|
|
11
|
+
isReadOnlyEligible,
|
|
12
|
+
lookupCapability
|
|
13
|
+
} from "@thyn-ai/sqai";
|
|
14
|
+
|
|
15
|
+
// src/schemas.ts
|
|
16
|
+
import { z } from "zod";
|
|
17
|
+
var filterConditionSchema = z.object({
|
|
18
|
+
column: z.string().optional().describe("Exact column name to filter on."),
|
|
19
|
+
op: z.enum(["eq", "in", "gt", "gte", "lt", "lte", "is_null", "is_not_null"]),
|
|
20
|
+
value: z.union([z.string(), z.number(), z.boolean()]).optional(),
|
|
21
|
+
values: z.array(z.union([z.string(), z.number(), z.boolean()])).optional().describe("Values for the 'in' operator.")
|
|
22
|
+
});
|
|
23
|
+
var filterSpecSchema = z.object({
|
|
24
|
+
time_filter: z.string().optional().describe("Named time window, e.g. 'last_30_days'."),
|
|
25
|
+
conditions: z.array(filterConditionSchema).optional()
|
|
26
|
+
});
|
|
27
|
+
var querySpecSchema = z.object({
|
|
28
|
+
metric: z.string().describe("The measure to compute, e.g. 'revenue'. Use exact field names from listSources."),
|
|
29
|
+
aggregation: z.enum(["sum", "avg", "count", "min", "max"]).optional(),
|
|
30
|
+
group_by: z.string().optional().describe("Column to group results by."),
|
|
31
|
+
filter: filterSpecSchema.optional(),
|
|
32
|
+
limit: z.number().int().positive().optional().describe("Maximum rows to return."),
|
|
33
|
+
order: z.enum(["asc", "desc"]).optional(),
|
|
34
|
+
source: z.string().optional().describe("Source name to query (maps to source_name). Omit to let the planner pick.")
|
|
35
|
+
});
|
|
36
|
+
var taggedScalarSchema = z.object({
|
|
37
|
+
type: z.enum(["decimal", "bigint"]),
|
|
38
|
+
value: z.string()
|
|
39
|
+
});
|
|
40
|
+
var algentaWireValueSchema = z.lazy(
|
|
41
|
+
() => z.union([
|
|
42
|
+
z.null(),
|
|
43
|
+
z.boolean(),
|
|
44
|
+
z.number(),
|
|
45
|
+
z.string(),
|
|
46
|
+
z.array(algentaWireValueSchema),
|
|
47
|
+
taggedScalarSchema,
|
|
48
|
+
z.record(z.string(), algentaWireValueSchema)
|
|
49
|
+
])
|
|
50
|
+
);
|
|
51
|
+
var singleBindingSchema = z.object({
|
|
52
|
+
parameter: z.union([z.string(), z.number()]).describe("Target parameter, by name or positional index."),
|
|
53
|
+
source: z.string().describe("Connected source name to pull the column from."),
|
|
54
|
+
field: z.string().describe("Column to extract."),
|
|
55
|
+
filter: filterSpecSchema.optional()
|
|
56
|
+
});
|
|
57
|
+
var alignedBindingSchema = z.object({
|
|
58
|
+
parameters: z.array(z.union([z.string(), z.number()])).describe("Target parameters, one per field, row-aligned."),
|
|
59
|
+
source: z.string(),
|
|
60
|
+
fields: z.array(z.string()).describe("Columns to extract, one per parameter."),
|
|
61
|
+
filter: filterSpecSchema.optional(),
|
|
62
|
+
alignment: z.literal("rowwise"),
|
|
63
|
+
nullPolicy: z.enum(["pairwise", "preserve"]).optional()
|
|
64
|
+
});
|
|
65
|
+
var computationBindingSchema = z.union([singleBindingSchema, alignedBindingSchema]);
|
|
66
|
+
var computationSpecSchema = z.object({
|
|
67
|
+
module: z.string().describe("Capability module, e.g. 'stats'. Discover via listSources."),
|
|
68
|
+
function: z.string().describe("Function inside the module, e.g. 'median'."),
|
|
69
|
+
args: z.array(algentaWireValueSchema).default([]).describe("Positional arguments."),
|
|
70
|
+
kwargs: z.record(z.string(), algentaWireValueSchema).optional().describe("Named arguments."),
|
|
71
|
+
bindings: z.array(computationBindingSchema).optional().describe("Pull columns from connected sources instead of pasting data inline."),
|
|
72
|
+
seed: z.union([z.number(), z.string()]).optional().describe("Required for simulation modules; makes runs replayable")
|
|
73
|
+
});
|
|
74
|
+
var queryDataInputSchema = z.discriminatedUnion("kind", [
|
|
75
|
+
z.object({
|
|
76
|
+
version: z.literal("1"),
|
|
77
|
+
kind: z.literal("query"),
|
|
78
|
+
spec: querySpecSchema
|
|
79
|
+
}),
|
|
80
|
+
z.object({
|
|
81
|
+
version: z.literal("1"),
|
|
82
|
+
kind: z.literal("computation"),
|
|
83
|
+
spec: computationSpecSchema
|
|
84
|
+
})
|
|
85
|
+
]);
|
|
86
|
+
var explainQueryInputSchema = queryDataInputSchema;
|
|
87
|
+
var listSourcesInputSchema = z.object({
|
|
88
|
+
capabilitySearch: z.string().optional().describe("Search the computation capability catalog; returns matching modules."),
|
|
89
|
+
module: z.string().optional().describe("List a module's functions with exact signatures.")
|
|
90
|
+
});
|
|
91
|
+
var cellSchema = z.union([z.number(), z.string(), z.boolean(), z.null()]);
|
|
92
|
+
var candidateSchema = z.looseObject({
|
|
93
|
+
source: z.string().optional(),
|
|
94
|
+
column: z.string().optional(),
|
|
95
|
+
role: z.string().optional(),
|
|
96
|
+
confidence: z.number().optional()
|
|
97
|
+
});
|
|
98
|
+
var errorOutputSchema = z.object({
|
|
99
|
+
status: z.literal("error"),
|
|
100
|
+
code: z.string(),
|
|
101
|
+
message: z.string(),
|
|
102
|
+
retryable: z.boolean(),
|
|
103
|
+
request_id: z.string().nullable(),
|
|
104
|
+
nearest_matches: z.array(z.string()).optional()
|
|
105
|
+
});
|
|
106
|
+
var clarificationOutputSchema = z.object({
|
|
107
|
+
status: z.literal("needs_clarification"),
|
|
108
|
+
question: z.string(),
|
|
109
|
+
candidates: z.array(candidateSchema),
|
|
110
|
+
explanation: z.array(z.string())
|
|
111
|
+
});
|
|
112
|
+
var rejectedOutputSchema = z.object({
|
|
113
|
+
status: z.literal("rejected"),
|
|
114
|
+
rejection_reason: z.string(),
|
|
115
|
+
candidates: z.array(candidateSchema),
|
|
116
|
+
explanation: z.array(z.string())
|
|
117
|
+
});
|
|
118
|
+
var queryOkOutputSchema = z.object({
|
|
119
|
+
status: z.literal("ok"),
|
|
120
|
+
kind: z.literal("query"),
|
|
121
|
+
data: z.object({
|
|
122
|
+
columns: z.array(z.string()),
|
|
123
|
+
rows: z.array(z.array(cellSchema))
|
|
124
|
+
}),
|
|
125
|
+
total_rows: z.number(),
|
|
126
|
+
returned_rows: z.number(),
|
|
127
|
+
truncated: z.boolean(),
|
|
128
|
+
result_id: z.string().optional(),
|
|
129
|
+
plan_hash: z.string().nullable(),
|
|
130
|
+
schema_revision: z.string().nullable(),
|
|
131
|
+
source_name: z.string().nullable(),
|
|
132
|
+
intent_signature: z.string().nullable(),
|
|
133
|
+
deterministic_scope: z.string().nullable(),
|
|
134
|
+
decision_path: z.string().nullable(),
|
|
135
|
+
validated: z.boolean().nullable(),
|
|
136
|
+
request_id: z.string().nullable(),
|
|
137
|
+
explanation: z.array(z.string())
|
|
138
|
+
});
|
|
139
|
+
var determinismEnvelopeSchema = z.object({
|
|
140
|
+
runtime_bundle_version: z.string(),
|
|
141
|
+
runtime_bundle_sha256: z.string(),
|
|
142
|
+
platform: z.string(),
|
|
143
|
+
architecture: z.string(),
|
|
144
|
+
kernel_build: z.string(),
|
|
145
|
+
precision_mode: z.string(),
|
|
146
|
+
thread_count: z.number(),
|
|
147
|
+
seed: z.union([z.number(), z.string()]).optional(),
|
|
148
|
+
input_hash: z.string()
|
|
149
|
+
});
|
|
150
|
+
var bindingProvenanceSchema = z.object({
|
|
151
|
+
source_name: z.string(),
|
|
152
|
+
fields: z.array(z.string()),
|
|
153
|
+
schema_revision: z.string().nullable(),
|
|
154
|
+
row_count: z.number(),
|
|
155
|
+
input_hash: z.string()
|
|
156
|
+
});
|
|
157
|
+
var computationOkOutputSchema = z.object({
|
|
158
|
+
status: z.literal("ok"),
|
|
159
|
+
kind: z.literal("computation"),
|
|
160
|
+
value: algentaWireValueSchema,
|
|
161
|
+
value_type: z.string(),
|
|
162
|
+
preview: algentaWireValueSchema.optional(),
|
|
163
|
+
element_count: z.number().optional(),
|
|
164
|
+
truncated: z.boolean(),
|
|
165
|
+
result_id: z.string().optional(),
|
|
166
|
+
invocation_hash: z.string(),
|
|
167
|
+
computation_hash: z.string(),
|
|
168
|
+
contract_hash: z.string(),
|
|
169
|
+
determinism: determinismEnvelopeSchema,
|
|
170
|
+
provenance: z.object({ bindings: z.array(bindingProvenanceSchema) }).optional(),
|
|
171
|
+
latency_ms: z.number(),
|
|
172
|
+
request_id: z.string()
|
|
173
|
+
});
|
|
174
|
+
var queryDataOutputSchema = z.union([
|
|
175
|
+
queryOkOutputSchema,
|
|
176
|
+
computationOkOutputSchema,
|
|
177
|
+
clarificationOutputSchema,
|
|
178
|
+
rejectedOutputSchema,
|
|
179
|
+
errorOutputSchema
|
|
180
|
+
]);
|
|
181
|
+
var sourceListOutputSchema = z.object({
|
|
182
|
+
sources: z.array(
|
|
183
|
+
z.object({
|
|
184
|
+
name: z.string(),
|
|
185
|
+
fields: z.array(
|
|
186
|
+
z.object({
|
|
187
|
+
name: z.string(),
|
|
188
|
+
type: z.string(),
|
|
189
|
+
ops: z.array(z.string())
|
|
190
|
+
})
|
|
191
|
+
),
|
|
192
|
+
row_count: z.number().nullable(),
|
|
193
|
+
schema_revision: z.string().nullable()
|
|
194
|
+
})
|
|
195
|
+
)
|
|
196
|
+
});
|
|
197
|
+
var moduleListOutputSchema = z.object({
|
|
198
|
+
modules: z.array(
|
|
199
|
+
z.object({
|
|
200
|
+
name: z.string(),
|
|
201
|
+
category: z.string(),
|
|
202
|
+
deterministic: z.boolean(),
|
|
203
|
+
function_count: z.number(),
|
|
204
|
+
sample_functions: z.array(z.string())
|
|
205
|
+
})
|
|
206
|
+
)
|
|
207
|
+
});
|
|
208
|
+
var functionListOutputSchema = z.object({
|
|
209
|
+
functions: z.array(
|
|
210
|
+
z.object({
|
|
211
|
+
name: z.string(),
|
|
212
|
+
signature: z.string(),
|
|
213
|
+
params: z.array(
|
|
214
|
+
z.object({
|
|
215
|
+
name: z.string(),
|
|
216
|
+
type: z.string(),
|
|
217
|
+
required: z.boolean()
|
|
218
|
+
})
|
|
219
|
+
),
|
|
220
|
+
returns: z.string(),
|
|
221
|
+
deterministic: z.boolean(),
|
|
222
|
+
seed_required: z.boolean()
|
|
223
|
+
})
|
|
224
|
+
)
|
|
225
|
+
});
|
|
226
|
+
var listSourcesOutputSchema = z.union([
|
|
227
|
+
sourceListOutputSchema,
|
|
228
|
+
moduleListOutputSchema,
|
|
229
|
+
functionListOutputSchema,
|
|
230
|
+
errorOutputSchema
|
|
231
|
+
]);
|
|
232
|
+
var explainQueryOkQueryOutputSchema = z.object({
|
|
233
|
+
status: z.literal("ok"),
|
|
234
|
+
kind: z.literal("query"),
|
|
235
|
+
resolved_plan: z.record(z.string(), z.unknown()),
|
|
236
|
+
plan: z.array(z.string()),
|
|
237
|
+
plan_hash: z.string().nullable(),
|
|
238
|
+
confidence: z.number(),
|
|
239
|
+
validated: z.boolean()
|
|
240
|
+
});
|
|
241
|
+
var explainQueryOkComputationOutputSchema = z.object({
|
|
242
|
+
status: z.literal("ok"),
|
|
243
|
+
kind: z.literal("computation"),
|
|
244
|
+
matched_signature: z.string(),
|
|
245
|
+
invocation_hash: z.string(),
|
|
246
|
+
seed_required: z.boolean()
|
|
247
|
+
});
|
|
248
|
+
var explainQueryOutputSchema = z.union([
|
|
249
|
+
explainQueryOkQueryOutputSchema,
|
|
250
|
+
explainQueryOkComputationOutputSchema,
|
|
251
|
+
clarificationOutputSchema,
|
|
252
|
+
rejectedOutputSchema,
|
|
253
|
+
errorOutputSchema
|
|
254
|
+
]);
|
|
255
|
+
|
|
256
|
+
// src/truncate.ts
|
|
257
|
+
function toCell(value) {
|
|
258
|
+
if (value === null || value === void 0) {
|
|
259
|
+
return null;
|
|
260
|
+
}
|
|
261
|
+
if (typeof value === "number" || typeof value === "string" || typeof value === "boolean") {
|
|
262
|
+
return value;
|
|
263
|
+
}
|
|
264
|
+
return JSON.stringify(value);
|
|
265
|
+
}
|
|
266
|
+
function shapeQueryResult(result, options) {
|
|
267
|
+
if (!Array.isArray(result)) {
|
|
268
|
+
return {
|
|
269
|
+
data: { columns: ["value"], rows: [[toCell(result)]] },
|
|
270
|
+
total_rows: 1,
|
|
271
|
+
returned_rows: 1,
|
|
272
|
+
truncated: false
|
|
273
|
+
};
|
|
274
|
+
}
|
|
275
|
+
const totalRows = result.length;
|
|
276
|
+
if (totalRows === 0) {
|
|
277
|
+
return { data: { columns: [], rows: [] }, total_rows: 0, returned_rows: 0, truncated: false };
|
|
278
|
+
}
|
|
279
|
+
const first = result[0];
|
|
280
|
+
let columns;
|
|
281
|
+
let toRow;
|
|
282
|
+
if (first !== null && typeof first === "object" && !Array.isArray(first)) {
|
|
283
|
+
columns = Object.keys(first);
|
|
284
|
+
toRow = (row) => columns.map((column) => toCell(row[column]));
|
|
285
|
+
} else {
|
|
286
|
+
columns = ["value"];
|
|
287
|
+
toRow = (row) => [toCell(row)];
|
|
288
|
+
}
|
|
289
|
+
let keep = Math.min(totalRows, Math.max(0, Math.floor(options.maxRowsToModel)));
|
|
290
|
+
const cellsPerRow = Math.max(1, columns.length);
|
|
291
|
+
if (keep * cellsPerRow > options.maxCellsToModel) {
|
|
292
|
+
keep = Math.max(0, Math.floor(options.maxCellsToModel / cellsPerRow));
|
|
293
|
+
}
|
|
294
|
+
return {
|
|
295
|
+
data: { columns, rows: result.slice(0, keep).map(toRow) },
|
|
296
|
+
total_rows: totalRows,
|
|
297
|
+
returned_rows: keep,
|
|
298
|
+
truncated: keep < totalRows
|
|
299
|
+
};
|
|
300
|
+
}
|
|
301
|
+
function byteLength(value) {
|
|
302
|
+
return Buffer.byteLength(JSON.stringify(value ?? null), "utf-8");
|
|
303
|
+
}
|
|
304
|
+
function shapeComputationValue(value, options) {
|
|
305
|
+
if (value === null || typeof value !== "object") {
|
|
306
|
+
return { value, truncated: false };
|
|
307
|
+
}
|
|
308
|
+
if (Array.isArray(value)) {
|
|
309
|
+
const elementCount = value.length;
|
|
310
|
+
let preview = value.slice(0, Math.min(elementCount, Math.max(0, options.maxElementsToModel)));
|
|
311
|
+
while (preview.length > 0 && byteLength(preview) > options.maxBytesToModel) {
|
|
312
|
+
preview = preview.slice(0, Math.floor(preview.length / 2));
|
|
313
|
+
}
|
|
314
|
+
return {
|
|
315
|
+
value: null,
|
|
316
|
+
preview,
|
|
317
|
+
element_count: elementCount,
|
|
318
|
+
truncated: preview.length < elementCount
|
|
319
|
+
};
|
|
320
|
+
}
|
|
321
|
+
if (byteLength(value) <= options.maxBytesToModel) {
|
|
322
|
+
return { value, truncated: false };
|
|
323
|
+
}
|
|
324
|
+
return { value: null, truncated: true };
|
|
325
|
+
}
|
|
326
|
+
function storeWithinCap(value, maxOutputBytes, store) {
|
|
327
|
+
if (byteLength(value) > maxOutputBytes) {
|
|
328
|
+
return void 0;
|
|
329
|
+
}
|
|
330
|
+
return store(value);
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
// src/tools.ts
|
|
334
|
+
var DEFAULT_OPTIONS = {
|
|
335
|
+
defaultLimit: 100,
|
|
336
|
+
maxExecutionRows: 1e3,
|
|
337
|
+
maxRowsToModel: 25,
|
|
338
|
+
maxCellsToModel: 250,
|
|
339
|
+
maxElementsToModel: 500,
|
|
340
|
+
maxBytesToModel: 32e3,
|
|
341
|
+
maxOutputBytes: 1e7
|
|
342
|
+
};
|
|
343
|
+
function toErrorOutput(error) {
|
|
344
|
+
if (error instanceof SqaiError) {
|
|
345
|
+
const nearest = error.details["nearest_matches"];
|
|
346
|
+
const nearestMatches = Array.isArray(nearest) && nearest.every((item) => typeof item === "string") ? nearest : void 0;
|
|
347
|
+
return {
|
|
348
|
+
status: "error",
|
|
349
|
+
code: error.code,
|
|
350
|
+
message: error.message,
|
|
351
|
+
retryable: error.retryable,
|
|
352
|
+
request_id: error.requestId,
|
|
353
|
+
...nearestMatches ? { nearest_matches: nearestMatches } : {}
|
|
354
|
+
};
|
|
355
|
+
}
|
|
356
|
+
return {
|
|
357
|
+
status: "error",
|
|
358
|
+
code: "internal_error",
|
|
359
|
+
message: error instanceof Error ? error.message : String(error),
|
|
360
|
+
retryable: false,
|
|
361
|
+
request_id: null
|
|
362
|
+
};
|
|
363
|
+
}
|
|
364
|
+
async function readyError(toolkit) {
|
|
365
|
+
try {
|
|
366
|
+
await toolkit.ensureReady();
|
|
367
|
+
return null;
|
|
368
|
+
} catch (error) {
|
|
369
|
+
return error instanceof SqaiError ? error : new SqaiError(
|
|
370
|
+
"source_connection_failed",
|
|
371
|
+
`Connecting the configured sources failed: ${error instanceof Error ? error.message : String(error)}`
|
|
372
|
+
);
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
function passthroughCandidates(resolution) {
|
|
376
|
+
return (resolution.candidates ?? []).map((candidate) => ({ ...candidate }));
|
|
377
|
+
}
|
|
378
|
+
function clarificationOutput(resolution) {
|
|
379
|
+
const candidates = resolution.candidates ?? [];
|
|
380
|
+
const names = candidates.slice(0, 5).map((candidate) => `${candidate.source}.${candidate.column}`);
|
|
381
|
+
const lead = resolution.explanation?.[0] ?? "The request is ambiguous.";
|
|
382
|
+
const question = names.length > 0 ? `${lead} Did you mean ${names.join(" or ")}?` : lead;
|
|
383
|
+
return {
|
|
384
|
+
status: "needs_clarification",
|
|
385
|
+
question,
|
|
386
|
+
candidates: passthroughCandidates(resolution),
|
|
387
|
+
explanation: resolution.explanation ?? []
|
|
388
|
+
};
|
|
389
|
+
}
|
|
390
|
+
function rejectedOutput(resolution) {
|
|
391
|
+
return {
|
|
392
|
+
status: "rejected",
|
|
393
|
+
rejection_reason: resolution.rejection_reason ?? "rejected",
|
|
394
|
+
candidates: passthroughCandidates(resolution),
|
|
395
|
+
explanation: resolution.explanation ?? []
|
|
396
|
+
};
|
|
397
|
+
}
|
|
398
|
+
function renderSignature(entry) {
|
|
399
|
+
const params = entry.signature.params;
|
|
400
|
+
const rendered = params.map((param) => `${param.name}${param.required === false ? "?" : ""}: ${param.type}`).join(", ");
|
|
401
|
+
return `${entry.name}(${rendered}) -> ${entry.signature.returns}`;
|
|
402
|
+
}
|
|
403
|
+
function splitCapabilityName(name) {
|
|
404
|
+
const separator = name.lastIndexOf(".");
|
|
405
|
+
return { module: name.slice(0, separator), functionName: name.slice(separator + 1) };
|
|
406
|
+
}
|
|
407
|
+
function toQuerySpec(spec, options) {
|
|
408
|
+
const sourceName = spec.source ?? options.source;
|
|
409
|
+
return {
|
|
410
|
+
metric: spec.metric,
|
|
411
|
+
...spec.aggregation ? { aggregation: spec.aggregation } : {},
|
|
412
|
+
...spec.group_by ? { group_by: spec.group_by } : {},
|
|
413
|
+
...spec.filter ? { filter: spec.filter } : {},
|
|
414
|
+
limit: Math.min(spec.limit ?? options.defaultLimit, options.maxExecutionRows),
|
|
415
|
+
...spec.order ? { order: spec.order } : {},
|
|
416
|
+
...sourceName ? { source_name: sourceName } : {}
|
|
417
|
+
};
|
|
418
|
+
}
|
|
419
|
+
function toComputationSpec(spec) {
|
|
420
|
+
return {
|
|
421
|
+
module: spec.module,
|
|
422
|
+
function: spec.function,
|
|
423
|
+
args: spec.args ?? [],
|
|
424
|
+
...spec.kwargs ? { kwargs: spec.kwargs } : {},
|
|
425
|
+
...spec.bindings ? { bindings: spec.bindings } : {},
|
|
426
|
+
...spec.seed !== void 0 ? { seed: spec.seed } : {}
|
|
427
|
+
};
|
|
428
|
+
}
|
|
429
|
+
var NUMBER_OPS = [
|
|
430
|
+
"sum",
|
|
431
|
+
"avg",
|
|
432
|
+
"count",
|
|
433
|
+
"min",
|
|
434
|
+
"max",
|
|
435
|
+
"eq",
|
|
436
|
+
"in",
|
|
437
|
+
"gt",
|
|
438
|
+
"gte",
|
|
439
|
+
"lt",
|
|
440
|
+
"lte",
|
|
441
|
+
"is_null",
|
|
442
|
+
"is_not_null"
|
|
443
|
+
];
|
|
444
|
+
var FILTER_ONLY_OPS = ["eq", "in", "is_null", "is_not_null"];
|
|
445
|
+
function opsForFieldType(type) {
|
|
446
|
+
if (type === "number") {
|
|
447
|
+
return [...NUMBER_OPS];
|
|
448
|
+
}
|
|
449
|
+
return [...FILTER_ONLY_OPS];
|
|
450
|
+
}
|
|
451
|
+
function describeSources(sources) {
|
|
452
|
+
return sources.map((source) => {
|
|
453
|
+
const typedFields = source.typed_fields.length > 0 ? source.typed_fields : source.fields.map((name) => ({ name, type: "unknown" }));
|
|
454
|
+
return {
|
|
455
|
+
name: source.name,
|
|
456
|
+
fields: typedFields.map((field) => ({
|
|
457
|
+
name: field.name,
|
|
458
|
+
type: field.type,
|
|
459
|
+
ops: opsForFieldType(field.type)
|
|
460
|
+
})),
|
|
461
|
+
row_count: source.row_count,
|
|
462
|
+
schema_revision: source.schema_revision
|
|
463
|
+
};
|
|
464
|
+
});
|
|
465
|
+
}
|
|
466
|
+
function searchModules(client, query) {
|
|
467
|
+
const matches = client.searchCapabilities(query, 100);
|
|
468
|
+
const byModule = /* @__PURE__ */ new Map();
|
|
469
|
+
for (const match of matches) {
|
|
470
|
+
const { module, functionName } = splitCapabilityName(match.entry.name);
|
|
471
|
+
let aggregate = byModule.get(module);
|
|
472
|
+
if (!aggregate) {
|
|
473
|
+
if (byModule.size >= 10) {
|
|
474
|
+
continue;
|
|
475
|
+
}
|
|
476
|
+
aggregate = {
|
|
477
|
+
name: module,
|
|
478
|
+
category: match.entry.category,
|
|
479
|
+
deterministic: true,
|
|
480
|
+
function_count: 0,
|
|
481
|
+
sample_functions: []
|
|
482
|
+
};
|
|
483
|
+
byModule.set(module, aggregate);
|
|
484
|
+
}
|
|
485
|
+
aggregate.function_count += 1;
|
|
486
|
+
if (aggregate.sample_functions.length < 5) {
|
|
487
|
+
aggregate.sample_functions.push(functionName);
|
|
488
|
+
}
|
|
489
|
+
}
|
|
490
|
+
return [...byModule.values()];
|
|
491
|
+
}
|
|
492
|
+
function moduleFunctions(client, index, moduleName) {
|
|
493
|
+
const prefix = `${moduleName}.`;
|
|
494
|
+
const entries = client.capabilities().capabilities.filter(
|
|
495
|
+
(entry) => entry.ai_sdk && isReadOnlyEligible(entry) && entry.name.startsWith(prefix)
|
|
496
|
+
);
|
|
497
|
+
if (entries.length === 0) {
|
|
498
|
+
throw new SqaiError("unsupported_operation", `Unknown capability module '${moduleName}'.`, {
|
|
499
|
+
details: { module: moduleName, nearest_matches: index().nearest(moduleName) }
|
|
500
|
+
});
|
|
501
|
+
}
|
|
502
|
+
return entries.map((entry) => ({
|
|
503
|
+
name: entry.name,
|
|
504
|
+
signature: renderSignature(entry),
|
|
505
|
+
params: entry.signature.params.map(
|
|
506
|
+
(param) => ({ name: param.name, type: param.type, required: param.required ?? true })
|
|
507
|
+
),
|
|
508
|
+
returns: entry.signature.returns,
|
|
509
|
+
deterministic: entry.deterministic,
|
|
510
|
+
seed_required: entry.seed_required ?? false
|
|
511
|
+
}));
|
|
512
|
+
}
|
|
513
|
+
function createSqaiTools(toolkit, options = {}) {
|
|
514
|
+
const opts = { ...DEFAULT_OPTIONS, ...options };
|
|
515
|
+
const client = toolkit.client;
|
|
516
|
+
let cachedIndex = null;
|
|
517
|
+
const contractIndex = () => {
|
|
518
|
+
if (!cachedIndex) {
|
|
519
|
+
cachedIndex = new ContractIndex(client.capabilities());
|
|
520
|
+
}
|
|
521
|
+
return cachedIndex;
|
|
522
|
+
};
|
|
523
|
+
const listSources = tool({
|
|
524
|
+
description: "Discover what SQAI can touch. No arguments: list connected data sources with exact field names, types, and allowed operations \u2014 call this before queryData so specs use exact names. With capabilitySearch: search the deterministic computation catalog and get matching modules. With module: list that module's functions with exact signatures.",
|
|
525
|
+
inputSchema: listSourcesInputSchema,
|
|
526
|
+
outputSchema: listSourcesOutputSchema,
|
|
527
|
+
execute: async (input) => {
|
|
528
|
+
const notReady = await readyError(toolkit);
|
|
529
|
+
if (notReady) {
|
|
530
|
+
return toErrorOutput(notReady);
|
|
531
|
+
}
|
|
532
|
+
try {
|
|
533
|
+
if (input?.capabilitySearch) {
|
|
534
|
+
return { modules: searchModules(client, input.capabilitySearch) };
|
|
535
|
+
}
|
|
536
|
+
if (input?.module) {
|
|
537
|
+
return { functions: moduleFunctions(client, contractIndex, input.module) };
|
|
538
|
+
}
|
|
539
|
+
return { sources: describeSources(client.listSources()) };
|
|
540
|
+
} catch (error) {
|
|
541
|
+
return toErrorOutput(error);
|
|
542
|
+
}
|
|
543
|
+
}
|
|
544
|
+
});
|
|
545
|
+
const queryData = tool({
|
|
546
|
+
description: "Execute one deterministic, read-only SQAI request. Use kind 'query' for tabular questions (aggregations, grouping, filtering) over connected sources \u2014 call listSources first for exact field names. Use kind 'computation' for math (statistics, financial, vector/matrix, simulation) over arrays: never paste large data into args \u2014 use bindings to pull columns from connected sources. Simulation modules require a seed. Large results are truncated for context; the full result stays retrievable via result_id.",
|
|
547
|
+
inputSchema: queryDataInputSchema,
|
|
548
|
+
outputSchema: queryDataOutputSchema,
|
|
549
|
+
execute: async (input) => {
|
|
550
|
+
const notReady = await readyError(toolkit);
|
|
551
|
+
if (notReady) {
|
|
552
|
+
return toErrorOutput(notReady);
|
|
553
|
+
}
|
|
554
|
+
try {
|
|
555
|
+
if (input.kind === "query") {
|
|
556
|
+
return await runQuery(input.spec);
|
|
557
|
+
}
|
|
558
|
+
return await runComputation(input.spec);
|
|
559
|
+
} catch (error) {
|
|
560
|
+
return toErrorOutput(error);
|
|
561
|
+
}
|
|
562
|
+
}
|
|
563
|
+
});
|
|
564
|
+
const explainQuery = tool({
|
|
565
|
+
description: "Dry-run a queryData request without executing it. kind 'query': resolve and verify the plan (resolved_plan, plan steps, plan_hash, confidence, validated). kind 'computation': check the capability exists and get its exact signature, a preview invocation_hash, and whether a seed is required. Use this to debug clarifications and rejections before running queryData.",
|
|
566
|
+
inputSchema: explainQueryInputSchema,
|
|
567
|
+
outputSchema: explainQueryOutputSchema,
|
|
568
|
+
execute: async (input) => {
|
|
569
|
+
const notReady = await readyError(toolkit);
|
|
570
|
+
if (notReady) {
|
|
571
|
+
return toErrorOutput(notReady);
|
|
572
|
+
}
|
|
573
|
+
try {
|
|
574
|
+
if (input.kind === "query") {
|
|
575
|
+
return await explainQueryPlan(input.spec);
|
|
576
|
+
}
|
|
577
|
+
return explainComputation(input.spec);
|
|
578
|
+
} catch (error) {
|
|
579
|
+
return toErrorOutput(error);
|
|
580
|
+
}
|
|
581
|
+
}
|
|
582
|
+
});
|
|
583
|
+
async function runQuery(spec) {
|
|
584
|
+
const querySpec = toQuerySpec(spec, opts);
|
|
585
|
+
const outcome = await client.ask(querySpec);
|
|
586
|
+
if (outcome.status === "needs_clarification") {
|
|
587
|
+
return clarificationOutput(outcome.resolution);
|
|
588
|
+
}
|
|
589
|
+
if (outcome.status === "rejected") {
|
|
590
|
+
return rejectedOutput(outcome.resolution);
|
|
591
|
+
}
|
|
592
|
+
const { data, resolution } = outcome;
|
|
593
|
+
const shaped = shapeQueryResult(data.result, opts);
|
|
594
|
+
const sourceName = data.resolved_source || resolution.resolved_source || null;
|
|
595
|
+
const resultId = storeWithinCap(
|
|
596
|
+
data.result,
|
|
597
|
+
opts.maxOutputBytes,
|
|
598
|
+
(value) => client.storeResult(value, sourceName ? [sourceName] : [])
|
|
599
|
+
);
|
|
600
|
+
return {
|
|
601
|
+
status: "ok",
|
|
602
|
+
kind: "query",
|
|
603
|
+
data: shaped.data,
|
|
604
|
+
total_rows: shaped.total_rows,
|
|
605
|
+
returned_rows: shaped.returned_rows,
|
|
606
|
+
truncated: shaped.truncated,
|
|
607
|
+
...resultId ? { result_id: resultId } : {},
|
|
608
|
+
plan_hash: data.plan_hash ?? resolution.plan_hash ?? null,
|
|
609
|
+
schema_revision: data.schema_revision ?? resolution.schema_revision ?? null,
|
|
610
|
+
source_name: sourceName,
|
|
611
|
+
intent_signature: resolution.intent_signature ?? null,
|
|
612
|
+
deterministic_scope: data.deterministic_scope ?? resolution.deterministic_scope ?? null,
|
|
613
|
+
decision_path: data.decision_path ?? resolution.decision_path ?? null,
|
|
614
|
+
validated: data.validated ?? resolution.validated ?? null,
|
|
615
|
+
request_id: data.request_id ?? resolution.request_id ?? null,
|
|
616
|
+
explanation: data.explanation ?? resolution.explanation ?? []
|
|
617
|
+
};
|
|
618
|
+
}
|
|
619
|
+
async function runComputation(spec) {
|
|
620
|
+
const result = await client.compute(toComputationSpec(spec));
|
|
621
|
+
const shaped = shapeComputationValue(result.value, opts);
|
|
622
|
+
const sourceIds = result.provenance?.bindings.map((binding) => binding.source_name) ?? [];
|
|
623
|
+
const resultId = storeWithinCap(
|
|
624
|
+
result.value,
|
|
625
|
+
opts.maxOutputBytes,
|
|
626
|
+
(value) => client.storeResult(value, sourceIds)
|
|
627
|
+
);
|
|
628
|
+
return {
|
|
629
|
+
status: "ok",
|
|
630
|
+
kind: "computation",
|
|
631
|
+
value: shaped.value,
|
|
632
|
+
value_type: result.value_type,
|
|
633
|
+
...shaped.preview !== void 0 ? { preview: shaped.preview } : {},
|
|
634
|
+
...shaped.element_count !== void 0 ? { element_count: shaped.element_count } : {},
|
|
635
|
+
truncated: shaped.truncated,
|
|
636
|
+
...resultId ? { result_id: resultId } : {},
|
|
637
|
+
invocation_hash: result.invocation_hash,
|
|
638
|
+
computation_hash: result.computation_hash,
|
|
639
|
+
contract_hash: result.contract_hash,
|
|
640
|
+
determinism: result.determinism,
|
|
641
|
+
...result.provenance ? { provenance: result.provenance } : {},
|
|
642
|
+
latency_ms: result.latency_ms,
|
|
643
|
+
request_id: result.request_id
|
|
644
|
+
};
|
|
645
|
+
}
|
|
646
|
+
async function explainQueryPlan(spec) {
|
|
647
|
+
const resolution = await client.resolve(toQuerySpec(spec, opts));
|
|
648
|
+
if (resolution.clarification_required) {
|
|
649
|
+
return clarificationOutput(resolution);
|
|
650
|
+
}
|
|
651
|
+
if (resolution.rejection_reason || !resolution.resolved_plan) {
|
|
652
|
+
return rejectedOutput(resolution);
|
|
653
|
+
}
|
|
654
|
+
const verification = await client.verify(resolution);
|
|
655
|
+
return {
|
|
656
|
+
status: "ok",
|
|
657
|
+
kind: "query",
|
|
658
|
+
resolved_plan: resolution.resolved_plan,
|
|
659
|
+
plan: resolution.plan ?? [],
|
|
660
|
+
plan_hash: resolution.plan_hash ?? null,
|
|
661
|
+
confidence: resolution.confidence,
|
|
662
|
+
validated: verification.valid === true
|
|
663
|
+
};
|
|
664
|
+
}
|
|
665
|
+
function explainComputation(spec) {
|
|
666
|
+
const index = contractIndex();
|
|
667
|
+
const entry = lookupCapability(index, spec.module, spec.function);
|
|
668
|
+
const bindings = spec.bindings ?? [];
|
|
669
|
+
const descriptors = bindings.flatMap(
|
|
670
|
+
(binding) => "parameters" in binding ? binding.parameters.map((parameter, position) => ({
|
|
671
|
+
parameter,
|
|
672
|
+
source_name: binding.source,
|
|
673
|
+
fields: [binding.fields[position] ?? ""],
|
|
674
|
+
input_hash: ""
|
|
675
|
+
})) : [
|
|
676
|
+
{
|
|
677
|
+
parameter: binding.parameter,
|
|
678
|
+
source_name: binding.source,
|
|
679
|
+
fields: [binding.field],
|
|
680
|
+
input_hash: ""
|
|
681
|
+
}
|
|
682
|
+
]
|
|
683
|
+
);
|
|
684
|
+
const previewHash = invocationHash({
|
|
685
|
+
module: spec.module,
|
|
686
|
+
function: spec.function,
|
|
687
|
+
args: spec.args ?? [],
|
|
688
|
+
kwargs: spec.kwargs ?? {},
|
|
689
|
+
resolved_bindings: descriptors,
|
|
690
|
+
seed: spec.seed ?? null,
|
|
691
|
+
contract_hash: index.contract.capability_contract_hash,
|
|
692
|
+
execution_scope: `${currentPlatformKey()}:${client.mode}`
|
|
693
|
+
});
|
|
694
|
+
return {
|
|
695
|
+
status: "ok",
|
|
696
|
+
kind: "computation",
|
|
697
|
+
matched_signature: renderSignature(entry),
|
|
698
|
+
invocation_hash: previewHash,
|
|
699
|
+
seed_required: entry.seed_required ?? false
|
|
700
|
+
};
|
|
701
|
+
}
|
|
702
|
+
return { listSources, queryData, explainQuery };
|
|
703
|
+
}
|
|
704
|
+
|
|
705
|
+
// src/toolkit.ts
|
|
706
|
+
var SQAIToolkit = class {
|
|
707
|
+
client;
|
|
708
|
+
sourceInputs;
|
|
709
|
+
readyPromise = null;
|
|
710
|
+
readyError = null;
|
|
711
|
+
constructor(config = {}) {
|
|
712
|
+
const { sources, ...clientConfig } = config;
|
|
713
|
+
this.client = new SQAI(clientConfig);
|
|
714
|
+
this.sourceInputs = sources ?? [];
|
|
715
|
+
}
|
|
716
|
+
/** Connected sources; connecting starts on first access. */
|
|
717
|
+
get ready() {
|
|
718
|
+
return this.ensureReady();
|
|
719
|
+
}
|
|
720
|
+
/** The stored connection failure, when `ready` rejected. */
|
|
721
|
+
get connectionError() {
|
|
722
|
+
return this.readyError;
|
|
723
|
+
}
|
|
724
|
+
ensureReady() {
|
|
725
|
+
if (!this.readyPromise) {
|
|
726
|
+
this.readyPromise = this.connectAll();
|
|
727
|
+
this.readyPromise.catch((error) => {
|
|
728
|
+
this.readyError = error instanceof SqaiError2 ? error : new SqaiError2(
|
|
729
|
+
"source_connection_failed",
|
|
730
|
+
`Connecting the configured sources failed: ${error instanceof Error ? error.message : String(error)}`
|
|
731
|
+
);
|
|
732
|
+
});
|
|
733
|
+
}
|
|
734
|
+
return this.readyPromise;
|
|
735
|
+
}
|
|
736
|
+
async connect(source, options = {}) {
|
|
737
|
+
return this.client.connect(source, options);
|
|
738
|
+
}
|
|
739
|
+
tools(options = {}) {
|
|
740
|
+
return createSqaiTools(this, options);
|
|
741
|
+
}
|
|
742
|
+
async connectAll() {
|
|
743
|
+
for (const input of this.sourceInputs) {
|
|
744
|
+
if (typeof input === "string" || Array.isArray(input)) {
|
|
745
|
+
await this.client.connect(input);
|
|
746
|
+
} else {
|
|
747
|
+
await this.client.connect(input.data, input.name ? { name: input.name } : {});
|
|
748
|
+
}
|
|
749
|
+
}
|
|
750
|
+
return this.client.listSources();
|
|
751
|
+
}
|
|
752
|
+
};
|
|
753
|
+
function createSQAI(config = {}) {
|
|
754
|
+
return new SQAIToolkit(config);
|
|
755
|
+
}
|
|
756
|
+
|
|
757
|
+
// src/index.ts
|
|
758
|
+
import { SQAI as SQAI2, SqaiError as SqaiError3 } from "@thyn-ai/sqai";
|
|
759
|
+
export {
|
|
760
|
+
SQAI2 as SQAI,
|
|
761
|
+
SQAIToolkit,
|
|
762
|
+
SqaiError3 as SqaiError,
|
|
763
|
+
algentaWireValueSchema,
|
|
764
|
+
bindingProvenanceSchema,
|
|
765
|
+
candidateSchema,
|
|
766
|
+
clarificationOutputSchema,
|
|
767
|
+
computationBindingSchema,
|
|
768
|
+
computationOkOutputSchema,
|
|
769
|
+
computationSpecSchema,
|
|
770
|
+
createSQAI,
|
|
771
|
+
createSqaiTools,
|
|
772
|
+
determinismEnvelopeSchema,
|
|
773
|
+
errorOutputSchema,
|
|
774
|
+
explainQueryInputSchema,
|
|
775
|
+
explainQueryOkComputationOutputSchema,
|
|
776
|
+
explainQueryOkQueryOutputSchema,
|
|
777
|
+
explainQueryOutputSchema,
|
|
778
|
+
filterConditionSchema,
|
|
779
|
+
filterSpecSchema,
|
|
780
|
+
functionListOutputSchema,
|
|
781
|
+
listSourcesInputSchema,
|
|
782
|
+
listSourcesOutputSchema,
|
|
783
|
+
moduleListOutputSchema,
|
|
784
|
+
queryDataInputSchema,
|
|
785
|
+
queryDataOutputSchema,
|
|
786
|
+
queryOkOutputSchema,
|
|
787
|
+
querySpecSchema,
|
|
788
|
+
rejectedOutputSchema,
|
|
789
|
+
shapeComputationValue,
|
|
790
|
+
shapeQueryResult,
|
|
791
|
+
sourceListOutputSchema,
|
|
792
|
+
storeWithinCap
|
|
793
|
+
};
|
|
794
|
+
//# sourceMappingURL=index.js.map
|