@deepagents/text2sql 0.38.1 → 1.0.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/README.md +166 -72
- package/dist/bin/sql.js +699 -0
- package/dist/index.d.ts +2 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1989 -1898
- package/dist/index.js.map +4 -4
- package/dist/lib/adapter-index.d.ts +35 -0
- package/dist/lib/adapter-index.d.ts.map +1 -0
- package/dist/lib/adapters/adapter.d.ts +1 -0
- package/dist/lib/adapters/adapter.d.ts.map +1 -1
- package/dist/lib/adapters/bigquery/index.js +24 -13
- package/dist/lib/adapters/bigquery/index.js.map +2 -2
- package/dist/lib/adapters/groundings/context.d.ts +2 -2
- package/dist/lib/adapters/groundings/context.d.ts.map +1 -1
- package/dist/lib/adapters/groundings/index.js +20 -9
- package/dist/lib/adapters/groundings/index.js.map +2 -2
- package/dist/lib/adapters/mysql/index.js +23 -12
- package/dist/lib/adapters/mysql/index.js.map +2 -2
- package/dist/lib/adapters/postgres/index.js +31 -18
- package/dist/lib/adapters/postgres/index.js.map +3 -3
- package/dist/lib/adapters/postgres/postgres.d.ts.map +1 -1
- package/dist/lib/adapters/spreadsheet/index.js +20 -9
- package/dist/lib/adapters/spreadsheet/index.js.map +2 -2
- package/dist/lib/adapters/sqlite/index.js +23 -12
- package/dist/lib/adapters/sqlite/index.js.map +2 -2
- package/dist/lib/adapters/sqlserver/index.js +31 -18
- package/dist/lib/adapters/sqlserver/index.js.map +3 -3
- package/dist/lib/adapters/sqlserver/sqlserver.d.ts.map +1 -1
- package/dist/lib/agents/question.agent.d.ts.map +1 -1
- package/dist/lib/agents/sql-transform-plugins.d.ts +3 -3
- package/dist/lib/agents/sql-transform-plugins.d.ts.map +1 -1
- package/dist/lib/agents/sql.agent.d.ts.map +1 -1
- package/dist/lib/instructions.d.ts +1 -1
- package/dist/lib/instructions.d.ts.map +1 -1
- package/dist/lib/sql-hooks.d.ts +15 -0
- package/dist/lib/sql-hooks.d.ts.map +1 -0
- package/dist/lib/sql-meta-hook.d.ts +20 -0
- package/dist/lib/sql-meta-hook.d.ts.map +1 -0
- package/dist/lib/sql-repair.d.ts +2 -0
- package/dist/lib/sql-repair.d.ts.map +1 -0
- package/dist/lib/sql-transform.d.ts +2 -0
- package/dist/lib/sql-transform.d.ts.map +1 -0
- package/dist/lib/sql.d.ts +5 -37
- package/dist/lib/sql.d.ts.map +1 -1
- package/dist/lib/synthesis/extractors/base-contextual-extractor.d.ts.map +1 -1
- package/dist/lib/synthesis/extractors/segmented-context-extractor.d.ts.map +1 -1
- package/dist/lib/synthesis/extractors/sql-extractor.d.ts.map +1 -1
- package/dist/lib/synthesis/index.js +59 -2
- package/dist/lib/synthesis/index.js.map +4 -4
- package/dist/lib/synthesis/synthesizers/breadth-evolver.d.ts.map +1 -1
- package/dist/lib/synthesis/synthesizers/depth-evolver.d.ts.map +1 -1
- package/dist/lib/synthesis/synthesizers/persona-generator.d.ts.map +1 -1
- package/package.json +13 -9
- package/dist/lib/agents/sql-command.d.ts +0 -11
- package/dist/lib/agents/sql-command.d.ts.map +0 -1
- package/dist/lib/demo.d.ts +0 -2
- package/dist/lib/demo.d.ts.map +0 -1
- package/dist/lib/sandbox.d.ts +0 -21
- package/dist/lib/sandbox.d.ts.map +0 -1
package/README.md
CHANGED
|
@@ -32,17 +32,9 @@ Requires Node.js LTS (20+).
|
|
|
32
32
|
|
|
33
33
|
```typescript
|
|
34
34
|
import { groq } from '@ai-sdk/groq';
|
|
35
|
-
import { InMemoryFs } from 'just-bash';
|
|
36
35
|
import pg from 'pg';
|
|
37
36
|
|
|
38
|
-
import {
|
|
39
|
-
ContextEngine,
|
|
40
|
-
InMemoryContextStore,
|
|
41
|
-
createBashTool,
|
|
42
|
-
createRoutingSandbox,
|
|
43
|
-
createVirtualSandbox,
|
|
44
|
-
} from '@deepagents/context';
|
|
45
|
-
import { Text2Sql, sqlSandboxExtension } from '@deepagents/text2sql';
|
|
37
|
+
import { Text2Sql } from '@deepagents/text2sql';
|
|
46
38
|
import {
|
|
47
39
|
Postgres,
|
|
48
40
|
columnValues,
|
|
@@ -58,7 +50,6 @@ const pool = new pg.Pool({
|
|
|
58
50
|
connectionString: process.env.DATABASE_URL,
|
|
59
51
|
});
|
|
60
52
|
|
|
61
|
-
const store = new InMemoryContextStore();
|
|
62
53
|
const adapter = new Postgres({
|
|
63
54
|
execute: async (sql) => {
|
|
64
55
|
const result = await pool.query(sql);
|
|
@@ -74,27 +65,11 @@ const adapter = new Postgres({
|
|
|
74
65
|
columnValues(),
|
|
75
66
|
],
|
|
76
67
|
});
|
|
77
|
-
const sandbox = await createBashTool({
|
|
78
|
-
sandbox: await createRoutingSandbox({
|
|
79
|
-
backend: await createVirtualSandbox({ fs: new InMemoryFs() }),
|
|
80
|
-
hostExtensions: [sqlSandboxExtension({ main: adapter })],
|
|
81
|
-
}),
|
|
82
|
-
});
|
|
83
68
|
|
|
84
69
|
const text2sql = new Text2Sql({
|
|
85
70
|
version: 'v1',
|
|
86
71
|
model: groq('openai/gpt-oss-20b'),
|
|
87
|
-
sandbox,
|
|
88
72
|
adapters: { main: adapter },
|
|
89
|
-
context: (...fragments) => {
|
|
90
|
-
const engine = new ContextEngine({
|
|
91
|
-
store,
|
|
92
|
-
chatId: 'chat-123',
|
|
93
|
-
userId: 'user-456',
|
|
94
|
-
});
|
|
95
|
-
engine.set(...fragments);
|
|
96
|
-
return engine;
|
|
97
|
-
},
|
|
98
73
|
});
|
|
99
74
|
|
|
100
75
|
// Generate SQL
|
|
@@ -107,33 +82,107 @@ console.log(sql);
|
|
|
107
82
|
|
|
108
83
|
The adapter-map key (`main` here) is the adapter name. Reuse that same key in
|
|
109
84
|
`text2sql.toSql(..., 'main')` and in any `sql validate <db> "..."` /
|
|
110
|
-
`sql run <db> "..."` sandbox
|
|
85
|
+
`sql run <db> "..."` calls from a sandbox where the package CLI is installed.
|
|
111
86
|
|
|
112
87
|
Adapter names must match `/^[A-Za-z_][A-Za-z0-9_]*$/`. If you build adapter
|
|
113
88
|
maps dynamically, use `isValidAdapterName(name)` to check one key or
|
|
114
89
|
`validateAdapterNames(names)` to fail fast before constructing `Text2Sql` or
|
|
115
|
-
`
|
|
90
|
+
the sandbox-side adapter module used by the `sql` CLI.
|
|
116
91
|
|
|
117
92
|
## AI Model Providers
|
|
118
93
|
|
|
119
94
|
Text2SQL works with any model provider supported by the [Vercel AI SDK](https://sdk.vercel.ai/docs), including OpenAI, Anthropic, Google, Groq, and more.
|
|
120
95
|
|
|
121
|
-
##
|
|
96
|
+
## Building a Conversational Agent
|
|
97
|
+
|
|
98
|
+
`Text2Sql` ships only the SQL-aware primitives: `toSql`, `toPairs`,
|
|
99
|
+
`AdapterIndexer`, and `instructions()`. You build the chat agent yourself by
|
|
100
|
+
composing a `ContextEngine`, a sandbox, and `agent` + `chat` from
|
|
101
|
+
`@deepagents/context`:
|
|
102
|
+
|
|
103
|
+
```typescript
|
|
104
|
+
import {
|
|
105
|
+
ContextEngine,
|
|
106
|
+
type ContextFragment,
|
|
107
|
+
InMemoryContextStore,
|
|
108
|
+
agent,
|
|
109
|
+
chat,
|
|
110
|
+
createContainerTool,
|
|
111
|
+
errorRecoveryGuardrail,
|
|
112
|
+
npm,
|
|
113
|
+
user,
|
|
114
|
+
} from '@deepagents/context';
|
|
115
|
+
import { createSqlCommandHooks, instructions } from '@deepagents/text2sql';
|
|
116
|
+
|
|
117
|
+
const store = new InMemoryContextStore();
|
|
118
|
+
const context = new ContextEngine({
|
|
119
|
+
store,
|
|
120
|
+
chatId: 'chat-123',
|
|
121
|
+
userId: 'user-456',
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
const sandbox = await createContainerTool({
|
|
125
|
+
installers: [npm('@deepagents/text2sql', { ensureRuntime: true })],
|
|
126
|
+
env: {
|
|
127
|
+
TEXT2SQL_ADAPTERS: '/workspace/text2sql-adapters.ts',
|
|
128
|
+
},
|
|
129
|
+
...createSqlCommandHooks({ adapters: { main: adapter } }),
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
const indexResult = await sandbox.sandbox.executeCommand('sql index');
|
|
133
|
+
if (indexResult.exitCode !== 0) throw new Error(indexResult.stderr);
|
|
134
|
+
const manifest = JSON.parse(indexResult.stdout) as { fragmentsPath: string };
|
|
135
|
+
const fragments = JSON.parse(
|
|
136
|
+
await sandbox.sandbox.readFile(manifest.fragmentsPath),
|
|
137
|
+
) as ContextFragment[];
|
|
138
|
+
context.set(...instructions(), ...fragments);
|
|
139
|
+
|
|
140
|
+
const ai = agent({
|
|
141
|
+
name: 'sql-assistant',
|
|
142
|
+
sandbox,
|
|
143
|
+
model: groq('openai/gpt-oss-20b'),
|
|
144
|
+
context,
|
|
145
|
+
guardrails: [errorRecoveryGuardrail],
|
|
146
|
+
maxGuardrailRetries: 3,
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
await context.continue(user('Show me the top 10 customers by revenue'));
|
|
150
|
+
const stream = await chat(ai);
|
|
122
151
|
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
152
|
+
for await (const chunk of stream) {
|
|
153
|
+
// handle streaming response
|
|
154
|
+
}
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
`instructions()` returns the SQL-flavored system fragments (policies, workflows,
|
|
158
|
+
clarifications, style guides) — spread them into `context.set()` alongside the
|
|
159
|
+
schema fragments returned by `sql index`. Add or replace them with your
|
|
160
|
+
own domain fragments as needed.
|
|
161
|
+
|
|
162
|
+
## Advanced: SQL CLI in Sandboxes
|
|
163
|
+
|
|
164
|
+
`sql validate <db> "..."` and `sql run <db> "..."` are real commands from the
|
|
165
|
+
`@deepagents/text2sql` package. `sql index [adapter ...]` writes schema
|
|
166
|
+
fragments plus progress events for chat setup. Install the package inside the
|
|
167
|
+
sandbox and set `TEXT2SQL_ADAPTERS` to a module whose default export is
|
|
168
|
+
`Record<string, Adapter>`. Missing `sql` means the sandbox was not prepared
|
|
169
|
+
correctly.
|
|
170
|
+
|
|
171
|
+
Spread `createSqlCommandHooks({ adapters })` into `createBashTool()` or
|
|
172
|
+
`createContainerTool()` for model-driven bash calls. The before hook preserves
|
|
173
|
+
the old virtual-command tolerance for common LLM quote mistakes, rewrites SQL
|
|
174
|
+
identifier backticks so bash does not run them as command substitutions, and
|
|
175
|
+
blocks raw database access so read-only and scope checks stay behind
|
|
176
|
+
`sql validate` / `sql run`. The after hook restores hidden `formattedSql`
|
|
177
|
+
metadata from the host adapter map without putting that concern into the real
|
|
178
|
+
CLI.
|
|
130
179
|
|
|
131
180
|
## Fragments
|
|
132
181
|
|
|
133
|
-
Inject domain knowledge
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
182
|
+
Inject domain knowledge by setting fragments on the `ContextEngine` you build
|
|
183
|
+
for the agent. Those fragments affect every `chat()` turn. If you need direct
|
|
184
|
+
SQL generation with extra fragments, use the lower-level `toSql({ fragments })`
|
|
185
|
+
helper exported from `@deepagents/text2sql`.
|
|
137
186
|
|
|
138
187
|
```typescript
|
|
139
188
|
import {
|
|
@@ -146,31 +195,25 @@ import {
|
|
|
146
195
|
} from '@deepagents/context';
|
|
147
196
|
|
|
148
197
|
const store = new InMemoryContextStore();
|
|
198
|
+
const context = new ContextEngine({
|
|
199
|
+
store,
|
|
200
|
+
chatId: 'chat-123',
|
|
201
|
+
userId: 'user-456',
|
|
202
|
+
});
|
|
149
203
|
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
action: 'Aggregate by department instead',
|
|
164
|
-
}),
|
|
165
|
-
example({
|
|
166
|
-
question: 'show me churned customers',
|
|
167
|
-
answer: `SELECT * FROM customers WHERE status = 'churned' ORDER BY churned_at DESC`,
|
|
168
|
-
}),
|
|
169
|
-
...fragments,
|
|
170
|
-
);
|
|
171
|
-
|
|
172
|
-
return engine;
|
|
173
|
-
};
|
|
204
|
+
context.set(
|
|
205
|
+
term('MRR', 'monthly recurring revenue'),
|
|
206
|
+
hint('Always exclude test accounts with email ending in @test.com'),
|
|
207
|
+
guardrail({
|
|
208
|
+
rule: 'Never expose individual salaries',
|
|
209
|
+
reason: 'Confidential HR data',
|
|
210
|
+
action: 'Aggregate by department instead',
|
|
211
|
+
}),
|
|
212
|
+
example({
|
|
213
|
+
question: 'show me churned customers',
|
|
214
|
+
answer: `SELECT * FROM customers WHERE status = 'churned' ORDER BY churned_at DESC`,
|
|
215
|
+
}),
|
|
216
|
+
);
|
|
174
217
|
```
|
|
175
218
|
|
|
176
219
|
**Domain fragments** (11 types): `term`, `hint`, `guardrail`, `example`, `explain`, `clarification`, `workflow`, `quirk`, `styleGuide`, `analogy`, `glossary`.
|
|
@@ -196,23 +239,75 @@ Control what schema metadata the AI receives:
|
|
|
196
239
|
|
|
197
240
|
## Conversations
|
|
198
241
|
|
|
199
|
-
`chat()`
|
|
242
|
+
`chat()` (from `@deepagents/context`) persists history through the
|
|
243
|
+
`ContextEngine` you build for the agent. Reuse the same store, `chatId`, and
|
|
244
|
+
`userId` to continue the same thread. Before each turn, append only the new
|
|
245
|
+
incoming message to the engine; do not replay earlier turns that are already
|
|
246
|
+
stored in the context store:
|
|
200
247
|
|
|
201
248
|
```typescript
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
]);
|
|
249
|
+
await context.continue(user('Show me orders from last month'));
|
|
250
|
+
const stream = await chat(ai);
|
|
205
251
|
|
|
206
252
|
for await (const chunk of stream) {
|
|
207
253
|
// handle streaming response
|
|
208
254
|
}
|
|
209
255
|
|
|
210
256
|
// Continue the same conversation
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
257
|
+
await context.continue(user('Now filter to only completed ones'));
|
|
258
|
+
const followUp = await chat(ai);
|
|
259
|
+
for await (const chunk of followUp) {
|
|
260
|
+
// handle streaming response
|
|
261
|
+
}
|
|
214
262
|
```
|
|
215
263
|
|
|
264
|
+
## Streaming Index Progress
|
|
265
|
+
|
|
266
|
+
`AdapterIndexer#index({ onProgress })` emits progress events while it warms or reads
|
|
267
|
+
the schema cache for host-side indexing. The sandbox CLI writes the same event
|
|
268
|
+
shape to the `eventsPath` file returned by `sql index`:
|
|
269
|
+
|
|
270
|
+
```typescript
|
|
271
|
+
import { createUIMessageStream } from 'ai';
|
|
272
|
+
|
|
273
|
+
import {
|
|
274
|
+
AdapterIndexer,
|
|
275
|
+
TEXT2SQL_INDEX_PROGRESS_CHUNK,
|
|
276
|
+
type Text2SqlIndexProgressEvent,
|
|
277
|
+
} from '@deepagents/text2sql';
|
|
278
|
+
|
|
279
|
+
const indexer = new AdapterIndexer({ adapters, version: 'v1' });
|
|
280
|
+
|
|
281
|
+
await context.continue(user('Show me top 10 customers'));
|
|
282
|
+
|
|
283
|
+
const stream = createUIMessageStream({
|
|
284
|
+
execute: async ({ writer }) => {
|
|
285
|
+
const head = await context.headMessage();
|
|
286
|
+
if (!head || head.name !== 'assistant') {
|
|
287
|
+
throw new Error(
|
|
288
|
+
'expected head to be an assistant message — call context.continue() before chat()',
|
|
289
|
+
);
|
|
290
|
+
}
|
|
291
|
+
writer.write({ type: 'start', messageId: head.id });
|
|
292
|
+
|
|
293
|
+
const fragments = await indexer.index({
|
|
294
|
+
onProgress: (event) =>
|
|
295
|
+
writer.write({
|
|
296
|
+
type: TEXT2SQL_INDEX_PROGRESS_CHUNK,
|
|
297
|
+
data: event,
|
|
298
|
+
}),
|
|
299
|
+
});
|
|
300
|
+
|
|
301
|
+
context.set(...instructions(), ...fragments);
|
|
302
|
+
writer.merge(await chat(ai));
|
|
303
|
+
},
|
|
304
|
+
});
|
|
305
|
+
```
|
|
306
|
+
|
|
307
|
+
Each progress event includes a generic Unix epoch `timestampMs` so clients can
|
|
308
|
+
calculate per-index, per-adapter, or per-phase durations without Text2SQL
|
|
309
|
+
imposing one duration policy.
|
|
310
|
+
|
|
216
311
|
`toSql()` is stateless. It reads the current schema fragments, but it does not
|
|
217
312
|
write messages, titles, or usage into your context store.
|
|
218
313
|
|
|
@@ -244,7 +339,6 @@ Full documentation available at [januarylabs.github.io/deepagents](https://janua
|
|
|
244
339
|
|
|
245
340
|
- [Getting Started](https://januarylabs.github.io/deepagents/docs/text2sql/getting-started)
|
|
246
341
|
- [Generate SQL](https://januarylabs.github.io/deepagents/docs/text2sql/to-sql)
|
|
247
|
-
- [Caller-Owned Sandbox](https://januarylabs.github.io/deepagents/docs/text2sql/sqlv3)
|
|
248
342
|
- [Teach the System](https://januarylabs.github.io/deepagents/docs/text2sql/teach-the-system)
|
|
249
343
|
- [Build Conversations](https://januarylabs.github.io/deepagents/docs/text2sql/build-conversations)
|
|
250
344
|
- [Grounding](https://januarylabs.github.io/deepagents/docs/text2sql/grounding)
|