Metadata-Version: 2.5
Name: leona-client
Version: 0.3.0
Summary: The one Python HTTP client for Leona Quantum: the Atlas, verified runs and estimates
License-Expression: MIT
License-File: LICENSE
Requires-Python: >=3.12
Requires-Dist: httpx<1,>=0.27
Requires-Dist: pydantic<3,>=2.11
Provides-Extra: notebook
Requires-Dist: ipython>=8; extra == 'notebook'
Description-Content-Type: text/markdown

# leona-client

The one Python HTTP client for [Leona Quantum](https://leonaqt.com): the Quantum
Atlas, verified runs, and resource estimates. Used by the `leona-mcp` MCP server, the
`%nala` Jupyter magic and the `leona-notebooks` CLI, so there is one place that knows
how to talk to the API instead of three.

## Quickstart

Requires Python 3.12 or later. Install the supported 0.3.0 release from PyPI:

```bash
python -m pip install "leona-client==0.3.0"
export LEONA_API_TOKEN=lq_pat_...   # Account → Access tokens on leonaqt.com; omit for Atlas-only use
```

```python
from leona_client import Client

client = Client.from_env()  # reads LEONA_API_URL / LEONA_API_TOKEN

# The Atlas needs no token.
areas = client.list_problem_areas()
hits = client.search_methods("phase estimation", max_qubits=8)
record = client.get_method(hits["results"][0]["slug"])

# Runs and estimates need a token with the `run` scope (ai-ops 362 option 1: tokens
# may read and start verified runs; hardware jobs come later, under their own scope).
run = client.start_run("Build a 3-qubit GHZ state and verify it")
run = client.wait_for_run(run.id, wait_s=300)
print(run.status, run.verifier_decision, run.verification_summary)
```

## What it is

- `Client` (`client.py`) — bearer-token control-plane calls: notebooks (`create`,
  `push`, `pull`, `versions`, `ask`, ...), runs (`start_run`, `get_run`, `list_runs`,
  `cancel_run`, `wait_for_run`) and estimates (`estimate_resources`), plus the Atlas
  convenience methods below. The token comes from `LEONA_API_TOKEN` — set it in your
  shell, never pass it as an argument or put it in a notebook cell — and every method
  that needs one raises a plain `LeonaClientError` (never an HTTP exception, never
  the token itself) when it is absent.
- `catalog.py` (`CatalogClient`) and `atlas.py` — the read-only Atlas: fetching the
  full public catalog (paged, cached, and refusing to answer from a partial read),
  and the site's own finder/search/verification/OpenQASM-export rules, copied from
  `apps/web/lib/repository/*.ts` and mirror-tested against them
  (`tests/test_mirrors.py`). No token, no write, nothing that runs or costs money.
  `Client.search_methods`/`get_method`/`list_problem_areas` and `leona-mcp`'s three
  read-only tools both call into this — one fetch implementation, one set of rules.

## Typed responses

Run methods return `leona_client.Run`, Qapp calls `leona_client.QappExecution` and
`check_circuit` a `leona_client.CheckVerdict` (all in `leona_client/models.py`). Each
names the fields this client reads — a run's `id`, `status`, `verifier_decision` and
`verification_summary`, for instance — and keeps every other field the API sends,
readable as an attribute (`run.task_prompt`, `run.created_at`) and returned by
`model_dump()` in the order the API sent it. A field the API adds later reaches you
without upgrading this package, and a status it adds is a string, not an error.
Fields this package does not name arrive as the JSON value the API sent: a timestamp
is an ISO 8601 string, a nested object a plain dict.

Estimates and plans return the raw JSON: their response shapes are route-local, because
nothing outside the web app's own planner reads them.

This package depends on `httpx` and `pydantic` and nothing else. In particular it does
not carry Leona's check engine or the contracts it validates against: `check_circuit`
sends the property as you wrote it, and Leona's API judges whether it is well formed
(a 422 naming the rule when it is not) as well as what the circuit does.

A check of a circuit family at several sizes sends the circuit your own code built at
each size, and Leona judges each against the reference built at that size:

```python
verdict = client.check_circuit(
    None,
    {"kind": "state", "reference": "ghz(n)"},
    circuits={n: qiskit.qasm3.dumps(build(n)) for n in (2, 3, 4)},
)
verdict.status                                        # "pass" only if EVERY size passed
[(row.size, row.status) for row in verdict.per_size]  # one row per size, own teeth
```

## In a notebook: `leona_client.notebook`

For Jupyter, VS Code or Colab, install the notebook extra (IPython) in your kernel and load the extension. Downloads retain their code, expectations and saved outputs; checks without the runtime say NOT CHECKED. Install the quantum framework/version named by your notebook's setup separately.

```python
%pip install -q "leona-client[notebook]==0.3.0"
%load_ext leona_client.notebook          # the %nala magic
%nala link <notebook id>
from leona_client.notebook import leona_check, leona_submit
```

Each check cell calls `leona_check({...the check...})`: the circuit your code holds there
is written as OpenQASM 3 in your kernel and judged by `check_circuit` — at several sizes,
your builder is called once per size and every circuit goes in one request. With no
token, a token Leona refuses, no network, or any error from the API, the cell says
**NOT CHECKED** and why; it never reports a pass Leona did not give. `leona_submit` prices
a hardware cell and, with a `hardware`-scoped token, submits it. The full walkthrough is
[the repository walkthrough](https://github.com/Leona-Quantum/leona/blob/dev/docs/notebooks/jupyter.md)
(repository access required).

## Never claim a run is verified unless the record says so

`run.verifier_decision` is one of `pass`/`fail`/`inconclusive`; `run.status` can be
`succeeded` while `verifier_decision` is not `pass` — a run can finish without its
generated code being verified correct. Read `verifier_decision` and
`verification_summary`, not just `status`, before telling anyone a result is
verified.

## Install without `uv`

The quickstart uses plain `pip`; `uv` and Git access are not required. All dependencies
resolve from PyPI. For notebook use, install `"leona-client[notebook]==0.3.0"`.

`leona-client` and `leona-mcp` are the two public packages. Leona's check engine,
contracts, sandbox and planner stay on the service; they are not installed in your
environment.

## Commands

`uv run pytest packages/py/client -q`
