Metadata-Version: 2.4
Name: baponi
Version: 0.1.0
Summary: Sandboxed code execution for AI agents
Project-URL: Homepage, https://baponi.ai
Project-URL: Documentation, https://docs.baponi.ai
Project-URL: Repository, https://github.com/baponi/baponi-python
Author-email: Baponi <hello@baponi.ai>
License-Expression: Apache-2.0
License-File: LICENSE
Keywords: agents,ai,code-execution,llm,sandbox
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: Apache Software License
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: Typing :: Typed
Requires-Python: >=3.10
Requires-Dist: httpx[http2]>=0.25.0
Requires-Dist: pydantic>=2.0
Provides-Extra: all
Requires-Dist: anthropic>=0.40; extra == 'all'
Requires-Dist: crewai>=0.80; extra == 'all'
Requires-Dist: google-genai>=1.0; extra == 'all'
Requires-Dist: langchain-core>=0.3; extra == 'all'
Requires-Dist: openai-agents>=0.9; extra == 'all'
Provides-Extra: anthropic
Requires-Dist: anthropic>=0.40; extra == 'anthropic'
Provides-Extra: crewai
Requires-Dist: crewai>=0.80; extra == 'crewai'
Provides-Extra: deepagents
Requires-Dist: deepagents>=0.2.4; extra == 'deepagents'
Provides-Extra: dev
Requires-Dist: deepagents>=0.2.4; extra == 'dev'
Requires-Dist: mypy>=1.8; extra == 'dev'
Requires-Dist: pytest-asyncio>=0.23; extra == 'dev'
Requires-Dist: pytest-httpx>=0.30; extra == 'dev'
Requires-Dist: pytest>=8.0; extra == 'dev'
Requires-Dist: respx>=0.21; extra == 'dev'
Provides-Extra: google
Requires-Dist: google-genai>=1.0; extra == 'google'
Provides-Extra: langchain
Requires-Dist: langchain-core>=0.3; extra == 'langchain'
Provides-Extra: openai
Requires-Dist: openai-agents>=0.9; extra == 'openai'
Description-Content-Type: text/markdown

# Baponi Python SDK

Sandboxed code execution for AI agents. Run Bash, Python, and Node.js in secure, isolated containers with sub-20ms overhead.

## Installation

```bash
pip install baponi
```

With framework integrations:

```bash
pip install baponi[langchain]     # LangChain
pip install baponi[openai]        # OpenAI Agents SDK
pip install baponi[anthropic]     # Anthropic
pip install baponi[google]        # Google Gemini
pip install baponi[crewai]        # CrewAI
pip install baponi[all]           # All frameworks
```

## Quick Start

```python
from baponi import Baponi

client = Baponi()  # reads BAPONI_API_KEY from env
result = client.execute("print('Hello!')")
print(result.stdout)  # Hello!
```

## Async

```python
from baponi import AsyncBaponi

async with AsyncBaponi() as client:
    result = await client.execute("print('Hello!')")
    print(result.stdout)
```

## Supported Languages

```python
client.execute("echo 'Bash'", language="bash")
client.execute("print('Python')")
client.execute("console.log('Node')", language="node")
```

## Persistent State

Pass a `thread_id` to persist files and installed packages across calls:

```python
client.execute("pip install pandas", language="bash", thread_id="analysis-session")
client.execute("""
import pandas as pd
df = pd.DataFrame({'x': [1, 2, 3]})
df.to_csv('/home/baponi/data.csv', index=False)
print(df.describe())
""", thread_id="analysis-session")
```

## Framework Integrations

### LangChain

```python
from baponi.langchain import code_sandbox
from langchain.agents import create_react_agent

agent = create_react_agent(llm, tools=[code_sandbox])
```

### OpenAI Agents SDK

```python
from baponi.openai import code_sandbox
from agents import Agent

agent = Agent(name="coder", tools=[code_sandbox])
```

### Anthropic

```python
from baponi.anthropic import code_sandbox_tool, handle_tool_call
import anthropic

client = anthropic.Anthropic()
response = client.messages.create(
    model="claude-sonnet-4-20250514",
    tools=[code_sandbox_tool],
    messages=[{"role": "user", "content": "Calculate fibonacci(10) in Python"}],
)

for block in response.content:
    if block.type == "tool_use":
        result = handle_tool_call(block.name, block.input)
        print(result)
```

### Google Gemini

```python
from baponi.google import code_sandbox
from google import genai

client = genai.Client()
chat = client.chats.create(
    model="gemini-2.5-flash",
    config={"tools": [code_sandbox]},
)
response = chat.send_message("Calculate pi to 100 digits")
```

### CrewAI

```python
from baponi.crewai import code_sandbox
from crewai import Agent

agent = Agent(role="Data Analyst", tools=[code_sandbox])
```

### Custom Configuration

All integrations support `create_code_sandbox()` for power users:

```python
from baponi.langchain import create_code_sandbox

sandbox = create_code_sandbox(
    api_key="sk-...",
    base_url="https://your-baponi-instance.com",
    thread_id="shared-session",        # Default thread for all calls
    timeout=120,                        # Default timeout
    metadata={"user_id": "usr_123"},   # Metadata on every call
)
```

## Error Handling

API errors and execution errors are separate concepts:

```python
from baponi import Baponi, AuthenticationError, RateLimitError

client = Baponi()

# API errors raise exceptions
try:
    result = client.execute("print(1)")
except AuthenticationError:
    print("Invalid API key")
except RateLimitError as e:
    print(f"Rate limited, retry after {e.retry_after}s")

# Execution errors return SandboxResult with success=False
result = client.execute("raise ValueError('oops')")
if not result.success:
    print(f"Code failed with exit code {result.exit_code}")
    print(f"stderr: {result.stderr}")
```

### Exception Hierarchy

| Exception | HTTP Status | Description |
|-----------|------------|-------------|
| `BaponiError` | — | Base exception for all API errors |
| `AuthenticationError` | 401 | Invalid or missing API key |
| `ForbiddenError` | 403 | Insufficient permissions |
| `RateLimitError` | 429 | Rate limit exceeded |
| `ThreadBusyError` | 409 | Thread already executing |
| `APITimeoutError` | 504 | Server-side timeout |
| `ServerError` | 500/503 | Server error |
| `APIValidationError` | 400 | Invalid request |

## Configuration

```python
from baponi import Baponi

# Self-hosted deployment
client = Baponi(
    api_key="sk-...",
    base_url="https://baponi.internal.company.com",
)

# Custom HTTP client (proxies, observability, custom TLS)
import httpx

http_client = httpx.Client(
    proxies="http://proxy.internal:8080",
    verify="/path/to/ca-bundle.crt",
)
client = Baponi(api_key="sk-...", http_client=http_client)

# Retry configuration
client = Baponi(
    api_key="sk-...",
    max_retries=0,    # Disable retries
    timeout=120.0,    # Connection timeout (not execution timeout)
)
```

## SandboxResult

```python
result = client.execute("print('hi')")

result.success              # bool — True if exit_code == 0
result.stdout               # str — standard output
result.stderr               # str — standard error
result.exit_code            # int — process exit code
result.network_egress_bytes # int — bytes sent to network
result.storage_egress_bytes # int — bytes written to storage
result.error                # str | None — error message if failed
result.model_dump()         # dict — Pydantic serialization
```

## Coming Soon

- **Files API** — upload/download files to sandbox threads (`/v1/files/*`)
- **Web Tools API** — web search and fetch from within sandboxes (`/v1/web/search`, `/v1/web/fetch`)
