Replace the whole filter panel with one sentence.
Your users stop translating what they want into dropdowns, checkboxes and date pickers — they just say it, in their own words. QueryForge turns that into a query you can trust.
The model never writes the query. It fills in a typed Query AST your config constrains, and deterministic Go compiles that to whatever database you run — Postgres SQL and MongoDB today, and a new backend is a generator, not a rewrite.
See it replace a filter panel → Configuration reference → Build a config → View on GitHub
SELECT or a find. There is no operator or config option that can write, update, or delete — and the library never connects to your database. It hands you the query; running it stays yours.A user asks a question in their own words:
"orders over 500 dollars in the last 30 days that were not cancelled, newest first, top 20"
You get a parameterized query you can trust — values bound, never pasted in:
SELECT status, created_at, amount, customer_name FROM orders
WHERE (status <> $1 AND created_at >= $2 AND amount > $3)
ORDER BY created_at DESC LIMIT 20
args: ["CANCELLED", "2026-06-29T08:49:06Z", 500]
The same question, against MongoDB, compiled from the identical intermediate representation:
{ amount: {$gt: 500}, createdAt: {$gte: "2026-06-29T08:49:06Z"}, status: {$ne: "CANCELLED"} }
Because you cannot check what comes back. Ask a model for SQL directly and it can invent a column, invent a table, quietly widen a filter, or hand you a statement that is only probably right — and you have no way to tell before you run it.
QueryForge never lets the model near a query string. The model fills in a typed form — the Query AST — constrained to a vocabulary your config registers. Ordinary deterministic Go does everything after that.
Natural language → [ AI planner ] → Query AST → [ generators ] → SQL / Mongo
(unbounded) model + config (typed) pure code, no AI
Everything that must be guaranteed lives on the right-hand side, where it can be tested offline.
Rejected by the validator before anything compiles, with "did you mean" suggestions.
The model emits structure, never a string. Values are bound; only config-supplied identifiers reach the statement.
Not expressible. The AST has no mutation node, so the guarantee holds by construction.
returnable: false is enforced on the default projection too, not only explicit select.
You get a typed refusal, not a plausible query built on a lookalike field.
Scope filters are AND-ed on after the model has answered — it never learns the field exists.
Gemini, Groq, Anthropic, local Ollama — a config change, never a code change. Fallback chains included.
go get github.com/awsaman-ai/queryforge
cfg, _ := qf.LoadConfig("orders.config.json")
engine := qf.New(cfg)
res, err := engine.Translate(ctx, "cancelled orders over 200 dollars", "sql", nil)
fmt.Println(res.Query.SQL) // SELECT … WHERE (status = $1 AND amount > $2) …
fmt.Println(res.Query.Args) // [CANCELLED 200]
fmt.Println(res.Explain) // plain-English readback of what it understood
Core has no third-party dependencies — standard library only.
That last argument is the scope — filters your application imposes on every query, whatever the user asked. Pass nil for none, or a map for multi-tenancy:
res, err := engine.Translate(ctx, "cancelled orders over 200 dollars", "sql", qf.Scope{
"subscriptionId": session.SubscriptionID,
"userId": session.UserID,
})
// … WHERE (subscriptionId = $1 AND userId = $2 AND status = $3 AND amount > $4)
bugs.csv.The deterministic half of the pipeline — AST → validate → generate — is covered by this library's own test suite, with no API key and no network. The half that cannot be covered that way is comprehension: whether the model reads a real sentence the way your user meant it. That is a statistical property of a remote service, so it needs a corpus and a pass rate rather than an assertion.
qfeval is a separate tool for
exactly that. You give it a CSV of sentences and the queries they should compile to; it runs them
through the real engine and grades each one — matched exactly, matched in a different spelling,
correctly refused, or wrong. On the shipped 25-case example corpus,
gemini-3.1-flash-lite scores 24 / 25, and the single failure is a
genuinely ambiguous sentence rather than a misread one. Point it at your own config and your own
sentences before you decide which model to pay for.
Apache-2.0 licensed. Phase 1: SQL (Postgres dialect) and MongoDB.