Skip to content

Refactor configuration around a single adapter interface (v0.2.0) - #2

Open
arunkumarry wants to merge 1 commit into
mainfrom
refactor/adapter-interface-config
Open

Refactor configuration around a single adapter interface (v0.2.0)#2
arunkumarry wants to merge 1 commit into
mainfrom
refactor/adapter-interface-config

Conversation

@arunkumarry

Copy link
Copy Markdown
Owner

What changed

Phase 1 of the ease-of-use refactor: the four hand-written caller lambdas (llm_caller, messages_caller, embedding_caller, classifier_caller) are replaced by a single adapter interface, configuration is restructured into validated groups, and the Rails initializer template shrinks from ~120 lines of mixed provider examples to a ~25-line working config.

Adapter interface

  • config.adapter = :ruby_llm / :open_ai / :anthropic — built-in adapters that lazily require their provider gem and raise an actionable error ("add gem \"ruby_llm\" to your Gemfile") if it's missing
  • LlmOptimizer::Adapters::Custom.new(chat:, embed:) — escape hatch for any other provider
  • An internal Legacy adapter wraps the deprecated 0.1.x caller lambdas automatically, so existing initializers keep working unchanged
  • The routing classifier now runs through the adapter via config.routing.classifier_model — no more hand-written classification lambda

Defensive lambda invocation (Invoker)

User lambdas only receive the keyword arguments their signature accepts. This fixes a real regression: since 0.1.6 the gem passed tools: unconditionally, so the documented ->(prompt, model:) signature crashed with unknown keyword: :tools on every call.

Grouped, validated configuration

  • config.routing.*, config.cache.*, config.compression.*, config.history.*, config.conversation.*
  • All flat 0.1.x keys remain as delegating aliases (including per-call overrides like route_to:)
  • configure validates values immediately; each optimize validates cross-field consistency; LlmOptimizer.validate! (added to the generated initializer) fails fast at boot with messages that say exactly what to set — replacing the old silent warn-and-disable behavior

Fixed in passing

  • schema config (added in 0.1.7) is now actually passed through to adapters — it was declared but never used
  • History summarization injected a [content, token_info] array as the summary message content instead of the summary string
  • An unset redis_url no longer silently connects to localhost Redis; the cache is skipped with a warning
  • tools/with_tools are now true aliases of one value; removed dead tools_caller key and the never-assigned @_current_llm_caller

Why

The initializer required writing 3–4 lambdas with subtly different signatures that drifted between releases — the README's own quick-start example crashed on 0.1.6+. Misconfigurations surfaced as silently degraded behavior at request time instead of clear errors. This PR fixes the root cause rather than patching the old shape (breaking changes are acceptable pre-1.0; version bumped to 0.2.0).

Reviewer notes

  • Default complex model changed claude-3-5-sonnet-20241022claude-sonnet-4-5; embedding_model default is now nil (adapter picks its own)
  • Enabling the cache without redis_url/embedding support now raises ConfigurationError instead of warning and disabling — intentional behavior change, noted in CHANGELOG and README
  • The OpenAI/Anthropic adapters accept a client: injection for apps that already build their own client
  • Verified: 186 tests pass, RuboCop clean, plus a 16-check end-to-end smoke script covering the new API, per-call overrides, cache round-trip, legacy 0.1.x config compatibility, and validation errors

Phase 2 (conversation/cache interaction, system-prompt seeding, dead EmbeddingClient removal, timeout enforcement) will follow separately.

🤖 Generated with Claude Code

Replaces the four hand-written caller lambdas (llm_caller,
messages_caller, embedding_caller, classifier_caller) with one
config.adapter integration point, restructures configuration into
validated groups, and shrinks the Rails initializer template to a
minimal working config.

- Add adapter layer: built-in :ruby_llm, :open_ai, :anthropic adapters
  (lazily required with actionable errors), Adapters::Custom for any
  provider, and an internal Legacy adapter so 0.1.x caller lambdas keep
  working unchanged
- Add Invoker: user lambdas only receive the keyword arguments their
  signature accepts, fixing the 0.1.6 regression where the documented
  ->(prompt, model:) signature crashed with unknown keyword: :tools
- Group configuration (routing/cache/compression/history/conversation)
  with flat 0.1.x keys kept as delegating aliases, including per-call
  overrides
- Add fail-fast validation: configure validates values, optimize
  validates cross-field consistency, LlmOptimizer.validate! checks
  everything at boot (replaces silent warn-and-disable)
- Route the classifier through the adapter via routing.classifier_model
- Wire the schema option through to adapters (declared but unused since
  0.1.7)
- Fix history summarization injecting [content, token_info] as message
  content; stop connecting to localhost Redis when redis_url is unset
- Rewrite initializer template (~25-line working config + commented
  appendix) and README; bump version to 0.2.0

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
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