mirror of
https://github.com/fawney19/Aether.git
synced 2026-09-02 09:20:22 +08:00
refactor: 前端全面替换 any 为 unknown 并统一错误处理,后端用量记录补写请求头/体
- 前端 API 层、stores、conversation 解析器、组件全面替换 any 为 unknown/具体类型 - 错误处理统一使用 parseApiError/getErrorStatus 替代 err.response?.data?.detail 模式 - 后端 handler/TaskService/UsageLifecycle/StreamTracker 链路传递 request_headers/request_body - streaming/pending 状态更新时可补写客户端和提供商的请求头及请求体 - 新增 TaskService 和 UsageService 相关测试
This commit is contained in:
@@ -38,7 +38,7 @@ async def test_task_service_execute_async_returns_execution_result() -> None:
|
||||
key=SimpleNamespace(id="k1"),
|
||||
)
|
||||
outcome = SubmitOutcome(
|
||||
candidate=candidate,
|
||||
candidate=candidate, # type: ignore[arg-type]
|
||||
candidate_keys=[{"index": 0, "provider_id": "p1"}],
|
||||
external_task_id="task_123",
|
||||
rule_lookup=None,
|
||||
@@ -48,7 +48,7 @@ async def test_task_service_execute_async_returns_execution_result() -> None:
|
||||
)
|
||||
|
||||
svc.submit_with_failover = AsyncMock(return_value=outcome) # type: ignore[method-assign]
|
||||
svc._recorder.get_candidate_keys = MagicMock( # type: ignore[attr-defined]
|
||||
svc._recorder.get_candidate_keys = MagicMock( # type: ignore[attr-defined, method-assign]
|
||||
return_value=[
|
||||
CandidateKey(candidate_index=0, retry_index=0, status="success", provider_id="p1")
|
||||
]
|
||||
@@ -72,3 +72,38 @@ async def test_task_service_execute_async_returns_execution_result() -> None:
|
||||
assert result.provider_task_id == "task_123"
|
||||
assert result.provider_id == "p1"
|
||||
assert result.candidate_index == 0
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_task_service_execute_sync_passes_request_headers_and_body() -> None:
|
||||
db = MagicMock()
|
||||
svc = TaskService(db)
|
||||
sentinel_result = object()
|
||||
svc._execute_sync_unified = AsyncMock( # type: ignore[method-assign]
|
||||
return_value=sentinel_result
|
||||
)
|
||||
|
||||
request_headers = {"authorization": "Bearer test", "x-trace-id": "abc123"}
|
||||
request_body = {"model": "gpt-4o-mini", "messages": [{"role": "user", "content": "hi"}]}
|
||||
request_body_ref = {"body": request_body}
|
||||
|
||||
result = await svc.execute(
|
||||
task_type="chat",
|
||||
task_mode=TaskMode.SYNC,
|
||||
api_format="openai:chat",
|
||||
model_name="gpt-4o-mini",
|
||||
user_api_key=MagicMock(id="u", user_id="user"),
|
||||
request_func=AsyncMock(),
|
||||
request_id="rid-sync",
|
||||
is_stream=True,
|
||||
request_headers=request_headers,
|
||||
request_body=request_body,
|
||||
request_body_ref=request_body_ref,
|
||||
)
|
||||
|
||||
assert result is sentinel_result
|
||||
svc._execute_sync_unified.assert_awaited_once() # type: ignore[attr-defined]
|
||||
kwargs = svc._execute_sync_unified.await_args.kwargs # type: ignore[attr-defined, union-attr]
|
||||
assert kwargs["request_headers"] == request_headers
|
||||
assert kwargs["request_body"] == request_body
|
||||
assert kwargs["request_body_ref"] == request_body_ref
|
||||
|
||||
@@ -299,3 +299,68 @@ class TestHelperMethods:
|
||||
|
||||
assert rate_multiplier == 0.8
|
||||
assert is_free_tier is False
|
||||
|
||||
|
||||
class TestUsageStatusUpdate:
|
||||
"""测试进行中状态更新对请求头/体的补写能力"""
|
||||
|
||||
def test_update_usage_status_can_persist_request_and_provider_payloads(self) -> None:
|
||||
usage = MagicMock()
|
||||
usage.status = "pending"
|
||||
usage.provider_name = "pending"
|
||||
usage.billing_status = "pending"
|
||||
usage.finalized_at = None
|
||||
usage.request_headers = None
|
||||
usage.request_body = None
|
||||
usage.provider_request_headers = None
|
||||
usage.provider_request_body = None
|
||||
|
||||
mock_db = MagicMock()
|
||||
mock_db.query.return_value.filter.return_value.first.return_value = usage
|
||||
|
||||
client_headers = {"authorization": "Bearer abc", "x-trace-id": "trace-1"}
|
||||
provider_headers = {"authorization": "Bearer upstream", "x-provider": "demo"}
|
||||
client_body = {"model": "gpt-4o-mini", "messages": [{"role": "user", "content": "hello"}]}
|
||||
provider_body = {
|
||||
"model": "upstream-model",
|
||||
"messages": [{"role": "user", "content": "hello"}],
|
||||
}
|
||||
|
||||
with (
|
||||
patch(
|
||||
"src.services.system.config.SystemConfigService.should_log_headers",
|
||||
return_value=True,
|
||||
),
|
||||
patch(
|
||||
"src.services.system.config.SystemConfigService.should_log_body",
|
||||
return_value=True,
|
||||
),
|
||||
patch(
|
||||
"src.services.system.config.SystemConfigService.mask_sensitive_headers",
|
||||
side_effect=lambda _db, h: {"masked": h},
|
||||
),
|
||||
patch(
|
||||
"src.services.system.config.SystemConfigService.truncate_body",
|
||||
side_effect=lambda _db, b, is_request=True: {
|
||||
"truncated": b,
|
||||
"is_request": is_request,
|
||||
},
|
||||
),
|
||||
):
|
||||
updated = UsageService.update_usage_status(
|
||||
db=mock_db,
|
||||
request_id="req-streaming-1",
|
||||
status="streaming",
|
||||
provider="demo-provider",
|
||||
request_headers=client_headers,
|
||||
request_body=client_body,
|
||||
provider_request_headers=provider_headers,
|
||||
provider_request_body=provider_body,
|
||||
)
|
||||
|
||||
assert updated is usage
|
||||
assert usage.request_headers == {"masked": client_headers}
|
||||
assert usage.provider_request_headers == {"masked": provider_headers}
|
||||
assert usage.request_body == {"truncated": client_body, "is_request": True}
|
||||
assert usage.provider_request_body == {"truncated": provider_body, "is_request": True}
|
||||
mock_db.commit.assert_called_once()
|
||||
|
||||
Reference in New Issue
Block a user