Metadata-Version: 2.4
Name: strait-python
Version: 0.1.1
Summary: Strait Python SDK
Author: Strait
License: MIT
Project-URL: Repository, https://github.com/strait-dev/strait-python
Project-URL: Issues, https://github.com/strait-dev/strait-python/issues
Project-URL: Changelog, https://github.com/strait-dev/strait-python/blob/main/CHANGELOG.md
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Classifier: Typing :: Typed
Requires-Python: >=3.11
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: httpx>=0.28.0
Provides-Extra: dev
Requires-Dist: mypy>=1.18.2; extra == "dev"
Requires-Dist: ruff>=0.14.1; extra == "dev"
Requires-Dist: pytest>=8.0; extra == "dev"
Requires-Dist: pytest-asyncio>=0.24; extra == "dev"
Requires-Dist: pytest-cov>=6.0; extra == "dev"
Dynamic: license-file

<p align="center">
  <img src=".github/header.png" alt="Strait" width="100%" />
</p>

<p align="center">
  <strong>Python SDK for the Strait API</strong>
</p>

<p align="center">
  <a href="https://github.com/strait-dev/strait-python/actions/workflows/ci.yml"><img src="https://github.com/strait-dev/strait-python/actions/workflows/ci.yml/badge.svg" alt="CI" /></a>
  <a href="https://pypi.org/project/strait-python/"><img src="https://img.shields.io/pypi/v/strait-python" alt="PyPI" /></a>
  <a href="https://pypi.org/project/strait-python/"><img src="https://img.shields.io/pypi/pyversions/strait-python" alt="Python" /></a>
  <a href="https://github.com/strait-dev/strait-python/blob/main/LICENSE"><img src="https://img.shields.io/badge/license-MIT-green" alt="License" /></a>
  <img src="https://img.shields.io/badge/types-mypy%20strict-blue" alt="Types" />
</p>

---

The official Python SDK for [Strait](https://strait.dev) -- an open-source job execution and workflow orchestration platform for humans and AI agents. Define jobs, build DAG workflows, run durable AI agents with cost tracking, and manage the full run lifecycle from Python.

Sync and async clients, 19 domain services (~186 operations), authoring DSL, composition helpers, FSM state machines, and PEP 561 type hints.

[Website](https://strait.dev) | [Platform Repo](https://github.com/strait-dev/strait) | [Documentation](https://docs.strait.dev) | [PyPI](https://pypi.org/project/strait-python/)

## Installation

```bash
pip install strait-python
```

Requires Python 3.11+.

## Quick Start

```python
from strait import Client

client = Client(base_url="https://api.strait.dev", api_key="your-key")

# List jobs
jobs = client.jobs.list()

# Trigger a job
run = client.jobs.trigger(job["id"], {"payload": {"key": "value"}})

# Get a run
run = client.runs.get(run["id"])
```

### Async

```python
from strait import AsyncClient

async with AsyncClient.from_env() as client:
    jobs = await client.jobs.list()
    run = await client.jobs.trigger("job-id", {"payload": {}})
```

## Configuration

Three ways to create a client:

```python
# From environment variables (STRAIT_BASE_URL, STRAIT_API_KEY)
client = Client.from_env()

# From strait.json file (token always from STRAIT_API_KEY env var)
client = Client.from_file()

# Inline
client = Client(base_url="https://api.strait.dev", api_key="your-key")
```

All methods work with `AsyncClient` too. See [Configuration Guide](docs/configuration.md) for details.

## Features

### Domain Operations (19 Services)

All 186 API operations organized into typed service classes:

| Service | Examples |
|---|---|
| `client.jobs` | list, create, get, update, delete, trigger, bulk_trigger |
| `client.runs` | list, get, delete, replay, bulk_cancel, get_dlq |
| `client.workflows` | list, create, trigger, get_diff, get_policy |
| `client.workflow_runs` | list, pause, resume, approve_step, skip_step |
| `client.deployments` | list, create, finalize, promote, rollback |
| `client.sdk_runs` | complete_run, heartbeat_run, checkpoint_run |
| `client.rbac` | list_roles, create_member, seed_roles |
| + 12 more | environments, secrets, api_keys, webhooks, ... |

### Authoring DSL

Define jobs, workflows, and AI agents with a type-safe DSL:

```python
from strait.authoring import define_job, define_workflow, JobOptions, WorkflowOptions
from strait.authoring import job_step, approval_step

wf = define_workflow(WorkflowOptions(
    name="Order Pipeline", slug="order-pipeline", project_id="proj-1",
    steps=[
        job_step("validate", "validate-job"),
        job_step("charge", "charge-job", depends_on=["validate"]),
        approval_step("approve", depends_on=["charge"]),
        job_step("ship", "ship-job", depends_on=["approve"]),
    ],
))
```

See [Authoring Guide](docs/authoring.md) for AI agents, event definitions, RunContext, and test harness.

### Composition Helpers

```python
from strait.composition import with_retry, wait_for_run, paginate, RetryOptions

# Retry with exponential backoff
result = with_retry(lambda: client.jobs.trigger("j1", payload), RetryOptions(attempts=5))

# Wait for a run to complete
run = wait_for_run(
    get_run=lambda rid: client.runs.get(rid),
    get_status=lambda r: r["status"],
    run_id="run-1",
)
```

See [Composition Guide](docs/composition.md) for cost budgets, checkpoint resume, pagination, and more.

### Error Handling

All errors inherit from `StraitError` with typed exceptions for each HTTP status:

```python
from strait import NotFoundError, RateLimitedError

try:
    job = client.jobs.get("nonexistent")
except NotFoundError as e:
    print(f"Not found: {e} (status={e.status})")
except RateLimitedError as e:
    print(f"Rate limited, retry after backoff")
```

See [Error Handling Guide](docs/errors.md) for the full exception hierarchy.

### Middleware

```python
from strait import Client, Middleware

mw = Middleware(
    on_request=lambda ctx: print(f"-> {ctx.method} {ctx.url}"),
    on_response=lambda ctx: print(f"<- {ctx.status} ({ctx.duration_ms}ms)"),
)
client = Client.from_env(middleware=[mw])
```

### FSM State Machines

```python
from strait.fsm import transition_run, RunStatus, RunEvent

next_status = transition_run(RunStatus.EXECUTING, RunEvent.COMPLETE)
assert next_status == RunStatus.COMPLETED
```

## Documentation

| Guide | Description |
|---|---|
| [Configuration](docs/configuration.md) | Client creation, auth types, strait.json, env vars |
| [Authoring](docs/authoring.md) | Jobs, workflows, agents, RunContext, test harness |
| [Composition](docs/composition.md) | Retry, wait, paginate, cost budget, checkpoint resume |
| [Errors](docs/errors.md) | Exception hierarchy, HTTP error mapping |
| [Examples](examples/) | Runnable code examples for all major features |

## Development

```bash
make bootstrap   # Create venv + install deps
make lint        # Run ruff (lint + format check)
make typecheck   # Run mypy (strict mode)
make test        # Run pytest with coverage
make run-all     # lint + typecheck + test
```

See [CONTRIBUTING.md](CONTRIBUTING.md) for the full contributor guide.

## License

MIT
