mirror of
https://github.com/fawney19/Aether.git
synced 2026-09-02 09:20:22 +08:00
fix: 用量记录保留空 headers/body,格式转换支持同 normalizer 透传
- 用量记录和遥测写入中将 truthiness 检查改为 is not None, 避免空 dict/list 被误丢弃 - FormatConversionRegistry 新增 _same_normalizer() 方法, 共享 normalizer 的格式(如 claude:chat/claude:cli)视为同格式透传 - 前端请求详情抽屉按数据可用性智能选择默认数据源 - 视频任务 finalize 支持 headers/original_headers 等别名字段
This commit is contained in:
@@ -77,6 +77,16 @@ class FormatConversionRegistry:
|
||||
raise FormatConversionError(format_id, format_id, f"未注册 Normalizer: {format_id}")
|
||||
return normalizer
|
||||
|
||||
def _same_normalizer(self, source_format: str, target_format: str) -> bool:
|
||||
"""判断两个 format_id 是否解析到同一个 normalizer 实例(即底层数据格式相同,可直接透传)。
|
||||
例如 claude:chat / claude:cli 共享 ClaudeNormalizer,gemini:chat / gemini:cli 共享 GeminiNormalizer。
|
||||
"""
|
||||
if str(source_format).upper() == str(target_format).upper():
|
||||
return True
|
||||
src = self.get_normalizer(source_format)
|
||||
tgt = self.get_normalizer(target_format)
|
||||
return src is not None and src is tgt
|
||||
|
||||
def _repair_internal_tool_call_ids(self, internal: InternalRequest) -> None:
|
||||
"""修复 InternalRequest 中空的 tool id/tool_use_id,避免上游校验报错。"""
|
||||
|
||||
@@ -122,11 +132,11 @@ class FormatConversionRegistry:
|
||||
target_variant: str | None = None,
|
||||
output_limit: int | None = None,
|
||||
) -> dict[str, Any]:
|
||||
if str(source_format).upper() == str(target_format).upper() and not target_variant:
|
||||
if self._same_normalizer(source_format, target_format) and not target_variant:
|
||||
return request
|
||||
|
||||
# 同格式 + variant: 优先尝试轻量补丁(跳过 internal 转换)
|
||||
if str(source_format).upper() == str(target_format).upper() and target_variant:
|
||||
# 同 normalizer + variant: 优先尝试轻量补丁(跳过 internal 转换)
|
||||
if self._same_normalizer(source_format, target_format) and target_variant:
|
||||
normalizer = self._require_normalizer(source_format)
|
||||
with _track_conversion_metrics(
|
||||
"request_patch", str(source_format).upper(), str(target_format).upper()
|
||||
@@ -159,11 +169,11 @@ class FormatConversionRegistry:
|
||||
output_limit: int | None = None,
|
||||
) -> dict[str, Any]:
|
||||
"""异步版本的 convert_request,在 internal 阶段执行图片 URL 下载等异步操作。"""
|
||||
if str(source_format).upper() == str(target_format).upper() and not target_variant:
|
||||
if self._same_normalizer(source_format, target_format) and not target_variant:
|
||||
return request
|
||||
|
||||
# 同格式 + variant: 优先尝试轻量补丁(跳过 internal 转换)
|
||||
if str(source_format).upper() == str(target_format).upper() and target_variant:
|
||||
# 同 normalizer + variant: 优先尝试轻量补丁(跳过 internal 转换)
|
||||
if self._same_normalizer(source_format, target_format) and target_variant:
|
||||
normalizer = self._require_normalizer(source_format)
|
||||
with _track_conversion_metrics(
|
||||
"request_patch", str(source_format).upper(), str(target_format).upper()
|
||||
@@ -208,7 +218,7 @@ class FormatConversionRegistry:
|
||||
如果提供,响应中的 model 字段将使用此值,
|
||||
而不是上游返回的映射后模型名。
|
||||
"""
|
||||
if str(source_format).upper() == str(target_format).upper():
|
||||
if self._same_normalizer(source_format, target_format):
|
||||
# 即使格式相同,也需要替换 model 字段
|
||||
if requested_model and isinstance(response, dict):
|
||||
response = dict(response) # 避免修改原始响应
|
||||
@@ -237,7 +247,7 @@ class FormatConversionRegistry:
|
||||
source_format: str,
|
||||
target_format: str,
|
||||
) -> dict[str, Any]:
|
||||
if str(source_format).upper() == str(target_format).upper():
|
||||
if self._same_normalizer(source_format, target_format):
|
||||
return error_response
|
||||
|
||||
src = self._require_normalizer(source_format)
|
||||
@@ -375,7 +385,7 @@ class FormatConversionRegistry:
|
||||
target_format: str,
|
||||
state: StreamState | None = None,
|
||||
) -> list[dict[str, Any]]:
|
||||
if str(source_format).upper() == str(target_format).upper():
|
||||
if self._same_normalizer(source_format, target_format):
|
||||
return [chunk]
|
||||
|
||||
src = self._require_normalizer(source_format)
|
||||
@@ -412,7 +422,7 @@ class FormatConversionRegistry:
|
||||
# ==================== 能力查询 ====================
|
||||
|
||||
def can_convert_request(self, source_format: str, target_format: str) -> bool:
|
||||
if str(source_format).upper() == str(target_format).upper():
|
||||
if self._same_normalizer(source_format, target_format):
|
||||
return True
|
||||
return (
|
||||
self.get_normalizer(source_format) is not None
|
||||
@@ -423,7 +433,7 @@ class FormatConversionRegistry:
|
||||
return self.can_convert_request(source_format, target_format)
|
||||
|
||||
def can_convert_stream(self, source_format: str, target_format: str) -> bool:
|
||||
if str(source_format).upper() == str(target_format).upper():
|
||||
if self._same_normalizer(source_format, target_format):
|
||||
return True
|
||||
src = self.get_normalizer(source_format)
|
||||
tgt = self.get_normalizer(target_format)
|
||||
@@ -432,7 +442,7 @@ class FormatConversionRegistry:
|
||||
return bool(src.capabilities.supports_stream and tgt.capabilities.supports_stream)
|
||||
|
||||
def can_convert_error(self, source_format: str, target_format: str) -> bool:
|
||||
if str(source_format).upper() == str(target_format).upper():
|
||||
if self._same_normalizer(source_format, target_format):
|
||||
return True
|
||||
src = self.get_normalizer(source_format)
|
||||
tgt = self.get_normalizer(target_format)
|
||||
|
||||
@@ -1704,6 +1704,15 @@ class TaskService:
|
||||
delta = task.completed_at - task.submitted_at
|
||||
response_time_ms = int(delta.total_seconds() * 1000)
|
||||
|
||||
request_headers: dict[str, Any] | None = None
|
||||
if isinstance(getattr(task, "request_metadata", None), dict):
|
||||
task_meta = task.request_metadata
|
||||
for header_key in ("request_headers", "headers", "original_headers"):
|
||||
raw_headers = task_meta.get(header_key)
|
||||
if isinstance(raw_headers, dict):
|
||||
request_headers = dict(raw_headers)
|
||||
break
|
||||
|
||||
try:
|
||||
await UsageService.record_usage_with_custom_cost(
|
||||
db=self.db,
|
||||
@@ -1734,11 +1743,7 @@ class TaskService:
|
||||
"fallback_created": True,
|
||||
"video_task_id": task.id,
|
||||
},
|
||||
request_headers=(
|
||||
(task.request_metadata or {}).get("request_headers")
|
||||
if isinstance(task.request_metadata, dict)
|
||||
else None
|
||||
),
|
||||
request_headers=request_headers,
|
||||
request_body=getattr(task, "original_request_body", None),
|
||||
provider_request_headers=None,
|
||||
response_headers=None,
|
||||
|
||||
@@ -108,12 +108,12 @@ def build_usage_params(
|
||||
|
||||
# 处理请求头(可能需要脱敏)
|
||||
processed_request_headers = None
|
||||
if should_log_headers and request_headers:
|
||||
if should_log_headers and request_headers is not None:
|
||||
processed_request_headers = SystemConfigService.mask_sensitive_headers(db, request_headers)
|
||||
|
||||
# 处理提供商请求头(可能需要脱敏)
|
||||
processed_provider_request_headers = None
|
||||
if should_log_headers and provider_request_headers:
|
||||
if should_log_headers and provider_request_headers is not None:
|
||||
processed_provider_request_headers = SystemConfigService.mask_sensitive_headers(
|
||||
db, provider_request_headers
|
||||
)
|
||||
@@ -124,33 +124,33 @@ def build_usage_params(
|
||||
processed_response_body = None
|
||||
processed_client_response_body = None
|
||||
if should_log_body:
|
||||
if request_body:
|
||||
if request_body is not None:
|
||||
processed_request_body = SystemConfigService.truncate_body(
|
||||
db, request_body, is_request=True
|
||||
)
|
||||
if provider_request_body:
|
||||
if provider_request_body is not None:
|
||||
processed_provider_request_body = SystemConfigService.truncate_body(
|
||||
db, provider_request_body, is_request=True
|
||||
)
|
||||
if response_body:
|
||||
if response_body is not None:
|
||||
processed_response_body = SystemConfigService.truncate_body(
|
||||
db, response_body, is_request=False
|
||||
)
|
||||
if client_response_body:
|
||||
if client_response_body is not None:
|
||||
processed_client_response_body = SystemConfigService.truncate_body(
|
||||
db, client_response_body, is_request=False
|
||||
)
|
||||
|
||||
# 处理响应头
|
||||
processed_response_headers = None
|
||||
if should_log_headers and response_headers:
|
||||
if should_log_headers and response_headers is not None:
|
||||
processed_response_headers = SystemConfigService.mask_sensitive_headers(
|
||||
db, response_headers
|
||||
)
|
||||
|
||||
# 处理返回给客户端的响应头
|
||||
processed_client_response_headers = None
|
||||
if should_log_headers and client_response_headers:
|
||||
if should_log_headers and client_response_headers is not None:
|
||||
processed_client_response_headers = SystemConfigService.mask_sensitive_headers(
|
||||
db, client_response_headers
|
||||
)
|
||||
|
||||
@@ -45,14 +45,14 @@ class UsageLifecycleMixin:
|
||||
|
||||
# 处理请求头
|
||||
processed_request_headers = None
|
||||
if should_log_headers and request_headers:
|
||||
if should_log_headers and request_headers is not None:
|
||||
processed_request_headers = SystemConfigService.mask_sensitive_headers(
|
||||
db, request_headers
|
||||
)
|
||||
|
||||
# 处理请求体
|
||||
processed_request_body = None
|
||||
if should_log_body and request_body:
|
||||
if should_log_body and request_body is not None:
|
||||
processed_request_body = SystemConfigService.truncate_body(
|
||||
db, request_body, is_request=True
|
||||
)
|
||||
@@ -265,17 +265,17 @@ class UsageLifecycleMixin:
|
||||
should_log_body = SystemConfigService.should_log_body(db)
|
||||
|
||||
processed_provider_headers = None
|
||||
if should_log_headers and provider_request_headers:
|
||||
if should_log_headers and provider_request_headers is not None:
|
||||
processed_provider_headers = SystemConfigService.mask_sensitive_headers(
|
||||
db, provider_request_headers
|
||||
)
|
||||
|
||||
processed_response_headers = None
|
||||
if should_log_headers and response_headers:
|
||||
if should_log_headers and response_headers is not None:
|
||||
processed_response_headers = dict(response_headers)
|
||||
|
||||
processed_response_body = None
|
||||
if should_log_body and response_body:
|
||||
if should_log_body and response_body is not None:
|
||||
processed_response_body = SystemConfigService.truncate_body(
|
||||
db, response_body, is_request=False
|
||||
)
|
||||
|
||||
@@ -252,18 +252,14 @@ class QueueTelemetryWriter(TelemetryWriter):
|
||||
|
||||
# Optional: Headers (masked)
|
||||
if self.include_headers:
|
||||
if kwargs.get("request_headers"):
|
||||
data["request_headers"] = self._mask_headers(kwargs["request_headers"])
|
||||
if kwargs.get("provider_request_headers"):
|
||||
data["provider_request_headers"] = self._mask_headers(
|
||||
kwargs["provider_request_headers"]
|
||||
)
|
||||
if kwargs.get("response_headers"):
|
||||
data["response_headers"] = self._mask_headers(kwargs["response_headers"])
|
||||
if kwargs.get("client_response_headers"):
|
||||
data["client_response_headers"] = self._mask_headers(
|
||||
kwargs["client_response_headers"]
|
||||
)
|
||||
for _hdr_key in (
|
||||
"request_headers",
|
||||
"provider_request_headers",
|
||||
"response_headers",
|
||||
"client_response_headers",
|
||||
):
|
||||
if kwargs.get(_hdr_key) is not None:
|
||||
data[_hdr_key] = self._mask_headers(kwargs[_hdr_key])
|
||||
|
||||
# Optional: Bodies (truncated)
|
||||
if self.include_bodies:
|
||||
|
||||
Reference in New Issue
Block a user