Coverage for src\genesis\inference\factory.py: 98%

35 statements  

« prev     ^ index     » next       coverage.py v7.15.4, created at 2026-08-26 14:20 +0800

1"""真实 InferenceEngine 工厂:读取环境变量/.env 构造 HttpLLMClient + InferenceEngine。 

2 

3设计要点: 

4- 不硬编码任何密钥;优先级 GENESIS_INFERENCE__* 环境变量 > 裸 DEEPSEEK_API_KEY/LLM_BASE_URL > 默认值。 

5- 自动加载仓库根目录 .env(被 .gitignore 忽略,密钥不入库)。 

6- engine=None 时由 orchestrator/qa_loop 调用,接通 P5-T10 人工质量门禁的真实 LLM 路径。 

7""" 

8from __future__ import annotations 

9 

10import os 

11from pathlib import Path 

12 

13from genesis.config import InferenceModels, ModelSpec 

14from genesis.inference.client import HttpLLMClient 

15from genesis.inference.engine import InferenceEngine 

16from genesis.inference.exceptions import LLMNotConfiguredError 

17 

18_DEFAULT_BASE_URL = "https://api.deepseek.com" 

19_DEFAULT_MODEL = "deepseek-chat" 

20_DEFAULT_FALLBACK = "qwen-max" 

21 

22 

23def _load_dotenv(path: Path = Path(".env")) -> None: 

24 """将 .env 中的 KEY=VALUE 注入 os.environ(仅当变量尚未设置时)。""" 

25 if not path.is_file(): 

26 return 

27 for raw in path.read_text(encoding="utf-8").splitlines(): 

28 line = raw.strip() 

29 if not line or line.startswith("#") or "=" not in line: 

30 continue 

31 key, _, val = line.partition("=") 

32 key = key.strip() 

33 val = val.strip() 

34 if len(val) >= 2 and val[0] in "\"'": 

35 if val[-1] == val[0]: 35 ↛ 37line 35 didn't jump to line 37 because the condition on line 35 was always true

36 val = val[1:-1] 

37 os.environ.setdefault(key, val) 

38 

39 

40def build_inference_engine() -> InferenceEngine: 

41 _load_dotenv() 

42 api_key = os.environ.get("GENESIS_INFERENCE__API_KEY") or os.environ.get("DEEPSEEK_API_KEY") 

43 base_url = ( 

44 os.environ.get("GENESIS_INFERENCE__BASE_URL") 

45 or os.environ.get("LLM_BASE_URL") 

46 or _DEFAULT_BASE_URL 

47 ) 

48 model = ( 

49 os.environ.get("GENESIS_INFERENCE__MODEL") 

50 or os.environ.get("LLM_MODEL") 

51 or _DEFAULT_MODEL 

52 ) 

53 fallback = ( 

54 os.environ.get("GENESIS_INFERENCE__FALLBACK_MODEL") 

55 or os.environ.get("LLM_FALLBACK_MODEL") 

56 or _DEFAULT_FALLBACK 

57 ) 

58 if not api_key: 

59 raise LLMNotConfiguredError( 

60 "缺少 LLM API Key:请设置 GENESIS_INFERENCE__API_KEY" 

61 "(或 .env / 环境变量 DEEPSEEK_API_KEY)" 

62 ) 

63 models = InferenceModels( 

64 primary=ModelSpec(name=model), 

65 fallback=ModelSpec(name=fallback), 

66 ) 

67 client = HttpLLMClient(base_url=base_url, api_key=api_key) 

68 return InferenceEngine(client=client, models=models)