Skip to content

feat(session): expose LLM response headers on assistant messages#26090

Closed
jtbnz wants to merge 5 commits into
anomalyco:devfrom
jtbnz:feat/expose-response-headers
Closed

feat(session): expose LLM response headers on assistant messages#26090
jtbnz wants to merge 5 commits into
anomalyco:devfrom
jtbnz:feat/expose-response-headers

Conversation

@jtbnz

@jtbnz jtbnz commented May 6, 2026

Copy link
Copy Markdown

Issue for this PR

Closes #26091

Type of change

  • Bug fix
  • New feature
  • Refactor / code improvement
  • Documentation

What does this PR do?

When using a LiteLLM proxy with an auto router, the actual model selected is only in the HTTP response headers (x-litellm-model-api-base, llm_provider-x-ms-deployment-name). The AI SDK captures these on finish-step events via value.response.headers, but processor.ts discards them.

This adds an optional responseHeaders field to the Assistant message schema and captures value.response.headers in the finish-step handler. The pattern already exists for APIError in the same file.

Two files changed:

  • packages/opencode/src/session/message-v2.ts — added responseHeaders: Schema.optional(Schema.Record(Schema.String, Schema.String))
  • packages/opencode/src/session/processor.ts — read value.response.headers when non-empty and store on the assistant message

The field is optional and messages are stored as JSON, so no migration is needed and existing messages are unaffected. Providers that don't return headers simply won't have the field set.

Example plugin using this: opencode-routed-model

How did you verify your code works?

  • bun turbo typecheck — all 12 packages pass
  • bun test test/session/ from packages/opencode — 296 pass, 0 fail
  • Manual test against a live LiteLLM proxy with complexity router: confirmed x-litellm-model-api-base and llm_provider-x-ms-deployment-name headers appear on the assistant message and are visible to plugins via message.updated bus events

Screenshots / recordings

Not a UI change.

Checklist

  • I have tested my changes locally
  • I have not included unrelated changes in this PR

@github-actions github-actions Bot added needs:compliance This means the issue will auto-close after 2 hours. and removed needs:compliance This means the issue will auto-close after 2 hours. labels May 6, 2026
@github-actions

github-actions Bot commented May 6, 2026

Copy link
Copy Markdown
Contributor

Thanks for updating your PR! It now meets our contributing guidelines. 👍

@jtbnz

jtbnz commented Jun 2, 2026

Copy link
Copy Markdown
Author

Update: rebased and restructured to follow the LLMEvent pipeline

After reviewing the codebase architecture, the original approach of reading value.response.headers directly in processor.ts was bypassing the established data flow. This update restructures the change to follow the same pattern used by providerMetadata.

What changed:

  • packages/llm/src/schema/events.ts — added responseHeaders to the StepFinish event schema (the correct entry point for data flowing from providers)
  • packages/opencode/src/session/llm/ai-sdk.ts — captures event.response?.headers from the AI SDK finish-step event and passes it into the stepFinish LLM event
  • packages/opencode/src/session/message-v2.tsresponseHeaders field on the Assistant schema unchanged in intent; also fixed a stray .annotate() call left by a bad merge
  • packages/opencode/src/session/processor.ts — updated to read value.responseHeaders (from the LLM event) instead of value.response.headers (AI SDK raw)

Verified: bun turbo typecheck — all 19 packages pass. Built and smoke-tested locally.

Adds an optional responseHeaders field to the Assistant message schema
and populates it from the HTTP response headers captured on each
finish-step event. This lets plugins (e.g. a LiteLLM auto-router)
read headers such as x-litellm-model-api-base to identify which model
was actually selected behind the proxy.

Headers flow through the shared LLMEvent pipeline (StepFinish schema ->
ai-sdk adapter -> processor) following the same pattern as providerMetadata.
The field is optional so existing messages and providers are unaffected.
@jtbnz jtbnz force-pushed the feat/expose-response-headers branch from da31490 to 6352455 Compare June 2, 2026 01:16
@github-actions

github-actions Bot commented Jun 6, 2026

Copy link
Copy Markdown
Contributor

Automated PR Cleanup

Thank you for contributing to opencode.

Due to the high volume of PRs from users and AI agents, we periodically close older PRs using automated criteria so maintainers can focus review time on the most active and community-supported contributions.

This PR was closed because it matched the following cleanup criteria:

  • The PR was created more than 1 month ago
  • The PR had fewer than 2 positive reactions
  • Positive reactions are counted as thumbs-up, heart, celebration, or rocket reactions on the PR

PRs created within the last month are not affected by this cleanup.

If you believe this PR was closed incorrectly, or if you are still actively working on it, please leave a comment explaining why it should be reopened. A maintainer can review and reopen it if appropriate.

Thanks again for taking the time to contribute.

@jtbnz

jtbnz commented Jun 6, 2026

Copy link
Copy Markdown
Author

Please reopen, as I am still waiting on this functionality to expose model selected when using an auto router model. as model costs increase it is more important for me to make sure the right model is selected for each task. I have kept this uptodate with the dev branch.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

LLM response headers are discarded, preventing plugins from accessing proxy routing metadata

1 participant