mirror of
https://github.com/hwchase17/langchain.git
synced 2026-06-09 10:17:00 +00:00
Add a first-party `langchain-openrouter` partner package (`ChatOpenRouter`) that wraps the official `openrouter` Python SDK, providing native support for OpenRouter-specific features that `ChatOpenAI` intentionally does not handle. Also adds scope-clarifying docstrings to `ChatOpenAI` / `BaseChatOpenAI` warning users away from using `base_url` overrides with third-party providers. --- Closes #31325 Closes #32967 Closes #32977 Closes #32981 Closes #33643 Closes #33757 Closes #34056 Closes #34797 Closes #34962 Supersedes #33902, #34867 (thank you @elonfeng and @okamototk for your initial work on this!) --- Bugs with upstream sdk: - https://github.com/OpenRouterTeam/python-sdk/issues/38 - https://github.com/OpenRouterTeam/python-sdk/issues/51 - https://github.com/OpenRouterTeam/python-sdk/issues/52
121 lines
3.1 KiB
Python
121 lines
3.1 KiB
Python
"""Standard integration tests for `ChatOpenRouter`."""
|
|
|
|
from langchain_core.messages import AIMessage, AIMessageChunk
|
|
from langchain_tests.integration_tests import ChatModelIntegrationTests
|
|
|
|
from langchain_openrouter.chat_models import ChatOpenRouter
|
|
|
|
MODEL_NAME = "openai/gpt-4o-mini"
|
|
|
|
|
|
class TestChatOpenRouter(ChatModelIntegrationTests):
|
|
"""Test `ChatOpenRouter` chat model."""
|
|
|
|
@property
|
|
def chat_model_class(self) -> type[ChatOpenRouter]:
|
|
"""Return class of chat model being tested."""
|
|
return ChatOpenRouter
|
|
|
|
@property
|
|
def chat_model_params(self) -> dict:
|
|
"""Parameters to create chat model instance for testing."""
|
|
return {
|
|
"model": MODEL_NAME,
|
|
"temperature": 0,
|
|
}
|
|
|
|
@property
|
|
def returns_usage_metadata(self) -> bool:
|
|
# Don't want to implement tests for now
|
|
return False
|
|
|
|
@property
|
|
def supports_json_mode(self) -> bool:
|
|
return False
|
|
|
|
@property
|
|
def supports_image_inputs(self) -> bool:
|
|
return True
|
|
|
|
@property
|
|
def supports_image_urls(self) -> bool:
|
|
return True
|
|
|
|
@property
|
|
def supports_video_inputs(self) -> bool:
|
|
return True
|
|
|
|
@property
|
|
def model_override_value(self) -> str:
|
|
return "openai/gpt-4o"
|
|
|
|
|
|
AUDIO_MODEL = "google/gemini-2.5-flash"
|
|
REASONING_MODEL = "openai/o3-mini"
|
|
|
|
|
|
class TestChatOpenRouterMultiModal(ChatModelIntegrationTests):
|
|
"""Tests for audio input and reasoning output capabilities.
|
|
|
|
Uses an audio-capable model as the base and creates separate model
|
|
instances for reasoning tests.
|
|
"""
|
|
|
|
@property
|
|
def chat_model_class(self) -> type[ChatOpenRouter]:
|
|
return ChatOpenRouter
|
|
|
|
@property
|
|
def chat_model_params(self) -> dict:
|
|
return {
|
|
"model": AUDIO_MODEL,
|
|
"temperature": 0,
|
|
}
|
|
|
|
@property
|
|
def returns_usage_metadata(self) -> bool:
|
|
# Don't want to implement tests for now
|
|
return False
|
|
|
|
@property
|
|
def supports_json_mode(self) -> bool:
|
|
return False
|
|
|
|
@property
|
|
def supports_image_inputs(self) -> bool:
|
|
return True
|
|
|
|
@property
|
|
def supports_image_urls(self) -> bool:
|
|
return True
|
|
|
|
@property
|
|
def supports_audio_inputs(self) -> bool:
|
|
return True
|
|
|
|
@property
|
|
def supports_video_inputs(self) -> bool:
|
|
return True
|
|
|
|
@property
|
|
def model_override_value(self) -> str:
|
|
return "openai/gpt-4o"
|
|
|
|
def invoke_with_reasoning_output(self, *, stream: bool = False) -> AIMessage:
|
|
"""Invoke a reasoning model to exercise reasoning token tracking."""
|
|
llm = ChatOpenRouter(
|
|
model=REASONING_MODEL,
|
|
reasoning={"effort": "medium"},
|
|
)
|
|
prompt = (
|
|
"Explain the relationship between the 2008/9 economic crisis and "
|
|
"the startup ecosystem in the early 2010s"
|
|
)
|
|
if stream:
|
|
full: AIMessageChunk | None = None
|
|
for chunk in llm.stream(prompt):
|
|
full = chunk if full is None else full + chunk # type: ignore[assignment]
|
|
assert full is not None
|
|
return full
|
|
return llm.invoke(prompt)
|