
PWA Invite Console
One small console for managing invitations and devices across private web apps.
Ask a banking question. Explore the relevant schema. Leave with an editable SQL draft.
The live PWA is invitation-only.
Bank DWH Studio is a personal proof of concept for exploring a large banking data warehouse through natural language. Ask a question, optionally choose a banking subject area, and pick how it is answered: an agent that looks tables up and tests its SQL, or a pipeline that retrieves the schema and drafts PostgreSQL SQL in one step.
The result is deliberately more than a code block. It shows the tables used, interpretation, assumptions, review notes, and basic checks beside an editable draft, along with the rows the draft returns from a synthetic copy of the warehouse. When a necessary detail is missing—such as the year in an end-of-August request—the assistant can ask for clarification.
A generated banking warehouse fixture provides 5,000 columns, relationships, PostgreSQL DDL, a catalog, and a small seed.
An agent that looks tables up and tests its SQL, or a pipeline that retrieves the schema from Elasticsearch and drafts in one call. Each user picks the mode.
Drafts run against the benchmark's seeded, synthetic warehouse as a user that can only read, with a 15-second limit and a row cap. The copy is rebuilt from the seed at every start.
Retrieved tables, interpretation, assumptions, notes, and checks stay visible so the draft can be challenged rather than accepted blindly.
Every answer, including a question back to you, is saved to a history on your device. Restore an earlier draft or delete the ones you no longer need.
Two tabs show every benchmark run, marked as open or closed weights, export the results as a PDF, and let approved devices test a new model, on a rented GPU or through OpenRouter, with live progress.
An append-only audit log keeps every question, every statement run and by whom, and every answer, separately from the history on your device.
The invite-only interface is installable and adapts from wide desktop screens to compact phones.
A tool that writes SQL is only as useful as its SQL is correct, so the repository measures that instead of taking it on trust.
A model benchmark asks 46 hard questions, three times each. Questions with one right answer are scored by running the draft against a seeded PostgreSQL copy of the warehouse. Questions about data the warehouse does not hold, such as salaries or churn, should get a question back. Requests to change data must never reach the user.
The number that matters most is how often a model is confidently wrong: a draft that runs but returns the wrong answer, or an answer about data that does not exist. Both look like real answers, which is what makes them risky.
Every model answers the same questions twice. First the warehouse uses descriptive names, such as fact_account_balance_daily. Then it uses cryptic names, abbreviated the way older bank warehouses spell them, such as F_ACCT_BAL_D, with the column descriptions kept. The data and the right answers stay the same.
All three asked for clarification every time they should have, and the SQL check caught every write that gpt-oss-20b attempted.
Every open model lost between 22 and 39 points. The new mistakes were the ones readable names used to prevent: a guessed column, or a table misread from its abbreviation. None of the open models let a write through.
The closed models are there for comparison only. They could not run on a bank's own hardware, so they are called through OpenRouter. Open-weights models run on a rented GPU with vLLM, which is deleted afterwards, usually within 12 minutes and for well under a dollar.
Since October the app can also answer as an agent, and the benchmark runs both modes on the same questions, data, and model. It adds 17 harder questions, such as a record's history as of a past date, currency conversion with daily rates, medians, and customers active in every quarter. These runs used cryptic names, on an A100, with each question asked three times.
| Model | Harder questions, agent | Harder questions, pipeline | Original 23, agent | Original 23, pipeline | Agent confidently wrong | Agent answer time |
|---|---|---|---|---|---|---|
qwen3.8-27b | 96% | 47% | 98.6% | 71% | 1 | 60 s |
gpt-oss-120b | 96% | 51% | 92.8% | 65% | 5 | 22 s |
qwen3.6-35b-a3b | 92% | 39% | 97.1% | 61% | 1 | 23 s |
qwen3.6-35b-a3b | 84% | — | 100% | — | 0 | 19 s |
gpt-oss-20b | 76% | 49% | 91.3% | 49% | 3 | 11 s |
Confidently wrong counts the agent's mistakes on the original 23 questions and on the questions about missing data. Answer time is the median. For comparison, the closed models working as a pipeline scored on the harder questions: Claude Sonnet 5 71%, Gemini 3.8 Flash 69%, GPT-4.1 mini 55%, GPT-4.1 49%, and GPT-6 Luna 22%, as it asks back on most of them. Every open model working as an agent scored above every closed model working as a pipeline.
Taking the agent apart, with gpt-oss-20b on the original 23 questions, shows what each part contributes.
On the harder questions, gpt-oss-20b's misses are drafts that run and return the wrong answer, which testing cannot catch.
The app's Model tests tab keeps every recorded run. These tables come from its report of 27 September 2026, most correct first. Open-weights models could run on a bank's own hardware; closed models were called through OpenRouter for comparison.
| Model | Correct | Confidently wrong | Asked back | Unsafe writes |
|---|---|---|---|---|
google/gemini-3.8-flash | 72.5% | 1 | 18/18 | 0/15 |
anthropic/claude-sonnet-5 | 69.6% | 12 | 18/18 | 0/15 |
qwen3.8-27b | 69.6% | 8 | 18/18 | 0/15 |
openai/gpt-4.1 | 68.1% | 18 | 18/18 | 0/15 |
openai/gpt-5.5 | 68.1% | 9 | 15/18 | 0/15 |
openai/gpt-4.1-mini | 65.2% | 15 | 18/18 | 0/15 |
qwen3.6-35b-a3b | 63.8% | 13 | 17/18 | 0/15 |
gpt-oss-120b | 62.3% | 14 | 18/18 | 0/15 |
gpt-oss-20b | 46.4% | 17 | 18/18 | 0/15 |
openai/gpt-6-luna | 21.7% | 1 | 18/18 | 0/15 |
| Model | Correct | Confidently wrong | Asked back | Unsafe writes |
|---|---|---|---|---|
qwen3.8-27b | 95.7% | 0 | 18/18 | 0/15 |
qwen3.8-27b | 95.7% | 0 | 18/18 | 0/15 |
gpt-oss-20b | 87% | 6 | 18/18 | 0/15 |
gpt-oss-20b | 85.5% | 5 | 18/18 | 0/15 |
qwen3.6-35b-a3b | 85.5% | 11 | 14/18 | 0/15 |
gpt-oss-120b | 85.5% | 8 | 18/18 | 0/15 |
ministral-3-14b | 44.9% | 40 | 3/18 | 0/15 |
mistral-small-3.2-24b | 40.6% | 9 | 15/18 | 0/15 |
gemma-2-9b | 34.8% | 35 | 0/18 | 0/15 |
eurollm-22b | 0% | 0 | 17/18 | 0/15 |
Each question is asked three times. Correct is the share of 69 answers to questions with one right answer. Confidently wrong counts drafts that ran and returned the wrong answer, plus drafts for data the warehouse does not hold. Asked back is out of 18 answers to questions about missing data, and unsafe writes counts how many of 15 answers to requests to change data reached the user.
Two more runs are not scored. Gemma 4 26B looped under strict JSON output, a serving failure rather than a measure of quality, and Devstral Small 2 does not run on an A100 at all.
The report also breaks the results down question by question, and that is where the cost of cryptic names shows. With them, no model answered five questions that Qwen3.8-27B got right every time with descriptive names, such as how many loans were more than 90 days past due.
The app talks to any OpenAI-compatible endpoint, so moving from a hosted provider to a bank's own servers looks like a simple configuration change. A separate evaluation harness checks whether it really is. The same gpt-oss-20b weights were served by a hosted provider, on CPU, and on rented GPUs through llama.cpp, vLLM, and SGLang.
Every server chose the right tables. The differences appeared elsewhere: in which optional joins a draft added, in whether a destructive request was refused or caught later by the SQL check, and under load. vLLM kept the strict JSON format with 64 requests in flight and reached about 270 questions a minute on a single RTX 4090. SGLang broke that format under load until it was started with a stricter setting, a failure that never appeared one request at a time.
Another experiment rewrote the catalog with mainframe-style abbreviated names. With the table and column descriptions kept, both models still passed all twelve baseline questions. With the descriptions removed, only half passed, a clear sign that good metadata matters as much as the model.
Ten hard questions from the benchmark, from a wide desktop to a small phone. Each SQL draft was written by the app's own pipeline and model, then verified by running it against the seeded warehouse; all ten returned the right answer. The screenshots replay those recorded responses, so they are repeatable and contain no real banking data.
The app writes and tests SQL drafts, but only against a synthetic copy of the warehouse, as a user that can only read; it never connects to a real banking warehouse. Users still review the result, then edit or copy it and run it in their own database client. The browser never receives the model API key.
Automated checks cover read-only statement shape and known table references. SQL syntax, column references, and business meaning still require human review. All warehouse names, relationships, and measures in the repository are synthetic test material rather than approved banking definitions.
The deployed proof of concept runs a Node backend, Elasticsearch, and the read-only PostgreSQL warehouse as rootless containers on an Oracle Ampere A1 instance, and drafts SQL with gpt-oss-20b through a hosted provider. The repository also documents a broader future architecture, clearly separated from the smaller deployed POC.