Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,10 @@ Each generated endpoint module exposes four callables: `sync`, `sync_detailed`,

For options (`api_key`, `base_url`, `max_retries`, `timeout`, `extension`), error classes, retry behavior, pagination, polling, sessions, and downstream-SDK extension hooks, see the [API reference](https://ionq.github.io/ionq-core-python/).

## Examples

See [`examples/`](examples/) for direct API examples, including sync and async downstream-SDK integration with the extension API.

## Versioning

This package follows [SemVer 2.0](https://semver.org/spec/v2.0.0.html), independent of the upstream REST API version - pass an explicit `base_url` to `IonQClient` to pin against a different API. Print the installed version with:
Expand Down
42 changes: 42 additions & 0 deletions examples/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
# Examples

These examples show direct `ionq-core` usage close to the IonQ REST API.

## Downstream SDK Integration

`downstream_integration.py` and `downstream_integration_async.py` demonstrate
how a higher-level SDK can wrap `ionq-core` with the extension API while still
using the generated endpoint modules for job submission and result retrieval.

The examples configure:

- a downstream SDK `User-Agent` token
- SDK-specific default headers
- sync or async HTTP event hooks for request, response, and error logging
- an error mapper that wraps `APIError` and `RateLimitError` into an
SDK-defined exception type

Install and run the sync example:

```sh
pip install ionq-core
export IONQ_API_KEY=your-api-key
python examples/downstream_integration.py
```

Run the async example:

```sh
python examples/downstream_integration_async.py
```

On Windows PowerShell, set the API key with:

```powershell
$env:IONQ_API_KEY = "your-api-key"
python examples/downstream_integration.py
python examples/downstream_integration_async.py
```

Both scripts submit a Bell-state circuit to the free `simulator`, wait for the
job to complete, and print the returned probabilities.
112 changes: 112 additions & 0 deletions examples/downstream_integration.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
# SPDX-FileCopyrightText: 2026 IonQ, Inc.
# SPDX-License-Identifier: Apache-2.0

"""Synchronous downstream-SDK integration example.

This script demonstrates how a downstream SDK can use the ionq-core extension
API to add SDK-specific headers, a User-Agent token, sync HTTP event logging,
and error mapping while submitting a Bell-state circuit to the simulator.
"""

from __future__ import annotations

from collections.abc import Mapping
from typing import Any

import httpx

from ionq_core import (
APIError,
AuthenticatedClient,
ClientExtension,
EventHook,
IonQClient,
RateLimitError,
wait_for_job,
)
from ionq_core.api.default import create_job, get_job_probabilities
from ionq_core.models.circuit_job_creation_payload import CircuitJobCreationPayload


class ExampleSDKError(Exception):
"""Exception type that a downstream SDK might expose to its users."""


class LoggingHook(EventHook):
"""Minimal sync event hook used by the example SDK."""

def on_request(self, request: httpx.Request) -> None:
print(f"example-sdk -> {request.method} {request.url}")

def on_response(self, request: httpx.Request, response: httpx.Response) -> None:
print(f"example-sdk <- {response.status_code} {request.method} {request.url.path}")

def on_error(self, request: httpx.Request, error: Exception) -> None:
print(f"example-sdk !! {request.method} {request.url.path}: {error}")


def map_ionq_error(error: Exception) -> Exception:
"""Translate ionq-core API errors into a downstream SDK exception."""
if isinstance(error, RateLimitError):
return ExampleSDKError(f"IonQ rate limit exceeded; retry after {error.retry_after!r} seconds")
if isinstance(error, APIError):
return ExampleSDKError(f"IonQ API request failed: {error.message}")
return error


def example_sdk_client() -> AuthenticatedClient:
"""Build an IonQ client configured as a downstream SDK would configure it."""
return IonQClient(
extension=ClientExtension(
user_agent_token="example-sdk/0.1",
default_headers={"X-Example-SDK": "sync"},
event_hooks=(LoggingHook(),),
error_mapper=map_ionq_error,
)
)


def bell_state_payload() -> CircuitJobCreationPayload:
"""Build the Bell-state job payload used by the example."""
return CircuitJobCreationPayload.from_dict(
{
"type": "ionq.circuit.v1",
"backend": "simulator",
"shots": 100,
"input": {
"gateset": "qis",
"qubits": 2,
"circuit": [
{"gate": "h", "target": 0},
{"gate": "cnot", "control": 0, "target": 1},
],
},
}
)


def run_bell_state(client: AuthenticatedClient) -> Mapping[str, float]:
"""Submit a Bell-state job, wait for completion, and fetch probabilities."""
created = create_job.sync(client=client, body=bell_state_payload())
if created is None:
raise ExampleSDKError("IonQ API did not return a job creation response")

completed = wait_for_job(client, created.id)
probabilities = get_job_probabilities.sync(uuid=completed.id, client=client)
if probabilities is None:
raise ExampleSDKError("IonQ API did not return probabilities")
return probabilities.additional_properties


def main() -> None:
"""Run the sync downstream integration example."""
client = example_sdk_client()
try:
probabilities: Mapping[str, Any] = run_bell_state(client)
print(dict(probabilities))
finally:
client.get_httpx_client().close()


if __name__ == "__main__":
main()
113 changes: 113 additions & 0 deletions examples/downstream_integration_async.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
# SPDX-FileCopyrightText: 2026 IonQ, Inc.
# SPDX-License-Identifier: Apache-2.0

"""Asynchronous downstream-SDK integration example.

This script demonstrates how a downstream SDK can use the ionq-core extension
API to add SDK-specific headers, a User-Agent token, async HTTP event logging,
and error mapping while submitting a Bell-state circuit to the simulator.
"""

from __future__ import annotations

import asyncio
from collections.abc import Mapping
from typing import Any

import httpx

from ionq_core import (
APIError,
AsyncEventHook,
AuthenticatedClient,
ClientExtension,
IonQClient,
RateLimitError,
async_wait_for_job,
)
from ionq_core.api.default import create_job, get_job_probabilities
from ionq_core.models.circuit_job_creation_payload import CircuitJobCreationPayload


class ExampleAsyncSDKError(Exception):
"""Exception type that an async downstream SDK might expose to its users."""


class AsyncLoggingHook(AsyncEventHook):
"""Minimal async event hook used by the example SDK."""

async def on_request(self, request: httpx.Request) -> None:
print(f"example-async-sdk -> {request.method} {request.url}")

async def on_response(self, request: httpx.Request, response: httpx.Response) -> None:
print(f"example-async-sdk <- {response.status_code} {request.method} {request.url.path}")

async def on_error(self, request: httpx.Request, error: Exception) -> None:
print(f"example-async-sdk !! {request.method} {request.url.path}: {error}")


def map_ionq_error(error: Exception) -> Exception:
"""Translate ionq-core API errors into a downstream async SDK exception."""
if isinstance(error, RateLimitError):
return ExampleAsyncSDKError(f"IonQ rate limit exceeded; retry after {error.retry_after!r} seconds")
if isinstance(error, APIError):
return ExampleAsyncSDKError(f"IonQ API request failed: {error.message}")
return error


def example_sdk_client() -> AuthenticatedClient:
"""Build an IonQ client configured as an async downstream SDK."""
return IonQClient(
extension=ClientExtension(
user_agent_token="example-async-sdk/0.1",
default_headers={"X-Example-SDK": "async"},
async_event_hooks=(AsyncLoggingHook(),),
error_mapper=map_ionq_error,
)
)


def bell_state_payload() -> CircuitJobCreationPayload:
"""Build the Bell-state job payload used by the example."""
return CircuitJobCreationPayload.from_dict(
{
"type": "ionq.circuit.v1",
"backend": "simulator",
"shots": 100,
"input": {
"gateset": "qis",
"qubits": 2,
"circuit": [
{"gate": "h", "target": 0},
{"gate": "cnot", "control": 0, "target": 1},
],
},
}
)


async def run_bell_state(client: AuthenticatedClient) -> Mapping[str, float]:
"""Submit a Bell-state job, wait for completion, and fetch probabilities."""
created = await create_job.asyncio(client=client, body=bell_state_payload())
if created is None:
raise ExampleAsyncSDKError("IonQ API did not return a job creation response")

completed = await async_wait_for_job(client, created.id)
probabilities = await get_job_probabilities.asyncio(uuid=completed.id, client=client)
if probabilities is None:
raise ExampleAsyncSDKError("IonQ API did not return probabilities")
return probabilities.additional_properties


async def amain() -> None:
"""Run the async downstream integration example."""
client = example_sdk_client()
try:
probabilities: Mapping[str, Any] = await run_bell_state(client)
print(dict(probabilities))
finally:
await client.get_async_httpx_client().aclose()


if __name__ == "__main__":
asyncio.run(amain())