sqlrite-ask — natural-language → SQL adapter for SQLRite.
Phase 7g.2 made this crate pure — it no longer depends on
sqlrite-engine. The canonical API takes a &str schema dump
(built however you like — see sqlrite::ConnectionAskExt::ask
for the engine-side helper that wraps it):
use sqlrite_ask::{ask_with_schema, AskConfig};
let schema = "\
CREATE TABLE users (\n id INTEGER PRIMARY KEY,\n name TEXT NOT NULL\n);\n";
let cfg = AskConfig::from_env()?; // reads SQLRITE_LLM_API_KEY etc.
let resp = ask_with_schema(schema, "How many users?", &cfg)?;
println!("{}", resp.sql);
# Ok::<(), sqlrite_ask::AskError>(())
For the engine-integrated form (conn.ask("...", &cfg)), enable
the sqlrite-engine crate's ask feature and bring its
ConnectionAskExt trait into scope:
use sqlrite::{Connection, ConnectionAskExt};
use sqlrite_ask::AskConfig;
let conn = Connection::open("foo.sqlrite")?;
let cfg = AskConfig::from_env()?;
let resp = conn.ask("How many users?", &cfg)?;
Why the split (Phase 7g.2 retro)
Wiring the REPL's .ask meta-command would have required the
engine binary to depend on sqlrite-ask, but sqlrite-ask
already depended on sqlrite-engine (for Connection,
Database, Table). Cargo's static cycle detection rejects that
shape even with optional = true. Solution: keep this crate pure
over &str inputs, move the engine integration (schema dump +
ConnectionAskExt) into sqlrite-engine itself behind an ask
feature. Now there's one direction of dep flow: engine →
sqlrite-ask, never the other way.
What this crate is
- Provider adapters (Anthropic now; OpenAI / Ollama later) that
POST one HTTP request to a chat-completion endpoint per
ask()call. - Prompt construction with a
cache_control: ephemeralbreakpoint on the schema block, so repeat calls against the same schema served from Anthropic's prompt cache. - Output parsing tolerant to fenced JSON / leading prose / strict JSON (model output drifts even with strict instructions).
AskConfig(env vars + explicit overrides),AskResponse { sql, explanation, usage },AskError.
What this crate is NOT
- Not an executor. The caller decides whether to run the
generated SQL. SDK convenience wrappers (
Python.Connection .ask_run,Node.db.askRun, etc.) layer that on top. - Not multi-turn. Stateless — every call is a fresh prompt.
- Not engine-coupled. Schema introspection lives on the
engine side as of v0.1.19 — see
sqlrite::ConnectionAskExt.
Configuration
[AskConfig] resolves in this priority order:
- Explicit values on the struct.
- Environment variables (
SQLRITE_LLM_*). - Built-in defaults (model =
claude-sonnet-4-6, max_tokens = 1024, cache TTL = 5 min).