Files
EvoScientist/tests/test_llm_structured_runtime.py
T
m4 c2743251e9 Initial commit of EvoScientist framework
Self-evolving AI scientist framework built on LangGraph/LangChain with
CLI/TUI core, FastAPI gateway, and Next.js frontend.

Co-Authored-By: Claude Opus 4 <noreply@anthropic.com>
2026-07-13 08:07:45 +08:00

155 lines
6.1 KiB
Python

from __future__ import annotations
import yaml
from unittest.mock import patch
import pytest
@pytest.fixture()
def structured_config(monkeypatch, tmp_path):
settings_path = tmp_path / "settings.yaml"
settings_path.write_text(
yaml.safe_dump(
{
"default_model": "claude-sonnet-4-6",
"providers": {
"hunnu-anthropic": {
"api_key": "sk-hunnu",
"base_url": "https://hunnuapi.top/v1",
"protocol": "anthropic",
"models": [
{
"id": "claude-sonnet-4-6",
"alias": "",
"max_tokens": 4096,
"supports_reasoning": False,
}
],
},
"custom-openai": {
"api_key": "sk-custom",
"base_url": "https://coding.example.com/v1",
"protocol": "openai",
"models": [
{
"id": "qwen3.6-plus",
"alias": "qwen3.6-plus",
"max_tokens": 65536,
"supports_reasoning": True,
"params": {"use_responses_api": False},
}
],
},
},
"model_defaults": {"max_tokens": 2048, "reasoning_effort": "high"},
}
),
encoding="utf-8",
)
monkeypatch.setattr("EvoScientist.config.settings.get_config_path", lambda: settings_path)
monkeypatch.setattr("EvoScientist.config.model_config._get_global_settings_path", lambda: settings_path)
from EvoScientist.llm import models
models._clear_structured_config_cache()
yield settings_path
models._clear_structured_config_cache()
def test_get_chat_model_uses_structured_default_provider(structured_config):
from EvoScientist.llm import get_chat_model
with patch("EvoScientist.llm.models.init_chat_model", return_value="mock_model") as mock_init:
get_chat_model()
call_kwargs = mock_init.call_args[1]
assert call_kwargs["model"] == "claude-sonnet-4-6"
assert call_kwargs["model_provider"] == "anthropic"
assert call_kwargs["api_key"] == "sk-hunnu"
assert call_kwargs["base_url"] == "https://hunnuapi.top"
def test_get_chat_model_routes_by_configured_protocol(structured_config):
from EvoScientist.llm import get_chat_model
with patch("EvoScientist.llm.models.init_chat_model", return_value="mock_model") as mock_init:
get_chat_model("qwen3.6-plus")
call_kwargs = mock_init.call_args[1]
assert call_kwargs["model"] == "qwen3.6-plus"
assert call_kwargs["model_provider"] == "openai"
assert call_kwargs["api_key"] == "sk-custom"
assert call_kwargs["base_url"] == "https://coding.example.com/v1"
assert call_kwargs["max_tokens"] == 65536
assert call_kwargs["use_responses_api"] is False
def test_provider_and_endpoint_params_are_merged(monkeypatch, tmp_path):
settings_path = tmp_path / "settings.yaml"
settings_path.write_text(
yaml.safe_dump(
{
"default_model": "gpt-5.5",
"providers": {
"openai": {
"api_key": "sk-provider",
"base_url": "https://provider.example",
"protocol": "openai",
"params": {"sanitize_openai_sdk_headers": False},
"endpoints": [
{
"name": "sub2api",
"api_key": "sk-endpoint",
"base_url": "http://124.232.163.75:3000",
"params": {"sanitize_openai_sdk_headers": True},
}
],
"models": [
{
"id": "gpt-5.5",
"alias": "gpt-5.5",
"max_tokens": 65000,
"supports_reasoning": True,
"endpoint": "sub2api",
"params": {"use_responses_api": True},
}
],
}
},
}
),
encoding="utf-8",
)
monkeypatch.setattr("EvoScientist.config.settings.get_config_path", lambda: settings_path)
monkeypatch.setattr("EvoScientist.config.model_config._get_global_settings_path", lambda: settings_path)
from EvoScientist.llm import get_chat_model
from EvoScientist.llm import models
models._clear_structured_config_cache()
with patch("EvoScientist.llm.models.init_chat_model", return_value="mock_model") as mock_init:
get_chat_model("gpt-5.5")
models._clear_structured_config_cache()
call_kwargs = mock_init.call_args.kwargs
assert call_kwargs["base_url"] == "http://124.232.163.75:3000"
assert call_kwargs["use_responses_api"] is True
assert type(call_kwargs["http_async_client"]).__name__ == "_DefaultAsyncHttpxClient"
assert "sanitize_openai_sdk_headers" not in call_kwargs
def test_get_chat_model_requires_structured_model(monkeypatch, tmp_path):
settings_path = tmp_path / "settings.yaml"
settings_path.write_text(
yaml.safe_dump({"default_model": "", "providers": {}}),
encoding="utf-8",
)
monkeypatch.setattr("EvoScientist.config.settings.get_config_path", lambda: settings_path)
monkeypatch.setattr("EvoScientist.config.model_config._get_global_settings_path", lambda: settings_path)
from EvoScientist.llm import get_chat_model
from EvoScientist.llm import models
models._clear_structured_config_cache()
with pytest.raises(ValueError, match="No structured LLM providers"):
get_chat_model("gpt-5-mini")