Version 0.1.0-alpha.1 · canonical source: iRoute repository

iRoute Python SDK

iroute is a dependency-light synchronous Python client for the iRoute v1 HTTP and SSE API. It returns decoded dictionaries, consumes events incrementally, and supports execution, polling, cancellation, approvals, artifacts, gateway health, and observability.

Routing, provider selection, validation, workflow retries, and fallback remain server-side.

Status and requirements

  • SDK version: 0.1.0a1
  • Minimum Python: 3.12
  • Verified baseline: Python 3.14
  • Runtime dependencies: standard library only
  • Package name: iroute

Once published, the public alpha is installed from PyPI as a prerelease package. The source installation below remains available.

Start iRoute

docker compose --file deploy/compose.sqlite.yaml up --build --wait

The default API URL is http://localhost:8080. See the shared SDK usage guide for source-run and authentication options.

Install from PyPI

python -m pip install --pre iroute==0.1.0a1

Install from source

Use an isolated environment and install the local SDK:

python3 -m venv .venv
. .venv/bin/activate
python -m pip install -e ../iRoute/sdks/python

For a checkout-only quick start, set PYTHONPATH=../iRoute/sdks/python/src instead of installing.

Create a client

import os

from iroute import IRouteClient

client = IRouteClient(
    os.getenv("IROUTE_URL", "http://localhost:8080"),
    token=os.getenv("IROUTE_TOKEN"),
    tenant_id=os.getenv("IROUTE_TENANT", "demo"),
    actor_id=os.getenv("IROUTE_ACTOR", "python-app"),
    permission_scopes=(),
    timeout=30,
)

In Development the identity options become headers. In JWT mode the server derives identity and scopes from token claims and will not allow headers to elevate them. opener can be replaced with a compatible callable for tests or a custom transport policy.

Submit an execution

import uuid

request = {
    "taskType": "email.draft",
    "input": {"purpose": "Confirm the Python SDK integration."},
    "idempotencyKey": f"python-email-{uuid.uuid4()}",
    "constraints": {
        "maxOutputTokens": 500,
        "deadlineMilliseconds": 30_000,
        "minimumQuality": 0.8,
    },
}

execution = client.execute(request)

print(execution["executionId"], execution["status"])

The shorthand form is also available when no other request fields are needed:

execution = client.execute(
    "email.draft",
    {"purpose": "Confirm the Python SDK integration."},
)

Prefer the dictionary form so you can supply a stable idempotency key and constraints.

Poll until terminal

import time

terminal = {"Succeeded", "Failed", "Cancelled", "TimedOut"}

while execution["status"] not in terminal:
    time.sleep(0.5)
    current = client.get(execution["executionId"])
    if current is None:
        raise RuntimeError("Execution is not visible to this tenant.")
    execution = current

if execution["status"] == "Succeeded":
    print(execution["outcome"]["output"])
else:
    error = execution.get("error") or {}
    print(error.get("code"), error.get("detail"))

WaitingForApproval is non-terminal.

Stream and reconnect to events

stream_events reads the response incrementally:

last_sequence = 0

for event in client.stream_events(execution["executionId"], last_sequence):
    print(event["sequence"], event["type"])
    last_sequence = event["sequence"]

Persist the last processed sequence and pass it back after reconnecting. Ignore unknown event types and fields.

The iterator occupies the calling thread while the connection is open. Run it in an application-managed worker thread if the rest of the application must remain responsive.

Cancel an execution

accepted = client.cancel(execution["executionId"])
if not accepted:
    print("Execution was not found.")

Cancellation is durable but cooperative. Poll or reconnect to events to confirm the terminal result.

Approve or deny an action

Read actionId from an approval.required event:

result = client.submit_approval(execution["executionId"], {
    "actionId": action_id,
    "approved": True,
    "reason": "Reviewed by an authorized operator",
})

execution = result["execution"]

The deciding token needs approval:grant and the action's required scope.

Retrieve an artifact

artifacts = (execution.get("outcome") or {}).get("artifacts", [])
if artifacts:
    artifact = client.get_artifact(artifacts[0]["artifactId"])
    print(artifact["content"] if artifact else "not found")

Not found returns None, including for cross-tenant access.

Health and observability

from datetime import datetime, timedelta, timezone

health = client.get_model_gateway_health()
print(health["gatewayId"], health["status"])

now = datetime.now(timezone.utc)
summary = client.get_observability_summary(
    from_time=(now - timedelta(hours=24)).isoformat(),
    to_time=now.isoformat(),
    task_type="email.draft",
)
print(summary["totals"]["executions"])

timeline = client.get_execution_timeline(execution["executionId"])
print(timeline["traceId"] if timeline else "not found")

Gateway health returns a decoded body for HTTP 200 and 503. Observability is tenant scoped. Reported cost has no currency in v1.

Errors and timeouts

from iroute import IRouteApiError

try:
    execution = client.execute(request)
except IRouteApiError as error:
    print(error.status, error.code, error.detail)
    print(error.response_body)

Transport timeout is configured in the client constructor. Standard-library network exceptions remain transport exceptions. The SDK does not automatically retry. Retrying a submission requires the same idempotency key.

Method reference

MethodResult
execute(request)Execution dictionary
execute(task_type, input_data)Shorthand execution dictionary
get(execution_id)Dictionary or None
cancel(execution_id)False when not found
submit_approval(execution_id, decision)Approval result dictionary
stream_events(execution_id, after_sequence=0)Incremental iterator
get_artifact(artifact_id)Dictionary or None
get_model_gateway_health()Health dictionary
get_observability_summary(...)Summary dictionary
get_execution_timeline(execution_id)Timeline or None

Run the example and tests

From the repository root:

PYTHONPATH=sdks/python/src python3 examples/sdks/python/execute.py
PYTHONPATH=sdks/python/src python3 -m unittest discover -s sdks/python/tests

Public wire shapes are defined by the OpenAPI document and JSON Schemas.