mirror of
https://github.com/fawney19/Aether.git
synced 2026-09-02 01:10:23 +08:00
refactor: 移除 Provider/Endpoint 级别的 timeout 配置,改用全局环境变量
- 废弃 Provider.timeout 和 ProviderEndpoint.timeout 字段(保留数据库列以兼容) - 非流式请求超时由 HTTP_REQUEST_TIMEOUT 环境变量控制(默认 300 秒) - 流式请求首字节超时由 STREAM_FIRST_BYTE_TIMEOUT 环境变量控制(默认 30 秒) - 移除前端表单中的超时配置输入框 - 简化 settings.py 中的 TTFB 超时解析逻辑 - 更新 API 请求/响应模型,移除 timeout 字段 - 更新导入导出功能,不再包含 timeout 配置
This commit is contained in:
@@ -65,7 +65,6 @@ async def list_provider_endpoints(
|
||||
- `api_format`: API 格式
|
||||
- `base_url`: 基础 URL
|
||||
- `custom_path`: 自定义路径
|
||||
- `timeout`: 超时时间(秒)
|
||||
- `max_retries`: 最大重试次数
|
||||
- `is_active`: 是否活跃
|
||||
- `total_keys`: Key 总数
|
||||
@@ -103,7 +102,6 @@ async def create_provider_endpoint(
|
||||
- `base_url`: 基础 URL
|
||||
- `custom_path`: 自定义路径(可选)
|
||||
- `header_rules`: 请求头规则列表(可选,支持 set/drop/rename 操作)
|
||||
- `timeout`: 超时时间(秒,默认 300)
|
||||
- `max_retries`: 最大重试次数(默认 2)
|
||||
- `config`: 额外配置(可选)
|
||||
- `proxy`: 代理配置(可选)
|
||||
@@ -139,7 +137,6 @@ async def get_endpoint(
|
||||
- `api_format`: API 格式
|
||||
- `base_url`: 基础 URL
|
||||
- `custom_path`: 自定义路径
|
||||
- `timeout`: 超时时间(秒)
|
||||
- `max_retries`: 最大重试次数
|
||||
- `is_active`: 是否活跃
|
||||
- `total_keys`: Key 总数
|
||||
@@ -170,7 +167,6 @@ async def update_endpoint(
|
||||
- `base_url`: 基础 URL
|
||||
- `custom_path`: 自定义路径
|
||||
- `header_rules`: 请求头规则列表
|
||||
- `timeout`: 超时时间(秒)
|
||||
- `max_retries`: 最大重试次数
|
||||
- `is_active`: 是否活跃
|
||||
- `config`: 额外配置
|
||||
@@ -306,7 +302,6 @@ class AdminCreateProviderEndpointAdapter(AdminApiAdapter):
|
||||
base_url=self.endpoint_data.base_url,
|
||||
custom_path=self.endpoint_data.custom_path,
|
||||
header_rules=self.endpoint_data.header_rules,
|
||||
timeout=self.endpoint_data.timeout,
|
||||
max_retries=self.endpoint_data.max_retries,
|
||||
is_active=True,
|
||||
config=self.endpoint_data.config,
|
||||
|
||||
@@ -317,14 +317,14 @@ async def test_model(
|
||||
logger.error(f"[test-model] Failed to decrypt API key: {e}")
|
||||
raise HTTPException(status_code=500, detail="Failed to decrypt API key")
|
||||
|
||||
# 构建请求配置(timeout 从 Provider 读取)
|
||||
# 构建请求配置
|
||||
endpoint_config = {
|
||||
"api_key": api_key_value,
|
||||
"api_key_id": api_key.id, # 添加API Key ID用于用量记录
|
||||
"base_url": endpoint.base_url,
|
||||
"api_format": endpoint.api_format,
|
||||
"extra_headers": get_extra_headers_from_endpoint(endpoint),
|
||||
"timeout": provider.timeout or TimeoutDefaults.HTTP_REQUEST,
|
||||
"timeout": TimeoutDefaults.HTTP_REQUEST,
|
||||
}
|
||||
|
||||
try:
|
||||
|
||||
@@ -140,7 +140,6 @@ async def create_provider(request: Request, db: Session = Depends(get_db)):
|
||||
- `provider_priority`: 提供商优先级(数字越小优先级越高,默认 100)
|
||||
- `is_active`: 是否启用(默认 true)
|
||||
- `concurrent_limit`: 并发限制(可选)
|
||||
- `timeout`: 请求超时(秒,可选)
|
||||
- `max_retries`: 最大重试次数(可选)
|
||||
- `proxy`: 代理配置(可选)
|
||||
- `config`: 额外配置信息(JSON,可选)
|
||||
@@ -176,7 +175,6 @@ async def update_provider(provider_id: str, request: Request, db: Session = Depe
|
||||
- `provider_priority`: 提供商优先级
|
||||
- `is_active`: 是否启用
|
||||
- `concurrent_limit`: 并发限制
|
||||
- `timeout`: 请求超时(秒)
|
||||
- `max_retries`: 最大重试次数
|
||||
- `proxy`: 代理配置
|
||||
- `config`: 额外配置信息(JSON)
|
||||
@@ -293,7 +291,6 @@ class AdminCreateProviderAdapter(AdminApiAdapter):
|
||||
provider_priority=validated_data.provider_priority,
|
||||
is_active=validated_data.is_active,
|
||||
concurrent_limit=validated_data.concurrent_limit,
|
||||
timeout=validated_data.timeout,
|
||||
max_retries=validated_data.max_retries,
|
||||
proxy=validated_data.proxy.model_dump() if validated_data.proxy else None,
|
||||
config=validated_data.config,
|
||||
|
||||
@@ -302,7 +302,6 @@ def _build_provider_summary(db: Session, provider: Provider) -> ProviderWithEndp
|
||||
quota_reset_day=provider.quota_reset_day,
|
||||
quota_last_reset_at=provider.quota_last_reset_at,
|
||||
quota_expires_at=provider.quota_expires_at,
|
||||
timeout=provider.timeout,
|
||||
max_retries=provider.max_retries,
|
||||
proxy=provider.proxy,
|
||||
total_endpoints=total_endpoints,
|
||||
|
||||
@@ -774,7 +774,6 @@ class AdminExportConfigAdapter(AdminApiAdapter):
|
||||
"api_format": ep.api_format,
|
||||
"base_url": ep.base_url,
|
||||
"header_rules": ep.header_rules,
|
||||
"timeout": ep.timeout,
|
||||
"max_retries": ep.max_retries,
|
||||
"is_active": ep.is_active,
|
||||
"custom_path": ep.custom_path,
|
||||
@@ -855,7 +854,6 @@ class AdminExportConfigAdapter(AdminApiAdapter):
|
||||
"provider_priority": provider.provider_priority,
|
||||
"is_active": provider.is_active,
|
||||
"concurrent_limit": provider.concurrent_limit,
|
||||
"timeout": provider.timeout,
|
||||
"max_retries": provider.max_retries,
|
||||
"proxy": provider.proxy,
|
||||
"config": provider.config,
|
||||
@@ -1006,7 +1004,6 @@ class AdminImportConfigAdapter(AdminApiAdapter):
|
||||
existing_provider.concurrent_limit = prov_data.get(
|
||||
"concurrent_limit"
|
||||
)
|
||||
existing_provider.timeout = prov_data.get("timeout", existing_provider.timeout)
|
||||
existing_provider.max_retries = prov_data.get(
|
||||
"max_retries", existing_provider.max_retries
|
||||
)
|
||||
@@ -1031,7 +1028,6 @@ class AdminImportConfigAdapter(AdminApiAdapter):
|
||||
provider_priority=prov_data.get("provider_priority", 100),
|
||||
is_active=prov_data.get("is_active", True),
|
||||
concurrent_limit=prov_data.get("concurrent_limit"),
|
||||
timeout=prov_data.get("timeout"),
|
||||
max_retries=prov_data.get("max_retries"),
|
||||
proxy=prov_data.get("proxy"),
|
||||
config=prov_data.get("config"),
|
||||
@@ -1064,7 +1060,6 @@ class AdminImportConfigAdapter(AdminApiAdapter):
|
||||
"base_url", existing_ep.base_url
|
||||
)
|
||||
existing_ep.header_rules = ep_data.get("header_rules")
|
||||
existing_ep.timeout = ep_data.get("timeout", 300)
|
||||
existing_ep.max_retries = ep_data.get("max_retries", 2)
|
||||
existing_ep.is_active = ep_data.get("is_active", True)
|
||||
existing_ep.custom_path = ep_data.get("custom_path")
|
||||
@@ -1079,7 +1074,6 @@ class AdminImportConfigAdapter(AdminApiAdapter):
|
||||
api_format=ep_data["api_format"],
|
||||
base_url=ep_data["base_url"],
|
||||
header_rules=ep_data.get("header_rules"),
|
||||
timeout=ep_data.get("timeout", 300),
|
||||
max_retries=ep_data.get("max_retries", 2),
|
||||
is_active=ep_data.get("is_active", True),
|
||||
custom_path=ep_data.get("custom_path"),
|
||||
|
||||
@@ -477,7 +477,7 @@ class ChatHandlerBase(BaseMessageHandler, ABC):
|
||||
|
||||
# 配置 HTTP 超时
|
||||
# 注意:read timeout 用于检测连接断开,不是整体请求超时
|
||||
# 整体请求超时由 asyncio.wait_for 控制,使用 provider.timeout
|
||||
# 整体请求超时由 asyncio.wait_for 控制,使用全局配置
|
||||
timeout_config = httpx.Timeout(
|
||||
connect=config.http_connect_timeout,
|
||||
read=config.http_read_timeout, # 使用全局配置,用于检测连接断开
|
||||
@@ -485,8 +485,8 @@ class ChatHandlerBase(BaseMessageHandler, ABC):
|
||||
pool=config.http_pool_timeout,
|
||||
)
|
||||
|
||||
# provider.timeout 作为整体请求超时(建立连接 + 获取首字节)
|
||||
request_timeout = float(provider.timeout or 300)
|
||||
# 流式请求使用 stream_first_byte_timeout 作为首字节超时
|
||||
request_timeout = config.stream_first_byte_timeout
|
||||
|
||||
# 创建 HTTP 客户端(支持代理配置,从 Provider 读取)
|
||||
from src.clients.http_client import HTTPClientPool
|
||||
@@ -528,7 +528,7 @@ class ChatHandlerBase(BaseMessageHandler, ABC):
|
||||
|
||||
try:
|
||||
# 使用 asyncio.wait_for 包裹整个"建立连接 + 获取首字节"阶段
|
||||
# provider.timeout 控制整体超时,避免上游长时间无响应
|
||||
# stream_first_byte_timeout 控制首字节超时,避免上游长时间无响应
|
||||
await asyncio.wait_for(_connect_and_prefetch(), timeout=request_timeout)
|
||||
|
||||
except asyncio.TimeoutError:
|
||||
@@ -717,7 +717,8 @@ class ChatHandlerBase(BaseMessageHandler, ABC):
|
||||
# 注意:使用 get_proxy_client 复用连接池,不再每次创建新客户端
|
||||
from src.clients.http_client import HTTPClientPool
|
||||
|
||||
request_timeout = float(provider.timeout or 300)
|
||||
# 非流式请求使用 http_request_timeout 作为整体超时
|
||||
request_timeout = config.http_request_timeout
|
||||
http_client = await HTTPClientPool.get_proxy_client(
|
||||
proxy_config=provider.proxy,
|
||||
)
|
||||
|
||||
@@ -490,8 +490,8 @@ class CliMessageHandlerBase(BaseMessageHandler):
|
||||
pool=config.http_pool_timeout,
|
||||
)
|
||||
|
||||
# provider.timeout 作为整体请求超时(建立连接 + 获取首字节)
|
||||
request_timeout = float(provider.timeout or 300)
|
||||
# 流式请求使用 stream_first_byte_timeout 作为首字节超时
|
||||
request_timeout = config.stream_first_byte_timeout
|
||||
|
||||
logger.debug(
|
||||
f" └─ [{self.request_id}] 发送流式请求: "
|
||||
@@ -539,7 +539,7 @@ class CliMessageHandlerBase(BaseMessageHandler):
|
||||
|
||||
try:
|
||||
# 使用 asyncio.wait_for 包裹整个"建立连接 + 获取首字节"阶段
|
||||
# provider.timeout 控制整体超时,避免上游长时间无响应
|
||||
# stream_first_byte_timeout 控制首字节超时,避免上游长时间无响应
|
||||
await asyncio.wait_for(_connect_and_prefetch(), timeout=request_timeout)
|
||||
|
||||
except asyncio.TimeoutError:
|
||||
@@ -1633,7 +1633,8 @@ class CliMessageHandlerBase(BaseMessageHandler):
|
||||
# 注意:使用 get_proxy_client 复用连接池,不再每次创建新客户端
|
||||
from src.clients.http_client import HTTPClientPool
|
||||
|
||||
request_timeout = float(provider.timeout or 300)
|
||||
# 非流式请求使用 http_request_timeout 作为整体超时
|
||||
request_timeout = config.http_request_timeout
|
||||
http_client = await HTTPClientPool.get_proxy_client(
|
||||
proxy_config=provider.proxy,
|
||||
)
|
||||
|
||||
@@ -214,16 +214,14 @@ class TimeoutDefaults:
|
||||
"""超时配置默认值(秒)
|
||||
|
||||
超时配置说明:
|
||||
- 全局默认值和 Provider 默认值统一为 120 秒
|
||||
- 120 秒是 LLM API 的合理默认值:
|
||||
* 大多数请求在 30 秒内完成
|
||||
* 复杂推理(如 Claude extended thinking)可能需要 60-90 秒
|
||||
* 120 秒足够覆盖大部分场景,同时避免线程池被长时间占用
|
||||
- 如需更长超时,可在 Provider 级别单独配置
|
||||
- 非流式请求超时由环境变量 HTTP_REQUEST_TIMEOUT 控制(默认 300 秒)
|
||||
- 流式请求首字节超时由环境变量 STREAM_FIRST_BYTE_TIMEOUT 控制(默认 30 秒)
|
||||
- 此处的常量仅用于无法访问 config 的场景(如模型查询测试)
|
||||
"""
|
||||
|
||||
# HTTP 请求默认超时(与 Provider 默认值保持一致)
|
||||
HTTP_REQUEST = 120 # 2分钟
|
||||
# HTTP 请求默认超时(用于模型查询等测试场景)
|
||||
# 与 config.http_request_timeout 默认值保持一致
|
||||
HTTP_REQUEST = 300 # 5分钟
|
||||
|
||||
# 数据库连接池获取超时
|
||||
DB_POOL = 30
|
||||
|
||||
@@ -144,6 +144,8 @@ class Config:
|
||||
self.http_read_timeout = float(os.getenv("HTTP_READ_TIMEOUT", "60.0"))
|
||||
self.http_write_timeout = float(os.getenv("HTTP_WRITE_TIMEOUT", "60.0"))
|
||||
self.http_pool_timeout = float(os.getenv("HTTP_POOL_TIMEOUT", "10.0"))
|
||||
# HTTP_REQUEST_TIMEOUT: 非流式请求整体超时(秒),默认 300 秒
|
||||
self.http_request_timeout = float(os.getenv("HTTP_REQUEST_TIMEOUT", "300.0"))
|
||||
|
||||
# HTTP 连接池配置
|
||||
# HTTP_MAX_CONNECTIONS: 最大连接数,影响并发能力
|
||||
@@ -167,10 +169,9 @@ class Config:
|
||||
# STREAM_PREFETCH_LINES: 预读行数,用于检测嵌套错误
|
||||
# STREAM_STATS_DELAY: 统计记录延迟(秒),等待流完全关闭
|
||||
# STREAM_FIRST_BYTE_TIMEOUT: 首字节超时(秒),等待首字节超过此时间触发故障转移
|
||||
# 范围: 10-120 秒,默认 30 秒(必须小于 http_write_timeout 避免竞态)
|
||||
self.stream_prefetch_lines = int(os.getenv("STREAM_PREFETCH_LINES", "5"))
|
||||
self.stream_stats_delay = float(os.getenv("STREAM_STATS_DELAY", "0.1"))
|
||||
self.stream_first_byte_timeout = self._parse_ttfb_timeout()
|
||||
self.stream_first_byte_timeout = float(os.getenv("STREAM_FIRST_BYTE_TIMEOUT", "30.0"))
|
||||
|
||||
# 请求体读取超时(秒)
|
||||
# REQUEST_BODY_TIMEOUT: 等待客户端发送完整请求体的超时时间
|
||||
@@ -289,39 +290,6 @@ class Config:
|
||||
# 最小 10 个保活连接,最大不超过 max_connections
|
||||
return max(10, min(keepalive, self.http_max_connections))
|
||||
|
||||
def _parse_ttfb_timeout(self) -> float:
|
||||
"""
|
||||
解析 TTFB 超时配置,带错误处理和范围限制
|
||||
|
||||
TTFB (Time To First Byte) 用于检测慢响应的 Provider,超时触发故障转移。
|
||||
此值必须小于 http_write_timeout,避免竞态条件。
|
||||
|
||||
Returns:
|
||||
超时时间(秒),范围 10-120,默认 30
|
||||
"""
|
||||
default_timeout = 30.0
|
||||
min_timeout = 10.0
|
||||
max_timeout = 120.0 # 必须小于 http_write_timeout (默认 60s) 的 2 倍
|
||||
|
||||
raw_value = os.getenv("STREAM_FIRST_BYTE_TIMEOUT", str(default_timeout))
|
||||
try:
|
||||
timeout = float(raw_value)
|
||||
except ValueError:
|
||||
# 延迟导入,避免循环依赖(Config 初始化时 logger 可能未就绪)
|
||||
self._ttfb_config_warning = (
|
||||
f"无效的 STREAM_FIRST_BYTE_TIMEOUT 配置 '{raw_value}',使用默认值 {default_timeout}秒"
|
||||
)
|
||||
return default_timeout
|
||||
|
||||
# 范围限制
|
||||
clamped = max(min_timeout, min(max_timeout, timeout))
|
||||
if clamped != timeout:
|
||||
self._ttfb_config_warning = (
|
||||
f"STREAM_FIRST_BYTE_TIMEOUT={timeout}秒超出范围 [{min_timeout}-{max_timeout}],"
|
||||
f"已调整为 {clamped}秒"
|
||||
)
|
||||
return clamped
|
||||
|
||||
def _validate_pool_config(self) -> None:
|
||||
"""验证连接池配置是否安全"""
|
||||
total_per_worker = self.db_pool_size + self.db_max_overflow
|
||||
@@ -369,10 +337,6 @@ class Config:
|
||||
if hasattr(self, "_pool_config_warning") and self._pool_config_warning:
|
||||
logger.warning(self._pool_config_warning)
|
||||
|
||||
# TTFB 超时配置警告
|
||||
if hasattr(self, "_ttfb_config_warning") and self._ttfb_config_warning:
|
||||
logger.warning(self._ttfb_config_warning)
|
||||
|
||||
# 管理员密码检查(必须在环境变量中设置)
|
||||
if hasattr(self, "_missing_admin_password") and self._missing_admin_password:
|
||||
logger.error("必须设置 ADMIN_PASSWORD 环境变量!")
|
||||
|
||||
@@ -88,7 +88,6 @@ class CreateProviderRequest(BaseModel):
|
||||
is_active: Optional[bool] = Field(True, description="是否启用")
|
||||
concurrent_limit: Optional[int] = Field(None, ge=0, description="并发限制")
|
||||
# 请求配置(从 Endpoint 迁移)
|
||||
timeout: Optional[int] = Field(300, ge=1, le=600, description="请求超时(秒)")
|
||||
max_retries: Optional[int] = Field(2, ge=0, le=10, description="最大重试次数")
|
||||
proxy: Optional[ProxyConfig] = Field(None, description="代理配置")
|
||||
config: Optional[Dict[str, Any]] = Field(None, description="其他配置")
|
||||
@@ -159,7 +158,6 @@ class UpdateProviderRequest(BaseModel):
|
||||
is_active: Optional[bool] = None
|
||||
concurrent_limit: Optional[int] = Field(None, ge=0)
|
||||
# 请求配置(从 Endpoint 迁移)
|
||||
timeout: Optional[int] = Field(None, ge=1, le=600, description="请求超时(秒)")
|
||||
max_retries: Optional[int] = Field(None, ge=0, le=10, description="最大重试次数")
|
||||
proxy: Optional[ProxyConfig] = Field(None, description="代理配置")
|
||||
config: Optional[Dict[str, Any]] = None
|
||||
|
||||
@@ -403,7 +403,6 @@ class ProviderUpdate(BaseModel):
|
||||
api_format: Optional[str] = None
|
||||
base_url: Optional[str] = None
|
||||
headers: Optional[dict] = None
|
||||
timeout: Optional[int] = Field(None, ge=1, le=600)
|
||||
max_retries: Optional[int] = Field(None, ge=0, le=10)
|
||||
priority: Optional[int] = None
|
||||
weight: Optional[float] = Field(None, gt=0)
|
||||
@@ -423,7 +422,6 @@ class ProviderResponse(BaseModel):
|
||||
api_format: str
|
||||
base_url: str
|
||||
headers: Optional[dict]
|
||||
timeout: int
|
||||
max_retries: int
|
||||
priority: int
|
||||
weight: float
|
||||
|
||||
@@ -552,11 +552,10 @@ class Provider(Base):
|
||||
concurrent_limit = Column(Integer, nullable=True) # 并发请求限制
|
||||
|
||||
# 请求配置(从 Endpoint 迁移,作为全局默认值)
|
||||
# 超时 300 秒对于 LLM API 是合理的默认值:
|
||||
# - 大多数请求在 30 秒内完成
|
||||
# - 复杂推理(如 Claude thinking)可能需要 60-120 秒
|
||||
# - 300 秒足够覆盖极端场景(如超长上下文、复杂工具调用)
|
||||
timeout = Column(Integer, default=300, nullable=True) # 请求超时(秒)
|
||||
# [已废弃] timeout 字段不再使用,超时由环境变量控制:
|
||||
# - 非流式请求: HTTP_REQUEST_TIMEOUT(默认 300 秒)
|
||||
# - 流式首字节: STREAM_FIRST_BYTE_TIMEOUT(默认 30 秒)
|
||||
timeout = Column(Integer, default=300, nullable=True) # [已废弃] 请求超时(秒)
|
||||
max_retries = Column(Integer, default=2, nullable=True) # 最大重试次数
|
||||
proxy = Column(JSONB, nullable=True) # 代理配置: {url, username, password, enabled}
|
||||
|
||||
@@ -604,7 +603,7 @@ class ProviderEndpoint(Base):
|
||||
|
||||
# 请求配置
|
||||
header_rules = Column(JSON, nullable=True) # 请求头规则 [{action, key, value, from, to}]
|
||||
timeout = Column(Integer, default=300) # 超时(秒)
|
||||
timeout = Column(Integer, default=300) # [已废弃] 超时(秒),由环境变量控制
|
||||
max_retries = Column(Integer, default=2) # 最大重试次数
|
||||
|
||||
# 状态
|
||||
|
||||
@@ -37,7 +37,6 @@ class ProviderEndpointCreate(BaseModel):
|
||||
description="请求头规则列表,支持 set/drop/rename 操作",
|
||||
)
|
||||
|
||||
timeout: int = Field(default=300, ge=10, le=600, description="超时时间(秒)")
|
||||
max_retries: int = Field(default=2, ge=0, le=10, description="最大重试次数")
|
||||
|
||||
# 额外配置
|
||||
@@ -81,7 +80,6 @@ class ProviderEndpointUpdate(BaseModel):
|
||||
description="请求头规则列表,支持 set/drop/rename 操作",
|
||||
)
|
||||
|
||||
timeout: Optional[int] = Field(default=None, ge=10, le=600, description="超时时间(秒)")
|
||||
max_retries: Optional[int] = Field(default=None, ge=0, le=10, description="最大重试次数")
|
||||
is_active: Optional[bool] = Field(default=None, description="是否启用")
|
||||
config: Optional[Dict[str, Any]] = Field(default=None, description="额外配置")
|
||||
@@ -117,7 +115,6 @@ class ProviderEndpointResponse(BaseModel):
|
||||
default=None, description="请求头规则列表"
|
||||
)
|
||||
|
||||
timeout: int
|
||||
max_retries: int
|
||||
|
||||
# 状态
|
||||
@@ -619,7 +616,6 @@ class ProviderWithEndpointsSummary(BaseModel):
|
||||
quota_expires_at: Optional[datetime] = Field(default=None, description="配额过期时间")
|
||||
|
||||
# 请求配置(从 Endpoint 迁移)
|
||||
timeout: Optional[int] = Field(default=300, description="请求超时(秒)")
|
||||
max_retries: Optional[int] = Field(default=2, description="最大重试次数")
|
||||
proxy: Optional[Dict[str, Any]] = Field(default=None, description="代理配置")
|
||||
|
||||
|
||||
@@ -1703,11 +1703,9 @@ class UsageService:
|
||||
Returns:
|
||||
请求状态列表
|
||||
"""
|
||||
from src.models.database import ProviderEndpoint
|
||||
|
||||
now = datetime.now(timezone.utc)
|
||||
|
||||
# 构建基础查询,包含端点的 timeout 配置
|
||||
# 构建基础查询
|
||||
query = db.query(
|
||||
Usage.id,
|
||||
Usage.status,
|
||||
@@ -1722,8 +1720,7 @@ class UsageService:
|
||||
Usage.first_byte_time_ms, # 首字时间 (TTFB)
|
||||
Usage.created_at,
|
||||
Usage.provider_endpoint_id,
|
||||
ProviderEndpoint.timeout.label("endpoint_timeout"),
|
||||
).outerjoin(ProviderEndpoint, Usage.provider_endpoint_id == ProviderEndpoint.id)
|
||||
)
|
||||
|
||||
# 管理员轮询:可附带 provider 与上游 key 名称(注意:不要在普通用户接口暴露上游 key 信息)
|
||||
if include_admin_fields:
|
||||
@@ -1751,8 +1748,8 @@ class UsageService:
|
||||
timeout_ids = []
|
||||
for r in records:
|
||||
if r.status in ("pending", "streaming") and r.created_at:
|
||||
# 使用端点配置的超时时间,若无则使用默认值
|
||||
timeout_seconds = r.endpoint_timeout or default_timeout_seconds
|
||||
# 使用全局配置的超时时间
|
||||
timeout_seconds = default_timeout_seconds
|
||||
|
||||
# 处理时区:如果 created_at 没有时区信息,假定为 UTC
|
||||
created_at = r.created_at
|
||||
|
||||
Reference in New Issue
Block a user