Metadata-Version: 2.4
Name: unitcause
Version: 0.3.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/Hemanth-stack/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.async_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
```

## Enforcement & Callbacks

### 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("...")
```

### Kill Callback

React when UnitCause kills a session (budget, loop detection, or manual kill):

```python
def on_kill(session, reason):
    print(f"Session killed: {reason}")
    # Clean up resources, save state, etc.

with uc.session(
    agent_name="agent",
    budget=5.00,
    on_kill=on_kill,
) as session:
    agent.run("...")
```

### Enforcement Actions

The SDK handles enforcement automatically:

- **warn** — Logged, `on_budget_warning` fired
- **throttle** — SDK auto-sleeps for the configured delay
- **kill** — `on_kill` fired, then raises `BudgetExceededError`, `LoopDetectedError`, or `SessionKilledError`

## Framework Integrations

### LangChain

```python
from langchain_openai import ChatOpenAI
from unitcause import UnitCause
from unitcause.integrations.langchain import UnitCauseCallbackHandler

uc = UnitCause()

with uc.session(agent_name="my-chain", budget=5.00) as session:
    handler = UnitCauseCallbackHandler(session)
    llm = ChatOpenAI(model="gpt-4o", callbacks=[handler])
    result = llm.invoke("Hello!")
```

### CrewAI

```python
from crewai import Agent, Task, Crew
from unitcause import UnitCause
from unitcause.integrations.crewai import unitcause_step_callback

uc = UnitCause()

with uc.session(agent_name="my-crew", budget=10.00) as session:
    callback = unitcause_step_callback(session)
    agent = Agent(role="Researcher", step_callback=callback, ...)
    crew = Crew(agents=[agent], tasks=[...])
    crew.kickoff()
```

### AutoGen

```python
from autogen import AssistantAgent, UserProxyAgent
from unitcause import UnitCause
from unitcause.integrations.autogen import UnitCauseHook

uc = UnitCause()

with uc.session(agent_name="autogen-chat", budget=5.00) as session:
    hook = UnitCauseHook(session)
    assistant = AssistantAgent("assistant", llm_config={...})
    hook.attach(assistant)
    
    user = UserProxyAgent("user")
    user.initiate_chat(assistant, message="Hello!")
```

### Custom Pricing

Override or add model pricing for cost estimation:

```python
from unitcause.integrations.pricing import register_pricing

register_pricing("my-fine-tuned-model", input_per_1k=0.01, output_per_1k=0.03)
```

## Health Check

```python
status = uc.health_check()
print(status)
# {'status': 'connected', 'latency_ms': 42, 'version': '0.2.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 import (
    BudgetExceededError,
    LoopDetectedError,
    SessionKilledError,
)

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")
except SessionKilledError as e:
    print(f"Session killed: {e.reason}")
```

## What's New in 0.2.0

- **Enforcement engine** — configurable warn / throttle / kill actions per policy
- **Loop detection** — dual strategy (exact hash match + pattern cycle detection)
- **on_kill callback** — react when sessions are killed
- **Throttle handling** — SDK auto-sleeps on throttle responses
- **Framework integrations** — LangChain, CrewAI, AutoGen adapters
- **Built-in cost estimation** — 60+ models with customizable pricing
- **SessionKilledError** — new exception for killed sessions

## Documentation

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

## License

MIT — see [LICENSE](LICENSE)
