codex86/api5
0
1import logging2import json3import time4import asyncio5import os6import traceback7import sys8from contextlib import asynccontextmanager9import random10 11import uvicorn12from fastapi import FastAPI, Request, HTTPException13from fastapi.responses import StreamingResponse14from fastapi.middleware.cors import CORSMiddleware15from fastapi.responses import JSONResponse16from google import genai17from google.genai import types18from typing import Optional, List, Dict, Any19 20# 简化日志配置21logging.basicConfig(22 level=logging.INFO,23 format='%(asctime)s [%(levelname)s]: %(message)s',24 datefmt='%Y-%m-%d %H:%M:%S'25)26logger = logging.getLogger(__name__)27 28# 模型配置29GEMINI_MODELS = {30 "gemini-2.0-flash-exp": "gemini-2.0-flash-exp",31 "gemini-2.5-flash-preview-05-20": "gemini-2.5-flash-preview-05-20",32 "gemini-2.5-flash": "gemini-2.5-flash",33 "gemini-2.5-flash-preview-04-17": "gemini-2.5-flash-preview-04-17",34 "gemini-2.5-pro": "gemini-2.5-pro"35}36 37# 支持的模型列表38SUPPORTED_MODELS = [39 {40 "id": "gemini-2.5-flash-preview-05-20",41 "object": "model",42 "created": int(time.time()),43 "owned_by": "google",44 "permission": [],45 "root": "gemini-2.5-flash-preview-05-20",46 "parent": None,47 "description": "Gemini 2.5 Flash Preview - 最新实验性模型"48 },49 {50 "id": "gemini-2.5-flash-preview-04-17",51 "object": "model",52 "created": int(time.time()),53 "owned_by": "google",54 "permission": [],55 "root": "gemini-2.5-flash-preview-04-17",56 "parent": None,57 "description": "gemini-2.5-flash-preview-04-17- 经典专业模型"58 },59 {60 "id": "gemini-2.5-flash",61 "object": "model",62 "created": int(time.time()),63 "owned_by": "google",64 "permission": [],65 "root": "gemini-2.5-flash",66 "parent": None,67 "description": "gemini-2.5-flash稳定经典专业模型"68 },69 {70 "id": "gemini-2.5-pro",71 "object": "model",72 "created": int(time.time()),73 "owned_by": "google",74 "permission": [],75 "root": "gemini-2.5-pro",76 "parent": None,77 "description": "gemini-2.5-pro稳定经典专业模型"78 }79]80 81 82def get_model_name(requested_model: str) -> str:83 print(f"实际模型名称:{GEMINI_MODELS.get(requested_model)}")84 return GEMINI_MODELS.get(requested_model, "gemini-2.5-flash")85 86 87def convert_messages(messages):88 content_parts = []89 system_instruction = None90 91 for message in messages:92 role = message.get("role", "user")93 content = message.get("content", "")94 95 if role == "system":96 system_instruction = content97 elif role == "assistant":98 content_parts.append({99 "role": "model",100 "parts": [{"text": content}]101 })102 elif role == "user":103 content_parts.append({104 "role": "user",105 "parts": [{"text": content}]106 })107 108 return content_parts, system_instruction109 110 111def handle_error(error):112 error_str = str(error).lower()113 114 if "prompt_feedback" in error_str:115 if "other" in error_str:116 return "您的输入内容可能过长或触发了安全策略。请尝试缩短您的问题。", "length"117 elif "safety" in error_str:118 return "您的请求被安全策略阻止。请尝试修改您的问题。", "content_filter"119 elif "safety" in error_str:120 return "您的请求被安全策略过滤。请尝试修改您的问题。", "content_filter"121 122 return "生成内容时遇到错误。请稍后重试。", "stop"123 124 125@asynccontextmanager126async def lifespan(app: FastAPI):127 try:128 setup_gemini() # 测试API密钥是否有效129 logger.info("应用启动完成")130 yield131 except Exception as e:132 logger.error(f"应用启动失败: {str(e)}")133 raise134 finally:135 logger.info("应用关闭")136 137 138# 创建FastAPI应用实例139app = FastAPI(140 lifespan=lifespan,141 title="Gemini Official API",142 version="1.3.0"143)144 145# 添加CORS中间件146app.add_middleware(147 CORSMiddleware,148 allow_origins=["*"],149 allow_credentials=True,150 allow_methods=["*"],151 allow_headers=["*"],152)153 154# API密钥列表155API_KEYS = [156 'AIzaSyAainmVAjjiQoUFqF1X3-5AI4_qUg1UkJw',157 'AIzaSyC68ywBZ0u-BYV1PmRbwldyymHBhRAqcpM'158]159 160# 当前使用的API密钥索引161current_key_index = 0162 163 164def get_random_api_key():165 return random.choice(API_KEYS)166 167 168def setup_gemini(api_key=None):169 if not api_key:170 api_key = get_random_api_key()171 172 if not API_KEYS:173 logger.error("请设置有效的API密钥列表")174 raise ValueError("API_KEYS未设置")175 176 client = genai.Client(api_key=api_key)177 return client, api_key178 179 180# 配置安全设置181SAFETY_SETTINGS = [182 types.SafetySetting(183 category=types.HarmCategory.HARM_CATEGORY_HARASSMENT,184 threshold=types.HarmBlockThreshold.BLOCK_NONE,185 ),186 types.SafetySetting(187 category=types.HarmCategory.HARM_CATEGORY_HATE_SPEECH,188 threshold=types.HarmBlockThreshold.BLOCK_NONE,189 ),190 types.SafetySetting(191 category=types.HarmCategory.HARM_CATEGORY_SEXUALLY_EXPLICIT,192 threshold=types.HarmBlockThreshold.BLOCK_NONE,193 ),194 types.SafetySetting(195 category=types.HarmCategory.HARM_CATEGORY_DANGEROUS_CONTENT,196 threshold=types.HarmBlockThreshold.BLOCK_NONE,197 ),198 types.SafetySetting(199 category=types.HarmCategory.HARM_CATEGORY_CIVIC_INTEGRITY,200 threshold=types.HarmBlockThreshold.BLOCK_NONE,201 ),202]203 204 205async def try_generate_content(model_name, content_parts, config, max_retries=3):206 last_error = None207 used_keys = set()208 209 for attempt in range(max_retries):210 try:211 # 获取新的API密钥(避免重复使用失败的密钥)212 available_keys = [key for key in API_KEYS if key not in used_keys]213 if not available_keys:214 # 如果所有密钥都试过了,重置使用记录215 used_keys.clear()216 available_keys = API_KEYS217 218 api_key = random.choice(available_keys)219 used_keys.add(api_key)220 221 client, current_key = setup_gemini(api_key)222 logger.info(f"尝试第 {attempt + 1} 次,使用密钥: {current_key[:20]}...")223 224 response = client.models.generate_content(225 model=model_name,226 contents=content_parts,227 config=config228 )229 230 return response, current_key231 232 except Exception as e:233 last_error = e234 error_str = str(e).lower()235 236 # 检查是否是需要重试的错误237 if any(code in error_str for code in ['400', '401', '403', '429', '500', '502', '503', '504']):238 logger.warning(f"第 {attempt + 1} 次尝试失败: {str(e)}")239 if attempt < max_retries - 1:240 await asyncio.sleep(1) # 等待1秒后重试241 continue242 else:243 # 非网络错误,直接抛出244 raise e245 246 # 所有重试都失败了247 raise last_error248 249 250@app.post("/v1/chat/completions")251async def chat_completions(request: Request):252 try:253 body = await request.json()254 255 messages = body.get('messages', [])256 stream = body.get('stream', False)257 max_tokens = body.get('max_tokens', 65536)258 temperature = body.get('temperature', 1.2)259 top_p = body.get('top_p', 0.0)260 requested_model = body.get('model', 'gemini-2.5-flash')261 model_name = get_model_name(requested_model)262 content_parts, system_instruction = convert_messages(messages)263 264 config = types.GenerateContentConfig(265 max_output_tokens=max_tokens,266 temperature=temperature,267 top_p=top_p,268 system_instruction=system_instruction,269 safety_settings=SAFETY_SETTINGS,270 )271 272 if stream:273 # 流式响应也需要重试机制274 client, api_key = setup_gemini()275 return StreamingResponse(276 stream_response_with_retry(client, model_name, content_parts, config),277 media_type='text/event-stream'278 )279 else:280 response, used_key = await try_generate_content(model_name, content_parts, config)281 282 response_text = response.text if response else ""283 finish_reason = "stop"284 285 if not response_text:286 response_text = "无法生成回复。请尝试修改您的问题。"287 288 logger.info(f"成功生成回复,使用密钥: {used_key[:20]}...")289 290 return {291 'id': f'chatcmpl-{int(time.time())}-{random.randint(1000, 9999)}',292 'object': 'chat.completion',293 'created': int(time.time()),294 'model': requested_model,295 'choices': [{296 'index': 0,297 'message': {298 'role': 'assistant',299 'content': response_text300 },301 'finish_reason': finish_reason302 }],303 'usage': {304 'prompt_tokens': len(content_parts),305 'completion_tokens': len(response_text.split()),306 'total_tokens': len(content_parts) + len(response_text.split())307 }308 }309 310 except Exception as e:311 logger.error(f"处理聊天请求出错: {str(e)}")312 error_message, finish_reason = handle_error(e)313 raise HTTPException(status_code=500, detail=str(e))314 315 316async def stream_response_with_retry(client, model_name, content_parts, config, max_retries=3):317 last_error = None318 used_keys = set()319 320 for attempt in range(max_retries):321 try:322 # 获取新的API密钥(避免重复使用失败的密钥)323 available_keys = [key for key in API_KEYS if key not in used_keys]324 if not available_keys:325 used_keys.clear()326 available_keys = API_KEYS327 328 api_key = random.choice(available_keys)329 used_keys.add(api_key)330 331 current_client, current_key = setup_gemini(api_key)332 logger.info(f"流式响应尝试第 {attempt + 1} 次,使用密钥: {current_key[:20]}...")333 334 for chunk in current_client.models.generate_content_stream(335 model=model_name,336 contents=content_parts,337 config=config338 ):339 # 确保chunk存在且有文本内容340 if chunk and hasattr(chunk, 'text') and chunk.text:341 data = {342 'id': f'chatcmpl-{int(time.time())}-{random.randint(1000, 9999)}',343 'object': 'chat.completion.chunk',344 'created': int(time.time()),345 'model': model_name,346 'choices': [{347 'index': 0,348 'delta': {349 'role': 'assistant',350 'content': chunk.text351 },352 'finish_reason': None353 }]354 }355 yield f'data: {json.dumps(data, ensure_ascii=False)}\n\n'356 await asyncio.sleep(0.01)357 358 # 发送结束标记359 final_data = {360 'id': f'chatcmpl-{int(time.time())}-{random.randint(1000, 9999)}',361 'object': 'chat.completion.chunk',362 'created': int(time.time()),363 'model': model_name,364 'choices': [{365 'index': 0,366 'delta': {},367 'finish_reason': 'stop'368 }]369 }370 yield f'data: {json.dumps(final_data, ensure_ascii=False)}\n\n'371 yield 'data: [DONE]\n\n'372 373 logger.info(f"流式响应成功,使用密钥: {current_key[:20]}...")374 return # 成功完成,退出重试循环375 376 except Exception as e:377 last_error = e378 error_str = str(e).lower()379 380 # 检查是否是需要重试的错误381 if any(code in error_str for code in ['400', '401', '403', '429', '500', '502', '503', '504']):382 logger.warning(f"流式响应第 {attempt + 1} 次尝试失败: {str(e)}")383 if attempt < max_retries - 1:384 await asyncio.sleep(1)385 continue386 else:387 # 非网络错误,直接处理388 break389 390 # 所有重试都失败了,返回错误信息391 logger.error(f"流式响应所有重试失败: {str(last_error)}")392 error_message, finish_reason = handle_error(last_error)393 394 error_data = {395 'id': f'chatcmpl-{int(time.time())}-error',396 'object': 'chat.completion.chunk',397 'created': int(time.time()),398 'model': model_name,399 'choices': [{400 'index': 0,401 'delta': {402 'role': 'assistant',403 'content': error_message404 },405 'finish_reason': finish_reason406 }]407 }408 yield f'data: {json.dumps(error_data, ensure_ascii=False)}\n\n'409 yield 'data: [DONE]\n\n'410 411 412@app.get("/v1/models")413async def list_models():414 try:415 # 返回 new-api 期望的格式,包含 success 字段416 return {417 "success": True,418 "data": SUPPORTED_MODELS419 }420 except Exception as e:421 logger.error(f"获取模型列表出错: {str(e)}")422 return {423 "success": False,424 "message": str(e),425 "data": []426 }427 428 429@app.get("/v1/models/{model_id}")430async def get_model_info(model_id: str):431 try:432 for model in SUPPORTED_MODELS:433 if model["id"] == model_id:434 return model435 raise HTTPException(status_code=404, detail=f"模型 {model_id} 未找到")436 except HTTPException:437 raise438 except Exception as e:439 logger.error(f"获取模型信息出错: {str(e)}")440 raise HTTPException(status_code=500, detail=str(e))441 442 443@app.get("/v1/chat/completions/v1/models")444async def list_models_alternative():445 try:446 return {447 "object": "list",448 "data": SUPPORTED_MODELS449 }450 except Exception as e:451 logger.error(f"获取模型列表出错: {str(e)}")452 raise HTTPException(status_code=500, detail=str(e))453 454 455@app.get("/health")456async def health_check():457 try:458 return {459 "status": "healthy",460 "timestamp": int(time.time()),461 "api": "gemini-official",462 "available_models": [model["id"] for model in SUPPORTED_MODELS],463 "version": "1.3.0"464 }465 except Exception as e:466 logger.error(f"健康检查失败: {str(e)}")467 return {468 "status": "unhealthy",469 "timestamp": int(time.time()),470 "error": str(e)471 }472 473 474@app.get("/")475async def root():476 return {477 "name": "Gemini Official API",478 "version": "1.3.0",479 "description": "Google Gemini官方API接口服务",480 "endpoints": {481 "models": "/v1/models",482 "models_alt": "/v1/chat/completions/v1/models",483 "chat": "/v1/chat/completions",484 "health": "/health"485 }486 }487 488 489@app.exception_handler(404)490async def not_found_handler(request: Request, exc: HTTPException):491 return {492 "error": "未找到",493 "requested_path": str(request.url.path),494 "message": "请求的路径不存在",495 "available_endpoints": {496 "models": "/v1/models",497 "models_alt": "/v1/chat/completions/v1/models",498 "chat": "/v1/chat/completions",499 "health": "/health",500 "info": "/"501 }502 }503 504 505if __name__ == "__main__":506 print("🚀 启动Gemini官方API服务器")507 print(f"📊 支持的模型: {[model['id'] for model in SUPPORTED_MODELS]}")508 print(f"🔑 已配置 {len(API_KEYS)} 个API密钥")509 print("🔄 支持自动重试和密钥轮换")510 print("✅ 已适配 new-api 格式要求")511 512 uvicorn.run(app, host="0.0.0.0", port=7860)513 