> ## Documentation Index
> Fetch the complete documentation index at: https://docs.sirenspec.dev/llms.txt
> Use this file to discover all available pages before exploring further.

# Providers

> SirenSpec supports OpenAI, Anthropic, and Ollama as LLM backends.

## Overview

Providers are selected via the `model` field on each agent definition using a `provider:model` URI:

```yaml theme={null}
agents:
  assistant:
    model: "openai:gpt-4o-mini"
```

SirenSpec resolves the provider at runtime, instantiates the appropriate client, and calls the model's chat completions API asynchronously.

***

## OpenAI

Uses the official [OpenAI Python SDK](https://github.com/openai/openai-python).

**URI format:** `openai:<model-name>`

```yaml theme={null}
agents:
  assistant:
    model: "openai:gpt-4o-mini"
    system: "You are a helpful assistant."
```

**Authentication:**

```bash theme={null}
export OPENAI_API_KEY=sk-...
```

**Examples:**

| URI                  | Model       |
| -------------------- | ----------- |
| `openai:gpt-4o-mini` | GPT-4o mini |
| `openai:gpt-4o`      | GPT-4o      |
| `openai:gpt-4-turbo` | GPT-4 Turbo |

Any model name supported by the OpenAI chat completions API can be used.

***

## Anthropic

Uses the official [Anthropic Python SDK](https://github.com/anthropics/anthropic-sdk-python).

**URI format:** `anthropic:<model-name>`

```yaml theme={null}
agents:
  assistant:
    model: "anthropic:claude-haiku-4-5-20251001"
    system: "You are a helpful assistant."
```

**Authentication:**

```bash theme={null}
export ANTHROPIC_API_KEY=sk-ant-...
```

**Examples:**

| URI                                   | Model         |
| ------------------------------------- | ------------- |
| `anthropic:claude-haiku-4-5-20251001` | Claude Haiku  |
| `anthropic:claude-sonnet-4-6`         | Claude Sonnet |
| `anthropic:claude-opus-4-7`           | Claude Opus   |

<Note>
  The Anthropic provider extracts any `system`-role message and passes it as the top-level `system` parameter, as required by the Anthropic Messages API. `max_tokens` defaults to `4096` but is overridden per node by `max_tokens_per_call`.
</Note>

***

## Ollama

Uses Ollama's OpenAI-compatible API via the OpenAI Python SDK.

**URI format:** `ollama:<model-name>`

```yaml theme={null}
agents:
  assistant:
    model: "ollama:llama3.2"
    system: "You are a helpful assistant."
```

**Configuration:**

| Environment variable | Default                     | Description                                                                  |
| -------------------- | --------------------------- | ---------------------------------------------------------------------------- |
| `OLLAMA_BASE_URL`    | `http://localhost:11434/v1` | Ollama server URL.                                                           |
| `OLLAMA_API_KEY`     | `ollama`                    | API key (for auth-protected deployments; most local setups don't need this). |

Start Ollama locally:

```bash theme={null}
ollama serve
ollama pull llama3.2
```

Then run your workflow:

```bash theme={null}
sirenspec run workflow.yaml
```

<Note>
  Ollama must be running and the model must be pulled locally before running a workflow that references it.
</Note>

***

## Mixing Providers

You can use different providers in a single workflow. Each agent independently resolves its own provider:

```yaml theme={null}
agents:
  classifier:
    model: "openai:gpt-4o-mini"
    system: "Classify the user's intent as 'question' or 'complaint'."

  responder:
    model: "anthropic:claude-haiku-4-5-20251001"
    system: "You are a customer support agent. Compose a helpful reply."

nodes:
  classify:
    agent: classifier
    writes: working.intent

  reply:
    agent: responder
    writes: output.reply

edges:
  - from: classify
    to: reply
```

This workflow calls OpenAI for classification and Anthropic for the final response.

***

## Error Handling

Provider URIs are validated when the workflow is executed. An invalid URI raises `sirenspec.exceptions.ProviderError` (a subclass of `SirenSpecError`):

```python theme={null}
import asyncio
from sirenspec import execute
from sirenspec.exceptions import ProviderError

try:
    asyncio.run(execute(workflow, user_input="hello"))
except ProviderError as exc:
    print(exc)  # e.g. "Unknown provider 'vertex'; supported: ['anthropic', 'ollama', 'openai']"
```

Common reasons a `ProviderError` is raised:

| Cause                   | Example URI         | Error message                                                    |
| ----------------------- | ------------------- | ---------------------------------------------------------------- |
| Missing colon separator | `openai-gpt4o`      | `Malformed provider URI '...'; expected 'provider:model' format` |
| Empty model name        | `openai:`           | `Malformed provider URI '...'; expected 'provider:model' format` |
| Unknown provider        | `vertex:gemini-pro` | `Unknown provider 'vertex'; supported: [...]`                    |

***

## Token Tracking

Each provider records prompt and completion token counts from every call. This usage data is surfaced in the execution trace under the `usage` field:

```json theme={null}
{
  "nodes": [
    {
      "id": "answer",
      "type": "agent",
      "tokens": 42,
      "usage": {
        "prompt_tokens": 30,
        "completion_tokens": 12,
        "estimated_usd": null
      },
      ...
    }
  ],
  "summary": {
    "total_tokens": 42,
    "total_usage": {
      "prompt_tokens": 30,
      "completion_tokens": 12,
      "total_tokens": 42,
      "estimated_usd": null
    },
    ...
  }
}
```

The `tokens` field is the sum of prompt and completion tokens; the `usage` field provides the detailed breakdown. `estimated_usd` is computed from a bundled LiteLLM pricing snapshot — it is `null` for models without a pricing entry (Ollama and other local backends), so the examples above show `null`. See [Budget & Cost Control](/budget#cost-estimation) for details.
