Quantum
Declarative web apps in XML, with AI and RAG built into the language. No build chain, no JavaScript, no frontend framework.
Quantum is a full-stack framework whose language is markup. State, database queries, forms, LLM calls and retrieval-augmented generation are all tags — not libraries you wire together. It takes its philosophy from ColdFusion and Adobe Flex: the markup is the app.
1.0, and honest about it. See Stability — the table there reflects what has actually been executed end-to-end, not what is aspirational.
The part that isn't like the others
Retrieval-augmented generation, as a language construct — this page answers a
question from the Markdown files in knowledge/ and lists the ones it used:
<q:component name="Ask">
<!-- The documents in knowledge/, split into chunks and embedded when the
page runs. persist="false" keeps the index in memory; without it, it is
stored in ./.quantum/knowledge and reused until a document changes. -->
<q:knowledge name="docs" persist="false" chunkSize="300" chunkOverlap="30">
<q:source type="directory" path="knowledge" pattern="*.md" />
</q:knowledge>
<q:set name="question" value="{query.q}" default="" />
<q:if condition="question">
<!-- The question retrieves the closest chunks; they reach the model
numbered, with the instruction to answer only from them and cite
them like [1]. -->
<q:llm name="answer" knowledge="docs" top="2">
<q:message role="user">{question}</q:message>
</q:llm>
</q:if>
<ui:window title="Ask the store">
<ui:form>
<ui:input bind="q" value="{question}" placeholder="Your question" />
<ui:button variant="primary">Ask</ui:button>
</ui:form>
<q:if condition="question">
<ui:text>{answer}</ui:text>
<q:if condition="answer_result.grounded">
<ui:text>Sources:</ui:text>
<q:loop items="{answer_result.sources}" var="s">
<ui:text>[{s.n}] {s.name}</ui:text>
</q:loop>
<q:else>
<ui:alert variant="warning">This answer cites none of the documents.</ui:alert>
</q:else>
</q:if>
</q:if>
</ui:window>
</q:component>
That splits the documents into chunks, embeds them, retrieves the closest ones
and asks the model to answer only from them, citing each one — in the markup.
answer_result.grounded says whether the answer cites any of them. There is no
Python file behind it. (Recipe:
Answers with their sources.)
An agent whose tools you write in Quantum, same idea:
<q:component name="Assistant">
<!-- The model never writes SQL: it picks a tool and its arguments. The
tool is a read-only query you wrote; its q:param says the argument's
type, and the model's value is converted to it before the query runs. -->
<q:agent name="stock" maxIterations="4" timeout="60000" onerror="continue">
<q:instruction>You help a shop owner. Use the tools to look at the data,
then answer in one sentence.</q:instruction>
<q:tool name="low_stock" description="Products with fewer units in stock than `below`">
<q:param name="below" type="integer" default="5" />
<q:function name="lowStock">
<q:query name="rows" datasource="db">
SELECT name, stock FROM products WHERE stock < :below ORDER BY stock
<q:param name="below" value="{below}" type="integer" />
</q:query>
<q:return value="{rows}" />
</q:function>
</q:tool>
<q:execute task="Which products are running out of stock?" />
</q:agent>
<ui:window title="Stock assistant">
<q:if condition="stock_result.success">
<ui:text>{stock}</ui:text>
<q:else>
<ui:alert variant="warning">The assistant did not finish: {stock_result.error.message}</ui:alert>
</q:else>
</q:if>
<!-- Every tool call the agent made, written out. -->
<q:loop items="{stock_result.actions}" var="a">
<ui:text>Called {a.call}</ui:text>
</q:loop>
</ui:window>
</q:component>
The model never writes SQL: it picks a tool and its arguments, and the
argument is converted to the q:param's type before the query runs. The
reasoning loop, the tool calls and the failure contract (onerror,
stock_result) are the runtime's job. (Recipe:
An agent over your database.)
The rest of the language
<q:component name="Products">
<!-- ?name=mouse from the URL; empty when it is not there. -->
<q:set name="term" value="{query.name}" default="" />
<!-- :pattern is bound to the q:param: the value is sent to the database
apart from the SQL, so it can never change what the SQL does. -->
<q:query name="products" datasource="db">
SELECT name, price FROM products WHERE name LIKE :pattern ORDER BY price
<q:param name="pattern" value="%{term}%" type="string" />
</q:query>
<ui:window title="Products">
<ui:text>{products_result.recordCount} products</ui:text>
<ui:table source="{products}">
<ui:column key="name" label="Name" />
<ui:column key="price" label="Price" />
</ui:table>
</ui:window>
</q:component>
Saved as components/index.q, with the database declared in
quantum.config.yaml, quantum start serves it at http://localhost:8080/,
and /?name=mouse filters it. (Recipe:
A query with parameters.)
q:query refuses to run SQL with an undeclared :param — parameterised queries are
enforced by the parser, not by discipline.
Also core: q:set with session. / application. / request. scopes, q:if,
q:function, q:action for form handling, q:data for CSV/JSON/XML import,
q:import / q:slot for composition.
The examples above are files of Cookbook
recipes, byte for byte, and the recipes run in CI; the quick start below is run
as shown (tests/docs/test_readme.py checks both).
Quick start
Requirements: Python 3.12+ and pip.
pip install quantum-framework
Create hello.q:
<q:component name="HelloWorld" xmlns:q="https://quantum.lang/ns">
<q:return value="Hello World!" />
</q:component>
quantum run hello.q
[EXEC] Executing component: HelloWorld
[SUCCESS] Result: Hello World!
For a web app, put .q files in components/ and run quantum start
(components/index.q is served at /). quantum stop stops it.
For the AI examples you also need a model server — Ollama
at http://localhost:11434 unless QUANTUM_LLM_BASE_URL says otherwise — and
the RAG extra. There is no built-in model name: say which one in
quantum.config.yaml (llm: model: phi3) or QUANTUM_LLM_DEFAULT_MODEL.
pip install "quantum-framework[rag]"
ollama pull phi3 && ollama pull nomic-embed-text
Declare datasources in quantum.config.yaml (next to components/) and q:query works
with nothing else running — SQLite needs no extra; PostgreSQL and MySQL drivers come with
pip install "quantum-framework[db]":
datasources:
db:
driver: sqlite
database: ./data/app.db
CLI
| Command | What it does |
|---|---|
run <file.q> |
Execute a component, or build a q:application (ui, terminal, game) |
start |
Start the web server (port 8080 by default; --port to change) |
stop |
Stop the server started by start |
check |
Check that pages parse, SQL compiles and query fields exist |
test |
Run the app's *.test.q tests |
console · desktop |
The application's pages in the terminal, or in a desktop window |
migrate |
Apply, roll back and plan database migrations |
admin |
Start the Quantum Admin (pip install "quantum-framework[admin]") |
pkg · jobs · mq |
Component packages (a page cannot import one yet), jobs, message queues — no stability promise |
From source
To work on Quantum itself:
git clone https://github.com/danielgregorio/quantum.git
cd quantum
pip install -e ".[dev]"
quantum run examples/hello.q
See CONTRIBUTING.md for the test suite and the architecture.
Documentation
Full docs at quantumframework.net:
- Start: Installation · Quick Start · Tutorial: build the tasks app
- Learn: Guide · Cookbook — short tested recipes · Showcase — the complete apps
- Look up: Reference — every tag, function, command and SPEC rule, generated from the code
- Plan: Stability — what 1.0 promises · Roadmap · Changelog
The site is also in Português, Español and 中文.
Stability
Support levels are defined in SUPPORT_TIERS.md. Short version:
| Tier | Surface |
|---|---|
| Core — documented, tested end to end, stable | q:component, q:set, q:if, q:loop, q:function, q:query, q:transaction, q:action, q:invoke, q:data, q:import / q:slot, q:file, q:mail, the Core set of ui:*, require_auth / require_role |
| AI — the Core contract plus a live test against a real model | q:llm, q:knowledge, q:agent |
| Experimental — they run, but no API stability promise | q:team, jobs, messaging, websockets, q:log / q:dump, ui:* outside the Core set, the terminal target |
| Experimental, and a full-trust escape hatch | Python scripting (q:python, q:pyclass, q:pyimport) — off by default, see SECURITY.md |
A functional audit in 2026-09 found that several of these surfaces had never been run end-to-end despite being documented as complete. They were fixed or re-labelled, and feature status is now verified by execution rather than asserted by hand.
From 1.0, Core and AI follow semantic versioning: a 1.x release does not break a program that uses only them — their meaning is fixed by the rules in SPEC.md, and a break waits for 2.0. Experimental and Laboratory surfaces carry no such promise and may change in any release.
Project layout
quantum/
├── quantum/
│ ├── core/ # Parser & AST (registry-based, modular)
│ ├── runtime/ # Execution engine, web server, renderer
│ └── cli/ # Command-line entry point
├── examples/ # runnable .q examples
├── tests/ # pytest suite; conformance/ cites SPEC.md
├── scripts/ # dev tools
└── docs/ # VitePress documentation
Adding a tag is one parser + one executor + a registry entry — see CONTRIBUTING.md.
Contributing
Read CONTRIBUTING.md for dev setup and how the modular parser/executor architecture works. By participating you agree to the Code of Conduct.
Found a security issue? Follow SECURITY.md — do not open a public issue.
License
MIT — see LICENSE.
Release files for quantum-framework 1.0.1
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| quantum_framework-1.0.1.tar.gz | 913.7 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| quantum_framework-1.0.1-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 1.8 MB
Release files / quantum_framework-1.0.1.tar.gz
| Download URL | quantum_framework-1.0.1.tar.gz |
|---|---|
| Size | 913.7 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
1eedbfecd9ec4cd898e986f4417141120a6f034e05c48c2c2ccda81d5f3192df
|
|
BLAKE2b-256 checksum How to use checksums |
2e84bff6fa1772fe947ebe834b1eb81a2f8a8996047b4f7242d16610ce3edbd5
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 26, 2026.
Transparency logRelease files / quantum_framework-1.0.1-py3-none-any.whl
| Download URL | quantum_framework-1.0.1-py3-none-any.whl |
|---|---|
| Size | 932.6 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
65b299c6b2aaea23b0481a1b1026a7c26cb1d406bdd81a5335391af222879e54
|
|
BLAKE2b-256 checksum How to use checksums |
86e41297bc6e7da650280170325a11ee83f9417f21829b553278cea129698a4a
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 26, 2026.
Transparency log