Compare commits

...

5 Commits

Author SHA1 Message Date
snaily
84052a2179 feat(auth): 增强Gemini API的认证机制支持URL参数
- 将generate_content和stream_generate_content端点的认证依赖从verify_goog_api_key更改为verify_key_or_goog_api_key
- 使Gemini API同时支持URL参数中的key和请求头中的x-goog-api-key进行认证
- 提高API的灵活性,便于不同客户端集成
2025-03-28 23:44:40 +08:00
snaily
2e7ecd88b5 feat: 增强Gemini API tools参数处理
- 修改GeminiRequest模型,使tools字段支持单个工具对象或工具对象列表
- 在gemini_chat_service中添加类型转换逻辑,确保tools始终以列表形式处理
- 提高API的灵活性和兼容性
2025-03-28 20:50:01 +08:00
snaily
0b1f3dfc04 feat(auth): 支持x-goog-api-key请求头认证
- 添加verify_key_or_goog_api_key方法,支持同时验证URL参数中的key和请求头中的x-goog-api-key
- 更新models接口使用新的认证方法,提高与Google API客户端的兼容性
2025-03-28 19:27:42 +08:00
snaily
c691c7c1cf fix:当没有可用工具时返回空列表而非包含空字典的列表
在_build_tools函数中,当没有工具配置可用时(即tool为空字典),现在会返回空列表[]而不是[{}]。这个防御性编程修复可以避免向Gemini API发送无效的工具配置,防止可能的API调用错误。
2025-03-25 15:18:27 +08:00
snaily
97db7eebf1 chore:修改图片处理逻辑,统一使用base64编码
将_convert_image函数中对非data:image格式URL的处理方式从直接返回URL改为转换为base64编码的内联数据。这样无论图片是以data URI形式还是URL形式提供,都会统一转换为base64编码,确保与API交互时图片数据格式的一致性。
2025-03-25 13:23:17 +08:00
5 changed files with 34 additions and 10 deletions

View File

@@ -72,3 +72,22 @@ class SecurityService:
raise HTTPException(status_code=401, detail="Invalid auth_token")
return token
async def verify_key_or_goog_api_key(
self, key: Optional[str] = None , x_goog_api_key: Optional[str] = Header(None)
) -> str:
"""验证URL中的key或请求头中的x-goog-api-key"""
# 如果URL中的key有效直接返回
if key in self.allowed_tokens or key == self.auth_token:
return key
# 否则检查请求头中的x-goog-api-key
if not x_goog_api_key:
logger.error("Invalid key and missing x-goog-api-key header")
raise HTTPException(status_code=401, detail="Invalid key and missing x-goog-api-key header")
if x_goog_api_key not in self.allowed_tokens and x_goog_api_key != self.auth_token:
logger.error("Invalid key and invalid x-goog-api-key")
raise HTTPException(status_code=401, detail="Invalid key and invalid x-goog-api-key")
return x_goog_api_key

View File

@@ -1,4 +1,4 @@
from typing import List, Optional, Dict, Any, Literal
from typing import List, Optional, Dict, Any, Literal, Union
from pydantic import BaseModel
@@ -34,7 +34,7 @@ class GeminiContent(BaseModel):
class GeminiRequest(BaseModel):
contents: List[GeminiContent] = []
tools: Optional[List[Dict[str, Any]]] = []
tools: Optional[Union[List[Dict[str, Any]], Dict[str, Any]]] = []
safetySettings: Optional[List[SafetySetting]] = None
generationConfig: Optional[GenerationConfig] = None
systemInstruction: Optional[SystemInstruction] = None

View File

@@ -49,11 +49,14 @@ def _convert_image(image_url: str) -> Dict[str, Any]:
"data": encoded_data
}
}
return {
"image_url": {
"url": image_url
else:
encoded_data = _convert_image_to_base64(image_url)
return {
"inline_data": {
"mime_type": "image/png",
"data": encoded_data
}
}
}
def _convert_image_to_base64(url: str) -> str:

View File

@@ -34,7 +34,7 @@ async def get_next_working_key(key_manager: KeyManager = Depends(get_key_manager
@router.get("/models")
@router_v1beta.get("/models")
async def list_models(
_=Depends(security_service.verify_key),
_=Depends(security_service.verify_key_or_goog_api_key),
key_manager: KeyManager = Depends(get_key_manager)
):
"""获取可用的Gemini模型列表"""
@@ -86,7 +86,7 @@ async def list_models(
async def generate_content(
model_name: str,
request: GeminiRequest,
_=Depends(security_service.verify_goog_api_key),
_=Depends(security_service.verify_key_or_goog_api_key),
api_key: str = Depends(get_next_working_key),
key_manager: KeyManager = Depends(get_key_manager)
):
@@ -118,7 +118,7 @@ async def generate_content(
async def stream_generate_content(
model_name: str,
request: GeminiRequest,
_=Depends(security_service.verify_goog_api_key),
_=Depends(security_service.verify_key_or_goog_api_key),
api_key: str = Depends(get_next_working_key),
key_manager: KeyManager = Depends(get_key_manager)
):

View File

@@ -44,6 +44,8 @@ def _build_tools(model: str, payload: Dict[str, Any]) -> List[Dict[str, Any]]:
tool = dict()
if payload and isinstance(payload, dict) and "tools" in payload:
if payload.get("tools") and isinstance(payload.get("tools"), dict):
payload["tools"] = [payload.get("tools")]
items = payload.get("tools", [])
if items and isinstance(items, list):
tool.update(_merge_tools(items))
@@ -62,7 +64,7 @@ def _build_tools(model: str, payload: Dict[str, Any]) -> List[Dict[str, Any]]:
tool.pop("googleSearch", None)
tool.pop("codeExecution", None)
return [tool]
return [tool] if tool else []
def _get_safety_settings(model: str) -> List[Dict[str, str]]: