mirror of
https://github.com/fawney19/Aether.git
synced 2026-09-03 01:40:21 +08:00
feat: 实现跨 API 格式自动转换功能
- 新增端点级 format_acceptance_config 配置,控制是否接受跨格式请求 - 重构 EndpointFormDialog 为卡片式布局,支持内联编辑和格式转换开关 - StreamProcessor 实现流式响应的跨格式转换,支持 OpenAI/Claude/Gemini 互转 - CacheAwareScheduler 按端点格式筛选候选,同格式优先于跨格式 - 健康度/熔断按 Provider 端点格式分桶,而非客户端请求格式 - 新增 format_conversion_total 和 format_conversion_duration_seconds 指标 - 新增全局配置 format_conversion_enabled 控制总开关 - Input 组件新增 size="sm" 尺寸选项
This commit is contained in:
@@ -278,7 +278,9 @@ class Usage(Base):
|
||||
request_id = Column(String(100), unique=True, index=True, nullable=False)
|
||||
provider_name = Column(String(100), nullable=False) # Provider 名称(非外键)
|
||||
model = Column(String(100), nullable=False)
|
||||
target_model = Column(String(100), nullable=True, comment="映射后的目标模型名(若无映射则为空)")
|
||||
target_model = Column(
|
||||
String(100), nullable=True, comment="映射后的目标模型名(若无映射则为空)"
|
||||
)
|
||||
|
||||
# Provider 侧追踪信息(记录最终成功的 Provider/Endpoint/Key)
|
||||
provider_id = Column(String(36), ForeignKey("providers.id", ondelete="SET NULL"), nullable=True)
|
||||
@@ -465,7 +467,9 @@ class LDAPConfig(Base):
|
||||
user_search_filter = Column(
|
||||
String(500), default="(uid={username})", nullable=False
|
||||
) # 用户搜索过滤器
|
||||
username_attr = Column(String(50), default="uid", nullable=False) # 用户名属性 (uid/sAMAccountName)
|
||||
username_attr = Column(
|
||||
String(50), default="uid", nullable=False
|
||||
) # 用户名属性 (uid/sAMAccountName)
|
||||
email_attr = Column(String(50), default="mail", nullable=False) # 邮箱属性
|
||||
display_name_attr = Column(String(50), default="cn", nullable=False) # 显示名称属性
|
||||
is_enabled = Column(Boolean, default=False, nullable=False) # 是否启用 LDAP 认证
|
||||
@@ -706,6 +710,14 @@ class ProviderEndpoint(Base):
|
||||
# 额外配置
|
||||
config = Column(JSON, nullable=True) # 端点特定配置(不推荐使用,优先使用专用字段)
|
||||
|
||||
# 格式转换配置
|
||||
format_acceptance_config = Column(
|
||||
JSON,
|
||||
nullable=True,
|
||||
default=None,
|
||||
comment="格式接受策略配置(跨格式转换开关/白黑名单等)",
|
||||
)
|
||||
|
||||
# 代理配置
|
||||
proxy = Column(JSONB, nullable=True) # 代理配置: {url, username, password}
|
||||
|
||||
@@ -1041,8 +1053,7 @@ class Model(Base):
|
||||
|
||||
# 获取所有最高优先级的映射
|
||||
top_priority_mappings = [
|
||||
mapping for mapping in sorted_mappings
|
||||
if mapping["priority"] == highest_priority
|
||||
mapping for mapping in sorted_mappings if mapping["priority"] == highest_priority
|
||||
]
|
||||
|
||||
# 如果有多个相同优先级的映射,通过哈希分散选择
|
||||
@@ -1119,9 +1130,7 @@ class ProviderAPIKey(Base):
|
||||
# 示例: {"cache_1h": true, "context_1m": true}
|
||||
|
||||
# 自适应 RPM 调整(仅当 rpm_limit = NULL 时生效)
|
||||
learned_rpm_limit = Column(
|
||||
Integer, nullable=True
|
||||
) # 学习到的 RPM 限制(自适应模式下的有效值)
|
||||
learned_rpm_limit = Column(Integer, nullable=True) # 学习到的 RPM 限制(自适应模式下的有效值)
|
||||
concurrent_429_count = Column(Integer, default=0, nullable=False) # 因并发导致的429次数
|
||||
rpm_429_count = Column(Integer, default=0, nullable=False) # 因RPM导致的429次数
|
||||
last_429_at = Column(DateTime(timezone=True), nullable=True) # 最后429时间
|
||||
@@ -1132,9 +1141,7 @@ class ProviderAPIKey(Base):
|
||||
utilization_samples = Column(
|
||||
JSON, nullable=True
|
||||
) # 利用率采样窗口 [{"ts": timestamp, "util": 0.8}, ...]
|
||||
last_probe_increase_at = Column(
|
||||
DateTime(timezone=True), nullable=True
|
||||
) # 上次探测性扩容时间
|
||||
last_probe_increase_at = Column(DateTime(timezone=True), nullable=True) # 上次探测性扩容时间
|
||||
|
||||
# 健康度追踪(按 API 格式存储)
|
||||
# 结构: {"CLAUDE": {"health_score": 1.0, "consecutive_failures": 0, "last_failure_at": null, "request_results_window": []}, ...}
|
||||
@@ -1568,7 +1575,9 @@ class RequestCandidate(Base):
|
||||
)
|
||||
|
||||
# 状态信息
|
||||
status = Column(String(20), nullable=False) # 'pending', 'streaming', 'success', 'failed', 'cancelled', 'skipped'
|
||||
status = Column(
|
||||
String(20), nullable=False
|
||||
) # 'pending', 'streaming', 'success', 'failed', 'cancelled', 'skipped'
|
||||
skip_reason = Column(Text, nullable=True) # 跳过/失败原因
|
||||
is_cached = Column(Boolean, default=False) # 是否为缓存亲和性候选
|
||||
|
||||
|
||||
@@ -45,6 +45,12 @@ class ProviderEndpointCreate(BaseModel):
|
||||
# 代理配置
|
||||
proxy: Optional[ProxyConfig] = Field(default=None, description="代理配置")
|
||||
|
||||
# 格式转换配置
|
||||
format_acceptance_config: Optional[Dict[str, Any]] = Field(
|
||||
default=None,
|
||||
description="格式接受策略配置(跨格式转换开关/白黑名单等)",
|
||||
)
|
||||
|
||||
@field_validator("api_format")
|
||||
@classmethod
|
||||
def validate_api_format(cls, v: str) -> str:
|
||||
@@ -85,6 +91,12 @@ class ProviderEndpointUpdate(BaseModel):
|
||||
config: Optional[Dict[str, Any]] = Field(default=None, description="额外配置")
|
||||
proxy: Optional[ProxyConfig] = Field(default=None, description="代理配置")
|
||||
|
||||
# 格式转换配置
|
||||
format_acceptance_config: Optional[Dict[str, Any]] = Field(
|
||||
default=None,
|
||||
description="格式接受策略配置(跨格式转换开关/白黑名单等)",
|
||||
)
|
||||
|
||||
@field_validator("base_url")
|
||||
@classmethod
|
||||
def validate_base_url(cls, v: Optional[str]) -> Optional[str]:
|
||||
@@ -126,6 +138,12 @@ class ProviderEndpointResponse(BaseModel):
|
||||
# 代理配置(响应中密码已脱敏)
|
||||
proxy: Optional[Dict[str, Any]] = Field(default=None, description="代理配置(密码已脱敏)")
|
||||
|
||||
# 格式转换配置
|
||||
format_acceptance_config: Optional[Dict[str, Any]] = Field(
|
||||
default=None,
|
||||
description="格式接受策略配置(跨格式转换开关/白黑名单等)",
|
||||
)
|
||||
|
||||
# 统计(从 Keys 聚合)
|
||||
total_keys: int = Field(default=0, description="总 Key 数量")
|
||||
active_keys: int = Field(default=0, description="活跃 Key 数量")
|
||||
|
||||
Reference in New Issue
Block a user