Metadata-Version: 2.5
Name: qubitra-sdk
Version: 0.1.0
Summary: Qubitra Python SDK (ADR-0041, codename niobium) — the brand-owning qubitra.* facade over a provider-adapter seam; targets the Qubitra API's /v1 surface.
Project-URL: Homepage, https://github.com/Qubitra/q-platform
Project-URL: Repository, https://github.com/Qubitra/q-platform
Project-URL: Issues, https://github.com/Qubitra/q-platform/issues
Project-URL: Changelog, https://github.com/Qubitra/q-platform/blob/main/pkg/niobium/CHANGELOG.md
License-Expression: Apache-2.0
License-File: LICENSE
Keywords: qpu,quantum,quantum-computing,qubitra,sdk
Classifier: Development Status :: 3 - Alpha
Classifier: Intended Audience :: Developers
Classifier: Intended Audience :: Science/Research
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: Scientific/Engineering :: Physics
Classifier: Typing :: Typed
Requires-Python: >=3.11
Requires-Dist: httpx>=0.28
Requires-Dist: pydantic>=2.5
Description-Content-Type: text/markdown

# niobium — the Qubitra Python SDK

The Python client for the Qubitra platform. Codename `niobium`; it installs as
`qubitra-sdk` and imports as `qubitra` (ADR-0041 D1).

The SDK speaks q-platform vocabulary — Backends, Jobs, Offerings — and talks to the
Qubitra API's public `/v1` surface. Which backend a Job actually lands on, and the
supply chain behind it, is the platform's business: the SDK never names a vendor.

## Install

```bash
pip install qubitra-sdk
```

Inside this repo it is a uv workspace member, so `uv sync` at the root installs it
editable and no publish step is involved.

## Quickstart

```python
from qubitra import QubitraClient

with QubitraClient() as client:
    for backend in client.backends.list():
        print(backend.id, backend.qubit_count, backend.supported_formats)

    job = client.jobs.submit(
        backend_id="qpu-superconducting-127q",
        circuit="OPENQASM 3.0; qubit[2] q; bit[2] c; h q[0]; cx q[0], q[1]; c = measure q;",
        shots=1024,
        name="bell-pair",
    )

    job = client.jobs.wait(job.id, timeout=600)
    if job.status.is_terminal and job.error:
        raise SystemExit(f"job {job.id} failed: {job.error}")

    print(client.jobs.result(job.id).counts)   # {"00": 512, "11": 512}
```

Two runnable versions of that sit in `examples/`: `submit_bell_pair.py` is the whole path,
and `list_backends.py` is the read-only smoke test. Both take their key from the environment:

```bash
QUBITRA_API_KEY=qpk_... uv run pkg/niobium/examples/submit_bell_pair.py
```

Against a local stack (`mise run dev:qplat`), add `QUBITRA_API_URL=http://localhost:8080` and
mint a key on the console's API keys page.

## Configuration

| Variable | Default | Meaning |
| --- | --- | --- |
| `QUBITRA_API_KEY` | — | API key (`qpk_…`), required. Resolves to your organization. |
| `QUBITRA_API_URL` | `http://localhost:8080` | The Qubitra deployment to reach. |

Both are also constructor arguments — `QubitraClient(api_key=…, api_url=…)` — which
take precedence over the environment.

## Surface

| Call | What it does |
| --- | --- |
| `client.backends.list()` / `.get(id)` | The backends available to you, and one by id. |
| `client.jobs.submit(...)` | Submit a circuit; returns a `Job` immediately. |
| `client.jobs.list()` | Your organization's jobs, newest first. |
| `client.jobs.get(id)` / `.result(id)` / `.cancel(id)` | Read status, fetch counts, cancel. |
| `client.jobs.wait(id)` | Poll until the Job reaches a terminal status. |
| `client.marketplace.offerings()` | The marketplace catalogue. |

`jobs.result` is valid once a Job is `COMPLETED`; asking earlier raises
`InvalidRequestError`.

Every failure is a `QubitraError`: `AuthenticationError`, `NotFoundError`,
`InvalidRequestError`, `InsufficientCreditsError`, or `ProviderError`. Each carries
the platform's RFC 9457 Problem Details body (ADR-0008) on `.detail` and its HTTP
status on `.status_code`.

Models are frozen pydantic models; the package ships `py.typed`, so a caller's type
checker sees the whole surface.

## Layout

- `src/qubitra/` — the public facade: `client.py`, `models.py`, `errors.py`,
  `credentials.py`.
- `src/qubitra/_providers/` — the adapter seam (ADR-0041 D2). `base.py` is the
  protocol the facade depends on; `qubitra.py` implements it over `/v1`. A second
  platform slots in here without touching the facade.
- `tests/` — facade behaviour over a fake adapter, plus contract tests driving the
  adapter's real request path against an httpx `MockTransport`.

The SDK is synchronous (ADR-0041 D7); client objects own their transport, so an
async twin is additive.

## Tasks

```bash
mise exec -- uv run pytest pkg/niobium -q     # tests
mise exec -- mypy pkg/niobium                 # types
mise exec -- ruff check pkg/niobium           # lint
```
