From 080a5c06f3264eb5d480ea7988f5b95fc1e34d04 Mon Sep 17 00:00:00 2001 From: X-iZhang Date: Fri, 3 Apr 2026 17:14:19 +0100 Subject: [PATCH] feat: enhance OpenRouter support with additional reasoning handling and model entries --- EvoScientist/llm/models.py | 13 +++++++++---- EvoScientist/stream/events.py | 16 ++++++++++++++-- 2 files changed, 23 insertions(+), 6 deletions(-) diff --git a/EvoScientist/llm/models.py b/EvoScientist/llm/models.py index b48dcf2..42473d9 100644 --- a/EvoScientist/llm/models.py +++ b/EvoScientist/llm/models.py @@ -120,13 +120,18 @@ _MODEL_ENTRIES: list[tuple[str, str, str]] = [ ("kimi-k2.5", "Pro/moonshotai/Kimi-K2.5", "siliconflow"), ("glm-4.7", "Pro/zai-org/GLM-4.7", "siliconflow"), # OpenRouter + ("claude-opus-4.6", "anthropic/claude-opus-4.6", "openrouter"), + ("claude-sonnet-4.6", "anthropic/claude-sonnet-4.6", "openrouter"), ("gpt-5.4", "openai/gpt-5.4", "openrouter"), - ("minimax-m2.5", "minimax/minimax-m2.5", "openrouter"), + ("gpt-5.3-codex", "openai/gpt-5.3-codex", "openrouter"), + ("gemini-3.1-pro", "google/gemini-3.1-pro-preview", "openrouter"), + ("gemini-3-flash", "google/gemini-3-flash-preview", "openrouter"), + ("kimi-k2.5", "moonshotai/kimi-k2.5", "openrouter"), + ("glm-5v-turbo", "z-ai/glm-5v-turbo", "openrouter"), + ("minimax-m2.7", "minimax/minimax-m2.7", "openrouter"), + ("mimo-v2-pro", "xiaomi/mimo-v2-pro", "openrouter"), ("grok-4.1-fast", "x-ai/grok-4.1-fast", "openrouter"), ("qwen3.5-122b", "qwen/qwen3.5-122b-a10b", "openrouter"), - ("gemini-3-flash", "google/gemini-3-flash-preview", "openrouter"), - ("claude-sonnet-4.6", "anthropic/claude-sonnet-4.6", "openrouter"), - ("glm-5-turbo", "z-ai/glm-5-turbo", "openrouter"), # Zhipu CodePlan (智谱代码计划 — coding-only endpoint) ("glm-5.1", "glm-5.1", "zhipu-code"), ("glm-5", "glm-5", "zhipu-code"), diff --git a/EvoScientist/stream/events.py b/EvoScientist/stream/events.py index 92a3fec..75951f5 100644 --- a/EvoScientist/stream/events.py +++ b/EvoScientist/stream/events.py @@ -735,10 +735,21 @@ def _process_chunk_content( """Process content blocks from an AI message chunk.""" content = chunk.content + # OpenRouter (langchain-openrouter) stores reasoning in + # additional_kwargs["reasoning_content"] instead of content blocks. + _additional = getattr(chunk, "additional_kwargs", None) or {} + _rc = _additional.get("reasoning_content") + _emitted_thinking = False + if _rc and isinstance(_rc, str): + yield emitter.thinking(_rc) + _emitted_thinking = True + if isinstance(content, str): if content: cleaned = _strip_legacy_thinking_tags(content) - if cleaned: + # Skip whitespace-only text (OpenRouter may send '\n\n' before + # reasoning chunks, which would prematurely trigger is_responding). + if cleaned and not cleaned.isspace(): yield emitter.text(cleaned) return @@ -771,7 +782,8 @@ def _process_chunk_content( if block_type in ("thinking", "reasoning"): thinking_text = block.get("thinking") or block.get("reasoning") or "" - if thinking_text: + # Skip if already emitted from additional_kwargs (avoid duplicates) + if thinking_text and not _emitted_thinking: yield emitter.thinking(thinking_text) elif block_type == "text":