# Tracia Python SDK

Python SDK for Tracia — the developer tool for storing, testing, and tracing LLM prompts. Published to PyPI as `tracia`.

This is the Python counterpart to `tracia-node`. Both SDKs implement the same v1 API surface. See `~/work/tracia/sdk-parity.md` for the canonical mapping between them.

## Coding Standards

### Naming
- ALWAYS use descriptive variable/parameter names
- NEVER use single-letter names (`p`, `r`, `k`) except in trivial callbacks like list comprehensions
- Use domain terminology: `prompt`, `span`, `provider`, `api_key`

### Comments
- Do NOT add comments that just describe what's obvious from names
- Only comment on WHY, not WHAT

### Enums and Constants
- ALWAYS use enums/constants for fixed values, never hardcoded strings
- Enums defined in `tracia/_errors.py` (TraciaErrorCode) and `tracia/_types.py` (LLMProvider)

## Tech Stack

- Python 3.10+
- httpx for HTTP requests (sync + async)
- Pydantic 2.0+ for type definitions and validation
- LiteLLM for unified LLM provider abstraction
- Hatchling for building
- pytest + pytest-asyncio for testing
- mypy (strict) for type checking
- ruff for linting

## Project Structure

```
tracia-python/
├── tracia/
│   ├── __init__.py          # Public API exports (55+ types)
│   ├── _client.py           # Main Tracia class (run_local, sessions, span scheduling)
│   ├── _types.py            # All Pydantic type definitions
│   ├── _http.py             # HttpClient + AsyncHttpClient (httpx-based)
│   ├── _llm.py              # LiteLLM wrapper (provider resolution, message conversion)
│   ├── _session.py          # TraciaSession (multi-turn trace linking)
│   ├── _streaming.py        # LocalStream + AsyncLocalStream
│   ├── _errors.py           # TraciaError + TraciaErrorCode enum
│   ├── _utils.py            # ID generation, validation, variable interpolation
│   ├── _constants.py        # BASE_URL, timeouts, retry config, env var mapping
│   ├── py.typed             # PEP 561 marker
│   └── resources/
│       ├── __init__.py
│       ├── prompts.py       # Prompts resource (list, get, create, update, delete, run)
│       └── spans.py         # Spans resource (create, get, list, evaluate)
├── tests/
│   ├── test_client.py
│   ├── test_errors.py
│   ├── test_llm.py
│   ├── test_types.py
│   └── test_utils.py
└── pyproject.toml           # Build config + dependencies
```

## Key Patterns

### Dual sync/async
Every public method has both sync and async variants. Async methods use the `a` prefix:
- `run_local()` / `arun_local()`
- `prompts.list()` / `prompts.alist()`
- `spans.create()` / `spans.acreate()`
- `flush()` / `aflush()`
- `close()` / `aclose()`

### Pydantic models with camelCase aliases
All types use snake_case fields with `Field(alias="camelCase")` for API serialization:
```python
class RunLocalResult(BaseModel):
    model_config = ConfigDict(populate_by_name=True)
    span_id: str = Field(alias="spanId")
    trace_id: str = Field(alias="traceId")
```

### Background span creation
Spans are submitted to the API in the background using `ThreadPoolExecutor`. Max 1000 pending spans, 2 retry attempts with 500ms delay.

### LiteLLM integration
Provider abstraction via LiteLLM. Supports OpenAI, Anthropic, Google. LiteLLM is imported dynamically with a helpful error message if missing.

### Context manager support
```python
with Tracia(api_key="...") as client:
    result = client.run_local(...)
# Resources cleaned up automatically
```

## How to Add New Methods

1. Add the type definitions to `tracia/_types.py` (Pydantic BaseModel, snake_case fields, camelCase aliases)
2. Add the method to the appropriate resource (`resources/prompts.py` or `resources/spans.py`) or to `_client.py`
3. Create BOTH sync and async variants
4. Export new types from `tracia/__init__.py`
5. Update `~/work/tracia/sdk-parity.md` with the new mapping
6. Check `~/work/tracia/tracia-node/` to verify the Node SDK has the equivalent

## Commands

```bash
pip install -e ".[dev]"           # Install with dev dependencies
pytest                            # Run tests
pytest --cov=tracia               # Run tests with coverage
mypy tracia/                      # Type checking (strict)
ruff check tracia/                # Lint
ruff format tracia/               # Format
```
