Skip to main content

Unofficial Python SDK for the OPUS workflow automation platform

Project description

opus-aaico

Unofficial Python SDK for the OPUS workflow automation platform by AAICO.

PyPI version Python versions License: MIT


Overview

opus-aaico provides a type-safe, production-ready interface to the OPUS REST API. It covers all 64 API endpoints across 8 resource modules, with both synchronous and asynchronous clients.

Key capabilities:

  • Full API coverage -- all OPUS endpoints wrapped in typed Python methods
  • Sync and async -- OpusClient for scripts, AsyncOpusClient for high-throughput services
  • High-level orchestration -- workflows.run() handles the entire job lifecycle in one call
  • Type safety -- Pydantic v2 models for all requests and responses
  • Resilient -- automatic retry with exponential backoff on transient failures
  • Minimal footprint -- only two runtime dependencies (httpx, pydantic)

Installation

pip install opus-aaico

Requires Python 3.9 or later.

Quick Start

from opus_aaico import OpusClient

client = OpusClient(api_key="your-api-key")

# Run a workflow end-to-end: initiate, execute, poll, and return results
result = client.workflows.run(
    workflow_id="wf-123",
    payload={"query": {"value": "Analyze this document", "type": "str"}},
)

print(result.status)       # "COMPLETED"
print(result.outputs)      # Workflow output variables
print(result.execution_time)  # Seconds elapsed

Authentication

from opus_aaico import OpusClient

# Option 1: Pass directly
client = OpusClient(api_key="sk-...")

# Option 2: With workspace context
client = OpusClient(
    api_key="sk-...",
    workspace_id="ws-...",
)

# Option 3: From environment variables
# Set OPUS_API_KEY, OPUS_WORKSPACE_ID (optional), OPUS_BASE_URL (optional)
client = OpusClient()
Environment Variable Purpose
OPUS_API_KEY API key (required if not passed to constructor)
OPUS_WORKSPACE_ID Default workspace ID
OPUS_BASE_URL Override base URL for staging or self-hosted instances

Resources

The client organizes the API into resource namespaces:

Resource Description Key Methods
client.workflows Workflow management and orchestration get, list, generate, run, share, send_email
client.jobs Job lifecycle initiate, execute, get_status, get_results, poll, search
client.files File upload, download, and search upload, download, search, generate, multipart_upload
client.reviews Human review and task management initiate, list, get, pick, submit_result, submit_output
client.api_keys API key management create, list, rotate, revoke, delete
client.credits Credit balance and usage tracking get_balance, get_usage, record_usage
client.policies Organizational policy management upload, list, get, get_summary, set_active
client.users User and project lookups list, list_projects, get_projects

Usage Examples

Run a Workflow

from opus_aaico import OpusClient

client = OpusClient(api_key="sk-...")

# High-level: one call handles initiate -> execute -> poll -> results
result = client.workflows.run(
    workflow_id="wf-123",
    payload={
        "query": {"value": "Summarize this report", "type": "str"},
        "document": {"value": "https://files.opus.com/doc.pdf", "type": "file"},
    },
    title="Q1 Report Analysis",
    poll_interval=2.0,   # seconds between status checks
    timeout=300,         # max wait time in seconds
)

print(result.status)
print(result.job_id)
print(result.outputs)
print(result.execution_time)

Upload a File

# Upload a local file (handles presigned URL flow automatically)
file_url = client.files.upload("./report.pdf")

# Use it in a workflow
result = client.workflows.run(
    workflow_id="wf-123",
    payload={
        "document": {"value": file_url, "type": "file"},
    },
)

Low-Level Job Control

# Step-by-step control when you need it
job = client.jobs.initiate(
    workflow_id="wf-123",
    title="My Job",
    description="Processing data",
)

client.jobs.execute(
    job_execution_id=job.job_execution_id,
    payload={"query": {"value": "Analyze", "type": "str"}},
)

# Poll until complete
status = client.jobs.poll(job.job_execution_id, timeout=120)

if status.status.value == "COMPLETED":
    results = client.jobs.get_results(job.job_execution_id)
    audit = client.jobs.get_audit(job.job_execution_id)

Search and List

# Search jobs
jobs = client.jobs.search(
    workflow_id="wf-123",
    status=["COMPLETED", "FAILED"],
    start_date="2026-01-01",
    max_results=50,
)

# List workflows
workflows = client.workflows.list(
    query="onboarding",
    active=True,
)

Async Usage

import asyncio
from opus_aaico import AsyncOpusClient

async def main():
    async with AsyncOpusClient(api_key="sk-...") as client:
        # Run multiple workflows concurrently
        tasks = [
            client.workflows.run(
                workflow_id="wf-123",
                payload={"query": {"value": f"Task {i}", "type": "str"}},
            )
            for i in range(5)
        ]
        results = await asyncio.gather(*tasks)
        for r in results:
            print(f"{r.job_id}: {r.status}")

asyncio.run(main())

Error Handling

All errors inherit from OpusError. Catch specific exceptions or the base class:

from opus_aaico import (
    OpusClient,
    AuthenticationError,
    NotFoundError,
    RateLimitError,
    ValidationError,
    PermissionDeniedError,
    OpusError,
)

try:
    result = client.workflows.run("wf-123", payload={...})
except AuthenticationError:
    # 401 -- invalid API key
    pass
except NotFoundError:
    # 404 -- workflow does not exist
    pass
except RateLimitError as e:
    # 429 -- retry after e.retry_after seconds
    pass
except ValidationError:
    # 400 -- invalid request parameters
    pass
except PermissionDeniedError:
    # 403 -- insufficient permissions
    pass
except OpusError as e:
    # Any other API error
    print(f"{e.message} (status={e.status_code})")

Error hierarchy:

OpusError
├── AuthenticationError       (401)
├── PermissionDeniedError     (403)
├── NotFoundError             (404)
├── ValidationError           (400)
├── RateLimitError            (429)  -- includes retry_after
├── APIError                  (5xx)
├── TimeoutError              (request or polling timeout)
└── ConnectionError           (network failure)

The SDK automatically retries on 429 and 5xx errors with exponential backoff (up to 3 retries by default).

Configuration

client = OpusClient(
    api_key="sk-...",              # required (or set OPUS_API_KEY)
    workspace_id="ws-...",         # optional default workspace
    base_url="https://operator.opus.com",  # override for staging
    timeout=30.0,                  # request timeout in seconds
    max_retries=3,                 # retries on transient failures
)

Both OpusClient and AsyncOpusClient support context managers for proper connection cleanup:

with OpusClient(api_key="sk-...") as client:
    result = client.workflows.run("wf-123", payload={...})

Architecture

src/opus_aaico/
├── __init__.py              # Public API: OpusClient, AsyncOpusClient, errors
├── _client.py               # HTTP layer: auth, retry, request handling
├── _sync.py                 # OpusClient (synchronous)
├── _async.py                # AsyncOpusClient (asynchronous)
├── _exceptions.py           # Typed error hierarchy
├── _constants.py            # Base URL, defaults, version
├── resources/               # One module per API resource
│   ├── workflows.py         # Workflow CRUD + run() orchestrator
│   ├── jobs.py              # Job lifecycle + polling
│   ├── files.py             # File upload/download + multipart
│   ├── reviews.py           # Human review management
│   ├── api_keys.py          # API key CRUD
│   ├── credits.py           # Credit tracking
│   ├── policies.py          # Policy management
│   └── users.py             # User and project lookups
├── types/                   # Pydantic v2 models for all DTOs
│   ├── enums.py             # JobStatus, ReviewType, etc.
│   ├── shared.py            # Base model, UserDetails, etc.
│   ├── workflows.py         # Workflow, WorkflowRunResult, etc.
│   ├── jobs.py              # JobAudit, JobSearchItem, etc.
│   └── ...                  # One file per resource
└── _utils/
    ├── polling.py           # Exponential backoff polling
    └── files.py             # Presigned URL upload helpers

Design principles:

  • Resource-based namespacing following the OpenAI/Stripe SDK pattern
  • Async-first internals with sync wrapper using httpx's dual transport
  • camelCase to snake_case conversion handled automatically by Pydantic aliases
  • Minimal dependencies -- only httpx and pydantic at runtime

Development

See CONTRIBUTING.md for setup instructions and development workflow.

Changelog

See CHANGELOG.md for release history.

License

MIT License. See LICENSE for details.

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

opus_aaico-0.1.2.tar.gz (39.3 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

opus_aaico-0.1.2-py3-none-any.whl (32.1 kB view details)

Uploaded Python 3

File details

Details for the file opus_aaico-0.1.2.tar.gz.

File metadata

  • Download URL: opus_aaico-0.1.2.tar.gz
  • Upload date:
  • Size: 39.3 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.13.7

File hashes

Hashes for opus_aaico-0.1.2.tar.gz
Algorithm Hash digest
SHA256 538a7356d152dcfd437131aa811756423db70a1c4cca29ea29394db05763a9f0
MD5 f3e2ffe46061118738f2371f5320c4f1
BLAKE2b-256 49f559af2ad2fd816ecb4adbaad37d4454037542f3f2c027cdfc172b9ce068b3

See more details on using hashes here.

File details

Details for the file opus_aaico-0.1.2-py3-none-any.whl.

File metadata

  • Download URL: opus_aaico-0.1.2-py3-none-any.whl
  • Upload date:
  • Size: 32.1 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.13.7

File hashes

Hashes for opus_aaico-0.1.2-py3-none-any.whl
Algorithm Hash digest
SHA256 1a21dbeaec907be53cd89f2b9748342f83765599b151cca2264d5a1ae7ebc99f
MD5 e65f62ef43e24f004d95509668408e37
BLAKE2b-256 cc2951f772e7f8657f1469b7e599c41a77b1ce2a8952409d13bf9e1387722137

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page