Skip to content

feat: let callers set headers and a per-call model - #14

Draft
simonx1 wants to merge 1 commit into
obie:mainfrom
simonx1:feat/per-request-options
Draft

simonx1 wants to merge 1 commit into
obie:mainfrom
simonx1:feat/per-request-options

Conversation

@simonx1

@simonx1 simonx1 commented Sep 18, 2026

Copy link
Copy Markdown

There was no way to add a header. Not one.

OpenRouter reads HTTP-Referer and X-Title for app attribution, tracing needs a correlation id, and a gateway in front of either may want a tenant header; all of it required replacing the transport or subclassing the provider to override #headers. The JS SDK takes headers per call and per client, and this took neither.

Nor was there a way to use a second model — model: is fixed when the client is built, so asking jev-preview one question meant a second client with a second connection and a second copy of the config.

client = RubyDecisionModel::Client.new(headers: { "HTTP-Referer" => "https://example.com" })
client.ask(state: state, questions: questions, model: "jev-preview", headers: { "X-Trace-Id" => id })

Headers merge provider, then client, then call, matched without regard to case so an override of content-type replaces Content-Type instead of travelling beside it. A per-call model resolves through the provider's aliases and leaves client.model alone.

Header names and values are checked for newlines — Net::HTTP rejects those with an ArgumentError raised from inside the request, which tells you nothing about which header did it.

Tests

test/request_options_test.rb, 15 cases, including that per-call values don't leak into the next call.


Draft: part of a security and API-coverage audit, opened for reference rather than as a request for immediate review. Independent of the other branches, each off main. Suite green on Ruby 3.2.11, 3.3.8 and 3.4.8.

🤖 Generated with Claude Code

There was no way to add a header. Not one. OpenRouter reads HTTP-Referer
and X-Title for app attribution, tracing needs a correlation id, and a
gateway in front of either may want a tenant header; all of it required
replacing the transport or subclassing the provider to override #headers.
The JS SDK takes headers per call and per client, and this took neither.

Nor was there a way to use a second model. `model:` is fixed when the
client is built, so asking jev-preview one question meant a second client
with a second connection and a second copy of the config.

Client.new takes headers:, ask takes model: and headers:. They merge
provider, then client, then call, matched without regard to case so an
override of "content-type" replaces "Content-Type" instead of travelling
beside it. A per-call model resolves through the provider's aliases like
any other and leaves client.model alone.

Header names and values are checked for newlines. Net::HTTP rejects those
with an ArgumentError raised from inside the request, which tells you
nothing about which header did it; this says so at the call site.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

This branch has not been deployed

No deployments
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