mirror of
https://github.com/fawney19/Aether.git
synced 2026-09-03 01:40:21 +08:00
style: 统一使用 PEP 604/585 现代类型语法
- Optional[X] → X | None - Dict[X, Y] → dict[X, Y] - List[X] → list[X] - Tuple[X, Y] → tuple[X, Y] - Set[X] → set[X] 涉及 10 个文件,共约 50 处改动。
This commit is contained in:
@@ -10,18 +10,18 @@
|
|||||||
self,
|
self,
|
||||||
request: Any, # 解析后的请求模型
|
request: Any, # 解析后的请求模型
|
||||||
http_request: Request, # FastAPI Request 对象
|
http_request: Request, # FastAPI Request 对象
|
||||||
original_headers: Dict[str, str], # 原始请求头
|
original_headers: dict[str, str], # 原始请求头
|
||||||
original_request_body: Dict[str, Any], # 原始请求体
|
original_request_body: dict[str, Any], # 原始请求体
|
||||||
query_params: Optional[Dict[str, str]] = None, # 查询参数
|
query_params: dict[str, str] | None = None, # 查询参数
|
||||||
) -> StreamingResponse: ...
|
) -> StreamingResponse: ...
|
||||||
|
|
||||||
async def process_sync(
|
async def process_sync(
|
||||||
self,
|
self,
|
||||||
request: Any,
|
request: Any,
|
||||||
http_request: Request,
|
http_request: Request,
|
||||||
original_headers: Dict[str, str],
|
original_headers: dict[str, str],
|
||||||
original_request_body: Dict[str, Any],
|
original_request_body: dict[str, Any],
|
||||||
query_params: Optional[Dict[str, str]] = None,
|
query_params: dict[str, str] | None = None,
|
||||||
) -> JSONResponse: ...
|
) -> JSONResponse: ...
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|||||||
@@ -565,7 +565,7 @@ class ChatAdapterBase(ApiAdapter):
|
|||||||
"cache_cost": float,
|
"cache_cost": float,
|
||||||
"request_cost": float,
|
"request_cost": float,
|
||||||
"total_cost": float,
|
"total_cost": float,
|
||||||
"tier_index": Optional[int], # 命中的阶梯索引
|
"tier_index": int | None, # 命中的阶梯索引
|
||||||
}
|
}
|
||||||
"""
|
"""
|
||||||
# 计算总输入上下文(使用子类可覆盖的方法)
|
# 计算总输入上下文(使用子类可覆盖的方法)
|
||||||
|
|||||||
@@ -61,9 +61,9 @@ class GeminiChatAdapter(ChatAdapterBase):
|
|||||||
|
|
||||||
def detect_capability_requirements(
|
def detect_capability_requirements(
|
||||||
self,
|
self,
|
||||||
headers: Dict[str, str], # noqa: ARG002 - 预留
|
headers: dict[str, str], # noqa: ARG002 - 预留
|
||||||
request_body: Optional[Dict[str, Any]] = None,
|
request_body: dict[str, Any] | None = None,
|
||||||
) -> Dict[str, bool]:
|
) -> dict[str, bool]:
|
||||||
"""检测是否需要 Gemini Files API 能力"""
|
"""检测是否需要 Gemini Files API 能力"""
|
||||||
if request_body and extract_file_names_from_request(request_body):
|
if request_body and extract_file_names_from_request(request_body):
|
||||||
return {"gemini_files_api": True}
|
return {"gemini_files_api": True}
|
||||||
|
|||||||
@@ -27,8 +27,8 @@ class GeminiChatHandler(ChatHandlerBase):
|
|||||||
async def _resolve_preferred_key_ids(
|
async def _resolve_preferred_key_ids(
|
||||||
self,
|
self,
|
||||||
model_name: str, # noqa: ARG002 - 仅做文件绑定
|
model_name: str, # noqa: ARG002 - 仅做文件绑定
|
||||||
request_body: Optional[Dict[str, Any]] = None,
|
request_body: dict[str, Any] | None = None,
|
||||||
) -> Optional[list[str]]:
|
) -> list[str] | None:
|
||||||
"""
|
"""
|
||||||
从 files/xxx 绑定关系中解析优先 Key ID 列表。
|
从 files/xxx 绑定关系中解析优先 Key ID 列表。
|
||||||
|
|
||||||
|
|||||||
@@ -60,7 +60,7 @@ HEADERS_TO_REMOVE = frozenset({
|
|||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
def _extract_gemini_api_key(request: Request) -> Optional[str]:
|
def _extract_gemini_api_key(request: Request) -> str | None:
|
||||||
"""
|
"""
|
||||||
从请求中提取 Gemini API Key
|
从请求中提取 Gemini API Key
|
||||||
|
|
||||||
@@ -76,9 +76,9 @@ def _extract_gemini_api_key(request: Request) -> Optional[str]:
|
|||||||
|
|
||||||
|
|
||||||
def _build_upstream_headers(
|
def _build_upstream_headers(
|
||||||
original_headers: Dict[str, str],
|
original_headers: dict[str, str],
|
||||||
upstream_api_key: str,
|
upstream_api_key: str,
|
||||||
) -> Dict[str, str]:
|
) -> dict[str, str]:
|
||||||
"""
|
"""
|
||||||
构建上游请求头
|
构建上游请求头
|
||||||
|
|
||||||
@@ -105,7 +105,7 @@ def _build_upstream_headers(
|
|||||||
def _build_upstream_url(
|
def _build_upstream_url(
|
||||||
base_url: str,
|
base_url: str,
|
||||||
path: str,
|
path: str,
|
||||||
query_params: Optional[Dict[str, Any]] = None,
|
query_params: dict[str, Any] | None = None,
|
||||||
is_upload: bool = False,
|
is_upload: bool = False,
|
||||||
) -> str:
|
) -> str:
|
||||||
"""
|
"""
|
||||||
@@ -145,8 +145,8 @@ def _build_upstream_url(
|
|||||||
def _resolve_files_model_name(
|
def _resolve_files_model_name(
|
||||||
db: Session,
|
db: Session,
|
||||||
user_api_key: ApiKey,
|
user_api_key: ApiKey,
|
||||||
user: Optional[User],
|
user: User | None,
|
||||||
) -> Optional[str]:
|
) -> str | None:
|
||||||
"""
|
"""
|
||||||
为 Files API 选择一个可用的模型名(用于 Key 选择与权限过滤)
|
为 Files API 选择一个可用的模型名(用于 Key 选择与权限过滤)
|
||||||
|
|
||||||
@@ -188,7 +188,7 @@ async def _select_provider_candidate(
|
|||||||
db: Session,
|
db: Session,
|
||||||
user_api_key: ApiKey,
|
user_api_key: ApiKey,
|
||||||
model_name: str,
|
model_name: str,
|
||||||
) -> Optional[ProviderCandidate]:
|
) -> ProviderCandidate | None:
|
||||||
"""选择支持 Files API 的 Provider/Endpoint/Key 组合"""
|
"""选择支持 Files API 的 Provider/Endpoint/Key 组合"""
|
||||||
scheduler = CacheAwareScheduler()
|
scheduler = CacheAwareScheduler()
|
||||||
candidates, _global_model_id = await scheduler.list_all_candidates(
|
candidates, _global_model_id = await scheduler.list_all_candidates(
|
||||||
@@ -210,7 +210,7 @@ async def _select_provider_candidate(
|
|||||||
async def _resolve_upstream_context(
|
async def _resolve_upstream_context(
|
||||||
request: Request,
|
request: Request,
|
||||||
db: Session,
|
db: Session,
|
||||||
) -> Tuple[str, str, str]:
|
) -> tuple[str, str, str]:
|
||||||
"""
|
"""
|
||||||
解析上游 Key 与 Base URL
|
解析上游 Key 与 Base URL
|
||||||
|
|
||||||
@@ -291,10 +291,10 @@ async def _resolve_upstream_context(
|
|||||||
async def _proxy_request(
|
async def _proxy_request(
|
||||||
method: str,
|
method: str,
|
||||||
upstream_url: str,
|
upstream_url: str,
|
||||||
headers: Dict[str, str],
|
headers: dict[str, str],
|
||||||
content: Optional[bytes] = None,
|
content: bytes | None = None,
|
||||||
json_body: Optional[Dict[str, Any]] = None,
|
json_body: dict[str, Any] | None = None,
|
||||||
file_key_id: Optional[str] = None,
|
file_key_id: str | None = None,
|
||||||
) -> Response:
|
) -> Response:
|
||||||
"""
|
"""
|
||||||
代理请求到上游 Gemini API
|
代理请求到上游 Gemini API
|
||||||
@@ -461,8 +461,8 @@ async def upload_file(
|
|||||||
async def list_files(
|
async def list_files(
|
||||||
request: Request,
|
request: Request,
|
||||||
db: Session = Depends(get_db),
|
db: Session = Depends(get_db),
|
||||||
pageSize: Optional[int] = None,
|
pageSize: int | None = None,
|
||||||
pageToken: Optional[str] = None,
|
pageToken: str | None = None,
|
||||||
):
|
):
|
||||||
"""
|
"""
|
||||||
列出已上传的文件
|
列出已上传的文件
|
||||||
|
|||||||
@@ -51,7 +51,7 @@ class VertexAuthService:
|
|||||||
|
|
||||||
# Token 缓存:使用 OrderedDict 实现 LRU
|
# Token 缓存:使用 OrderedDict 实现 LRU
|
||||||
# key = client_email, value = (token, expires_at)
|
# key = client_email, value = (token, expires_at)
|
||||||
_token_cache: OrderedDict[str, Tuple[str, float]] = OrderedDict()
|
_token_cache: OrderedDict[str, tuple[str, float]] = OrderedDict()
|
||||||
_cache_max_size: int = 100 # 最多缓存 100 个 Service Account 的 Token
|
_cache_max_size: int = 100 # 最多缓存 100 个 Service Account 的 Token
|
||||||
|
|
||||||
# Token 请求端点
|
# Token 请求端点
|
||||||
@@ -168,7 +168,7 @@ class VertexAuthService:
|
|||||||
raise VertexAuthError(f"Failed to get access token: {e}")
|
raise VertexAuthError(f"Failed to get access token: {e}")
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def clear_cache(cls, client_email: Optional[str] = None) -> None:
|
def clear_cache(cls, client_email: str | None = None) -> None:
|
||||||
"""
|
"""
|
||||||
清除 Token 缓存
|
清除 Token 缓存
|
||||||
|
|
||||||
@@ -181,7 +181,7 @@ class VertexAuthService:
|
|||||||
cls._token_cache.clear()
|
cls._token_cache.clear()
|
||||||
|
|
||||||
|
|
||||||
async def get_vertex_access_token(service_account_json: str) -> Tuple[str, str]:
|
async def get_vertex_access_token(service_account_json: str) -> tuple[str, str]:
|
||||||
"""
|
"""
|
||||||
便捷函数:获取 Vertex AI Access Token 和 Project ID
|
便捷函数:获取 Vertex AI Access Token 和 Project ID
|
||||||
|
|
||||||
|
|||||||
@@ -55,7 +55,7 @@ class GeminiRequest(BaseModelWithExtras):
|
|||||||
|
|
||||||
model: str | None = Field(default=None, description="模型名称,从 URL 路径提取(内部使用)")
|
model: str | None = Field(default=None, description="模型名称,从 URL 路径提取(内部使用)")
|
||||||
contents: list[GeminiContent]
|
contents: list[GeminiContent]
|
||||||
# 以下字段全部使用 Dict[str, Any] 透传,不做结构验证
|
# 以下字段全部使用 dict[str, Any] 透传,不做结构验证
|
||||||
system_instruction: dict[str, Any] | None = Field(default=None, alias="systemInstruction")
|
system_instruction: dict[str, Any] | None = Field(default=None, alias="systemInstruction")
|
||||||
tools: list[dict[str, Any]] | None = None
|
tools: list[dict[str, Any]] | None = None
|
||||||
tool_config: dict[str, Any] | None = Field(default=None, alias="toolConfig")
|
tool_config: dict[str, Any] | None = Field(default=None, alias="toolConfig")
|
||||||
@@ -88,7 +88,7 @@ class GeminiFileMetadata(BaseModelWithExtras):
|
|||||||
用于上传文件时指定的元数据信息
|
用于上传文件时指定的元数据信息
|
||||||
"""
|
"""
|
||||||
|
|
||||||
display_name: Optional[str] = Field(default=None, alias="displayName")
|
display_name: str | None = Field(default=None, alias="displayName")
|
||||||
|
|
||||||
|
|
||||||
class GeminiFileUploadRequest(BaseModelWithExtras):
|
class GeminiFileUploadRequest(BaseModelWithExtras):
|
||||||
@@ -98,7 +98,7 @@ class GeminiFileUploadRequest(BaseModelWithExtras):
|
|||||||
用于 media.upload API 的请求体
|
用于 media.upload API 的请求体
|
||||||
"""
|
"""
|
||||||
|
|
||||||
file: Optional[GeminiFileMetadata] = None
|
file: GeminiFileMetadata | None = None
|
||||||
|
|
||||||
|
|
||||||
class GeminiFile(BaseModelWithExtras):
|
class GeminiFile(BaseModelWithExtras):
|
||||||
@@ -108,20 +108,20 @@ class GeminiFile(BaseModelWithExtras):
|
|||||||
表示已上传到 Gemini API 的文件
|
表示已上传到 Gemini API 的文件
|
||||||
"""
|
"""
|
||||||
|
|
||||||
name: Optional[str] = None # 文件名,格式:files/xxx
|
name: str | None = None # 文件名,格式:files/xxx
|
||||||
display_name: Optional[str] = Field(default=None, alias="displayName")
|
display_name: str | None = Field(default=None, alias="displayName")
|
||||||
mime_type: Optional[str] = Field(default=None, alias="mimeType")
|
mime_type: str | None = Field(default=None, alias="mimeType")
|
||||||
size_bytes: Optional[str] = Field(default=None, alias="sizeBytes")
|
size_bytes: str | None = Field(default=None, alias="sizeBytes")
|
||||||
create_time: Optional[str] = Field(default=None, alias="createTime")
|
create_time: str | None = Field(default=None, alias="createTime")
|
||||||
update_time: Optional[str] = Field(default=None, alias="updateTime")
|
update_time: str | None = Field(default=None, alias="updateTime")
|
||||||
expiration_time: Optional[str] = Field(default=None, alias="expirationTime")
|
expiration_time: str | None = Field(default=None, alias="expirationTime")
|
||||||
sha256_hash: Optional[str] = Field(default=None, alias="sha256Hash")
|
sha256_hash: str | None = Field(default=None, alias="sha256Hash")
|
||||||
uri: Optional[str] = None # 文件 URI,用于在请求中引用
|
uri: str | None = None # 文件 URI,用于在请求中引用
|
||||||
download_uri: Optional[str] = Field(default=None, alias="downloadUri")
|
download_uri: str | None = Field(default=None, alias="downloadUri")
|
||||||
state: Optional[str] = None # PROCESSING, ACTIVE, FAILED
|
state: str | None = None # PROCESSING, ACTIVE, FAILED
|
||||||
error: Optional[Dict[str, Any]] = None
|
error: dict[str, Any] | None = None
|
||||||
# 视频文件元数据
|
# 视频文件元数据
|
||||||
video_metadata: Optional[Dict[str, Any]] = Field(default=None, alias="videoMetadata")
|
video_metadata: dict[str, Any] | None = Field(default=None, alias="videoMetadata")
|
||||||
|
|
||||||
|
|
||||||
class GeminiFileListResponse(BaseModelWithExtras):
|
class GeminiFileListResponse(BaseModelWithExtras):
|
||||||
@@ -131,8 +131,8 @@ class GeminiFileListResponse(BaseModelWithExtras):
|
|||||||
用于 files.list API 的响应体
|
用于 files.list API 的响应体
|
||||||
"""
|
"""
|
||||||
|
|
||||||
files: Optional[List["GeminiFile"]] = None
|
files: list["GeminiFile"] | None = None
|
||||||
next_page_token: Optional[str] = Field(default=None, alias="nextPageToken")
|
next_page_token: str | None = Field(default=None, alias="nextPageToken")
|
||||||
|
|
||||||
|
|
||||||
class GeminiFileUploadResponse(BaseModelWithExtras):
|
class GeminiFileUploadResponse(BaseModelWithExtras):
|
||||||
@@ -142,7 +142,7 @@ class GeminiFileUploadResponse(BaseModelWithExtras):
|
|||||||
用于 media.upload API 的响应体
|
用于 media.upload API 的响应体
|
||||||
"""
|
"""
|
||||||
|
|
||||||
file: Optional[GeminiFile] = None
|
file: GeminiFile | None = None
|
||||||
|
|
||||||
|
|
||||||
class GeminiFilePart(BaseModelWithExtras):
|
class GeminiFilePart(BaseModelWithExtras):
|
||||||
@@ -153,7 +153,7 @@ class GeminiFilePart(BaseModelWithExtras):
|
|||||||
使用 file_data 字段引用文件 URI
|
使用 file_data 字段引用文件 URI
|
||||||
"""
|
"""
|
||||||
|
|
||||||
file_data: Optional[Dict[str, Any]] = Field(default=None, alias="fileData")
|
file_data: dict[str, Any] | None = Field(default=None, alias="fileData")
|
||||||
# fileData 格式:{"mimeType": "...", "fileUri": "..."}
|
# fileData 格式:{"mimeType": "...", "fileUri": "..."}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -295,7 +295,7 @@ def calculate_request_cost(
|
|||||||
"cache_cost": float,
|
"cache_cost": float,
|
||||||
"request_cost": float,
|
"request_cost": float,
|
||||||
"total_cost": float,
|
"total_cost": float,
|
||||||
"tier_index": Optional[int],
|
"tier_index": int | None,
|
||||||
}
|
}
|
||||||
"""
|
"""
|
||||||
# 构建标准化 usage
|
# 构建标准化 usage
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ async def store_file_key_mapping(file_name: str, key_id: str) -> None:
|
|||||||
await CacheService.set(cache_key, str(key_id), ttl_seconds=FILE_MAPPING_TTL_SECONDS)
|
await CacheService.set(cache_key, str(key_id), ttl_seconds=FILE_MAPPING_TTL_SECONDS)
|
||||||
|
|
||||||
|
|
||||||
async def get_file_key_mapping(file_name: str) -> Optional[str]:
|
async def get_file_key_mapping(file_name: str) -> str | None:
|
||||||
cache_key = build_file_mapping_key(file_name)
|
cache_key = build_file_mapping_key(file_name)
|
||||||
if not cache_key:
|
if not cache_key:
|
||||||
return None
|
return None
|
||||||
@@ -50,7 +50,7 @@ async def delete_file_key_mapping(file_name: str) -> None:
|
|||||||
await CacheService.delete(cache_key)
|
await CacheService.delete(cache_key)
|
||||||
|
|
||||||
|
|
||||||
def _extract_file_name_from_uri(file_uri: str) -> Optional[str]:
|
def _extract_file_name_from_uri(file_uri: str) -> str | None:
|
||||||
"""
|
"""
|
||||||
从 fileUri 提取 files/xxx 名称。
|
从 fileUri 提取 files/xxx 名称。
|
||||||
|
|
||||||
@@ -70,11 +70,11 @@ def _extract_file_name_from_uri(file_uri: str) -> Optional[str]:
|
|||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
def extract_file_names_from_request(payload: Optional[Dict[str, Any]]) -> Set[str]:
|
def extract_file_names_from_request(payload: dict[str, Any] | None) -> set[str]:
|
||||||
"""
|
"""
|
||||||
从 Gemini 请求体中提取 fileUri 使用到的 files/xxx 名称集合。
|
从 Gemini 请求体中提取 fileUri 使用到的 files/xxx 名称集合。
|
||||||
"""
|
"""
|
||||||
results: Set[str] = set()
|
results: set[str] = set()
|
||||||
|
|
||||||
def walk(node: Any) -> None:
|
def walk(node: Any) -> None:
|
||||||
if isinstance(node, dict):
|
if isinstance(node, dict):
|
||||||
|
|||||||
@@ -72,8 +72,8 @@ def build_provider_url(
|
|||||||
query_params: dict[str, Any] | None = None,
|
query_params: dict[str, Any] | None = None,
|
||||||
path_params: dict[str, Any] | None = None,
|
path_params: dict[str, Any] | None = None,
|
||||||
is_stream: bool = False,
|
is_stream: bool = False,
|
||||||
key: Optional["ProviderAPIKey"] = None,
|
key: "ProviderAPIKey" | None = None,
|
||||||
decrypted_auth_config: Optional[Dict[str, Any]] = None,
|
decrypted_auth_config: dict[str, Any] | None = None,
|
||||||
) -> str:
|
) -> str:
|
||||||
"""
|
"""
|
||||||
根据 endpoint 配置生成请求 URL
|
根据 endpoint 配置生成请求 URL
|
||||||
@@ -175,7 +175,7 @@ def _resolve_default_path(api_format: str | None) -> str:
|
|||||||
|
|
||||||
# Vertex AI 模型默认 region 映射
|
# Vertex AI 模型默认 region 映射
|
||||||
# 用户可以通过 auth_config.model_regions 覆盖
|
# 用户可以通过 auth_config.model_regions 覆盖
|
||||||
VERTEX_AI_DEFAULT_MODEL_REGIONS: Dict[str, str] = {
|
VERTEX_AI_DEFAULT_MODEL_REGIONS: dict[str, str] = {
|
||||||
# Gemini 3 系列(使用 global)
|
# Gemini 3 系列(使用 global)
|
||||||
"gemini-3-pro-image-preview": "global",
|
"gemini-3-pro-image-preview": "global",
|
||||||
# Gemini 2.0 系列
|
# Gemini 2.0 系列
|
||||||
@@ -200,10 +200,10 @@ VERTEX_AI_DEFAULT_MODEL_REGIONS: Dict[str, str] = {
|
|||||||
def _build_vertex_ai_url(
|
def _build_vertex_ai_url(
|
||||||
key: "ProviderAPIKey",
|
key: "ProviderAPIKey",
|
||||||
*,
|
*,
|
||||||
path_params: Optional[Dict[str, Any]] = None,
|
path_params: dict[str, Any] | None = None,
|
||||||
query_params: Optional[Dict[str, Any]] = None,
|
query_params: dict[str, Any] | None = None,
|
||||||
is_stream: bool = False,
|
is_stream: bool = False,
|
||||||
decrypted_auth_config: Optional[Dict[str, Any]] = None,
|
decrypted_auth_config: dict[str, Any] | None = None,
|
||||||
) -> str:
|
) -> str:
|
||||||
"""
|
"""
|
||||||
构建 Vertex AI URL
|
构建 Vertex AI URL
|
||||||
@@ -236,7 +236,7 @@ def _build_vertex_ai_url(
|
|||||||
from src.core.crypto import crypto_service
|
from src.core.crypto import crypto_service
|
||||||
|
|
||||||
# 优先使用传入的已解密配置,避免重复解密
|
# 优先使用传入的已解密配置,避免重复解密
|
||||||
auth_config: Dict[str, Any] = {}
|
auth_config: dict[str, Any] = {}
|
||||||
if decrypted_auth_config:
|
if decrypted_auth_config:
|
||||||
auth_config = decrypted_auth_config
|
auth_config = decrypted_auth_config
|
||||||
else:
|
else:
|
||||||
|
|||||||
Reference in New Issue
Block a user