How LLM Providers Are Managed in ai-memory: A Complete Technical Guide
LLM providers in ai-memory are abstracted behind the LlmProvider trait, with eight concrete implementations (OpenAI, Anthropic, Google, Gemini, Copilot, Local, OpenAI OAuth, and OIDC) instantiated through a factory pattern that reads from Figment configuration.
The ai-memory crate orchestrates multiple Large Language Model backends through a unified, async-compatible interface. At the core of this system lies the LlmProvider trait defined in crates/ai-memory-llm/src/provider.rs, which decouples the rest of the codebase from vendor-specific HTTP implementations and authentication mechanisms.
The LlmProvider Trait: Core Abstraction
The LlmProvider trait defines a minimal, provider-agnostic API that all LLM backends must implement. This design enables type-erased, thread-safe provider storage via Arc<dyn LlmProvider> across the MCP server and hook router.
The trait specifies six essential methods:
name()– Returns a human-readable identifier (e.g.,"openai","anthropic")model()– Provides the model name used in HTTP requestscomplete()– Executes plain-text chat completioncomplete_with_operation_id()– Completion tied to a logical operation ID (default fallback tocomplete)complete_structured_raw()– JSON-schema-constrained completion returningserde_json::Valuecomplete_structured_raw_with_operation_id()– Operation-aware structured completion (default forwards to raw method)
The trait carries Send + Sync bounds, allowing safe concurrent access from any Tokio task without cloning connection state.
Supported LLM Provider Implementations
ai-memory ships eight concrete implementations, each isolated to its own source file under crates/ai-memory-llm/src/:
| Provider | Source File | Key Implementation Details |
|---|---|---|
| OpenAI | openai.rs |
reqwest client with rustls-tls-native-roots; operation IDs mapped to OpenAI's assistant_id field |
| Anthropic | anthropic.rs |
Messages API endpoint with streaming support and completion_id metadata handling |
| Google Vertex AI | google.rs |
Service-account authentication; model name mapping to Vertex AI identifiers |
| Gemini | gemini.rs |
Native Gemini request format with specialized schema handling |
| GitHub Copilot | copilot.rs |
Copilot completions endpoint supporting both plain-text and structured outputs |
| Local (in-process) | local.rs |
Pure-Rust embeddings via candle crate for offline/test environments |
| OpenAI OAuth | openai_oauth.rs |
Token refresh logic layered over base OpenAI provider |
| OIDC | oidc.rs |
Generic OpenID Connect token handling for OIDC-capable providers |
Each implementation handles its own authentication, request serialization, error mapping, and response parsing while exposing the same uniform interface.
Provider Factory Pattern and Configuration
Provider instantiation is centralized through LlmProviderFactory in crates/ai-memory-llm/src/factory.rs. The factory consumes Figment configuration to determine which concrete provider to build, injects appropriate credentials, and returns a boxed dyn LlmProvider.
Standard construction pattern:
let cfg = figment::Figment::new()
.merge(figment::providers::Toml::file("ai-memory.toml"));
let provider = ai_memory_llm::factory::make_provider(&cfg)?;
// Returns Box<dyn LlmProvider>
Configuration values—API keys, model names, endpoint URLs, OAuth scopes—are sourced from ai-memory.toml and processed through the core Config struct in crates/ai-memory-core/src/config.rs. This centralization ensures credential isolation and environment-specific overrides without code changes.
Typed Helper Functions for Structured Output
Beyond raw trait methods, ai-memory provides high-level helpers in crates/ai-memory-llm/src/provider.rs (lines 70-106) that automate JSON schema generation and deserialization:
use ai_memory_llm::{provider::LlmProvider, complete_structured};
use ai_memory_llm::types::{ChatRequest, ChatResponse};
let request = ChatRequest::new("Explain quantum tunneling");
let response: MyResponse = complete_structured(&*provider, request).await?;
These helpers leverage schemars for compile-time schema derivation, eliminating manual JSON Schema construction while maintaining full type safety. The complete_structured and complete_structured_with_operation_id functions wrap the raw trait methods, bridging the gap between Rust's type system and LLM JSON outputs.
Thread Safety and Concurrency Model
The Send + Sync trait bounds enable shared provider ownership across async boundaries. A single Arc<dyn LlmProvider> can be:
- Stored in the MCP server state
- Passed to hook routers
- Cloned into spawned Tokio tasks
This design avoids connection-per-request overhead while maintaining safety guarantees. Providers internally manage their own connection pooling and rate limiting where appropriate.
Summary
- Trait abstraction:
LlmProviderisolates vendor-specific HTTP logic behind six core methods - Eight implementations: OpenAI, Anthropic, Google, Gemini, Copilot, Local, OpenAI OAuth, and OIDC each live in dedicated source files
- Factory instantiation:
LlmProviderFactoryreads Figment configuration to build the correct provider at runtime - Type-safe helpers:
complete_structuredfunctions automate JSON schema handling viaschemars - Thread-safe design:
Send + Syncbounds permitArc<dyn LlmProvider>sharing across Tokio tasks
Frequently Asked Questions
How do I add a new LLM provider to ai-memory?
Implement the LlmProvider trait for your provider struct in a new file under crates/ai-memory-llm/src/, then register it in the factory at crates/ai-memory-llm/src/factory.rs. Your implementation must satisfy Send + Sync and handle its own HTTP client configuration and authentication.
Can I use multiple LLM providers simultaneously in the same ai-memory instance?
Yes. Because providers are trait objects, you can instantiate multiple Box<dyn LlmProvider> or Arc<dyn LlmProvider> values from the factory and route requests to different providers based on operation type, cost optimization, or fallback logic.
Where are API credentials stored in ai-memory?
Credentials reside in the Figment-managed configuration file (ai-memory.toml). The Config struct in crates/ai-memory-core/src/config.rs parses these values and passes them to the factory, keeping secrets out of source code and enabling environment-specific configuration files.
What is the purpose of operation IDs in LLM completions?
Operation IDs enable request tracing and idempotency across distributed systems. The complete_with_operation_id and complete_structured_raw_with_operation_id methods allow callers to associate a logical operation identifier with a completion request, which providers like OpenAI map to native fields (assistant_id) for tracking and potential retry semantics.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →