Metadata-Version: 2.4
Name: qbridge-agent
Version: 0.4.0
Summary: Local, compiler-backed CUDA-Q optimization agent
License-Expression: BUSL-1.1
Requires-Python: >=3.9
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: qbridge[quantum]>=0.4.0
Requires-Dist: httpx>=0.27
Provides-Extra: dev
Requires-Dist: pytest>=7.0; extra == "dev"
Requires-Dist: ruff>=0.6.0; extra == "dev"
Dynamic: license-file

<p align="center">
  <img src="docs/brand-q.svg" alt="Q" width="56" height="56" />
  <br />
  <strong>QBridge Agent</strong>
</p>

<p align="center">
  <strong>Natural language → QBridge DSL → local CUDA-Q solve.</strong><br />
  No cloud compiler. No coding-agent detour.
</p>

QBridge-Agent is a lean optimization agent for quantum developers who already know they want **QAOA on CUDA-Q**, not a general-purpose LLM rewriting Python files until something runs. You describe a bounded binary optimization problem — knapsack, MaxCut, portfolio, scheduling — and the agent:

1. **Translates** your request into validated [QBridge DSL](https://github.com/ProfessionalQwerty/qbridge)
2. **Compiles** locally with the installed `qbridge` package (deterministic, auditable)
3. **Executes** on your NVIDIA CUDA-Q target and prints the decoded assignment

No Aider loop. No hand-written `@cudaq.kernel`. No cloud compile endpoint.

![Natural-language knapsack: NL → DSL → CUDA-Q solve](docs/nl-mode-screenshot.png)

*`qbridge "Knapsack with 5 items, values [6,5,8,9,6], weights [2,3,4,5,3], capacity 8"`*

---

## Why this beats a coding agent for QUBO problems

| | **QBridge-Agent** | **General coding agent (e.g. Aider)** |
|---|---|---|
| Output | Validated QBridge DSL → compiler-lowering | Ad-hoc CUDA-Q scripts |
| Compile path | Deterministic local `qbridge` | LLM edits until syntax runs |
| Verification | Independent constraint/objective check | Usually none |
| Token cost (T1/T2 suite) | **~1,294** mean tokens/trial | **~6,602** (**5.10×**) |
| Wall time (same suite) | **~6.6 s** | **~42.7 s** (**~6.49×**) |
| Verified trials (18-run benchmark) | **12 / 18** | **0 / 18** |

Benchmark: frozen six-task T1/T2 CUDA-Q suite, **`tencent/hy3:free`** (default CLI model), NVIDIA `cusvsim_fp32`, three trials per system. Prior `openai/gpt-4o-mini` run: **16/18 verified**, **2.68×** tokens, **~3.36×** wall time — see [evaluations/README.md](evaluations/README.md).

---

## Table of contents

1. [Install](#install)
2. [CLI experience](#cli-experience)
3. [Environment setup (WSL + CUDA-Q)](#environment-setup-wsl--cuda-q)
4. [Mode A — DSL file (no API key)](#mode-a--dsl-file-no-api-key)
5. [Mode B — natural language](#mode-b--natural-language)
6. [Workspace layout](#workspace-layout)
7. [Programmatic API](#programmatic-api)
8. [Worked examples](#worked-examples)
9. [CLI reference](#cli-reference)
10. [Troubleshooting](#troubleshooting)
11. [Benchmarks & evaluations](#benchmarks--evaluations)
12. [Scope & non-goals](#scope--non-goals)
13. [License & releases](#license--releases)

---

## Install

**Requirements**

| Component | Version / notes |
|---|---|
| Python | 3.9+ |
| [`qbridge[quantum]`](https://pypi.org/project/qbridge/) | **≥ 0.4.0** — the CUDA-Q compiler (presolve, dual compile, init_strategy) |
| CUDA-Q | **0.15.0**, NVIDIA target available |
| OpenRouter API key | First-run setup for NL mode (stored locally) |

**PyPI**

```bash
pip install "qbridge[quantum]>=0.4.0" qbridge-agent
```

**Source checkout**

```bash
git clone https://github.com/ProfessionalQwerty/qbridge-agent.git
cd qbridge-agent
pip install -e ".[dev]"
```

Verify:

```bash
qbridge --help
qbridge-compile --help   # from qbridge package
```

---

## CLI experience

QBridge-Agent is a **single-shot CLI** — one quoted problem or one DSL file per invocation. There is no persistent chat shell; output is structured, timed, and color-coded for terminals.

### First launch

Running `qbridge` with no arguments (or your first NL prompt) opens **setup**:

```text
──────────────────────────────────────────────────────────
  QBridge-Agent  local compiler-backed CUDA-Q optimization
──────────────────────────────────────────────────────────

 Welcome to QBridge.
 Describe an optimization problem in plain English.
 We translate to QBridge DSL, compile locally, and run CUDA-Q.

 OpenRouter setup  keys at https://openrouter.ai/keys
 Paste your key and press Enter (visible input — paste works reliably on Windows/WSL)
 OpenRouter API key: sk-or-v1-...
 Received  sk-or-v1-abc...xyz (73 chars)
 Model [openai/gpt-4o-mini]:
 ✓ Ready  model=tencent/hy3:free
```

Credentials are saved under `~/.qbridge-agent/` (`credentials.json` + `config.json`). Nothing is sent to a QBridge cloud endpoint — only OpenRouter for NL translation.

### Everyday commands

| Command | What it does |
|---|---|
| `qbridge` | Show configured model and usage hints |
| `qbridge "…"` | NL → DSL → compile → CUDA-Q solve |
| `qbridge problem.py` | DSL file → compile → solve (no API key) |
| `qbridge --setup` | Re-enter OpenRouter key and default model |
| `qbridge --doctor` | Test env vs saved key; show which is active |
| `qbridge --no-solve problem.py` | Compile only, skip GPU solve |

### Terminal output (NL mode)

Each run prints a **banner**, **timed steps**, **structured blocks**, and **token usage**:

```text
 ● Request  Knapsack with 5 items, values [6,5,8,9,6], weights …
 ● Model  tencent/hy3:free
 ● Translating DSL and compiling locally
 ✓ Translating DSL and compiling locally  12.69s

 Generated DSL
   path: qbridge-output/problem.py

 Solution
   Backend:    cudaq:nvidia
   Objective:  14.0
   Feasible:   True
   q0: 1  …

 Token usage
   prompt: 1078  completion: 158  total: 1236  wall time: 12.68s

 ✓ Done  workspace: …/qbridge-output
```

On failure, the CLI prints a red error line and actionable hints (e.g. `qbridge --setup` after a 401).

### Credential precedence

| Source | Priority |
|---|---|
| `OPENROUTER_API_KEY` env | Used **only** if it looks like a real `sk-or-…` key (≥ 20 chars) |
| `~/.qbridge-agent/credentials.json` | Default after setup |
| Placeholder env (`your-key`, etc.) | **Ignored** — saved file key is used instead |

Run `qbridge --doctor` to see which key is active and whether OpenRouter accepts it.

### Styling

- Cyan banner and bullets, magenta section headers, blue values, green success marks
- Respects `NO_COLOR` and `FORCE_COLOR`
- Falls back to ASCII glyphs (`>`, `OK`, `X`) when Unicode is unavailable

---

## Environment setup (WSL + CUDA-Q)

CUDA-Q with NVIDIA targets is **Linux-first**. On Windows, use **WSL2** — not bare `python.exe`, not an arbitrary WSL distro.

**Verified example (Windows host → Ubuntu WSL)**

```bash
wsl -d Ubuntu -- /home/dilip/qbridge-env/bin/python3 -c "import cudaq; print(cudaq.__version__)"
# CUDA-Q Version 0.15.0
```

**Suggested virtualenv bootstrap (inside WSL Ubuntu)**

```bash
python3 -m venv ~/qbridge-env
source ~/qbridge-env/bin/activate
pip install "qbridge[quantum]>=0.4.0" qbridge-agent
python -c "import cudaq; cudaq.set_target(\"nvidia\"); print(cudaq.get_target())"
```

**Run the agent**

```bash
qbridge "Knapsack with 5 items, values [6,5,8,9,6], weights [2,3,4,5,3], capacity 8"
```

If you previously exported a placeholder like `OPENROUTER_API_KEY=your-key` in your shell, remove it:

```bash
unset OPENROUTER_API_KEY
qbridge --doctor
```

---

## Mode A — DSL file (no API key)

When you already have — or hand-write — QBridge DSL, the agent is a **compile + solve CLI** with structured output.

```bash
qbridge examples/knapsack_n5.py --workspace ./qbridge-output
```

**Compile only** (emit CUDA-Q kernel, skip GPU solve):

```bash
qbridge examples/knapsack_n5.py --workspace ./qbridge-output --no-solve
```

**Expected terminal flow**

```text
──────────────────────────────────────────────────────────
  QBridge-Agent  local compiler-backed CUDA-Q optimization
──────────────────────────────────────────────────────────

 ● Loading QBridge DSL  qbridge-output
 ● Compiling to CUDA-Q
 ✓ Compiling to CUDA-Q  1.24s
 Artifacts
   CUDA-Q kernel: …/compiled_output/kernel_execution.py
   DSL source:    …/problem.py
 ● Running CUDA-Q on NVIDIA target
 ✓ Running CUDA-Q on NVIDIA target  2.81s
 Solution
   Backend:       cudaq:nvidia
   Objective:     …
   Feasible:      True
 ✓ Done  workspace: /…/qbridge-output
```

The DSL file must use `import qbridge as qb` and declare variables, objective, and constraints — **not arbitrary Python**. Full DSL rules: [QBridge compiler README](https://github.com/ProfessionalQwerty/qbridge#dsl-reference).

---

## Mode B — natural language

After first-run setup, describe your problem in plain English:

```bash
qbridge "Knapsack: 5 items, values [6,5,8,9,6], weights [2,3,4,5,3], capacity 8"
```

Reconfigure anytime:

```bash
qbridge --setup
```

**Model override for one run**

```bash
qbridge "MaxCut on a 4-node cycle with edge weights 1,2,3,4" --model openai/gpt-4o-mini
```

| When to use | Mode |
|---|---|
| You have DSL already, or want zero API cost | **A — `.py` file** |
| Exploring problems, demos, NL interface | **B — quoted prompt** |
| CI / regression | **A** (deterministic input) |

Token usage is printed at the end of NL runs.

---

## Workspace layout

Every run writes artifacts under `--workspace` (default: `./qbridge-output`):

```text
qbridge-output/
├── problem.py                    # DSL copy (Mode A) or generated DSL (Mode B)
├── prompt.txt                    # Mode B only — your NL request
├── compiled_output/
│   ├── kernel_execution.py       # Generated CUDA-Q kernel
│   ├── compile_meta.json         # Local compiler metadata
│   └── solution.json             # Assignment + objective + feasibility
├── _usage.json                   # Mode B — token counts + timings
└── response.txt                  # Mode B — raw LLM response (if pipeline saves it)
```

**Reading `solution.json`**

```json
{
  "assignment": {"x0": 1, "x1": 0, "x2": 1},
  "objective_value": 19.0,
  "constraints_satisfied": true,
  "backend": "cudaq:nvidia",
  "cudaq_target": "Target nvidia ..."
}
```

If `constraints_satisfied` is `false`, the QAOA sample decoded to an infeasible assignment — tighten `SOLVE_KWARGS` in the generated DSL or re-run.

---

## Programmatic API

### Full NL pipeline

```python
from pathlib import Path
from agent.cudaq_entry import run_cudaq_case

report = run_cudaq_case(
    prompt="Select at most 3 projects maximizing value under budget 100 ...",
    workspace=Path("./qbridge-output"),
    model="openai/gpt-4o-mini",
    api_key="...",  # or ~/.qbridge-agent/credentials.json
)

print(report["ok"], report["solution"], report["usage"])
```

### DSL-only pipeline (no LLM)

```python
from pathlib import Path
from agent.compiler_interface.pipeline import run_quantum_pipeline

dsl = Path("examples/knapsack_n5.py").read_text()
report = run_quantum_pipeline(
    "ignored",
    complete_fn=lambda _: dsl,
    workspace_root=Path("./qbridge-output"),
    include_solve=True,
)
print(report.ok, report.solution)
```

---

## Worked examples

| Example | File | Command |
|---|---|---|
| 5-item knapsack | [`examples/knapsack_n5.py`](examples/knapsack_n5.py) | `qbridge examples/knapsack_n5.py --workspace ./out` |
| MaxCut (NL) | [`examples/maxcut_agent_run.md`](examples/maxcut_agent_run.md) | See file for quoted prompt |
| Portfolio (NL) | [`examples/optimize_portfolio.md`](examples/optimize_portfolio.md) | Budget + group cover constraints |
| Scheduling (NL) | [`examples/scheduling_agent_run.md`](examples/scheduling_agent_run.md) | Resource + time windows |

**MaxCut prompt example**

```bash
qbridge "MaxCut on triangle graph nodes 0-1-2 with edge weights 1,2,3. Binary vars x0,x1,x2." \
  --workspace ./maxcut-out
```

---

## CLI reference

```
qbridge [request] [--workspace WORKSPACE] [--no-solve] [--setup] [--doctor] [--model MODEL]
qbridge-agent   # alias
```

| Argument | Description |
|---|---|
| `request` | Quoted NL problem **or** path to `.py` DSL file |
| `--setup` | Re-enter OpenRouter key and model |
| `--doctor` | Diagnose credential sources and OpenRouter connectivity |
| `--workspace PATH` | Output directory (default: `./qbridge-output`) |
| `--no-solve` | Compile only (DSL mode) |
| `--model ID` | Override saved model for one run |

**Credentials:** `~/.qbridge-agent/credentials.json` (key) and `config.json` (model). A valid `OPENROUTER_API_KEY` env var overrides the saved file; placeholder values are ignored.

**Exit codes**

| Code | Meaning |
|---:|---|
| `0` | Success |
| `1` | Compile/solve/agent failure |
| `2` | Setup cancelled / missing credentials |

**Environment variables**

| Variable | Used by |
|---|---|
| `OPENROUTER_API_KEY` | NL mode (optional if setup completed) |
| `QBRIDGE_BENCH_MODEL` / `QBRIDGE_MODEL` | Default model override |
| `NO_COLOR` / `FORCE_COLOR` | Terminal styling |

---

## Troubleshooting

| Symptom | Cause | Fix |
|---|---|---|
| `401 Unauthorized` from OpenRouter | Bad/stale key or placeholder env | `unset OPENROUTER_API_KEY`; `qbridge --doctor`; `qbridge --setup` |
| `--doctor` shows env `your-key` | Doc placeholder left in shell | `unset OPENROUTER_API_KEY` |
| Setup rejects pasted key | Hidden input mangled paste (older versions) | Upgrade to ≥ 0.3.1; use visible paste prompt |
| `No module named 'cudaq'` | Wrong Python / no CUDA-Q | Use WSL Ubuntu + `qbridge[quantum]` env |
| `No module named 'qbridge.dsl_build'` | Wrong `qbridge` package installed | `pip install "qbridge[quantum]>=0.3.0"` from PyPI |
| `constraints_satisfied: false` | Weak QAOA / penalty settings | Edit generated DSL `SOLVE_KWARGS` (↑ `alpha`, `p`, `n_seeds`) |
| Garbled Unicode on Windows terminal | Legacy code page | Use Windows Terminal; agent falls back to ASCII glyphs |
| Agent generates invalid DSL | LLM drift | Retry; or write DSL manually (Mode A) |

---

## Benchmarks & evaluations

Frozen six-task T1/T2 CUDA-Q suite (`evaluations/benchmark_tasks.json`). Each trial records OpenRouter token usage, wall time, and independent constraint verification.

**Latest run (`tencent/hy3:free`, 20 July 2026)**

| System | Total tokens (mean) | Wall time (mean) | Verified |
|---|---:|---:|---:|
| QBridge-Agent | 1,294 | 6.58 s | 12 / 18 |
| Aider | 6,602 | 42.70 s | 0 / 18 |

Artifacts: `evaluations/results/20260720_hy3_token_tracking/results.jsonl`

Reproduce (WSL + CUDA-Q):

```bash
wsl -d Ubuntu -- bash -c '
  cd /mnt/c/Users/you/qbridge-agent &&
  export PYTHONPATH=$(pwd) &&
  unset OPENROUTER_API_KEY &&
  export OPENROUTER_API_KEY=$(python3 -c "import json; from pathlib import Path; print(json.loads(Path.home().joinpath(\".qbridge-agent/credentials.json\").read_text())[\"openrouter_api_key\"])") &&
  /home/you/qbridge-env/bin/python3 evaluations/run_benchmark.py \
    --model tencent/hy3:free --trials 3 \
    --results evaluations/results/my_hy3_run
'
```

Summarize a completed run:

```bash
python evaluations/summarize_results.py evaluations/results/my_hy3_run/results.jsonl
```

See [evaluations/README.md](evaluations/README.md) for methodology, the `gpt-4o-mini` baseline, token capture (`AIDER_USAGE_LOG`), and timing field definitions.

---

## Scope & non-goals

**Supported**

- Small constrained binary/integer optimization expressible in current QBridge DSL
- Local compile + NVIDIA CUDA-Q execution
- NL → DSL translation via OpenRouter

**Not supported (by design)**

- Arbitrary Python optimization scripts
- General quantum algorithms (VQE chemistry, Shor, custom ansätze)
- Cloud QBridge compile/solve APIs
- Interactive multi-turn chat UI (one `qbridge "..."` per problem)

Unsupported inputs are **errors**, not best-effort guesses.

---

## License & releases

- Package: **BUSL-1.1** (see [LICENSE](LICENSE))
- Release process: [RELEASE.md](RELEASE.md)
- Compiler DSL docs: [github.com/ProfessionalQwerty/qbridge](https://github.com/ProfessionalQwerty/qbridge)

**Current release:** `qbridge-agent` **0.3.1** with `qbridge[quantum]>=0.3.0`.
