refactor(gateway): 重构 ai pipeline 规划链路

This commit is contained in:
fawney19
2026-04-14 01:27:04 +08:00
parent 37bb120d20
commit 53acfbabf6
74 changed files with 7357 additions and 4380 deletions

View File

@@ -267,7 +267,7 @@ pub fn sync_report_represents_failure(
.body_json
.as_ref()
.and_then(|body| body.get("error"))
.is_some()
.is_some_and(|value| !value.is_null())
}
pub fn should_handle_local_sync_report(
@@ -433,6 +433,10 @@ mod tests {
error_body_payload.body_json = Some(json!({"error": {"message": "bad request"}}));
assert!(sync_report_represents_failure(&error_body_payload, None));
let mut null_error_payload = sample_sync_report("openai_chat_sync_success", 200);
null_error_payload.body_json = Some(json!({"error": null}));
assert!(!sync_report_represents_failure(&null_error_payload, None));
let success_payload = sample_sync_report("openai_chat_sync_success", 200);
assert!(!sync_report_represents_failure(&success_payload, None));
}

View File

@@ -659,7 +659,7 @@ fn infer_sync_terminal_state(
} else if status_code >= 400
|| provider_response
.and_then(|value| value.get("error"))
.is_some()
.is_some_and(|value| !value.is_null())
{
UsageTerminalState::Failed
} else {
@@ -2514,6 +2514,70 @@ mod tests {
);
}
#[test]
fn sync_terminal_usage_treats_null_error_field_as_success() {
let plan = ExecutionPlan {
request_id: "req-sync-null-error-1".to_string(),
candidate_id: Some("cand-sync-null-error-1".to_string()),
provider_name: Some("OpenAI".to_string()),
provider_id: "provider-1".to_string(),
endpoint_id: "endpoint-1".to_string(),
key_id: "key-1".to_string(),
method: "POST".to_string(),
url: "https://example.com/v1/messages".to_string(),
headers: BTreeMap::new(),
content_type: Some("application/json".to_string()),
content_encoding: None,
body: RequestBody::from_json(json!({"model": "gpt-5.4"})),
stream: false,
client_api_format: "claude:cli".to_string(),
provider_api_format: "openai:cli".to_string(),
model_name: Some("gpt-5.4".to_string()),
proxy: None,
tls_profile: None,
timeouts: None,
};
let payload = GatewaySyncReportRequest {
trace_id: "trace-sync-null-error-1".to_string(),
report_kind: "claude_cli_sync_success".to_string(),
report_context: Some(json!({
"client_api_format": "claude:cli",
"provider_api_format": "openai:cli",
"needs_conversion": true,
})),
status_code: 200,
headers: BTreeMap::new(),
body_json: Some(json!({
"id": "resp_1",
"status": "completed",
"error": null,
"usage": {
"input_tokens": 24,
"output_tokens": 11,
"total_tokens": 35
}
})),
client_body_json: Some(json!({
"type": "message",
"usage": {
"input_tokens": 24,
"output_tokens": 11
}
})),
body_base64: None,
telemetry: None,
};
let event =
build_sync_terminal_usage_event(&plan, payload.report_context.as_ref(), &payload)
.expect("usage event should build");
assert_eq!(event.event_type, UsageEventType::Completed);
assert_eq!(event.data.status_code, Some(200));
assert_eq!(event.data.input_tokens, Some(24));
assert_eq!(event.data.output_tokens, Some(11));
}
#[test]
fn manual_terminal_seed_event_builder_sanitizes_headers_and_metadata_but_preserves_bodies() {
let event = build_terminal_usage_event_from_seed(TerminalUsageSeed {