- 新增 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%
120 lines
4.5 KiB
Python
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)
|