Skip to content

refactor(ai): use OpenAIChat assistant hook and drop OpenRouterBody cast - #57616

Merged
rekram1-node merged 1 commit into
devfrom
openrouter-body-typing
Oct 21, 2026
Merged

rekram1-node merged 1 commit into
devfrom
openrouter-body-typing

Conversation

@rekram1-node

Copy link
Copy Markdown
Collaborator

Problem

OpenRouter lowered assistant reasoning details in a post-processing pass over body.messages using sourceAssistants[assistantIndex++], and cast the returned payload as OpenRouterBody (whose rest record used Schema.Any). When OpenAIChat.fromRequest skipped an empty assistant message during lowering, assistantIndex fell out of alignment with request.messages.

Change

  • Project assistant reasoning and reasoning_details inside OpenAIChat.fromRequest's assistant lowering hook so each lowered assistant message is paired with its exact source Message.
  • Decode known OpenRouter options with lenient(...) schemas and type OpenRouterBody with Options.fields + JsonObject so protocol.body.from returns a strongly-typed OpenRouterBody without as OpenRouterBody.
 protocol.body.from(request)
   OpenAIChat.fromRequest(request, {
     cacheControl: cacheControl(),
+    assistant: (source, message) => attachReasoningDetails(source, message),
   })
-  zip body.messages with sourceAssistants[assistantIndex++]
-  return { ...body, messages, ...bodyOptions(...) } as OpenRouterBody
+  bodyOptions(body, request.providerOptions, request.generation?.maxTokens)
+    decodeOptions(openrouter)
+    return { ...options, ...body, ...knownOptions }

Out of scope

  • Streaming parser behavior (OpenAIChat.protocol.stream is unchanged).

@opencode-agent opencode-agent Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, the reasoning-alignment fix works. I ran the updated OpenRouter test against the base and it fails there, with reasoning coming back undefined. With this PR all OpenRouter tests pass, and packages/ai type-checks.

Public API is unchanged; this is internal to the OpenRouter provider body builder.

One regression to fix before merging. Unknown pass-through options in providerOptions used to override the lowered body, and now the body overrides them. OpenAIChat.fromRequest emits keys like temperature even when their value is undefined, so options such as providerOptions: { temperature: 0.1 } are now silently dropped even when the request doesn't set them (details inline).

? { include: false }
: known.usage
return {
...options,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This flips the precedence between pass-through options and the lowered body. Before, the result was { ...body, messages, ...bodyOptions(...) }, so unknown providerOptions could override body fields. Now ...body comes after ...options. OpenAIChat.fromRequest emits keys such as temperature, top_p and max_tokens even when their value is undefined, so providerOptions: { temperature: 0.1 } now vanishes from the request even though the request never set a temperature. I checked: base sends "temperature": 0.1, and this PR omits it. Spreading ...options after ...body, as before, would keep the old behaviour.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

body should win

@rekram1-node
rekram1-node merged commit a2cb1c4 into dev Oct 21, 2026
22 checks passed
@rekram1-node
rekram1-node deleted the openrouter-body-typing branch October 21, 2026 05:48
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