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