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:

  1. Payload normalization: Converting provider-specific request/response formats into BeliefState's universal LLMCall and LLMResponse models.
  2. 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:

✅ Automatic Retry ✅ Timeout Handling ✅ Health Checks ✅ Structured Logging ✅ API Key Validation
🟢

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

AdapterBest ForEmbeddingsCost
OpenAIBest overall extraction quality, widest model selection✅ Native (text-embedding-3-small)Pay per token
AnthropicLong context conversations, strong reasoning❌ No embedding API — use Dual-AdapterPay per token
GeminiLarge context windows, Google Cloud integration✅ Native (text-embedding-004)Pay per token / Free tier
OllamaLocal/private deployment, zero cost, offline use✅ Native (nomic-embed-text)Free (local GPU)
LiteLLMMulti-provider routing, Azure/Bedrock/Cohere access✅ Via LiteLLM routingVaries 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

ParameterTypeDefaultDescription
clientOptional[AsyncOpenAI]NonePre-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.
modelstr"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_modelstr"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_kwargsOptional[dict]{}Additional keyword arguments forwarded to the embedding API call. Example: {"dimensions": 256} to use OpenAI's dimension reduction feature for smaller, faster vectors.
timeoutfloat30.0Maximum time (in seconds) to wait for a single API call before raising a timeout error. Applies to both chat completions and embedding calls.
retry_configOptional[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

ParameterTypeDefaultDescription
clientOptional[AsyncAnthropic]NonePre-configured anthropic.AsyncAnthropic client instance. If None, a client is created from the ANTHROPIC_API_KEY environment variable.
modelstr"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".
timeoutfloat30.0Maximum time (in seconds) to wait for a single API call.
retry_configOptional[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

ParameterTypeDefaultDescription
clientOptional[Any]NonePre-configured Gemini client instance. If None, a client is created from the GOOGLE_API_KEY or GEMINI_API_KEY environment variable.
modelstr"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_modelstr"text-embedding-004"Embedding model for vector generation. Used in semantic similarity search during contradiction detection.
timeoutfloat30.0Maximum time (in seconds) to wait for a single API call.
retry_configOptional[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

ParameterTypeDefaultDescription
clientOptional[AsyncClient]NonePre-configured ollama.AsyncClient instance. If None, a client is created using the host and port parameters.
modelstr"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_modelstr"nomic-embed-text"Embedding model for vector generation. Must be pulled locally. Recommended: "nomic-embed-text" (768 dims, fast, good quality).
hoststr"http://localhost"Ollama server hostname. Change if running Ollama on a different machine or in a Docker container.
portint11434Ollama server port. Default Ollama port is 11434.
timeoutfloat120.0Maximum time (in seconds) per request. Local models may need more time than cloud APIs, especially on CPU-only machines. Increase for larger models.
retry_configOptional[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

ParameterTypeDefaultDescription
modelstr(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_modelstrNoneLiteLLM embedding model identifier. Examples: "cohere/embed-english-v3.0", "azure/text-embedding-ada-002". If not set, embedding calls will raise an error.
timeoutfloat30.0Maximum time (in seconds) per request.
retry_configOptional[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

ParameterTypeDefaultDescription
max_retriesint3Maximum 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_delayfloat1.0Base delay (in seconds) before the first retry. Each subsequent retry multiplies this by exponential_base.
max_delayfloat30.0Maximum delay (in seconds) between retries. The exponential growth is capped at this value.
exponential_basefloat2.0Multiplier for exponential backoff. Delay formula: min(initial_delay × base^attempt, max_delay). With defaults: 1s → 2s → 4s.
jitterboolTrueAdd 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:

AttemptComputed DelayWith Jitter (range)
11.0s0.5s – 1.0s
22.0s1.0s – 2.0s
34.0s2.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 standardized LLMCall structure containing a unified list of message dictionaries.
  • to_llm_response(raw_response): Normalizes the provider's returned response object into an LLMResponse structure, 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.