Skip to content

Conversation

abhishekbhakat
Copy link

@abhishekbhakat abhishekbhakat commented Aug 3, 2025

Native Model for OpenRouter

This document outlines the implementation of a native model for OpenRouter, leveraging the OpenAI SDK internally to improve maintainability.

Reasoning Parameter Structure

The native model handles reasoning parameters differently depending on the provider:

  • OpenAI: Utilizes the openai_reasoning_effort parameter with accepted values: low, medium, or high.
  • OpenRouter: Employs a more granular set of reasoning parameters injected via extra_body.reasoning:
    • openrouter_reasoning_effort
    • openrouter_reasoning_max_tokens
    • openrouter_reasoning_enabled
    • openrouter_reasoning_exclude

Finish Reason Handling

Both models use the same validation pipeline from _openai_compat.py, with one key difference:

  • OpenAI Model: Maps 5 finish reason values: stop, length, tool_calls, content_filter, function_call
  • OpenRouter Model: Maps 6 finish reason values, adding error to handle OpenRouter-specific error states

Unknown finish reasons are gracefully handled by mapping to None rather than raising validation errors.

Error Response Handling

OpenRouter can return HTTP 200 responses with an error field in the body when upstream providers fail. This implementation detects such responses and raises ModelHTTPError appropriately.

closes: #2323

Copy link
Contributor

hyperlint-ai bot commented Aug 3, 2025

PR Change Summary

Added native support for the OpenRouter model in the documentation, enhancing the API's capabilities and providing detailed setup instructions.

  • Introduced the OpenRouterModel in the API documentation.
  • Added installation and configuration instructions for OpenRouter.
  • Updated existing documentation to reference OpenRouter support.

Modified Files

  • docs/models/index.md
  • docs/models/openai.md

Added Files

  • docs/api/models/openrouter.md
  • docs/models/openrouter.md

How can I customize these reviews?

Check out the Hyperlint AI Reviewer docs for more information on how to customize the review.

If you just want to ignore it on this PR, you can add the hyperlint-ignore label to the PR. Future changes won't trigger a Hyperlint review.

Note specifically for link checks, we only check the first 30 links in a file and we cache the results for several hours (for instance, if you just added a page, you might experience this). Our recommendation is to add hyperlint-ignore to the PR to ignore the link check for this PR.

@abhishekbhakat abhishekbhakat marked this pull request as draft August 3, 2025 19:09
@abhishekbhakat abhishekbhakat marked this pull request as ready for review August 4, 2025 03:25
@DouweM
Copy link
Collaborator

DouweM commented Aug 4, 2025

@abhishekbhakat The reasoning parameters can already be set using the ModelSettings.extra_body field, as your code shows. I think it would be good to explicitly document that on under https://ai.pydantic.dev/models/openai/#openai-compatible-models and https://ai.pydantic.dev/models/openai/#openrouter, if you'd be up for up that.

As for the finish_reasons, if OpenRouter's "OpenAI Chat Completions-compatible" API is returning values that are not actually compatible with their spec, that seems like a bug on OpenRouter's side. Do you have an specific example of that that I can report to them? If they officially support additional values, I'd prefer to map those to the OpenAI values and keep the original values on vendor_metadata (along with any other interesting metadata OpenRouter returns).

As it stands, I don't think the slight increase in convenience justifies the additional burden of maintaining this code and keeping it in sync with OpenAIModel. I'd feel differently if this were implemented as a subclass of OpenAIModel, which would require some extra hooks on the base class. Are you interested in looking into that? If you end up doing so, please note that to follow the pattern of the existing model classes, OpenRouterModel should take an instance of the OpenRouterProvider, not the base URL etc directly.

I'll close this PR but feel free to submit a new one that involves less duplication.

@DouweM DouweM closed this Aug 4, 2025
@DouweM DouweM reopened this Sep 17, 2025
@DouweM
Copy link
Collaborator

DouweM commented Sep 17, 2025

@abhishekbhakat Having seen some more OpenRouter issues show up recently, I've changed my mind on this and think we should indeed have a separate OpenRouterModel, even if it involves a lot of duplication of OpenAIChatModel code: #2936

Are you interested in updating this PR to match the latest code in OpenAIChatModel and address the bugs mentioned in that PR (and possibly the features too)?

@abhishekbhakat
Copy link
Author

Yeah. Now I feel that you feel what I feel about this 😂.

I'll update the PR this weekend.

@DouweM
Copy link
Collaborator

DouweM commented Sep 18, 2025

@abhishekbhakat Awesome, thank you! Can you see how we can keep the amount of copy-pasting to a minimum? Subclassing OpenAIChatModel may be an option if we add some hooks to change the behavior. Or if we really need a new class, hopefully we can at least extract the common bits into helper functions.

@abhishekbhakat abhishekbhakat force-pushed the openrouter branch 4 times, most recently from dd9a185 to af72bd8 Compare September 21, 2025 14:09
@abhishekbhakat
Copy link
Author

I took a stab at updating the PR with extracting some common compat. And perhaps, it will be an ongoing effort as and when openrouter starts to drift apart from openai.
There are many pyright issues unrelated to the changes I made.

@abhishekbhakat abhishekbhakat force-pushed the openrouter branch 3 times, most recently from 9fac917 to cc66d80 Compare September 21, 2025 14:47
@abhishekbhakat abhishekbhakat marked this pull request as draft September 21, 2025 14:55
@abhishekbhakat abhishekbhakat marked this pull request as ready for review September 22, 2025 14:09
@abhishekbhakat abhishekbhakat marked this pull request as draft September 22, 2025 14:09
@moritzwilksch
Copy link
Contributor

Thanks for working on this! Just saw that OpenRouter are launching an alpha verison of the responses API so in the future we'll profit from having this dedicated model class :)

@abhishekbhakat abhishekbhakat force-pushed the openrouter branch 3 times, most recently from 00ad298 to dc1bc75 Compare September 26, 2025 19:59
Copy link
Collaborator

@DouweM DouweM left a comment

Choose a reason for hiding this comment

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

@abhishekbhakat Thanks for picking this up again. I haven't done a full review yet, because looking through the diff there seem to be a lot of changes that are not strictly necessary. Can you see if you can reduce the diff so that I can focus on just the bits that were pulled out of OpenAIChatModel, and the new model?

@abhishekbhakat
Copy link
Author

I'm still working through the PR. When I tried to do extract some common functions, at lot of coverage gaps occurred.
I can leave the redundant code so that it will be much cleaner but then if openai changes something it will 2 places which needs to be modified.

PR is not ready for review at this point.

@abhishekbhakat abhishekbhakat force-pushed the openrouter branch 2 times, most recently from 61f898a to 926f64d Compare October 1, 2025 14:20
@abhishekbhakat abhishekbhakat marked this pull request as ready for review October 1, 2025 19:34
@abhishekbhakat abhishekbhakat requested a review from DouweM October 1, 2025 19:35
@abhishekbhakat
Copy link
Author

@DouweM Can you take a look now ?

@abhishekbhakat abhishekbhakat marked this pull request as draft October 1, 2025 20:05
@abhishekbhakat abhishekbhakat marked this pull request as ready for review October 1, 2025 20:43
@abhishekbhakat abhishekbhakat marked this pull request as draft October 1, 2025 20:43
@abhishekbhakat abhishekbhakat marked this pull request as ready for review October 1, 2025 21:18
@DouweM
Copy link
Collaborator

DouweM commented Oct 3, 2025

@abhishekbhakat I'll review this on Monday, sorry for the delay!

@DouweM
Copy link
Collaborator

DouweM commented Oct 7, 2025

@abhishekbhakat What do you think of the implementation at #3089, which is a lot simpler because it subclasses OpenAIChatModel? Do you foresee any OpenRouter-specific features or behavior that subclass-and-override-hooks behavior wouldn't be able to capture?

@abhishekbhakat
Copy link
Author

@abhishekbhakat What do you think of the implementation at #3089, which is a lot simpler because it subclasses OpenAIChatModel? Do you foresee any OpenRouter-specific features or behavior that subclass-and-override-hooks behavior wouldn't be able to capture?

IMO, openrouter being an aggregator OpenAI SDK is a lifesaver for it. And I'm very 50-50 at seeing openrouter diverge from openai. They definitely cannot create a new SDK all together in the forseable future (which is relatively short in the AI terms).

I would say let's proceed with #3089. And we can always revisit if they actually decide to move away.

@DouweM
Copy link
Collaborator

DouweM commented Oct 8, 2025

Closed in favor of #3089

@DouweM DouweM closed this Oct 8, 2025
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.

Handle error response from OpenRouter as exception instead of validation failure

3 participants