Files
microfish/backend/app/config.py
Kunthawat Greethong f395309207 feat: add DeepSeek and Xiaomi MiMo LLM provider presets
- Add providers.py with 5 provider presets (OpenAI, DeepSeek, Xiaomi MiMo, Alibaba DashScope, MiniMax)
- Add LLM_PROVIDER env var for one-line provider switching
- Improve <think> tag stripping for reasoning models
- Add .env.example with documented configuration
- Update README with provider configuration section
2026-06-17 11:13:34 +07:00

134 lines
4.7 KiB
Python
Raw 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.
"""
配置管理
统一从项目根目录的 .env 文件加载配置
"""
import os
from dotenv import load_dotenv
# 加载项目根目录的 .env 文件
# 路径: MiroFish/.env (相对于 backend/app/config.py)
project_root_env = os.path.join(os.path.dirname(__file__), '../../.env')
if os.path.exists(project_root_env):
load_dotenv(project_root_env, override=True)
else:
# 如果根目录没有 .env尝试加载环境变量用于生产环境
load_dotenv(override=True)
def _resolve_llm_config() -> tuple[str, str, str | None]:
"""
解析LLM配置。
优先级:
1. 如果设置了 LLM_PROVIDER使用提供商预设填充 base_url 和 model但可被显式值覆盖
2. 否则使用 LLM_BASE_URL / LLM_MODEL_NAME兼容原有行为
Returns:
(base_url, model_name, provider_name_or_none)
"""
from .providers import get_provider
provider_name = os.environ.get('LLM_PROVIDER', '').strip()
explicit_base_url = os.environ.get('LLM_BASE_URL', '').strip()
explicit_model = os.environ.get('LLM_MODEL_NAME', '').strip()
if provider_name:
preset = get_provider(provider_name)
if preset is None:
provider_names = ["openai", "deepseek", "xiaomi_mimo", "alibaba_dashscope", "minimax"]
available = ", ".join(provider_names)
raise ValueError(
f"未知的 LLM_PROVIDER: '{provider_name}'. "
f"可用值: {available}"
)
# 显式值优先于预设默认值
base_url = explicit_base_url or preset.base_url
model = explicit_model or preset.default_model
return base_url, model, provider_name
else:
# 兼容原有行为:无 LLM_PROVIDER 时直接使用显式值
base_url = explicit_base_url or 'https://api.openai.com/v1'
model = explicit_model or 'gpt-4o-mini'
return base_url, model, None
# 在模块加载时解析配置(避免重复计算)
_llm_base_url, _llm_model_name, _llm_provider = _resolve_llm_config()
class Config:
"""Flask配置类"""
# Flask配置
SECRET_KEY = os.environ.get('SECRET_KEY', 'mirofish-secret-key')
DEBUG = os.environ.get('FLASK_DEBUG', 'True').lower() == 'true'
# JSON配置 - 禁用ASCII转义让中文直接显示而不是 \uXXXX 格式)
JSON_AS_ASCII = False
# LLM配置统一使用OpenAI格式
LLM_API_KEY = os.environ.get('LLM_API_KEY')
LLM_PROVIDER = _llm_provider # e.g. "deepseek" or None
LLM_BASE_URL = _llm_base_url
LLM_MODEL_NAME = _llm_model_name
# Zep配置
ZEP_API_KEY = os.environ.get('ZEP_API_KEY')
# 文件上传配置
MAX_CONTENT_LENGTH = 50 * 1024 * 1024 # 50MB
UPLOAD_FOLDER = os.path.join(os.path.dirname(__file__), '../uploads')
ALLOWED_EXTENSIONS = {'pdf', 'md', 'txt', 'markdown'}
# 文本处理配置
DEFAULT_CHUNK_SIZE = 500 # 默认切块大小
DEFAULT_CHUNK_OVERLAP = 50 # 默认重叠大小
# OASIS模拟配置
OASIS_DEFAULT_MAX_ROUNDS = int(os.environ.get('OASIS_DEFAULT_MAX_ROUNDS', '10'))
OASIS_SIMULATION_DATA_DIR = os.path.join(os.path.dirname(__file__), '../uploads/simulations')
# OASIS平台可用动作配置
OASIS_TWITTER_ACTIONS = [
'CREATE_POST', 'LIKE_POST', 'REPOST', 'FOLLOW', 'DO_NOTHING', 'QUOTE_POST'
]
OASIS_REDDIT_ACTIONS = [
'LIKE_POST', 'DISLIKE_POST', 'CREATE_POST', 'CREATE_COMMENT',
'LIKE_COMMENT', 'DISLIKE_COMMENT', 'SEARCH_POSTS', 'SEARCH_USER',
'TREND', 'REFRESH', 'DO_NOTHING', 'FOLLOW', 'MUTE'
]
# Report Agent配置
REPORT_AGENT_MAX_TOOL_CALLS = int(os.environ.get('REPORT_AGENT_MAX_TOOL_CALLS', '5'))
REPORT_AGENT_MAX_REFLECTION_ROUNDS = int(os.environ.get('REPORT_AGENT_MAX_REFLECTION_ROUNDS', '2'))
REPORT_AGENT_TEMPERATURE = float(os.environ.get('REPORT_AGENT_TEMPERATURE', '0.5'))
@classmethod
def validate(cls) -> list[str]:
"""验证必要配置"""
errors: list[str] = []
if not cls.LLM_API_KEY:
errors.append("LLM_API_KEY 未配置")
if not cls.ZEP_API_KEY:
errors.append("ZEP_API_KEY 未配置")
return errors
@classmethod
def get_active_provider_info(cls) -> dict:
"""返回当前活跃的LLM提供商信息用于日志/API展示"""
from .providers import get_provider
info = {
"provider": cls.LLM_PROVIDER or "custom",
"base_url": cls.LLM_BASE_URL,
"model": cls.LLM_MODEL_NAME,
}
if cls.LLM_PROVIDER:
preset = get_provider(cls.LLM_PROVIDER)
if preset:
info["display_name"] = preset.display_name
info["notes"] = preset.notes
return info