Repository navigation
Releases
v0.11.13
wiremux 0.11.13
Highlights
Messages requests that Anthropic rejected now use one message per role, with each tool result before any text.
A failed tool result is no longer sent as an ordinary success on Chat Completions, Responses, or Gemini.
Bug fixes
-
Chat Completions, Responses, and Gemini sent a failed tool result as a success. Those APIs have no field for a tool error. Encode now stops with
hard-error at items[N].is_error: chat-completions has no tool error field(orresponses, orgemini).send,stream_with_loss, the proxy, andwiremux mapreturn that error instead of the success body. Messages still writesis_error. Converse still writestoolResult.statusoferror. A payload of{"ok":false}stays a success whenis_erroris false (#732). -
Anthropic rejected two user messages or two assistant messages in a row. A parallel tool turn was one message per call or result, so the next Messages request failed before the model saw it. Encode now joins those turns into one message. That covers parallel calls, parallel results (a failure can sit beside a success), reasoning followed by a call or by text, and a decoded tool call plus text. Hosted tool items and unknown JSON are still copied through unchanged (#734).
-
Anthropic rejected a user message when text came before
tool_result. The error istool_useids found withouttool_resultblocks immediately after. Messages encode writes everytool_resultfirst, then any text, including when a later user turn is joined onto earlier text. A blank user turn does not insert"."when a tool result follows it. A user turn with no tool result still uses"."for empty text (#735). -
Anthropic rejected a failed tool result whose content was empty. The error is
content cannot be empty if is_error is true. Messages encode writes"."for that result. The loss log recordsempty error tool result became '.'. A successful tool result with no text stays empty, andis_errorstays on the failed result (#736).
Upgrading
Set wiremux and wiremux-auth to 0.11.13. No public struct or enum field was added, so existing literals still compile.
Encoding is_error: true to Chat Completions, Responses, or Gemini now fails instead of producing the success body. Messages and Converse still encode that failure.
Full changelog
v0.11.12
wiremux 0.11.12
Highlights
Tool JSON inside a cache breakpoint is sent, and a Gemini stream keeps each argument on the call that owns it.
A rate limit inside HTTP 200 comes back as HTTP 429, and a context-window stop is no longer a normal end.
Bug fixes
-
A cache breakpoint around a JSON tool result sent an empty string or a dot. Chat, Responses, Converse, and Messages now send that JSON. A second breakpoint, text beside the JSON, and
FunctionOutput.outputstay with it. Gemini already kept the JSON (#728). -
Gemini stream argument bytes were appended to the only open call. A delta whose index is not open yet waits on that index, including a custom-tool delta. A later start at that index fills the name and keeps the bytes. Two open calls still keep their own arguments (#728).
-
A rate limit or server error inside HTTP 200 looked like a normal reply. Gemini code 429 or
RESOURCE_EXHAUSTEDis HTTP 429, and Chaterror.typeisrate_limit_exceeded.api_error,server_error,overloaded_error, numeric 503, and GeminiUNAVAILABLEare HTTP 503, the same transient server error the client already returns. A billing error,INVALID_ARGUMENT, and aninvalid_request_errorstay HTTP 400 (#730). -
A rejected key or a missing model was classified from a nearby word. A bad-key phrase is HTTP 401 even when the numeric code is 5xx. Code 408 with a rate-limit phrase stays a rate limit. A numeric 4xx other than 408 and 429 wins over that phrase.
model not found,no such model,models/<id> is not found, and JSON code 404 are a missing model.does not existcounts only when the text is about a model (#730). -
A context-window stop encoded as a normal end. Chat writes
length. Converse writesmax_tokens. Gemini writesMAX_TOKENS. Responses writesincompleteon the stream event, onStreamEncoder::finish, and on complete JSON. Converseend_turn, Chattool_calls, andstop_sequenceencode as Responsescompleted(#730). -
A malformed function call or a content filter became a tool stop. Messages
content_filterencodes asrefusal, including when a tool is open. Geminimalformed_function_callstaysMALFORMED_FUNCTION_CALLeven when the vendor token is empty. Chat writescontent_filter. Converse writescontent_filtered. Responses writesfailed. A Responses status ofcancelledstayscancelled. The spellingcanceledencodes ascancelled(#730). -
Bedrock rejected the next turn after
tool_choicenone. When the history already has a call or a result,toolConfig.toolsstays andtoolChoiceis not set. A turn with no tool block still omitstoolConfig(#730). -
A Gemini tool result dropped
fileData.fileUri. PDF and image file URIs round-trip onfunctionResponse.parts. An S3 document and a blank file URL are dropped and recorded, withS3 document has no Gemini slotordocument url is empty(#730). -
A Gemini part that was only
thoughtSignaturewas dropped. That signature is sent back on the next request (#730). -
Messages rejected a request that set both
temperatureandtop_p.temperatureis sent andtop_pis dropped, because that API rejects the pair.top_palone is still sent (#730). -
A Responses assistant message sent image, audio, or file parts as user input. Those parts are dropped and recorded. Assistant text stays
output_text. A user message still sendsinput_image(#730). -
Chat tool audio was written into the tool text. The bytes are not sent. The loss log records
no slot(#730). -
A Messages web citation dropped
encrypted_indexandcited_text. Non-empty values are kept. Missing values still encode as empty strings on the way back to Messages (#730).
Upgrading
Set wiremux and wiremux-auth to 0.11.12. No public struct or enum field was added, so existing literals still compile.
A Responses context-window stop is incomplete. A cancelled turn stays cancelled. The proxy returns HTTP 429 for a rate limit that arrived as HTTP 200, and HTTP 503 for a server error in that body. Messages sends temperature and drops top_p when both are set.
Full changelog
v0.11.11
wiremux 0.11.11
Highlights
When a model calls two tools, each tool receives the arguments that belong to it.
A Chat tool result keeps its output when a cache breakpoint wraps an image or a document instead of text.
Bug fixes
-
Parallel tool calls mixed their arguments. When two calls were announced and the argument text arrived afterward, the first call was empty and both payloads were appended to the second call. Each argument stays on the call that owns it (#724).
-
A request that was only a system cache checkpoint encoded as an empty message list. Chat and Messages reject that request. Responses names the dropped checkpoint. Converse says the system block still needs a message turn. A Messages request with no items is still an empty list (#724).
-
A Chat tool result lost its output when a cache breakpoint wrapped an image, document, video, or search result. The original tool output is sent. Nested text still uses the text parts (#720).
-
A Responses tool result whose only part was JSON was sent as an empty string. The JSON text is sent. A later breakpoint that adds no parts no longer clears an image already kept on that result (#722).
-
Messages wrote
"model": ""when the destination model was empty. The key is omitted on the complete body and on the stream start frame. A named destination model is still written (#722, #723).
Upgrading
Set wiremux and wiremux-auth to 0.11.11. No public struct or enum field was added, so existing literals still compile.
A caller that maps parallel tool calls now sees each argument on the call that owns it. A Messages request or stream with an empty destination model omits model instead of sending an empty string.
Full changelog
v0.11.10
wiremux 0.11.10
Highlights
A tool result or system prompt wrapped in more than one cache breakpoint keeps its text.
Converse no longer puts a "." in front of tool text that sits inside a breakpoint.
Bug fixes
-
Chat and Gemini dropped tool text wrapped in two cache breakpoints. The text stays. Chat writes the outer breakpoint mode and records each inner mode as a drop. Gemini records every layer and still sends the tool text or inline image (#713).
-
Messages, Gemini, and Converse sent an empty system prompt when two cache breakpoints wrapped the text. The system text stays (#715).
-
Converse sent "." and then the real tool text when a cache breakpoint wrapped that text. The tool result is the text alone. A tool result that is actually empty still becomes "." (#716).
-
A Chat tool image inside a cache breakpoint disappeared with an empty loss log. The image is still omitted, because Chat tool messages have no image slot. The loss log records that drop and the breakpoint mode (#717).
Upgrading
Set wiremux and wiremux-auth to 0.11.10. No public struct or enum field was added, so existing literals still compile.
A caller that encoded nested cache breakpoints around tool text or a system prompt now sees that text on the wire. A Converse tool result no longer starts with a "." when the text was inside a breakpoint.
Full changelog
v0.11.9
wiremux 0.11.9
Highlights
A token cap of 0 is now an error on Chat, Responses, and Gemini, the same as Messages and Converse.
Chat and Responses no longer crash when a cache breakpoint wraps thinking, or when two breakpoints wrap text.
Bug fixes
-
Chat, Responses, and Gemini sent a token cap of 0.
max_tokens,max_completion_tokens,max_output_tokens, andgenerationConfig.maxOutputTokensof 0 are now the errormax_tokens must be at least 1, and no request body is produced. An omitted cap stays omitted. A value of 1 is still sent (#708). -
Chat and Responses crashed when a cache breakpoint wrapped thinking. Two nested breakpoints around text crashed the same way. Encode peels every breakpoint layer. Thinking is dropped the same way a top-level thinking part is dropped, and the text inside nested breakpoints is still sent (#709).
-
tokio1.54.0 andctutils0.4.2 were still in the lockfile. Those crates are now 1.54.1 and 0.4.4 (RUSTSEC-2026-0384 and RUSTSEC-2026-0385) (#706).
Upgrading
Set wiremux and wiremux-auth to 0.11.9. No public struct or enum field was added, so existing literals still compile.
A caller that set max_tokens to 0 on Chat, Responses, or Gemini now gets MapError instead of a request body. Messages still fills an omitted cap with 4096. Chat, Responses, and Gemini still leave an omitted cap off the request.
Full changelog
v0.11.8
wiremux 0.11.8
Highlights
Image, audio, and document parts that are only whitespace are no longer sent. Responses include is written only when you set sampling.include.
Out-of-range temperature, top_p, Chat frequency and presence penalties, Messages and Converse max_tokens of 0, and Chat n of 0 are rejected before the request is sent.
Bug fixes
-
Responses always sent
include: ["reasoning.encrypted_content"]. The field is omitted whensampling.includeis empty. Other entries are written as you set them, andreasoning.encrypted_contentis included only when that list already contains it (#687). -
OpenAI still received fields those models reject. Chat omits
stopono3ando4-mini, and omitslogprobsongpt-5,gpt-5-mini, andgpt-5-nano.gpt-5.1andgpt-5.2omitlogprobsunless reasoning effort isnone. Responses omitstemperature,top_p, andtop_logprobsunder that same gpt-5 rule. Each omission is recorded as a drop (#688). -
A system or developer
content_partslist lost its text on Messages, Gemini, and Converse. Those wires now send the text parts from the list. Image, document, audio, and raw parts in that list are dropped and recorded (#688). -
A Chat host never saw a Converse validation error. A cross-wire failure body that wiremux recognizes is rewritten into the caller's dialect, and the upstream status stays, including 5xx. Same-wire errors stay raw. An HTTP 200 vendor error still becomes 400 (#689).
-
Two different tool ids became one Messages tool id. Characters other than letters, digits,
_, and-still become_, and a run of them is one_. A second id that lands on the same string gets_2, then_3. An id that already contains__keeps both underscores (#690). -
A Gemini tool image or tool text inside a cache breakpoint was dropped. The image and the text stay on the Gemini request. Other wires are unchanged (#691, #692).
-
A blank document file id was still sent. Chat, Responses, Messages, and Gemini do not send a document file id that is empty or only spaces (#693, #694).
-
A whitespace image URL was sent, and Gemini wrote
[image: ]. A URL that is empty after trimming is omitted on Chat, Responses, Messages, and Gemini. A non-empty https URL is still the Gemini text placeholder (#695). -
Blank image bytes, audio data, and document bytes were still uploaded. A payload that is empty after trimming is omitted, including a data URL whose base64 payload is only spaces. A non-empty payload is unchanged. Messages has no audio slot (#696).
-
An empty stop string was sent. Chat, Messages, Gemini, and Converse omit an empty stop entry and keep any remaining entry. A stop entry that is only spaces is still sent. Responses still has no stop field.
o3ando4-ministill omitstopfor every entry (#697). -
Messages sent
max_tokens: 0. That value is now an error,max_tokens must be at least 1. An omittedmax_tokensstill becomes 4096, with the existing record that Messages requiresmax_tokens(#698). -
Converse sent
maxTokens: 0, and temperature ortopPoutside 0 to 1.max_tokensof 0 is an error. Temperature and top_p outside 0 to 1, including a value that is not finite, are errors (#699, #700). -
Chat and Responses sent temperature above 2 and top_p above 1. When the field would be sent, temperature must be from 0 to 2 and top_p from 0 to 1. Messages uses 0 to 1 for both.
gpt-5,gpt-5-mini,gpt-5-nano, andgpt-5.1orgpt-5.2with a reasoning effort other thannonestill omit those fields instead of returning this error (#701). -
Chat sent
frequency_penaltyorpresence_penaltyoutside -2 to 2. Those values are errors, including on models where temperature is omitted (#702). -
Chat sent
n: 0. That value is an error,n must be at least 1.nof 1 is still sent (#703). -
Newer Claude models were sent a forced tool choice. For
claude-opus-5-5,claude-sonnet-5-5,claude-fable-5-1, andclaude-mythos-5-1, Messagestoolandanyare sent asauto, and the loss detail ismodel forces tool_choice auto. A dated suffix is the same family. A same-wire Messages client can put the original choice back withrestore_same_wire_messages_tool_choice(#684). -
rustls-webpki0.103.15 still trusted emSign intermediates. The lockfile is now 0.103.16 (RUSTSEC-2026-0382) (#687).
Upgrading
Set wiremux and wiremux-auth to 0.11.8. No public struct or enum field was added, so existing literals still compile.
Encoded JSON omits the blank parts and rejected fields above, and out-of-range sampling returns MapError instead of a vendor status. A same-wire Messages caller that still needs tool or any on the Claude families listed above should call restore_same_wire_messages_tool_choice after encode.
Gemini still sends maxOutputTokens of 0. Chat still sends logit_bias, and it still sends n of 1 or greater. Gemini still sends frequency and presence penalties.
Full changelog
v0.11.7
wiremux 0.11.7
Highlights
A same-wire Chat stream keeps the vendor error type and code. A model list that is only an error object uses the vendor message.
A response.failed frame that still has assistant text, a tool call, or audio returns that vendor error instead of the text.
Bug fixes
-
A Chat stream error stored the vendor code in
error.type. When the client setstreamto true and the upstream HTTP 200 body was only an error object,error.codedisappeared. Theevent: errordata is that JSON, so both fields stay. A non-stream request is HTTP 400 with the same JSON. A Responses stream still uses the flat error event, withmessageandcodeat the top (#683). -
A model list error looked like an empty catalog. An error object with no
dataormodelsarray now uses the vendor message.data: nullwithout an error object is still the generic catalog error (#681). -
response.failedreturned assistant text and hid the vendor error. A frame with assistant text, a tool call, or audio, including a type-only frame, returns the vendor error. Encrypted reasoning and a hosted item with an id are still kept. The proxy returns the vendor error and does not forward that text as the assistant answer (#679). -
The proxy example said every request path is ignored. It now names
/count_tokensand:countTokens, and it says a same-wire Chat error object stays that JSON (#682).
Upgrading
Set wiremux and wiremux-auth to 0.11.7. Existing call sites do not need changes. A host that read error.type on a same-wire Chat stream error now sees the vendor type, not the code. A host that treated assistant text on response.failed as the answer now gets the vendor error when that text, a tool call, or audio is present.
Full changelog
v0.11.6
wiremux 0.11.6
Highlights
A streaming Responses failure keeps encrypted reasoning and a hosted tool payload instead of dropping them.
The proxy sends a header API key, including the shipped OpenRouter Codex profile, and a catalog ingest no longer leaves a partial write behind.
Bug fixes
-
A streaming
response.failedframe dropped encrypted reasoning. The client returnedplease waitand no events, even whenencrypted_contentwas set. That payload, and a hosted item that carries an id or other fields, is now delivered. Assistant text, a tool call, audio, or an empty shell on that frame still returns the vendor message (#672). -
A non-JSON 200 looked like a JSON parse error.
sendandlist_modelsnow return a vendor error whose message is the page text. A stream that never becomes a frame uses that same vendor error (#672). -
An error object hid encrypted reasoning and a hosted payload. Non-empty
encrypted_contentstays part of the answer, including on a failed response. An empty encrypted string does not. A hosted item with an id stays part of the answer. An empty message, empty output text, empty audio, and a hosted item that is only a type still return the vendor message (#669). -
The proxy returned 401 for a header API key.
auth statusalready showed a token for shippedopenrouter-codex, andwiremux proxystopped before the upstream call. The proxy now sends thatAuthorizationorx-api-keyheader. A credentials file written after the proxy starts is used on the next request. If the token endpoint cannot be reached, the proxy returns 502 instead of 401. The 401 text names[oauth],access_env,[headers] Authorization or x-api-key, andauth_scheme = "none".auth loginuses the same list (#673, #674). -
Ingest left an earlier profile in place when a later write failed. Each profile is written to its own temp file first. Live files are replaced only after every temp write succeeds. A failed temp write removes those temps and leaves the existing profiles unchanged (#675).
Upgrading
Set wiremux and wiremux-auth to 0.11.6. Existing call sites do not need changes. A host that treated every Responses response.failed frame as an error now receives encrypted reasoning and hosted payloads when the frame carries them. A non-JSON HTTP 200 from send or list_models is a vendor error, not a JSON parse error.
Full changelog
v0.11.5
wiremux 0.11.5
Highlights
When a response has no text, refusal, audio, or named tool call, you get the vendor's message back.
The proxy returns that message as a client error instead of HTTP 502.
Bug fixes
-
A failed Responses body finished as
failedand dropped the vendor error.decode_responsereturns the vendor message, such asplease wait, when the body has no text, refusal, audio, or named tool call. A real leaf beside the error stays a completion. An SSEresponse.failedframe stays an error (#663). -
The proxy treated an empty Responses output shell as a success. A cross-wire caller received HTTP 502 and the text
decode upstream body:. The proxy now returns the vendor message as a client error (#665). -
An empty Messages text or thinking block hid the vendor error. A blank string counted as a finished answer, so a cross-wire caller received HTTP 502. The proxy returns the vendor message. A non-empty string, a typed citation, or a thinking signature still counts as a completion (#666).
Upgrading
Set wiremux and wiremux-auth to 0.11.5. This release adds no public fields. Callers that treated an empty Responses output shell, or an empty Messages text or thinking block, as a finished answer now receive the vendor error.
Full changelog
v0.11.4
wiremux 0.11.4
Highlights
A Messages image that is only a file id stays on the next Messages request.
An empty part no longer hides a vendor error.
Bug fixes
-
An empty part hid the vendor error. Gemini, Converse, and Messages treated an empty part, an empty output block, or an empty tool or image block as a finished answer. The vendor error is returned. A named tool, image bytes, a file id, a file URI, and a nonempty
stopReasonstill count as a completion (#660). -
A Messages image that was only a file id disappeared on the next request. The text stayed and the picture did not. Same-wire Messages keeps
source.typeoffileandsource.file_id. A Responses request drops that image, and the loss line saysMessages image file_id has no Responses slot(#661). -
Chat image detail was dropped, and an empty image, audio clip, or tool call hid an error.
detailloworhighon a Chat data URL round-trips.autostays omitted. Messages, Gemini, and Converse still receive the bytes and record the detail. An emptyimage_url, an emptyinput_audio, ortool_callswith an empty object no longer hides a vendor error (#658). -
Tool-call arguments that arrived before the name left that call. Those fragments stay on the call. A stream that ends without an id or a name still releases them (#658).
-
Azure model lists requested the wrong URL.
list_modelsGETs{base}/openai/modelsand keepsapi-versionfrom the chat path. A body withdata[].idis the catalog./openai/v1,/api/v3, Gemini, and Vertex URLs are unchanged (#658).
Upgrading
MSRV is still 1.95. Use 0.11.4 from crates.io.
No public struct or enum variant gained a field. A struct literal from 0.11.3 still compiles.
Same-wire Messages now keeps an image whose source type is file and whose file_id is set. A Responses body does not include that block. Decode of an empty Gemini part, an empty Converse output block, or an empty Messages tool or image block returns the vendor error instead of an empty success.