sqlrite 0.1.20__tar.gz → 0.1.21__tar.gz
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.
- {sqlrite-0.1.20 → sqlrite-0.1.21}/Cargo.lock +6 -6
- {sqlrite-0.1.20 → sqlrite-0.1.21}/Cargo.toml +1 -1
- {sqlrite-0.1.20 → sqlrite-0.1.21}/PKG-INFO +68 -10
- {sqlrite-0.1.20 → sqlrite-0.1.21}/desktop/package.json +1 -1
- {sqlrite-0.1.20 → sqlrite-0.1.21}/docs/phase-7-plan.md +1 -1
- {sqlrite-0.1.20 → sqlrite-0.1.21}/docs/roadmap.md +1 -1
- {sqlrite-0.1.20 → sqlrite-0.1.21}/pyproject.toml +1 -1
- {sqlrite-0.1.20 → sqlrite-0.1.21}/sdk/python/Cargo.toml +1 -1
- {sqlrite-0.1.20 → sqlrite-0.1.21}/sdk/python/README.md +67 -9
- {sqlrite-0.1.20 → sqlrite-0.1.21}/sdk/python/src/lib.rs +392 -0
- sqlrite-0.1.21/sdk/python/tests/test_ask.py +450 -0
- {sqlrite-0.1.20 → sqlrite-0.1.21}/sqlrite-ask/Cargo.toml +1 -1
- {sqlrite-0.1.20 → sqlrite-0.1.21}/.github/workflows/ci.yml +0 -0
- {sqlrite-0.1.20 → sqlrite-0.1.21}/.github/workflows/release-pr.yml +0 -0
- {sqlrite-0.1.20 → sqlrite-0.1.21}/.github/workflows/release.yml +0 -0
- {sqlrite-0.1.20 → sqlrite-0.1.21}/.github/workflows/rust.yml +0 -0
- {sqlrite-0.1.20 → sqlrite-0.1.21}/.gitignore +0 -0
- {sqlrite-0.1.20 → sqlrite-0.1.21}/CODE_OF_CONDUCT.md +0 -0
- {sqlrite-0.1.20 → sqlrite-0.1.21}/LICENSE +0 -0
- {sqlrite-0.1.20 → sqlrite-0.1.21}/MAINTAINERS +0 -0
- {sqlrite-0.1.20 → sqlrite-0.1.21}/Makefile +0 -0
- {sqlrite-0.1.20 → sqlrite-0.1.21}/README.md +0 -0
- {sqlrite-0.1.20 → sqlrite-0.1.21}/desktop/index.html +0 -0
- {sqlrite-0.1.20 → sqlrite-0.1.21}/desktop/package-lock.json +0 -0
- {sqlrite-0.1.20 → sqlrite-0.1.21}/desktop/src/App.svelte +0 -0
- {sqlrite-0.1.20 → sqlrite-0.1.21}/desktop/src/app.css +0 -0
- {sqlrite-0.1.20 → sqlrite-0.1.21}/desktop/src/main.ts +0 -0
- {sqlrite-0.1.20 → sqlrite-0.1.21}/desktop/src/vite-env.d.ts +0 -0
- {sqlrite-0.1.20 → sqlrite-0.1.21}/desktop/svelte.config.js +0 -0
- {sqlrite-0.1.20 → sqlrite-0.1.21}/desktop/tsconfig.json +0 -0
- {sqlrite-0.1.20 → sqlrite-0.1.21}/desktop/vite.config.ts +0 -0
- {sqlrite-0.1.20 → sqlrite-0.1.21}/docs/_index.md +0 -0
- {sqlrite-0.1.20 → sqlrite-0.1.21}/docs/architecture.md +0 -0
- {sqlrite-0.1.20 → sqlrite-0.1.21}/docs/design-decisions.md +0 -0
- {sqlrite-0.1.20 → sqlrite-0.1.21}/docs/desktop.md +0 -0
- {sqlrite-0.1.20 → sqlrite-0.1.21}/docs/embedding.md +0 -0
- {sqlrite-0.1.20 → sqlrite-0.1.21}/docs/file-format.md +0 -0
- {sqlrite-0.1.20 → sqlrite-0.1.21}/docs/getting-started.md +0 -0
- {sqlrite-0.1.20 → sqlrite-0.1.21}/docs/pager.md +0 -0
- {sqlrite-0.1.20 → sqlrite-0.1.21}/docs/release-plan.md +0 -0
- {sqlrite-0.1.20 → sqlrite-0.1.21}/docs/release-secrets.md +0 -0
- {sqlrite-0.1.20 → sqlrite-0.1.21}/docs/smoke-test.md +0 -0
- {sqlrite-0.1.20 → sqlrite-0.1.21}/docs/sql-engine.md +0 -0
- {sqlrite-0.1.20 → sqlrite-0.1.21}/docs/storage-model.md +0 -0
- {sqlrite-0.1.20 → sqlrite-0.1.21}/docs/supported-sql.md +0 -0
- {sqlrite-0.1.20 → sqlrite-0.1.21}/docs/usage.md +0 -0
- {sqlrite-0.1.20 → sqlrite-0.1.21}/examples/README.md +0 -0
- {sqlrite-0.1.20 → sqlrite-0.1.21}/examples/c/Makefile +0 -0
- {sqlrite-0.1.20 → sqlrite-0.1.21}/examples/c/hello.c +0 -0
- {sqlrite-0.1.20 → sqlrite-0.1.21}/examples/go/go.mod +0 -0
- {sqlrite-0.1.20 → sqlrite-0.1.21}/examples/go/hello.go +0 -0
- {sqlrite-0.1.20 → sqlrite-0.1.21}/examples/nodejs/hello.mjs +0 -0
- {sqlrite-0.1.20 → sqlrite-0.1.21}/examples/python/hello.py +0 -0
- {sqlrite-0.1.20 → sqlrite-0.1.21}/examples/rust/quickstart.rs +0 -0
- {sqlrite-0.1.20 → sqlrite-0.1.21}/examples/wasm/Makefile +0 -0
- {sqlrite-0.1.20 → sqlrite-0.1.21}/examples/wasm/index.html +0 -0
- {sqlrite-0.1.20 → sqlrite-0.1.21}/images/SQLRite - Desktop.png +0 -0
- {sqlrite-0.1.20 → sqlrite-0.1.21}/images/SQLRite Data Structures.png +0 -0
- {sqlrite-0.1.20 → sqlrite-0.1.21}/images/SQLRite Simple SQL Execution High Level Diagram.png +0 -0
- {sqlrite-0.1.20 → sqlrite-0.1.21}/images/SQLRite Simple SQL INSERT Execution High Level Diagram (Insert Row).png +0 -0
- {sqlrite-0.1.20 → sqlrite-0.1.21}/images/SQLRite Simple SQL INSERT Execution High Level Diagram.png +0 -0
- {sqlrite-0.1.20 → sqlrite-0.1.21}/images/SQLRite_logo.png +0 -0
- {sqlrite-0.1.20 → sqlrite-0.1.21}/images/architecture.png +0 -0
- {sqlrite-0.1.20 → sqlrite-0.1.21}/rust-toolchain.toml +0 -0
- {sqlrite-0.1.20 → sqlrite-0.1.21}/samples/AST.delete.example +0 -0
- {sqlrite-0.1.20 → sqlrite-0.1.21}/samples/AST.insert.exemple +0 -0
- {sqlrite-0.1.20 → sqlrite-0.1.21}/samples/AST.select.example +0 -0
- {sqlrite-0.1.20 → sqlrite-0.1.21}/samples/AST.update.example +0 -0
- {sqlrite-0.1.20 → sqlrite-0.1.21}/samples/CREATE TABLE sqlrite_schema.sql +0 -0
- {sqlrite-0.1.20 → sqlrite-0.1.21}/samples/CREATE_TABLE with duplicate.sql +0 -0
- {sqlrite-0.1.20 → sqlrite-0.1.21}/samples/CREATE_TABLE.sql +0 -0
- {sqlrite-0.1.20 → sqlrite-0.1.21}/samples/INSERT.sql +0 -0
- {sqlrite-0.1.20 → sqlrite-0.1.21}/scripts/bump-version.sh +0 -0
- {sqlrite-0.1.20 → sqlrite-0.1.21}/sdk/go/README.md +0 -0
- {sqlrite-0.1.20 → sqlrite-0.1.21}/sdk/go/conn.go +0 -0
- {sqlrite-0.1.20 → sqlrite-0.1.21}/sdk/go/go.mod +0 -0
- {sqlrite-0.1.20 → sqlrite-0.1.21}/sdk/go/rows.go +0 -0
- {sqlrite-0.1.20 → sqlrite-0.1.21}/sdk/go/sqlrite.go +0 -0
- {sqlrite-0.1.20 → sqlrite-0.1.21}/sdk/go/sqlrite_test.go +0 -0
- {sqlrite-0.1.20 → sqlrite-0.1.21}/sdk/go/stmt.go +0 -0
- {sqlrite-0.1.20 → sqlrite-0.1.21}/sdk/python/tests/test_sqlrite.py +0 -0
- {sqlrite-0.1.20 → sqlrite-0.1.21}/sqlrite-ask/src/lib.rs +0 -0
- {sqlrite-0.1.20 → sqlrite-0.1.21}/sqlrite-ask/src/prompt.rs +0 -0
- {sqlrite-0.1.20 → sqlrite-0.1.21}/sqlrite-ask/src/provider/anthropic.rs +0 -0
- {sqlrite-0.1.20 → sqlrite-0.1.21}/sqlrite-ask/src/provider/mock.rs +0 -0
- {sqlrite-0.1.20 → sqlrite-0.1.21}/sqlrite-ask/src/provider/mod.rs +0 -0
- {sqlrite-0.1.20 → sqlrite-0.1.21}/sqlrite-ask/tests/anthropic_http.rs +0 -0
- {sqlrite-0.1.20 → sqlrite-0.1.21}/src/ask/mod.rs +0 -0
- {sqlrite-0.1.20 → sqlrite-0.1.21}/src/ask/schema.rs +0 -0
- {sqlrite-0.1.20 → sqlrite-0.1.21}/src/connection.rs +0 -0
- {sqlrite-0.1.20 → sqlrite-0.1.21}/src/error.rs +0 -0
- {sqlrite-0.1.20 → sqlrite-0.1.21}/src/lib.rs +0 -0
- {sqlrite-0.1.20 → sqlrite-0.1.21}/src/main.rs +0 -0
- {sqlrite-0.1.20 → sqlrite-0.1.21}/src/meta_command/mod.rs +0 -0
- {sqlrite-0.1.20 → sqlrite-0.1.21}/src/repl/mod.rs +0 -0
- {sqlrite-0.1.20 → sqlrite-0.1.21}/src/sql/db/database.rs +0 -0
- {sqlrite-0.1.20 → sqlrite-0.1.21}/src/sql/db/mod.rs +0 -0
- {sqlrite-0.1.20 → sqlrite-0.1.21}/src/sql/db/secondary_index.rs +0 -0
- {sqlrite-0.1.20 → sqlrite-0.1.21}/src/sql/db/table.rs +0 -0
- {sqlrite-0.1.20 → sqlrite-0.1.21}/src/sql/executor.rs +0 -0
- {sqlrite-0.1.20 → sqlrite-0.1.21}/src/sql/hnsw.rs +0 -0
- {sqlrite-0.1.20 → sqlrite-0.1.21}/src/sql/mod.rs +0 -0
- {sqlrite-0.1.20 → sqlrite-0.1.21}/src/sql/pager/cell.rs +0 -0
- {sqlrite-0.1.20 → sqlrite-0.1.21}/src/sql/pager/file.rs +0 -0
- {sqlrite-0.1.20 → sqlrite-0.1.21}/src/sql/pager/header.rs +0 -0
- {sqlrite-0.1.20 → sqlrite-0.1.21}/src/sql/pager/hnsw_cell.rs +0 -0
- {sqlrite-0.1.20 → sqlrite-0.1.21}/src/sql/pager/index_cell.rs +0 -0
- {sqlrite-0.1.20 → sqlrite-0.1.21}/src/sql/pager/interior_page.rs +0 -0
- {sqlrite-0.1.20 → sqlrite-0.1.21}/src/sql/pager/mod.rs +0 -0
- {sqlrite-0.1.20 → sqlrite-0.1.21}/src/sql/pager/overflow.rs +0 -0
- {sqlrite-0.1.20 → sqlrite-0.1.21}/src/sql/pager/page.rs +0 -0
- {sqlrite-0.1.20 → sqlrite-0.1.21}/src/sql/pager/pager.rs +0 -0
- {sqlrite-0.1.20 → sqlrite-0.1.21}/src/sql/pager/table_page.rs +0 -0
- {sqlrite-0.1.20 → sqlrite-0.1.21}/src/sql/pager/varint.rs +0 -0
- {sqlrite-0.1.20 → sqlrite-0.1.21}/src/sql/pager/wal.rs +0 -0
- {sqlrite-0.1.20 → sqlrite-0.1.21}/src/sql/parser/create.rs +0 -0
- {sqlrite-0.1.20 → sqlrite-0.1.21}/src/sql/parser/insert.rs +0 -0
- {sqlrite-0.1.20 → sqlrite-0.1.21}/src/sql/parser/mod.rs +0 -0
- {sqlrite-0.1.20 → sqlrite-0.1.21}/src/sql/parser/select.rs +0 -0
- {sqlrite-0.1.20 → sqlrite-0.1.21}/src/sql/tokenizer.rs +0 -0
|
@@ -3804,7 +3804,7 @@ dependencies = [
|
|
|
3804
3804
|
|
|
3805
3805
|
[[package]]
|
|
3806
3806
|
name = "sqlrite-ask"
|
|
3807
|
-
version = "0.1.
|
|
3807
|
+
version = "0.1.21"
|
|
3808
3808
|
dependencies = [
|
|
3809
3809
|
"serde",
|
|
3810
3810
|
"serde_json",
|
|
@@ -3815,7 +3815,7 @@ dependencies = [
|
|
|
3815
3815
|
|
|
3816
3816
|
[[package]]
|
|
3817
3817
|
name = "sqlrite-desktop"
|
|
3818
|
-
version = "0.1.
|
|
3818
|
+
version = "0.1.21"
|
|
3819
3819
|
dependencies = [
|
|
3820
3820
|
"serde",
|
|
3821
3821
|
"serde_json",
|
|
@@ -3827,7 +3827,7 @@ dependencies = [
|
|
|
3827
3827
|
|
|
3828
3828
|
[[package]]
|
|
3829
3829
|
name = "sqlrite-engine"
|
|
3830
|
-
version = "0.1.
|
|
3830
|
+
version = "0.1.21"
|
|
3831
3831
|
dependencies = [
|
|
3832
3832
|
"clap",
|
|
3833
3833
|
"env_logger",
|
|
@@ -3844,7 +3844,7 @@ dependencies = [
|
|
|
3844
3844
|
|
|
3845
3845
|
[[package]]
|
|
3846
3846
|
name = "sqlrite-ffi"
|
|
3847
|
-
version = "0.1.
|
|
3847
|
+
version = "0.1.21"
|
|
3848
3848
|
dependencies = [
|
|
3849
3849
|
"cbindgen",
|
|
3850
3850
|
"sqlrite-engine",
|
|
@@ -3852,7 +3852,7 @@ dependencies = [
|
|
|
3852
3852
|
|
|
3853
3853
|
[[package]]
|
|
3854
3854
|
name = "sqlrite-nodejs"
|
|
3855
|
-
version = "0.1.
|
|
3855
|
+
version = "0.1.21"
|
|
3856
3856
|
dependencies = [
|
|
3857
3857
|
"napi",
|
|
3858
3858
|
"napi-build",
|
|
@@ -3862,7 +3862,7 @@ dependencies = [
|
|
|
3862
3862
|
|
|
3863
3863
|
[[package]]
|
|
3864
3864
|
name = "sqlrite-python"
|
|
3865
|
-
version = "0.1.
|
|
3865
|
+
version = "0.1.21"
|
|
3866
3866
|
dependencies = [
|
|
3867
3867
|
"pyo3",
|
|
3868
3868
|
"sqlrite-engine",
|
|
@@ -27,7 +27,7 @@ resolver = "3"
|
|
|
27
27
|
# `package =` key so the import name stays `sqlrite` internally:
|
|
28
28
|
# sqlrite = { package = "sqlrite-engine", path = "…" }
|
|
29
29
|
name = "sqlrite-engine"
|
|
30
|
-
version = "0.1.
|
|
30
|
+
version = "0.1.21"
|
|
31
31
|
authors = ["Joao Henrique Machado Silva <joaoh82@gmail.com>"]
|
|
32
32
|
edition = "2024"
|
|
33
33
|
rust-version = "1.85"
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: sqlrite
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.21
|
|
4
4
|
Classifier: Development Status :: 3 - Alpha
|
|
5
5
|
Classifier: Intended Audience :: Developers
|
|
6
6
|
Classifier: License :: OSI Approved :: MIT License
|
|
@@ -127,24 +127,82 @@ print(cur.fetchall()) # [('alice', 'integer')]
|
|
|
127
127
|
|
|
128
128
|
> `json_object_keys` returns a JSON-array text rather than a table-valued result (set-returning functions aren't supported yet).
|
|
129
129
|
|
|
130
|
-
### Natural-language → SQL (Phase 7g
|
|
130
|
+
### Natural-language → SQL (Phase 7g.4)
|
|
131
131
|
|
|
132
|
-
|
|
132
|
+
`Connection.ask(question)` generates SQL from a natural-language question via the configured LLM provider (Anthropic by default). `Connection.ask_run(question)` is the convenience that calls `ask()` then immediately executes the generated SQL.
|
|
133
133
|
|
|
134
134
|
```python
|
|
135
|
-
# 7g.4 preview — not yet released
|
|
136
135
|
import sqlrite
|
|
137
136
|
|
|
138
137
|
conn = sqlrite.connect("foo.sqlrite")
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
print(resp.sql) # "SELECT COUNT(*) FROM users WHERE age > 30"
|
|
142
|
-
print(resp.explanation) # "Counts users over the age threshold."
|
|
138
|
+
conn.execute("CREATE TABLE users (id INTEGER PRIMARY KEY, name TEXT, age INTEGER)")
|
|
139
|
+
conn.execute("INSERT INTO users (name, age) VALUES ('alice', 30)")
|
|
143
140
|
|
|
144
|
-
#
|
|
145
|
-
|
|
141
|
+
# Reads SQLRITE_LLM_API_KEY from the environment.
|
|
142
|
+
resp = conn.ask("How many users are over 30?")
|
|
143
|
+
print(resp.sql) # "SELECT COUNT(*) FROM users WHERE age > 30"
|
|
144
|
+
print(resp.explanation) # "Counts users over the age threshold."
|
|
145
|
+
|
|
146
|
+
# Caller decides whether to run the SQL — ask() does NOT auto-execute.
|
|
147
|
+
cur = conn.cursor()
|
|
148
|
+
cur.execute(resp.sql)
|
|
149
|
+
print(cur.fetchall()) # [(1,)]
|
|
150
|
+
|
|
151
|
+
# Or one-shot:
|
|
152
|
+
rows = conn.ask_run("How many users are over 30?").fetchall()
|
|
146
153
|
```
|
|
147
154
|
|
|
155
|
+
#### Configuration
|
|
156
|
+
|
|
157
|
+
Three precedence layers — explicit-wins:
|
|
158
|
+
|
|
159
|
+
1. **Per-call config**: `conn.ask("...", sqlrite.AskConfig(api_key="..."))`
|
|
160
|
+
2. **Per-connection config**: `conn.set_ask_config(cfg)` — applies to all subsequent `ask()` / `ask_run()` calls on this connection
|
|
161
|
+
3. **Environment vars** (zero-config fallback): `SQLRITE_LLM_PROVIDER` / `_API_KEY` / `_MODEL` / `_MAX_TOKENS` / `_CACHE_TTL`
|
|
162
|
+
|
|
163
|
+
```python
|
|
164
|
+
# Path 1: env (zero config) — same env vars as REPL/Desktop
|
|
165
|
+
resp = conn.ask("how many users?")
|
|
166
|
+
|
|
167
|
+
# Path 2: explicit per-call (highest precedence)
|
|
168
|
+
cfg = sqlrite.AskConfig(
|
|
169
|
+
api_key="sk-ant-...",
|
|
170
|
+
model="claude-haiku-4-5",
|
|
171
|
+
max_tokens=512,
|
|
172
|
+
cache_ttl="1h", # "5m" (default) | "1h" | "off"
|
|
173
|
+
)
|
|
174
|
+
resp = conn.ask("how many users?", cfg)
|
|
175
|
+
|
|
176
|
+
# Path 3: per-connection (set once, reuse)
|
|
177
|
+
conn.set_ask_config(cfg)
|
|
178
|
+
resp = conn.ask("how many users?") # uses cfg
|
|
179
|
+
resp = conn.ask("count by age") # uses cfg
|
|
180
|
+
|
|
181
|
+
# Or build from env explicitly:
|
|
182
|
+
cfg = sqlrite.AskConfig.from_env()
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
#### Defaults
|
|
186
|
+
|
|
187
|
+
`provider="anthropic"`, `model="claude-sonnet-4-6"`, `max_tokens=1024`, `cache_ttl="5m"`. The schema dump goes inside an Anthropic prompt-cache breakpoint — repeat asks against the same DB hit the cache (verify via `resp.usage.cache_read_input_tokens`).
|
|
188
|
+
|
|
189
|
+
#### Errors
|
|
190
|
+
|
|
191
|
+
Missing API key → `sqlrite.SQLRiteError("missing API key (set SQLRITE_LLM_API_KEY ...)")`. API errors (4xx/5xx) bubble up as `SQLRiteError` with the status code + Anthropic's structured error message. `ask_run()` on an empty SQL response (model declined to generate) raises with the model's explanation rather than executing the empty string.
|
|
192
|
+
|
|
193
|
+
#### What `AskResponse` carries
|
|
194
|
+
|
|
195
|
+
```python
|
|
196
|
+
resp.sql # str — generated SQL, ready to execute (or '' if model declined)
|
|
197
|
+
resp.explanation # str — one-sentence rationale (may be empty)
|
|
198
|
+
resp.usage.input_tokens
|
|
199
|
+
resp.usage.output_tokens
|
|
200
|
+
resp.usage.cache_creation_input_tokens
|
|
201
|
+
resp.usage.cache_read_input_tokens
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
`AskConfig.__repr__` and `AskResponse.__repr__` deliberately don't include the API key — printing config in logs won't leak it.
|
|
205
|
+
|
|
148
206
|
## API surface
|
|
149
207
|
|
|
150
208
|
| Function / Method | Purpose |
|
|
@@ -252,7 +252,7 @@ let rows = conn.execute(&resp.sql)?;
|
|
|
252
252
|
- **✅ 7g.1 — `sqlrite-ask` crate (foundational, ~750 LOC code + tests).** New separate crate (not feature-gated on the engine) so the engine stays pure-SQL with no HTTP / async deps. Owns: provider adapters (Anthropic in 7g.1; OpenAI / Ollama follow-ups), prompt construction, schema introspection helper that walks `Database.tables` directly (typed walk — cheaper + more robust than reflecting through `sqlrite_master`), the `AskResponse` type, configuration loading from env (`SQLRITE_LLM_PROVIDER` / `_API_KEY` / `_MODEL` / `_MAX_TOKENS` / `_CACHE_TTL`) or a passed config struct. Depends on `sqlrite-engine` for the schema introspection. Public API: `ask()` free function, `ConnectionAskExt::ask` trait extension on `sqlrite::Connection`, `AskConfig::from_env()`, `AskResponse { sql, explanation, usage }`. Default model `claude-sonnet-4-6` per the cost-quality NL→SQL sweet spot. Sync `ureq` HTTP (rejected reqwest::blocking — pulls tokio in even on the blocking path); JSON request/response shapes hand-rolled in serde_json (~120 LOC) — there is no official Anthropic Rust SDK, and rolling our own matches Q5's "build it yourself" theme. Schema dump goes inside a `<schema>...</schema>` block with `cache_control: ephemeral` so repeat asks against the same DB hit Anthropic's prompt cache (5-min TTL default; 1-hour TTL via `CacheTtl::OneHour`). Output parsing is tolerant — strict JSON, fenced JSON, or JSON-with-leading-prose all parse — because real LLM output drifts even with strict prompts. 30 tests pass (26 unit + 4 integration via `tiny_http` localhost mock).
|
|
253
253
|
- **✅ 7g.2 — REPL `.ask` + structural refactor.** New `MetaCommand::Ask(String)` variant + `handle_ask` that calls `sqlrite::ask::ask_with_database`, prints the generated SQL + rationale, prompts `Run? [Y/n] ` via rustyline (Ctrl-C / EOF map to skip — paranoid default for LLM-generated SQL), and pipes through `process_command` if confirmed. The thin REPL part itself is ~120 LOC. **Required a bigger structural fix that 7g.1 didn't anticipate:** wiring the binary to call into `sqlrite-ask` created a cargo cycle (`sqlrite-engine[bin] → sqlrite-ask → sqlrite-engine[lib]`) that even `optional = true` doesn't break — cargo's static cycle detection includes all potential edges. Resolution: flipped the dep direction. `sqlrite-ask` is now pure (no engine dep, canonical API takes a `&str` schema dump + `&str` question). Schema introspection + the `Connection`/`Database` integration (`ConnectionAskExt`, `ask`, `ask_with_database`, `ask_with_provider`, `ask_with_database_and_provider`) moved into `sqlrite-engine` itself under a new `ask` feature (default-on for the CLI binary, off for `--no-default-features` / WASM). Public API for end users is unchanged in spirit: `use sqlrite::{Connection, ConnectionAskExt}` instead of `use sqlrite_ask::ConnectionAskExt`. Net effect — `sqlrite-ask` shrunk + got easier to test in isolation; the engine carries the integration weight, scoped behind a feature flag so SDK / WASM / Tauri builds opt in. 30+ tests pass on both sides (sqlrite-ask: 20 unit + 4 integration; engine ask module: 6 schema + 3 .ask parser).
|
|
254
254
|
- **✅ 7g.3 — Desktop UI "Ask…" button.** New "Ask…" button in the editor toolbar plus a slide-in composer panel above the editor surface (question textarea + "Generate SQL" button + explanation slot). Submitting calls a new `ask_sql` Tauri command that reads `AskConfig::from_env()` server-side, locks the engine's `Database`, calls `sqlrite::ask::ask_with_database` (so the schema dump + LLM HTTP call stay in the Rust backend — the API key never crosses into the webview). Generated SQL drops into the editor textarea for the user to review + Run themselves; the rationale shows in the panel; an empty SQL response (model declined) surfaces the model's explanation in the same slot. Cmd/Ctrl+Enter in the question textarea submits; Esc closes. ~110 LOC of Svelte + ~30 LOC of Rust + ~85 LOC of CSS. As a side benefit, ergonomic re-exports added on `sqlrite::ask::*` (`AskConfig`, `AskResponse`, `AskError`, `Provider`, etc.) so library callers don't have to add `sqlrite-ask` as a direct dep alongside the engine.
|
|
255
|
-
-
|
|
255
|
+
- **✅ 7g.4 — Python SDK `conn.ask` / `ask_run` / `AskConfig`.** PyO3 wrappers over `sqlrite::ask::*`. New `Connection.ask(question, config=None)` returns an `AskResponse` (`.sql` / `.explanation` / `.usage` with cache-hit fields). `Connection.ask_run(question, config=None)` generates SQL then immediately executes via cursor — convenience for one-shot scripts/notebooks. `Connection.set_ask_config(cfg)` stashes a per-connection config. `AskConfig(api_key=..., model=..., max_tokens=..., cache_ttl=..., base_url=...)` constructor + `AskConfig.from_env()` static method. Three precedence layers: per-call > per-connection > env > defaults. `AskConfig.__repr__` and `AskResponse.__repr__` deliberately omit the API key — printing config in logs won't leak it. Empty SQL response (model declined) on `ask_run()` raises `SQLRiteError` with the model's explanation rather than executing the empty string. ~370 LOC of Rust binding + 415 LOC of pytest tests across 20 cases (config construction, env-var parsing, error paths, full happy-path through a localhost HTTP mock built on Python's stdlib `http.server`). README rewritten — three precedence layers explained, defaults enumerated, errors documented.
|
|
256
256
|
- **7g.5 — Node.js SDK (~80 LOC).** Same shape via napi-rs. `db.ask(question)` / `db.askRun(question)`.
|
|
257
257
|
- **7g.6 — Go SDK (~80 LOC).** cgo wrapper. `sqlrite.Ask(db, question)` returns `(AskResponse, error)`.
|
|
258
258
|
- **7g.7 — WASM SDK (~150 LOC, see Q9).** Either skipped, or implemented with a JS-side fetch hook (the WASM binary calls back into JS to make the HTTP request, since `reqwest`'s wasm32 story is messy and CORS/keys are a separate problem).
|
|
@@ -476,7 +476,7 @@ Approved sub-phases (Q1–Q10 resolved):
|
|
|
476
476
|
- **✅ 7d — HNSW ANN index** — three PRs: 7d.1 (algorithm w/ recall@10 ≥ 0.95), 7d.2 (SQL integration + query optimizer), 7d.3 (persistence + DELETE/UPDATE rebuild). `CREATE INDEX … USING hnsw (col)`; fixed defaults `M=16, ef_construction=200, ef_search=50` (Q2). New `KIND_HNSW` cell tag.
|
|
477
477
|
- **✅ 7e — JSON column type + path queries** — `JSON` data type stored as canonical text (validated via `serde_json::from_str` at INSERT/UPDATE time; SQLite-JSON1-style — Q3 scope correction since bincode was removed in Phase 3c). Functions: `json_extract` / `json_type` / `json_array_length` / `json_object_keys`. Path subset supports `$`, `.key`, `[N]`, chained. `json_object_keys` returns a JSON-array text rather than a table-valued result (no set-returning functions in the executor yet).
|
|
478
478
|
- **7f — ~~Full-text search with BM25~~** — **deferred to Phase 8** (Q1).
|
|
479
|
-
- **7g — `ask()` API across the product surface** — natural-language → SQL via Anthropic API (Q4), Anthropic-first then OpenAI + Ollama follow-ups. Foundational **✅ 7g.1** introduces a new `sqlrite-ask` crate (Q10 — separate crate, not a feature flag) — `ask_with_schema()` over `&str` inputs (Phase 7g.2 made it pure — see retrospective below), sync `ureq` POST to `/v1/messages`, schema-aware prompt with prompt-caching on the schema dump (Sonnet 4.6 default; configurable). **✅ 7g.2** wires the REPL's `.ask` meta-command (`MetaCommand::Ask(String)` + confirm-and-run UX) and adds the `sqlrite::ask` module on the engine side (gated under a new `ask` feature) carrying `ConnectionAskExt` + the schema introspection helper. **✅ 7g.3** adds the desktop "Ask…" composer (slide-in panel above the editor; Tauri command runs the LLM call in the Rust backend so the API key stays out of the webview). Per-product adapters in 7g.
|
|
479
|
+
- **7g — `ask()` API across the product surface** — natural-language → SQL via Anthropic API (Q4), Anthropic-first then OpenAI + Ollama follow-ups. Foundational **✅ 7g.1** introduces a new `sqlrite-ask` crate (Q10 — separate crate, not a feature flag) — `ask_with_schema()` over `&str` inputs (Phase 7g.2 made it pure — see retrospective below), sync `ureq` POST to `/v1/messages`, schema-aware prompt with prompt-caching on the schema dump (Sonnet 4.6 default; configurable). **✅ 7g.2** wires the REPL's `.ask` meta-command (`MetaCommand::Ask(String)` + confirm-and-run UX) and adds the `sqlrite::ask` module on the engine side (gated under a new `ask` feature) carrying `ConnectionAskExt` + the schema introspection helper. **✅ 7g.3** adds the desktop "Ask…" composer (slide-in panel above the editor; Tauri command runs the LLM call in the Rust backend so the API key stays out of the webview). **✅ 7g.4** ships the Python SDK surface — `conn.ask(question, config=None)` returns an `AskResponse(.sql, .explanation, .usage)`; `conn.ask_run()` adds the one-shot generate-and-execute convenience; `AskConfig` carries the three-layer precedence (per-call > per-connection > env > defaults). Per-product adapters in 7g.5-7g.8 cover Node.js, Go, WASM (JS-callback shape per Q9), and the MCP `ask` tool — and fold in the SDK README catch-up for VECTOR / JSON / HNSW capabilities along the way.
|
|
480
480
|
- **7h — MCP server adapter** — new `sqlrite-mcp` binary, hand-rolled JSON-RPC + tool framework (Q5).
|
|
481
481
|
|
|
482
482
|
Total scope budget: ~3-4 kLOC of new Rust across the wave. Each sub-phase ships as its own PR + release wave through the Phase 6 pipeline. The Phase 7 wave will likely close out **v0.2.0** (first minor bump after the 0.1.x Phase 6 cycle). Two new product lines added to lockstep versioning: `sqlrite-ask` and `sqlrite-mcp`.
|
|
@@ -4,7 +4,7 @@ build-backend = "maturin"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "sqlrite"
|
|
7
|
-
version = "0.1.
|
|
7
|
+
version = "0.1.21"
|
|
8
8
|
description = "Python bindings for SQLRite — a small, embeddable SQLite clone written in Rust."
|
|
9
9
|
authors = [{ name = "Joao Henrique Machado Silva", email = "joaoh82@gmail.com" }]
|
|
10
10
|
license = { text = "MIT" }
|
|
@@ -102,24 +102,82 @@ print(cur.fetchall()) # [('alice', 'integer')]
|
|
|
102
102
|
|
|
103
103
|
> `json_object_keys` returns a JSON-array text rather than a table-valued result (set-returning functions aren't supported yet).
|
|
104
104
|
|
|
105
|
-
### Natural-language → SQL (Phase 7g
|
|
105
|
+
### Natural-language → SQL (Phase 7g.4)
|
|
106
106
|
|
|
107
|
-
|
|
107
|
+
`Connection.ask(question)` generates SQL from a natural-language question via the configured LLM provider (Anthropic by default). `Connection.ask_run(question)` is the convenience that calls `ask()` then immediately executes the generated SQL.
|
|
108
108
|
|
|
109
109
|
```python
|
|
110
|
-
# 7g.4 preview — not yet released
|
|
111
110
|
import sqlrite
|
|
112
111
|
|
|
113
112
|
conn = sqlrite.connect("foo.sqlrite")
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
print(resp.sql) # "SELECT COUNT(*) FROM users WHERE age > 30"
|
|
117
|
-
print(resp.explanation) # "Counts users over the age threshold."
|
|
113
|
+
conn.execute("CREATE TABLE users (id INTEGER PRIMARY KEY, name TEXT, age INTEGER)")
|
|
114
|
+
conn.execute("INSERT INTO users (name, age) VALUES ('alice', 30)")
|
|
118
115
|
|
|
119
|
-
#
|
|
120
|
-
|
|
116
|
+
# Reads SQLRITE_LLM_API_KEY from the environment.
|
|
117
|
+
resp = conn.ask("How many users are over 30?")
|
|
118
|
+
print(resp.sql) # "SELECT COUNT(*) FROM users WHERE age > 30"
|
|
119
|
+
print(resp.explanation) # "Counts users over the age threshold."
|
|
120
|
+
|
|
121
|
+
# Caller decides whether to run the SQL — ask() does NOT auto-execute.
|
|
122
|
+
cur = conn.cursor()
|
|
123
|
+
cur.execute(resp.sql)
|
|
124
|
+
print(cur.fetchall()) # [(1,)]
|
|
125
|
+
|
|
126
|
+
# Or one-shot:
|
|
127
|
+
rows = conn.ask_run("How many users are over 30?").fetchall()
|
|
121
128
|
```
|
|
122
129
|
|
|
130
|
+
#### Configuration
|
|
131
|
+
|
|
132
|
+
Three precedence layers — explicit-wins:
|
|
133
|
+
|
|
134
|
+
1. **Per-call config**: `conn.ask("...", sqlrite.AskConfig(api_key="..."))`
|
|
135
|
+
2. **Per-connection config**: `conn.set_ask_config(cfg)` — applies to all subsequent `ask()` / `ask_run()` calls on this connection
|
|
136
|
+
3. **Environment vars** (zero-config fallback): `SQLRITE_LLM_PROVIDER` / `_API_KEY` / `_MODEL` / `_MAX_TOKENS` / `_CACHE_TTL`
|
|
137
|
+
|
|
138
|
+
```python
|
|
139
|
+
# Path 1: env (zero config) — same env vars as REPL/Desktop
|
|
140
|
+
resp = conn.ask("how many users?")
|
|
141
|
+
|
|
142
|
+
# Path 2: explicit per-call (highest precedence)
|
|
143
|
+
cfg = sqlrite.AskConfig(
|
|
144
|
+
api_key="sk-ant-...",
|
|
145
|
+
model="claude-haiku-4-5",
|
|
146
|
+
max_tokens=512,
|
|
147
|
+
cache_ttl="1h", # "5m" (default) | "1h" | "off"
|
|
148
|
+
)
|
|
149
|
+
resp = conn.ask("how many users?", cfg)
|
|
150
|
+
|
|
151
|
+
# Path 3: per-connection (set once, reuse)
|
|
152
|
+
conn.set_ask_config(cfg)
|
|
153
|
+
resp = conn.ask("how many users?") # uses cfg
|
|
154
|
+
resp = conn.ask("count by age") # uses cfg
|
|
155
|
+
|
|
156
|
+
# Or build from env explicitly:
|
|
157
|
+
cfg = sqlrite.AskConfig.from_env()
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
#### Defaults
|
|
161
|
+
|
|
162
|
+
`provider="anthropic"`, `model="claude-sonnet-4-6"`, `max_tokens=1024`, `cache_ttl="5m"`. The schema dump goes inside an Anthropic prompt-cache breakpoint — repeat asks against the same DB hit the cache (verify via `resp.usage.cache_read_input_tokens`).
|
|
163
|
+
|
|
164
|
+
#### Errors
|
|
165
|
+
|
|
166
|
+
Missing API key → `sqlrite.SQLRiteError("missing API key (set SQLRITE_LLM_API_KEY ...)")`. API errors (4xx/5xx) bubble up as `SQLRiteError` with the status code + Anthropic's structured error message. `ask_run()` on an empty SQL response (model declined to generate) raises with the model's explanation rather than executing the empty string.
|
|
167
|
+
|
|
168
|
+
#### What `AskResponse` carries
|
|
169
|
+
|
|
170
|
+
```python
|
|
171
|
+
resp.sql # str — generated SQL, ready to execute (or '' if model declined)
|
|
172
|
+
resp.explanation # str — one-sentence rationale (may be empty)
|
|
173
|
+
resp.usage.input_tokens
|
|
174
|
+
resp.usage.output_tokens
|
|
175
|
+
resp.usage.cache_creation_input_tokens
|
|
176
|
+
resp.usage.cache_read_input_tokens
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
`AskConfig.__repr__` and `AskResponse.__repr__` deliberately don't include the API key — printing config in logs won't leak it.
|
|
180
|
+
|
|
123
181
|
## API surface
|
|
124
182
|
|
|
125
183
|
| Function / Method | Purpose |
|