Ivan Savchenko
← All demos
Tally

Ask your database in plain English. Built so it can only read, and shows its SQL.

Tally turns a business question into checked, read-only SQL, a chart and an answer whose numbers are checked against the result.

82.0%
execution accuracy, 89 answerable questions, as run (87.6% after fixes, re-asking only 7 questions)
0 of 15
unsafe queries executed out of 15 hostile requests
28.1% → 84.4%
without vs with the semantic layer, 32-question subset (9 → 27 right)
93.8% vs 84.4%
the whole schema beat retrieval on this small schema (same 32 questions)
Interactive demo

See which layer stops an unsafe request

An offline demo model, gullible on purpose, writes SQL for your request. Watch the validator, the cost guard and the read-only role decide, then switch layers off. The layers here are rough stand-ins written for this page (regexes, placeholder table sizes), not Tally's code. Ask about revenue last quarter to see Tally ask first, and twice: gross or net, then calendar or fiscal quarters.

A small browser version with a deterministic fake model — no API key, nothing leaves your device. The real project is in the repo.

What it solves

A manager who wants a number files a request with an analyst and waits, because the data sits in a database only a few people can query. A naive chat-with-your-database tool can run a query that changes data, hand out customer emails and phone numbers, or answer confidently with the wrong number.

How it works

  1. Retrieval finds the relevant tables, and a reviewed semantic layer supplies what the schema cannot say (net revenue means items minus refunds, in USD, without test accounts). Ambiguous terms such as gross or net, calendar or fiscal quarter get a question before any model call.
  2. The model writes the SQL, and plain code decides whether it runs: a sqlglot validator (one SELECT, allow-listed tables and functions, no personal-data columns, enforced LIMIT), an EXPLAIN cost guard, then a read-only database login with a timeout.
  3. SQL errors go back to the model for a bounded self-correction, while writes and personal data are blocked without a second try. Every number in the answer must appear in the result (or in the question or the SQL's literals), otherwise the model rewrites the answer once and then a template answer replaces it. The SQL is shown with a plain-English explanation.

The honest verdict

Measured on a synthetic 18-table database with 117 questions and requests that an AI agent wrote in the same session that built the repository, using the free model nemotron-3-super: execution accuracy was 82.0% as run on the 89 answerable questions. The 87.6% figure is not a clean re-run: after two bugs the run exposed were fixed, only the 7 affected questions were re-asked, on the same benchmark, so 82.0% is the number to trust. Between one answerable question in five (as run) and one in eight (after the re-ask) was missed. Half of the 16 as-run misses were plausible SQL with a subtle error (a LIMIT 1 that drops a tie, a window that starts a month too late), which is why the SQL is shown next to every answer; the rest were provider refusals, a validator false positive, guessed literals that returned NULL, one arguably right answer and a needless clarification. None of the 15 unsafe requests executed anything unsafe, with the real free model or with an offline model that writes whatever it is asked: in the offline case the validator (12) and the cost guard (2) blocked 14 and the offline model itself refused 1. On a 32-question subset the semantic layer was the biggest lever (28.1% without it, 84.4% with it). Two assumptions failed: sending the whole 18-table schema beat retrieval (93.8% vs 84.4%), and the strict number check flagged a correct answer until it was fixed. Other limits: a median answer took 15.1 s, the free tier sometimes refused or rate-limited requests, Claude Opus and Qwen each had a question blocked by the function allow-list (fixed since; those evals were not re-run), and the safety layers protect the data, not the conclusion. Numbers on your schema will differ.

PythonFastAPIPostgreSQLsqlglot
Next demoScout →