feat(runtime): add QUDORA provider - #1292
Conversation
|
Important Review skippedAuto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro Plus Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
0092483 to
abdffee
Compare
Codecov Report❌ Patch coverage is
📢 Thoughts on this report? Let us know! |
1110662 to
a13fcd5
Compare
Argus reviewAuto-review is off for this repo. Tick the box below to run a review on this PR.
Estimated cost
Tip: you can also comment |
|
@ryanhill1 I have added some changes to #1306 and tested against |
Add a direct-REST QUDORA runtime integration under qbraid/runtime/qudora (provider, device, job, and session classes), register lazy-load and packaging entry-points, and add fully-mocked tests. Reuses the existing qasm2/qasm3 program specs, so no vendor dependency or converter is needed. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
a13fcd5 to
0dd4f9e
Compare
* fix(qudora): correct backend field mapping against the live API
Verified the provider against api.qudora.com with a real token. The mocked
fixtures were modeled on assumption rather than a captured response, and
diverged from what the API returns in ways that hid two bugs:
- `_build_profile` read `backend["id"]`, a key no backend record has (the
numeric identifier is `user_id`). `qudora_backend_id` was therefore `None`
for every device, so `QudoraDevice.status()` always took its "id
unavailable" branch and reported ONLINE without calling the status
endpoint, which was unreachable in practice. `/backends/status/108`
returns "Idle"; the `username` is rejected with a 422.
- `QudoraJob.result()` read `device_id` from the job record's `target`, but
QUDORA echoes the backend's display name there ("QVLS-Q1 Emulator"), not
the id jobs are submitted against. Results came back with a `device_id`
matching no device the provider can return; it now prefers the device's id.
The listing also publishes `simulator`, `basis_gates`, `status_name` and
`is_online`, so `simulator=True` is no longer hardcoded and `basis_gates`
is populated. Device ids are backend `username`s, which are email addresses.
Per CONTRIBUTING, required fields are now indexed directly rather than
fetched with a default: an unmapped `JobStatusName` raises instead of
degrading to `JobStatus.UNKNOWN`, which is not terminal and would have made
`result()` poll a finished job until `wait_for_final_state` timed out.
Annotations use PEP 604 syntax, and the mypy exclusion is dropped — the
remaining errors are the same structural ones rigetti, quantinuum and pasqal
already produce unexcluded.
Fixtures are rebuilt from captured payloads, and `test_qudora_remote.py`
adds credentialed tests behind the `remote` marker that assert the response
shape the unit tests hard-code, so upstream drift fails loudly. Adding
`tests/runtime/qudora/__init__.py` also puts the suite back under pylint,
which skipped the directory entirely for lack of a package marker.
Registers `qudora` in docs/api/qbraid.runtime.rst, the only provider missing.
* fix(qudora): detect QASM version via shared typing, not a header scan
`_detect_language` scanned line-by-line for a prefix of "OPENQASM", which
reads a version named inside a block comment as the program's own:
/*
OPENQASM 2.0;
*/
OPENQASM 3.0;
...
returned OpenQASM2, sending the wrong `language` to QUDORA. It also accepted
`OPENQASM 3` with no semicolon as a valid declaration.
Delegates to `qbraid.programs.typer.get_qasm_type_alias`, which routes through
pyqasm's `extract_qasm_version` (already a dependency here via `_validate_qasm`):
comments are stripped before the header is located, and the version must match
`OPENQASM <major>[.<minor>];`. The alias is mapped to the QUDORA language, so a
dialect QUDORA does not accept — `qasm2_kirin` — is now named in the error
rather than silently matching the "OPENQASM 2" prefix.
Also documents why `submit()` defaults `name` to "qbraid" rather than omitting
it. QUDORA's schema requires a string: both a null value and a missing key are
rejected with 422 `{"type": "string_type", "loc": ["body", "name"]}`, verified
against the live API and pinned by a remote test.
* fix(qudora): raise typed errors and resolve device_id without a device
Unmapped vendor status values raised a bare KeyError out of QudoraDevice.status()
and QudoraJob._map_status(). Raising is right -- defaulting a device to ONLINE or a
job to UNKNOWN would report a state QUDORA never sent, and UNKNOWN is not terminal,
so result() would poll a finished job until wait_for_final_state timed out. Only the
type was wrong: raise QudoraDeviceError / QudoraJobError, matching the AQT provider,
and name the known values in the message.
Result.device_id fell back to the job record's `target` when the job carried no
device, and `target` is the backend's display name ("QVLS-Q1 Emulator") rather than
the username jobs are submitted against. Resolve it through the backend listing so a
job loaded without a device still yields an id get_device() accepts, falling back to
the display name only for a backend that is no longer published.
available_settings() indexed `user_settings_schema` strictly but then tolerated a
None value. Every backend publishes the schema (asserted live), so drop the `or {}`
and cover the one legitimate empty case: a schema declaring no properties.
Type-check clean now that runtime/qudora/ is no longer in the mypy exclude list:
TargetProfile allows extra keys but the __init__ mypy synthesizes only names the
declared fields, so the vendor-specific ones are passed as a mapping, which keeps
the declared arguments checked. The two deliberate signature narrowings are ignored
explicitly rather than widened.
Also assert the job-record field contract on a cancelled job: result() reads
`user_error` by strict index only on the non-completed path, which the existing
completed-job contract test cannot reach.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: TheGupta2012 <harshit.11235@gmail.com>
Summary of changes
Adds a direct-REST QUDORA runtime provider to the qBraid SDK. It talks to
https://api.qudora.comover HTTPS withBearerauth (no vendor SDK) and reuses qBraid's existingqasm2/qasm3program specs, so there is no new dependency and no converter.qbraid/runtime/qudora/package:QudoraSession(qbraid_coreSessionsubclass with bearer auth and retries),QudoraProvider(get_devices/get_device,_build_profile;device_idis the backendusername),QudoraDevice(status,submit,available_settings, OpenQASM language auto-detection with apyqasmvalidate hook), andQudoraJob(status mapping, result parsing).qudoraqbraid.providers/qbraid.jobsentry-points and mypy exclude inpyproject.toml, plus lazy-load registration inqbraid/runtime/__init__.py.tests/runtime/qudora/suite and atests/runtime/test_loader.pyupdate.Related
qBraid/qbraid-runtime-apibranchfeature/qudora-integration.