Skip to content

feat: multi-provider LLM support via req_llm#22

Merged
errantsky merged 3 commits intomainfrom
claude/hopeful-haibt
Mar 6, 2026
Merged

feat: multi-provider LLM support via req_llm#22
errantsky merged 3 commits intomainfrom
claude/hopeful-haibt

Conversation

@errantsky
Copy link
Owner

Summary

  • Multi-provider LLM backend via req_llm v1.6 — supports Anthropic, OpenAI, Ollama (local models), Gemini, Groq, and any provider req_llm supports
  • Named model map (config.models) replaces hardcoded model_large/model_small with flexible %{large: "provider:model", small: "provider:model"} specs
  • Legacy Anthropic client preserved as RLM.LLM.Anthropic for users needing direct API control
  • New replay system with RLM.Replay.replay/2 for deterministic replay of recorded runs, including code patching and fallback: :live mode
  • Comprehensive review fixes addressing silent failures, documentation accuracy, and error handling across the codebase

Changes

New Modules

  • RLM.LLM.ReqLLM — default multi-provider LLM backend
  • RLM.LLM.Anthropic — legacy direct Anthropic API client
  • RLM.Replay — replay orchestrator with patch and fallback support
  • RLM.Replay.Tape — tape struct + builder from EventLog events
  • RLM.Replay.LLM — tape-only LLM behaviour impl
  • RLM.Replay.FallbackLLM — tape-then-live LLM behaviour impl

Modified Modules

  • RLM.LLM — refactored to behaviour + shared utilities only
  • RLM.Config — added models map, resolve_model/2, context_window_for/2
  • RLM.Worker — uses named model map via model_key instead of inline lookups
  • RLM — updated public API to pass model_key: in worker opts

Documentation & Examples

  • CLAUDE.md — updated module map, config fields, agent orientation section
  • CHANGELOG.md — full Added/Changed/Fixed entries
  • README.md — updated usage examples and env var references
  • examples/local_models.exs — new example for Ollama/local model usage
  • All existing examples updated for ANTHROPIC_API_KEY

Tests

  • test/rlm/config_test.exs — 16 tests for Config resolution functions
  • test/rlm/replay_test.exs — comprehensive replay system tests
  • 178 tests total, 0 failures

Test plan

  • mix compile --warnings-as-errors passes
  • mix test — 178 tests, 0 failures
  • mix format --check-formatted passes
  • 5 review agents run (code-reviewer, silent-failure-hunter, comment-analyzer, code-simplifier, pr-test-analyzer)
  • Manual test with Ollama: RLM.run("Hello", "Echo this", models: %{large: "ollama:qwen3:8b"})
  • Manual test with mix test --include live_api (requires ANTHROPIC_API_KEY)

🤖 Generated with Claude Code

errantsky and others added 3 commits March 6, 2026 08:32
Replace the monolithic RLM.LLM with a behaviour + two implementations:
- RLM.LLM.ReqLLM (new default) — delegates to req_llm v1.6, supports
  Anthropic, OpenAI, Ollama, Gemini, Groq via "provider:model" specs
- RLM.LLM.Anthropic — preserved legacy hand-rolled Anthropic client

Add a named model map (config.models) with Config.resolve_model/2 and
Config.context_window_for/2. Workers use model_key atoms (:large, :small,
or custom) instead of inline config.model_large/model_small lookups.

API key resolution now checks ANTHROPIC_API_KEY first, falls back to
CLAUDE_API_KEY. All 162 tests pass unchanged (MockLLM unaffected).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Fix usage key mismatch: check both provider-specific and normalized
  key names for cache tokens (cache_creation_input_tokens vs
  cache_creation_tokens, cache_read_input_tokens vs cached_tokens)
- Add catch-all clause for unsupported message roles in build_context
- Make resolve_model!/2 actually raise on unknown keys (matching the !
  convention) instead of silently falling back to :large
- Add missing {:ok, non_string} clause in Config.resolve_model/2 to
  return a descriptive error instead of CaseClauseError
- Fix FallbackLLM default from RLM.LLM (now behaviour-only) to
  RLM.LLM.ReqLLM
- Strip "anthropic:" provider prefix in RLM.LLM.Anthropic before
  sending to the Anthropic API (models map stores prefixed specs)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Review agents identified several issues across 5 categories:

Silent failures:
- encode_object/1 returns {:error, :no_content} instead of empty string
- Tape.get_events/1 catches :noproc specifically, logs unexpected exits
- FallbackLLM logs when transitioning from tape to live LLM
- extract_usage/1 warns when all usage fields are nil
- context_window_for/2 warns on unknown model keys

Documentation:
- CLAUDE.md: remove stale cost config rows, fix env var references,
  fix models default to bare names, rewrite agent orientation section
  with key contracts, DI patterns, and common modification patterns
- Fix moduledocs: Replay (default module), Anthropic (differentiator),
  Worker (provider-agnostic), LLM behaviour (bare model names)
- Fix "Ollama (via vLLM)" → "Ollama (local models)" in config/req_llm

Examples:
- Update all examples from CLAUDE_API_KEY to ANTHROPIC_API_KEY
- Add examples/local_models.exs for Ollama/local model usage
- Update mix rlm.examples task with local_models entry

Tests:
- Add test/rlm/config_test.exs with 16 tests covering resolve_model/2,
  context_window_for/2, load/1 defaults, models map, and API key

Config:
- Remove unused top-level `require Logger` from config.ex

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@errantsky errantsky force-pushed the claude/hopeful-haibt branch from 11d9cd1 to ce8a6e8 Compare March 6, 2026 16:32
@errantsky errantsky merged commit 5e4c13d into main Mar 6, 2026
@errantsky errantsky deleted the claude/hopeful-haibt branch March 6, 2026 16:32
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant