diff --git a/backend/python/app/api/admin/models.py b/backend/python/app/api/admin/models.py index 4537c09..5e1b7f3 100644 --- a/backend/python/app/api/admin/models.py +++ b/backend/python/app/api/admin/models.py @@ -8,6 +8,7 @@ from typing import List, Optional from pydantic import BaseModel from ...database import get_db, SessionLocal from ...models import AIModel, ModelProvider, SiteSetting +from ...models.litellm_sync import sync_create_model, sync_update_model, sync_delete_model, sync_health_check from ...auth import get_current_admin from ...middleware.log_service import LogService from ...capability_registry import ( @@ -475,6 +476,21 @@ async def create_model( db.add(model) db.commit() + # #965: LiteLLM model sync + import threading + def _sync_create(): + try: + from ...models.litellm_sync import sync_create_model + litellm_model = "openai/" + data.name + api_base = provider.api_base_url if provider else data.endpoint or "" + api_key = data.api_key or (provider.api_key if provider else "") or "" + if api_base: + sync_create_model(data.name, litellm_model, api_base.rstrip("/"), api_key) + except Exception as e: + import logging + logging.getLogger("aiflow").warning(f"[litellm-sync] create failed: {e}") + threading.Thread(target=_sync_create, daemon=True).start() + LogService.log_audit( admin_id=admin_user.id, admin_name=admin_user.username, @@ -588,6 +604,24 @@ async def update_model( model.updated_at = datetime.now() db.commit() + # #965: LiteLLM model sync + import threading + def _sync_update(): + try: + from ...models.litellm_sync import sync_update_model + litellm_model = "openai/" + (data.name if data.name else model.name) + provider = db.query(ModelProvider).filter(ModelProvider.id == model.provider_id).first() + api_base = provider.api_base_url if provider else model.endpoint or "" + api_key = data.api_key if data.api_key else (provider.api_key if provider else "") or "" + old_name = model.name + new_name = data.name if data.name else old_name + if api_base: + sync_update_model(old_name, new_name, litellm_model, api_base.rstrip("/"), api_key) + except Exception as e: + import logging + logging.getLogger("aiflow").warning(f"[litellm-sync] update failed: {e}") + threading.Thread(target=_sync_update, daemon=True).start() + LogService.log_audit( admin_id=admin_user.id, admin_name=admin_user.username, @@ -612,6 +646,16 @@ async def delete_model( raise HTTPException(status_code=404, detail="模型不存在") model_name = model.name + # #965: 同步删除到 LiteLLM(异步) + delete_model_name = model.name + import threading + def _sync_delete(): + try: + sync_delete_model(delete_model_name) + except Exception as e: + logging.getLogger("aiflow").warning(f"[litellm-sync] delete failed for {delete_model_name}: {e}") + threading.Thread(target=_sync_delete, daemon=True).start() + db.delete(model) db.commit() diff --git a/backend/python/app/core/litellm_sync.py b/backend/python/app/core/litellm_sync.py new file mode 100644 index 0000000..e29e8f0 --- /dev/null +++ b/backend/python/app/core/litellm_sync.py @@ -0,0 +1,37 @@ +import logging, os, httpx +logger = logging.getLogger(__name__) +LITM = os.getenv("LITELLM_HOST", "qihu-aiflow-litellm") +LITP = os.getenv("LITELLM_PORT", "4000") +LITK = os.getenv("LITELLM_MASTER_KEY", "sk-litellm-master-key-aiflow-2026") +BASE = f"http://{LITM}:{LITP}" +_client = None + +def _gc(): + global _client + if _client is None: + _client = httpx.Client(base_url=BASE, headers={"Authorization": f"Bearer {LITK}", "Content-Type": "application/json"}, timeout=15) + return _client + +def _ss(op, body): + try: + r = _gc().post(op, json=body) + if r.status_code in (200,201): + logger.info(f"[litellm-sync] {op} OK") + return True + logger.warning(f"[litellm-sync] {op} FAIL {r.status_code}") + return False + except Exception as e: + logger.error(f"[litellm-sync] {op} ERR {e}") + return False + +def sync_create_model(model_name, litellm_model, api_base, api_key): + return _ss("/model/new", {"model_name":model_name, "litellm_params":{"model":litellm_model, "api_base":api_base, "api_key":api_key}}) + +def sync_update_model(old_name, new_name, litellm_model, api_base, api_key): + if old_name != new_name: + _ss("/model/delete", {"id":old_name}) + return sync_create_model(new_name, litellm_model, api_base, api_key) + return _ss("/model/update", {"model_name":new_name, "litellm_params":{"model":litellm_model, "api_base":api_base, "api_key":api_key}}) + +def sync_delete_model(model_name): + return _ss("/model/delete", {"id":model_name})