NEDB Docs / Running it / Cast — the AI planner

Cast turns a short English prompt into a query plan, using a 3.33M-parameter model that runs locally on CPU — no API key, no network call, no per-token bill. Ten clauses and six operators is the whole grammar; small enough that a small model can learn it completely, and small enough that shipping every query to a frontier model is an absurd amount of machinery.

The safety properties

PropertyWhat it means
The model never executesIt emits text; the text goes through the same query path a hand-typed query uses. There is no second executor to audit.
Validation is parsingParse and execute share one code path, so they cannot disagree about what is well-formed. Invalid output is refused with the offending text.
execute defaults to falseYou get a plan for review. Running a guess silently is worse than admitting uncertainty.

That last default earns its keep. A real miss: "paid orders over 100" planned as LIMIT 100 instead of total > 100 — and the count came back correct anyway, because both paid orders happened to exceed 100. A count-only assertion would have scored it a pass. A human reading the plan catches it; an auto-executing client does not.

Why the planner lives in the engine

The hard part of NL querying is the schema, and a client's copy is stale on arrival. The engine holds the live collection list, so a plan naming a collection that does not exist returns 422 with the reason — never silently empty rows, because zero rows reads as "no matching data", which would be a lie. Every daemon client inherits this.

Drift — the failure valid cannot catch

A model outside its vocabulary substitutes a memorised literal: "memories about pricing" became SEARCH "handoff" — which parses, names a real collection, returns real rows, and answers a question nobody asked. Measured on the released checkpoint: in-vocabulary terms copied 3/3; out-of-vocabulary terms 0/3. So the response carries a drift field when a quoted literal does not appear in the prompt — advisory, never fatal, validated at 24/24 with zero false alarms (correctly inferred enum values stay silent). An unattended caller should gate on all three: plan["valid"] and plan["collection_known"] and not plan.get("drift").

Where it is strong (and weak) — measured

ClauseExact-plan match (eval)
TRACE caused_by96.5%
TRAVERSE93.3%
single WHERE91.2%
LIMIT91.1%
SEARCH90.5%
ORDER BY87.7%
two+ WHERE85.1% (61.2% adversarial holdout)
GROUP BY + aggregate77.0%

Two habits avoid most misses: name the field when a number could be a limit ("orders with total over 100" beats "orders over 100"), and check numbers over four digits — digits tokenize one at a time, so 400000 can come back 4000. The model card publishes every failure mode with examples.

Enabling it

# compile-time: cargo install nedb-engine --features cast
# weights (~13 MB): GitHub release asset, checksum-verified on load
nedbd --dag --cast ./data

Off by default, feature-gated at compile time and flag-gated at runtime. Built without the feature the route returns 501 (not 404) so clients can detect the capability. Without weights, the daemon logs loudly and serves everything else normally.

Dogfooding note: the model was trained with NEDB's own parser as corpus generator (200,000 pairs in 16.5 s, perfect labels), grader (parsed-plan equality, not string equality) and gate (no example enters unless it round-trips to a canonically identical plan). Training lineage — datasets → runs → checkpoints → evals — lives in a NEDB database, chained by caused_by. The engine did not just host the model; it built and audited it.