LLM is a lightweight Elixir client that provides a unified interface for interacting with Large Language Model APIs. It normalizes requests and responses across multiple providers, so your application code stays provider-agnostic.
Design Philosophy
LLM follows three core principles:
Normalized data model — Messages, tools, responses, and usage are represented by the same structs regardless of which provider you use. Switch from OpenAI to Anthropic by changing a single option.
Adapter pattern — Each provider's wire format (JSON encoding, SSE streaming, auth headers) lives in a dedicated adapter module. The core library never references provider-specific JSON keys.
Streaming first —
generate/2is built on top ofstream/2. All requests go through the streaming path, and the final response is assembled by collecting chunks. This means tool calls, thinking blocks, and error handling work identically whether you stream or not.
Architecture
┌─────────────┐
│ LLM.generate │
│ LLM.stream │
└──────┬──────┘
│
┌──────▼──────┐
│ LLM.Stream │ ← orchestration layer
└──────┬──────┘
│
┌────────────────┼────────────────┐
│ │ │
┌────────▼───────┐ ┌─────▼──────┐ ┌───────▼──────┐
│ LLM.Provider. │ │ LLM.Adapter│ │ LLM.HTTPClient│
│ Resolver │ │ .OpenAI │ │ │
└────────────────┘ │ .Anthropic │ └───────────────┘
│ .Gemini │
│ .OpenAIResp│
└────────────┘Request flow
LLM.generate/2orLLM.stream/2builds anLLM.Contextfrom the prompt and options.LLM.Provider.Resolverresolves the provider option into a full config map with adapter, base URL, and API key.LLM.Stream.start/2calls the adapter'sbuild_request/2to encode the context into provider-specific JSON, then sends an HTTP POST viaLLM.HTTPClient.LLM.Stream.next/1receives SSE events from the HTTP response and delegates to the adapter'sdecode_chunk/2to produce normalized chunk structs.LLM.Stream.collect/2accumulates chunks, auto-executes tool calls, and loops until the stream ends ormax_roundsis reached.- The final
LLM.Responsecontains a normalizedLLM.Message, usage stats, and the stop reason.
Key modules
| Module | Role |
|---|---|
LLM | Public API — generate/2, stream/2, providers/0, models/1 |
LLM.Context | Request context: system prompt, messages, tools |
LLM.Message | Normalized message across all providers |
LLM.Tool | Tool definition behaviour and inline creation |
LLM.Response | Normalized response with message, usage, stop reason |
LLM.Usage | Token usage information |
LLM.Stream | Streaming orchestration, chunk collection, tool loop |
LLM.Adapter | Behaviour for wire format translation |
LLM.Provider | Provider configuration behaviour |
LLM.HTTPClient | HTTP client behaviour (swappable for tests) |
What's next
- Getting Started — install, configure, and make your first request
- Messages, Roles, and Tool Calls — message structure, roles, and tool call lifecycle
- Providers — configure and switch between providers
- Streaming — work with streaming responses
- Tools — define and use tool calls
- Configuration — API keys, HTTP client, and runtime options