fix(deps): pin openrouter below 0.11 to avoid SSE stream regressions (#373)
* fix(openrouter): address SSE stream leak by closing response iterator * refactor(openrouter): pass through SDK args in SSE leak patch * test(openrouter): make SSE leak tests version-agnostic across SDK generations * fix(openrouter): remove SSE stream leak patch and update dependencies
This commit is contained in:
@@ -79,6 +79,11 @@ def main():
|
||||
warnings.filterwarnings(
|
||||
"ignore", message=".*type is unknown and inference may fail.*"
|
||||
)
|
||||
# v3 streaming is a deliberate choice (#268), so its beta notice is noise.
|
||||
# Matched by message, not category, to keep other beta warnings visible.
|
||||
warnings.filterwarnings(
|
||||
"ignore", message=".*v3 streaming protocol on Pregel is experimental.*"
|
||||
)
|
||||
from ..config import load_config
|
||||
from .commands import _configure_logging
|
||||
|
||||
|
||||
@@ -25,6 +25,8 @@ dependencies = [
|
||||
"langchain-google-genai>=4.2",
|
||||
"langchain-ollama>=1.1",
|
||||
"langchain-openrouter>=0.2.5",
|
||||
# 0.11.0+ SSE regressions: teardown noise + mid-turn ReadTimeout
|
||||
"openrouter>=0.10.8,<0.11.0",
|
||||
"tavily-python>=0.7",
|
||||
"pyyaml>=6.0",
|
||||
"rich>=15.0",
|
||||
|
||||
@@ -381,6 +381,13 @@ class TestSlashModelIdFallback:
|
||||
# =============================================================================
|
||||
|
||||
|
||||
def _sdk_retry_supports_status_codes_override() -> bool:
|
||||
"""openrouter>=0.11 only; the 429 override degrades to a no-op below that."""
|
||||
from openrouter.utils.retries import RetryConfig
|
||||
|
||||
return "status_codes_override" in getattr(RetryConfig, "__annotations__", {})
|
||||
|
||||
|
||||
class TestThirdPartyRouting:
|
||||
@patch("EvoScientist.llm.models.init_chat_model")
|
||||
def test_siliconflow_routes_through_openai(self, mock_init, monkeypatch):
|
||||
@@ -471,6 +478,11 @@ class TestThirdPartyRouting:
|
||||
|
||||
# --- OpenRouter upstream 429 retry ---
|
||||
|
||||
@pytest.mark.skipif(
|
||||
not _sdk_retry_supports_status_codes_override(),
|
||||
reason="openrouter<0.11 RetryConfig lacks status_codes_override; "
|
||||
"the 429 override no-ops there by design (models.py hasattr guard)",
|
||||
)
|
||||
def test_openrouter_429_added_to_retryable_status_codes(self, monkeypatch):
|
||||
"""Upstream 429s must become retryable on the real SDK client.
|
||||
|
||||
@@ -501,6 +513,11 @@ class TestThirdPartyRouting:
|
||||
retry_config = model.client.sdk_configuration.retry_config
|
||||
assert getattr(retry_config, "status_codes_override", None) is None
|
||||
|
||||
@pytest.mark.skipif(
|
||||
not _sdk_retry_supports_status_codes_override(),
|
||||
reason="openrouter<0.11 RetryConfig lacks status_codes_override; "
|
||||
"the 429 override no-ops there by design (models.py hasattr guard)",
|
||||
)
|
||||
def test_openrouter_429_retried_on_the_wire(self, monkeypatch):
|
||||
"""End-to-end: a 429 with Retry-After is retried and the retry succeeds."""
|
||||
import httpx
|
||||
|
||||
@@ -973,6 +973,7 @@ dependencies = [
|
||||
{ name = "lazy-loader" },
|
||||
{ name = "markdownify" },
|
||||
{ name = "nest-asyncio" },
|
||||
{ name = "openrouter" },
|
||||
{ name = "prompt-toolkit" },
|
||||
{ name = "psutil" },
|
||||
{ name = "python-dotenv" },
|
||||
@@ -1083,6 +1084,7 @@ requires-dist = [
|
||||
{ name = "lazy-loader", specifier = ">=0.5" },
|
||||
{ name = "markdownify", specifier = ">=1.2" },
|
||||
{ name = "nest-asyncio", specifier = ">=1.6" },
|
||||
{ name = "openrouter", specifier = ">=0.10.8,<0.11.0" },
|
||||
{ name = "pre-commit", marker = "extra == 'dev'", specifier = ">=3.5.0" },
|
||||
{ name = "prompt-toolkit", specifier = ">=3.0" },
|
||||
{ name = "psutil", specifier = ">=6.0" },
|
||||
@@ -2920,7 +2922,7 @@ wheels = [
|
||||
|
||||
[[package]]
|
||||
name = "openrouter"
|
||||
version = "0.11.44"
|
||||
version = "0.10.8"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "httpcore" },
|
||||
@@ -2928,9 +2930,9 @@ dependencies = [
|
||||
{ name = "jsonpath-python" },
|
||||
{ name = "pydantic" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/38/66/1c239f07abd22c7eb505e664deb31eee7b9d1451a4f7e7249e60711b42b7/openrouter-0.11.44.tar.gz", hash = "sha256:7a61c42949a96a6c2da2bb7810f7ba99581271c2344deecff34d2bbae9b83157", size = 380246, upload-time = "2026-07-17T20:59:07.669Z" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/38/4c/1fed3f4cbcf0962d8117b54f1740696c98d0dc2e3d5c34459d46effe8a3b/openrouter-0.10.8.tar.gz", hash = "sha256:ac7f4cea1ec1c2a063b0ad9792a8f64dde68ed053279b6c42bee849a50afc47c", size = 340259, upload-time = "2026-06-28T15:33:38.067Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/08/c4/1572fff0d9169c0aa849ebaa7d492508eb2d4d583b8b5caf9a5a97760123/openrouter-0.11.44-py3-none-any.whl", hash = "sha256:b787f132a00fe058d8772985d5ec25dfa446b2ad00309be03143f8d8f5b5371b", size = 837128, upload-time = "2026-07-17T20:59:06.287Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/15/ae/76fc8ad57f6717e9231fbd7313e5623733d86f1a2149baa458772164f456/openrouter-0.10.8-py3-none-any.whl", hash = "sha256:ff9b3cd2f55a3f905d21f56517344da9f9556ffbd2cb9d31ddc388c4b930aab6", size = 748497, upload-time = "2026-06-28T15:33:36.627Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
||||
Reference in New Issue
Block a user