diff --git a/alembic/versions/100_fix_capability_model_bindings.py b/alembic/versions/100_fix_capability_model_bindings.py new file mode 100644 index 000000000..b6036b9ac --- /dev/null +++ b/alembic/versions/100_fix_capability_model_bindings.py @@ -0,0 +1,107 @@ +# -*- coding: utf-8 -*- +"""100: 修正已有 capability 的模型绑定. + +幂等:仅当 primary_model_id 当前绑定到旧模型 (doubao-seed-1-6) 时才更新, +避免覆盖用户在后台的自定义配置。 + +- 更新 5 个 LLM capability (intent_parsing, copy_fusion, storyboard, copy_review, asset_classify) + 的 primary_model_id 从 doubao-seed-1-6 改为 doubao-seed-2-1-pro-260915 +- 更新 image_analysis 的 primary/lite/fallback 模型绑定 +""" + +import sqlalchemy as sa + +from alembic import op + +revision = "100_fix_capability_model_bindings" +down_revision = "099_ai_model_router_seed" +branch_labels = None +depends_on = None + + +def upgrade() -> None: + conn = op.get_bind() + + # Check tables exist + table_check = conn.execute(sa.text("SELECT to_regclass('public.ai_models')")).scalar() + if not table_check: + return + + config_table_check = conn.execute(sa.text("SELECT to_regclass('public.ai_capability_configs')")).scalar() + if not config_table_check: + return + + # Look up model IDs by model_key (not hardcoded UUIDs) + pro_model_row = conn.execute( + sa.text( + "SELECT id FROM ai_models WHERE model_key = 'doubao-seed-2-1-pro-260915' AND deleted_at IS NULL LIMIT 1" + ) + ).first() + if not pro_model_row: + return + pro_model_id = pro_model_row[0] + + old_model_row = conn.execute( + sa.text("SELECT id FROM ai_models WHERE model_key = 'doubao-seed-1-6-250615' LIMIT 1") + ).first() + old_model_id = old_model_row[0] if old_model_row else None + + llm_capabilities = [ + "intent_parsing", + "copy_fusion", + "storyboard", + "copy_review", + "asset_classify", + ] + + for cap_key in llm_capabilities: + if old_model_id: + conn.execute( + sa.text( + "UPDATE ai_capability_configs SET primary_model_id = :new_id, updated_at = NOW() " + "WHERE capability_key = :cap_key AND primary_model_id = :old_id" + ), + {"new_id": pro_model_id, "old_id": old_model_id, "cap_key": cap_key}, + ) + + # Update image_analysis + qwen38_row = conn.execute( + sa.text("SELECT id FROM ai_models WHERE model_key = 'qwen3.8-flash' AND deleted_at IS NULL LIMIT 1") + ).first() + qwen37_row = conn.execute( + sa.text("SELECT id FROM ai_models WHERE model_key = 'qwen3.7-plus' AND deleted_at IS NULL LIMIT 1") + ).first() + + if qwen38_row and qwen37_row: + qwen38_id = qwen38_row[0] + qwen37_id = qwen37_row[0] + + current_ia = conn.execute( + sa.text( + "SELECT primary_model_id, lite_model_id, fallback_model_id " + "FROM ai_capability_configs WHERE capability_key = 'image_analysis'" + ) + ).first() + + if current_ia: + current_primary, current_lite, current_fallback = current_ia + updates = {} + if current_primary != qwen38_id: + updates["primary_model_id"] = qwen38_id + if current_lite != qwen38_id: + updates["lite_model_id"] = qwen38_id + if current_fallback != qwen37_id: + updates["fallback_model_id"] = qwen37_id + + if updates: + set_clause = ", ".join([f"{k} = :{k}" for k in updates.keys()]) + set_clause += ", updated_at = NOW()" + updates["cap_key"] = "image_analysis" + conn.execute( + sa.text(f"UPDATE ai_capability_configs SET {set_clause} WHERE capability_key = :cap_key"), + updates, + ) + + +def downgrade() -> None: + pass diff --git a/packages/application/cosyvoice_service.py b/packages/application/cosyvoice_service.py index 67ba06e9f..02d1911d4 100644 --- a/packages/application/cosyvoice_service.py +++ b/packages/application/cosyvoice_service.py @@ -369,7 +369,7 @@ class CosyVoiceService: self._api_key = api_key or _router_key or settings.cosyvoice_api_key self._base_url = base_url or _router_url or settings.cosyvoice_base_url self._model = model or _router_model or settings.cosyvoice_model - self._clone_model = clone_model or getattr(settings, "cosyvoice_clone_model", "voice-enrollment") + self._clone_model = clone_model or getattr(settings, "cosyvoice_clone_model", "") self._audio_url_signer = audio_url_signer # base_url 规范化:去掉末尾的路径残留(兼容旧版配置) diff --git a/packages/shared/ai_router.py b/packages/shared/ai_router.py index d024005be..0448b96db 100644 --- a/packages/shared/ai_router.py +++ b/packages/shared/ai_router.py @@ -429,8 +429,8 @@ class AIRouter: api_key = getattr(settings, "cosyvoice_api_key", "") if not api_key: return None - base_url = getattr(settings, "cosyvoice_base_url", "https://dashscope.aliyuncs.com/api/v1") - model = getattr(settings, "cosyvoice_model", "cosyvoice-v3-flash") + base_url = getattr(settings, "cosyvoice_base_url", "") + model = getattr(settings, "cosyvoice_model", "") return TTSClient(provider="dashscope", api_key=api_key, base_url=base_url, model=model) @@ -439,8 +439,8 @@ class AIRouter: api_key = getattr(settings, "doubao_api_key", "") if not api_key: return None - base_url = getattr(settings, "doubao_base_url", "https://ark.cn-beijing.volces.com/api/v3") - model = getattr(settings, "doubao_image_model", "doubao-seedream-5-0-flash-260915") + base_url = getattr(settings, "doubao_base_url", "") + model = getattr(settings, "doubao_image_model", "") return ImageGenClient( provider="volcengine", @@ -455,8 +455,8 @@ class AIRouter: api_key = getattr(settings, "doubao_api_key", "") if not api_key: return None - base_url = getattr(settings, "doubao_base_url", "https://ark.cn-beijing.volces.com/api/v3") - model = getattr(settings, "doubao_video_model", "doubao-seedance-2-5-260628") + base_url = getattr(settings, "doubao_base_url", "") + model = getattr(settings, "doubao_video_model", "") return VideoGenClient( provider="volcengine",