Skip to content

Provider strategy: the researched proposal WORLD.md asks for (measured coupling, three options, one lean) #22

Description

@minerva-sky

WORLD.md names provider strategy an open question and says it "needs a researched proposal with a real cost, not a drive-by PR." This is that proposal. Nothing lands from this issue; it exists so the decision can be made on evidence.

Measured coupling (the real cost baseline)

OpenAI specifics touch exactly three files (grep over lib/):

  • lib/agentic/llm_client.rb — 23 OpenAI references: client construction, error taxonomy mapping, response envelope parsing. This is ~all of it.
  • lib/agentic/structured_outputs.rb — schema DSL emits OpenAI response_format JSON-schema shape. Load-bearing: TaskPlanner, agent assembly, and task output schemas all depend on structured outputs working.
  • lib/agentic/cli.rbOPENAI_ACCESS_TOKEN env check.

Everything else (orchestrator, journal, planner logic, capability system) is already provider-blind behind LlmClient/LlmResponse/LlmConfig. The abstraction seam exists; the question is only what plugs into it. Related: #16 (ruby-openai is unbounded in the gemspec, locked at 7.1.0 while consumers resolve 8.3.0).

The options

1. Status quo — ruby-openai (community gem). Cost: zero today. Risk: it's a community shim OpenAI now competes with (official SDK exists); our 7.1.0→8.3.0 constraint gap (#16) shows the maintenance current we're swimming against. Structured outputs remain hand-rolled.

2. Official openai gem (openai/openai-ruby). OpenAI-maintained, ships typed structured-output helpers (OpenAI::BaseModel, ArrayOf, EnumOf, UnionOfdocs). Cost: rewrite llm_client.rb internals + error mapping; our StructuredOutputs::Schema DSL could delegate or stay as-is. Stays single-provider — honest if that's the strategy.

3. ruby_llm (crmne/ruby_llm) as the provider layer. One dependency replacing ruby-openai, providers become config (repo, site). The fact that decides it for me: as of its June 2026 releases it does cross-provider structured outputs (Anthropic and Bedrock added — releases) — which is this gem's one hard provider requirement. Ruby-idiomatic, active, and it is not a framework adoption (anti-goal safe): we'd consume its client + schema layer behind our existing LlmClient seam and keep our own plan/capability/orchestration identity. Cost: same llm_client.rb rewrite as option 2, plus mapping our retry/rate-limit config onto its knobs; dependency count stays at parity (swap, not add).

(Also surveyed and rejected: langchainrb — framework adoption, exactly the anti-goal; llm.rb/instructor-rb — narrower than what we'd replace.)

My lean, held loosely

Option 3, but only when a second provider has a concrete user — WORLD.md's own rule is two concrete uses before one abstraction, and "which second provider earns the abstraction" is still unanswered. Until then, option 2 is the defensible near-term move if #16 forces a ruby-openai major-version decision anyway: if we must rewrite llm_client.rb internals for a constraint bump, rewriting against the SDK that can't be abandoned by a third party is the better spend. Concretely: decide #16 and this together, not separately.

Experiment that would validate before committing

Spike branch, no merge: swap LlmClient internals to the candidate, leave the public complete(messages, output_schema:) contract untouched, run the full suite (~610 examples) + benchmark/. Deliverables: diff size (predicted <400 lines confined to the three files above), suite verdict, latency delta. One spike per candidate, ~an afternoon each — cheap enough to buy the decision with data instead of taste.


Origin: loop:self session 2026-08-21. Per WORLD.md, no provider abstraction lands until this is decided; this issue is L0 decision-support only.

Sources: openai/openai-ruby · OpenAI Ruby API reference · crmne/ruby_llm · ruby_llm releases · rubyllm.com

Metadata

Metadata

Assignees

No one assigned

    Labels

    loop:selfForward-thinking improvement loop (WORLD.md-aligned)status:analyzedAnalyzed, awaiting decision

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions