Files
2026Technology-Competition/tests/test_inference_factory.py
T
lhl 958e2602cc feat(inference): 接通真实 LLM 引擎工厂(P5-T10 门禁接线)
- 新增 inference/factory.build_inference_engine:读 GENESIS_INFERENCE__* / 裸
  DEEPSEEK_API_KEY·LLM_BASE_URL 环境变量与 .env,构造 HttpLLMClient + InferenceEngine
- orchestrator/qa_loop 的 engine=None 分支改用工厂,真正接通真实 LLM 路径
- 脚本注入校验改为通用(非空段落数 + 残留占位符),适配真实模式
- 补工厂测试(缺密钥/前缀变量/裸变量/默认值/.env 解析),覆盖率 99.04%
2026-08-13 23:11:31 +08:00

120 lines
4.5 KiB
Python

"""真实 InferenceEngine 工厂测试(env 驱动,无需真实密钥)。
覆盖:
- 缺 API Key → 抛 LLMNotConfiguredError
- GENESIS_ 前缀环境变量构造
- 裸 DEEPSEEK_API_KEY / LLM_BASE_URL 兼容
- .env 文件加载
"""
from __future__ import annotations
import os
from pathlib import Path
import pytest
from genesis.inference.exceptions import LLMNotConfiguredError
from genesis.inference.factory import build_inference_engine, _load_dotenv
def _isolate(monkeypatch: pytest.MonkeyPatch) -> None:
"""屏蔽 .env 自动加载与所有相关环境变量,保证测试确定性。"""
monkeypatch.setattr("genesis.inference.factory._load_dotenv", lambda *a, **k: None)
for k in (
"GENESIS_INFERENCE__API_KEY",
"GENESIS_INFERENCE__BASE_URL",
"GENESIS_INFERENCE__MODEL",
"GENESIS_INFERENCE__FALLBACK_MODEL",
"DEEPSEEK_API_KEY",
"LLM_BASE_URL",
"LLM_MODEL",
"LLM_FALLBACK_MODEL",
):
monkeypatch.delenv(k, raising=False)
def test_missing_key_raises(monkeypatch: pytest.MonkeyPatch) -> None:
_isolate(monkeypatch)
with pytest.raises(LLMNotConfiguredError):
build_inference_engine()
def test_builds_from_genesis_env(monkeypatch: pytest.MonkeyPatch) -> None:
_isolate(monkeypatch)
monkeypatch.setenv("GENESIS_INFERENCE__API_KEY", "sk-test")
monkeypatch.setenv("GENESIS_INFERENCE__BASE_URL", "https://llm.example.com")
monkeypatch.setenv("GENESIS_INFERENCE__MODEL", "my-model")
monkeypatch.setenv("GENESIS_INFERENCE__FALLBACK_MODEL", "my-fallback")
eng = build_inference_engine()
assert eng._model_names(None) == ["my-model", "my-fallback"]
assert eng._client._base_url == "https://llm.example.com"
assert eng._client._api_key == "sk-test"
def test_builds_from_bare_env(monkeypatch: pytest.MonkeyPatch) -> None:
_isolate(monkeypatch)
monkeypatch.setenv("DEEPSEEK_API_KEY", "sk-bare")
monkeypatch.setenv("LLM_BASE_URL", "https://bare.example.com")
eng = build_inference_engine()
assert eng._client._api_key == "sk-bare"
assert eng._client._base_url == "https://bare.example.com"
assert eng._model_names(None) == ["deepseek-chat", "qwen-max"]
def test_default_base_url_when_absent(monkeypatch: pytest.MonkeyPatch) -> None:
_isolate(monkeypatch)
monkeypatch.setenv("GENESIS_INFERENCE__API_KEY", "sk-test")
eng = build_inference_engine()
assert eng._client._base_url == "https://api.deepseek.com"
def test_load_dotenv(tmp_path: pytest.TempPathFactory, monkeypatch: pytest.MonkeyPatch) -> None:
_isolate(monkeypatch)
env_file = tmp_path / ".env"
env_file.write_text(
'GENESIS_INFERENCE__API_KEY=sk-dot\n'
'GENESIS_INFERENCE__BASE_URL=https://dot.example.com\n',
encoding="utf-8",
)
_load_dotenv(env_file)
assert os.environ.get("GENESIS_INFERENCE__API_KEY") == "sk-dot"
assert os.environ.get("GENESIS_INFERENCE__BASE_URL") == "https://dot.example.com"
for k in ("GENESIS_INFERENCE__API_KEY", "GENESIS_INFERENCE__BASE_URL"):
monkeypatch.delenv(k, raising=False)
def test_load_dotenv_missing_file_noop(tmp_path: pytest.TempPathFactory, monkeypatch: pytest.MonkeyPatch) -> None:
# 路径不存在时不应抛异常,且不引入任何变量
for k in ("GENESIS_INFERENCE__API_KEY", "GENESIS_INFERENCE__BASE_URL", "GENESIS_INFERENCE__MODEL"):
monkeypatch.delenv(k, raising=False)
_load_dotenv(tmp_path / "nonexistent.env")
assert os.environ.get("GENESIS_INFERENCE__API_KEY") is None
def test_load_dotenv_skips_comments_and_strips_quotes(tmp_path: pytest.TempPathFactory, monkeypatch: pytest.MonkeyPatch) -> None:
for k in (
"GENESIS_INFERENCE__API_KEY",
"GENESIS_INFERENCE__BASE_URL",
"GENESIS_INFERENCE__MODEL",
):
monkeypatch.delenv(k, raising=False)
env_file = tmp_path / ".env"
env_file.write_text(
"# 这是注释行\n"
"GENESIS_INFERENCE__API_KEY=\"sk-quoted\"\n"
"GENESIS_INFERENCE__BASE_URL='https://quoted.example.com'\n"
"MALFORMED_LINE_WITHOUT_EQUALS\n"
"GENESIS_INFERENCE__MODEL=deepseek-chat\n",
encoding="utf-8",
)
_load_dotenv(env_file)
assert os.environ["GENESIS_INFERENCE__API_KEY"] == "sk-quoted"
assert os.environ["GENESIS_INFERENCE__BASE_URL"] == "https://quoted.example.com"
assert os.environ["GENESIS_INFERENCE__MODEL"] == "deepseek-chat"
for k in (
"GENESIS_INFERENCE__API_KEY",
"GENESIS_INFERENCE__BASE_URL",
"GENESIS_INFERENCE__MODEL",
):
monkeypatch.delenv(k, raising=False)