返回 ViMax
config.py
根目录 / agent_runtime / config.py
1 from __future__ import annotations
2
3 import os
4 from functools import lru_cache
5 from pathlib import Path
6 from typing import Any
7
8 import yaml
9 from utils.image_selection import image_candidate_count_from_config
10
11 DEFAULT_LLM_MODEL = "gpt-5.5"
12 DEFAULT_LLM_MODEL_PROVIDER = "openai"
13 DEFAULT_LLM_BASE_URL = "https://yunwu.ai/v1"
14 DEFAULT_IMAGE_MODEL = "gemini-3.1-flash-image-preview"
15 DEFAULT_IMAGE_BASE_URL = "https://yunwu.ai"
16 DEFAULT_VIDEO_MODEL = "veo3.1-fast"
17 DEFAULT_VIDEO_BASE_URL = "https://openrouter.ai/api/v1"
18 DEFAULT_EMBEDDING_MODEL = "text-embedding-3-small"
19 DEFAULT_EMBEDDING_MODEL_PROVIDER = "openai"
20 DEFAULT_RERANKER_MODEL = "BAAI/bge-reranker-v2-m3"
21
22
23 @lru_cache(maxsize=4)
24 def load_agent_config(workspace_root: str | Path = ".") -> dict[str, Any]:
25 path = Path(workspace_root).resolve() / "configs" / "agent.local.yaml"
26 if not path.exists():
27 return {}
28 try:
29 payload = yaml.safe_load(path.read_text(encoding="utf-8")) or {}
30 except yaml.YAMLError as exc:
31 raise RuntimeError(f"Invalid configs/agent.local.yaml: {exc}") from exc
32 if not isinstance(payload, dict):
33 raise RuntimeError("configs/agent.local.yaml must be a YAML mapping")
34 return payload
35
36
37 def config_value(section: str, key: str, env_names: list[str], default: str = "", workspace_root: str | Path = ".") -> str:
38 for env_name in env_names:
39 value = os.environ.get(env_name)
40 if value:
41 return value
42 section_payload = load_agent_config(workspace_root).get(section, {})
43 if isinstance(section_payload, dict):
44 value = section_payload.get(key)
45 if isinstance(value, str) and value:
46 return value
47 return default
48
49
50 def llm_model(workspace_root: str | Path = ".") -> str:
51 return config_value("llm", "model", ["VIMAX_LLM_MODEL"], DEFAULT_LLM_MODEL, workspace_root)
52
53
54 def llm_model_provider(workspace_root: str | Path = ".") -> str:
55 return config_value("llm", "model_provider", ["VIMAX_LLM_MODEL_PROVIDER"], DEFAULT_LLM_MODEL_PROVIDER, workspace_root)
56
57
58 def llm_base_url(workspace_root: str | Path = ".") -> str:
59 return config_value("llm", "base_url", ["VIMAX_LLM_BASE_URL"], DEFAULT_LLM_BASE_URL, workspace_root)
60
61
62 def llm_api_key(workspace_root: str | Path = ".") -> str:
63 return config_value("llm", "api_key", ["VIMAX_LLM_API_KEY", "VIMAX_API_KEY"], "", workspace_root)
64
65
66 def image_model(workspace_root: str | Path = ".") -> str:
67 return config_value("image", "model", ["VIMAX_IMAGE_MODEL"], DEFAULT_IMAGE_MODEL, workspace_root)
68
69
70 def image_base_url(workspace_root: str | Path = ".") -> str:
71 return config_value("image", "base_url", ["VIMAX_IMAGE_BASE_URL"], DEFAULT_IMAGE_BASE_URL, workspace_root)
72
73
74 def image_api_key(workspace_root: str | Path = ".") -> str:
75 return config_value("image", "api_key", ["VIMAX_IMAGE_API_KEY", "VIMAX_LLM_API_KEY", "VIMAX_API_KEY"], llm_api_key(workspace_root), workspace_root)
76
77
78 def image_num_candidates(workspace_root: str | Path = ".") -> int:
79 return image_candidate_count_from_config(load_agent_config(workspace_root))
80
81
82
83 def embedding_model(workspace_root: str | Path = ".") -> str:
84 return config_value("embedding", "model", ["VIMAX_EMBEDDING_MODEL"], DEFAULT_EMBEDDING_MODEL, workspace_root)
85
86
87 def embedding_model_provider(workspace_root: str | Path = ".") -> str:
88 return config_value("embedding", "model_provider", ["VIMAX_EMBEDDING_MODEL_PROVIDER"], DEFAULT_EMBEDDING_MODEL_PROVIDER, workspace_root)
89
90
91 def embedding_base_url(workspace_root: str | Path = ".") -> str:
92 return config_value("embedding", "base_url", ["VIMAX_EMBEDDING_BASE_URL"], "", workspace_root)
93
94
95 def embedding_api_key(workspace_root: str | Path = ".") -> str:
96 return config_value("embedding", "api_key", ["VIMAX_EMBEDDING_API_KEY"], "", workspace_root)
97
98
99 def reranker_model(workspace_root: str | Path = ".") -> str:
100 return config_value("reranker", "model", ["VIMAX_RERANKER_MODEL"], DEFAULT_RERANKER_MODEL, workspace_root)
101
102
103 def reranker_base_url(workspace_root: str | Path = ".") -> str:
104 return config_value("reranker", "base_url", ["VIMAX_RERANKER_BASE_URL"], "", workspace_root)
105
106
107 def reranker_api_key(workspace_root: str | Path = ".") -> str:
108 return config_value("reranker", "api_key", ["VIMAX_RERANKER_API_KEY"], "", workspace_root)
109
110
111 def video_model(workspace_root: str | Path = ".") -> str:
112 return config_value("video", "model", ["VIMAX_VIDEO_MODEL"], DEFAULT_VIDEO_MODEL, workspace_root)
113
114
115 def video_base_url(workspace_root: str | Path = ".") -> str:
116 return config_value("video", "base_url", ["VIMAX_VIDEO_BASE_URL"], DEFAULT_VIDEO_BASE_URL, workspace_root)
117
118
119 def video_api_key(workspace_root: str | Path = ".") -> str:
120 return config_value("video", "api_key", ["VIMAX_VIDEO_API_KEY", "VIMAX_LLM_API_KEY", "VIMAX_API_KEY"], llm_api_key(workspace_root), workspace_root)
121
122
123 def api_provider_from_base_url(base_url: str) -> str:
124 normalized = base_url.strip().lower()
125 if "openrouter.ai" in normalized:
126 return "openrouter"
127 if "yunwu.ai" in normalized:
128 return "yunwu"
129 return ""
130
131
132 def video_provider(workspace_root: str | Path = ".") -> str:
133 """Infer the video API relay/provider from video.base_url.
134
135 This is not a model provider setting. OpenRouter/Yunwu are transport/API
136 gateways here, so users should configure base_url and let the adapter pick
137 the matching implementation.
138 """
139 return api_provider_from_base_url(video_base_url(workspace_root))
140
140 lines PYTHON