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:
@@ -338,6 +338,37 @@ async def test_queue_writer_include_headers_bodies(monkeypatch: Any) -> None:
|
||||
assert "response_body" in event.data
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_queue_writer_failure_preserves_empty_request_headers(monkeypatch: Any) -> None:
|
||||
"""失败事件在传入空请求头时也应保留 request_headers 字段。"""
|
||||
dummy = DummyRedis()
|
||||
|
||||
async def _get_redis_client(require_redis: bool = False) -> Any:
|
||||
return dummy
|
||||
|
||||
monkeypatch.setattr("src.services.usage.telemetry_writer.get_redis_client", _get_redis_client)
|
||||
|
||||
writer = QueueTelemetryWriter(
|
||||
request_id="req-empty-hdr",
|
||||
user_id="user-1",
|
||||
api_key_id="key-1",
|
||||
log_level="full",
|
||||
)
|
||||
await writer.record_failure(
|
||||
provider="test",
|
||||
model="model",
|
||||
status_code=500,
|
||||
error_message="boom",
|
||||
request_headers={},
|
||||
request_body={"message": "x"},
|
||||
)
|
||||
|
||||
event = UsageEvent.from_stream_fields(dummy.calls[0][1])
|
||||
assert "request_headers" in event.data
|
||||
assert event.data["request_headers"] == {}
|
||||
assert event.data["request_body"] == {"message": "x"}
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_event_to_record_body_deserialization(monkeypatch: Any) -> None:
|
||||
"""测试 _event_to_record 正确反序列化 body 字符串为 dict"""
|
||||
|
||||
@@ -189,3 +189,40 @@ async def test_video_finalize_strict_mode_missing_required_marks_failed(
|
||||
kwargs = update_settled.call_args.kwargs
|
||||
assert kwargs["total_cost_usd"] == 0.0
|
||||
assert kwargs["status"] == "failed"
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_video_finalize_fallback_reads_headers_alias(monkeypatch: pytest.MonkeyPatch) -> None:
|
||||
usage_query = MagicMock()
|
||||
usage_query.filter.return_value.first.return_value = None
|
||||
user_query = MagicMock()
|
||||
user_query.filter.return_value.first.return_value = SimpleNamespace(id="u1")
|
||||
api_key_query = MagicMock()
|
||||
api_key_query.filter.return_value.first.return_value = SimpleNamespace(id="ak1")
|
||||
provider_query = MagicMock()
|
||||
provider_query.filter.return_value.first.return_value = SimpleNamespace(
|
||||
id="p1",
|
||||
name="prov1",
|
||||
)
|
||||
|
||||
db = MagicMock()
|
||||
db.query.side_effect = [usage_query, user_query, api_key_query, provider_query]
|
||||
|
||||
task = _make_task(
|
||||
status=VideoStatus.FAILED.value,
|
||||
error_message="boom",
|
||||
request_metadata={"headers": {"x-test-header": "1"}},
|
||||
)
|
||||
|
||||
record_usage = AsyncMock(return_value=SimpleNamespace(id="usage-1"))
|
||||
monkeypatch.setattr(
|
||||
"src.services.usage.service.UsageService.record_usage_with_custom_cost",
|
||||
record_usage,
|
||||
)
|
||||
|
||||
svc = TaskService(db)
|
||||
ok = await svc.finalize_video_task(task)
|
||||
|
||||
assert ok is True
|
||||
kwargs = record_usage.call_args.kwargs
|
||||
assert kwargs["request_headers"] == {"x-test-header": "1"}
|
||||
|
||||
Reference in New Issue
Block a user