Files
Aether/_deprecated_py_src/api/public/gemini.py

242 lines
6.8 KiB
Python
Raw Normal View History

2025-12-10 20:52:44 +08:00
"""
Gemini API 专属端点
托管 Gemini API 相关路由:
- /v1beta/models/{model}:generateContent
- /v1beta/models/{model}:streamGenerateContent
注意:
- Gemini API model URL 路径中而不是请求体中
- /v1beta/models (列表) /v1beta/models/{model} (详情) models.py 统一处理
2025-12-10 20:52:44 +08:00
"""
from dataclasses import dataclass
from typing import Any
2025-12-10 20:52:44 +08:00
from fastapi import APIRouter, Depends, Request
from sqlalchemy.orm import Session
from src.api.base.adapter import ApiAdapter, ApiMode
from src.api.base.context import ApiRequestContext
from src.api.base.pipeline import get_pipeline
2025-12-10 20:52:44 +08:00
from src.database import get_db
router = APIRouter(tags=["Gemini API"])
pipeline = get_pipeline()
2025-12-10 20:52:44 +08:00
def _is_cli_request(request: Request) -> bool:
"""
判断是否为 CLI 请求
检查顺序:
1. x-app header 包含 "cli"
2. user-agent 包含 "GeminiCLI" "gemini-cli"
"""
# 检查 x-app header
x_app = request.headers.get("x-app", "")
if "cli" in x_app.lower():
return True
# 检查 user-agent
user_agent = request.headers.get("user-agent", "")
user_agent_lower = user_agent.lower()
if "geminicli" in user_agent_lower or "gemini-cli" in user_agent_lower:
return True
return False
def _build_adapter_for_request(request: Request) -> Any:
"""按请求类型懒加载 Gemini 适配器,降低模块导入开销。"""
if _is_cli_request(request):
from src.api.handlers.gemini_cli import build_gemini_cli_adapter
return build_gemini_cli_adapter()
from src.api.handlers.gemini import build_gemini_adapter
return build_gemini_adapter()
@dataclass
class PublicGeminiContentAdapter(ApiAdapter):
model: str
stream: bool
name = "public.gemini.content"
mode = ApiMode.STANDARD
def _delegate(self, request: Request) -> Any:
return _build_adapter_for_request(request)
def extract_api_key(self, request: Request) -> str | None:
return self._delegate(request).extract_api_key(request)
def authorize(self, context: ApiRequestContext) -> None:
return self._delegate(context.request).authorize(context)
def detect_capability_requirements(
self,
headers: dict[str, str],
request_body: dict[str, Any] | None = None,
) -> dict[str, bool]:
adapter = _build_adapter_for_request(
Request(
{
"type": "http",
"asgi": {"version": "3.0"},
"http_version": "1.1",
"method": "POST",
"scheme": "http",
"path": "/",
"raw_path": b"/",
"query_string": b"",
"headers": [
(str(key).lower().encode(), str(value).encode()) for key, value in headers.items()
],
"client": ("127.0.0.1", 0),
"server": ("testserver", 80),
}
)
)
return adapter.detect_capability_requirements(headers, request_body)
def get_audit_metadata(
self,
context: ApiRequestContext,
*,
success: bool,
status_code: int | None,
error: str | None = None,
) -> dict[str, Any]:
return self._delegate(context.request).get_audit_metadata(
context,
success=success,
status_code=status_code,
error=error,
)
def api_format_hint_for_request(self, request: Request) -> str:
return self._delegate(request).allowed_api_formats[0]
def path_params(self) -> dict[str, Any]:
return {"model": self.model, "stream": self.stream}
async def handle(self, context: ApiRequestContext) -> Any:
return await self._delegate(context.request).handle(context)
2025-12-10 20:52:44 +08:00
@router.post("/v1beta/models/{model}:generateContent")
async def generate_content(
model: str,
http_request: Request,
db: Session = Depends(get_db),
) -> Any:
2025-12-10 20:52:44 +08:00
"""
Gemini generateContent API
2025-12-10 20:52:44 +08:00
兼容 Google Gemini API 格式的代理接口非流式
**认证方式**:
- `x-goog-api-key` 请求头
- `?key=` URL 参数
**请求格式**:
```json
{
"contents": [{"parts": [{"text": "Hello"}]}]
}
```
**路径参数**:
- `model`: 模型名称 gemini-2.0-flash
2025-12-10 20:52:44 +08:00
"""
adapter = PublicGeminiContentAdapter(model=model, stream=False)
2025-12-10 20:52:44 +08:00
return await pipeline.run(
adapter=adapter,
http_request=http_request,
db=db,
mode=adapter.mode,
api_format_hint=adapter.api_format_hint_for_request(http_request),
path_params=adapter.path_params(),
2025-12-10 20:52:44 +08:00
)
@router.post("/v1beta/models/{model}:streamGenerateContent")
async def stream_generate_content(
model: str,
http_request: Request,
db: Session = Depends(get_db),
) -> Any:
2025-12-10 20:52:44 +08:00
"""
Gemini streamGenerateContent API
2025-12-10 20:52:44 +08:00
兼容 Google Gemini API 格式的代理接口流式
**认证方式**:
- `x-goog-api-key` 请求头
- `?key=` URL 参数
**路径参数**:
- `model`: 模型名称 gemini-2.0-flash
2025-12-10 20:52:44 +08:00
注意: Gemini API 通过 URL 端点区分流式/非流式不需要在请求体中添加 stream 字段
"""
adapter = PublicGeminiContentAdapter(model=model, stream=True)
2025-12-10 20:52:44 +08:00
return await pipeline.run(
adapter=adapter,
http_request=http_request,
db=db,
mode=adapter.mode,
api_format_hint=adapter.api_format_hint_for_request(http_request),
path_params=adapter.path_params(),
2025-12-10 20:52:44 +08:00
)
# 兼容 v1 路径(部分 SDK 可能使用 generateContent
2025-12-10 20:52:44 +08:00
@router.post("/v1/models/{model}:generateContent")
async def generate_content_v1(
model: str,
http_request: Request,
db: Session = Depends(get_db),
) -> Any:
"""
Gemini generateContent API (v1 兼容)
v1 版本 API 端点兼容部分使用旧版路径的 SDK
"""
adapter = PublicGeminiContentAdapter(model=model, stream=False)
return await pipeline.run(
adapter=adapter,
http_request=http_request,
db=db,
mode=adapter.mode,
api_format_hint=adapter.api_format_hint_for_request(http_request),
path_params=adapter.path_params(),
)
2025-12-10 20:52:44 +08:00
@router.post("/v1/models/{model}:streamGenerateContent")
async def stream_generate_content_v1(
model: str,
http_request: Request,
db: Session = Depends(get_db),
) -> Any:
"""
Gemini streamGenerateContent API (v1 兼容)
v1 版本流式 API 端点兼容部分使用旧版路径的 SDK
"""
adapter = PublicGeminiContentAdapter(model=model, stream=True)
return await pipeline.run(
adapter=adapter,
http_request=http_request,
db=db,
mode=adapter.mode,
api_format_hint=adapter.api_format_hint_for_request(http_request),
path_params=adapter.path_params(),
)