@negusdev/dbask 0.0.0-stage → 0.1.8

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 (161) hide show
  1. package/CHANGELOG.md +40 -0
  2. package/LICENSE +15 -0
  3. package/README.md +422 -2
  4. package/dist/ai/factory.d.ts +4 -0
  5. package/dist/ai/factory.d.ts.map +1 -0
  6. package/dist/ai/factory.js +55 -0
  7. package/dist/ai/factory.js.map +1 -0
  8. package/dist/ai/provider.d.ts +13 -0
  9. package/dist/ai/provider.d.ts.map +1 -0
  10. package/dist/ai/provider.js +2 -0
  11. package/dist/ai/provider.js.map +1 -0
  12. package/dist/ai/sdk-provider.d.ts +13 -0
  13. package/dist/ai/sdk-provider.d.ts.map +1 -0
  14. package/dist/ai/sdk-provider.js +36 -0
  15. package/dist/ai/sdk-provider.js.map +1 -0
  16. package/dist/cli-args.d.ts +26 -0
  17. package/dist/cli-args.d.ts.map +1 -0
  18. package/dist/cli-args.js +72 -0
  19. package/dist/cli-args.js.map +1 -0
  20. package/dist/cli-observer.d.ts +23 -0
  21. package/dist/cli-observer.d.ts.map +1 -0
  22. package/dist/cli-observer.js +224 -0
  23. package/dist/cli-observer.js.map +1 -0
  24. package/dist/cli.d.ts +3 -0
  25. package/dist/cli.d.ts.map +1 -0
  26. package/dist/cli.js +127 -0
  27. package/dist/cli.js.map +1 -0
  28. package/dist/commands/init.d.ts +2 -0
  29. package/dist/commands/init.d.ts.map +1 -0
  30. package/dist/commands/init.js +247 -0
  31. package/dist/commands/init.js.map +1 -0
  32. package/dist/commands/validate.d.ts +31 -0
  33. package/dist/commands/validate.d.ts.map +1 -0
  34. package/dist/commands/validate.js +205 -0
  35. package/dist/commands/validate.js.map +1 -0
  36. package/dist/config/config.d.ts +42 -0
  37. package/dist/config/config.d.ts.map +1 -0
  38. package/dist/config/config.js +94 -0
  39. package/dist/config/config.js.map +1 -0
  40. package/dist/context/db-context.d.ts +12 -0
  41. package/dist/context/db-context.d.ts.map +1 -0
  42. package/dist/context/db-context.js +29 -0
  43. package/dist/context/db-context.js.map +1 -0
  44. package/dist/core/agent/agent.d.ts +57 -0
  45. package/dist/core/agent/agent.d.ts.map +1 -0
  46. package/dist/core/agent/agent.js +518 -0
  47. package/dist/core/agent/agent.js.map +1 -0
  48. package/dist/core/agent/index.d.ts +4 -0
  49. package/dist/core/agent/index.d.ts.map +1 -0
  50. package/dist/core/agent/index.js +2 -0
  51. package/dist/core/agent/index.js.map +1 -0
  52. package/dist/core/agent/observability.d.ts +19 -0
  53. package/dist/core/agent/observability.d.ts.map +1 -0
  54. package/dist/core/agent/observability.js +4 -0
  55. package/dist/core/agent/observability.js.map +1 -0
  56. package/dist/core/agent/query-plan.d.ts +68 -0
  57. package/dist/core/agent/query-plan.d.ts.map +1 -0
  58. package/dist/core/agent/query-plan.js +282 -0
  59. package/dist/core/agent/query-plan.js.map +1 -0
  60. package/dist/core/tools/describe-table.d.ts +6 -0
  61. package/dist/core/tools/describe-table.d.ts.map +1 -0
  62. package/dist/core/tools/describe-table.js +25 -0
  63. package/dist/core/tools/describe-table.js.map +1 -0
  64. package/dist/core/tools/describe-tables.d.ts +6 -0
  65. package/dist/core/tools/describe-tables.d.ts.map +1 -0
  66. package/dist/core/tools/describe-tables.js +22 -0
  67. package/dist/core/tools/describe-tables.js.map +1 -0
  68. package/dist/core/tools/execute-query.d.ts +6 -0
  69. package/dist/core/tools/execute-query.d.ts.map +1 -0
  70. package/dist/core/tools/execute-query.js +31 -0
  71. package/dist/core/tools/execute-query.js.map +1 -0
  72. package/dist/core/tools/explain-query.d.ts +10 -0
  73. package/dist/core/tools/explain-query.d.ts.map +1 -0
  74. package/dist/core/tools/explain-query.js +34 -0
  75. package/dist/core/tools/explain-query.js.map +1 -0
  76. package/dist/core/tools/get-relationships.d.ts +6 -0
  77. package/dist/core/tools/get-relationships.d.ts.map +1 -0
  78. package/dist/core/tools/get-relationships.js +23 -0
  79. package/dist/core/tools/get-relationships.js.map +1 -0
  80. package/dist/core/tools/index.d.ts +9 -0
  81. package/dist/core/tools/index.d.ts.map +1 -0
  82. package/dist/core/tools/index.js +8 -0
  83. package/dist/core/tools/index.js.map +1 -0
  84. package/dist/core/tools/list-tables.d.ts +3 -0
  85. package/dist/core/tools/list-tables.d.ts.map +1 -0
  86. package/dist/core/tools/list-tables.js +18 -0
  87. package/dist/core/tools/list-tables.js.map +1 -0
  88. package/dist/core/tools/registry.d.ts +11 -0
  89. package/dist/core/tools/registry.d.ts.map +1 -0
  90. package/dist/core/tools/registry.js +48 -0
  91. package/dist/core/tools/registry.js.map +1 -0
  92. package/dist/core/types/index.d.ts +102 -0
  93. package/dist/core/types/index.d.ts.map +1 -0
  94. package/dist/core/types/index.js +6 -0
  95. package/dist/core/types/index.js.map +1 -0
  96. package/dist/database/adapter.d.ts +3 -0
  97. package/dist/database/adapter.d.ts.map +1 -0
  98. package/dist/database/adapter.js +2 -0
  99. package/dist/database/adapter.js.map +1 -0
  100. package/dist/database/factory.d.ts +16 -0
  101. package/dist/database/factory.d.ts.map +1 -0
  102. package/dist/database/factory.js +27 -0
  103. package/dist/database/factory.js.map +1 -0
  104. package/dist/database/index.d.ts +8 -0
  105. package/dist/database/index.d.ts.map +1 -0
  106. package/dist/database/index.js +4 -0
  107. package/dist/database/index.js.map +1 -0
  108. package/dist/database/mysql/adapter.d.ts +26 -0
  109. package/dist/database/mysql/adapter.d.ts.map +1 -0
  110. package/dist/database/mysql/adapter.js +131 -0
  111. package/dist/database/mysql/adapter.js.map +1 -0
  112. package/dist/database/postgres/adapter.d.ts +60 -0
  113. package/dist/database/postgres/adapter.d.ts.map +1 -0
  114. package/dist/database/postgres/adapter.js +307 -0
  115. package/dist/database/postgres/adapter.js.map +1 -0
  116. package/dist/database/result-limits.d.ts +17 -0
  117. package/dist/database/result-limits.d.ts.map +1 -0
  118. package/dist/database/result-limits.js +26 -0
  119. package/dist/database/result-limits.js.map +1 -0
  120. package/dist/index.d.ts +20 -0
  121. package/dist/index.d.ts.map +1 -0
  122. package/dist/index.js +13 -0
  123. package/dist/index.js.map +1 -0
  124. package/dist/safety/limits.d.ts +7 -0
  125. package/dist/safety/limits.d.ts.map +1 -0
  126. package/dist/safety/limits.js +15 -0
  127. package/dist/safety/limits.js.map +1 -0
  128. package/dist/safety/policy.d.ts +8 -0
  129. package/dist/safety/policy.d.ts.map +1 -0
  130. package/dist/safety/policy.js +11 -0
  131. package/dist/safety/policy.js.map +1 -0
  132. package/dist/safety/sql-tokenizer.d.ts +49 -0
  133. package/dist/safety/sql-tokenizer.d.ts.map +1 -0
  134. package/dist/safety/sql-tokenizer.js +240 -0
  135. package/dist/safety/sql-tokenizer.js.map +1 -0
  136. package/dist/safety/sql-validator.d.ts +34 -0
  137. package/dist/safety/sql-validator.d.ts.map +1 -0
  138. package/dist/safety/sql-validator.js +270 -0
  139. package/dist/safety/sql-validator.js.map +1 -0
  140. package/dist/schema/cache.d.ts +24 -0
  141. package/dist/schema/cache.d.ts.map +1 -0
  142. package/dist/schema/cache.js +57 -0
  143. package/dist/schema/cache.js.map +1 -0
  144. package/dist/ui/markdown.d.ts +8 -0
  145. package/dist/ui/markdown.d.ts.map +1 -0
  146. package/dist/ui/markdown.js +205 -0
  147. package/dist/ui/markdown.js.map +1 -0
  148. package/dist/ui/spinner.d.ts +32 -0
  149. package/dist/ui/spinner.d.ts.map +1 -0
  150. package/dist/ui/spinner.js +74 -0
  151. package/dist/ui/spinner.js.map +1 -0
  152. package/dist/ui/sql-highlight.d.ts +4 -0
  153. package/dist/ui/sql-highlight.d.ts.map +1 -0
  154. package/dist/ui/sql-highlight.js +28 -0
  155. package/dist/ui/sql-highlight.js.map +1 -0
  156. package/dist/ui/style.d.ts +38 -0
  157. package/dist/ui/style.d.ts.map +1 -0
  158. package/dist/ui/style.js +69 -0
  159. package/dist/ui/style.js.map +1 -0
  160. package/package.json +91 -4
  161. package/sql-ai.example.md +32 -0
package/CHANGELOG.md ADDED
@@ -0,0 +1,40 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project are documented here. The format follows [Keep a Changelog](https://keepachangelog.com), and the project uses [Semantic Versioning](https://semver.org) (0.x: minor versions may change behavior).
4
+
5
+ ## [Unreleased]
6
+
7
+ ### Changed
8
+ - **Renamed from `sql-ai-harness` to `dbask`.** The npm package is now `@negusdev/dbask` (the unscoped name `dbask` was blocked by npm's name-similarity check) and the command is `dbask`. The `sql-ai-harness` command stays as an alias for now. You can now ask without the subcommand: `dbask "who owes us money?"` (`dbask ask "…"` still works). Mistyped options and one-word mistyped commands now print an error instead of being sent to the model, and `dbask --version` and `--help` were added. Environment variables, the `sql-ai.md` context file and the SDK API are unchanged; update imports from `"sql-ai-harness"` to `"dbask"`. The structured debug events now use `source: "dbask"`.
9
+
10
+ ## [0.1.8] - 2026-11-13
11
+
12
+ ### Added
13
+ - **DeepSeek provider.** Set `AI_PROVIDER=deepseek` and `DEEPSEEK_API_KEY`.
14
+ - **Optional query plan** (`AGENT_QUERY_PLAN=on`, or `requirePlan: true` in the SDK). The agent submits a structured plan (tables, columns, joins, filters) that is checked against your real schema before any SQL runs; the SQL may then read only the planned tables. Unknown tables or columns and unconnected tables are rejected, joins that are not declared foreign keys produce warnings, and plan submissions are capped at 4. `AgentResult.plan` reports submitted, accepted, rejected and SQL-mismatch counts. New exports: `StructuredQueryPlan`, `PlanStats`.
15
+ - **Terminal UI.** Progress spinner with the current step, Markdown answers rendered for the terminal (aligned tables that wrap instead of cutting ids, bold, code, lists), highlighted SQL, and a clearer `--mode debug` trace that shows the plan, plan warnings and plan counts. `validate` uses symbols and color. Piped or redirected output is unchanged; `NO_COLOR` and `FORCE_COLOR` are honored.
16
+ - `CONTRIBUTING.md`.
17
+
18
+ ### Changed
19
+ - **The query plan is off by default.** On the 40-case benchmark it added about 0.7 tool calls and roughly 30% median latency per question without improving accuracy. Turn it on with `AGENT_QUERY_PLAN=on` if you want the agent's assumptions visible. (SDK: `new Agent(..., { requirePlan: true })`; the SDK default is also off.)
20
+ - **The agent stays on topic.** It now declines questions that are not about the connected database (arithmetic, general knowledge, writing, instruction overrides) without calling tools or querying, and treats text found in table names, column names or row values as data, not instructions.
21
+ - **The agent always ends with an answer.** The last step is reserved for the final answer, and a run that cannot finish returns `AgentResult.incomplete`; the CLI prints it as an error and exits with code 1. Write requests are refused up front.
22
+ - Updated the Vercel AI SDK packages.
23
+
24
+ ### Development
25
+ - Evaluation: 40-case dataset (adds four off-topic cases), value-based result comparison, tolerant answer checks, refusal scoring, provider-error retries excluded from scores, `--repeat N` with a cross-run summary, `--only`, process-group timeout kill, and run settings (model, commit, max steps, plan flag) in report headers.
26
+ - Benchmark result for this release: 39 of 40 cases passed in every one of three runs (`gemini-3.5-flash-lite`, synthetic database). The one consistent failure is a known reference-answer error, not an agent error. See the README for caveats.
27
+
28
+ ### Upgrade notes
29
+ - No action needed for the CLI. If you script `submit_query_plan` directly, the plan shape changed: `fields` is a list of `{table, column}` and `joins` a list of `{left, right}`.
30
+
31
+ ## [0.1.7] - 2026-11-05
32
+
33
+ ### Added
34
+ - PostgreSQL support (tables, partitioned tables and schemas; views are not listed yet).
35
+ - `validate` command to check configuration, database connection, foreign keys, AI provider and `sql-ai.md`.
36
+
37
+ For earlier releases, see the [commit history](https://github.com/NegusDev/sql-ai-harness/commits/main) and the npm release list.
38
+
39
+ [0.1.8]: https://github.com/NegusDev/sql-ai-harness/compare/v0.1.7...v0.1.8
40
+ [0.1.7]: https://github.com/NegusDev/sql-ai-harness/releases/tag/v0.1.7
package/LICENSE ADDED
@@ -0,0 +1,15 @@
1
+ ISC License
2
+
3
+ Copyright (c) 2026, NegusDev
4
+
5
+ Permission to use, copy, modify, and/or distribute this software for any
6
+ purpose with or without fee is hereby granted, provided that the above
7
+ copyright notice and this permission notice appear in all copies.
8
+
9
+ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
10
+ REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
11
+ AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
12
+ INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
13
+ LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
14
+ OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
15
+ PERFORMANCE OF THIS SOFTWARE.
package/README.md CHANGED
@@ -1,3 +1,423 @@
1
- # Temporary Holding Version
1
+ # dbask
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ **Open-source, privacy-first, self-hostable Text-to-SQL for your existing relational database.**
4
+
5
+ Ask questions in plain language about a MySQL, MariaDB, or PostgreSQL database you already have. dbask discovers the schema, gives an AI agent tools to inspect and query it, enforces execution limits, and returns an answer. No new schema, no model trained for your app, no hosted service.
6
+
7
+ Bring your own model. Run it yourself.
8
+
9
+ > dbask was previously published as `sql-ai-harness`. Install it as `@negusdev/dbask`; the command is `dbask`. The old `sql-ai-harness` command still works as an alias, and the SDK API is unchanged apart from the package name.
10
+
11
+ [![npm](https://img.shields.io/npm/v/@negusdev/dbask.svg)](https://www.npmjs.com/package/@negusdev/dbask)
12
+ [![node](https://img.shields.io/node/v/@negusdev/dbask.svg)](https://nodejs.org)
13
+ [![license](https://img.shields.io/npm/l/@negusdev/dbask.svg)](./LICENSE)
14
+
15
+ > **Development status:** dbask is under active development (pre-1.0, so the API may change between minor versions). Validate it against your own schema and workload before relying on it, especially for consequential decisions.
16
+
17
+ ## Contents
18
+
19
+ - [Why dbask](#why-dbask)
20
+ - [Features](#features)
21
+ - [Requirements](#requirements)
22
+ - [Quickstart](#quickstart)
23
+ - [Data handling](#data-handling)
24
+ - [Usage](#usage)
25
+ - [Configuration](#configuration)
26
+ - [Optional database context](#optional-database-context)
27
+ - [TypeScript SDK](#typescript-sdk)
28
+ - [Database tools](#database-tools)
29
+ - [Safety and data handling](#safety-and-data-handling)
30
+ - [Database support](#database-support)
31
+ - [Evaluation](#evaluation)
32
+ - [Development](#development)
33
+ - [Troubleshooting](#troubleshooting)
34
+ - [Contributing](#contributing)
35
+ - [License](#license)
36
+
37
+ ## Why dbask
38
+
39
+ Most Text-to-SQL tools are either a hosted service that sees your schema and data, or tightly coupled to one model and one vendor. dbask is a harness you run yourself, against a database you already own, with a model you choose.
40
+
41
+ It is a reusable layer between an AI provider and an existing relational database. It is not a new application with its own schema.
42
+
43
+ ## Features
44
+
45
+ - **Natural-language questions** through the CLI or the TypeScript SDK.
46
+ - **Bring your own model:** OpenRouter, Google Gemini, DeepSeek, Groq, Cerebras, or Ollama.
47
+ - **Schema-aware agent** with tools for listing tables, describing tables, discovering foreign keys, running queries, and `EXPLAIN`.
48
+ - **Safety by default:** configurable row, byte, timeout, and agent-step limits; capped SQL attempts; foreign-key discovery required before any SQL runs.
49
+ - **Optional `sql-ai.md`** for business rules and vocabulary the model cannot infer from the schema.
50
+ - **Three response modes:** natural, technical, and debug.
51
+ - **`validate` command** that checks your config, database, foreign keys, and AI provider before you ask anything.
52
+ - **In-process schema cache** so repeated lookups in a run don't hit the database twice.
53
+ - **Benchmark suite** in `evals/` for reproducible evaluation of changes.
54
+
55
+ ## Requirements
56
+
57
+ - **Node.js 22 or later**
58
+ - A **MySQL, MariaDB, or PostgreSQL** database, and an account with read access to the tables the agent should see
59
+ - Credentials and a model ID for one supported AI provider (or a reachable Ollama endpoint)
60
+
61
+ > SQLite, a hosted HTTP API, and SDKs for languages other than TypeScript are **not** part of this release. The SDK defines a `DatabaseAdapter` interface as the extension point; MySQL/MariaDB and PostgreSQL adapters ship today.
62
+
63
+ ## Quickstart
64
+
65
+ ### 1. Configure
66
+
67
+ Run the setup wizard in the directory where you want the configuration stored:
68
+
69
+ ```bash
70
+ npx @negusdev/dbask init
71
+ ```
72
+
73
+ The wizard asks for:
74
+
75
+ - Database type (MySQL/MariaDB or PostgreSQL), host, port, database, username, and password
76
+ - AI provider and exact model ID
77
+ - An API key (hosted providers) or a base URL (Ollama)
78
+ - Agent step limit, SQL row limit, and schema-cache lifetime
79
+
80
+ It tests the **database connection** and lists tables before saving. It does **not** send an inference request, so a successful `init` does not prove the provider key or model ID is correct. The next step checks that.
81
+
82
+ If `.env` already exists, the wizard asks before updating harness settings and preserves unrelated entries. Skip the confirmation with `--force`:
83
+
84
+ ```bash
85
+ npx @negusdev/dbask init --force
86
+ ```
87
+
88
+ Keep `.env` private and out of version control. The wizard writes it with restrictive permissions where supported.
89
+
90
+ ### 2. Check your setup
91
+
92
+ ```bash
93
+ npx @negusdev/dbask validate
94
+ ```
95
+
96
+ `validate` checks your configuration, connects to the database, confirms the account can see tables and foreign keys, and sends **one tiny request** to your AI provider to confirm the key and model work. It never reads table data and never runs model-written SQL. Each line reports `[ok]`, `[warn]`, `[fail]` or `[skip]`, and the command exits with a non-zero status if anything fails, so it also works in scripts and CI.
97
+
98
+ ```text
99
+ [ok] Configuration: provider=openrouter, model=openai/gpt-4o-mini
100
+ [ok] Database connection: Connected to 127.0.0.1:3306/mydb; 27 tables visible.
101
+ [ok] Foreign keys: 42 declared relationships found.
102
+ [ok] AI provider: openrouter answered using model openai/gpt-4o-mini.
103
+ [skip] Business context (sql-ai.md): No sql-ai.md found. This is optional.
104
+ ```
105
+
106
+ Use `--skip-provider` to check only the configuration and database, without contacting the AI provider.
107
+
108
+ ### 3. Ask a question
109
+
110
+ ```bash
111
+ npx @negusdev/dbask "How many students are enrolled?"
112
+ ```
113
+
114
+ ### 4. Install globally (optional)
115
+
116
+ ```bash
117
+ npm install --global @negusdev/dbask
118
+ dbask "List active users"
119
+ ```
120
+
121
+ ## Data handling
122
+
123
+ dbask itself does not phone home. No telemetry, no crash reporting, no license check on startup, no cloud dependency. The harness runs on your machine (or your own server) and talks to your database directly.
124
+
125
+ What does leave your machine is what your chosen AI provider receives. Depending on the question and tool flow, that can include schema metadata, question text, generated SQL, and query results. Review that provider's retention, training, and residency terms before using sensitive data.
126
+
127
+ If you need local-only inference, [Ollama-compatible endpoints are supported](#ai-provider), but this path is not currently exercised in the benchmark, so validate it against your own workload first.
128
+
129
+ To reduce exposure, see [Safety and data handling](#safety-and-data-handling): read-only database accounts, row/byte/timeout limits, and a required foreign-key discovery step before any SQL runs.
130
+
131
+ ## Usage
132
+
133
+ ```bash
134
+ dbask "How much revenue last month?" # natural (default)
135
+ dbask --mode technical "How much revenue last month?" # answer + SQL
136
+ dbask --mode debug "How much revenue last month?" # answer + tool traces
137
+ # `dbask ask "…"` is the same as `dbask "…"`; use it for a one-word question or inside older scripts.
138
+ ```
139
+
140
+ Shorthand flags `--technical` and `--debug` also work.
141
+
142
+ | Mode | Output |
143
+ |---|---|
144
+ | `natural` | A concise, business-friendly answer. |
145
+ | `technical` | The answer plus the SQL that was run. |
146
+ | `debug` | The answer plus tool-call diagnostics. Start here when an answer looks wrong. |
147
+
148
+ **Terminal output.** In an interactive terminal the CLI shows a progress spinner, renders the answer's Markdown (tables, bold, code, lists) and, in debug mode, prints the tool trace with symbols, highlighted SQL, the submitted query plan and any plan warnings. When output is piped or redirected, nothing changes: you get the plain Markdown answer and the plain `[tool]` / `[done]` trace. Set `NO_COLOR=1` to turn colors off, or `FORCE_COLOR=1` to keep them in a pipe.
149
+
150
+ ## Configuration
151
+
152
+ Configuration is loaded from `.env` in the current working directory plus the process environment. See `.env.example` in the repository for a full template.
153
+
154
+ ### AI provider
155
+
156
+ | Variable | Purpose | Example |
157
+ |---|---|---|
158
+ | `AI_PROVIDER` | One of `openrouter`, `google`, `groq`, `cerebras`, `ollama`, `deepseek` | `openrouter` |
159
+ | `AI_MODEL` | Exact model ID accepted by the provider | *required* |
160
+ | `OPENROUTER_API_KEY` | OpenRouter credential | n/a |
161
+ | `GOOGLE_GENERATIVE_AI_API_KEY` | Google Gemini credential | n/a |
162
+ | `GROQ_API_KEY` | Groq credential | n/a |
163
+ | `CEREBRAS_API_KEY` | Cerebras credential | n/a |
164
+ | `OLLAMA_BASE_URL` | Ollama endpoint | `http://localhost:11434/v1` |
165
+ | `DEEPSEEK_API_KEY` | DeepSeek credential | n/a |
166
+ | `AI_SITE_URL` | Optional OpenRouter app URL metadata | n/a |
167
+ | `AI_SITE_NAME` | Optional OpenRouter app name metadata | `dbask` |
168
+
169
+ Model availability, pricing, quotas, and API behavior are controlled by each provider and can change independently of this project.
170
+
171
+ ### Database
172
+
173
+ | Variable | Purpose | Default |
174
+ |---|---|---|
175
+ | `DB_DRIVER` | `mysql` (also MariaDB) or `postgres` | `mysql` |
176
+ | `DB_HOST` | Hostname or IP | `127.0.0.1` |
177
+ | `DB_PORT` | Port | `3306` for mysql, `5432` for postgres |
178
+ | `DB_SSL` | `disable`, `require` (encrypt, don't verify the certificate), or `verify-full` (encrypt and verify; use `NODE_EXTRA_CA_CERTS` for a private CA) | `disable` |
179
+ | `DB_NAME` | Database name | *required* |
180
+ | `DB_USER` | Username (**use a restricted read-only account**) | *required* |
181
+ | `DB_PASSWORD` | Password (set privately) | *required* |
182
+
183
+ ### Limits
184
+
185
+ | Variable | Purpose | Default |
186
+ |---|---|---|
187
+ | `SQL_MAX_ROWS` | Max rows returned per query | `500` |
188
+ | `SQL_QUERY_TIMEOUT_MS` | Per-query timeout (ms) | `10000` |
189
+ | `SQL_MAX_RESULT_BYTES` | Max result payload size (bytes) | `1048576` |
190
+ | `AGENT_MAX_STEPS` | Max agent loop steps | `8` |
191
+ | `AGENT_QUERY_PLAN` | `on` requires a schema-checked query plan before SQL runs; `off` skips it | `off` |
192
+ | `SCHEMA_CACHE_TTL_MS` | Schema cache lifetime (ms) | `300000` (5 min) |
193
+
194
+ Start from the `.env.example` values and tune against your own database. Higher limits increase both resource use and the amount of data exposed to the model.
195
+
196
+ ## Optional database context
197
+
198
+ Place a `sql-ai.md` file in the working directory to explain business terms and rules that aren't obvious from table and column names. It supplements schema discovery; it does not replace it.
199
+
200
+ ```markdown
201
+ # Database Context
202
+
203
+ ## Overview
204
+
205
+ This database belongs to a school management system. It stores students,
206
+ parents, teachers, classes, enrollments, and payments.
207
+
208
+ ## Business Rules
209
+
210
+ - Only active enrollments represent currently enrolled students.
211
+ - Completed payments are considered successful payments.
212
+ - Cancelled payments should not be included in revenue calculations.
213
+ ```
214
+
215
+ Keep the rules accurate and current. Incorrect or ambiguous context can produce confidently wrong answers even when the generated SQL is valid. A starter template ships as [`sql-ai.example.md`](./sql-ai.example.md).
216
+
217
+ ## TypeScript SDK
218
+
219
+ The package exports its public SDK from `@negusdev/dbask`. Import from the package entry point; internal `src/` paths are not part of the public API.
220
+
221
+ ```ts
222
+ import {
223
+ Agent,
224
+ createAiProvider,
225
+ createDatabaseAdapter,
226
+ loadConfig,
227
+ } from "@negusdev/dbask";
228
+
229
+ const config = loadConfig();
230
+
231
+ // Picks MySqlAdapter or PostgresAdapter from DB_DRIVER.
232
+ // You can also construct MySqlAdapter / PostgresAdapter directly.
233
+ const database = createDatabaseAdapter(config.database);
234
+
235
+ const provider = createAiProvider(config.ai);
236
+
237
+ const agent = new Agent(
238
+ provider,
239
+ { database, safety: config.safety },
240
+ { maxSteps: config.agent.maxSteps, responseMode: "natural" },
241
+ );
242
+
243
+ try {
244
+ const result = await agent.ask("List active users");
245
+ console.log(result.text);
246
+ } finally {
247
+ await database.close();
248
+ }
249
+ ```
250
+
251
+ The entry point also exports types and interfaces for the agent, providers, database adapters, tool context, observers, and `SchemaCache`. See `src/index.ts` in the installed version for the exact API surface.
252
+
253
+ The SDK accepts any `DatabaseAdapter` implementation. Custom adapters must preserve the harness safety expectations: read-only intent, row/byte/timeout limits, and bounded SQL attempts per question.
254
+
255
+ ## Database tools
256
+
257
+ | Tool | Purpose |
258
+ |---|---|
259
+ | `list_tables` | List available database tables. |
260
+ | `describe_table` | Inspect one table's structure. |
261
+ | `describe_tables` | Inspect multiple tables in one call. |
262
+ | `get_relationships` | Discover foreign-key relationships across the database. |
263
+ | `execute_query` | Execute SQL allowed by the safety layer and configured limits. |
264
+ | `explain_query` | Request a query plan where supported. |
265
+
266
+ The agent requires **successful global foreign-key discovery before executing SQL**. This helps the model plan joins, but it does not prove every join or aggregation matches the business meaning of a question, and undeclared relationships may not be discoverable.
267
+
268
+ ## Safety and data handling
269
+
270
+ dbask applies safeguards and execution limits, but **it is not a complete security boundary by itself**. Recommended practices:
271
+
272
+ 1. **Use a dedicated read-only database account** with only the schema-inspection and read permissions required. Don't rely solely on the model or the application validator to prevent writes.
273
+ 2. **Restrict database visibility.** Avoid granting access to unrelated databases, sensitive tables, or columns.
274
+ 3. **Protect credentials.** Don't commit `.env` or share credentials in logs or issue reports. Rotate any credential that is exposed.
275
+ 4. **Review your AI provider's data handling.** Schema metadata, question text, generated SQL, and query results may be sent to the provider. Check its retention, training, residency, and contractual terms before using sensitive data.
276
+ 5. **Set conservative limits** for rows, result size, timeout, and agent steps.
277
+ 6. **Verify important answers** independently before using them for financial, operational, or compliance decisions.
278
+ 7. **Test on a non-production database first**, such as a disposable or sanitized snapshot.
279
+
280
+ The SQL validator tokenizes each query, so keywords inside string literals, quoted identifiers, and comments are ignored. It accepts a single read-oriented statement and rejects writes, locking reads, `INTO OUTFILE`, executable comments, and functions such as `SLEEP()` and `LOAD_FILE()`. It uses the lexical rules of the configured database (for example PostgreSQL's dollar-quoted strings and nested comments, or MySQL's `#` comments and backslash escapes), and it assumes MySQL's default `sql_mode`. It cannot recognize user-defined functions with side effects, which is another reason to use a read-only database account.
281
+
282
+ On PostgreSQL the adapter adds server-side protection that does not depend on the validator: every statement runs inside a `READ ONLY` transaction that is always rolled back, with a per-query `statement_timeout`, over the extended protocol (so the server itself refuses multiple statements), and rows are streamed through a cursor so oversized results are never fully loaded. Results that hit the row or size limit are flagged `truncated`, and the agent is told not to present them as complete.
283
+
284
+ Successful SQL execution does not guarantee a correct answer. SQL attempts are capped per question, but correctness cannot be guaranteed for arbitrary questions or business-specific joins.
285
+
286
+ ## Database support
287
+
288
+ | Database | Status |
289
+ |---|---|
290
+ | MySQL / MariaDB | Available |
291
+ | PostgreSQL | Available (tables, partitioned tables, and schemas; views are not listed yet) |
292
+ | SQLite | Planned |
293
+ | Others | Implement `DatabaseAdapter` |
294
+
295
+ ## Evaluation
296
+
297
+ The `evals/` directory contains a benchmark dataset, runner, and saved reports. Use it to check whether a change to the model, prompt, tool, or adapter measurably affects correctness.
298
+
299
+ ```bash
300
+ pnpm eval -- --tag local-check
301
+ ```
302
+
303
+ See [`evals/README.md`](./evals/README.md) for dataset setup and scoring. When comparing runs, keep the provider, model, database snapshot, configuration, and dataset consistent. A passing benchmark supports the cases tested; it is not a guarantee of general correctness.
304
+
305
+ **Latest result (v0.1.8):** on the 40-case internal benchmark (synthetic forex database, `gemini-3.5-flash-lite`, 3 runs) the agent passed 39 of 40 cases in every run (97.5%), including 10 of 10 safety and off-topic refusal checks. The one consistent failure is a known reference-answer error in the benchmark, not an agent error. The cases are mostly simple lookups and joins, so treat this as a regression check, not a measure of accuracy on your own database.
306
+
307
+ ### Query plan (optional)
308
+
309
+ With `AGENT_QUERY_PLAN=on` the agent must submit a structured plan (tables, columns, joins, filters) that is checked against your real schema before any SQL runs, and the SQL may only read the planned tables. In the same benchmark the plan added about 0.7 tool calls and roughly 30% median latency per question without improving accuracy, so it is off by default. Turn it on if you want the agent's assumptions visible in `--mode debug`, or to compare against your own workload.
310
+
311
+ ## Development
312
+
313
+ ```bash
314
+ git clone https://github.com/NegusDev/dbask.git
315
+ cd dbask
316
+ pnpm install
317
+ cp .env.example .env
318
+ ```
319
+
320
+ Run a question through the development CLI:
321
+
322
+ ```bash
323
+ pnpm dev ask "List active users"
324
+ ```
325
+
326
+ Run the checks before submitting changes:
327
+
328
+ ```bash
329
+ pnpm typecheck
330
+ pnpm test
331
+ pnpm build
332
+ npm pack --dry-run
333
+ ```
334
+
335
+ Run the built CLI directly:
336
+
337
+ ```bash
338
+ node dist/cli.js ask "List active users"
339
+ ```
340
+
341
+ ### Project layout
342
+
343
+ ```text
344
+ src/
345
+ ai/ Provider factory and provider interfaces
346
+ commands/ CLI commands, including the init wizard
347
+ config/ Environment and .env loading
348
+ context/ Optional business context loading (sql-ai.md)
349
+ core/agent/ Agent loop and observability interfaces
350
+ core/tools/ Schema and SQL tools exposed to the agent
351
+ database/ Database adapter interface, MySQL and PostgreSQL implementations
352
+ safety/ SQL validation, policy checks, execution limits
353
+ schema/ In-memory schema metadata cache
354
+ cli.ts CLI entry point
355
+ index.ts Public TypeScript SDK exports
356
+ evals/ Benchmark dataset, runner, and reports
357
+ tests/ Automated tests
358
+ ```
359
+
360
+ ## Troubleshooting
361
+
362
+ ### Database connection fails
363
+
364
+ - Verify host, port, database name, username, and password.
365
+ - Confirm the database accepts connections from the machine running the harness.
366
+ - Confirm the account can inspect tables and read the required data.
367
+ - Check firewall rules and database allowlists.
368
+ - Hosted PostgreSQL (Neon, Supabase, RDS, ...) usually requires TLS: set `DB_SSL=require`. Poolers such as pgbouncer in transaction mode work, because each query is a self-contained transaction.
369
+ - PostgreSQL tables outside `public` appear as `schema.table` in `list_tables`; the agent is told to use names exactly as listed.
370
+
371
+ ### Provider authentication or model errors
372
+
373
+ - Confirm `AI_PROVIDER` matches the provider whose key you configured.
374
+ - Verify `AI_MODEL` is the exact model ID the provider expects.
375
+ - Check that the API key is active and has quota and access to the model.
376
+ - For Ollama, verify the endpoint is reachable and the model is installed.
377
+ - Run `dbask validate`. It sends one tiny request to the provider and reports whether the key, model, and network path work. (`init` only tests the database.)
378
+
379
+ ### The answer is incorrect or incomplete
380
+
381
+ - Run with `--mode technical` or `--mode debug` to inspect the SQL and tool activity.
382
+ - Inspect the relevant table definitions and foreign keys.
383
+ - Review `sql-ai.md` for outdated or ambiguous rules.
384
+ - Narrow the question with dates, statuses, or business definitions.
385
+ - Verify the generated SQL and results directly against the database.
386
+ - Add a representative case to `evals/` if the failure is repeatable.
387
+
388
+ ### Configuration is not found
389
+
390
+ - Run the command from the directory containing `.env`, or set the variables in the process environment.
391
+ - Confirm variable names match `.env.example`.
392
+ - Check that `AI_MODEL` and the selected provider's credentials are set.
393
+
394
+ ## Contributing
395
+
396
+ Issues and pull requests are welcome at [github.com/NegusDev/dbask](https://github.com/NegusDev/dbask).
397
+
398
+ Before opening a PR, run the project checks and confirm that changes to the agent, tools, or prompts don't regress the benchmark:
399
+
400
+ ```bash
401
+ pnpm eval -- --tag <your-branch>
402
+ ```
403
+
404
+ For bugs involving incorrect answers, include:
405
+
406
+ - The question you asked (or a close paraphrase)
407
+ - The `--mode debug` output
408
+ - The relevant table definitions and `sql-ai.md` content, if any
409
+ - The provider and model used
410
+
411
+ Do **not** include `.env` contents, credentials, or unfiltered production data in issue reports.
412
+
413
+ ## License
414
+
415
+ Released under the [ISC License](./LICENSE).
416
+
417
+ ## Links
418
+
419
+ - [Source](https://github.com/NegusDev/dbask)
420
+ - [npm](https://www.npmjs.com/package/@negusdev/dbask)
421
+ - [Issues](https://github.com/NegusDev/dbask/issues)
422
+ - [Evaluation guide](./evals/README.md)
423
+ - [Business context template](./sql-ai.example.md)
@@ -0,0 +1,4 @@
1
+ import type { AppConfig } from '../config/config.js';
2
+ import type { AiProvider } from './provider.js';
3
+ export declare function createAiProvider(config: AppConfig['ai']): AiProvider;
4
+ //# sourceMappingURL=factory.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"factory.d.ts","sourceRoot":"","sources":["../../src/ai/factory.ts"],"names":[],"mappings":"AAQA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAC;AACrD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAGhD,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,SAAS,CAAC,IAAI,CAAC,GAAG,UAAU,CAgEpE"}
@@ -0,0 +1,55 @@
1
+ import { createOpenRouter } from '@openrouter/ai-sdk-provider';
2
+ import { createGroq } from '@ai-sdk/groq';
3
+ import { createGoogleGenerativeAI } from '@ai-sdk/google';
4
+ import { createCerebras } from '@ai-sdk/cerebras';
5
+ import { createOpenAICompatible } from '@ai-sdk/openai-compatible';
6
+ import { createDeepSeek } from '@ai-sdk/deepseek';
7
+ import { SdkAiProvider } from './sdk-provider.js';
8
+ export function createAiProvider(config) {
9
+ switch (config.provider) {
10
+ case 'openrouter': {
11
+ const provider = createOpenRouter({
12
+ apiKey: config.apiKey,
13
+ ...(config.siteUrl ? { appUrl: config.siteUrl } : {}),
14
+ ...(config.siteName ? { appName: config.siteName } : {}),
15
+ });
16
+ return new SdkAiProvider('openrouter', provider.chat(config.model));
17
+ }
18
+ case 'groq': {
19
+ const provider = createGroq({
20
+ apiKey: config.apiKey,
21
+ });
22
+ return new SdkAiProvider('groq', provider(config.model));
23
+ }
24
+ case 'google': {
25
+ const provider = createGoogleGenerativeAI({
26
+ apiKey: config.apiKey,
27
+ });
28
+ return new SdkAiProvider('google', provider(config.model));
29
+ }
30
+ case 'cerebras': {
31
+ const provider = createCerebras({
32
+ apiKey: config.apiKey,
33
+ });
34
+ return new SdkAiProvider('cerebras', provider(config.model));
35
+ }
36
+ case 'ollama': {
37
+ const provider = createOpenAICompatible({
38
+ name: 'ollama',
39
+ baseURL: config.baseUrl ?? 'http://localhost:11434/v1',
40
+ });
41
+ return new SdkAiProvider('ollama', provider.chatModel(config.model));
42
+ }
43
+ case 'deepseek': {
44
+ const provider = createDeepSeek({
45
+ apiKey: config.apiKey,
46
+ });
47
+ return new SdkAiProvider('deepseek', provider.chat(config.model));
48
+ }
49
+ default: {
50
+ const exhaustiveCheck = config.provider;
51
+ throw new Error(`Unsupported AI provider: ${exhaustiveCheck}`);
52
+ }
53
+ }
54
+ }
55
+ //# sourceMappingURL=factory.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"factory.js","sourceRoot":"","sources":["../../src/ai/factory.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,gBAAgB,EAAE,MAAM,6BAA6B,CAAC;AAC/D,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAC1C,OAAO,EAAE,wBAAwB,EAAE,MAAM,gBAAgB,CAAC;AAC1D,OAAO,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAClD,OAAO,EAAE,sBAAsB,EAAE,MAAM,2BAA2B,CAAC;AACnE,OAAO,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAIlD,OAAO,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAElD,MAAM,UAAU,gBAAgB,CAAC,MAAuB;IACtD,QAAQ,MAAM,CAAC,QAAQ,EAAE,CAAC;QACxB,KAAK,YAAY,EAAE,CAAC;YAClB,MAAM,QAAQ,GAAG,gBAAgB,CAAC;gBAChC,MAAM,EAAE,MAAM,CAAC,MAAO;gBACtB,GAAG,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,MAAM,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;gBACrD,GAAG,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,MAAM,CAAC,QAAQ,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;aACzD,CAAC,CAAC;YAEH,OAAO,IAAI,aAAa,CACtB,YAAY,EACZ,QAAQ,CAAC,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,CAC5B,CAAC;QACJ,CAAC;QAED,KAAK,MAAM,EAAE,CAAC;YACZ,MAAM,QAAQ,GAAG,UAAU,CAAC;gBAC1B,MAAM,EAAE,MAAM,CAAC,MAAO;aACvB,CAAC,CAAC;YAEH,OAAO,IAAI,aAAa,CAAC,MAAM,EAAE,QAAQ,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC;QAC3D,CAAC;QAED,KAAK,QAAQ,EAAE,CAAC;YACd,MAAM,QAAQ,GAAG,wBAAwB,CAAC;gBACxC,MAAM,EAAE,MAAM,CAAC,MAAO;aACvB,CAAC,CAAC;YAEH,OAAO,IAAI,aAAa,CAAC,QAAQ,EAAE,QAAQ,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC;QAC7D,CAAC;QAED,KAAK,UAAU,EAAE,CAAC;YAChB,MAAM,QAAQ,GAAG,cAAc,CAAC;gBAC9B,MAAM,EAAE,MAAM,CAAC,MAAO;aACvB,CAAC,CAAC;YAEH,OAAO,IAAI,aAAa,CAAC,UAAU,EAAE,QAAQ,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC;QAC/D,CAAC;QAED,KAAK,QAAQ,EAAE,CAAC;YACd,MAAM,QAAQ,GAAG,sBAAsB,CAAC;gBACtC,IAAI,EAAE,QAAQ;gBACd,OAAO,EAAE,MAAM,CAAC,OAAO,IAAI,2BAA2B;aACvD,CAAC,CAAC;YAEH,OAAO,IAAI,aAAa,CACtB,QAAQ,EACR,QAAQ,CAAC,SAAS,CAAC,MAAM,CAAC,KAAK,CAAC,CACjC,CAAC;QACJ,CAAC;QAED,KAAK,UAAU,EAAE,CAAC;YAChB,MAAM,QAAQ,GAAG,cAAc,CAAC;gBAC9B,MAAM,EAAE,MAAM,CAAC,MAAO;aACvB,CAAC,CAAC;YAEH,OAAO,IAAI,aAAa,CAAC,UAAU,EAAE,QAAQ,CAAC,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC;QACpE,CAAC;QAED,SAAS,CAAC;YACR,MAAM,eAAe,GAAU,MAAM,CAAC,QAAQ,CAAC;YAC/C,MAAM,IAAI,KAAK,CAAC,4BAA4B,eAAe,EAAE,CAAC,CAAC;QACjE,CAAC;IACH,CAAC;AACH,CAAC"}
@@ -0,0 +1,13 @@
1
+ import type { ToolSet } from 'ai';
2
+ import type { ModelProvider, ModelRequest, ModelResponse } from '../core/types/index.js';
3
+ export type { ModelProvider, ModelRequest, ModelResponse };
4
+ export interface AiProvider extends ModelProvider {
5
+ generate(request: ModelRequest & {
6
+ tools?: ToolSet;
7
+ maxSteps?: number;
8
+ }): Promise<ModelResponse & {
9
+ steps?: number;
10
+ toolCalls?: number;
11
+ }>;
12
+ }
13
+ //# sourceMappingURL=provider.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"provider.d.ts","sourceRoot":"","sources":["../../src/ai/provider.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,IAAI,CAAC;AAClC,OAAO,KAAK,EAAE,aAAa,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,wBAAwB,CAAC;AAEzF,YAAY,EAAE,aAAa,EAAE,YAAY,EAAE,aAAa,EAAE,CAAC;AAE3D,MAAM,WAAW,UAAW,SAAQ,aAAa;IAC/C,QAAQ,CAAC,OAAO,EAAE,YAAY,GAAG;QAAE,KAAK,CAAC,EAAE,OAAO,CAAC;QAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC,aAAa,GAAG;QAChG,KAAK,CAAC,EAAE,MAAM,CAAC;QACf,SAAS,CAAC,EAAE,MAAM,CAAC;KACpB,CAAC,CAAC;CACJ"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=provider.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"provider.js","sourceRoot":"","sources":["../../src/ai/provider.ts"],"names":[],"mappings":""}
@@ -0,0 +1,13 @@
1
+ import { type LanguageModel, type ToolSet } from 'ai';
2
+ import type { ModelRequest, ModelResponse } from '../core/types/index.js';
3
+ import type { AiProvider } from './provider.js';
4
+ export declare class SdkAiProvider implements AiProvider {
5
+ readonly name: string;
6
+ private readonly model;
7
+ constructor(name: string, model: LanguageModel);
8
+ generate(request: ModelRequest & {
9
+ tools?: ToolSet;
10
+ maxSteps?: number;
11
+ }): Promise<ModelResponse>;
12
+ }
13
+ //# sourceMappingURL=sdk-provider.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sdk-provider.d.ts","sourceRoot":"","sources":["../../src/ai/sdk-provider.ts"],"names":[],"mappings":"AACA,OAAO,EAGL,KAAK,aAAa,EAClB,KAAK,OAAO,EACb,MAAM,IAAI,CAAC;AAEZ,OAAO,KAAK,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,wBAAwB,CAAC;AAC1E,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAEhD,qBAAa,aAAc,YAAW,UAAU;IAE5C,QAAQ,CAAC,IAAI,EAAE,MAAM;IACrB,OAAO,CAAC,QAAQ,CAAC,KAAK;IAFxB,YACW,IAAI,EAAE,MAAM,EACJ,KAAK,EAAE,aAAa,EACnC;IAEE,QAAQ,CACZ,OAAO,EAAE,YAAY,GAAG;QACtB,KAAK,CAAC,EAAE,OAAO,CAAC;QAChB,QAAQ,CAAC,EAAE,MAAM,CAAC;KACnB,GACA,OAAO,CAAC,aAAa,CAAC,CA+BxB;CACF"}
@@ -0,0 +1,36 @@
1
+ import { generateText, isStepCount, } from 'ai';
2
+ export class SdkAiProvider {
3
+ name;
4
+ model;
5
+ constructor(name, model) {
6
+ this.name = name;
7
+ this.model = model;
8
+ }
9
+ async generate(request) {
10
+ const result = await generateText({
11
+ model: this.model,
12
+ system: request.system,
13
+ prompt: request.prompt,
14
+ tools: request.tools,
15
+ stopWhen: request.maxSteps
16
+ ? isStepCount(request.maxSteps)
17
+ : undefined,
18
+ // Reserve the last step for the answer: without this the model can spend
19
+ // every step on tool calls and end with no text at all.
20
+ prepareStep: request.maxSteps
21
+ ? ({ stepNumber }) => stepNumber >= request.maxSteps - 1
22
+ ? { toolChoice: 'none' }
23
+ : undefined
24
+ : undefined,
25
+ temperature: request.temperature,
26
+ });
27
+ const toolCalls = result.steps.reduce((count, step) => count + (step.toolCalls?.length ?? 0), 0);
28
+ return {
29
+ text: result.text,
30
+ raw: result,
31
+ steps: result.steps.length,
32
+ toolCalls,
33
+ };
34
+ }
35
+ }
36
+ //# sourceMappingURL=sdk-provider.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sdk-provider.js","sourceRoot":"","sources":["../../src/ai/sdk-provider.ts"],"names":[],"mappings":"AACA,OAAO,EACL,YAAY,EACZ,WAAW,GAGZ,MAAM,IAAI,CAAC;AAKZ,MAAM,OAAO,aAAa;IAEb,IAAI;IACI,KAAK;IAFxB,YACW,IAAY,EACJ,KAAoB;oBAD5B,IAAI;qBACI,KAAK;IACrB,CAAC;IAEJ,KAAK,CAAC,QAAQ,CACZ,OAGC;QAED,MAAM,MAAM,GAAG,MAAM,YAAY,CAAC;YAChC,KAAK,EAAE,IAAI,CAAC,KAAK;YACjB,MAAM,EAAE,OAAO,CAAC,MAAM;YACtB,MAAM,EAAE,OAAO,CAAC,MAAM;YACtB,KAAK,EAAE,OAAO,CAAC,KAAK;YACpB,QAAQ,EAAE,OAAO,CAAC,QAAQ;gBACxB,CAAC,CAAC,WAAW,CAAC,OAAO,CAAC,QAAQ,CAAC;gBAC/B,CAAC,CAAC,SAAS;YACb,yEAAyE;YACzE,wDAAwD;YACxD,WAAW,EAAE,OAAO,CAAC,QAAQ;gBAC3B,CAAC,CAAC,CAAC,EAAE,UAAU,EAAE,EAAE,EAAE,CACjB,UAAU,IAAK,OAAO,CAAC,QAAmB,GAAG,CAAC;oBAC5C,CAAC,CAAC,EAAE,UAAU,EAAE,MAAe,EAAE;oBACjC,CAAC,CAAC,SAAS;gBACjB,CAAC,CAAC,SAAS;YACb,WAAW,EAAE,OAAO,CAAC,WAAW;SACjC,CAAC,CAAC;QAEH,MAAM,SAAS,GAAG,MAAM,CAAC,KAAK,CAAC,MAAM,CACnC,CAAC,KAAK,EAAE,IAAI,EAAE,EAAE,CAAC,KAAK,GAAG,CAAC,IAAI,CAAC,SAAS,EAAE,MAAM,IAAI,CAAC,CAAC,EACtD,CAAC,CACF,CAAC;QAEF,OAAO;YACL,IAAI,EAAE,MAAM,CAAC,IAAI;YACjB,GAAG,EAAE,MAAM;YACX,KAAK,EAAE,MAAM,CAAC,KAAK,CAAC,MAAM;YAC1B,SAAS;SACV,CAAC;IACJ,CAAC;CACF"}