| import asyncio |
| import json |
| import logging |
| import time |
| from typing import List, Dict, Any, Optional |
| from fastapi import APIRouter, HTTPException, Request, Depends, status |
| from fastapi.responses import StreamingResponse |
| from app import config |
|
|
| |
| from app.api.models import ChatCompletionRequest, ChatCompletionResponse, ModelList |
| from app.core.services.gemini import GeminiClient |
| from app.core.keys.manager import APIKeyManager |
| import httpx |
| from app.api.middleware import verify_proxy_key |
| |
| from app.core.processing.main_handler import process_request |
| |
|
|
| from app.core.dependencies import get_key_manager, get_http_client |
| |
| logger = logging.getLogger('my_logger') |
|
|
|
|
| router = APIRouter() |
|
|
| |
| from app.core.cache.manager import CacheManager |
| from app.core.dependencies import verify_admin_token |
|
|
| |
| from app.api.models import CachedContentEntry, CacheListResponse |
|
|
| @router.get("/v1/models", response_model=ModelList) |
| async def list_models( |
| key_manager: APIKeyManager = Depends(get_key_manager), |
| http_client: httpx.AsyncClient = Depends(get_http_client) |
| ): |
| """ |
| 处理获取可用模型列表的 GET 请求。 |
| """ |
| active_keys_count = key_manager.get_active_keys_count() |
| |
| if not GeminiClient.AVAILABLE_MODELS and active_keys_count > 0: |
| logger.info("首次请求模型列表,尝试获取...") |
| try: |
| key_to_use = None |
| with key_manager.keys_lock: |
| if key_manager.api_keys: key_to_use = key_manager.api_keys[0] |
| if key_to_use: |
| |
| all_models = await GeminiClient.list_available_models(key_to_use, http_client) |
| |
| GeminiClient.AVAILABLE_MODELS = [model.replace("models/", "") for model in all_models] |
| logger.info(f"成功获取可用模型: {GeminiClient.AVAILABLE_MODELS}") |
| else: logger.error("无法找到有效 Key 来获取模型列表。") |
| except Exception as e: |
| logger.error(f"获取模型列表失败: {e}") |
| GeminiClient.AVAILABLE_MODELS = [] |
|
|
| |
| logger.info("接收到列出模型的请求", extra={'request_type': 'list_models', 'status_code': 200}) |
| |
| return ModelList(data=[{"id": model, "object": "model", "created": int(time.time()), "owned_by": "organization-owner"} for model in GeminiClient.AVAILABLE_MODELS]) |
|
|
|
|
| @router.post("/v1/chat/completions", response_model=ChatCompletionResponse, status_code=status.HTTP_200_OK) |
| async def chat_completions( |
| request_data: ChatCompletionRequest, |
| request: Request, |
| |
| auth_data: Dict[str, Any] = Depends(verify_proxy_key), |
| key_manager: APIKeyManager = Depends(get_key_manager), |
| http_client: httpx.AsyncClient = Depends(get_http_client) |
| ): |
| """ |
| 处理聊天补全的 POST 请求(流式和非流式)。 |
| """ |
| request_type = 'stream' if request_data.stream else 'non-stream' |
| |
| |
| response = await process_request( |
| chat_request=request_data, |
| http_request=request, |
| request_type=request_type, |
| auth_data=auth_data, |
| key_manager=key_manager, |
| http_client=http_client |
| ) |
|
|
| if response is None: |
| |
| |
| |
| logger.error("process_request 意外返回 None。") |
| raise HTTPException(status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail="请求处理中断或失败") |
|
|
| return response |
|
|
| @router.get("/debug/config", include_in_schema=False) |
| async def debug_config(): |
| """ |
| 调试接口:返回应用程序读取到的 WEB_UI_PASSWORDS 配置。 |
| """ |
| return {"WEB_UI_PASSWORDS": config.WEB_UI_PASSWORDS} |
|
|
|
|
| |
| @router.get("/cache", response_model=CacheListResponse, dependencies=[Depends(verify_admin_token)]) |
| async def list_caches(): |
| """ |
| 获取缓存列表。 |
| 注意: app/core/cache_manager.py 中的列表功能尚未实现,此处返回模拟数据。 |
| """ |
| logger.info("接收到获取缓存列表的请求") |
| |
| |
| mock_caches = [ |
| CachedContentEntry( |
| id="mock-cache-id-1", |
| content_hash="mockhash123", |
| associated_key_id="mock-key-1", |
| created_at=datetime.utcnow(), |
| expires_at=datetime.utcnow() + timedelta(days=1) |
| ), |
| CachedContentEntry( |
| id="mock-cache-id-2", |
| content_hash="mockhash456", |
| associated_key_id="mock-key-2", |
| created_at=datetime.utcnow() - timedelta(hours=1), |
| expires_at=datetime.utcnow() + timedelta(hours=23) |
| ) |
| ] |
| return CacheListResponse(total=len(mock_caches), caches=mock_caches) |
|
|
| @router.get("/cache/{cache_id}", response_model=CachedContentEntry, dependencies=[Depends(verify_admin_token)]) |
| async def get_cache_details(cache_id: str): |
| """ |
| 根据缓存 ID 获取特定缓存的详细信息。 |
| 注意: app/core/cache_manager.py 中的获取详细信息功能尚未实现,此处返回模拟数据。 |
| """ |
| logger.info(f"接收到获取缓存详细信息的请求,ID: {cache_id}") |
| |
| |
| if cache_id == "mock-cache-id-1": |
| return CachedContentEntry( |
| id="mock-cache-id-1", |
| content_hash="mockhash123", |
| associated_key_id="mock-key-1", |
| created_at=datetime.utcnow(), |
| expires_at=datetime.utcnow() + timedelta(days=1) |
| ) |
| elif cache_id == "mock-cache-id-2": |
| return CachedContentEntry( |
| id="mock-cache-id-2", |
| content_hash="mockhash456", |
| associated_key_id="mock-key-2", |
| created_at=datetime.utcnow() - timedelta(hours=1), |
| expires_at=datetime.utcnow() + timedelta(hours=23) |
| ) |
| else: |
| raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail="缓存未找到") |
|
|
|
|
| @router.delete("/cache/{cache_id}", dependencies=[Depends(verify_admin_token)]) |
| async def delete_single_cache(cache_id: str): |
| """ |
| 根据缓存 ID 删除特定缓存。 |
| """ |
| logger.info(f"接收到删除缓存的请求,ID: {cache_id}") |
| success = await cache_manager.delete_cache(cache_id) |
| if success: |
| return {"message": f"缓存 {cache_id} 删除成功"} |
| else: |
| |
| |
| |
| logger.warning(f"尝试删除缓存 {cache_id} 失败或 cache_manager 未完全实现") |
| |
| |
| return {"message": f"尝试删除缓存 {cache_id},请检查日志确认结果"} |
|
|
|
|
| @router.delete("/cache", dependencies=[Depends(verify_admin_token)]) |
| async def clear_all_caches(): |
| """ |
| 清空所有缓存。 |
| 注意: app/core/cache_manager.py 中的清空功能尚未实现,此处调用 invalidate_expired_caches 作为占位。 |
| """ |
| logger.info("接收到清空所有缓存的请求") |
| |
| |
| await cache_manager.invalidate_expired_caches() |
| |
| logger.warning("清空所有缓存的功能未完全实现,调用了 invalidate_expired_caches 作为占位") |
| return {"message": "尝试清空所有缓存,请检查日志确认结果"} |
|
|
| |
| from datetime import datetime, timedelta |
|
|