mirror of
https://github.com/fawney19/Aether.git
synced 2026-09-02 01:10:23 +08:00
- 删除全部 Python 源码 (src/) 及 Alembic 迁移脚本,归档至 _deprecated_py_src/ - 重构 Rust gateway ai_pipeline: 拆分 planner/finalize 模块,新增 contracts/adaptation 层 - 重组 handlers 模块为 admin/public/proxy/internal/shared 子模块结构 - 新增 executor 模块,引入 Rust 原生数据库迁移 (aether-data/migrations) - 简化 CI/Docker 构建流程,移除 base image 二级构建,统一为单一 app image - 移除 Python 相关基础设施文件 (entrypoint.sh, gunicorn_conf.py, Dockerfile.base)
108 lines
4.0 KiB
Python
108 lines
4.0 KiB
Python
from __future__ import annotations
|
|
|
|
from typing import Any
|
|
|
|
import httpx
|
|
|
|
from src.core.exceptions import ProviderNotAvailableException
|
|
from src.core.logger import logger
|
|
from src.services.request.result import RequestMetadata
|
|
|
|
|
|
class TaskFailureOperationsService:
|
|
"""任务失败收敛相关操作(异常元数据与统一抛错)。"""
|
|
|
|
@staticmethod
|
|
def attach_metadata_to_error(
|
|
error: Exception | None,
|
|
candidate: Any | None,
|
|
model_name: str,
|
|
api_format: str,
|
|
) -> None:
|
|
"""Attach candidate metadata onto exception for usage recording."""
|
|
if not error or not candidate:
|
|
return
|
|
|
|
existing_metadata = getattr(error, "request_metadata", None)
|
|
if existing_metadata and getattr(existing_metadata, "api_format", None):
|
|
return
|
|
|
|
metadata = RequestMetadata(
|
|
provider_request_headers=(
|
|
getattr(existing_metadata, "provider_request_headers", {})
|
|
if existing_metadata
|
|
else {}
|
|
),
|
|
provider=getattr(existing_metadata, "provider", None) or str(candidate.provider.name),
|
|
model=getattr(existing_metadata, "model", None) or model_name,
|
|
provider_id=getattr(existing_metadata, "provider_id", None)
|
|
or str(candidate.provider.id),
|
|
provider_endpoint_id=(
|
|
getattr(existing_metadata, "provider_endpoint_id", None)
|
|
or str(candidate.endpoint.id)
|
|
),
|
|
provider_api_key_id=(
|
|
getattr(existing_metadata, "provider_api_key_id", None) or str(candidate.key.id)
|
|
),
|
|
api_format=api_format,
|
|
)
|
|
setattr(error, "request_metadata", metadata)
|
|
|
|
@staticmethod
|
|
def raise_all_failed_exception(
|
|
request_id: str | None,
|
|
max_attempts: int,
|
|
last_candidate: Any | None,
|
|
model_name: str,
|
|
api_format: str,
|
|
last_error: Exception | None = None,
|
|
) -> None:
|
|
"""Raise a unified 'all candidates failed' exception."""
|
|
logger.error(" [{}] 所有 {} 个组合均失败", request_id, max_attempts)
|
|
|
|
request_metadata = None
|
|
if last_candidate:
|
|
request_metadata = {
|
|
"provider": last_candidate.provider.name,
|
|
"model": model_name,
|
|
"provider_id": str(last_candidate.provider.id),
|
|
"provider_endpoint_id": str(last_candidate.endpoint.id),
|
|
"provider_api_key_id": str(last_candidate.key.id),
|
|
"api_format": api_format,
|
|
}
|
|
|
|
upstream_status: int | None = None
|
|
upstream_response: str | None = None
|
|
if last_error:
|
|
if isinstance(last_error, httpx.HTTPStatusError):
|
|
upstream_status = last_error.response.status_code
|
|
upstream_response = getattr(last_error, "upstream_response", None)
|
|
if not upstream_response:
|
|
try:
|
|
upstream_response = last_error.response.text
|
|
except Exception:
|
|
pass
|
|
else:
|
|
upstream_status = getattr(last_error, "upstream_status", None)
|
|
upstream_response = getattr(last_error, "upstream_response", None)
|
|
|
|
if (
|
|
not upstream_response
|
|
or not upstream_response.strip()
|
|
or upstream_response.startswith("Unable to read")
|
|
):
|
|
upstream_response = str(last_error)
|
|
|
|
friendly_message = "服务暂时不可用,请稍后重试"
|
|
if last_error:
|
|
last_error_message = getattr(last_error, "message", None)
|
|
if last_error_message and isinstance(last_error_message, str):
|
|
friendly_message = last_error_message
|
|
|
|
raise ProviderNotAvailableException(
|
|
friendly_message,
|
|
request_metadata=request_metadata,
|
|
upstream_status=upstream_status,
|
|
upstream_response=upstream_response,
|
|
)
|