mirror of
https://github.com/fawney19/Aether.git
synced 2026-09-02 17:30:23 +08:00
feat: add Vertex AI authentication support for provider API keys
- Add auth_type field to ProviderAPIKey model (api_key or vertex_ai) - Implement Vertex AI OAuth token generation with service account - Update transport layer to handle Vertex AI authentication - Add Vertex AI endpoint URL generation in request builder - Update frontend KeyFormDialog to support auth_type selection - Add migration for auth_type column in provider_api_keys table
This commit is contained in:
@@ -1111,8 +1111,22 @@ class ProviderAPIKey(Base):
|
||||
# None 表示支持所有格式(兼容历史数据),空列表 [] 表示不支持任何格式
|
||||
api_formats = Column(JSON, nullable=True, default=list) # ["CLAUDE", "CLAUDE_CLI"]
|
||||
|
||||
# API密钥信息
|
||||
api_key = Column(String(500), nullable=False) # API密钥(加密存储)
|
||||
# 认证类型
|
||||
# - "api_key": 标准 API Key 认证(默认)
|
||||
# - "vertex_ai": Google Vertex AI 认证(Service Account JSON)
|
||||
# - 未来可扩展:oauth2, azure_ad, aws_iam 等
|
||||
auth_type = Column(String(20), default="api_key", nullable=False)
|
||||
|
||||
# API密钥(加密存储)
|
||||
# - auth_type="api_key" 时:存储 API Key 字符串
|
||||
# - auth_type="vertex_ai" 等:可为空,敏感凭证存在 auth_config 中
|
||||
api_key = Column(String(500), nullable=False) # 保持 NOT NULL 兼容历史数据
|
||||
|
||||
# 认证配置(加密存储)
|
||||
# - auth_type="api_key" 时:可为空
|
||||
# - auth_type="vertex_ai" 时:存储加密后的 Service Account JSON
|
||||
# - auth_type="oauth2" 时:存储加密后的 {client_id, client_secret, token_url, scope}
|
||||
auth_config = Column(Text, nullable=True)
|
||||
name = Column(String(100), nullable=False) # 密钥名称(必填,用于识别)
|
||||
note = Column(String(500), nullable=True) # 备注说明(可选)
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@ ProviderEndpoint 相关的 API 模型定义
|
||||
|
||||
import re
|
||||
from datetime import datetime
|
||||
from typing import Any, Dict, List, Optional
|
||||
from typing import Any, Dict, List, Literal, Optional
|
||||
|
||||
from pydantic import BaseModel, ConfigDict, Field, field_validator
|
||||
|
||||
@@ -166,7 +166,15 @@ class EndpointAPIKeyCreate(BaseModel):
|
||||
default=None, min_length=1, description="支持的 API 格式列表(必填,路由层校验)"
|
||||
)
|
||||
|
||||
api_key: str = Field(..., min_length=3, max_length=500, description="API Key(将自动加密)")
|
||||
api_key: str = Field(default="", max_length=500, description="API Key(标准认证时必填,将自动加密)")
|
||||
auth_type: Literal["api_key", "vertex_ai"] = Field(
|
||||
default="api_key",
|
||||
description="认证类型:api_key(标准 API Key)或 vertex_ai(Vertex AI Service Account)"
|
||||
)
|
||||
auth_config: Optional[Dict[str, Any]] = Field(
|
||||
default=None,
|
||||
description="认证配置(JSON):vertex_ai 时存储完整 Service Account JSON"
|
||||
)
|
||||
name: str = Field(..., min_length=1, max_length=100, description="密钥名称(必填,用于识别)")
|
||||
|
||||
# 成本计算
|
||||
@@ -313,7 +321,15 @@ class EndpointAPIKeyUpdate(BaseModel):
|
||||
)
|
||||
|
||||
api_key: Optional[str] = Field(
|
||||
default=None, min_length=3, max_length=500, description="API Key(将自动加密)"
|
||||
default=None, min_length=3, max_length=500, description="API Key(标准认证时使用,将自动加密)"
|
||||
)
|
||||
auth_type: Optional[Literal["api_key", "vertex_ai"]] = Field(
|
||||
default=None,
|
||||
description="认证类型:api_key(标准 API Key)或 vertex_ai(Vertex AI Service Account)"
|
||||
)
|
||||
auth_config: Optional[Dict[str, Any]] = Field(
|
||||
default=None,
|
||||
description="认证配置(JSON):vertex_ai 时存储完整 Service Account JSON"
|
||||
)
|
||||
name: Optional[str] = Field(default=None, min_length=1, max_length=100, description="密钥名称")
|
||||
rate_multipliers: Optional[Dict[str, float]] = Field(
|
||||
@@ -445,6 +461,8 @@ class EndpointAPIKeyResponse(BaseModel):
|
||||
# Key 信息(脱敏)
|
||||
api_key_masked: str = Field(..., description="脱敏后的 Key")
|
||||
api_key_plain: Optional[str] = Field(default=None, description="完整的 Key")
|
||||
auth_type: str = Field(default="api_key", description="认证类型:api_key 或 vertex_ai")
|
||||
# auth_config 不在响应中返回(包含敏感信息),前端通过 auth_type 判断类型
|
||||
name: str = Field(..., description="密钥名称")
|
||||
|
||||
# 成本计算
|
||||
|
||||
@@ -74,6 +74,87 @@ class GeminiUsageMetadata(BaseModelWithExtras):
|
||||
total_token_count: int = Field(default=0, alias="totalTokenCount")
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# 文件 API 模型
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
class GeminiFileMetadata(BaseModelWithExtras):
|
||||
"""
|
||||
Gemini 文件元数据
|
||||
|
||||
用于上传文件时指定的元数据信息
|
||||
"""
|
||||
|
||||
display_name: Optional[str] = Field(default=None, alias="displayName")
|
||||
|
||||
|
||||
class GeminiFileUploadRequest(BaseModelWithExtras):
|
||||
"""
|
||||
Gemini 文件上传请求
|
||||
|
||||
用于 media.upload API 的请求体
|
||||
"""
|
||||
|
||||
file: Optional[GeminiFileMetadata] = None
|
||||
|
||||
|
||||
class GeminiFile(BaseModelWithExtras):
|
||||
"""
|
||||
Gemini 文件资源
|
||||
|
||||
表示已上传到 Gemini API 的文件
|
||||
"""
|
||||
|
||||
name: Optional[str] = None # 文件名,格式:files/xxx
|
||||
display_name: Optional[str] = Field(default=None, alias="displayName")
|
||||
mime_type: Optional[str] = Field(default=None, alias="mimeType")
|
||||
size_bytes: Optional[str] = Field(default=None, alias="sizeBytes")
|
||||
create_time: Optional[str] = Field(default=None, alias="createTime")
|
||||
update_time: Optional[str] = Field(default=None, alias="updateTime")
|
||||
expiration_time: Optional[str] = Field(default=None, alias="expirationTime")
|
||||
sha256_hash: Optional[str] = Field(default=None, alias="sha256Hash")
|
||||
uri: Optional[str] = None # 文件 URI,用于在请求中引用
|
||||
download_uri: Optional[str] = Field(default=None, alias="downloadUri")
|
||||
state: Optional[str] = None # PROCESSING, ACTIVE, FAILED
|
||||
error: Optional[Dict[str, Any]] = None
|
||||
# 视频文件元数据
|
||||
video_metadata: Optional[Dict[str, Any]] = Field(default=None, alias="videoMetadata")
|
||||
|
||||
|
||||
class GeminiFileListResponse(BaseModelWithExtras):
|
||||
"""
|
||||
Gemini 文件列表响应
|
||||
|
||||
用于 files.list API 的响应体
|
||||
"""
|
||||
|
||||
files: Optional[List["GeminiFile"]] = None
|
||||
next_page_token: Optional[str] = Field(default=None, alias="nextPageToken")
|
||||
|
||||
|
||||
class GeminiFileUploadResponse(BaseModelWithExtras):
|
||||
"""
|
||||
Gemini 文件上传响应
|
||||
|
||||
用于 media.upload API 的响应体
|
||||
"""
|
||||
|
||||
file: Optional[GeminiFile] = None
|
||||
|
||||
|
||||
class GeminiFilePart(BaseModelWithExtras):
|
||||
"""
|
||||
Gemini 文件引用部分
|
||||
|
||||
用于在请求内容中引用已上传的文件
|
||||
使用 file_data 字段引用文件 URI
|
||||
"""
|
||||
|
||||
file_data: Optional[Dict[str, Any]] = Field(default=None, alias="fileData")
|
||||
# fileData 格式:{"mimeType": "...", "fileUri": "..."}
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Thought Signature 常量
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
Reference in New Issue
Block a user