Metadata-Version: 2.4
Name: dobby-ai-sdk
Version: 0.4.0
Summary: Official Python SDK for the Dobby AI Platform — Home for your AI agents
Project-URL: Homepage, https://dobby-ai.com
Project-URL: Documentation, https://dobby-ai.com/docs/sdk
Project-URL: Repository, https://github.com/gil-dobby/dobby-sdk-python
Author-email: Dobby AI <dev@dobby-ai.com>
License-Expression: MIT
License-File: LICENSE
Keywords: a2a,agents,ai,dobby,gateway,llm,mcp
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.9
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Requires-Python: >=3.9
Requires-Dist: httpx>=0.25.0
Requires-Dist: openai>=1.0.0
Requires-Dist: pydantic>=2.0.0
Provides-Extra: dev
Requires-Dist: mypy>=1.8.0; extra == 'dev'
Requires-Dist: pytest-asyncio>=0.23.0; extra == 'dev'
Requires-Dist: pytest-httpx>=0.30.0; extra == 'dev'
Requires-Dist: pytest>=7.0.0; extra == 'dev'
Requires-Dist: ruff>=0.3.0; extra == 'dev'
Description-Content-Type: text/markdown

# Dobby SDK for Python

Official Python SDK for the [Dobby AI Platform](https://dobby-ai.com) — Home for your AI agents.

## Installation

```bash
pip install dobby-ai-sdk
```

## Quick Start

```python
from dobby_sdk import DobbyClient

client = DobbyClient(api_key="gk_user_...")

# LLM calls (OpenAI-compatible)
response = client.chat.completions.create(
    model="claude-sonnet-4-20250514",
    messages=[{"role": "user", "content": "Hello from Dobby!"}],
)
print(response.choices[0].message.content)

# Streaming
stream = client.chat.completions.create(
    model="gpt-4o-mini",
    messages=[{"role": "user", "content": "Explain AI agents"}],
    stream=True,
)
for chunk in stream:
    print(chunk.choices[0].delta.content or "", end="")
```

## Control Points — inline policy enforcement

Ask Dobby for a policy verdict **before** your agent runs a risky action, and let
your own code decide what to do. Unlike a gateway, Dobby returns the decision —
*your code is the enforcement point* (the classic PDP/PEP split; same family as
OPA / Cerbos) — and you **report back what you did**: `honored=True` if you stopped,
`honored=False` if you overrode the deny and proceeded anyway. That honored-vs-
overridden split is the auditable **adherence** signal — rolled up across your
Org → Tenant → Process layers — that a gateway cannot produce.

```python
from dobby_sdk import DobbyClient, DobbyPolicyDenied

client = DobbyClient(api_key="sk_live_...", org_id="org_...", tenant_id="tenant_...")

try:
    # enforce() raises DobbyPolicyDenied if the policy blocks the action.
    client.controls.enforce("send_external_email", arguments={"to": addr})
    send_external_email(addr)              # runs only if the policy allowed it

except DobbyPolicyDenied as denied:
    # Your code halts the action — then report the honored stop.
    client.controls.report(
        denied.verdict.decision_id, honored=True, action_taken="stopped"
    )
```

Prefer to branch yourself instead of catching? Use `check()`, which returns a
`Verdict` and never raises on a deny:

```python
verdict = client.controls.check("run_command", arguments={"cmd": cmd})
# verdict.allowed (bool) | verdict.reason | verdict.action | verdict.decision_id

if verdict.allowed:
    run_command(cmd)                       # allowed -> proceed
else:
    # The policy said STOP. Your code decides what happens — and reports it honestly:
    #   A) honor the deny (recommended): stop, report honored=True
    client.controls.report(verdict.decision_id, honored=True, action_taken="stopped")
    #   B) override the deny (your call): proceed anyway, report honored=False
    #      run_command(cmd)
    #      client.controls.report(verdict.decision_id, honored=False, action_taken="proceeded")
    # honored vs overridden is exactly what the Adherence metric counts.
```

## Task Management

```python
# Create a task
task = client.tasks.create(
    title="Review PR #42",
    priority="high",
    agent_name="dobby-code-reviewer-agent",
)

# List pending tasks
tasks = client.tasks.list(status="pending")

# Approve a task (HITL)
client.approvals.approve(task["id"], comment="Looks good!")
```

## Agent Fleet

```python
# List all agents
agents = client.agents.list()

# Register an external agent
agent = client.agents.register(
    display_name="Research Agent",
    framework="crewai",
    protocol="a2a",
    endpoint_url="https://my-agent.example.com",
)

# Pause/resume
client.agents.pause("agent_abc123")
```

## Cost Tracking

```python
# Organization cost summary
costs = client.costs.summary(period="30d")

# Per-agent breakdown
agent_costs = client.costs.by_agent(period="7d")
```

## Async Support

```python
from dobby_sdk import AsyncDobbyClient

async with AsyncDobbyClient(api_key="gk_user_...") as client:
    response = await client.chat.completions.create(
        model="claude-sonnet-4-20250514",
        messages=[{"role": "user", "content": "Hello async!"}],
    )
```

## Configuration

```python
client = DobbyClient(
    api_key="gk_user_...",         # or DOBBY_API_KEY env var
    base_url="https://dobby-ai.com",  # or DOBBY_BASE_URL
    org_id="org_...",              # or DOBBY_ORG_ID
    tenant_id="tenant_...",        # or DOBBY_TENANT_ID
    timeout=120.0,
    max_retries=2,
)
```

## Error Handling

```python
from dobby_sdk import DobbyAuthError, DobbyRateLimitError, DobbyBudgetExceededError

try:
    response = client.chat.completions.create(...)
except DobbyAuthError:
    print("Invalid or expired API key")
except DobbyRateLimitError as e:
    print(f"Rate limited. Retry after: {e.retry_after}s")
except DobbyBudgetExceededError:
    print("Organization budget limit reached")
```
