Spaces:
Running
Running
File size: 5,827 Bytes
8a03d2c | 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 128 129 130 131 132 133 134 | """模型配置构建器"""
import json
import time
from typing import Any, cast
from src.core import MODELS_CONFIG_FILE
from ..utils.logger import get_logger
from ..utils.string_utils import snake_to_camel
# 初始化日志
logger = get_logger(__name__)
class ModelConfigBuilder:
"""解析模型名称、处理后缀、构建生成配置"""
_cached_map: dict[str, str] | None = None
_last_load_time: float = 0
def __init__(self) -> None:
# 只有在启动阶段打印
from src.utils.logger import get_request_id
if not get_request_id():
logger.info("模型配置构建器初始化完成", extra={
"model_count": len(self._get_model_map())
})
def _get_model_map(self) -> dict[str, str]:
# 简单缓存机制,每 60 秒检查一次文件更新
current_time = time.time()
if ModelConfigBuilder._cached_map is not None and current_time - ModelConfigBuilder._last_load_time < 60:
return ModelConfigBuilder._cached_map
try:
with open(MODELS_CONFIG_FILE, 'r', encoding='utf-8') as f:
config = json.load(f)
ModelConfigBuilder._cached_map = cast(dict[str, str], config.get('alias_map', {}))
ModelConfigBuilder._last_load_time = current_time
logger.debug("模型配置文件加载成功", extra={
"config_file": MODELS_CONFIG_FILE,
"alias_count": len(ModelConfigBuilder._cached_map)
})
except Exception as e:
logger.warning(f"模型配置文件加载失败,使用空配置", extra={
"config_file": MODELS_CONFIG_FILE,
"error": str(e)
})
if ModelConfigBuilder._cached_map is None:
ModelConfigBuilder._cached_map = {}
return ModelConfigBuilder._cached_map or {}
def parse_model_name(self, model: str) -> str:
"""
解析模型名称,返回 backend_model
"""
return self._get_model_map().get(model, model)
def build_generation_config(
self,
gen_config: dict[str, Any],
gemini_payload: dict[str, Any] | None = None,
**kwargs: Any
) -> dict[str, Any]:
"""构建生成配置"""
# 防止修改原始配置对象
final_config = gen_config.copy()
# 1. 直接合并用户提供的配置
if gemini_payload:
user_gen_config_raw = gemini_payload.get('generationConfig', {}) or gemini_payload.get('generation_config', {})
if user_gen_config_raw:
user_gen_config: dict[str, Any] = {}
# 显式转换为 Dict (如果它是 Pydantic model)
if hasattr(user_gen_config_raw, 'model_dump'):
user_gen_config = user_gen_config_raw.model_dump(exclude_none=True)
elif isinstance(user_gen_config_raw, dict):
user_gen_config = cast(dict[str, Any], user_gen_config_raw)
if user_gen_config:
final_config.update(user_gen_config)
# 1.5 合并 kwargs 中的生成配置参数
for k, v in kwargs.items():
# 直接添加所有 kwargs 参数,让转换函数处理驼峰转换
final_config[k] = v
# 2. 统一转换为 camelCase (适配 Vcore AI API)
return self._convert_to_gemini_format(final_config)
def _convert_to_gemini_format(self, config: dict[str, Any]) -> dict[str, Any]:
"""将 snake_case 配置转换为 camelCase"""
converted: dict[str, Any] = {}
for k, v in config.items():
camel_key = snake_to_camel(k)
# 特殊处理 thinkingConfig 中的 thinkingLevel 值
if camel_key == "thinkingConfig" and isinstance(v, dict):
thinking_config: dict[str, Any] = self._camelize_nested(cast(dict[str, Any], v))
if "thinkingLevel" in thinking_config:
# 将小写的 thinking level 转换为大写
level = thinking_config["thinkingLevel"]
if isinstance(level, str):
thinking_config["thinkingLevel"] = level.upper()
converted[camel_key] = thinking_config
elif camel_key in {"imageConfig", "speechConfig", "audioTimestamp", "routingConfig"} and isinstance(v, dict):
converted[camel_key] = self._camelize_nested(cast(dict[str, Any], v))
elif camel_key == "topK" and isinstance(v, (int, float)):
# topK 最大值为 63,防止 API 报错
converted[camel_key] = min(63, int(v))
else:
converted[camel_key] = v
return converted
def _camelize_nested(self, value: Any) -> Any:
"""递归把 Gemini 配置中的 snake_case 转为 camelCase。"""
if isinstance(value, dict):
return {snake_to_camel(str(k)): self._camelize_nested(v) for k, v in value.items()}
if isinstance(value, list):
return [self._camelize_nested(item) for item in value]
return value
def build_safety_settings(self) -> list[dict[str, str]]:
"""构建安全设置"""
return [
{"category": "HARM_CATEGORY_HARASSMENT", "threshold": "BLOCK_NONE"},
{"category": "HARM_CATEGORY_HATE_SPEECH", "threshold": "BLOCK_NONE"},
{"category": "HARM_CATEGORY_SEXUALLY_EXPLICIT", "threshold": "BLOCK_NONE"},
{"category": "HARM_CATEGORY_DANGEROUS_CONTENT", "threshold": "BLOCK_NONE"},
{"category": "HARM_CATEGORY_CIVIC_INTEGRITY", "threshold": "BLOCK_NONE"}
]
|