Metadata-Version: 2.4
Name: unitcause
Version: 0.1.0
Summary: Python SDK for UnitCause — real-time cost governance for AI agents
Project-URL: Homepage, https://unitcause.com
Project-URL: Documentation, https://unitcause.com/docs
Project-URL: Repository, https://github.com/unitcause/unitcause-python
Project-URL: Changelog, https://unitcause.com/changelog
Author-email: UnitCause <support@unitcause.com>
License-Expression: MIT
License-File: LICENSE
Keywords: agents,ai,budget,cost,governance,llm,monitoring,observability
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: Programming Language :: Python :: 3.13
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Classifier: Typing :: Typed
Requires-Python: >=3.9
Requires-Dist: httpx>=0.24.0
Requires-Dist: pydantic>=2.0.0
Provides-Extra: all
Requires-Dist: crewai>=0.1.0; extra == 'all'
Requires-Dist: langchain-core>=0.1.0; extra == 'all'
Requires-Dist: pyautogen>=0.2.0; extra == 'all'
Provides-Extra: autogen
Requires-Dist: pyautogen>=0.2.0; extra == 'autogen'
Provides-Extra: crewai
Requires-Dist: crewai>=0.1.0; extra == 'crewai'
Provides-Extra: dev
Requires-Dist: mypy>=1.0; extra == 'dev'
Requires-Dist: pytest-asyncio>=0.21; extra == 'dev'
Requires-Dist: pytest-httpx>=0.30; extra == 'dev'
Requires-Dist: pytest>=7.0; extra == 'dev'
Requires-Dist: ruff>=0.1.0; extra == 'dev'
Provides-Extra: langchain
Requires-Dist: langchain-core>=0.1.0; extra == 'langchain'
Description-Content-Type: text/markdown

# UnitCause Python SDK

**Real-time cost governance for AI agents.** Set budgets. Stop loops. Ship confidently.

[![PyPI version](https://badge.fury.io/py/unitcause.svg)](https://pypi.org/project/unitcause/)
[![Python 3.9+](https://img.shields.io/badge/python-3.9+-blue.svg)](https://www.python.org/downloads/)
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)

## Installation

```bash
pip install unitcause
```

With framework integrations:

```bash
pip install unitcause[langchain]
pip install unitcause[crewai]
pip install unitcause[autogen]
pip install unitcause[all]
```

## Quick Start

```python
import os
from unitcause import UnitCause

# Set your API key
os.environ["UNITCAUSE_API_KEY"] = "uc_live_..."

uc = UnitCause()

# Wrap your agent run in a session
with uc.session(agent_name="data-analyst", budget=5.00) as session:
    # Run your agent — UnitCause tracks every LLM call
    result = agent.run("Analyze Q4 revenue trends")
    
    print(f"Total cost: ${session.total_cost:.4f}")
    print(f"Tokens used: {session.total_tokens:,}")
    print(f"LLM calls: {session.call_count}")
```

## Async Support

```python
import asyncio
from unitcause import UnitCause

uc = UnitCause()

async def run_agent():
    async with uc.session(agent_name="researcher", budget=2.00) as session:
        result = await agent.arun("Find top ML papers from 2025")
        print(f"Cost: ${session.total_cost:.4f}")

asyncio.run(run_agent())
```

## Manual Step Tracking

If you're not using auto-instrumentation, you can manually report each LLM call:

```python
with uc.session(agent_name="my-agent", budget=1.00) as session:
    # After each LLM call, report it
    response = session.report_step(
        action="llm_call",
        model="gpt-4o",
        tokens_in=500,
        tokens_out=200,
        cost_usd=0.0035,
    )
    
    # The response tells you whether to continue
    if response.action == "kill":
        print(f"Session killed: {response.reason}")
        break
```

## Budget Callbacks

```python
def on_warning(session, utilization):
    print(f"⚠️ Budget {utilization:.0%} used")

def on_exceeded(session, cost):
    print(f"🛑 Budget exceeded: ${cost:.4f}")
    return False  # Return True to allow continuing

with uc.session(
    agent_name="analyst",
    budget=5.00,
    on_budget_warning=on_warning,
    on_budget_exceeded=on_exceeded,
) as session:
    result = agent.run("...")
```

## Health Check

```python
status = uc.health_check()
print(status)
# {'status': 'connected', 'latency_ms': 42, 'version': '0.1.0'}
```

## Configuration

### Environment Variables

| Variable | Default | Description |
|----------|---------|-------------|
| `UNITCAUSE_API_KEY` | — | Your API key |
| `UNITCAUSE_BASE_URL` | `https://api.unitcause.com` | API endpoint |
| `UNITCAUSE_ENVIRONMENT` | `production` | Environment label |
| `UNITCAUSE_DISABLED` | `false` | Disable all tracking |
| `UNITCAUSE_LOG_LEVEL` | `WARNING` | Logging level |
| `UNITCAUSE_DEFAULT_BUDGET` | — | Default session budget (USD) |

### Programmatic Configuration

```python
from unitcause import UnitCause, Config

uc = UnitCause(
    api_key="uc_live_...",
    base_url="https://api.unitcause.com",
    environment="staging",
    disabled=False,
    log_level="DEBUG",
    retry_config={
        "max_retries": 3,
        "backoff_factor": 0.5,
    },
)
```

## Exception Handling

```python
from unitcause.exceptions import (
    BudgetExceededError,
    LoopDetectedError,
    AuthenticationError,
)

try:
    with uc.session(agent_name="agent", budget=1.00) as session:
        agent.run("...")
except BudgetExceededError as e:
    print(f"Budget exceeded: ${e.actual_cost:.4f} / ${e.budget_limit:.4f}")
except LoopDetectedError as e:
    print(f"Loop detected: {e.iteration_count} iterations")
```

## Documentation

Full documentation at [unitcause.com/docs](https://unitcause.com/docs)

## License

MIT — see [LICENSE](LICENSE)
