Files
2026-04-20 21:52:35 +08:00

407 lines
15 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""
EPEEKit 集中配置。
所有可调参数从环境变量读取,每次调用实时读取(不缓存),确保 load_dotenv() 后生效。
"""
import os
from typing import Any
def get_llm_max_iterations() -> int:
return int(os.getenv("LLM_MAX_ITERATIONS", "5"))
# ─── LLM 模型注册表 ─────────────────────────────────────
#
# 每个模型的配置说明:
# id — 前端/API 使用的短 ID
# name — 显示名称
# provider — API 提供者(对应 _get_client 的分发键)
# model_id — 传给 OpenAI SDK 的 model 参数
# description — 前端下拉列表说明文字
# vision — 是否支持多模态图片输入
LLM_MODELS: dict[str, dict[str, Any]] = {
"gpt-5.4": {
"id": "gpt-5.4",
"name": "GPT-5.4",
"provider": "vectorengine",
"model_id": "gpt-5.4",
"description": "知识工作与计算机操控最强1M 上下文",
"vision": True,
},
"claude-sonnet-4-6": {
"id": "claude-sonnet-4-6",
"name": "Claude Sonnet 4.6",
"provider": "vectorengine",
"model_id": "claude-sonnet-4-6",
"description": "高性价比编码与日常任务",
"vision": True,
},
"claude-opus-4-6": {
"id": "claude-opus-4-6",
"name": "Claude Opus 4.6",
"provider": "vectorengine",
"model_id": "claude-opus-4-6",
"description": "编码与专家级推理最强128K 输出",
"vision": True,
},
"gemini-3.1-pro-preview": {
"id": "gemini-3.1-pro-preview",
"name": "Gemini 3.1 Pro",
"provider": "vectorengine",
"model_id": "gemini-3.1-pro-preview",
"description": "推理最强、价格最低2M 上下文",
"vision": True,
},
"glm-4.7": {
"id": "glm-4.7",
"name": "GLM-4.7",
"provider": "vectorengine",
"model_id": "glm-4.7",
"description": "智谱 AI中文能力突出免费额度",
"vision": False,
},
"gpt-4o-mini": {
"id": "gpt-4o-mini",
"name": "GPT-4o Mini",
"provider": "vectorengine",
"model_id": "gpt-4o-mini",
"description": "轻量快速、高性价比",
"vision": True,
},
"deepseek-chat": {
"id": "deepseek-chat",
"name": "DeepSeek Chat",
"provider": "deepseek",
"model_id": "deepseek-chat",
"description": "中文对话优化(直连)",
"vision": False,
},
}
def get_default_llm_model_id() -> str:
"""返回 .env 中配置的默认 LLM 短 ID不在注册表中则回退到 gpt-5.4。"""
env_model = os.getenv("LLM_MODEL", "gpt-5.4")
if env_model in LLM_MODELS:
return env_model
return "gpt-5.4"
def get_llm_model_config(model_id: str | None = None) -> dict[str, Any]:
"""根据短 ID 获取 LLM 模型配置,未指定或不存在则使用默认模型。"""
if model_id and model_id in LLM_MODELS:
return LLM_MODELS[model_id]
return LLM_MODELS[get_default_llm_model_id()]
def get_llm_models_list() -> list[dict]:
"""返回前端下拉列表所需的 LLM 模型摘要信息。"""
return [
{
"id": cfg["id"],
"name": cfg["name"],
"description": cfg["description"],
"vision": cfg.get("vision", False),
}
for cfg in LLM_MODELS.values()
]
# ─── 图像模型注册表 ─────────────────────────────────────
#
# 每个模型的配置说明:
# id — 前端/API 使用的短 ID
# name — 显示名称
# provider — 生成服务提供者(对应 image_gen.py 中的 Provider
# model_id — Replicate 上的完整模型 ID
# description — 前端下拉列表中的说明文字
# supports_ref_image — 是否原生支持参考图输入IP-Adapter 等)
# ref_image_param — 传给 Replicate 的参考图参数名(模型间可能不同)
# num_images_param — 批量生成参数名Flux 用 num_outputsKolors 用 number_of_images
# default_params — 默认推理参数
IMAGE_MODELS: dict[str, dict[str, Any]] = {
"gpt-image-1.5": {
"id": "gpt-image-1.5",
"name": "GPT Image 1.5",
"provider": "openai",
"model_id": "gpt-image-1.5",
"description": "OpenAI 最强生图,文字渲染与 prompt 理解最佳,支持多图参考",
"supports_ref_image": True,
"max_ref_images": 16,
"default_params": {
"size": "1024x1024",
"quality": "high",
},
},
"gemini-3.1-flash-image": {
"id": "gemini-3.1-flash-image",
"name": "Gemini 3.1 Flash Image",
"provider": "gemini_native",
"model_id": "gemini-3.1-flash-image-preview",
"description": "Google 原生生图,速度快、价格低,支持多图参考(最多 14 张)",
"supports_ref_image": True,
"max_ref_images": 14,
"default_params": {},
},
"flux-schnell": {
"id": "flux-schnell",
"name": "Flux Schnell",
"provider": "replicate",
"model_id": "black-forest-labs/flux-schnell",
"description": "快速生成,适合快速迭代",
"supports_ref_image": False,
"num_images_param": "num_outputs",
"default_params": {
"aspect_ratio": "1:1",
"output_format": "png",
},
},
"flux-dev": {
"id": "flux-dev",
"name": "Flux Dev",
"provider": "replicate",
"model_id": "black-forest-labs/flux-dev",
"description": "高质量生成,细节更好",
"supports_ref_image": False,
"num_images_param": "num_outputs",
"default_params": {
"aspect_ratio": "1:1",
"output_format": "png",
},
},
"sdxl": {
"id": "sdxl",
"name": "Stable Diffusion XL",
"provider": "replicate",
"model_id": "stability-ai/sdxl:39ed52f2a78e934b3ba6e2a89f5b1c712de7dfea535525255b1aa35c5565e08b",
"description": "经典 SDXL支持 negative prompt",
"supports_ref_image": False,
"num_images_param": "num_outputs",
"default_params": {
"width": 1024,
"height": 1024,
"num_inference_steps": 50,
"guidance_scale": 7.5,
},
},
"instant-style": {
"id": "instant-style",
"name": "InstantStyle",
"provider": "replicate",
"model_id": "jyoung105/instant-style:c6f01e12f31cb99f9ee774a78992a71294f630a6f433d9aecfdc33b816fc4baa",
"description": "强风格迁移,画风还原度高(较慢)",
"supports_ref_image": True,
"ref_image_param": "style_image",
"num_images_param": "num_outputs",
"default_params": {
"width": 1024,
"height": 1024,
"num_inference_steps": 30,
"guidance_scale": 5,
"style_strength": 1.0,
"block_mode": "style-only",
"adapter_mode": "original",
},
},
"kolors-ipadapter": {
"id": "kolors-ipadapter",
"name": "Kolors IP-Adapter",
"provider": "replicate",
"model_id": "fofr/kolors-with-ipadapter:5a1a92b2c0f81813225d48ed8e411813da41aa84e7582fb705d1af46eea36eed",
"description": "风格参考生成,上传参考图效果最佳",
"supports_ref_image": True,
"ref_image_param": "image",
"num_images_param": "number_of_images",
"default_params": {
"width": 1024,
"height": 1024,
"steps": 25,
"cfg": 4,
"ip_adapter_weight": 0.8,
"ip_adapter_weight_type": "style transfer precise",
"output_format": "png",
},
},
# Mesh 管道专用的内部模型internal=True 不展示到前端下拉):
# 输入两张图image=原图做 IP-Adapter 风格参考controlnet_input=normal/depth 结构参考。
# 用途:在 Stage 3 对 Trellis 抽出来的 normal 帧做二次重绘,还原原画风。
"ip-adapter-controlnet-depth": {
"id": "ip-adapter-controlnet-depth",
"name": "IP-Adapter + ControlNet Depth (内部)",
"provider": "replicate",
"model_id": "chigozienri/ip_adapter-sdxl-controlnet-depth:0436c8702ef52616be5c30948551b3af6a86c821cca9b01f11ac297624fff14c",
"description": "Mesh 管道 Stage 3 专用IP-Adapter 保风格 + ControlNet 保几何",
"internal": True,
"supports_ref_image": True,
"ref_image_param": "image", # IP-Adapter 参考图字段
"default_params": {
"scale": 0.75, # IP-Adapter 权重(建议 0.7-0.8
"controlnet_conditioning_scale": 0.8, # 结构约束(建议 0.7-0.9
"prompt": "same building, same art style, consistent with reference",
"negative_prompt": "blurry, distorted, different style, realistic photo, photograph",
"num_outputs": 1,
"num_inference_steps": 30,
},
},
}
def get_style_restore_model_id() -> str:
"""Mesh 管道 Stage 3 使用的内部模型短 ID。"""
return "ip-adapter-controlnet-depth"
# ─── 视角变换模型注册表 ─────────────────────────────────
#
# 视角变换模型不用于生图,而是将已有图片转换为不同视角。
# 输入通常只需一张图,输出为多个固定视角的图片。
VIEW_TRANSFORM_MODELS: dict[str, dict[str, Any]] = {
"zero123plus": {
"id": "zero123plus",
"name": "Zero123++",
"provider": "replicate",
"model_id": "jd7h/zero123plusplus:c69c6559a29011b576f1ff0371b3bc1add2856480c60520c7e9ce0b40a6e9052",
"description": "单图生成 6 个固定视角,适合建筑/物体的多角度预览",
"pipeline": "grid",
"output_views": [
{"azimuth": 30, "elevation": 30},
{"azimuth": 90, "elevation": -20},
{"azimuth": 150, "elevation": 30},
{"azimuth": 210, "elevation": -20},
{"azimuth": 270, "elevation": 30},
{"azimuth": 330, "elevation": -20},
],
"grid_layout": {"cols": 2, "rows": 3},
"enabled": True,
},
"trellis": {
"id": "trellis",
"name": "Trellis (Mesh Pipeline)",
"provider": "replicate",
"model_id": "firtoz/trellis:e8f6c45206993f297372f5436b90350817bd9b4a0d52d2a76df50c1c8afa2b3c",
"description": "3D 重建 + 任意视角 + 风格还原,建筑友好(~30s 重建 + 72s/张重绘)",
"pipeline": "mesh",
"default_params": {
"texture_size": 1024,
"mesh_simplify": 0.95,
"generate_color": True,
"generate_normal": True,
"generate_model": True,
"save_gaussian_ply": False,
"ss_sampling_steps": 12,
"slat_sampling_steps": 12,
"ss_guidance_strength": 7.5,
"slat_guidance_strength": 3.0,
},
"default_azimuths": [0, 60, 120, 180, 240, 300],
"default_elevations": [0, 0, 0, 0, 0, 0],
"enabled": True,
},
"hunyuan3d": {
"id": "hunyuan3d",
"name": "Hunyuan3D-2 (高质量)",
"provider": "replicate",
"model_id": "tencent/hunyuan3d-2:b1b9449a1277e10402781c5d41eb30c0a0683504fb23fab591ca9dfc2aabe1cb",
"description": "几何质量最佳(~127s需自定义渲染器二期启用",
"pipeline": "mesh",
"default_params": {
"steps": 50,
"guidance_scale": 5.5,
"octree_resolution": 256,
"remove_background": True,
},
"enabled": False,
},
}
def get_view_transform_model_config(model_id: str | None = None) -> dict[str, Any]:
"""获取视角变换模型配置,默认返回 zero123plus。"""
if model_id and model_id in VIEW_TRANSFORM_MODELS:
return VIEW_TRANSFORM_MODELS[model_id]
return VIEW_TRANSFORM_MODELS["zero123plus"]
def get_view_transform_models_list() -> list[dict]:
"""返回前端下拉列表所需的视角变换模型摘要信息(只包含 enabled=True 的)。"""
return [
{
"id": cfg["id"],
"name": cfg["name"],
"description": cfg["description"],
"pipeline": cfg.get("pipeline", "grid"),
}
for cfg in VIEW_TRANSFORM_MODELS.values()
if cfg.get("enabled", True)
]
def get_default_image_model_id() -> str:
"""返回 .env 中配置的默认模型短 ID若不在注册表中则回退到 flux-schnell。"""
env_model = os.getenv("IMAGE_MODEL", "flux-schnell")
for mid, cfg in IMAGE_MODELS.items():
if cfg["model_id"] == env_model or mid == env_model:
return mid
return "flux-schnell"
def get_image_model_config(model_id: str | None = None) -> dict[str, Any]:
"""根据短 ID 获取模型配置,未指定或不存在则使用默认模型。"""
if model_id and model_id in IMAGE_MODELS:
return IMAGE_MODELS[model_id]
return IMAGE_MODELS[get_default_image_model_id()]
def get_ref_image_model_id() -> str | None:
"""返回有参考图时推荐使用的模型 ID第一个 supports_ref_image=True 的模型)。"""
for mid, cfg in IMAGE_MODELS.items():
if cfg.get("supports_ref_image"):
return mid
return None
def get_image_models_list() -> list[dict]:
"""返回前端下拉列表所需的模型摘要信息(过滤掉 internal=True 的内部模型)。"""
return [
{
"id": cfg["id"],
"name": cfg["name"],
"description": cfg["description"],
"supports_ref_image": cfg.get("supports_ref_image", False),
}
for cfg in IMAGE_MODELS.values()
if not cfg.get("internal", False)
]
# ─── 记忆系统配置 ─────────────────────────────────────
def get_deepseek_api_key() -> str:
return os.getenv("DEEPSEEK_API_KEY", "")
def get_ollama_base_url() -> str:
return os.getenv("OLLAMA_BASE_URL", "http://localhost:11434")
def get_mem0_embedding_model() -> str:
return os.getenv("MEM0_EMBEDDING_MODEL", "nomic-embed-text")
def get_max_recent_turns() -> int:
return int(os.getenv("MAX_RECENT_TURNS", "20"))
# ─── 图像输出配置 ─────────────────────────────────────
def get_image_aspect_ratio() -> str:
return os.getenv("IMAGE_ASPECT_RATIO", "1:1")
def get_image_output_format() -> str:
return os.getenv("IMAGE_OUTPUT_FORMAT", "png")