Metadata-Version: 2.4
Name: quantufai
Version: 0.1.0
Summary: QuantufAI developer SDK: quote quantum runs before any spend, read job status/results/receipts, export circuits, and simulate on the free sandbox — this SDK can price and read, it can never spend.
Author: QuantufAI, Inc.
License: Proprietary
Requires-Python: >=3.9
Description-Content-Type: text/markdown
Provides-Extra: verify
Requires-Dist: cryptography>=41; extra == "verify"

# quantufai — the QuantufAI Python SDK

Quantum compute a program can **price and read — never spend**.

> ⚠️ **Publish gate:** this package is repo-code only. It is **not** on PyPI
> and must not be published or linked from public docs until the founder
> confirms counsel's provisional filing. Local installs are fine:
> `pip install -e sdks/python`.

## What this SDK can do

| You want | Call | Key scope |
|---|---|---|
| "What would this run cost?" | `client.quote(circuit, shots=...)` | `quotes:read` |
| "Is it done yet?" | `client.job_status(job_id)` / `client.wait(job_id)` | `jobs:read` |
| Counts + ledger + error bars, verbatim | `client.job_result(job_id)` | `jobs:read` |
| The signed, tamper-evident receipt | `client.governed_receipt(job_id)` | `jobs:read` |
| Check a receipt you hold | `client.verify_receipt(receipt)` | `jobs:read` (platform tier) / none (offline tier) |
| The exact circuit that ran, as Qiskit/Cirq/Braket/pytket/QASM | `client.export_circuit(job_id, format=..., which=...)` | `results:export` |
| Download the result artifact | `client.export_result(job_id)` | `results:export` |
| Run on the **free local simulator** ($0) | `client.sandbox_simulate(circuit)` | `runs:simulate` (sandbox key) |
| What can this key do? | `client.me()` | `account:read` |

## What this SDK cannot do — by design, not omission

**There is no dispatch method. There is no approval method. There is no
billing method.** Spending money on quantum hardware requires a human
approving a signed quote in the QuantufAI dashboard:

- The platform enforces quote-before-spend server-side; scoped API keys
  cannot approve a spend.
- On top of that, this SDK's one execution method (`sandbox_simulate`) pins
  every request to the free local simulator (`preferredProviders:
  ["classical"]`), so **even a key carrying the paid `runs:execute` scope
  cannot reach billable hardware through this SDK**. Zero eligible providers
  is a typed failure on the platform — never a silent reroute.

An AI agent driving this client can tell you exactly what an experiment would
cost and read every receipt — and cannot buy anything.

## REFUSED is a status, not an exception

Every deliberate platform refusal — missing scope, sandbox clamp, someone
else's job, quota exhausted, unprovable circuit translation — comes back as a
typed `Refusal` with the platform's `code`, `message`, and `details`
verbatim:

```python
result = client.job_result("not-my-job")
if isinstance(result, quantufai.Refusal):
    print(result.status)   # "REFUSED"
    print(result.code)     # e.g. "job_not_found", "insufficient_scope"
    print(result.details)  # e.g. {"requiredScope": "jobs:read", ...}
```

Exceptions are reserved for transport failures (`TransportError`) and
unexpected 5xx answers (`PlatformError`, body preserved).

## Quickstart (sandbox — no card, $0)

```python
import quantufai

client = quantufai.Client(
    api_key="qfai_sk_...",        # or $QUANTUFAI_API_KEY; sandbox tier: mint
                                  # with {"tier": "sandbox"} in the dashboard
    base_url="https://YOUR_HOST", # or $QUANTUFAI_BASE_URL
)

bell = """OPENQASM 2.0;
include "qelib1.inc";
qreg q[2]; creg c[2];
h q[0]; cx q[0], q[1];
measure q -> c;"""

# 1. Price it first — nothing is reserved, charged, or dispatched by quoting.
quotes = client.quote(bell, shots=1000, qubits=2)
if isinstance(quotes, quantufai.Refusal):
    raise SystemExit(f"refused: {quotes.code} — {quotes.message}")
for q in quotes.quotes:
    print(f"{q.provider}: ~${q.estimated_cost_usd} ({q.reasons})")

# 2. Run on the FREE local simulator (the only execution this SDK performs).
run = client.sandbox_simulate(bell, shots=1000, qubits=2)
if isinstance(run, quantufai.Refusal):
    raise SystemExit(f"refused: {run.code} — {run.message}")
print(run.state, run.counts)   # real statevector physics, real counts, $0

# 3. Poll (async runs), then fetch the signed receipt.
result = client.wait(run.job_id)
receipt = client.governed_receipt(run.job_id)

# 4. Verify the receipt — offline Ed25519 tier and/or the platform's check.
report = client.verify_receipt(receipt)
print(report.verified, report.offline.public_signature)

# 5. Take the exact circuit home. `which` is required — the platform refuses
#    to guess between "original" and "as-dispatched".
code = client.export_circuit(run.job_id, format="qiskit", which="as-dispatched")
print(code.source)
```

## Receipts: what the tiers mean

- **Publicly-verifiable tier** (`publicSignature`, Ed25519): anyone can
  verify offline with QuantufAI's published public key —
  `verify_receipt(receipt, public_key=..., offline_only=True)` or the
  standalone `tools/verify-receipt.mjs`. Needs the optional extra:
  `pip install 'quantufai[verify]'`.
- **Server-attested tier** (`signature`, HMAC): the platform's own
  attestation; only the platform can check it (that's what the platform tier
  of `verify_receipt` asks for). Older receipts carry only this tier — the
  SDK reports that honestly instead of pretending to a verdict.

## Results are verbatim

Counts are never re-binned, error bars are never computed client-side —
`JobResult.error_bars` returns exactly the uncertainty fields the platform
attached (with their location in the payload), and returns nothing when the
platform attached none.

## History: this package replaces deleted fabricating stubs

The repo previously carried `sdks/python/quantufai.py` and
`sdks/js/quantufai.ts` — stubs that called endpoints that never existed and
**invented job statuses by pattern-matching chat text**. They were deleted
(PR #579) and a CI test keeps them deleted. This package is their honest
replacement: every call maps to a real, mounted, scope-gated endpoint, and
anything the platform refuses surfaces as a typed `REFUSED`.

## Publish checklist (post-filing — founder go required)

1. Founder confirms counsel's provisional filing covers the receipt/verifier
   disclosures (roadmap #4 gate).
2. `python -m build` from `sdks/python/`; check the wheel installs clean.
3. Reserve/publish `quantufai` on PyPI from the org account (never a personal
   one); enable 2FA + trusted publishing.
4. Only then link the SDK from `/docs` (the docs deliberately do not mention
   it today).
5. Announcement claim (exact words matter): "quantum compute an AI agent can
   spend safely — quote-before-spend enforced, auditable receipts." **Never
   claim "first MCP"** (IBM's Qiskit MCP servers and Conductor's CODA exist);
   "first *governed* one" is the true, stronger claim.
