fix(ai): harden responses replay and failure diagnostics

This commit is contained in:
elky
2026-07-30 17:19:54 +08:00
parent 1ab4f079c9
commit 050eb77508
29 changed files with 700 additions and 142 deletions
@@ -236,6 +236,15 @@ async fn admin_monitoring_trace_request_falls_back_to_usage_routing_snapshot() {
usage.provider_api_key_id = Some("provider-key-1".to_string());
usage.error_message = Some("no local stream plans".to_string());
usage.response_time_ms = Some(45);
usage.request_metadata = Some(json!({
"routing_candidate_skip_reason": "provider_request_body_build_failed",
"routing_failure_diagnostic": {
"kind": "request_body_build",
"path": "$.reasoning.summary",
"message": "上游请求体语义校验失败",
"safe_to_show": true
}
}));
let usage_repository = Arc::new(InMemoryUsageReadRepository::seed(vec![usage]));
let data_state =
@@ -267,6 +276,10 @@ async fn admin_monitoring_trace_request_falls_back_to_usage_routing_snapshot() {
assert_eq!(payload["final_status"], json!("failed"));
assert_eq!(payload["candidates"][0]["id"], json!("routing-cand-1"));
assert_eq!(payload["candidates"][0]["status"], json!("failed"));
assert_eq!(
payload["candidates"][0]["skip_reason"],
json!("provider_request_body_build_failed")
);
assert_eq!(
payload["candidates"][0]["error_type"],
json!("no_local_stream_plans")
@@ -279,6 +292,10 @@ async fn admin_monitoring_trace_request_falls_back_to_usage_routing_snapshot() {
payload["candidates"][0]["extra_data"]["execution_path"],
json!("local_execution_runtime_miss")
);
assert_eq!(
payload["candidates"][0]["extra_data"]["failure_diagnostic"]["path"],
json!("$.reasoning.summary")
);
}
#[tokio::test]
@@ -203,7 +203,7 @@ fn build_admin_monitoring_usage_routing_snapshot_trace(
endpoint_id: usage.provider_endpoint_id.clone(),
key_id: usage.provider_api_key_id.clone(),
status,
skip_reason: None,
skip_reason: usage.routing_candidate_skip_reason().map(ToOwned::to_owned),
is_cached: false,
status_code: usage.status_code,
error_type: usage
@@ -356,6 +356,9 @@ fn build_admin_monitoring_usage_routing_snapshot_extra_data(
if let Some(candidate_index) = usage.routing_candidate_index() {
object.insert("candidate_index".to_string(), json!(candidate_index));
}
if let Some(diagnostic) = usage.routing_failure_diagnostic() {
object.insert("failure_diagnostic".to_string(), diagnostic.clone());
}
Some(Value::Object(object))
}