fix antigravity reasoning streaming and terminal errors

This commit is contained in:
ZheFox
2026-09-04 16:36:40 +08:00
parent 18d78dd6c9
commit 66d6c17d2d
4 changed files with 319 additions and 21 deletions
@@ -455,7 +455,10 @@ fn gemini_part_is_client_semantic(part: &Value) -> bool {
return true;
}
if part.get("thought").and_then(Value::as_bool) == Some(true) {
return false;
return part
.get("text")
.and_then(Value::as_str)
.is_some_and(|text| !text.is_empty());
}
if part.keys().all(|key| key == "thoughtSignature") {
return false;
@@ -584,17 +587,12 @@ mod tests {
}
#[test]
fn gemini_gate_waits_through_thought_and_commits_on_text() {
fn gemini_gate_commits_on_first_nonempty_thought() {
let mut gate = StreamCommitGate::new(gemini_policy());
let thought = b"data: {\"response\":{\"candidates\":[{\"content\":{\"role\":\"model\",\"parts\":[{\"thought\":true,\"text\":\"checking\"}]}}]}}\n\n";
let text = b"data: {\"response\":{\"candidates\":[{\"content\":{\"role\":\"model\",\"parts\":[{\"text\":\"answer\"}]}}]}}\n\n";
assert_eq!(
gate.observe_provider_bytes(thought),
StreamPrecommitObservation::Pending
);
assert_eq!(
gate.observe_provider_bytes(text),
StreamPrecommitObservation::Commit
);
assert_eq!(gate.state(), StreamCommitState::Committed);
@@ -615,7 +613,7 @@ mod tests {
#[test]
fn gemini_gate_rejects_malformed_function_call_before_commit() {
let mut gate = StreamCommitGate::new(gemini_policy());
let thought = b"data: {\"response\":{\"candidates\":[{\"content\":{\"role\":\"model\",\"parts\":[{\"thought\":true,\"text\":\"calling\"}]}}]}}\n\n";
let thought = b"data: {\"response\":{\"candidates\":[{\"content\":{\"role\":\"model\",\"parts\":[{\"thoughtSignature\":\"signature\",\"text\":\"\"}]}}]}}\n\n";
let malformed = b"data: {\"response\":{\"candidates\":[{\"content\":{\"role\":\"model\",\"parts\":[{\"thoughtSignature\":\"signature\",\"text\":\"\"}]},\"finishReason\":\"MALFORMED_FUNCTION_CALL\",\"finishMessage\":\"Malformed function call: Function call is empty - no input to parse.\"}]}}\n\n";
assert_eq!(
@@ -10561,7 +10561,7 @@ mod tests {
}
#[tokio::test]
async fn malformed_antigravity_function_call_retries_before_stream_commit() {
async fn malformed_antigravity_function_call_streams_thought_then_fails_in_band() {
let request_id = "req-antigravity-malformed-function-call";
let plan = antigravity_gemini_stream_plan(request_id);
let provider_catalog = provider_catalog_for_plan(
@@ -10640,10 +10640,35 @@ mod tests {
None,
)
.await
.expect("malformed Antigravity stream should resolve through failover");
.expect("malformed Antigravity stream should return a client stream")
.expect("the first reasoning delta should commit the selected candidate");
assert!(response.is_none());
assert_eq!(retry_scope, AiAttemptRetryScope::Candidate);
assert_eq!(response.status(), StatusCode::OK);
let body = to_bytes(response.into_body(), usize::MAX)
.await
.expect("response body should read");
let body = String::from_utf8(body.to_vec()).expect("response body should be utf8");
assert!(
body.contains("event: response.reasoning_summary_text.delta\n"),
"{body}"
);
assert!(
body.contains("\"delta\":\"Validating the document.\""),
"{body}"
);
assert!(body.contains("event: response.failed\n"), "{body}");
assert!(
body.contains("\"code\":\"MALFORMED_FUNCTION_CALL\""),
"{body}"
);
assert!(
body.contains(
"\"message\":\"Malformed function call: Function call is empty - no input to parse.\""
),
"{body}"
);
assert!(!body.contains("unsupported_finish_reason"), "{body}");
assert_eq!(retry_scope, AiAttemptRetryScope::Provider);
}
fn tunnel_proxy_snapshot(base_url: String) -> aether_contracts::ProxySnapshot {