chore: post-deployment cleanup and docs update
- Make AI routing rules DB-driven (read from system_configs, removed from config.py) - Add translation quota tracking to LLM translation (OpenAIProvider) - Add Alibaba MT ECS RAM role support (STS token, no AccessKey needed) - Fix admin sidebar link for AI模型配置 page - Fix Quota.vue API path (quotas → translation-quotas) - Fix login auto-redirect to dashboard - Add provider dropdown selects to AI routing config UI - Clean up stale ai_provider_* system_configs records - Remove OpencodeGo, Spark providers (code + DB) - Update deploy config: nginx port 8000, systemd cwd
This commit is contained in:
@@ -288,11 +288,14 @@ class AdminService:
|
||||
|
||||
async def _seed_default_configs(self):
|
||||
defaults = [
|
||||
SystemConfig(key="ai_provider_translate", value={"primary": "sensenova", "fallback": ["alibaba-mt", "opencode_go"]}, description="翻译任务 AI 模型选择"),
|
||||
SystemConfig(key="ai_provider_reply", value={"primary": "sensenova", "fallback": ["opencode_go"]}, description="回复建议 AI 模型选择"),
|
||||
SystemConfig(key="ai_provider_marketing", value={"primary": "sensenova", "fallback": ["opencode_go"]}, description="营销文案 AI 模型选择"),
|
||||
SystemConfig(key="ai_provider_extract", value={"primary": "sensenova", "fallback": ["opencode_go"]}, description="信息提取 AI 模型选择"),
|
||||
SystemConfig(key="ai_provider_quotation", value={"primary": "sensenova", "fallback": ["opencode_go"]}, description="报价单 AI 模型选择"),
|
||||
SystemConfig(key="ai_routing", value={
|
||||
"translate": {"primary": "sensenova", "fallback": ["alibaba-mt", "nvidia"]},
|
||||
"reply": {"primary": "sensenova", "fallback": ["nvidia"]},
|
||||
"marketing": {"primary": "sensenova", "fallback": ["nvidia"]},
|
||||
"extract": {"primary": "sensenova", "fallback": ["nvidia"]},
|
||||
"quotation": {"primary": "sensenova", "fallback": ["nvidia"]},
|
||||
"chat": {"primary": "sensenova", "fallback": ["nvidia"]},
|
||||
}, description="AI 路由规则:各任务的主选/备用供应商"),
|
||||
SystemConfig(key="feature_guest_mode", value={"enabled": True}, description="游客模式开关"),
|
||||
SystemConfig(key="feature_wechat_login", value={"enabled": False}, description="微信登录开关"),
|
||||
SystemConfig(key="feature_registration", value={"enabled": True}, description="新用户注册开关"),
|
||||
@@ -305,6 +308,19 @@ class AdminService:
|
||||
self.db.add(cfg)
|
||||
await self.db.flush()
|
||||
|
||||
async def _migrate_routing_configs(self):
|
||||
from sqlalchemy import delete
|
||||
# Remove stale individual routing keys (replaced by consolidated ai_routing)
|
||||
stale_prefixes = ["ai_provider_translate", "ai_provider_reply", "ai_provider_marketing",
|
||||
"ai_provider_extract", "ai_provider_quotation"]
|
||||
for key in stale_prefixes:
|
||||
await self.db.execute(
|
||||
delete(SystemConfig).where(SystemConfig.key == key)
|
||||
)
|
||||
if stale_prefixes:
|
||||
await self.db.flush()
|
||||
logger.info("Cleaned up stale ai_provider_* routing configs")
|
||||
|
||||
async def list_config(self) -> List[Dict[str, Any]]:
|
||||
result = await self.db.execute(
|
||||
select(func.count(SystemConfig.id))
|
||||
@@ -312,6 +328,28 @@ class AdminService:
|
||||
if result.scalar() == 0:
|
||||
await self._seed_default_configs()
|
||||
|
||||
await self._migrate_routing_configs()
|
||||
|
||||
# Ensure consolidated ai_routing exists
|
||||
result = await self.db.execute(
|
||||
select(SystemConfig).where(SystemConfig.key == "ai_routing")
|
||||
)
|
||||
if not result.scalar_one_or_none():
|
||||
self.db.add(SystemConfig(
|
||||
key="ai_routing",
|
||||
value={
|
||||
"translate": {"primary": "sensenova", "fallback": ["alibaba-mt", "nvidia"]},
|
||||
"reply": {"primary": "sensenova", "fallback": ["nvidia"]},
|
||||
"marketing": {"primary": "sensenova", "fallback": ["nvidia"]},
|
||||
"extract": {"primary": "sensenova", "fallback": ["nvidia"]},
|
||||
"quotation": {"primary": "sensenova", "fallback": ["nvidia"]},
|
||||
"chat": {"primary": "sensenova", "fallback": ["nvidia"]},
|
||||
},
|
||||
description="AI 路由规则:各任务的主选/备用供应商",
|
||||
))
|
||||
await self.db.flush()
|
||||
logger.info("Seeded ai_routing config")
|
||||
|
||||
result = await self.db.execute(
|
||||
select(SystemConfig).order_by(SystemConfig.key)
|
||||
)
|
||||
@@ -336,6 +374,12 @@ class AdminService:
|
||||
config.value = value
|
||||
config.updated_at = datetime.utcnow()
|
||||
await self.db.flush()
|
||||
|
||||
if key == "ai_routing":
|
||||
from app.ai.router import get_ai_router
|
||||
await get_ai_router().reload_from_db(self.db)
|
||||
logger.info("AI router reloaded after ai_routing config update")
|
||||
|
||||
return {
|
||||
"key": config.key,
|
||||
"value": config.value,
|
||||
|
||||
@@ -12,6 +12,10 @@ class TranslationQuotaService:
|
||||
def __init__(self, db: AsyncSession):
|
||||
self.db = db
|
||||
|
||||
def _default_desc(self, version: str) -> str:
|
||||
labels = {"ecommerce": "阿里云翻译电商版", "general": "阿里云翻译通用版", "llm": "AI模型翻译"}
|
||||
return labels.get(version, f"阿里云翻译{version}版")
|
||||
|
||||
async def _get_or_create(self, version: str) -> TranslationQuota:
|
||||
result = await self.db.execute(
|
||||
select(TranslationQuota).where(TranslationQuota.version == version)
|
||||
@@ -25,7 +29,7 @@ class TranslationQuotaService:
|
||||
used_chars=0,
|
||||
current_month=now.strftime("%Y-%m"),
|
||||
enabled=True,
|
||||
description=f"阿里云翻译{version}版",
|
||||
description=self._default_desc(version),
|
||||
)
|
||||
self.db.add(quota)
|
||||
await self.db.flush()
|
||||
@@ -57,8 +61,7 @@ class TranslationQuotaService:
|
||||
return remaining
|
||||
|
||||
async def get_all_quotas(self) -> list:
|
||||
default_versions = ["ecommerce", "general"]
|
||||
for v in default_versions:
|
||||
for v in ("ecommerce", "general", "llm"):
|
||||
await self._get_or_create(v)
|
||||
|
||||
result = await self.db.execute(select(TranslationQuota).order_by(TranslationQuota.version))
|
||||
|
||||
Reference in New Issue
Block a user