File size: 4,510 Bytes
456cef9
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
#!/usr/bin/env python3
"""基于环境变量生成 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:  # noqa: BLE001
        print(f"生成配置文件时出现错误: {exc}", file=sys.stderr)
        sys.exit(1)