@reasoningco/infer 0.0.2

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.
Files changed (59) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +42 -0
  3. package/dist/catalog.d.ts +39 -0
  4. package/dist/catalog.d.ts.map +1 -0
  5. package/dist/catalog.js +249 -0
  6. package/dist/catalog.js.map +1 -0
  7. package/dist/connectors.d.ts +63 -0
  8. package/dist/connectors.d.ts.map +1 -0
  9. package/dist/connectors.js +263 -0
  10. package/dist/connectors.js.map +1 -0
  11. package/dist/dates.d.ts +35 -0
  12. package/dist/dates.d.ts.map +1 -0
  13. package/dist/dates.js +293 -0
  14. package/dist/dates.js.map +1 -0
  15. package/dist/entity-resolver.d.ts +29 -0
  16. package/dist/entity-resolver.d.ts.map +1 -0
  17. package/dist/entity-resolver.js +91 -0
  18. package/dist/entity-resolver.js.map +1 -0
  19. package/dist/errors.d.ts +10 -0
  20. package/dist/errors.d.ts.map +1 -0
  21. package/dist/errors.js +28 -0
  22. package/dist/errors.js.map +1 -0
  23. package/dist/index.d.ts +43 -0
  24. package/dist/index.d.ts.map +1 -0
  25. package/dist/index.js +240 -0
  26. package/dist/index.js.map +1 -0
  27. package/dist/llm.d.ts +50 -0
  28. package/dist/llm.d.ts.map +1 -0
  29. package/dist/llm.js +311 -0
  30. package/dist/llm.js.map +1 -0
  31. package/dist/query-validator.d.ts +6 -0
  32. package/dist/query-validator.d.ts.map +1 -0
  33. package/dist/query-validator.js +136 -0
  34. package/dist/query-validator.js.map +1 -0
  35. package/dist/schemas.d.ts +170 -0
  36. package/dist/schemas.d.ts.map +1 -0
  37. package/dist/schemas.js +44 -0
  38. package/dist/schemas.js.map +1 -0
  39. package/dist/security.d.ts +9 -0
  40. package/dist/security.d.ts.map +1 -0
  41. package/dist/security.js +97 -0
  42. package/dist/security.js.map +1 -0
  43. package/dist/sql-builder.d.ts +5 -0
  44. package/dist/sql-builder.d.ts.map +1 -0
  45. package/dist/sql-builder.js +121 -0
  46. package/dist/sql-builder.js.map +1 -0
  47. package/dist/sql-validator.d.ts +8 -0
  48. package/dist/sql-validator.d.ts.map +1 -0
  49. package/dist/sql-validator.js +160 -0
  50. package/dist/sql-validator.js.map +1 -0
  51. package/dist/testing.d.ts +22 -0
  52. package/dist/testing.d.ts.map +1 -0
  53. package/dist/testing.js +78 -0
  54. package/dist/testing.js.map +1 -0
  55. package/dist/types.d.ts +214 -0
  56. package/dist/types.d.ts.map +1 -0
  57. package/dist/types.js +27 -0
  58. package/dist/types.js.map +1 -0
  59. package/package.json +72 -0
package/dist/llm.js ADDED
@@ -0,0 +1,311 @@
1
+ import { InferError } from './errors.js';
2
+ // ── OpenRouter Provider ──
3
+ export class OpenRouterProvider {
4
+ apiKey;
5
+ model;
6
+ baseUrl;
7
+ constructor(config) {
8
+ this.apiKey = config.apiKey;
9
+ this.model = config.model ?? 'anthropic/claude-haiku-4-5';
10
+ this.baseUrl = config.baseUrl ?? 'https://openrouter.ai/api/v1';
11
+ }
12
+ async chat(messages, options) {
13
+ const timeout = options?.timeout ?? 10000;
14
+ const controller = new AbortController();
15
+ const timer = setTimeout(() => controller.abort(), timeout);
16
+ try {
17
+ const response = await fetch(`${this.baseUrl}/chat/completions`, {
18
+ method: 'POST',
19
+ headers: {
20
+ 'Content-Type': 'application/json',
21
+ 'Authorization': `Bearer ${this.apiKey}`,
22
+ },
23
+ body: JSON.stringify({
24
+ model: this.model,
25
+ messages,
26
+ temperature: options?.temperature ?? 0,
27
+ max_tokens: options?.maxTokens ?? 1000,
28
+ }),
29
+ signal: controller.signal,
30
+ });
31
+ if (!response.ok) {
32
+ const status = response.status;
33
+ if (status === 429)
34
+ throw new InferError('RATE_LIMITED', 'planner', 'Rate limited by LLM provider');
35
+ throw new InferError('LLM_UNAVAILABLE', 'planner', `LLM provider returned ${status}`);
36
+ }
37
+ const data = await response.json();
38
+ return data.choices?.[0]?.message?.content ?? '';
39
+ }
40
+ catch (error) {
41
+ if (error instanceof InferError)
42
+ throw error;
43
+ if (error?.name === 'AbortError') {
44
+ throw new InferError('LLM_TIMEOUT', 'planner', 'LLM request timed out');
45
+ }
46
+ throw new InferError('LLM_UNAVAILABLE', 'planner', 'LLM request failed');
47
+ }
48
+ finally {
49
+ clearTimeout(timer);
50
+ }
51
+ }
52
+ }
53
+ // ── OpenAI Provider ──
54
+ export class OpenAIProvider {
55
+ apiKey;
56
+ model;
57
+ baseUrl;
58
+ constructor(config) {
59
+ this.apiKey = config.apiKey;
60
+ this.model = config.model ?? 'gpt-4o';
61
+ this.baseUrl = config.baseUrl ?? 'https://api.openai.com/v1';
62
+ }
63
+ async chat(messages, options) {
64
+ const timeout = options?.timeout ?? 10000;
65
+ const controller = new AbortController();
66
+ const timer = setTimeout(() => controller.abort(), timeout);
67
+ try {
68
+ const response = await fetch(`${this.baseUrl}/chat/completions`, {
69
+ method: 'POST',
70
+ headers: {
71
+ 'Content-Type': 'application/json',
72
+ 'Authorization': `Bearer ${this.apiKey}`,
73
+ },
74
+ body: JSON.stringify({
75
+ model: this.model,
76
+ messages,
77
+ temperature: options?.temperature ?? 0,
78
+ max_tokens: options?.maxTokens ?? 1000,
79
+ }),
80
+ signal: controller.signal,
81
+ });
82
+ if (!response.ok) {
83
+ const status = response.status;
84
+ if (status === 429)
85
+ throw new InferError('RATE_LIMITED', 'planner', 'Rate limited');
86
+ throw new InferError('LLM_UNAVAILABLE', 'planner', `OpenAI returned ${status}`);
87
+ }
88
+ const data = await response.json();
89
+ return data.choices?.[0]?.message?.content ?? '';
90
+ }
91
+ catch (error) {
92
+ if (error instanceof InferError)
93
+ throw error;
94
+ if (error?.name === 'AbortError')
95
+ throw new InferError('LLM_TIMEOUT', 'planner', 'Timed out');
96
+ throw new InferError('LLM_UNAVAILABLE', 'planner', 'OpenAI request failed');
97
+ }
98
+ finally {
99
+ clearTimeout(timer);
100
+ }
101
+ }
102
+ }
103
+ // ── Anthropic Provider ──
104
+ export class AnthropicProvider {
105
+ apiKey;
106
+ model;
107
+ baseUrl;
108
+ constructor(config) {
109
+ this.apiKey = config.apiKey;
110
+ this.model = config.model ?? 'claude-sonnet-4-6';
111
+ this.baseUrl = config.baseUrl ?? 'https://api.anthropic.com/v1';
112
+ }
113
+ async chat(messages, options) {
114
+ const timeout = options?.timeout ?? 10000;
115
+ const controller = new AbortController();
116
+ const timer = setTimeout(() => controller.abort(), timeout);
117
+ try {
118
+ // Separate system message from user/assistant messages
119
+ const systemMsg = messages.find(m => m.role === 'system')?.content ?? '';
120
+ const chatMessages = messages.filter(m => m.role !== 'system');
121
+ const response = await fetch(`${this.baseUrl}/messages`, {
122
+ method: 'POST',
123
+ headers: {
124
+ 'Content-Type': 'application/json',
125
+ 'x-api-key': this.apiKey,
126
+ 'anthropic-version': '2023-06-01',
127
+ },
128
+ body: JSON.stringify({
129
+ model: this.model,
130
+ system: systemMsg,
131
+ messages: chatMessages,
132
+ max_tokens: options?.maxTokens ?? 1000,
133
+ temperature: options?.temperature ?? 0,
134
+ }),
135
+ signal: controller.signal,
136
+ });
137
+ if (!response.ok) {
138
+ const status = response.status;
139
+ if (status === 429)
140
+ throw new InferError('RATE_LIMITED', 'planner', 'Rate limited');
141
+ throw new InferError('LLM_UNAVAILABLE', 'planner', `Anthropic returned ${status}`);
142
+ }
143
+ const data = await response.json();
144
+ return data.content?.[0]?.text ?? '';
145
+ }
146
+ catch (error) {
147
+ if (error instanceof InferError)
148
+ throw error;
149
+ if (error?.name === 'AbortError')
150
+ throw new InferError('LLM_TIMEOUT', 'planner', 'Timed out');
151
+ throw new InferError('LLM_UNAVAILABLE', 'planner', 'Anthropic request failed');
152
+ }
153
+ finally {
154
+ clearTimeout(timer);
155
+ }
156
+ }
157
+ }
158
+ // ── Ollama Provider ──
159
+ export class OllamaProvider {
160
+ model;
161
+ baseUrl;
162
+ constructor(config) {
163
+ this.model = config.model ?? 'llama3';
164
+ this.baseUrl = config.baseUrl ?? 'http://localhost:11434';
165
+ }
166
+ async chat(messages, options) {
167
+ const timeout = options?.timeout ?? 30000;
168
+ const controller = new AbortController();
169
+ const timer = setTimeout(() => controller.abort(), timeout);
170
+ try {
171
+ const response = await fetch(`${this.baseUrl}/api/chat`, {
172
+ method: 'POST',
173
+ headers: { 'Content-Type': 'application/json' },
174
+ body: JSON.stringify({
175
+ model: this.model,
176
+ messages,
177
+ stream: false,
178
+ options: {
179
+ temperature: options?.temperature ?? 0,
180
+ num_predict: options?.maxTokens ?? 1000,
181
+ },
182
+ }),
183
+ signal: controller.signal,
184
+ });
185
+ if (!response.ok)
186
+ throw new InferError('LLM_UNAVAILABLE', 'planner', `Ollama returned ${response.status}`);
187
+ const data = await response.json();
188
+ return data.message?.content ?? '';
189
+ }
190
+ catch (error) {
191
+ if (error instanceof InferError)
192
+ throw error;
193
+ if (error?.name === 'AbortError')
194
+ throw new InferError('LLM_TIMEOUT', 'planner', 'Timed out');
195
+ throw new InferError('LLM_UNAVAILABLE', 'planner', 'Ollama request failed');
196
+ }
197
+ finally {
198
+ clearTimeout(timer);
199
+ }
200
+ }
201
+ }
202
+ // ── Provider Factory ──
203
+ export function createLLMProvider(config) {
204
+ switch (config.provider) {
205
+ case 'openrouter':
206
+ if (!config.apiKey)
207
+ throw new Error('apiKey required for OpenRouter');
208
+ return new OpenRouterProvider({ apiKey: config.apiKey, model: config.model, baseUrl: config.baseUrl });
209
+ case 'openai':
210
+ if (!config.apiKey)
211
+ throw new Error('apiKey required for OpenAI');
212
+ return new OpenAIProvider({ apiKey: config.apiKey, model: config.model, baseUrl: config.baseUrl });
213
+ case 'anthropic':
214
+ if (!config.apiKey)
215
+ throw new Error('apiKey required for Anthropic');
216
+ return new AnthropicProvider({ apiKey: config.apiKey, model: config.model, baseUrl: config.baseUrl });
217
+ case 'ollama':
218
+ return new OllamaProvider({ model: config.model, baseUrl: config.baseUrl });
219
+ default:
220
+ throw new Error(`Unknown LLM provider: ${config.provider}`);
221
+ }
222
+ }
223
+ // ── System Prompt Auto-Generation (from ChefOS smartPlanner.ts buildCatalogSummary) ──
224
+ export function buildCatalogSummary(catalog) {
225
+ const lines = [
226
+ 'You are a query planner for a database with these tables:',
227
+ '',
228
+ ];
229
+ for (const [id, ds] of Object.entries(catalog.datasets)) {
230
+ lines.push(`**${id}** (${ds.table.schema}.${ds.table.name}${ds.estimatedRows ? `, ~${formatRowCount(ds.estimatedRows)} rows` : ''})`);
231
+ // Columns
232
+ const colDescs = ds.selectableColumns.map(col => {
233
+ const type = ds.columnTypes[col] || 'text';
234
+ return `${col} (${type})`;
235
+ });
236
+ lines.push(` Columns: ${colDescs.join(', ')}`);
237
+ // Metrics
238
+ if (ds.metrics.length > 0) {
239
+ const metricDescs = ds.metrics.map(m => `${m.id} = ${m.expression}${m.description ? ` — "${m.description}"` : ''}`);
240
+ lines.push(` Metrics: ${metricDescs.join(', ')}`);
241
+ }
242
+ // Dimensions
243
+ const groupableDims = ds.dimensions.filter(d => d.groupable);
244
+ if (groupableDims.length > 0) {
245
+ lines.push(` Group by: ${groupableDims.map(d => d.column).join(', ')}`);
246
+ }
247
+ // Entities
248
+ if (ds.entities.length > 0) {
249
+ lines.push(` Entities: ${ds.entities.map(e => `${e.label} (${e.key})`).join(', ')}`);
250
+ }
251
+ // Joins
252
+ if (ds.joins && ds.joins.length > 0) {
253
+ lines.push(` Joins: ${ds.joins.map(j => `→ ${j.table} via ${j.localColumn}`).join(', ')}`);
254
+ }
255
+ // Date column
256
+ if (ds.dateColumn) {
257
+ lines.push(` Date column: ${ds.dateColumn}`);
258
+ }
259
+ lines.push('');
260
+ }
261
+ lines.push('Given a user question, return a JSON query plan with this shape:');
262
+ lines.push('```json');
263
+ lines.push(JSON.stringify({
264
+ dataset: "dataset_id",
265
+ metrics: ["metric_id"],
266
+ groupBy: ["column"],
267
+ filters: [{ field: "column", op: "=", value: "value" }],
268
+ dateRange: { mode: "relative", phrase: "last 30 days" },
269
+ orderBy: [{ field: "column", dir: "desc" }],
270
+ limit: 50,
271
+ answerMode: "aggregate_table",
272
+ }, null, 2));
273
+ lines.push('```');
274
+ lines.push('');
275
+ lines.push('Rules:');
276
+ lines.push('- Only use datasets, columns, and metrics listed above');
277
+ lines.push('- Use answerMode "scalar" for single-number questions like "how many orders"');
278
+ lines.push('- Use answerMode "aggregate_table" for grouped data like "revenue by month"');
279
+ lines.push('- Use answerMode "rows" for listing individual records');
280
+ lines.push('- Date ranges: use "relative" with natural phrases like "last 7 days", "this month", "last quarter"');
281
+ lines.push('- Return ONLY the JSON object, no explanation');
282
+ return lines.join('\n');
283
+ }
284
+ // ── JSON Extraction (from ChefOS reportPlanner.ts) ──
285
+ export function extractJSON(text) {
286
+ // Strip markdown fences
287
+ let cleaned = text.trim();
288
+ const fenceMatch = cleaned.match(/```(?:json)?\s*([\s\S]*?)```/);
289
+ if (fenceMatch) {
290
+ cleaned = fenceMatch[1].trim();
291
+ }
292
+ // Try to find JSON object
293
+ const jsonMatch = cleaned.match(/\{[\s\S]*\}/);
294
+ if (jsonMatch) {
295
+ try {
296
+ return JSON.parse(jsonMatch[0]);
297
+ }
298
+ catch {
299
+ throw new InferError('LLM_INVALID_RESPONSE', 'planner', 'Failed to parse LLM JSON output');
300
+ }
301
+ }
302
+ throw new InferError('LLM_INVALID_RESPONSE', 'planner', 'No JSON found in LLM response');
303
+ }
304
+ function formatRowCount(n) {
305
+ if (n >= 1_000_000)
306
+ return `${(n / 1_000_000).toFixed(1)}M`;
307
+ if (n >= 1_000)
308
+ return `${(n / 1_000).toFixed(0)}K`;
309
+ return String(n);
310
+ }
311
+ //# sourceMappingURL=llm.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"llm.js","sourceRoot":"","sources":["../src/llm.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,6 @@
1
+ import type { InferCatalog, ValidatedSmartPlan } from './types.js';
2
+ export declare function validateQueryPlan(rawPlan: unknown, catalog: InferCatalog, options?: {
3
+ maxLimit?: number;
4
+ defaultLimit?: number;
5
+ }): ValidatedSmartPlan;
6
+ //# sourceMappingURL=query-validator.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"query-validator.d.ts","sourceRoot":"","sources":["../src/query-validator.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EACV,YAAY,EAAgC,kBAAkB,EAG/D,MAAM,YAAY,CAAC;AAKpB,wBAAgB,iBAAiB,CAC/B,OAAO,EAAE,OAAO,EAChB,OAAO,EAAE,YAAY,EACrB,OAAO,CAAC,EAAE;IAAE,QAAQ,CAAC,EAAE,MAAM,CAAC;IAAC,YAAY,CAAC,EAAE,MAAM,CAAA;CAAE,GACrD,kBAAkB,CA0IpB"}
@@ -0,0 +1,136 @@
1
+ import { InferError } from './errors.js';
2
+ import { SmartQueryPlanSchema } from './schemas.js';
3
+ import { resolveDateRange } from './dates.js';
4
+ import { allowlistedMetricIds, allowlistedGroupByColumns } from './types.js';
5
+ export function validateQueryPlan(rawPlan, catalog, options) {
6
+ const maxLimit = options?.maxLimit ?? 200;
7
+ const defaultLimit = options?.defaultLimit ?? 200;
8
+ // 1. Parse through Zod
9
+ const parseResult = SmartQueryPlanSchema.safeParse(rawPlan);
10
+ if (!parseResult.success) {
11
+ throw new InferError('PLAN_INVALID', 'validator', 'Invalid query plan structure', {
12
+ zodErrors: parseResult.error.flatten(),
13
+ });
14
+ }
15
+ const plan = parseResult.data;
16
+ // 2. Validate dataset exists in catalog
17
+ const dataset = catalog.datasets[plan.dataset];
18
+ if (!dataset) {
19
+ throw new InferError('PLAN_OUT_OF_SCOPE', 'validator', `Unknown dataset: ${plan.dataset}`);
20
+ }
21
+ // 3. Determine answer mode
22
+ const answerMode = plan.answerMode ?? 'rows';
23
+ // 4. Validate select columns (for "rows" mode)
24
+ const select = [];
25
+ if (answerMode === 'rows' && plan.select && plan.select.length > 0) {
26
+ for (const col of plan.select) {
27
+ if (!dataset.selectableColumns.includes(col)) {
28
+ throw new InferError('PLAN_INVALID', 'validator', `Column not selectable: ${col}`);
29
+ }
30
+ // Check sensitive columns
31
+ if (dataset.sensitiveColumns.includes(col)) {
32
+ throw new InferError('PLAN_SENSITIVE_COLUMN', 'validator', `Access to sensitive column denied`);
33
+ }
34
+ select.push(col);
35
+ }
36
+ }
37
+ // 5. Validate metrics
38
+ const allowedMetrics = allowlistedMetricIds(dataset);
39
+ const metrics = [];
40
+ if (plan.metrics && plan.metrics.length > 0) {
41
+ for (const metricId of plan.metrics) {
42
+ if (!allowedMetrics.has(metricId)) {
43
+ throw new InferError('PLAN_INVALID', 'validator', `Unknown metric: ${metricId}`);
44
+ }
45
+ const metricDef = dataset.metrics.find(m => m.id === metricId);
46
+ if (metricDef)
47
+ metrics.push(metricDef);
48
+ }
49
+ }
50
+ // 6. Validate groupBy columns
51
+ const allowedGroupBy = allowlistedGroupByColumns(dataset);
52
+ const groupBy = [];
53
+ if (plan.groupBy && plan.groupBy.length > 0) {
54
+ for (const col of plan.groupBy) {
55
+ if (!allowedGroupBy.has(col)) {
56
+ throw new InferError('PLAN_INVALID', 'validator', `Column not groupable: ${col}`);
57
+ }
58
+ if (dataset.sensitiveColumns.includes(col)) {
59
+ throw new InferError('PLAN_SENSITIVE_COLUMN', 'validator', `Access to sensitive column denied`);
60
+ }
61
+ groupBy.push(col);
62
+ }
63
+ }
64
+ // 7. Validate filters
65
+ const filters = [];
66
+ let paramIndex = 1;
67
+ if (plan.filters && plan.filters.length > 0) {
68
+ for (const filter of plan.filters) {
69
+ if (!dataset.filterableColumns.includes(filter.field)) {
70
+ throw new InferError('PLAN_INVALID', 'validator', `Column not filterable: ${filter.field}`);
71
+ }
72
+ if (dataset.sensitiveColumns.includes(filter.field)) {
73
+ throw new InferError('PLAN_SENSITIVE_COLUMN', 'validator', `Access to sensitive column denied`);
74
+ }
75
+ filters.push({
76
+ field: filter.field,
77
+ op: filter.op,
78
+ value: filter.value,
79
+ paramIndex: paramIndex++,
80
+ });
81
+ }
82
+ }
83
+ // 8. Resolve date range
84
+ let dateRange;
85
+ let timeMode = dataset.timeConfig.defaultMode;
86
+ if (plan.dateRange) {
87
+ if (plan.dateRange.mode === 'none') {
88
+ timeMode = 'none';
89
+ }
90
+ else if (plan.dateRange.mode === 'latest') {
91
+ timeMode = 'latest';
92
+ }
93
+ else if (plan.dateRange.mode === 'relative' && plan.dateRange.phrase) {
94
+ const resolved = resolveDateRange({ phrase: plan.dateRange.phrase });
95
+ if (resolved) {
96
+ dateRange = resolved;
97
+ timeMode = 'relative';
98
+ }
99
+ }
100
+ else if (plan.dateRange.mode === 'explicit' && plan.dateRange.startDate && plan.dateRange.endDate) {
101
+ dateRange = { startDate: plan.dateRange.startDate, endDate: plan.dateRange.endDate };
102
+ timeMode = 'absolute';
103
+ }
104
+ }
105
+ // 9. Validate orderBy
106
+ const orderBy = [];
107
+ if (plan.orderBy && plan.orderBy.length > 0) {
108
+ for (const ob of plan.orderBy) {
109
+ // orderBy can reference select columns, groupBy columns, or metric ids
110
+ const validField = dataset.selectableColumns.includes(ob.field) ||
111
+ groupBy.includes(ob.field) ||
112
+ metrics.some(m => m.id === ob.field);
113
+ if (!validField) {
114
+ // Silently skip invalid orderBy rather than throwing
115
+ continue;
116
+ }
117
+ orderBy.push({ field: ob.field, dir: ob.dir === 'desc' ? 'desc' : 'asc' });
118
+ }
119
+ }
120
+ // 10. Cap limit
121
+ const limit = Math.min(plan.limit ?? defaultLimit, maxLimit);
122
+ return {
123
+ dataset,
124
+ answerMode,
125
+ select,
126
+ metrics,
127
+ groupBy,
128
+ filters,
129
+ resolvedEntities: [], // populated later by entity resolver
130
+ dateRange,
131
+ timeMode,
132
+ orderBy,
133
+ limit,
134
+ };
135
+ }
136
+ //# sourceMappingURL=query-validator.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"query-validator.js","sourceRoot":"","sources":["../src/query-validator.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,170 @@
1
+ import { z } from "zod";
2
+ export declare const FilterSchema: z.ZodObject<{
3
+ field: z.ZodString;
4
+ op: z.ZodEnum<["=", "!=", ">", "<", ">=", "<=", "in", "not_in", "like", "ilike"]>;
5
+ value: z.ZodUnion<[z.ZodString, z.ZodNumber, z.ZodBoolean, z.ZodArray<z.ZodUnion<[z.ZodString, z.ZodNumber]>, "many">]>;
6
+ }, "strip", z.ZodTypeAny, {
7
+ value: string | number | boolean | (string | number)[];
8
+ field: string;
9
+ op: "=" | "!=" | ">" | "<" | ">=" | "<=" | "in" | "not_in" | "like" | "ilike";
10
+ }, {
11
+ value: string | number | boolean | (string | number)[];
12
+ field: string;
13
+ op: "=" | "!=" | ">" | "<" | ">=" | "<=" | "in" | "not_in" | "like" | "ilike";
14
+ }>;
15
+ export declare const EntityFilterSchema: z.ZodObject<{
16
+ resolver: z.ZodString;
17
+ input: z.ZodString;
18
+ outputColumn: z.ZodString;
19
+ }, "strip", z.ZodTypeAny, {
20
+ input: string;
21
+ resolver: string;
22
+ outputColumn: string;
23
+ }, {
24
+ input: string;
25
+ resolver: string;
26
+ outputColumn: string;
27
+ }>;
28
+ export declare const DateRangeSchema: z.ZodObject<{
29
+ mode: z.ZodEnum<["relative", "explicit", "latest", "none"]>;
30
+ phrase: z.ZodOptional<z.ZodString>;
31
+ startDate: z.ZodOptional<z.ZodString>;
32
+ endDate: z.ZodOptional<z.ZodString>;
33
+ }, "strip", z.ZodTypeAny, {
34
+ mode: "latest" | "none" | "relative" | "explicit";
35
+ startDate?: string | undefined;
36
+ endDate?: string | undefined;
37
+ phrase?: string | undefined;
38
+ }, {
39
+ mode: "latest" | "none" | "relative" | "explicit";
40
+ startDate?: string | undefined;
41
+ endDate?: string | undefined;
42
+ phrase?: string | undefined;
43
+ }>;
44
+ export declare const OrderBySchema: z.ZodObject<{
45
+ field: z.ZodString;
46
+ dir: z.ZodDefault<z.ZodEnum<["asc", "desc"]>>;
47
+ }, "strip", z.ZodTypeAny, {
48
+ field: string;
49
+ dir: "asc" | "desc";
50
+ }, {
51
+ field: string;
52
+ dir?: "asc" | "desc" | undefined;
53
+ }>;
54
+ export declare const SmartQueryPlanSchema: z.ZodObject<{
55
+ dataset: z.ZodString;
56
+ select: z.ZodDefault<z.ZodOptional<z.ZodArray<z.ZodString, "many">>>;
57
+ metrics: z.ZodDefault<z.ZodOptional<z.ZodArray<z.ZodString, "many">>>;
58
+ groupBy: z.ZodDefault<z.ZodOptional<z.ZodArray<z.ZodString, "many">>>;
59
+ filters: z.ZodDefault<z.ZodOptional<z.ZodArray<z.ZodObject<{
60
+ field: z.ZodString;
61
+ op: z.ZodEnum<["=", "!=", ">", "<", ">=", "<=", "in", "not_in", "like", "ilike"]>;
62
+ value: z.ZodUnion<[z.ZodString, z.ZodNumber, z.ZodBoolean, z.ZodArray<z.ZodUnion<[z.ZodString, z.ZodNumber]>, "many">]>;
63
+ }, "strip", z.ZodTypeAny, {
64
+ value: string | number | boolean | (string | number)[];
65
+ field: string;
66
+ op: "=" | "!=" | ">" | "<" | ">=" | "<=" | "in" | "not_in" | "like" | "ilike";
67
+ }, {
68
+ value: string | number | boolean | (string | number)[];
69
+ field: string;
70
+ op: "=" | "!=" | ">" | "<" | ">=" | "<=" | "in" | "not_in" | "like" | "ilike";
71
+ }>, "many">>>;
72
+ entityFilters: z.ZodDefault<z.ZodOptional<z.ZodArray<z.ZodObject<{
73
+ resolver: z.ZodString;
74
+ input: z.ZodString;
75
+ outputColumn: z.ZodString;
76
+ }, "strip", z.ZodTypeAny, {
77
+ input: string;
78
+ resolver: string;
79
+ outputColumn: string;
80
+ }, {
81
+ input: string;
82
+ resolver: string;
83
+ outputColumn: string;
84
+ }>, "many">>>;
85
+ dateRange: z.ZodOptional<z.ZodObject<{
86
+ mode: z.ZodEnum<["relative", "explicit", "latest", "none"]>;
87
+ phrase: z.ZodOptional<z.ZodString>;
88
+ startDate: z.ZodOptional<z.ZodString>;
89
+ endDate: z.ZodOptional<z.ZodString>;
90
+ }, "strip", z.ZodTypeAny, {
91
+ mode: "latest" | "none" | "relative" | "explicit";
92
+ startDate?: string | undefined;
93
+ endDate?: string | undefined;
94
+ phrase?: string | undefined;
95
+ }, {
96
+ mode: "latest" | "none" | "relative" | "explicit";
97
+ startDate?: string | undefined;
98
+ endDate?: string | undefined;
99
+ phrase?: string | undefined;
100
+ }>>;
101
+ orderBy: z.ZodDefault<z.ZodOptional<z.ZodArray<z.ZodObject<{
102
+ field: z.ZodString;
103
+ dir: z.ZodDefault<z.ZodEnum<["asc", "desc"]>>;
104
+ }, "strip", z.ZodTypeAny, {
105
+ field: string;
106
+ dir: "asc" | "desc";
107
+ }, {
108
+ field: string;
109
+ dir?: "asc" | "desc" | undefined;
110
+ }>, "many">>>;
111
+ limit: z.ZodOptional<z.ZodNumber>;
112
+ answerMode: z.ZodDefault<z.ZodOptional<z.ZodEnum<["rows", "aggregate_table", "scalar"]>>>;
113
+ }, "strip", z.ZodTypeAny, {
114
+ select: string[];
115
+ dataset: string;
116
+ metrics: string[];
117
+ groupBy: string[];
118
+ filters: {
119
+ value: string | number | boolean | (string | number)[];
120
+ field: string;
121
+ op: "=" | "!=" | ">" | "<" | ">=" | "<=" | "in" | "not_in" | "like" | "ilike";
122
+ }[];
123
+ entityFilters: {
124
+ input: string;
125
+ resolver: string;
126
+ outputColumn: string;
127
+ }[];
128
+ orderBy: {
129
+ field: string;
130
+ dir: "asc" | "desc";
131
+ }[];
132
+ answerMode: "rows" | "aggregate_table" | "scalar";
133
+ dateRange?: {
134
+ mode: "latest" | "none" | "relative" | "explicit";
135
+ startDate?: string | undefined;
136
+ endDate?: string | undefined;
137
+ phrase?: string | undefined;
138
+ } | undefined;
139
+ limit?: number | undefined;
140
+ }, {
141
+ dataset: string;
142
+ select?: string[] | undefined;
143
+ metrics?: string[] | undefined;
144
+ groupBy?: string[] | undefined;
145
+ filters?: {
146
+ value: string | number | boolean | (string | number)[];
147
+ field: string;
148
+ op: "=" | "!=" | ">" | "<" | ">=" | "<=" | "in" | "not_in" | "like" | "ilike";
149
+ }[] | undefined;
150
+ entityFilters?: {
151
+ input: string;
152
+ resolver: string;
153
+ outputColumn: string;
154
+ }[] | undefined;
155
+ dateRange?: {
156
+ mode: "latest" | "none" | "relative" | "explicit";
157
+ startDate?: string | undefined;
158
+ endDate?: string | undefined;
159
+ phrase?: string | undefined;
160
+ } | undefined;
161
+ orderBy?: {
162
+ field: string;
163
+ dir?: "asc" | "desc" | undefined;
164
+ }[] | undefined;
165
+ limit?: number | undefined;
166
+ answerMode?: "rows" | "aggregate_table" | "scalar" | undefined;
167
+ }>;
168
+ export type SmartQueryPlanInput = z.input<typeof SmartQueryPlanSchema>;
169
+ export type SmartQueryPlanOutput = z.output<typeof SmartQueryPlanSchema>;
170
+ //# sourceMappingURL=schemas.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"schemas.d.ts","sourceRoot":"","sources":["../src/schemas.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAIxB,eAAO,MAAM,YAAY;;;;;;;;;;;;EASvB,CAAC;AAIH,eAAO,MAAM,kBAAkB;;;;;;;;;;;;EAI7B,CAAC;AAIH,eAAO,MAAM,eAAe;;;;;;;;;;;;;;;EAK1B,CAAC;AAIH,eAAO,MAAM,aAAa;;;;;;;;;EAGxB,CAAC;AAIH,eAAO,MAAM,oBAAoB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAW/B,CAAC;AAIH,MAAM,MAAM,mBAAmB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,oBAAoB,CAAC,CAAC;AACvE,MAAM,MAAM,oBAAoB,GAAG,CAAC,CAAC,MAAM,CAAC,OAAO,oBAAoB,CAAC,CAAC"}