From d05053ed60dc14e38fc3bd7a29932615c6f6fc74 Mon Sep 17 00:00:00 2001 From: shashank-100 Date: Sun, 6 Sep 2026 13:13:27 +0530 Subject: [PATCH] fix(openrouter): pass custom fields through SDK extra_body --- browser_use/llm/openrouter/chat.py | 4 ++-- tests/ci/models/test_llm_openrouter.py | 32 ++++++++++++++++++++++++++ 2 files changed, 34 insertions(+), 2 deletions(-) diff --git a/browser_use/llm/openrouter/chat.py b/browser_use/llm/openrouter/chat.py index eaa68190c..899b382e4 100644 --- a/browser_use/llm/openrouter/chat.py +++ b/browser_use/llm/openrouter/chat.py @@ -167,7 +167,7 @@ class ChatOpenRouter(BaseChatModel): top_p=self.top_p, seed=self.seed, extra_headers=extra_headers, - **(self.extra_body or {}), + extra_body=self.extra_body, ) choice = self._get_first_choice(response) @@ -199,7 +199,7 @@ class ChatOpenRouter(BaseChatModel): type='json_schema', ), extra_headers=extra_headers, - **(self.extra_body or {}), + extra_body=self.extra_body, ) choice = self._get_first_choice(response) diff --git a/tests/ci/models/test_llm_openrouter.py b/tests/ci/models/test_llm_openrouter.py index 333f9df98..3e82bd7e6 100644 --- a/tests/ci/models/test_llm_openrouter.py +++ b/tests/ci/models/test_llm_openrouter.py @@ -1,7 +1,9 @@ """Regression tests for OpenRouter client setup and response handling.""" +import json from unittest.mock import AsyncMock, patch +import httpx import pytest from openai.types.chat import ChatCompletion, ChatCompletionMessage from openai.types.chat.chat_completion import Choice @@ -16,6 +18,36 @@ class Answer(BaseModel): answer: str +@pytest.mark.parametrize('structured', [False, True]) +@pytest.mark.parametrize('extra_body', [None, {}, {'provider': {'order': ['test-provider']}, 'transforms': ['middle-out']}]) +async def test_extra_body_reaches_http_request(structured: bool, extra_body: dict | None): + """Send provider-specific fields through the real SDK into the JSON request body.""" + requests: list[httpx.Request] = [] + + def handle_request(request: httpx.Request) -> httpx.Response: + requests.append(request) + return httpx.Response(200, json=_completion(content='{"answer":"ok"}' if structured else 'ok').model_dump()) + + async with httpx.AsyncClient(transport=httpx.MockTransport(handle_request)) as client: + llm = ChatOpenRouter(model='openai/gpt-4o', api_key='test-key', http_client=client, extra_body=extra_body) + result = await llm.ainvoke([UserMessage(content='question')], output_format=Answer if structured else None) + + assert result.completion == (Answer(answer='ok') if structured else 'ok') + assert len(requests) == 1 + body = json.loads(requests[0].content) + assert body['model'] == 'openai/gpt-4o' + assert body['messages'] == [{'role': 'user', 'content': 'question'}] + assert 'extra_body' not in body + if extra_body: + assert body['provider'] == {'order': ['test-provider']} + assert body['transforms'] == ['middle-out'] + else: + assert 'provider' not in body + assert 'transforms' not in body + if structured: + assert body['response_format']['type'] == 'json_schema' + + def _completion(*, content: str | None = 'ok', choices: bool = True) -> ChatCompletion: return ChatCompletion( id='chatcmpl-test',