Feat/ai4sci tool protocol #1

Open
ouyangbo wants to merge 10 commits from feat/ai4sci-tool-protocol into Ai4Sci
4 changed files with 45 additions and 5 deletions
Showing only changes of commit da6ca38d53 - Show all commits
+3 -1
View File
@@ -288,7 +288,9 @@ class EvoScientistConfig:
# a deploy-style langgraph server instead of the in-terminal CLI/TUI.
ui_backend: Literal["cli", "tui", "webui"] = "tui"
log_level: str = "warning"
reasoning_effort: str = "high"
# Empty means use the provider/model default. A non-empty value is an
# explicit user override exported as EVOSCIENTIST_REASONING_EFFORT.
reasoning_effort: str = ""
# Anthropic prompt caching for OpenRouter anthropic/* models. Opt out if
# cache-write costs outweigh the benefit for a workflow.
openrouter_anthropic_prompt_cache: bool = True
+15 -3
View File
@@ -42,6 +42,12 @@ _DEEPSEEK_BASE_URL = "https://api.deepseek.com"
_MOONSHOT_BASE_URL = "https://api.moonshot.cn/v1"
_KIMI_CODING_BASE_URL = "https://api.kimi.com/coding/"
def _resolve_reasoning_effort(default: str) -> str:
"""Return the configured reasoning effort or a provider-specific default."""
return os.environ.get("EVOSCIENTIST_REASONING_EFFORT", "").strip() or default
# Providers routed through the OpenAI provider with a custom base_url.
# Maps provider name → (base_url or None, env var for API key).
_OPENAI_ROUTED_PROVIDERS: dict[str, tuple[str | None, str]] = {
@@ -359,11 +365,17 @@ def _apply_auto_config(
# ccproxy uses Chat Completions which doesn't support reasoning.
pass
else:
_eff = (
_default_effort = (
"xhigh"
if ("5.4" in model_id or "5.5" in model_id or "codex" in model_id)
if (
"5.4" in model_id
or "5.5" in model_id
or "5.6" in model_id
or "codex" in model_id
)
else "high"
)
_eff = _resolve_reasoning_effort(_default_effort)
kwargs["reasoning"] = {"effort": _eff, "summary": "auto"}
# Google GenAI: surface thinking traces
@@ -508,7 +520,7 @@ def get_chat_model(
# passback (OpenRouter's `/responses` beta is stateless, store=false —
# "Item with id 'rs_...' not found"); the patch strips them on passback,
# so enabling `summary` is safe. See langchain-ai/langchain#37777.
effort = os.environ.get("EVOSCIENTIST_REASONING_EFFORT", "").strip() or "high"
effort = _resolve_reasoning_effort("high")
kwargs.setdefault("reasoning", {"effort": effort, "summary": "auto"})
# App attribution (issue #339): identify EvoScientist to OpenRouter so
# usage is credited to the project (app rankings, model app tabs,
+1 -1
View File
@@ -130,7 +130,7 @@ class TestEvoScientistConfig:
assert config.show_thinking is True
assert config.ui_backend == "tui"
assert config.log_level == "warning"
assert config.reasoning_effort == "high"
assert config.reasoning_effort == ""
assert config.openrouter_anthropic_prompt_cache is True
assert config.openrouter_http_referer == (
"https://github.com/EvoScientist/EvoScientist"
+26
View File
@@ -2432,6 +2432,11 @@ class TestPatchOpenrouterStripResponsesReasoning:
class TestAutoConfig:
@pytest.fixture(autouse=True)
def _clear_reasoning_effort_env(self, monkeypatch):
"""Keep auto-config tests independent of the developer environment."""
monkeypatch.delenv("EVOSCIENTIST_REASONING_EFFORT", raising=False)
@patch("EvoScientist.llm.models.init_chat_model")
def test_anthropic_4_5_thinking(self, mock_init, monkeypatch):
"""Anthropic 4-5 models get enabled thinking with budget."""
@@ -2521,6 +2526,7 @@ class TestAutoConfig:
"""gpt-5.4+ and codex models get xhigh reasoning."""
mock_init.return_value = "mock_model"
monkeypatch.delenv("OPENAI_BASE_URL", raising=False)
monkeypatch.delenv("EVOSCIENTIST_REASONING_EFFORT", raising=False)
get_chat_model("gpt-5.4", provider="openai")
assert mock_init.call_args[1]["reasoning"] == {
@@ -2540,6 +2546,26 @@ class TestAutoConfig:
"summary": "auto",
}
get_chat_model("gpt-5.6-sol", provider="openai")
assert mock_init.call_args[1]["reasoning"] == {
"effort": "xhigh",
"summary": "auto",
}
@patch("EvoScientist.llm.models.init_chat_model")
def test_openai_reasoning_effort_from_env(self, mock_init, monkeypatch):
"""Native OpenAI reasoning effort should be configurable via env var."""
mock_init.return_value = "mock_model"
monkeypatch.delenv("OPENAI_BASE_URL", raising=False)
monkeypatch.setenv("EVOSCIENTIST_REASONING_EFFORT", "high")
get_chat_model("gpt-5.5", provider="openai")
assert mock_init.call_args[1]["reasoning"] == {
"effort": "high",
"summary": "auto",
}
@patch("EvoScientist.llm.models.init_chat_model")
def test_openai_reasoning_high_fallback(self, mock_init, monkeypatch):
"""Other OpenAI models get high reasoning effort."""