# Mandatum Python SDK

Python SDK for Mandatum - prompt management with code-level customization.

## Installation

```bash
pip install mandatum

# Or install with specific provider support
pip install mandatum[openai]
pip install mandatum[anthropic]
pip install mandatum[all]  # All providers
```

## Quick Start

### OpenAI Integration

```python
from mandatum import Mandatum

# Initialize Mandatum client
mandatum = Mandatum(api_key="md_xxxxx")

# Get wrapped OpenAI client
OpenAI = mandatum.openai.OpenAI
client = OpenAI()

# Use exactly like normal OpenAI SDK - automatically logged!
response = client.chat.completions.create(
    model="gpt-4",
    messages=[{"role": "user", "content": "Hello, world!"}],

    # Optional: Mandatum-specific metadata
    md_tags=["production", "feature-x"],
    md_environment="production",
    md_metadata={"user_id": "123"}
)

print(response.choices[0].message.content)
```

### Anthropic Integration

```python
from mandatum import Mandatum

mandatum = Mandatum(api_key="md_xxxxx")

# Get wrapped Anthropic client
Anthropic = mandatum.anthropic.Anthropic
client = Anthropic()

# Use normally - automatically logged!
response = client.messages.create(
    model="claude-3-sonnet-20240229",
    max_tokens=1024,
    messages=[{"role": "user", "content": "Hello, Claude!"}],

    # Mandatum metadata
    md_tags=["test"],
    md_environment="development"
)

print(response.content[0].text)
```

### Async Support

```python
import asyncio
from mandatum import Mandatum

mandatum = Mandatum(api_key="md_xxxxx")

# Get wrapped async client
AsyncOpenAI = mandatum.openai.AsyncOpenAI
client = AsyncOpenAI()

async def main():
    response = await client.chat.completions.create(
        model="gpt-3.5-turbo",
        messages=[{"role": "user", "content": "Hello!"}]
    )
    print(response.choices[0].message.content)

asyncio.run(main())
```

## Configuration

### Environment Variables

```bash
# Required
export MANDATUM_API_KEY="md_xxxxx"

# Optional
export MANDATUM_BASE_URL="https://api.mandatum.io/api/v1"  # Default: http://localhost:8000/api/v1
```

### Initialization Options

```python
mandatum = Mandatum(
    api_key="md_xxxxx",                    # API key (or set MANDATUM_API_KEY)
    base_url="http://localhost:8000/api/v1",  # Mandatum API URL
    organization_id="org_xxxxx",            # Optional organization ID
    async_logging=True,                     # Log asynchronously (default: True)
    debug=False                             # Enable debug logging (default: False)
)
```

## Metadata Parameters

Add Mandatum-specific metadata to any LLM call:

- `md_tags`: List of tags for filtering/grouping (e.g., `["production", "feature-x"]`)
- `md_environment`: Environment name (e.g., `"production"`, `"staging"`, `"development"`)
- `md_metadata`: Custom JSON metadata (e.g., `{"user_id": "123", "session_id": "abc"}`)
- `md_prompt_id`: UUID of associated prompt in Mandatum
- `md_version_tag`: Version tag for the prompt
- `md_parent_request_id`: UUID of parent request (for chained calls)

## Features

- **Transparent Wrapping**: Use OpenAI/Anthropic SDKs normally with zero code changes
- **Automatic Logging**: All requests logged to Mandatum automatically
- **Cost Tracking**: Automatic cost calculation per request
- **Latency Monitoring**: Track request duration for every call
- **Token Counting**: Input/output token counts captured
- **Error Logging**: Failed requests logged with error details
- **Async Support**: Full async/await support for both OpenAI and Anthropic
- **Background Logging**: Non-blocking async logging doesn't slow down LLM calls

## How It Works

1. Mandatum wraps the native OpenAI/Anthropic SDK clients
2. When you call `.create()`, the wrapper intercepts the request
3. The actual LLM API call is made (locally, nothing sent to Mandatum yet)
4. Response metadata (tokens, latency, cost) is captured
5. Request data is sent to Mandatum API in a background thread (non-blocking)
6. Original response is returned to your code immediately

Your API keys for OpenAI/Anthropic are never sent to Mandatum - all LLM requests are made directly from your machine.

## Examples

### With Prompt Management

```python
from mandatum import Mandatum
import uuid

mandatum = Mandatum(api_key="md_xxxxx")
OpenAI = mandatum.openai.OpenAI
client = OpenAI()

# Link request to a Mandatum prompt
response = client.chat.completions.create(
    model="gpt-4",
    messages=[{"role": "user", "content": "Translate to French: Hello"}],
    md_prompt_id=str(uuid.UUID("prompt-uuid-here")),
    md_version_tag="v1.2.0"
)
```

### With Request Chains

```python
# First request
response1 = client.chat.completions.create(
    model="gpt-4",
    messages=[{"role": "user", "content": "What is Python?"}]
)

# Get request ID from Mandatum dashboard or API
parent_request_id = "request-uuid-from-first-call"

# Chained request
response2 = client.chat.completions.create(
    model="gpt-4",
    messages=[{"role": "user", "content": "Give me a code example"}],
    md_parent_request_id=parent_request_id,
    md_tags=["chained-request"]
)
```

## Development

### Local Installation

```bash
cd sdk/python
pip install -e .
```

### Testing

```bash
# Set up environment
export MANDATUM_API_KEY="your_test_key"
export OPENAI_API_KEY="your_openai_key"

# Run test script
python test_sdk.py
```

## Support

- **Documentation**: https://docs.mandatum.io
- **Issues**: https://github.com/mandatum/mandatum/issues
- **Email**: support@mandatum.io

## License

MIT License - see LICENSE file for details.
