Adapters
Adapters are the bridge between your LLM provider's native SDK and BeliefState's universal tracking pipeline. Each adapter implements the ProviderAdapter protocol, handling two critical jobs:
- Payload normalization: Converting provider-specific request/response formats into BeliefState's universal
LLMCallandLLMResponsemodels. - Internal LLM operations: Executing belief extraction prompts, generating vector embeddings for semantic search, and running NLI judge checks.
All adapters include built-in production features:
OpenAI
GPT-4o, GPT-4, GPT-3.5
Anthropic
Claude 3.5, Claude 3
Gemini
Gemini 2.0, 1.5
Ollama
Llama 3, Mistral, etc.
LiteLLM
100+ providers
Choosing an Adapter
| Adapter | Best For | Embeddings | Cost |
|---|---|---|---|
| OpenAI | Best overall extraction quality, widest model selection | ✅ Native (text-embedding-3-small) | Pay per token |
| Anthropic | Long context conversations, strong reasoning | ❌ No embedding API — use Dual-Adapter | Pay per token |
| Gemini | Large context windows, Google Cloud integration | ✅ Native (text-embedding-004) | Pay per token / Free tier |
| Ollama | Local/private deployment, zero cost, offline use | ✅ Native (nomic-embed-text) | Free (local GPU) |
| LiteLLM | Multi-provider routing, Azure/Bedrock/Cohere access | ✅ Via LiteLLM routing | Varies by provider |
OpenAI
The OpenAI adapter supports all GPT chat completion models and OpenAI's embedding models. It uses the official openai Python SDK with async client support. The API key is read from the OPENAI_API_KEY environment variable, or can be passed via a pre-configured client instance.
Constructor Parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
client | Optional[AsyncOpenAI] | None | Pre-configured openai.AsyncOpenAI client instance. If provided, the adapter uses this client directly instead of creating one from environment variables. Useful for custom base URLs (Azure OpenAI), proxy configs, or testing with mocks. |
model | str | "gpt-4o-mini" | Chat completion model used for belief extraction and NLI judge calls. Examples: "gpt-4o", "gpt-4o-mini", "gpt-3.5-turbo". |
embed_model | str | "text-embedding-3-small" | Embedding model used for generating belief vectors (used in semantic similarity search and contradiction detection). Options: "text-embedding-3-small" (1536 dims, cheapest), "text-embedding-3-large" (3072 dims, best quality). |
embed_kwargs | Optional[dict] | {} | Additional keyword arguments forwarded to the embedding API call. Example: {"dimensions": 256} to use OpenAI's dimension reduction feature for smaller, faster vectors. |
timeout | float | 30.0 | Maximum time (in seconds) to wait for a single API call before raising a timeout error. Applies to both chat completions and embedding calls. |
retry_config | Optional[RetryConfig] | RetryConfig() | Retry strategy for transient API errors. See RetryConfig for details. |
pythonfrom beliefstate.adapters import OpenAIAdapter
adapter = OpenAIAdapter(
model="gpt-4o", # Chat completion model
embed_model="text-embedding-3-small", # Embedding model
timeout=30.0, # Request timeout (seconds)
)
# Health check — verify API key and connectivity
is_healthy = await adapter.health_check()
💡 Environment Variable: Set OPENAI_API_KEY in your environment, or pass a pre-configured client: OpenAIAdapter(client=AsyncOpenAI(api_key="sk-..."))
Anthropic (Claude)
The Anthropic adapter supports all Claude models. It uses the official anthropic Python SDK. The API key is read from the ANTHROPIC_API_KEY environment variable.
Constructor Parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
client | Optional[AsyncAnthropic] | None | Pre-configured anthropic.AsyncAnthropic client instance. If None, a client is created from the ANTHROPIC_API_KEY environment variable. |
model | str | "claude-3-5-sonnet-latest" | Chat model used for belief extraction and NLI judge calls. Examples: "claude-3-5-sonnet-latest", "claude-3-opus-latest", "claude-3-haiku-20240307". |
timeout | float | 30.0 | Maximum time (in seconds) to wait for a single API call. |
retry_config | Optional[RetryConfig] | RetryConfig() | Retry strategy for transient API errors. |
pythonfrom beliefstate.adapters import AnthropicAdapter
adapter = AnthropicAdapter(
model="claude-3-5-sonnet-latest",
timeout=30.0
)
⚠️ No Embedding API: Anthropic does not provide an embeddings API. Use the Dual-Adapter pattern with OpenAI or Ollama as the internal_adapter for embedding generation. Without embeddings, semantic similarity search and contradiction detection cannot function.
Google Gemini
The Gemini adapter supports Google's Generative AI models through the google-generativeai SDK. It provides both chat completion and embedding capabilities. The API key is read from GOOGLE_API_KEY or GEMINI_API_KEY environment variable.
Constructor Parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
client | Optional[Any] | None | Pre-configured Gemini client instance. If None, a client is created from the GOOGLE_API_KEY or GEMINI_API_KEY environment variable. |
model | str | "gemini-2.0-flash" | Chat model used for belief extraction and NLI judge calls. Examples: "gemini-2.0-flash", "gemini-1.5-pro", "gemini-1.5-flash". |
embed_model | str | "text-embedding-004" | Embedding model for vector generation. Used in semantic similarity search during contradiction detection. |
timeout | float | 30.0 | Maximum time (in seconds) to wait for a single API call. |
retry_config | Optional[RetryConfig] | RetryConfig() | Retry strategy for transient API errors. |
pythonfrom beliefstate.adapters import GeminiAdapter
adapter = GeminiAdapter(
model="gemini-2.0-flash",
embed_model="text-embedding-004",
timeout=30.0
)
Set GOOGLE_API_KEY or GEMINI_API_KEY environment variable.
Ollama (Local)
The Ollama adapter connects to a locally running Ollama server for completely free, offline LLM inference. It supports any model available in your local Ollama installation. Ollama is the recommended choice as an internal_adapter in the Dual-Adapter pattern — zero API costs for background tracking.
Constructor Parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
client | Optional[AsyncClient] | None | Pre-configured ollama.AsyncClient instance. If None, a client is created using the host and port parameters. |
model | str | "llama3.2" | Chat model for belief extraction and NLI judge calls. Must be pulled locally first via ollama pull <model>. Examples: "llama3.2", "mistral", "qwen2.5", "phi3". |
embed_model | str | "nomic-embed-text" | Embedding model for vector generation. Must be pulled locally. Recommended: "nomic-embed-text" (768 dims, fast, good quality). |
host | str | "http://localhost" | Ollama server hostname. Change if running Ollama on a different machine or in a Docker container. |
port | int | 11434 | Ollama server port. Default Ollama port is 11434. |
timeout | float | 120.0 | Maximum time (in seconds) per request. Local models may need more time than cloud APIs, especially on CPU-only machines. Increase for larger models. |
retry_config | Optional[RetryConfig] | RetryConfig() | Retry strategy for transient errors (e.g., Ollama server temporarily unavailable). |
pythonfrom beliefstate.adapters import OllamaAdapter
adapter = OllamaAdapter(
model="llama3.2",
embed_model="nomic-embed-text",
host="http://localhost",
port=11434
)
💡 Tip: Ollama is perfect as an internal_adapter in the Dual-Adapter pattern — completely free, fast, and runs locally. Pull both models before first use: ollama pull llama3.2 && ollama pull nomic-embed-text
LiteLLM (Multi-Provider)
The LiteLLM adapter uses the LiteLLM library to route requests to 100+ LLM providers through a unified API. It supports Azure OpenAI, AWS Bedrock, Cohere, Vertex AI, and many more. Provider-specific API keys are configured via environment variables as documented by LiteLLM.
Constructor Parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
model | str | (required) | LiteLLM model identifier with provider prefix. Examples: "azure/gpt-4", "bedrock/anthropic.claude-3-sonnet", "vertex_ai/gemini-pro". See LiteLLM docs for the full list. |
embed_model | str | None | LiteLLM embedding model identifier. Examples: "cohere/embed-english-v3.0", "azure/text-embedding-ada-002". If not set, embedding calls will raise an error. |
timeout | float | 30.0 | Maximum time (in seconds) per request. |
retry_config | Optional[RetryConfig] | RetryConfig() | Retry strategy for transient errors. |
pythonfrom beliefstate.adapters import LiteLLMAdapter
# Route to any of 100+ providers via LiteLLM
adapter = LiteLLMAdapter(
model="azure/gpt-4", # Azure OpenAI
embed_model="cohere/embed-english-v3.0" # Cohere embeddings
)
# Or use AWS Bedrock
adapter = LiteLLMAdapter(model="bedrock/anthropic.claude-3-sonnet")
RetryConfig
All adapters accept an optional RetryConfig that controls how transient API errors (rate limits, timeouts, server errors) are retried. The default configuration uses exponential backoff with jitter to avoid thundering herd problems when a provider recovers from an outage.
Constructor Parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
max_retries | int | 3 | Maximum number of retry attempts per API call. After this many failures, the error is raised to the caller. Set to 0 to disable retries entirely. |
initial_delay | float | 1.0 | Base delay (in seconds) before the first retry. Each subsequent retry multiplies this by exponential_base. |
max_delay | float | 30.0 | Maximum delay (in seconds) between retries. The exponential growth is capped at this value. |
exponential_base | float | 2.0 | Multiplier for exponential backoff. Delay formula: min(initial_delay × base^attempt, max_delay). With defaults: 1s → 2s → 4s. |
jitter | bool | True | Add random jitter to retry delays. When enabled, the computed delay is multiplied by a random factor between 0.5 and 1.0. This prevents multiple clients from retrying at exactly the same time (thundering herd), which is critical in multi-worker deployments. |
pythonfrom beliefstate.adapters.common import RetryConfig
# Conservative retry for rate-limited APIs
config = RetryConfig(
max_retries=5,
initial_delay=2.0,
max_delay=60.0,
exponential_base=2.0,
jitter=True,
)
# Pass to any adapter
adapter = OpenAIAdapter(model="gpt-4o", retry_config=config)
Retry Delay Schedule (with defaults)
With initial_delay=1.0, exponential_base=2.0, max_delay=30.0, and jitter=True:
| Attempt | Computed Delay | With Jitter (range) |
|---|---|---|
| 1 | 1.0s | 0.5s – 1.0s |
| 2 | 2.0s | 1.0s – 2.0s |
| 3 | 4.0s | 2.0s – 4.0s |
Transient vs. Permanent Errors
The retry mechanism only retries transient errors — those that might succeed on a subsequent attempt. These include:
- Rate limit errors (HTTP 429) — Provider is throttling requests
- Server errors (HTTP 500, 502, 503) — Provider is temporarily unavailable
- Timeout errors — Request took too long
- Connection errors — Network issues
Permanent errors (invalid API key, malformed request, model not found) are raised immediately without retrying.
Dual-Adapter Architecture
The Dual-Adapter pattern lets you use a premium model for your application's user-facing responses and a cheaper/local model for all background belief tracking work. This can reduce tracking costs by 90%+ while maintaining identical application quality.
When to Use Dual-Adapter
- Cost optimization: Your app uses GPT-4o ($$$) but tracking can run on Llama 3 via Ollama (free).
- Anthropic users: Claude doesn't offer an embedding API — you must use a second adapter for embeddings.
- Privacy: Keep user data local by running extraction on Ollama while the app uses a cloud provider.
- Latency: Offload tracking to a fast local model so it doesn't compete for API rate limits.
pythonfrom beliefstate.adapters import AnthropicAdapter, OllamaAdapter
# Your app uses Claude 3.5 Sonnet (premium)
app_adapter = AnthropicAdapter(model="claude-3-5-sonnet-latest")
# Background tracking uses local Llama 3 (free!)
bg_adapter = OllamaAdapter(model="llama3", embed_model="nomic-embed-text")
tracker = BeliefTracker(
config=config,
adapter=app_adapter, # Intercepts the Claude API payload
internal_adapter=bg_adapter # Runs extraction, embeddings, and judge
)
How It Works
┌─────────────────────┐ ┌─────────────────────┐ │ App Adapter │ │ Internal Adapter │ │ (Claude 3.5 Sonnet) │ │ (Ollama / Llama3) │ │ │ │ │ │ • to_llm_call() │ │ • generate() │ │ • to_llm_response()│ ────────▶ │ • get_embeddings() │ │ (payload parsing) │ belief tasks │ • health_check() │ └─────────────────────┘ └─────────────────────┘
Payload Normalization
Different model providers use radically different SDK payload signatures. For example:
- OpenAI: Accesses response text via
response.choices[0].message.content. - Anthropic: Accesses response text via
response.content[0].text. - Google Gemini: Accesses response text via
response.text.
Each adapter implements the ProviderAdapter protocol to normalize inputs and outputs:
-
to_llm_call(*args, **kwargs):Intercepts raw arguments passed to the provider SDK and extracts the conversation history, converting them into a standardizedLLMCallstructure containing a unified list of message dictionaries. -
to_llm_response(raw_response):Normalizes the provider's returned response object into anLLMResponsestructure, exposing the message content, model name, and token usage metadata.
This decoupling is what enables the Dual-Adapter pattern: the premium adapter parses the main application LLM calls, while the internal adapter handles all background vector embeddings, fact extractions, and logical checks using completely different API calls.