Packages
req_llm
1.26.0
1.28.0
1.27.0
1.26.0
1.25.0
1.24.0
1.23.0
1.22.0
1.21.1
1.21.0
1.20.0
1.19.0
1.18.0
1.17.1
1.17.0
1.16.0
1.15.0
1.14.0
1.13.0
1.12.0
1.11.0
1.10.0
1.9.0
1.8.0
1.7.1
1.7.0
1.6.0
1.5.1
1.5.0
1.4.1
1.4.0
1.3.0
1.2.0
1.1.0
1.0.0
1.0.0-rc.8
1.0.0-rc.7
1.0.0-rc.6
1.0.0-rc.5
1.0.0-rc.4
1.0.0-rc.3
1.0.0-rc.2
1.0.0-rc.1
Composable Elixir library for LLM interactions built on Req & Finch
Current section
Files
Jump to
Current section
Files
AGENTS.md
# AGENTS.md - ReqLLM Development Guide
**IMPORTANT: DO NOT WRITE COMMENTS INTO THE BODY OF ANY FUNCTIONS.**
## Project Overview
ReqLLM is a composable Elixir library for AI interactions built on Req, providing a unified interface to AI providers through a plugin-based architecture. The library uses OpenAI Chat Completions as the baseline API standard, with providers implementing translation layers for non-compatible APIs.
## Common Commands
### Build & Test
- `mix test` - Run all tests using cached fixtures
- `mix test test/req_llm_test.exs` - Run specific test file
- `mix test --only describe:"model/1 top-level API"` - Run specific describe block
- `LIVE=true mix test` - Run against real APIs and (re)generate fixtures
- `REQ_LLM_DEBUG=1 mix test` - Run tests with verbose fixture debugging output
- `mix compile` - Compile the project
- `mix quality` or `mix q` - Run quality checks (format, compile --warnings-as-errors, dialyzer, credo)
### Coverage Validation
- `mix mc` or `mix req_llm.model_compat` - Show models with passing fixtures
- `mix mc "*:*"` - Validate all models (parallel, fixture-based)
- `mix mc --sample` - Validate sample model subset (config/config.exs)
- `mix mc anthropic` - Validate all Anthropic models
- `mix mc "openai:gpt-4o"` - Validate specific model
- `mix mc "xai:*" --record` - Re-record fixtures for xAI models
- `mix mc --available` - List all models from registry (priv/models_dev/)
**Coverage System Architecture:**
- **Model Registry**: provided by the `llm_db` dependency (no manual sync needed)
- **Fixture State**: `priv/supported_models.json` (auto-generated artifact)
- **Parallel Execution**: Tests run concurrently for speed
- **State Tracking**: Skips models with passing fixtures unless `--record` or `--record-all`
#### Test Filtering with Semantic Tags
ReqLLM uses structured key/value tags for precise test filtering:
**Tag Dimensions:**
- `category` - Test type (`:core`, `:streaming`, `:tools`, `:embedding`)
- `provider` - LLM provider (`:anthropic`, `:openai`, `:google`, `:groq`, `:openrouter`, `:xai`)
**Examples:**
- `mix test --only "category:core"` - Run all core tests
- `mix test --only "provider:anthropic"` - Run Anthropic tests only
- `mix test --only "category:core" --only "provider:openrouter"` - Run OpenRouter core tests
- `LIVE=true mix test --only "category:core" --only "provider:anthropic"` - Regenerate Anthropic core fixtures
### Code Quality
- `mix format` - Format Elixir code
- `mix format --check-formatted` - Check if code is properly formatted
- `mix dialyzer` - Run Dialyzer type analysis
- `mix credo --strict` - Run Credo linting (includes custom rule to enforce no comments in function bodies)
## Architecture & Structure
### Core Structure
- `lib/req_llm.ex` - Main API facade with generate_text/3, stream_text/3, generate_object/4
- `lib/req_llm/` - Core modules (Model, Provider, Error structures)
- `lib/req_llm/providers/` - Provider-specific implementations (Anthropic, OpenAI, etc.)
- `test/` - Three-tier testing architecture (see `test/AGENTS.md` for detailed testing guide)
- `test/req_llm/` - Core package tests (NO API calls, unit tests with mocks)
- `test/provider/` - Mocked provider-specific tests (NO API calls, tests provider nuances)
- `test/coverage/` - Live API coverage tests (fixture-based, high-level API only)
- `test/support/` - Shared helpers (live fixtures, HTTP mocks, test macros)
### Core Data Structures
- `ReqLLM.Context` - Conversation history as a collection of messages
- `ReqLLM.Message` - Single conversation message with multi-modal content support
- `ReqLLM.Message.ContentPart` - Individual content piece (text, image, tool call, etc.)
- `ReqLLM.Tool` - Function calling definition with schema and callback
- `ReqLLM.StreamChunk` - Unified streaming response format across providers
- `LLMDB.Model` - Canonical model metadata struct used by ReqLLM
- `ReqLLM.Response` - High-level LLM response with context and metadata
### Model Specs
- ReqLLM supports full explicit model specs for models that are not in LLMDB yet
- Keep this path backwards compatible: public APIs may still receive a plain map as `model_spec`
- Internally, plain-map model specs should be normalized into `%LLMDB.Model{}` as early as possible
- Prefer documenting advanced examples with `ReqLLM.model!/1` or `LLMDB.Model.new!/1`
- Do not require LLMDB catalog membership for full model specs; only require the metadata needed to route the request
### Provider Architecture
- Each provider implements `ReqLLM.Provider` behavior with callbacks:
- `prepare_request/4` - Configure operation-specific requests (non-streaming only)
- `attach/3` - Set up authentication and Req pipeline steps (non-streaming only)
- `encode_body/1` - Transform context to provider JSON (non-streaming only)
- `decode_response/1` - Parse API responses (non-streaming only)
- `attach_stream/4` - Build complete Finch streaming request (streaming only, optional)
- `decode_sse_event/2` - Decode provider SSE events to StreamChunk structs (streaming only, optional)
- `extract_usage/2` - Extract usage/cost data (optional)
- `translate_options/3` - Provider-specific parameter translation (optional)
- **Response Assembly**: `ReqLLM.Provider.ResponseBuilder` behaviour centralizes response construction from StreamChunks
- `ResponseBuilder.for_model/1` routes to provider-specific builders (Anthropic, Google, OpenAI Responses API)
- `Provider.Defaults.ResponseBuilder` handles OpenAI-compatible providers
- Custom builders ensure streaming and non-streaming produce identical Response structs
- Providers use `ReqLLM.Provider.DSL` macro for registration and metadata loading
- **Non-streaming**: Core API uses provider's `attach/3` to compose Req requests with provider-specific steps
- **Streaming**: Uses Finch with provider's `attach_stream/4` to build streaming requests and `decode_sse_event/2` to parse SSE events
- **Options Translation**: Providers can implement `translate_options/3` to handle model-specific parameter requirements (e.g., OpenAI o1 models require `max_completion_tokens` instead of `max_tokens`)
### Encoding/Decoding System
- Provider callbacks handle encoding/decoding requests and responses
- Built-in defaults provide OpenAI-style wire format handling
- Providers can override `encode_body/1` and `decode_response/1` for custom formats
## Code Style & Conventions
### General Style
- Follow standard Elixir conventions and use `mix format` for consistent formatting
- Use `@moduledoc` and `@doc` for comprehensive documentation
- Prefer pattern matching over conditionals where possible
- Use `{:ok, result}` / `{:error, reason}` tuple returns for fallible operations
- **No inline comments in method bodies** - code should be self-explanatory through clear naming and structure
### Imports & Dependencies
- Minimize imports, prefer explicit module calls (e.g., `ReqLLM.model!/1`)
- Group deps in mix.exs: runtime deps first, then dev/test deps with `, only: [:dev, :test]`
### Types & Validation
- Use Zoi schemas for structured data with validation:
```elixir
@schema [
field1: [type: :string, required: true],
field2: [type: :integer, default: 0]
]
```
- Use Splode for structured error handling with specific error types
### Error Handling
- Return `{:ok, result}` or `{:error, %ReqLLM.Error{}}` tuples
- Use Splode error types: `ReqLLM.Error.API`, `ReqLLM.Error.Parse`, `ReqLLM.Error.Auth`
- Include helpful error messages and context in error structs
### Dialyzer
- Only add entries to `.dialyzer_ignore.exs` as an absolute last resort
- Prefer fixing the underlying type issue or improving type specs
### Testing & Fixture Workflow
- Tests are grouped by *capability*, not by individual function-call
- All suites use `ReqLLM.Test.LiveFixture.use_fixture/3` to abstract live vs cached responses
- Cached JSON fixtures live next to the test in `fixtures/<provider>/<test_name>.json` and are automatically written when the `LIVE=true` env-var is set
- Most suites run `async: true`. Suites that write fixtures are forced to synchronous execution via `@moduletag :capture_log`
```elixir
defmodule CoreTest do
use ReqLLM.Test.LiveFixture, provider: :openai
use ExUnit.Case, async: true
describe "generate_text/3" do
test "basic happy-path" do
{:ok, text} =
use_fixture(:provider, "core-basic", fn ->
ReqLLM.generate_text!("openai:gpt-4o", "Hello!")
end)
assert text =~ "Hello"
end
end
end
```
## GitHub Review Labels
Use GitHub PR labels to track review state after triage:
- `ready_to_merge` - No blocking review findings remain, the PR is merge-clean, and GitHub CI is green
- `needs_work` - Blocking findings, missing coverage, merge conflicts, or failing CI still need follow-up before merge
### Review Label Rules
1. After every substantive PR review, apply exactly one of `ready_to_merge` or `needs_work`
2. When setting one review-state label, remove the other one in the same step
3. Only mark `ready_to_merge` when the reviewer believes the PR can merge without further code changes
4. Use `needs_work` when there are correctness risks, behavior regressions, missing critical tests, unresolved review comments, merge conflicts, or red GitHub checks
5. If you push follow-up commits to a PR branch, re-check the label state before finishing; do not leave stale `ready_to_merge` labels on a PR that still needs validation
### Common Commands
```bash
gh pr edit <number> --add-label ready_to_merge --remove-label needs_work
gh pr edit <number> --add-label needs_work --remove-label ready_to_merge
```
## Release Hygiene
- Do not modify `CHANGELOG.md`; release notes are generated from Git history during release, so keep changes focused on proper Conventional Commits.