Repository navigation
Move OpenRouter BYOK billing out of Imp.Core - #303
Conversation
Imp.Clients.ReqLLM.resolve_openrouter_charge/2 now resolves a charge only for an OpenRouter model whose usage has "is_byok": the fee plus upstream_inference_cost when both are numbers, otherwise nil. Response metadata and stream normalization store that figure as :charge, and a cache hit drops it so a repeat does not bill the stored charge. Core treats a present :charge as final, nil included, and otherwise still reads usage "cost" or metadata :cost; usage sanitization leaves Decimal values intact so a non-numeric figure stays unknown instead of raising. The BYOK cases now live with the ReqLLM client and stream tests, and a raw envelope that carries no resolved charge reports the fee alone. Fixes deepfates#265
|
Thanks for taking this on. The rule now sits with the ReqLLM client and core has no provider names, which is what #265 asked for. A few things before this can merge:
|
resolve_openrouter_charge no longer checks the model spec, so tuple and map OpenRouter specs cost the fee plus the upstream charge too. The client always records :charge (the BYOK sum, or the reported cost), and a raw BYOK envelope with no resolved charge reads as unknown instead of the fee. Drops the Decimal pass-through in sanitize_usage_value and its test cases. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
@deepfates Thanks, all four code points are in 5df1cfc. The model check is gone, so On point 5: I don't have a clean |
|
Thanks for the quick turnaround, and sorry for the wait. I checked 5df1cfc against each point and merged it into current main locally. It merges cleanly, and Points 1, 3 and 4 are done. The Remaining before merge:
Thanks again. This is close. |
Core now reads the client's :charge and then metadata :cost, and no longer looks at usage "cost" or "is_byok", so it names no provider fields. A raw envelope with no resolved charge reads as unknown. The raw-envelope cost tests set :charge in their envelope instead of usage "cost". resolve_openrouter_charge/2 becomes Imp.Clients.ReqLLM.charge/1, which returns the figure directly, in the client and in Imp.Streaming.Execution.
|
Point 1 is in 4b5d920. Core no longer reads usage I took the optional point 3 as well:
|
Version 0.16.0 with install lines ~> 0.16.0 and tag links for v0.16.0. The changelog's Unreleased section goes under 0.16.0, and the release notes describe #353, #303 and #354. A minor release: request_retry: is a new option on Imp.Run.start/3, Imp.Core reads a call's charge from metadata :cost or req_llm.charge instead of usage "cost", and a forced submit with invalid arguments goes to the extractor.
What changed
Imp.Clients.ReqLLM.resolve_openrouter_charge/2 now resolves a charge only for an OpenRouter model whose usage has "is_byok": the fee plus upstream_inference_cost when both are numbers, otherwise nil. Response metadata and stream normalization store that figure as :charge, and a cache hit drops it so a repeat does not bill the stored charge. Core treats a present :charge as final, nil included, and otherwise still reads usage "cost" or metadata :cost; usage sanitization leaves Decimal values intact so a non-numeric figure stays unknown instead of raising. The BYOK cases now live with the ReqLLM client and stream tests, and a raw envelope that carries no resolved charge reports the fee alone.
Callers of the provider-neutral response path were billed with OpenRouter's bring-your-own-key rule: a usage map marked is_byok turned the reported cost into OpenRouter's fee plus the upstream inference charge, or left the charge unknown when either figure was missing. That one provider's accounting showed up on every core response, including raw envelopes that had never been normalized by an OpenRouter client. Imp.Core.reported_cost/2 matched usage with "is_byok" and added "cost" to cost_details.upstream_inference_cost. The OpenRouter field names and the fee-plus-upstream rule sat in the provider-neutral cost reader instead of with the client that sees OpenRouter usage.
Fixes #265
How it was verified
Run
mix check— it is the ordinary merge signal and it needs no credentials.Paste the summary line. Run any further gate your change touches
(
mix protocol.check,mix package.check,mix quality.check,mix dialyzer.check);CONTRIBUTING.mdlists them all.Say what you could not run and why. Provider-backed, research-scale and
benchmark checks need credentials, datasets or spend: they are the
maintainer's to run, and an outside contributor is not expected to.
Documentation
Update ExDoc, guides or Livebooks alongside a public API change.