Phase 7g.7: WASM SDK askPrompt() / askParse() (Q9 split shape)#70
Merged
Conversation
The structurally different SDK in the wave: per Q9, the WASM module
does the schema-aware prompt construction in-page but does NOT make
the HTTP request itself. The JS caller's backend handles the LLM
API call. Browser tab never sees the API key, never POSTs to a
third-party LLM endpoint, never deals with CORS.
## Public surface — two-step JS API
```js
import init, { Database } from '@joaoh82/sqlrite-wasm';
await init();
const db = new Database();
db.exec(`CREATE TABLE users (id INTEGER PRIMARY KEY, name TEXT, age INTEGER)`);
// Step 1: build the request body in-browser
const payload = db.askPrompt('How many users are over 30?');
// → Anthropic /v1/messages body: { model, max_tokens, system, messages }
// Step 2: caller's backend forwards to Anthropic with the API key
const apiResponse = await fetch('/api/llm/complete', {
method: 'POST',
body: JSON.stringify(payload),
}).then(r => r.text());
// Step 3: WASM parses the response back
const result = db.askParse(apiResponse);
// → { sql, explanation, usage: {...} }
```
Backend proxy is ~10 lines of Express / Cloudflare Worker / Vercel
Edge — README has the worked example (Q9 doc requirement).
## What's new
* `Database.askPrompt(question, options?)` — builds Anthropic's
/v1/messages request body. The cache_control marker on the
schema block is preserved so prompt caching works through the
proxy.
* `Database.askParse(rawApiResponse)` — parses the full Anthropic
API response (text content + usage) back into
`{ sql, explanation, usage: { inputTokens, outputTokens,
cacheCreationInputTokens, cacheReadInputTokens } }`.
Tolerant of fenced JSON / leading prose in the model's text
(same parser the other SDKs use).
* `AskPromptOptions` class with `model`, `maxTokens`, `cacheTtl`
fields. Defaults match every other SDK: claude-sonnet-4-6 /
1024 / 5m.
## Required structural refactor
Wiring the WASM SDK forced three feature/visibility changes that
weren't strictly necessary before:
1. **`sqlrite-ask` got an `http` feature flag.** `ureq` is now
optional behind `[features] default = ["http"], http =
["dep:ureq"]`. The `AnthropicProvider`, `ask_with_schema`, and
`provider::anthropic` module are all gated on `http`. The
wasm-safe parts — `prompt::build_system`, `parse_response`,
`AskConfig`, `AskResponse`, `AskUsage`, `AskError`, `Provider`
trait, generic `ask_with_schema_and_provider<P>` — stay
always-on. The WASM SDK depends on sqlrite-ask with
`default-features = false`.
2. **Engine: `sqlrite::ask::schema` un-gated.** The schema dump is
pure-engine code (no sqlrite-ask dep), but it lived inside the
`#[cfg(feature = "ask")]` module so wasm-safe consumers couldn't
reach it. Moved the `schema` submodule to always-on; gated only
the sqlrite-ask-importing parts (ConnectionAskExt + free
functions) under the `ask` feature.
3. **`sqlrite_ask::parse_response` made public.** Was `fn`-private
before; the WASM SDK calls it on the model's text content after
JS hands the API response back through `askParse`.
Verified all three configurations build clean:
* `cargo check -p sqlrite-engine --features cli,ask,file-locks`
— REPL binary build (existing, unchanged).
* `cargo check -p sqlrite-engine --no-default-features` — minimal
library build (existing, unchanged).
* `cd sdk/wasm && wasm-pack build --target web` — wasm32 target
builds clean (~30s release build), generates `pkg/sqlrite_wasm.d.ts`
with proper TypeScript types for `askPrompt` / `askParse` /
`AskPromptOptions`.
## Why JSON-as-Anthropic-shape rather than a provider-neutral form
`askPrompt` returns Anthropic's `/v1/messages` body shape directly.
The JS caller can forward it as-is to Anthropic without translation.
Non-Anthropic providers (OpenAI, Ollama) translate server-side on
the user's backend — the WASM SDK doesn't need to know about other
providers, and the user's backend is the natural place to handle
provider variety anyway. This matches how the WASM split addresses
key safety in the first place: the trust boundary is the user's
backend, so logic that depends on the choice of provider lives
there too.
## Tests
This is a JS-API SDK whose Rust side has no testable behavior
beyond "delegates to existing infrastructure" — no new unit tests
on the WASM side. Verification path:
* `cargo test --workspace --exclude sqlrite-desktop --exclude
sqlrite-python --exclude sqlrite-nodejs` — 301/301 still pass
(the engine + sqlrite-ask refactors moved code without changing
behavior).
* `cd sdk/wasm && cargo check && wasm-pack build --target web` —
both clean.
* Browser-side smoke: load the build into the existing
`examples/wasm/` console, run a CREATE TABLE + askPrompt() +
look at the JSON.
## Docs
* `sdk/wasm/README.md` — "coming soon" section replaced with the
full Q9-required worked example: two-step flow, complete
Node/Express backend proxy showing where the API key lives,
`AskPromptOptions` reference, non-Anthropic-provider note,
cache-hit verification via `usage.cacheReadInputTokens`. About
100 LOC of new prose.
* `docs/phase-7-plan.md` — 7g.7 marked ✅ with the structural-
refactor list and the `wasm-pack build` verification note.
* `docs/roadmap.md` — 7g bullet updated; 7g.8 (MCP `ask` tool) is
the only sub-phase left.
cargo fmt clean across all touched crates. Pre-existing 2 warnings
on `sdk/wasm/src/lib.rs` are unchanged (cosmetic `let mut` on lines
that don't need it; in functions I didn't touch).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Per Q9, the WASM SDK has a different
ask()shape than every other SDK. The browser tab does the schema-aware prompt construction in-page, but does NOT make the HTTP call itself. The JS caller's backend handles the LLM API call. Browser tab never sees the API key, never POSTs to a third-party LLM endpoint, never deals with CORS.The complete worked example (with a minimal Node/Express backend proxy showing where the API key lives) is in
sdk/wasm/README.mdper Q9's documentation requirement.Why this design (recap from Q9)
Two reasons direct browser-to-LLM calls don't work:
api.anthropic.comunless the LLM provider serves CORS headers. They don't, by design — they don't want users embedding API keys in client-side JS.Both problems disappear server-side. The WASM SDK split makes the trust boundary the user's own backend, where the API key already lives.
Required structural refactor (sqlrite-ask + engine)
Wiring 7g.7 forced three feature/visibility changes:
1.
sqlrite-askgot anhttpfeature flagAnthropicProvider,ask_with_schema, and theprovider::anthropicmodule are all gated onhttp. The wasm-safe parts —prompt::build_system,parse_response,AskConfig,AskResponse,AskUsage,AskError,Providertrait, genericask_with_schema_and_provider<P>— stay always-on.2. Engine:
sqlrite::ask::schemaun-gatedThe schema dump is pure-engine code (no sqlrite-ask dep), but it lived inside the
#[cfg(feature = "ask")]module. Movedpub mod schema;to always-on; gated only the sqlrite-ask-importing parts (ConnectionAskExt+ free functions) under theaskfeature.3.
sqlrite_ask::parse_responsemade publicWas
fn-private; the WASM SDK calls it on the model's text content after JS hands the API response back throughaskParse.Build verification — three feature configurations clean
cargo check -p sqlrite-engine --features cli,ask,file-lockscargo check -p sqlrite-engine --no-default-featurescd sdk/wasm && wasm-pack build --target webpkg/sqlrite_wasm.d.tswith TypeScript types foraskPrompt,askParse,AskPromptOptions.What's new (WASM API surface)
db.askPrompt(question, options?)Object/v1/messagesrequest body.db.askParse(rawApiResponse){ sql, explanation, usage }AskResponseshape.new AskPromptOptions()AskPromptOptionsmodel/maxTokens/cacheTtlfields. Defaults match every other SDK:claude-sonnet-4-6/1024/5m.AskPromptOptionsis the only WASM-side class becauseaskPromptis the only call that takes config (the LLM API call itself is the user's backend's responsibility).Tests
This is a JS-API SDK whose Rust side delegates to existing infrastructure — no new unit tests. Verification path:
cargo fmt --all -- --check— clean (engine + sqlrite-ask)(cd sdk/wasm && cargo fmt -- --check)— cleancargo test --workspace --exclude sqlrite-desktop --exclude sqlrite-python --exclude sqlrite-nodejs— 301/301 still pass (engine + sqlrite-ask refactors moved code without changing behavior)cd sdk/wasm && cargo check— native lib builds (rlib only)cd sdk/wasm && wasm-pack build --target web— wasm32 target builds clean ✅cd sdk/wasm && wasm-pack build --target web && python -m http.serveragainst the existingexamples/wasm/console; calldb.askPrompt('list users')and verify the JSON shapeWhy JSON-as-Anthropic-shape rather than provider-neutral
askPromptreturns Anthropic's/v1/messagesbody directly. The JS caller can forward it as-is to Anthropic without translation. Non-Anthropic providers (OpenAI, Ollama) translate server-side on the user's backend — the WASM SDK doesn't need to know about other providers, and the user's backend is the natural place to handle provider variety. This matches how the WASM split addresses key safety in the first place: the trust boundary is the user's backend, so logic that depends on the choice of provider lives there too.Docs
sdk/wasm/README.md— "coming soon" replaced with full Q9-required worked example: two-step flow, complete Node/Express backend proxy,AskPromptOptionsreference, non-Anthropic-provider note, cache-hit verification viausage.cacheReadInputTokens. About 100 LOC of new prose.docs/phase-7-plan.md— 7g.7 marked ✅ with the structural-refactor list.docs/roadmap.md— 7g bullet updated. 7g.8 (MCPasktool) is the only sub-phase left in 7g.Next up after merge
release-pr.ymlatv0.1.24→@joaoh82/sqlrite-wasm@0.1.24carries the newaskPrompt/askParsesurface.asktool. Hooks into the Phase 7h MCP server framework (deferred since 7h hasn't shipped yet — pivot here might be reordering 7h before 7g.8 to avoid blocking).sqlrite-mcpbinary + JSON-RPC + tool framework. New product line; same shape assqlrite-askjoined the lockstep wave in 7g.1.🤖 Generated with Claude Code