|
|
|
|
|
"""基于环境变量生成 Toolify 配置文件。"""
|
|
|
|
|
|
import os
|
|
|
import sys
|
|
|
from pathlib import Path
|
|
|
from typing import Any, List
|
|
|
|
|
|
import yaml
|
|
|
|
|
|
|
|
|
def _str_to_bool(value: str | None, default: bool) -> bool:
|
|
|
"""将字符串环境变量解析为布尔值。"""
|
|
|
if value is None:
|
|
|
return default
|
|
|
normalized = value.strip().lower()
|
|
|
if normalized in {"1", "true", "yes", "y", "on"}:
|
|
|
return True
|
|
|
if normalized in {"0", "false", "no", "n", "off"}:
|
|
|
return False
|
|
|
return default
|
|
|
|
|
|
|
|
|
def _parse_list(env_value: str | None) -> List[str]:
|
|
|
"""将逗号分隔的环境变量解析为字符串列表。"""
|
|
|
if not env_value:
|
|
|
return []
|
|
|
return [item.strip() for item in env_value.split(",") if item.strip()]
|
|
|
|
|
|
|
|
|
def _load_config_from_env() -> Any:
|
|
|
"""尝试从直接提供的 YAML 或 JSON 环境变量加载配置。"""
|
|
|
direct_yaml = os.getenv("TOOLIFY_CONFIG_YAML")
|
|
|
if direct_yaml:
|
|
|
return yaml.safe_load(direct_yaml)
|
|
|
|
|
|
direct_json = os.getenv("TOOLIFY_CONFIG_JSON")
|
|
|
if direct_json:
|
|
|
return yaml.safe_load(direct_json)
|
|
|
|
|
|
return None
|
|
|
|
|
|
|
|
|
def _build_default_config() -> dict:
|
|
|
"""根据基础环境变量构造最小可用配置。"""
|
|
|
openai_api_key = os.getenv("OPENAI_API_KEY")
|
|
|
allowed_keys = _parse_list(os.getenv("CLIENT_ALLOWED_KEYS"))
|
|
|
|
|
|
if not openai_api_key:
|
|
|
raise RuntimeError("缺少 OPENAI_API_KEY 环境变量,无法生成配置。")
|
|
|
|
|
|
if not allowed_keys:
|
|
|
raise RuntimeError("缺少 CLIENT_ALLOWED_KEYS 环境变量,无法生成配置。")
|
|
|
|
|
|
base_url = os.getenv("OPENAI_BASE_URL", "https://api.openai.com/v1").rstrip("/")
|
|
|
models = _parse_list(os.getenv("OPENAI_MODELS", "gpt-4o-mini,gpt-4o"))
|
|
|
if not models:
|
|
|
raise RuntimeError("OPENAI_MODELS 环境变量解析为空,请至少指定一个模型。")
|
|
|
|
|
|
server_port = int(os.getenv("SERVER_PORT", os.getenv("PORT", "8000")))
|
|
|
server_host = os.getenv("SERVER_HOST", "0.0.0.0")
|
|
|
server_timeout = int(os.getenv("SERVER_TIMEOUT", "180"))
|
|
|
|
|
|
features_log_level = os.getenv("FEATURE_LOG_LEVEL", "INFO").upper()
|
|
|
|
|
|
prompt_template = os.getenv("FEATURE_PROMPT_TEMPLATE")
|
|
|
if prompt_template:
|
|
|
prompt_template = prompt_template.replace("\\n", "\n")
|
|
|
|
|
|
return {
|
|
|
"server": {
|
|
|
"port": server_port,
|
|
|
"host": server_host,
|
|
|
"timeout": server_timeout,
|
|
|
},
|
|
|
"upstream_services": [
|
|
|
{
|
|
|
"name": os.getenv("OPENAI_NAME", "openai"),
|
|
|
"base_url": base_url,
|
|
|
"api_key": openai_api_key,
|
|
|
"description": os.getenv("OPENAI_DESCRIPTION", "OpenAI Service"),
|
|
|
"is_default": True,
|
|
|
"models": models,
|
|
|
}
|
|
|
],
|
|
|
"client_authentication": {
|
|
|
"allowed_keys": allowed_keys,
|
|
|
},
|
|
|
"features": {
|
|
|
"enable_function_calling": _str_to_bool(os.getenv("FEATURE_ENABLE_FUNCTION_CALLING"), True),
|
|
|
"log_level": features_log_level if features_log_level else "INFO",
|
|
|
"convert_developer_to_system": _str_to_bool(os.getenv("FEATURE_CONVERT_DEVELOPER_TO_SYSTEM"), True),
|
|
|
"prompt_template": prompt_template,
|
|
|
"key_passthrough": _str_to_bool(os.getenv("FEATURE_KEY_PASSTHROUGH"), False),
|
|
|
"model_passthrough": _str_to_bool(os.getenv("FEATURE_MODEL_PASSTHROUGH"), False),
|
|
|
},
|
|
|
}
|
|
|
|
|
|
|
|
|
def main() -> None:
|
|
|
"""入口函数:生成配置文件。"""
|
|
|
config_path = Path(os.getenv("CONFIG_PATH", "/app/config.yaml"))
|
|
|
|
|
|
if config_path.exists():
|
|
|
print(f"配置文件已存在,跳过生成: {config_path}")
|
|
|
return
|
|
|
|
|
|
config_obj = _load_config_from_env()
|
|
|
if config_obj is None:
|
|
|
config_obj = _build_default_config()
|
|
|
|
|
|
if not isinstance(config_obj, dict):
|
|
|
raise ValueError("环境变量提供的配置必须是字典结构。")
|
|
|
|
|
|
config_path.parent.mkdir(parents=True, exist_ok=True)
|
|
|
config_yaml = yaml.safe_dump(config_obj, sort_keys=False, allow_unicode=True)
|
|
|
config_path.write_text(config_yaml, encoding="utf-8")
|
|
|
print(f"已生成配置文件: {config_path}")
|
|
|
|
|
|
|
|
|
if __name__ == "__main__":
|
|
|
try:
|
|
|
main()
|
|
|
except Exception as exc:
|
|
|
print(f"生成配置文件时出现错误: {exc}", file=sys.stderr)
|
|
|
sys.exit(1)
|
|
|
|