Metadata-Version: 2.4
Name: dualeai
Version: 0.1.0
Summary: Python client SDK for Duale AI agent tasks and tools
Author: Duale AI
License-Expression: Apache-2.0
Project-URL: Homepage, https://duale.ai
Project-URL: Documentation, https://duale.ai/en/docs/sdk
Project-URL: Repository, https://github.com/dualeai/dualeai-python.git
Project-URL: Changelog, https://github.com/dualeai/dualeai-python/releases
Project-URL: Issues, https://github.com/dualeai/dualeai-python/issues
Keywords: ai,agents,sdk,dualeai,orchestration
Classifier: Development Status :: 3 - Alpha
Classifier: Intended Audience :: Developers
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Programming Language :: Python :: 3.14
Classifier: Programming Language :: Python :: Implementation :: CPython
Classifier: Typing :: Typed
Requires-Python: >=3.10
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: aiofiles~=24.1.0
Requires-Dist: aiohttp~=3.13
Requires-Dist: hpke-http[aiohttp,zstd]~=1.6
Requires-Dist: aiojobs~=1.4
Requires-Dist: aiosqlite~=0.20
Requires-Dist: opentelemetry-api~=1.44
Requires-Dist: opentelemetry-exporter-otlp-proto-http~=1.44
Requires-Dist: opentelemetry-sdk~=1.44
Requires-Dist: pydantic-settings~=2.10
Requires-Dist: pydantic~=2.12
Requires-Dist: python-dotenv~=1.0
Requires-Dist: redis[hiredis]~=5.2
Requires-Dist: structlog~=25.3
Requires-Dist: tenacity~=9.0
Requires-Dist: typing-extensions~=4.12
Requires-Dist: uuid-utils~=0.11
Provides-Extra: telemetry
Requires-Dist: opentelemetry-instrumentation~=0.65b0; extra == "telemetry"
Requires-Dist: opentelemetry-instrumentation-aiohttp-client~=0.65b0; extra == "telemetry"
Requires-Dist: opentelemetry-instrumentation-redis~=0.65b0; extra == "telemetry"
Requires-Dist: opentelemetry-instrumentation-sqlite3~=0.65b0; extra == "telemetry"
Requires-Dist: opentelemetry-instrumentation-system-metrics~=0.65b0; extra == "telemetry"
Provides-Extra: dev
Requires-Dist: dualeai[telemetry]; extra == "dev"
Requires-Dist: aioboto3~=15.5; extra == "dev"
Requires-Dist: aioresponses~=0.7; extra == "dev"
Requires-Dist: freezegun~=1.5; extra == "dev"
Requires-Dist: moto[s3,server]~=5.1; extra == "dev"
Requires-Dist: hypothesis~=6.151; extra == "dev"
Requires-Dist: packaging~=24.2; extra == "dev"
Requires-Dist: psutil~=7.1; extra == "dev"
Requires-Dist: ty~=0.0; extra == "dev"
Requires-Dist: pytest-aiohttp~=1.1; extra == "dev"
Requires-Dist: pytest-socket~=0.7; extra == "dev"
Requires-Dist: pytest-asyncio~=1.3; extra == "dev"
Requires-Dist: pytest-codspeed~=4.3; extra == "dev"
Requires-Dist: pytest-cov~=7.1; extra == "dev"
Requires-Dist: pytest-repeat~=0.9; extra == "dev"
Requires-Dist: pytest-timeout~=2.4; extra == "dev"
Requires-Dist: pytest-xdist[psutil]~=3.8; extra == "dev"
Requires-Dist: pytest~=9.0; extra == "dev"
Requires-Dist: ruff~=0.7; extra == "dev"
Requires-Dist: toml~=0.10; extra == "dev"
Requires-Dist: twine~=6.0; extra == "dev"
Requires-Dist: vulture~=2.14; extra == "dev"
Dynamic: license-file

# Duale AI Python SDK

Submit bounded AI agent work to the Duale AI managed runtime and receive typed results in Python. The SDK is not a
direct model-provider client.

**Status:** Public preview. Interfaces can change before a stable release.

## Install

The distribution is named `dualeai`; import it as `dualeai`. Python 3.10 or newer is required.

```bash
python -m pip install dualeai
```

## Before the first request

Before you start, you need a Tenant, an API token, and an eligible model pool. The token identifies the Agent Identity
for a Task. Set that identity's public identifier in `DUALEAI_AGENT_ID` only when you host Tools.

Set `DUALEAI_TENANT_ID` for Library work. Every Library route takes the Tenant as a path segment, and task-scoped
attachments create a Library, so uploads need it too. `upload_attachments()` does not read `DUALEAI_AGENT_ID`: it takes
an `agent_id` keyword, and falls back to the sole agent registered on the SDK when exactly one is registered. Pass
`agent_id=sdk.agent_id` to route an upload through the configured identity.

Set the token shown during provisioning:

```bash
export DUALEAI_TOKEN=dualeai_your_token_here
```

Requests use `https://api.duale.ai` by default. Set `DUALEAI_ENDPOINT` only when your access instructions name another
environment.

## Submit a typed Task request

```python
import asyncio

from pydantic import BaseModel

from dualeai import ask, create_sdk


class SupportDecision(BaseModel):
    next_action: str
    reason: str


async def main() -> None:
    async with create_sdk() as sdk:
        response = await ask(
            action="Review this support case and return the next safe action.",
            res=SupportDecision,
            sdk=sdk,
        )
        print(response.task_id)
        decision = await response.model()
        print(decision.next_action)


asyncio.run(main())
```

Save the example as `quickstart.py`, then run it:

```bash
python quickstart.py
```

A successful run prints a Task identifier and the validated next action.

`ask()` returns an `AgentResponse` handle before the Platform has necessarily accepted the submission.
`await response.model()` waits for the observable outcome and validates a completed Task Result against
`SupportDecision`. Persist `response.task_id` before waiting so you can reconcile a transport or replay failure.

## Before live use

Three boundaries matter before live use:

- Streaming output can be replaced. Clear accumulated content on `BridgeContentResetResponse` and take the final result
  from `response.model()`.
- Customer Tool handling is not exactly once. For a state-changing Customer Tool, authorize each action and reconcile
  uncertain External Effects against a durable record.
- Uncaught Tool exception text can reach a model provider. Do not put credentials, personal data, or other secrets in
  exception messages.

## Next steps

- [Agent harness](https://duale.ai/en/docs/agent-harness) defines Agent, Task, Executor, Tool, Context, and the other
  runtime terms.
- [SDK concepts](https://duale.ai/en/docs/sdk/concepts) explains Task outcomes, routing, streaming, and continuation.
- [Authoring Tools](https://duale.ai/en/docs/sdk/tools) covers Tool publication, delivery, retries, identifiers, and
  model-facing errors.
- [Attachments](https://duale.ai/en/docs/sdk/attachments) and [Library
  management](https://duale.ai/en/docs/sdk/manage-libraries) cover documents.
- [Errors and reliability](https://duale.ai/en/docs/sdk/errors) defines typed failures and recovery.
- [API reference](https://duale.ai/en/docs/sdk/reference) lists the supported application-facing interfaces.

## Test without the Platform

`MockSDK` provides keyed responses and an in-memory Library client for local tests:

```python
import asyncio

from dualeai.testing import MockSDK


async def main() -> None:
    mock = MockSDK()
    mock.set_mock_response("Summarize invoice", {"result": "data"})
    result = await mock.mock_ask("Summarize invoice")
    print(result)


asyncio.run(main())
```

`mock_ask()` is a keyed lookup. It does not simulate the real Task transport, streaming, Tool dispatch, or terminal
errors.

## Contributing and security

See [CONTRIBUTING.md](CONTRIBUTING.md) for the development workflow. Report suspected vulnerabilities through the
private routes in [SECURITY.md](SECURITY.md), not through a public issue.

## License

Apache-2.0.
