# Boring-OS Python SDK

Python SDK for the Boring-OS tool orchestration platform. Provides native access to tools and seamless integration with popular AI agent frameworks.

## Installation

```bash
# Core SDK
pip install boring-os

# With framework adapters
pip install boring-os[langchain]   # LangChain support
pip install boring-os[adk]         # Google ADK support
pip install boring-os[bedrock]     # AWS Bedrock support
pip install boring-os[openai]      # OpenAI support
pip install boring-os[all]         # All adapters
```

## Quick Start

```python
from boring_os import BoringOS

# Initialize client
client = BoringOS(
    base_url="http://localhost:8080",
    api_key="bos_dev_key_for_local_testing"
)

# Call a tool synchronously (recommended for agents)
result = client.tools.call("get_customer_profile", customer_id="cust_123")
print(result)
# {'customer_id': 'cust_123', 'name': 'John Smith', ...}

# List available tools
tools = client.tools.list()
for tool in tools.tools:
    print(f"{tool.name}: {tool.description}")
```

## Async Usage

```python
import asyncio
from boring_os import BoringOS

async def main():
    async with BoringOS(api_key="...") as client:
        # Concurrent tool calls
        results = await asyncio.gather(
            client.tools.call_async("get_customer_profile", customer_id="123"),
            client.tools.call_async("get_billing_details", customer_id="123"),
        )
        print(results)

asyncio.run(main())
```

## Framework Integrations

### LangChain

```python
from boring_os import BoringOS
from boring_os.adapters.langchain import get_langchain_tools

client = BoringOS(api_key="...")
tools = get_langchain_tools(client)

# Use with LangChain agent
from langchain.agents import create_tool_calling_agent
agent = create_tool_calling_agent(llm, tools, prompt)
```

### Google ADK

```python
from boring_os import BoringOS
from boring_os.adapters.adk import BoringOSToolProvider

client = BoringOS(api_key="...")
provider = BoringOSToolProvider(client)

# Use with ADK agent
from google.adk import Agent
agent = Agent(tools=provider.get_tools())
```

### AWS Bedrock

```python
from boring_os import BoringOS
from boring_os.adapters.bedrock import create_action_group_schema, bedrock_handler

client = BoringOS(api_key="...")

# Generate OpenAPI schema for action group
schema = create_action_group_schema(client, tools=["get_customer_profile"])

# Or use Lambda handler decorator
@bedrock_handler(client)
def lambda_handler(event, context):
    pass  # Handled automatically
```

### OpenAI

```python
from boring_os import BoringOS
from boring_os.adapters.openai import get_openai_tools, execute_tool_call

client = BoringOS(api_key="...")
tools = get_openai_tools(client)

# Use with OpenAI
response = openai.chat.completions.create(
    model="gpt-4",
    messages=messages,
    tools=tools
)

# Execute tool calls from response
if response.choices[0].message.tool_calls:
    for tool_call in response.choices[0].message.tool_calls:
        result = execute_tool_call(client, tool_call)
```

## Observability with Opik

The SDK integrates with [Opik](https://github.com/comet-ml/opik) for comprehensive tracing of all tool calls.

### Installation

```bash
pip install boring-os[opik]
```

### Basic Tracing

```python
from boring_os import BoringOS

# Enable Opik tracing
client = BoringOS(
    api_key="bos_dev_key_for_local_testing",
    enable_tracing=True,
    opik_project="my-agent",
    opik_use_local=True,  # Use local Opik server
)

# All tool calls are now traced
result = client.tools.call("get_customer_profile", customer_id="123")
# -> Creates span in Opik dashboard at http://localhost:5173
```

### With Comet.com Cloud

```python
from boring_os import BoringOS

# Use Comet cloud for production
client = BoringOS(
    api_key="...",
    enable_tracing=True,
    opik_use_local=False,
    opik_api_key="your-comet-api-key",
    opik_workspace="your-workspace",
)
```

### Manual Span Control

```python
from boring_os import BoringOS
import opik

client = BoringOS(api_key="...", enable_tracing=True)

# Create parent trace for agent workflow
@opik.track(name="agent_workflow")
def handle_request(user_query: str):
    # Child spans auto-nested under parent
    profile = client.tools.call("get_customer_profile", customer_id="123")
    billing = client.tools.call("get_billing_details", customer_id="123")
    return {"profile": profile, "billing": billing}
```

### Running Local Opik Server

```bash
# Clone and run Opik
git clone https://github.com/comet-ml/opik.git
cd opik
./opik.sh

# Dashboard available at http://localhost:5173
```

## Error Handling

```python
from boring_os import BoringOS
from boring_os.exceptions import (
    ToolNotFoundError,
    ToolTimeoutError,
    ValidationError,
    RateLimitError,
)

client = BoringOS(api_key="...")

try:
    result = client.tools.call("unknown_tool")
except ToolNotFoundError as e:
    print(f"Tool not found: {e.tool_name}")
except ToolTimeoutError as e:
    print(f"Timed out after {e.timeout_seconds}s")
except ValidationError as e:
    print(f"Invalid params: {e.details}")
except RateLimitError as e:
    print(f"Rate limited, retry after {e.retry_after}s")
```

## License

MIT
