c2743251e9
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>
155 lines
6.1 KiB
Python
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")
|