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