@deepagents/text2sql 0.39.0 → 2.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/README.md +157 -74
- package/dist/bin/sql.js +795 -0
- package/dist/index.d.ts +2 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3376 -3309
- 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/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 -38
- 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 +9 -5
- 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
|
@@ -26,24 +26,15 @@ npm install mysql2 # MySQL / MariaDB
|
|
|
26
26
|
npm install @google-cloud/bigquery # BigQuery
|
|
27
27
|
```
|
|
28
28
|
|
|
29
|
-
Requires Node.js LTS
|
|
29
|
+
Requires Node.js LTS
|
|
30
30
|
|
|
31
31
|
## Quick Start
|
|
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
|
-
user,
|
|
45
|
-
} from '@deepagents/context';
|
|
46
|
-
import { Text2Sql, sqlSandboxExtension } from '@deepagents/text2sql';
|
|
37
|
+
import { Text2Sql } from '@deepagents/text2sql';
|
|
47
38
|
import {
|
|
48
39
|
Postgres,
|
|
49
40
|
columnValues,
|
|
@@ -59,12 +50,6 @@ const pool = new pg.Pool({
|
|
|
59
50
|
connectionString: process.env.DATABASE_URL,
|
|
60
51
|
});
|
|
61
52
|
|
|
62
|
-
const store = new InMemoryContextStore();
|
|
63
|
-
const engine = new ContextEngine({
|
|
64
|
-
store,
|
|
65
|
-
chatId: 'chat-123',
|
|
66
|
-
userId: 'user-456',
|
|
67
|
-
});
|
|
68
53
|
const adapter = new Postgres({
|
|
69
54
|
execute: async (sql) => {
|
|
70
55
|
const result = await pool.query(sql);
|
|
@@ -80,19 +65,11 @@ const adapter = new Postgres({
|
|
|
80
65
|
columnValues(),
|
|
81
66
|
],
|
|
82
67
|
});
|
|
83
|
-
const sandbox = await createBashTool({
|
|
84
|
-
sandbox: await createRoutingSandbox({
|
|
85
|
-
backend: await createVirtualSandbox({ fs: new InMemoryFs() }),
|
|
86
|
-
hostExtensions: [sqlSandboxExtension({ main: adapter })],
|
|
87
|
-
}),
|
|
88
|
-
});
|
|
89
68
|
|
|
90
69
|
const text2sql = new Text2Sql({
|
|
91
70
|
version: 'v1',
|
|
92
71
|
model: groq('openai/gpt-oss-20b'),
|
|
93
|
-
sandbox,
|
|
94
72
|
adapters: { main: adapter },
|
|
95
|
-
context: engine,
|
|
96
73
|
});
|
|
97
74
|
|
|
98
75
|
// Generate SQL
|
|
@@ -105,32 +82,131 @@ console.log(sql);
|
|
|
105
82
|
|
|
106
83
|
The adapter-map key (`main` here) is the adapter name. Reuse that same key in
|
|
107
84
|
`text2sql.toSql(..., 'main')` and in any `sql validate <db> "..."` /
|
|
108
|
-
`sql run <db> "..."` sandbox
|
|
85
|
+
`sql run <db> "..."` calls from a sandbox where the package CLI is installed.
|
|
109
86
|
|
|
110
87
|
Adapter names must match `/^[A-Za-z_][A-Za-z0-9_]*$/`. If you build adapter
|
|
111
88
|
maps dynamically, use `isValidAdapterName(name)` to check one key or
|
|
112
89
|
`validateAdapterNames(names)` to fail fast before constructing `Text2Sql` or
|
|
113
|
-
`
|
|
90
|
+
the sandbox-side adapter module used by the `sql` CLI.
|
|
114
91
|
|
|
115
92
|
## AI Model Providers
|
|
116
93
|
|
|
117
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.
|
|
118
95
|
|
|
119
|
-
##
|
|
96
|
+
## Building a Conversational Agent
|
|
97
|
+
|
|
98
|
+
`Text2Sql` owns SQL generation and schema indexing, but you own the chat loop.
|
|
99
|
+
Build the agent by composing a `ContextEngine`, a sandbox, and `agent` +
|
|
100
|
+
`chat` from `@deepagents/context`:
|
|
101
|
+
|
|
102
|
+
```typescript
|
|
103
|
+
import {
|
|
104
|
+
ContextEngine,
|
|
105
|
+
type ContextFragment,
|
|
106
|
+
InMemoryContextStore,
|
|
107
|
+
agent,
|
|
108
|
+
chat,
|
|
109
|
+
createContainerTool,
|
|
110
|
+
errorRecoveryGuardrail,
|
|
111
|
+
npm,
|
|
112
|
+
user,
|
|
113
|
+
} from '@deepagents/context';
|
|
114
|
+
import { createSqlCommandHooks, instructions } from '@deepagents/text2sql';
|
|
115
|
+
|
|
116
|
+
const store = new InMemoryContextStore();
|
|
117
|
+
const context = new ContextEngine({
|
|
118
|
+
store,
|
|
119
|
+
chatId: 'chat-123',
|
|
120
|
+
userId: 'user-456',
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
const sandbox = await createContainerTool({
|
|
124
|
+
installers: [npm('@deepagents/text2sql', { ensureRuntime: true })],
|
|
125
|
+
volumes: [
|
|
126
|
+
{
|
|
127
|
+
type: 'bind',
|
|
128
|
+
hostPath: process.cwd(),
|
|
129
|
+
containerPath: '/workspace',
|
|
130
|
+
readOnly: true,
|
|
131
|
+
},
|
|
132
|
+
],
|
|
133
|
+
env: {
|
|
134
|
+
TEXT2SQL_ADAPTERS: '/workspace/text2sql-adapters.ts',
|
|
135
|
+
},
|
|
136
|
+
...createSqlCommandHooks({ adapters: { main: adapter } }),
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
const indexResult = await sandbox.sandbox.executeCommand('sql index');
|
|
140
|
+
if (indexResult.exitCode !== 0) throw new Error(indexResult.stderr);
|
|
141
|
+
const manifest = JSON.parse(indexResult.stdout) as { fragmentsPath: string };
|
|
142
|
+
const fragments = JSON.parse(
|
|
143
|
+
await sandbox.sandbox.readFile(manifest.fragmentsPath),
|
|
144
|
+
) as ContextFragment[];
|
|
145
|
+
context.set(...instructions(), ...fragments);
|
|
146
|
+
|
|
147
|
+
const ai = agent({
|
|
148
|
+
name: 'sql-assistant',
|
|
149
|
+
sandbox,
|
|
150
|
+
model: groq('openai/gpt-oss-20b'),
|
|
151
|
+
context,
|
|
152
|
+
guardrails: [errorRecoveryGuardrail],
|
|
153
|
+
maxGuardrailRetries: 3,
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
await context.continue(user('Show me the top 10 customers by revenue'));
|
|
157
|
+
const stream = await chat(ai);
|
|
158
|
+
|
|
159
|
+
for await (const chunk of stream) {
|
|
160
|
+
// handle streaming response
|
|
161
|
+
}
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
The `/workspace/text2sql-adapters.ts` module must exist in the sandbox and
|
|
165
|
+
default-export your adapter map. Mount your project into `/workspace` (as above)
|
|
166
|
+
or upload/write that module before you call `sql index` or `chat()`.
|
|
167
|
+
|
|
168
|
+
`instructions()` returns the SQL-flavored system fragments (policies, workflows,
|
|
169
|
+
clarifications, style guides) — spread them into `context.set()` alongside the
|
|
170
|
+
schema fragments returned by `sql index`. Add or replace them with your
|
|
171
|
+
own domain fragments as needed.
|
|
172
|
+
|
|
173
|
+
## Advanced: SQL CLI in Sandboxes
|
|
120
174
|
|
|
121
|
-
`
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
`
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
175
|
+
`sql validate <db> "..."` and `sql run <db> "..."` are real commands from the
|
|
176
|
+
`@deepagents/text2sql` package. `sql index` writes schema fragments plus
|
|
177
|
+
progress events for chat setup, indexing all configured adapters by default
|
|
178
|
+
(same as `--all`) unless adapter names are provided.
|
|
179
|
+
|
|
180
|
+
Install the package inside the sandbox and set `TEXT2SQL_ADAPTERS` to a module
|
|
181
|
+
whose default export is `Record<string, Adapter>`. Missing `sql` means the
|
|
182
|
+
sandbox was not prepared correctly.
|
|
183
|
+
|
|
184
|
+
`sql index` output details:
|
|
185
|
+
|
|
186
|
+
- `stdout`: JSON manifest with `fragmentsPath`, `eventsPath`, adapters, and
|
|
187
|
+
fragment count.
|
|
188
|
+
- `--verbose pretty` or `--verbose json`: mirrors progress events to `stderr`
|
|
189
|
+
while keeping `stdout` as the manifest.
|
|
190
|
+
- `--out-dir <path>`: writes artifacts under that path (default:
|
|
191
|
+
`$TEXT2SQL_OUT_DIR` or `./sql`).
|
|
192
|
+
|
|
193
|
+
Set `TEXT2SQL_INDEX_VERSION` to manage cache invalidation across runs. Cache
|
|
194
|
+
keys are `index-<version>-<adapter>`, so bump the version when schema changes.
|
|
195
|
+
|
|
196
|
+
Spread `createSqlCommandHooks({ adapters })` into `createBashTool()` or
|
|
197
|
+
`createContainerTool()` for model-driven bash calls. The before hook preserves
|
|
198
|
+
the old virtual-command tolerance for common LLM quote mistakes, rewrites SQL
|
|
199
|
+
identifier backticks so bash does not run them as command substitutions, and
|
|
200
|
+
blocks raw database access so read-only and scope checks stay behind
|
|
201
|
+
`sql validate` / `sql run`. The after hook restores hidden `formattedSql`
|
|
202
|
+
metadata from the host adapter map without putting that concern into the real
|
|
203
|
+
CLI.
|
|
128
204
|
|
|
129
205
|
## Fragments
|
|
130
206
|
|
|
131
|
-
Inject domain knowledge by setting fragments on the `ContextEngine` you
|
|
132
|
-
|
|
133
|
-
generation with extra fragments, use the lower-level `toSql({ fragments })`
|
|
207
|
+
Inject domain knowledge by setting fragments on the `ContextEngine` you build
|
|
208
|
+
for the agent. Those fragments affect every `chat()` turn. If you need direct
|
|
209
|
+
SQL generation with extra fragments, use the lower-level `toSql({ fragments })`
|
|
134
210
|
helper exported from `@deepagents/text2sql`.
|
|
135
211
|
|
|
136
212
|
```typescript
|
|
@@ -144,13 +220,13 @@ import {
|
|
|
144
220
|
} from '@deepagents/context';
|
|
145
221
|
|
|
146
222
|
const store = new InMemoryContextStore();
|
|
147
|
-
const
|
|
223
|
+
const context = new ContextEngine({
|
|
148
224
|
store,
|
|
149
225
|
chatId: 'chat-123',
|
|
150
226
|
userId: 'user-456',
|
|
151
227
|
});
|
|
152
228
|
|
|
153
|
-
|
|
229
|
+
context.set(
|
|
154
230
|
term('MRR', 'monthly recurring revenue'),
|
|
155
231
|
hint('Always exclude test accounts with email ending in @test.com'),
|
|
156
232
|
guardrail({
|
|
@@ -188,62 +264,69 @@ Control what schema metadata the AI receives:
|
|
|
188
264
|
|
|
189
265
|
## Conversations
|
|
190
266
|
|
|
191
|
-
`chat()` persists history through the
|
|
192
|
-
Reuse the same store, `chatId`, and
|
|
193
|
-
Before each turn, append only the new
|
|
194
|
-
replay earlier turns that are already
|
|
267
|
+
`chat()` (from `@deepagents/context`) persists history through the
|
|
268
|
+
`ContextEngine` you build for the agent. Reuse the same store, `chatId`, and
|
|
269
|
+
`userId` to continue the same thread. Before each turn, append only the new
|
|
270
|
+
incoming message to the engine; do not replay earlier turns that are already
|
|
271
|
+
stored in the context store:
|
|
195
272
|
|
|
196
273
|
```typescript
|
|
197
|
-
await
|
|
198
|
-
const stream = await
|
|
274
|
+
await context.continue(user('Show me orders from last month'));
|
|
275
|
+
const stream = await chat(ai);
|
|
199
276
|
|
|
200
277
|
for await (const chunk of stream) {
|
|
201
278
|
// handle streaming response
|
|
202
279
|
}
|
|
203
280
|
|
|
204
281
|
// Continue the same conversation
|
|
205
|
-
await
|
|
206
|
-
const followUp = await
|
|
282
|
+
await context.continue(user('Now filter to only completed ones'));
|
|
283
|
+
const followUp = await chat(ai);
|
|
207
284
|
for await (const chunk of followUp) {
|
|
208
285
|
// handle streaming response
|
|
209
286
|
}
|
|
210
287
|
```
|
|
211
288
|
|
|
212
|
-
|
|
213
|
-
|
|
289
|
+
## Streaming Index Progress
|
|
290
|
+
|
|
291
|
+
`AdapterIndexer#index({ onProgress })` emits progress events while it warms or reads
|
|
292
|
+
the schema cache for host-side indexing. The sandbox CLI writes the same event
|
|
293
|
+
shape to the `eventsPath` file returned by `sql index`:
|
|
214
294
|
|
|
215
295
|
```typescript
|
|
296
|
+
import { createUIMessageStream } from 'ai';
|
|
297
|
+
|
|
216
298
|
import {
|
|
299
|
+
AdapterIndexer,
|
|
217
300
|
TEXT2SQL_INDEX_PROGRESS_CHUNK,
|
|
218
301
|
type Text2SqlIndexProgressEvent,
|
|
219
302
|
} from '@deepagents/text2sql';
|
|
220
303
|
|
|
221
|
-
|
|
222
|
-
const stream = await text2sql.chat();
|
|
223
|
-
const startedAt = new Map<string, number>();
|
|
304
|
+
const indexer = new AdapterIndexer({ adapters, version: 'v1' });
|
|
224
305
|
|
|
225
|
-
|
|
226
|
-
if (chunk.type === TEXT2SQL_INDEX_PROGRESS_CHUNK) {
|
|
227
|
-
const event = chunk.data as Text2SqlIndexProgressEvent;
|
|
228
|
-
const scope =
|
|
229
|
-
[event.adapter, event.phase].filter(Boolean).join(':') || 'index';
|
|
306
|
+
await context.continue(user('Show me top 10 customers'));
|
|
230
307
|
|
|
231
|
-
|
|
232
|
-
|
|
308
|
+
const stream = createUIMessageStream({
|
|
309
|
+
execute: async ({ writer }) => {
|
|
310
|
+
const head = await context.headMessage();
|
|
311
|
+
if (!head || head.name !== 'assistant') {
|
|
312
|
+
throw new Error(
|
|
313
|
+
'expected head to be an assistant message — call context.continue() before chat()',
|
|
314
|
+
);
|
|
233
315
|
}
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
}
|
|
316
|
+
writer.write({ type: 'start', messageId: head.id });
|
|
317
|
+
|
|
318
|
+
const fragments = await indexer.index({
|
|
319
|
+
onProgress: (event) =>
|
|
320
|
+
writer.write({
|
|
321
|
+
type: TEXT2SQL_INDEX_PROGRESS_CHUNK,
|
|
322
|
+
data: event,
|
|
323
|
+
}),
|
|
324
|
+
});
|
|
325
|
+
|
|
326
|
+
context.set(...instructions(), ...fragments);
|
|
327
|
+
writer.merge(await chat(ai));
|
|
328
|
+
},
|
|
329
|
+
});
|
|
247
330
|
```
|
|
248
331
|
|
|
249
332
|
Each progress event includes a generic Unix epoch `timestampMs` so clients can
|
|
@@ -281,7 +364,7 @@ Full documentation available at [januarylabs.github.io/deepagents](https://janua
|
|
|
281
364
|
|
|
282
365
|
- [Getting Started](https://januarylabs.github.io/deepagents/docs/text2sql/getting-started)
|
|
283
366
|
- [Generate SQL](https://januarylabs.github.io/deepagents/docs/text2sql/to-sql)
|
|
284
|
-
- [
|
|
367
|
+
- [Agent + SQLite Store Recipe](https://januarylabs.github.io/deepagents/docs/text2sql/recipes/agent-sqlite-store)
|
|
285
368
|
- [Teach the System](https://januarylabs.github.io/deepagents/docs/text2sql/teach-the-system)
|
|
286
369
|
- [Build Conversations](https://januarylabs.github.io/deepagents/docs/text2sql/build-conversations)
|
|
287
370
|
- [Grounding](https://januarylabs.github.io/deepagents/docs/text2sql/grounding)
|