From 71d19d420c32b655d159e47b7aba340a9ccbeceb Mon Sep 17 00:00:00 2001 From: Matt Perpick Date: Wed, 18 Feb 2026 16:03:23 -0600 Subject: [PATCH 1/4] Add fuzz testing for Google/Gemini provider Adds Google roundtrip and Chat->Google two-arm fuzz suites following the existing pattern for OpenAI/Anthropic. Includes a Discovery JSON schema loader, prop_filter for valid API payloads, and a Python validation script for checking payloads against the real Gemini API. Also defaults missing Content.role to "user" to match real API behavior. Co-Authored-By: Claude Opus 4.6 --- crates/lingua/src/providers/google/convert.rs | 24 +- crates/lingua/tests/fuzz/Makefile | 26 +- crates/lingua/tests/fuzz/main.rs | 247 +++++++++++++++++- crates/lingua/tests/fuzz/schema_strategy.rs | 14 + mise.toml | 3 + .../case-00616f16cb8603d4.meta.json | 7 + .../case-00616f16cb8603d4.request.json | 74 ++++++ .../case-04afc1e70d055310.meta.json | 26 ++ .../case-04afc1e70d055310.request.json | 64 +++++ .../case-05048e3ab95af455.meta.json | 30 +++ .../case-05048e3ab95af455.request.json | 69 +++++ .../case-0680230824e2f95c.meta.json | 24 ++ .../case-0680230824e2f95c.request.json | 39 +++ .../case-08c80730b339ee0d.meta.json | 21 ++ .../case-08c80730b339ee0d.request.json | 74 ++++++ .../case-09fa42838b8cfd7b.meta.json | 28 ++ .../case-09fa42838b8cfd7b.request.json | 87 ++++++ .../case-0af37cb5b59d868b.meta.json | 7 + .../case-0af37cb5b59d868b.request.json | 98 +++++++ .../case-0f6021d1f374dc8f.meta.json | 30 +++ .../case-0f6021d1f374dc8f.request.json | 32 +++ .../case-0f9d6200213116a0.meta.json | 25 ++ .../case-0f9d6200213116a0.request.json | 48 ++++ .../case-16f47bea5be70dfb.meta.json | 25 ++ .../case-16f47bea5be70dfb.request.json | 79 ++++++ .../case-1e0526fa83578884.meta.json | 21 ++ .../case-1e0526fa83578884.request.json | 59 +++++ .../case-260034010784dcec.meta.json | 32 +++ .../case-260034010784dcec.request.json | 68 +++++ .../case-2ee666392a1d7d74.meta.json | 25 ++ .../case-2ee666392a1d7d74.request.json | 37 +++ .../case-310ed34a7ad861b4.meta.json | 21 ++ .../case-310ed34a7ad861b4.request.json | 29 ++ .../case-33d13b18d488f747.meta.json | 28 ++ .../case-33d13b18d488f747.request.json | 98 +++++++ .../case-34ba485a1406f69c.meta.json | 28 ++ .../case-34ba485a1406f69c.request.json | 60 +++++ .../case-4ec638697bbccbe3.meta.json | 23 ++ .../case-4ec638697bbccbe3.request.json | 56 ++++ .../case-64011eae99c883f4.meta.json | 37 +++ .../case-64011eae99c883f4.request.json | 99 +++++++ .../case-a3ec1907e0d629d5.meta.json | 28 ++ .../case-a3ec1907e0d629d5.request.json | 80 ++++++ .../case-00015db5103bca8c.meta.json | 7 + .../case-00015db5103bca8c.request.json | 53 ++++ .../case-0005d480caf84338.meta.json | 15 ++ .../case-0005d480caf84338.request.json | 97 +++++++ .../case-007bd4500a682a93.meta.json | 9 + .../case-007bd4500a682a93.request.json | 27 ++ .../case-011c75aef5297a6e.meta.json | 11 + .../case-011c75aef5297a6e.request.json | 52 ++++ .../case-051ebd07ca42b0d6.meta.json | 10 + .../case-051ebd07ca42b0d6.request.json | 23 ++ .../case-066263d9b371b047.meta.json | 14 + .../case-066263d9b371b047.request.json | 50 ++++ .../case-08e697e9ba45fe3e.meta.json | 15 ++ .../case-08e697e9ba45fe3e.request.json | 28 ++ .../case-0bde07b217c86337.meta.json | 24 ++ .../case-0bde07b217c86337.request.json | 94 +++++++ .../case-117472f2bf6ae259.meta.json | 11 + .../case-117472f2bf6ae259.request.json | 157 +++++++++++ .../case-138766d1b5d34371.meta.json | 22 ++ .../case-138766d1b5d34371.request.json | 69 +++++ .../case-375c9df694c3d0a3.meta.json | 24 ++ .../case-375c9df694c3d0a3.request.json | 95 +++++++ .../case-562df898165dbfc6.meta.json | 16 ++ .../case-562df898165dbfc6.request.json | 104 ++++++++ .../case-65163e2b3d0cab80.meta.json | 9 + .../case-65163e2b3d0cab80.request.json | 15 ++ .../case-97dcb376a3bad1c9.meta.json | 14 + .../case-97dcb376a3bad1c9.request.json | 24 ++ .../case-a2d3e2321403c3b4.meta.json | 24 ++ .../case-a2d3e2321403c3b4.request.json | 50 ++++ .../case-c18e28e85b79bba8.meta.json | 14 + .../case-c18e28e85b79bba8.request.json | 116 ++++++++ scripts/validate_google_payload.py | 116 ++++++++ 76 files changed, 3431 insertions(+), 8 deletions(-) create mode 100644 payloads/fuzz-snapshots/chat-google-two-arm/case-00616f16cb8603d4.meta.json create mode 100644 payloads/fuzz-snapshots/chat-google-two-arm/case-00616f16cb8603d4.request.json create mode 100644 payloads/fuzz-snapshots/chat-google-two-arm/case-04afc1e70d055310.meta.json create mode 100644 payloads/fuzz-snapshots/chat-google-two-arm/case-04afc1e70d055310.request.json create mode 100644 payloads/fuzz-snapshots/chat-google-two-arm/case-05048e3ab95af455.meta.json create mode 100644 payloads/fuzz-snapshots/chat-google-two-arm/case-05048e3ab95af455.request.json create mode 100644 payloads/fuzz-snapshots/chat-google-two-arm/case-0680230824e2f95c.meta.json create mode 100644 payloads/fuzz-snapshots/chat-google-two-arm/case-0680230824e2f95c.request.json create mode 100644 payloads/fuzz-snapshots/chat-google-two-arm/case-08c80730b339ee0d.meta.json create mode 100644 payloads/fuzz-snapshots/chat-google-two-arm/case-08c80730b339ee0d.request.json create mode 100644 payloads/fuzz-snapshots/chat-google-two-arm/case-09fa42838b8cfd7b.meta.json create mode 100644 payloads/fuzz-snapshots/chat-google-two-arm/case-09fa42838b8cfd7b.request.json create mode 100644 payloads/fuzz-snapshots/chat-google-two-arm/case-0af37cb5b59d868b.meta.json create mode 100644 payloads/fuzz-snapshots/chat-google-two-arm/case-0af37cb5b59d868b.request.json create mode 100644 payloads/fuzz-snapshots/chat-google-two-arm/case-0f6021d1f374dc8f.meta.json create mode 100644 payloads/fuzz-snapshots/chat-google-two-arm/case-0f6021d1f374dc8f.request.json create mode 100644 payloads/fuzz-snapshots/chat-google-two-arm/case-0f9d6200213116a0.meta.json create mode 100644 payloads/fuzz-snapshots/chat-google-two-arm/case-0f9d6200213116a0.request.json create mode 100644 payloads/fuzz-snapshots/chat-google-two-arm/case-16f47bea5be70dfb.meta.json create mode 100644 payloads/fuzz-snapshots/chat-google-two-arm/case-16f47bea5be70dfb.request.json create mode 100644 payloads/fuzz-snapshots/chat-google-two-arm/case-1e0526fa83578884.meta.json create mode 100644 payloads/fuzz-snapshots/chat-google-two-arm/case-1e0526fa83578884.request.json create mode 100644 payloads/fuzz-snapshots/chat-google-two-arm/case-260034010784dcec.meta.json create mode 100644 payloads/fuzz-snapshots/chat-google-two-arm/case-260034010784dcec.request.json create mode 100644 payloads/fuzz-snapshots/chat-google-two-arm/case-2ee666392a1d7d74.meta.json create mode 100644 payloads/fuzz-snapshots/chat-google-two-arm/case-2ee666392a1d7d74.request.json create mode 100644 payloads/fuzz-snapshots/chat-google-two-arm/case-310ed34a7ad861b4.meta.json create mode 100644 payloads/fuzz-snapshots/chat-google-two-arm/case-310ed34a7ad861b4.request.json create mode 100644 payloads/fuzz-snapshots/chat-google-two-arm/case-33d13b18d488f747.meta.json create mode 100644 payloads/fuzz-snapshots/chat-google-two-arm/case-33d13b18d488f747.request.json create mode 100644 payloads/fuzz-snapshots/chat-google-two-arm/case-34ba485a1406f69c.meta.json create mode 100644 payloads/fuzz-snapshots/chat-google-two-arm/case-34ba485a1406f69c.request.json create mode 100644 payloads/fuzz-snapshots/chat-google-two-arm/case-4ec638697bbccbe3.meta.json create mode 100644 payloads/fuzz-snapshots/chat-google-two-arm/case-4ec638697bbccbe3.request.json create mode 100644 payloads/fuzz-snapshots/chat-google-two-arm/case-64011eae99c883f4.meta.json create mode 100644 payloads/fuzz-snapshots/chat-google-two-arm/case-64011eae99c883f4.request.json create mode 100644 payloads/fuzz-snapshots/chat-google-two-arm/case-a3ec1907e0d629d5.meta.json create mode 100644 payloads/fuzz-snapshots/chat-google-two-arm/case-a3ec1907e0d629d5.request.json create mode 100644 payloads/fuzz-snapshots/google-roundtrip/case-00015db5103bca8c.meta.json create mode 100644 payloads/fuzz-snapshots/google-roundtrip/case-00015db5103bca8c.request.json create mode 100644 payloads/fuzz-snapshots/google-roundtrip/case-0005d480caf84338.meta.json create mode 100644 payloads/fuzz-snapshots/google-roundtrip/case-0005d480caf84338.request.json create mode 100644 payloads/fuzz-snapshots/google-roundtrip/case-007bd4500a682a93.meta.json create mode 100644 payloads/fuzz-snapshots/google-roundtrip/case-007bd4500a682a93.request.json create mode 100644 payloads/fuzz-snapshots/google-roundtrip/case-011c75aef5297a6e.meta.json create mode 100644 payloads/fuzz-snapshots/google-roundtrip/case-011c75aef5297a6e.request.json create mode 100644 payloads/fuzz-snapshots/google-roundtrip/case-051ebd07ca42b0d6.meta.json create mode 100644 payloads/fuzz-snapshots/google-roundtrip/case-051ebd07ca42b0d6.request.json create mode 100644 payloads/fuzz-snapshots/google-roundtrip/case-066263d9b371b047.meta.json create mode 100644 payloads/fuzz-snapshots/google-roundtrip/case-066263d9b371b047.request.json create mode 100644 payloads/fuzz-snapshots/google-roundtrip/case-08e697e9ba45fe3e.meta.json create mode 100644 payloads/fuzz-snapshots/google-roundtrip/case-08e697e9ba45fe3e.request.json create mode 100644 payloads/fuzz-snapshots/google-roundtrip/case-0bde07b217c86337.meta.json create mode 100644 payloads/fuzz-snapshots/google-roundtrip/case-0bde07b217c86337.request.json create mode 100644 payloads/fuzz-snapshots/google-roundtrip/case-117472f2bf6ae259.meta.json create mode 100644 payloads/fuzz-snapshots/google-roundtrip/case-117472f2bf6ae259.request.json create mode 100644 payloads/fuzz-snapshots/google-roundtrip/case-138766d1b5d34371.meta.json create mode 100644 payloads/fuzz-snapshots/google-roundtrip/case-138766d1b5d34371.request.json create mode 100644 payloads/fuzz-snapshots/google-roundtrip/case-375c9df694c3d0a3.meta.json create mode 100644 payloads/fuzz-snapshots/google-roundtrip/case-375c9df694c3d0a3.request.json create mode 100644 payloads/fuzz-snapshots/google-roundtrip/case-562df898165dbfc6.meta.json create mode 100644 payloads/fuzz-snapshots/google-roundtrip/case-562df898165dbfc6.request.json create mode 100644 payloads/fuzz-snapshots/google-roundtrip/case-65163e2b3d0cab80.meta.json create mode 100644 payloads/fuzz-snapshots/google-roundtrip/case-65163e2b3d0cab80.request.json create mode 100644 payloads/fuzz-snapshots/google-roundtrip/case-97dcb376a3bad1c9.meta.json create mode 100644 payloads/fuzz-snapshots/google-roundtrip/case-97dcb376a3bad1c9.request.json create mode 100644 payloads/fuzz-snapshots/google-roundtrip/case-a2d3e2321403c3b4.meta.json create mode 100644 payloads/fuzz-snapshots/google-roundtrip/case-a2d3e2321403c3b4.request.json create mode 100644 payloads/fuzz-snapshots/google-roundtrip/case-c18e28e85b79bba8.meta.json create mode 100644 payloads/fuzz-snapshots/google-roundtrip/case-c18e28e85b79bba8.request.json create mode 100755 scripts/validate_google_payload.py diff --git a/crates/lingua/src/providers/google/convert.rs b/crates/lingua/src/providers/google/convert.rs index ca633d88..6792bf0f 100644 --- a/crates/lingua/src/providers/google/convert.rs +++ b/crates/lingua/src/providers/google/convert.rs @@ -55,12 +55,7 @@ impl TryFromLLM for Message { type Error = ConvertError; fn try_from(content: GoogleContent) -> Result { - let role = content - .role - .as_deref() - .ok_or(ConvertError::MissingRequiredField { - field: "role".to_string(), - })?; + let role = content.role.as_deref().unwrap_or("user"); let parts = content.parts.ok_or(ConvertError::MissingRequiredField { field: "parts".to_string(), })?; @@ -877,6 +872,23 @@ mod tests { assert_eq!(fc.name.as_deref(), Some("get_weather")); } + #[test] + fn test_google_content_to_message_missing_role_defaults_to_user() { + let content = GoogleContent { + role: None, + parts: Some(vec![text_part("Hello".to_string())]), + }; + + let message = >::try_from(content).unwrap(); + match message { + Message::User { content } => match content { + UserContent::String(s) => assert_eq!(s, "Hello"), + _ => panic!("Expected string content"), + }, + _ => panic!("Expected user message"), + } + } + #[test] fn test_google_to_universal_simple() { let request = GenerateContentRequest { diff --git a/crates/lingua/tests/fuzz/Makefile b/crates/lingua/tests/fuzz/Makefile index 8eb222d5..9b6e480f 100644 --- a/crates/lingua/tests/fuzz/Makefile +++ b/crates/lingua/tests/fuzz/Makefile @@ -1,4 +1,4 @@ -.PHONY: run stats prune run-openai stats-openai prune-openai run-responses stats-responses prune-responses run-anthropic stats-anthropic prune-anthropic run-two-arm stats-two-arm prune-two-arm run-three-arm stats-three-arm prune-three-arm refresh-snapshots +.PHONY: run stats prune run-openai stats-openai prune-openai run-responses stats-responses prune-responses run-anthropic stats-anthropic prune-anthropic run-google stats-google prune-google run-two-arm stats-two-arm prune-two-arm run-google-two-arm stats-google-two-arm prune-google-two-arm run-three-arm stats-three-arm prune-three-arm refresh-snapshots run: run-openai @@ -33,6 +33,24 @@ stats-anthropic: prune-anthropic: cargo test -p lingua --test fuzz anthropic_roundtrip_prune_snapshots -- --ignored --nocapture --exact +run-google: + cargo test -p lingua --test fuzz google_roundtrip -- --ignored --nocapture --exact + +stats-google: + cargo test -p lingua --test fuzz google_roundtrip_stats -- --ignored --nocapture --exact + +prune-google: + cargo test -p lingua --test fuzz google_roundtrip_prune_snapshots -- --ignored --nocapture --exact + +run-google-two-arm: + cargo test -p lingua --test fuzz chat_google_two_arm -- --ignored --nocapture --exact + +stats-google-two-arm: + cargo test -p lingua --test fuzz chat_google_two_arm_stats -- --ignored --nocapture --exact + +prune-google-two-arm: + cargo test -p lingua --test fuzz chat_google_two_arm_prune_snapshots -- --ignored --nocapture --exact + run-two-arm: cargo test -p lingua --test fuzz chat_anthropic_two_arm -- --ignored --nocapture --exact @@ -64,8 +82,14 @@ refresh-snapshots: -cargo test -p lingua --test fuzz chat_anthropic_two_arm_stats -- --ignored --nocapture --exact -cargo test -p lingua --test fuzz chat_responses_anthropic_three_arm -- --ignored --nocapture --exact -cargo test -p lingua --test fuzz chat_responses_anthropic_three_arm_stats -- --ignored --nocapture --exact + -cargo test -p lingua --test fuzz google_roundtrip -- --ignored --nocapture --exact + -cargo test -p lingua --test fuzz google_roundtrip_stats -- --ignored --nocapture --exact + -cargo test -p lingua --test fuzz chat_google_two_arm -- --ignored --nocapture --exact + -cargo test -p lingua --test fuzz chat_google_two_arm_stats -- --ignored --nocapture --exact cargo test -p lingua --test fuzz openai_roundtrip_prune_snapshots -- --ignored --nocapture --exact cargo test -p lingua --test fuzz responses_roundtrip_prune_snapshots -- --ignored --nocapture --exact cargo test -p lingua --test fuzz anthropic_roundtrip_prune_snapshots -- --ignored --nocapture --exact cargo test -p lingua --test fuzz chat_anthropic_two_arm_prune_snapshots -- --ignored --nocapture --exact cargo test -p lingua --test fuzz chat_responses_anthropic_three_arm_prune_snapshots -- --ignored --nocapture --exact + cargo test -p lingua --test fuzz google_roundtrip_prune_snapshots -- --ignored --nocapture --exact + cargo test -p lingua --test fuzz chat_google_two_arm_prune_snapshots -- --ignored --nocapture --exact diff --git a/crates/lingua/tests/fuzz/main.rs b/crates/lingua/tests/fuzz/main.rs index 37db5d19..1d24e312 100644 --- a/crates/lingua/tests/fuzz/main.rs +++ b/crates/lingua/tests/fuzz/main.rs @@ -25,6 +25,8 @@ const SNAPSHOT_SUITE_ANTHROPIC: &str = "anthropic-roundtrip"; const SNAPSHOT_SUITE_CHAT_ANTHROPIC_TWO_ARM: &str = "chat-anthropic-two-arm"; const SNAPSHOT_SUITE_CHAT_RESPONSES_ANTHROPIC_THREE_ARM: &str = "chat-responses-anthropic-three-arm"; +const SNAPSHOT_SUITE_GOOGLE: &str = "google-roundtrip"; +const SNAPSHOT_SUITE_CHAT_GOOGLE_TWO_ARM: &str = "chat-google-two-arm"; fn workspace_root() -> PathBuf { Path::new(env!("CARGO_MANIFEST_DIR")) @@ -497,6 +499,123 @@ fn assert_responses_roundtrip_verbose(payload: &Value) -> Result { assert_provider_roundtrip_verbose(ProviderFormat::Responses, payload) } +fn assert_google_roundtrip(payload: &Value) -> Option> { + assert_provider_roundtrip(ProviderFormat::Google, payload) +} + +fn assert_google_roundtrip_verbose(payload: &Value) -> Result { + assert_provider_roundtrip_verbose(ProviderFormat::Google, payload) +} + +fn assert_chat_google_two_arm(payload: &Value) -> Option> { + let chat = adapter_for_format(ProviderFormat::ChatCompletions)?; + let google = adapter_for_format(ProviderFormat::Google)?; + + let universal_1 = match chat.request_to_universal(payload.clone()) { + Ok(v) => v, + Err(e) => return Some(vec![format!("chat->universal error: {e}")]), + }; + let google_1 = match google.request_from_universal(&universal_1) { + Ok(v) => v, + Err(e) => return Some(vec![format!("universal->google(1) error: {e}")]), + }; + let universal_2 = match google.request_to_universal(google_1.clone()) { + Ok(v) => v, + Err(e) => return Some(vec![format!("google->universal(1) error: {e}")]), + }; + let google_2 = match google.request_from_universal(&universal_2) { + Ok(v) => v, + Err(e) => return Some(vec![format!("universal->google(2) error: {e}")]), + }; + let universal_3 = match google.request_to_universal(google_2.clone()) { + Ok(v) => v, + Err(e) => return Some(vec![format!("google->universal(2) error: {e}")]), + }; + let chat_out = match chat.request_from_universal(&universal_3) { + Ok(v) => v, + Err(e) => return Some(vec![format!("universal->chat error: {e}")]), + }; + + let mut issues = Vec::new(); + let universal_1_json = serde_json::to_value(&universal_1).unwrap_or(Value::Null); + let universal_2_json = serde_json::to_value(&universal_2).unwrap_or(Value::Null); + append_diff_issues( + "universal(1->2):", + &universal_1_json, + &universal_2_json, + &mut issues, + ); + append_diff_issues("google(1->2):", &google_1, &google_2, &mut issues); + append_diff_issues("chat(final):", payload, &chat_out, &mut issues); + + Some(issues) +} + +fn assert_chat_google_two_arm_verbose(payload: &Value) -> Result { + let chat = adapter_for_format(ProviderFormat::ChatCompletions) + .ok_or_else(|| "No chat-completions adapter".to_string())?; + let google = adapter_for_format(ProviderFormat::Google) + .ok_or_else(|| "No google adapter".to_string())?; + + let universal_1 = chat + .request_to_universal(payload.clone()) + .map_err(|e| format!("chat->universal error: {e}"))?; + let google_1 = google + .request_from_universal(&universal_1) + .map_err(|e| format!("universal->google(1) error: {e}"))?; + let universal_2 = google + .request_to_universal(google_1.clone()) + .map_err(|e| format!("google->universal(1) error: {e}"))?; + let google_2 = google + .request_from_universal(&universal_2) + .map_err(|e| format!("universal->google(2) error: {e}"))?; + let universal_3 = google + .request_to_universal(google_2.clone()) + .map_err(|e| format!("google->universal(2) error: {e}"))?; + let chat_out = chat + .request_from_universal(&universal_3) + .map_err(|e| format!("universal->chat error: {e}"))?; + + let mut issues = Vec::new(); + let universal_1_json = serde_json::to_value(&universal_1).unwrap_or(Value::Null); + let universal_2_json = serde_json::to_value(&universal_2).unwrap_or(Value::Null); + append_diff_issues( + "universal(1->2):", + &universal_1_json, + &universal_2_json, + &mut issues, + ); + append_diff_issues("google(1->2):", &google_1, &google_2, &mut issues); + append_diff_issues("chat(final):", payload, &chat_out, &mut issues); + + if issues.is_empty() { + return Ok(true); + } + + Err(format!( + "chat->universal->google->universal->google->universal->chat mismatch:\n{}\n\n\ + chat_input: {}\n\ + universal_1: {}\n\ + google_1: {}\n\ + universal_2: {}\n\ + google_2: {}\n\ + universal_3: {}\n\ + chat_output: {}", + issues + .iter() + .map(|i| format!(" {i}")) + .collect::>() + .join("\n"), + as_pretty_json(payload), + as_pretty_json(&universal_1), + as_pretty_json(&google_1), + as_pretty_json(&universal_2), + as_pretty_json(&google_2), + as_pretty_json(&universal_3), + as_pretty_json(&chat_out), + )) +} + fn assert_chat_responses_anthropic_three_arm(payload: &Value) -> Option> { let chat = adapter_for_format(ProviderFormat::ChatCompletions)?; let responses = adapter_for_format(ProviderFormat::Responses)?; @@ -666,7 +785,9 @@ fn assert_chat_responses_anthropic_three_arm_verbose(payload: &Value) -> Result< // ============================================================================ mod strategies { - use super::schema_strategy::{load_openapi_definitions, strategy_for_schema_name}; + use super::schema_strategy::{ + load_discovery_definitions, load_openapi_definitions, strategy_for_schema_name, + }; use super::*; fn specs_dir() -> String { @@ -701,6 +822,50 @@ mod strategies { ) .boxed() } + + pub fn arb_google_payload() -> BoxedStrategy { + let defs = + load_discovery_definitions(&format!("{}/specs/google/discovery.json", specs_dir())); + strategy_for_schema_name("GenerateContentRequest", &defs) + .prop_filter("payload must parse as Google params", |payload| { + lingua::providers::google::try_parse_google(payload).is_ok() + }) + .prop_filter( + "contents must be valid Google API input (valid roles, non-empty parts with data)", + |payload| { + let part_has_data = |part: &Value| { + part.get("text").and_then(Value::as_str).is_some() + || part.get("inlineData").and_then(|d| d.get("data")).is_some() + || part + .get("fileData") + .and_then(|d| d.get("fileUri")) + .is_some() + || part + .get("functionCall") + .and_then(|d| d.get("name")) + .is_some() + || part + .get("functionResponse") + .and_then(|d| d.get("name")) + .is_some() + }; + payload + .get("contents") + .and_then(|c| c.as_array()) + .is_some_and(|contents| { + contents.iter().all(|entry| { + matches!( + entry.get("role").and_then(Value::as_str), + Some("user" | "model") + ) && entry.get("parts").and_then(|p| p.as_array()).is_some_and( + |parts| !parts.is_empty() && parts.iter().all(part_has_data), + ) + }) + }) + }, + ) + .boxed() + } } // ============================================================================ @@ -978,6 +1143,19 @@ fn chat_responses_anthropic_three_arm_saved_snapshots() { ); } +#[test] +fn google_roundtrip_saved_snapshots() { + run_saved_snapshots_suite(SNAPSHOT_SUITE_GOOGLE, assert_google_roundtrip); +} + +#[test] +fn chat_google_two_arm_saved_snapshots() { + run_saved_snapshots_suite( + SNAPSHOT_SUITE_CHAT_GOOGLE_TWO_ARM, + assert_chat_google_two_arm, + ); +} + /// Prune fuzz snapshots in a loop until stable: /// - remove malformed request/meta pairs /// - remove orphan meta files @@ -1018,6 +1196,21 @@ fn chat_responses_anthropic_three_arm_prune_snapshots() { ); } +#[test] +#[ignore] +fn google_roundtrip_prune_snapshots() { + run_prune_snapshots_suite(SNAPSHOT_SUITE_GOOGLE, assert_google_roundtrip); +} + +#[test] +#[ignore] +fn chat_google_two_arm_prune_snapshots() { + run_prune_snapshots_suite( + SNAPSHOT_SUITE_CHAT_GOOGLE_TWO_ARM, + assert_chat_google_two_arm, + ); +} + /// Fail on the first error with verbose output (input, output, diff). /// Use for debugging a specific issue. #[test] @@ -1085,6 +1278,32 @@ fn chat_responses_anthropic_three_arm() { ); } +#[test] +#[ignore] +fn google_roundtrip() { + run_fail_fast_suite( + SNAPSHOT_SUITE_GOOGLE, + "google", + "request-roundtrip", + strategies::arb_google_payload(), + assert_google_roundtrip, + assert_google_roundtrip_verbose, + ); +} + +#[test] +#[ignore] +fn chat_google_two_arm() { + run_fail_fast_suite( + SNAPSHOT_SUITE_CHAT_GOOGLE_TWO_ARM, + "chat-completions", + "chat-google-two-arm", + strategies::arb_openai_payload(), + assert_chat_google_two_arm, + assert_chat_google_two_arm_verbose, + ); +} + /// Run all cases and report an aggregated summary of unique issues. /// Use for triaging the full scope of failures. #[test] @@ -1151,3 +1370,29 @@ fn chat_responses_anthropic_three_arm_stats() { assert_chat_responses_anthropic_three_arm, ); } + +#[test] +#[ignore] +fn google_roundtrip_stats() { + run_stats_suite( + SNAPSHOT_SUITE_GOOGLE, + "google", + "request-roundtrip", + "Google roundtrip fuzz", + strategies::arb_google_payload(), + assert_google_roundtrip, + ); +} + +#[test] +#[ignore] +fn chat_google_two_arm_stats() { + run_stats_suite( + SNAPSHOT_SUITE_CHAT_GOOGLE_TWO_ARM, + "chat-completions", + "chat-google-two-arm", + "Chat->Google two-arm fuzz", + strategies::arb_openai_payload(), + assert_chat_google_two_arm, + ); +} diff --git a/crates/lingua/tests/fuzz/schema_strategy.rs b/crates/lingua/tests/fuzz/schema_strategy.rs index e1f0ff76..67532908 100644 --- a/crates/lingua/tests/fuzz/schema_strategy.rs +++ b/crates/lingua/tests/fuzz/schema_strategy.rs @@ -322,6 +322,20 @@ pub fn load_openapi_definitions(spec_path: &str) -> Map { .unwrap_or_else(|| panic!("No components.schemas in {}", spec_path)) } +/// Load a Google Discovery REST spec and extract the top-level `schemas` map. +pub fn load_discovery_definitions(spec_path: &str) -> Map { + let content = std::fs::read_to_string(spec_path) + .unwrap_or_else(|e| panic!("Failed to read spec at {}: {}", spec_path, e)); + + let spec: Value = serde_json::from_str(&content) + .unwrap_or_else(|e| panic!("Failed to parse spec at {}: {}", spec_path, e)); + + spec.get("schemas") + .and_then(|s| s.as_object()) + .cloned() + .unwrap_or_else(|| panic!("No schemas in {}", spec_path)) +} + /// Build a strategy for a named schema from the definitions map. pub fn strategy_for_schema_name( name: &str, diff --git a/mise.toml b/mise.toml index b646d7dd..6a798e2b 100644 --- a/mise.toml +++ b/mise.toml @@ -1,2 +1,5 @@ +[env] +_.file = ".env" + [tools] pnpm = "10.26.2" diff --git a/payloads/fuzz-snapshots/chat-google-two-arm/case-00616f16cb8603d4.meta.json b/payloads/fuzz-snapshots/chat-google-two-arm/case-00616f16cb8603d4.meta.json new file mode 100644 index 00000000..e907e6f9 --- /dev/null +++ b/payloads/fuzz-snapshots/chat-google-two-arm/case-00616f16cb8603d4.meta.json @@ -0,0 +1,7 @@ +{ + "issues": [ + "chat->universal error: Conversion to universal format failed: data did not match any variant of untagged enum ChatCompletionRequestMessageContent" + ], + "kind": "chat-google-two-arm", + "provider": "chat-completions" +} diff --git a/payloads/fuzz-snapshots/chat-google-two-arm/case-00616f16cb8603d4.request.json b/payloads/fuzz-snapshots/chat-google-two-arm/case-00616f16cb8603d4.request.json new file mode 100644 index 00000000..5c6b4743 --- /dev/null +++ b/payloads/fuzz-snapshots/chat-google-two-arm/case-00616f16cb8603d4.request.json @@ -0,0 +1,74 @@ +{ + "audio": { + "format": "flac", + "voice": ".!KNF2Zkn5,f h?,Ig" + }, + "function_call": "none", + "functions": [ + { + "name": "n5 8w!1.?3,,??W,!v?O.,y?2!,8?i?tF,!..I?2? .gEt" + } + ], + "logit_bias": {}, + "logprobs": false, + "max_completion_tokens": 652, + "messages": [ + { + "content": "?..7?68e pC?pf? t", + "name": "4.Xn..a1,B", + "refusal": null, + "role": "assistant" + }, + { + "content": [ + { + "text": null, + "type": null + } + ], + "name": "lOe?mtn3?zrU?,g!,MuKSzAYuG!.6,Q,.7.,Y?T6", + "role": "user" + } + ], + "modalities": null, + "model": "gpt-5-mini-2025-08-07", + "n": -860, + "presence_penalty": 54.71461087452379, + "reasoning_effort": "minimal", + "response_format": { + "type": "json_object" + }, + "seed": 374, + "store": true, + "stream_options": { + "include_obfuscation": true + }, + "tool_choice": { + "allowed_tools": { + "mode": "required", + "tools": [ + {}, + {} + ] + }, + "type": "allowed_tools" + }, + "tools": [ + { + "function": { + "name": "D!ZHX,3v,0,IH" + }, + "type": "function" + }, + { + "custom": { + "format": { + "type": "text" + }, + "name": ".,XfS!9GnFE8" + }, + "type": "custom" + } + ], + "web_search_options": {} +} diff --git a/payloads/fuzz-snapshots/chat-google-two-arm/case-04afc1e70d055310.meta.json b/payloads/fuzz-snapshots/chat-google-two-arm/case-04afc1e70d055310.meta.json new file mode 100644 index 00000000..d117b649 --- /dev/null +++ b/payloads/fuzz-snapshots/chat-google-two-arm/case-04afc1e70d055310.meta.json @@ -0,0 +1,26 @@ +{ + "issues": [ + "universal(1->2): changed: params.reasoning.canonical (\"effort\" -> \"budget_tokens\")", + "universal(1->2): changed: params.seed (965 -> null)", + "universal(1->2): changed: params.stream (true -> null)", + "universal(1->2): changed: params.tools.length (3 -> 1)", + "universal(1->2): changed: params.logprobs (false -> null)", + "universal(1->2): changed: params.parallel_tool_calls (true -> null)", + "universal(1->2): changed: messages.length (2 -> 1)", + "google(1->2): lost: systemInstruction", + "chat(final): lost: web_search_options", + "chat(final): lost: seed", + "chat(final): lost: n", + "chat(final): lost: stream_options", + "chat(final): lost: modalities", + "chat(final): lost: logprobs", + "chat(final): lost: stream", + "chat(final): lost: function_call", + "chat(final): lost: parallel_tool_calls", + "chat(final): lost: audio", + "chat(final): changed: messages.length (2 -> 1)", + "chat(final): changed: tools.length (3 -> 1)" + ], + "kind": "chat-google-two-arm", + "provider": "chat-completions" +} diff --git a/payloads/fuzz-snapshots/chat-google-two-arm/case-04afc1e70d055310.request.json b/payloads/fuzz-snapshots/chat-google-two-arm/case-04afc1e70d055310.request.json new file mode 100644 index 00000000..6705ffdf --- /dev/null +++ b/payloads/fuzz-snapshots/chat-google-two-arm/case-04afc1e70d055310.request.json @@ -0,0 +1,64 @@ +{ + "audio": { + "format": "wav", + "voice": "W!W.s60,.!qRRHBY??AI" + }, + "function_call": "none", + "logprobs": false, + "max_completion_tokens": 17, + "messages": [ + { + "content": null, + "name": "hC,2.CDLKTNfe!AUmB2oq83k.0lO?q,lf?13O6.Lk.", + "role": "function" + }, + { + "content": "9.tf..W?t4a2.5..Na.?,4.Wmo43j,", + "name": "??,,0.K3,A. e", + "role": "system" + } + ], + "modalities": null, + "model": "ns!?6,y!.9.t f", + "n": -125, + "parallel_tool_calls": true, + "reasoning_effort": "low", + "seed": 965, + "stream": true, + "stream_options": null, + "tool_choice": { + "function": { + "name": "!p.Z.Y,7" + }, + "type": "function" + }, + "tools": [ + { + "function": { + "description": "n!n8.E9!,A ,3Xs 87?.yx", + "name": "IEii ,7tON6r1.zYHzLc? 6hJ!Y.l", + "parameters": {} + }, + "type": "function" + }, + { + "custom": { + "name": "a?Y.!?,pDvp,.m?0,!k" + }, + "type": "custom" + }, + { + "custom": { + "description": "?B!L ja.S?7m", + "name": "? 8mi?9jiV,Gf,4,3X.Rn?,?fL?O.,f34,!8x,!," + }, + "type": "custom" + } + ], + "web_search_options": { + "user_location": { + "approximate": {}, + "type": "approximate" + } + } +} diff --git a/payloads/fuzz-snapshots/chat-google-two-arm/case-05048e3ab95af455.meta.json b/payloads/fuzz-snapshots/chat-google-two-arm/case-05048e3ab95af455.meta.json new file mode 100644 index 00000000..b99df818 --- /dev/null +++ b/payloads/fuzz-snapshots/chat-google-two-arm/case-05048e3ab95af455.meta.json @@ -0,0 +1,30 @@ +{ + "issues": [ + "universal(1->2): changed: messages.length (2 -> 1)", + "universal(1->2): changed: params.seed (-465 -> null)", + "universal(1->2): changed: params.top_logprobs (3 -> null)", + "universal(1->2): changed: params.reasoning.canonical (\"effort\" -> \"budget_tokens\")", + "universal(1->2): changed: params.stream (true -> null)", + "universal(1->2): changed: params.parallel_tool_calls (true -> null)", + "universal(1->2): changed: params.frequency_penalty (-14.320129888587324 -> null)", + "universal(1->2): changed: params.store (true -> null)", + "universal(1->2): changed: params.logprobs (false -> null)", + "universal(1->2): changed: params.tools.length (3 -> 1)", + "chat(final): lost: n", + "chat(final): lost: modalities", + "chat(final): lost: logprobs", + "chat(final): lost: store", + "chat(final): lost: top_logprobs", + "chat(final): lost: stream", + "chat(final): lost: function_call", + "chat(final): lost: parallel_tool_calls", + "chat(final): lost: stream_options", + "chat(final): lost: frequency_penalty", + "chat(final): lost: prediction", + "chat(final): lost: seed", + "chat(final): changed: messages.length (2 -> 1)", + "chat(final): changed: tools.length (3 -> 1)" + ], + "kind": "chat-google-two-arm", + "provider": "chat-completions" +} diff --git a/payloads/fuzz-snapshots/chat-google-two-arm/case-05048e3ab95af455.request.json b/payloads/fuzz-snapshots/chat-google-two-arm/case-05048e3ab95af455.request.json new file mode 100644 index 00000000..ce186577 --- /dev/null +++ b/payloads/fuzz-snapshots/chat-google-two-arm/case-05048e3ab95af455.request.json @@ -0,0 +1,69 @@ +{ + "frequency_penalty": -14.320129888587324, + "function_call": { + "name": "X,Qr??v Bx T " + }, + "logprobs": false, + "max_completion_tokens": -165, + "messages": [ + { + "content": "8!,u0e ???,0.?mVFs", + "role": "tool", + "tool_call_id": "fX2.xOiU,.Uc.4i..,,Hdg?NqD2,,llBs ,..,d" + }, + { + "content": null, + "name": ",?w?4?2,", + "role": "function" + } + ], + "modalities": [ + "audio", + "audio" + ], + "model": "gpt-4-0314", + "n": 720, + "parallel_tool_calls": true, + "prediction": { + "content": "aX,U6fU,!qHk", + "type": "content" + }, + "reasoning_effort": "medium", + "seed": -465, + "stop": [ + " .a!Kk8!.. .oHXN?y,v.HJE9. ,7", + "5?obn?uA2k.HO!.,g8EE?.3, l,,,?,R,l2su", + "ThRiC.41r.,8?lnp9I,qqSJ,?e..eow.l..yV ?dW.hu89t", + "1u,!i7 a." + ], + "store": true, + "stream": true, + "stream_options": { + "include_obfuscation": false, + "include_usage": false + }, + "tools": [ + { + "custom": { + "description": "?Yp wJ ,T,5!a30m.?L?pLk,5,d,L.MgG7,! ", + "name": "R,.!05gtHST??Nf.?v?i.M.o,M,,50C,?C33a,qR" + }, + "type": "custom" + }, + { + "function": { + "name": "? . TEFt5. .i yoR17!7ZMWro,7.x.!", + "parameters": {}, + "strict": null + }, + "type": "function" + }, + { + "custom": { + "name": "A.,j5?x.3Fm43O" + }, + "type": "custom" + } + ], + "top_logprobs": 3 +} diff --git a/payloads/fuzz-snapshots/chat-google-two-arm/case-0680230824e2f95c.meta.json b/payloads/fuzz-snapshots/chat-google-two-arm/case-0680230824e2f95c.meta.json new file mode 100644 index 00000000..548ffd21 --- /dev/null +++ b/payloads/fuzz-snapshots/chat-google-two-arm/case-0680230824e2f95c.meta.json @@ -0,0 +1,24 @@ +{ + "issues": [ + "universal(1->2): changed: params.top_logprobs (571 -> null)", + "universal(1->2): changed: params.stream (false -> null)", + "universal(1->2): changed: messages[0].content[0].output (\"\" -> {\"output\":\"\"})", + "chat(final): lost: logit_bias", + "chat(final): lost: n", + "chat(final): lost: prediction", + "chat(final): lost: stream_options", + "chat(final): lost: top_logprobs", + "chat(final): lost: stream", + "chat(final): lost: reasoning_effort", + "chat(final): lost: function_call", + "chat(final): lost: modalities", + "chat(final): lost: web_search_options", + "chat(final): lost: messages[0].name", + "chat(final): added: messages[0].tool_call_id", + "chat(final): changed: messages[0].role (\"function\" -> \"tool\")", + "chat(final): changed: messages[0].content (null -> \"{\\\"output\\\":\\\"\\\"}\")", + "chat(final): changed: stop (\"qWI.O,G YvnirG?!..6Ne?a?Y ?B?.. Xg..\" -> [\"qWI.O,G YvnirG?!..6Ne?a?Y ?B?.. Xg..\"])" + ], + "kind": "chat-google-two-arm", + "provider": "chat-completions" +} diff --git a/payloads/fuzz-snapshots/chat-google-two-arm/case-0680230824e2f95c.request.json b/payloads/fuzz-snapshots/chat-google-two-arm/case-0680230824e2f95c.request.json new file mode 100644 index 00000000..a9361a04 --- /dev/null +++ b/payloads/fuzz-snapshots/chat-google-two-arm/case-0680230824e2f95c.request.json @@ -0,0 +1,39 @@ +{ + "function_call": "auto", + "logit_bias": {}, + "messages": [ + { + "content": null, + "name": "rE?nA ..Iaz7,", + "role": "function" + } + ], + "modalities": [ + "audio", + "text", + "audio" + ], + "model": "gpt-3.5-turbo-0125", + "n": 79, + "prediction": { + "content": "6F???!I8E3q.?l s!HCJ6r,6ipo,a1,tfnL n?GLD..iI", + "type": "content" + }, + "reasoning_effort": null, + "stop": "qWI.O,G YvnirG?!..6Ne?a?Y ?B?.. Xg..", + "stream": false, + "stream_options": { + "include_obfuscation": false + }, + "top_logprobs": 571, + "web_search_options": { + "user_location": { + "approximate": { + "city": "U1?x4.0vCuuf5758r.92.", + "country": "KHvr?1?,.", + "region": ".P R.5a,U!j.uH3!?.ZayNP5.???UnH?z.,?s.,xa2ixO.B" + }, + "type": "approximate" + } + } +} diff --git a/payloads/fuzz-snapshots/chat-google-two-arm/case-08c80730b339ee0d.meta.json b/payloads/fuzz-snapshots/chat-google-two-arm/case-08c80730b339ee0d.meta.json new file mode 100644 index 00000000..b6f8617e --- /dev/null +++ b/payloads/fuzz-snapshots/chat-google-two-arm/case-08c80730b339ee0d.meta.json @@ -0,0 +1,21 @@ +{ + "issues": [ + "universal(1->2): changed: params.tools.length (3 -> 2)", + "universal(1->2): changed: params.logprobs (false -> null)", + "universal(1->2): changed: params.stream (true -> null)", + "universal(1->2): changed: messages.length (1 -> 0)", + "google(1->2): lost: systemInstruction", + "chat(final): lost: functions", + "chat(final): lost: logit_bias", + "chat(final): lost: stream", + "chat(final): lost: stream_options", + "chat(final): lost: verbosity", + "chat(final): lost: tool_choice", + "chat(final): lost: logprobs", + "chat(final): changed: stop (\"f0OdJ.T,\" -> [\"f0OdJ.T,\"])", + "chat(final): changed: tools.length (3 -> 2)", + "chat(final): changed: messages.length (1 -> 0)" + ], + "kind": "chat-google-two-arm", + "provider": "chat-completions" +} diff --git a/payloads/fuzz-snapshots/chat-google-two-arm/case-08c80730b339ee0d.request.json b/payloads/fuzz-snapshots/chat-google-two-arm/case-08c80730b339ee0d.request.json new file mode 100644 index 00000000..0906e86d --- /dev/null +++ b/payloads/fuzz-snapshots/chat-google-two-arm/case-08c80730b339ee0d.request.json @@ -0,0 +1,74 @@ +{ + "functions": [ + { + "description": ".u.OqTee1,?", + "name": "?,k.z? ?kB8v!E.?,1tIg..4xR2i?", + "parameters": {} + }, + { + "description": "v,4!S?52L?", + "name": "?Vm?0L!,14.HRw?oUn8L!19!.5o.u,. ", + "parameters": {} + }, + { + "name": "S.911sUGo..,. .DxB,c? .?2 dY8W ?5?e,Q ,s?8li ?.a?", + "parameters": {} + }, + { + "description": "?44wV1?!Mn.D92z! ?,!iq8 G,r. ,?", + "name": ",q.?FT5?", + "parameters": {} + } + ], + "logit_bias": {}, + "logprobs": false, + "messages": [ + { + "content": "4 .!3.4D,pb! 0gNe!oyk?R47.jK", + "name": ".0?.??!.??i..V.Hi6.c. eLS8v2!x,3", + "role": "system" + } + ], + "model": "u1!7B!g.d?wEVv!Pt?CVD?11 2..M", + "stop": "f0OdJ.T,", + "stream": true, + "stream_options": { + "include_obfuscation": true, + "include_usage": true + }, + "tool_choice": { + "allowed_tools": { + "mode": "auto", + "tools": [ + {}, + {} + ] + }, + "type": "allowed_tools" + }, + "tools": [ + { + "custom": { + "description": "?70,.S?.,O,?3.N43X,L? 6 f", + "name": "n,kI,A" + }, + "type": "custom" + }, + { + "function": { + "name": "GhZ?,!1.j .3?!,.?4.6", + "parameters": {} + }, + "type": "function" + }, + { + "function": { + "name": " ", + "parameters": {}, + "strict": null + }, + "type": "function" + } + ], + "verbosity": "medium" +} diff --git a/payloads/fuzz-snapshots/chat-google-two-arm/case-09fa42838b8cfd7b.meta.json b/payloads/fuzz-snapshots/chat-google-two-arm/case-09fa42838b8cfd7b.meta.json new file mode 100644 index 00000000..5ec098b8 --- /dev/null +++ b/payloads/fuzz-snapshots/chat-google-two-arm/case-09fa42838b8cfd7b.meta.json @@ -0,0 +1,28 @@ +{ + "issues": [ + "universal(1->2): changed: messages.length (1 -> 0)", + "universal(1->2): changed: params.top_logprobs (-747 -> null)", + "universal(1->2): changed: params.seed (-877 -> null)", + "universal(1->2): changed: params.tools.length (2 -> 1)", + "universal(1->2): changed: params.presence_penalty (49.989426326552554 -> null)", + "universal(1->2): changed: params.store (false -> null)", + "google(1->2): lost: systemInstruction", + "chat(final): lost: n", + "chat(final): lost: store", + "chat(final): lost: functions", + "chat(final): lost: presence_penalty", + "chat(final): lost: audio", + "chat(final): lost: modalities", + "chat(final): lost: function_call", + "chat(final): lost: logit_bias", + "chat(final): lost: seed", + "chat(final): lost: top_logprobs", + "chat(final): lost: verbosity", + "chat(final): lost: web_search_options", + "chat(final): changed: messages.length (1 -> 0)", + "chat(final): changed: stop (\"O,.1.cI.s\" -> [\"O,.1.cI.s\"])", + "chat(final): changed: tools.length (2 -> 1)" + ], + "kind": "chat-google-two-arm", + "provider": "chat-completions" +} diff --git a/payloads/fuzz-snapshots/chat-google-two-arm/case-09fa42838b8cfd7b.request.json b/payloads/fuzz-snapshots/chat-google-two-arm/case-09fa42838b8cfd7b.request.json new file mode 100644 index 00000000..2f5a7332 --- /dev/null +++ b/payloads/fuzz-snapshots/chat-google-two-arm/case-09fa42838b8cfd7b.request.json @@ -0,0 +1,87 @@ +{ + "audio": { + "format": "aac", + "voice": "?PoW!1.,c?0.x!??!!?9,rF!,o??,.!n1 ,W4Sl" + }, + "function_call": { + "name": "?,M1 ..,ig??." + }, + "functions": [ + { + "description": "Bb!2G9?!.xE6V5X,C7 3! ZYLib,!. nIC.", + "name": ". ,?j.O,, C?" + }, + { + "description": ",.oB! Qq,n,?!1y,BQ,G,Ym,! uRE5?", + "name": ",7K, 8Uu,r!43?Faoz?.,.,?.Mkgc k,!Us?r?uQ ?j ,44", + "parameters": {} + }, + { + "name": "?q .?,S .u yo a.!q,?4,h6H.ShnxC?.ONI?", + "parameters": {} + }, + { + "description": ",h?7?f4gkagr.4e!,cLW3", + "name": "?!!?8pSg21?4m 2,j,9y1D" + }, + { + "description": ".Z.,?4?,VF0Z,.0,?i,.??V26?tUl9m2Lsdyk,", + "name": "ti9,lBD.dU??KubFqF.9?, mub.?u..m0,Hxw4P" + } + ], + "logit_bias": {}, + "messages": [ + { + "content": [ + { + "text": ".K.SW,!En!oHS??z", + "type": "text" + }, + { + "text": "6c!lN!?. 6X?p ?QzS ! 6I", + "type": "text" + }, + { + "text": "5.O", + "type": "text" + } + ], + "role": "developer" + } + ], + "modalities": null, + "model": "gpt-4-turbo", + "n": -841, + "presence_penalty": 49.989426326552554, + "response_format": { + "type": "json_object" + }, + "seed": -877, + "stop": "O,.1.cI.s", + "store": false, + "tools": [ + { + "function": { + "name": "E" + }, + "type": "function" + }, + { + "custom": { + "name": ",0?,0,,?Q!.9Q,9?..9W, oC!???? 6.I9M.3bHy?CI0l?ht" + }, + "type": "custom" + } + ], + "top_logprobs": -747, + "verbosity": null, + "web_search_options": { + "search_context_size": "medium", + "user_location": { + "approximate": { + "country": "vD u?,x" + }, + "type": "approximate" + } + } +} diff --git a/payloads/fuzz-snapshots/chat-google-two-arm/case-0af37cb5b59d868b.meta.json b/payloads/fuzz-snapshots/chat-google-two-arm/case-0af37cb5b59d868b.meta.json new file mode 100644 index 00000000..4895b408 --- /dev/null +++ b/payloads/fuzz-snapshots/chat-google-two-arm/case-0af37cb5b59d868b.meta.json @@ -0,0 +1,7 @@ +{ + "issues": [ + "chat->universal error: Conversion to universal format failed: invalid type: null, expected a string" + ], + "kind": "chat-google-two-arm", + "provider": "chat-completions" +} diff --git a/payloads/fuzz-snapshots/chat-google-two-arm/case-0af37cb5b59d868b.request.json b/payloads/fuzz-snapshots/chat-google-two-arm/case-0af37cb5b59d868b.request.json new file mode 100644 index 00000000..8947d006 --- /dev/null +++ b/payloads/fuzz-snapshots/chat-google-two-arm/case-0af37cb5b59d868b.request.json @@ -0,0 +1,98 @@ +{ + "function_call": { + "name": "5rg1,OEx.!gn?7cxP4,0dd?Q?,.f?62 X1..6p,8" + }, + "functions": [ + { + "name": ".b.?!0.4qmSKM!!5.. .RYUUY!,64.z.u!IVO" + }, + { + "name": " t!?!,aO,88a E??,3" + }, + { + "name": " ,ceGQ?.S!.. V!,.CN0?2?m??6??3?U" + }, + { + "name": "F?KE?5KP5NK..Ue.l! ??DxLX!", + "parameters": {} + } + ], + "logit_bias": {}, + "logprobs": true, + "messages": [ + { + "audio": { + "id": "I???" + }, + "function_call": null, + "refusal": "? .4YfA?!w, Q!!?FzK!.O.h.u?qCSu", + "role": "assistant", + "tool_calls": [ + { + "function": { + "arguments": null, + "name": null + }, + "id": "h8,Z?,wnL13??x.3?w,m?m54D eU?V!4qNa?cp?", + "type": "function" + } + ] + } + ], + "model": "tDu?!8TeZ.?.!weY!?D!,..AxNjG.", + "prediction": { + "content": "X?2?.Pj!.vQ,.!J7.zgWZW7 ,RA", + "type": "content" + }, + "presence_penalty": 93.829137528793, + "reasoning_effort": "high", + "response_format": { + "type": "json_object" + }, + "seed": 794, + "stop": [ + ".HL.p?b.,b!,!v?", + "XE?5X0V6Y4i1N?qI!4?P?Y,Y. .y3,7 9v,I?1,2", + "!?4m0s.!F2.?8,,E70!", + "1I,.Eu?0r?.?." + ], + "stream": false, + "stream_options": null, + "tool_choice": { + "allowed_tools": { + "mode": "required", + "tools": [ + {}, + {}, + {} + ] + }, + "type": "allowed_tools" + }, + "tools": [ + { + "custom": { + "description": ",. AT,7.ZGQ0vu0Z.? V.MU,Q3.Ht.?M ?6t,", + "name": "49CMAK!," + }, + "type": "custom" + }, + { + "custom": { + "description": "kD e.o", + "format": { + "type": "text" + }, + "name": "??q6,HB 2k?v,?!g M" + }, + "type": "custom" + }, + { + "custom": { + "description": "229y8G4C?.Gx,34s4te7,Fn?? .q.RI", + "name": ", " + }, + "type": "custom" + } + ] +} diff --git a/payloads/fuzz-snapshots/chat-google-two-arm/case-0f6021d1f374dc8f.meta.json b/payloads/fuzz-snapshots/chat-google-two-arm/case-0f6021d1f374dc8f.meta.json new file mode 100644 index 00000000..9560e951 --- /dev/null +++ b/payloads/fuzz-snapshots/chat-google-two-arm/case-0f6021d1f374dc8f.meta.json @@ -0,0 +1,30 @@ +{ + "issues": [ + "universal(1->2): changed: messages.length (1 -> 0)", + "universal(1->2): changed: params.parallel_tool_calls (false -> null)", + "universal(1->2): changed: params.logprobs (true -> null)", + "universal(1->2): changed: params.store (true -> null)", + "universal(1->2): changed: params.top_logprobs (-581 -> null)", + "universal(1->2): changed: params.stream (true -> null)", + "universal(1->2): changed: params.reasoning.canonical (\"effort\" -> \"budget_tokens\")", + "universal(1->2): changed: params.seed (573 -> null)", + "google(1->2): lost: systemInstruction", + "chat(final): lost: n", + "chat(final): lost: logit_bias", + "chat(final): lost: store", + "chat(final): lost: seed", + "chat(final): lost: verbosity", + "chat(final): lost: logprobs", + "chat(final): lost: max_tokens", + "chat(final): lost: stream", + "chat(final): lost: modalities", + "chat(final): lost: parallel_tool_calls", + "chat(final): lost: top_logprobs", + "chat(final): lost: function_call", + "chat(final): added: max_completion_tokens", + "chat(final): changed: messages.length (1 -> 0)", + "chat(final): changed: reasoning_effort (\"minimal\" -> \"low\")" + ], + "kind": "chat-google-two-arm", + "provider": "chat-completions" +} diff --git a/payloads/fuzz-snapshots/chat-google-two-arm/case-0f6021d1f374dc8f.request.json b/payloads/fuzz-snapshots/chat-google-two-arm/case-0f6021d1f374dc8f.request.json new file mode 100644 index 00000000..28cf1575 --- /dev/null +++ b/payloads/fuzz-snapshots/chat-google-two-arm/case-0f6021d1f374dc8f.request.json @@ -0,0 +1,32 @@ +{ + "function_call": { + "name": "6." + }, + "logit_bias": {}, + "logprobs": true, + "max_tokens": -780, + "messages": [ + { + "content": [ + { + "text": "FnoiN.!,?JL6?1es?5?", + "type": "text" + } + ], + "role": "developer" + } + ], + "modalities": null, + "model": "gpt-4-turbo-preview", + "n": 353, + "parallel_tool_calls": false, + "reasoning_effort": "minimal", + "response_format": { + "type": "json_object" + }, + "seed": 573, + "store": true, + "stream": true, + "top_logprobs": -581, + "verbosity": null +} diff --git a/payloads/fuzz-snapshots/chat-google-two-arm/case-0f9d6200213116a0.meta.json b/payloads/fuzz-snapshots/chat-google-two-arm/case-0f9d6200213116a0.meta.json new file mode 100644 index 00000000..2ad3f311 --- /dev/null +++ b/payloads/fuzz-snapshots/chat-google-two-arm/case-0f9d6200213116a0.meta.json @@ -0,0 +1,25 @@ +{ + "issues": [ + "universal(1->2): added: params.tools[1].parameters.items", + "universal(1->2): changed: messages.length (3 -> 1)", + "universal(1->2): changed: params.presence_penalty (-78.0783387945037 -> null)", + "universal(1->2): changed: params.top_logprobs (-218 -> null)", + "universal(1->2): changed: params.stream (false -> null)", + "universal(1->2): changed: params.logprobs (false -> null)", + "google(1->2): lost: systemInstruction", + "chat(final): lost: presence_penalty", + "chat(final): lost: logprobs", + "chat(final): lost: logit_bias", + "chat(final): lost: audio", + "chat(final): lost: stream", + "chat(final): lost: stream_options", + "chat(final): lost: verbosity", + "chat(final): lost: top_logprobs", + "chat(final): lost: tools[1].function.strict", + "chat(final): added: tools[1].function.parameters.items", + "chat(final): changed: messages.length (3 -> 1)", + "chat(final): changed: stop (\".?,O,m.zJVSm bFX,.,.Rg.OB,\" -> [\".?,O,m.zJVSm bFX,.,.Rg.OB,\"])" + ], + "kind": "chat-google-two-arm", + "provider": "chat-completions" +} diff --git a/payloads/fuzz-snapshots/chat-google-two-arm/case-0f9d6200213116a0.request.json b/payloads/fuzz-snapshots/chat-google-two-arm/case-0f9d6200213116a0.request.json new file mode 100644 index 00000000..49fe0c4b --- /dev/null +++ b/payloads/fuzz-snapshots/chat-google-two-arm/case-0f9d6200213116a0.request.json @@ -0,0 +1,48 @@ +{ + "audio": { + "format": "mp3", + "voice": "cedar" + }, + "logit_bias": {}, + "logprobs": false, + "max_completion_tokens": -236, + "messages": [ + { + "content": "m u.??a.21lI4,!?", + "name": "s.Q,..Y 2 UgW9L!.6Zq368,.?quY5O.PH .s,?V.1", + "role": "developer" + }, + { + "content": "!. p .?", + "name": "z.QMa9 JWyz,m?.Wc?g,I..!! Rl?3?arL?xjkG", + "role": "system" + }, + { + "content": "?iM..Rc,h2iyR.!!,?xP72.iK!M0,t W! lCgjP!ks,!ex,!, ", + "role": "assistant" + } + ], + "model": "n?36xW.,,!!..y?.d3.1NjounY ,F.", + "presence_penalty": -78.0783387945037, + "stop": ".?,O,m.zJVSm bFX,.,.Rg.OB,", + "stream": false, + "stream_options": {}, + "tools": [ + { + "function": { + "name": "B.qb?,2x1fK!!S?,V4E!JN?hf.,dU.G.WYDk8.rxI.Y0xuH.!?" + }, + "type": "function" + }, + { + "function": { + "name": "kcG!yMV ?3qs.3 !?J6,.?LzDX1HZ", + "parameters": {}, + "strict": null + }, + "type": "function" + } + ], + "top_logprobs": -218, + "verbosity": "high" +} diff --git a/payloads/fuzz-snapshots/chat-google-two-arm/case-16f47bea5be70dfb.meta.json b/payloads/fuzz-snapshots/chat-google-two-arm/case-16f47bea5be70dfb.meta.json new file mode 100644 index 00000000..15f916c0 --- /dev/null +++ b/payloads/fuzz-snapshots/chat-google-two-arm/case-16f47bea5be70dfb.meta.json @@ -0,0 +1,25 @@ +{ + "issues": [ + "universal(1->2): changed: messages.length (1 -> 0)", + "universal(1->2): changed: params.response_format.format_type (\"JsonSchema\" -> \"JsonObject\")", + "universal(1->2): changed: params.seed (438 -> null)", + "universal(1->2): changed: params.store (false -> null)", + "universal(1->2): changed: params.stream (false -> null)", + "google(1->2): lost: systemInstruction", + "chat(final): lost: stream", + "chat(final): lost: prediction", + "chat(final): lost: web_search_options", + "chat(final): lost: modalities", + "chat(final): lost: seed", + "chat(final): lost: store", + "chat(final): lost: functions", + "chat(final): lost: logit_bias", + "chat(final): lost: audio", + "chat(final): lost: function_call", + "chat(final): lost: response_format.json_schema", + "chat(final): changed: response_format.type (\"json_schema\" -> \"json_object\")", + "chat(final): changed: messages.length (1 -> 0)" + ], + "kind": "chat-google-two-arm", + "provider": "chat-completions" +} diff --git a/payloads/fuzz-snapshots/chat-google-two-arm/case-16f47bea5be70dfb.request.json b/payloads/fuzz-snapshots/chat-google-two-arm/case-16f47bea5be70dfb.request.json new file mode 100644 index 00000000..3dbd306c --- /dev/null +++ b/payloads/fuzz-snapshots/chat-google-two-arm/case-16f47bea5be70dfb.request.json @@ -0,0 +1,79 @@ +{ + "audio": { + "format": "opus", + "voice": "ballad" + }, + "function_call": "none", + "functions": [ + { + "description": "l.iLO??Nj?Mg ?,.", + "name": "Go?.k!.dNG.,6.64Gg.9cfMP3w, fCdg??lt??,,YpIm .Y.68", + "parameters": {} + }, + { + "name": "5Zi!tj," + }, + { + "description": "XKu!? z .pA3k,0.2D5za 08a!,,hqJ.5K?,w,.j?!6", + "name": "?D,l ,5H9Wb? ", + "parameters": {} + }, + { + "description": ",!.5??w6z,9qP.pR.i,?R.?,?paRrUJcdB ?OD.?4a5K.,", + "name": "g?t,9?G Via?v,", + "parameters": {} + } + ], + "logit_bias": {}, + "messages": [ + { + "content": [ + { + "text": "b..??.d97 ???E7z,b,1.???", + "type": "text" + } + ], + "role": "developer" + } + ], + "modalities": null, + "model": "gpt-4-turbo-2024-04-09", + "prediction": { + "content": [ + { + "text": ",fL.?,.O8H?!.Cy6rWC7pK!,e?", + "type": "text" + } + ], + "type": "content" + }, + "response_format": { + "json_schema": { + "description": "bAw Vx,T8z?uT", + "name": ".5UiXyS 6ege9yt?4.64.", + "strict": null + }, + "type": "json_schema" + }, + "seed": 438, + "store": false, + "stream": false, + "tools": [ + { + "function": { + "description": "S.NGOKC.?3,", + "name": "S q!gI06 e,w,?l,s??4.SG9W?pZS3,.V?ZE,.FsiZ,.9" + }, + "type": "function" + } + ], + "web_search_options": { + "user_location": { + "approximate": { + "country": "!.6.H 9q6!?Ex??,", + "timezone": "0.," + }, + "type": "approximate" + } + } +} diff --git a/payloads/fuzz-snapshots/chat-google-two-arm/case-1e0526fa83578884.meta.json b/payloads/fuzz-snapshots/chat-google-two-arm/case-1e0526fa83578884.meta.json new file mode 100644 index 00000000..b399dd55 --- /dev/null +++ b/payloads/fuzz-snapshots/chat-google-two-arm/case-1e0526fa83578884.meta.json @@ -0,0 +1,21 @@ +{ + "issues": [ + "universal(1->2): changed: messages.length (2 -> 1)", + "universal(1->2): changed: params.logprobs (false -> null)", + "google(1->2): lost: systemInstruction", + "chat(final): lost: max_tokens", + "chat(final): lost: modalities", + "chat(final): lost: audio", + "chat(final): lost: functions", + "chat(final): lost: stream_options", + "chat(final): lost: logprobs", + "chat(final): lost: function_call", + "chat(final): lost: reasoning_effort", + "chat(final): lost: web_search_options", + "chat(final): lost: prediction", + "chat(final): changed: max_completion_tokens (-256 -> -818)", + "chat(final): changed: messages.length (2 -> 1)" + ], + "kind": "chat-google-two-arm", + "provider": "chat-completions" +} diff --git a/payloads/fuzz-snapshots/chat-google-two-arm/case-1e0526fa83578884.request.json b/payloads/fuzz-snapshots/chat-google-two-arm/case-1e0526fa83578884.request.json new file mode 100644 index 00000000..794e7536 --- /dev/null +++ b/payloads/fuzz-snapshots/chat-google-two-arm/case-1e0526fa83578884.request.json @@ -0,0 +1,59 @@ +{ + "audio": { + "format": "flac", + "voice": "cedar" + }, + "function_call": { + "name": "8g!O zxpcI1H,?Z!?., ?W!mhI5!!.3.a" + }, + "functions": [ + { + "name": ".3Xd" + }, + { + "name": ".49Xv,?E.3!??4,fveEgNO76?Id.IpW.,0.n.OE.?v,.Q 4.", + "parameters": {} + }, + { + "description": ",nf2,8?7.goi?l", + "name": "5 ?u.????,,,.L?.,8d?.", + "parameters": {} + }, + { + "name": "k39Zt.,kv?.sR 8!rC,", + "parameters": {} + } + ], + "logprobs": false, + "max_completion_tokens": -256, + "max_tokens": -818, + "messages": [ + { + "content": "Q.F?,w1 .U,Sr3b??7u0, .D.?i.4y", + "name": " 84g4?e?RE,sJ.f?q.8,,w3CgtUE! ?.Ee", + "role": "function" + }, + { + "content": ".t?o,.Zy.E.1Ka.,cRz ..6??9?Pj.y,CJB. is2zQ", + "role": "system" + } + ], + "modalities": [ + "audio", + "audio" + ], + "model": "2g C3i", + "prediction": { + "content": "8,!!u?d,q3K,h?,? !!., ?t?!Xbpx5.,mB4 wI1Gks.,.vZ8", + "type": "content" + }, + "reasoning_effort": null, + "stream_options": null, + "tool_choice": { + "function": { + "name": "XZ 2Xq xr,?g9H6!.?7A?ncLt,y,K??" + }, + "type": "function" + }, + "web_search_options": {} +} diff --git a/payloads/fuzz-snapshots/chat-google-two-arm/case-260034010784dcec.meta.json b/payloads/fuzz-snapshots/chat-google-two-arm/case-260034010784dcec.meta.json new file mode 100644 index 00000000..218a8e7f --- /dev/null +++ b/payloads/fuzz-snapshots/chat-google-two-arm/case-260034010784dcec.meta.json @@ -0,0 +1,32 @@ +{ + "issues": [ + "universal(1->2): added: params.response_format.json_schema.schema.items", + "universal(1->2): changed: params.presence_penalty (42.567216991235036 -> null)", + "universal(1->2): changed: params.top_logprobs (778 -> null)", + "universal(1->2): changed: params.response_format.json_schema.strict (false -> null)", + "universal(1->2): changed: params.response_format.json_schema.name (\"! !c6Z4,a. 7?vRj??.?7,.GoO09Mh\" -> \"response\")", + "universal(1->2): changed: params.tools.length (2 -> 1)", + "universal(1->2): changed: params.parallel_tool_calls (true -> null)", + "universal(1->2): changed: params.logprobs (false -> null)", + "universal(1->2): changed: params.seed (476 -> null)", + "universal(1->2): changed: messages.length (1 -> 0)", + "google(1->2): lost: systemInstruction", + "chat(final): lost: stream_options", + "chat(final): lost: function_call", + "chat(final): lost: presence_penalty", + "chat(final): lost: logit_bias", + "chat(final): lost: top_logprobs", + "chat(final): lost: logprobs", + "chat(final): lost: audio", + "chat(final): lost: functions", + "chat(final): lost: parallel_tool_calls", + "chat(final): lost: seed", + "chat(final): lost: response_format.json_schema.strict", + "chat(final): added: response_format.json_schema.schema.items", + "chat(final): changed: response_format.json_schema.name (\"! !c6Z4,a. 7?vRj??.?7,.GoO09Mh\" -> \"response\")", + "chat(final): changed: messages.length (1 -> 0)", + "chat(final): changed: tools.length (2 -> 1)" + ], + "kind": "chat-google-two-arm", + "provider": "chat-completions" +} diff --git a/payloads/fuzz-snapshots/chat-google-two-arm/case-260034010784dcec.request.json b/payloads/fuzz-snapshots/chat-google-two-arm/case-260034010784dcec.request.json new file mode 100644 index 00000000..98d2ca31 --- /dev/null +++ b/payloads/fuzz-snapshots/chat-google-two-arm/case-260034010784dcec.request.json @@ -0,0 +1,68 @@ +{ + "audio": { + "format": "mp3", + "voice": "aNp??T! .53?zm,q 0O4c!ITUF i?7J..,U.?" + }, + "function_call": { + "name": "p5,!,X.E,,! Hi.xfo.uF?!?.?fRKv. .ce!l!!!" + }, + "functions": [ + { + "name": ",.! ,A,7w,", + "parameters": {} + } + ], + "logit_bias": {}, + "logprobs": false, + "max_completion_tokens": 39, + "messages": [ + { + "content": [ + { + "text": "54MSC!,,?X?5.j,?2n2??,Hh.??.3", + "type": "text" + }, + { + "text": " p!ox7,!..UBr?6,4 ,I4nG9O0Y?hkM5I Ak2.IS n.f7Cs.", + "type": "text" + }, + { + "text": "6?.ngc!???,t.333", + "type": "text" + } + ], + "name": "NjH?7g,9,r XSfG.XHuuC.P,,.zc?3DYqwz Y56?Wk..O T6H", + "role": "developer" + } + ], + "model": "gpt-4-turbo", + "parallel_tool_calls": true, + "presence_penalty": 42.567216991235036, + "response_format": { + "json_schema": { + "name": "! !c6Z4,a. 7?vRj??.?7,.GoO09Mh", + "schema": {}, + "strict": false + }, + "type": "json_schema" + }, + "seed": 476, + "stream_options": null, + "tools": [ + { + "function": { + "name": "pdU.vXy8bF", + "parameters": {}, + "strict": null + }, + "type": "function" + }, + { + "custom": { + "name": "n4!?0P?,,t5pIF 2?h,n3UP p3l.B27.?A" + }, + "type": "custom" + } + ], + "top_logprobs": 778 +} diff --git a/payloads/fuzz-snapshots/chat-google-two-arm/case-2ee666392a1d7d74.meta.json b/payloads/fuzz-snapshots/chat-google-two-arm/case-2ee666392a1d7d74.meta.json new file mode 100644 index 00000000..1c1d2c38 --- /dev/null +++ b/payloads/fuzz-snapshots/chat-google-two-arm/case-2ee666392a1d7d74.meta.json @@ -0,0 +1,25 @@ +{ + "issues": [ + "universal(1->2): changed: params.reasoning.canonical (\"effort\" -> \"budget_tokens\")", + "universal(1->2): changed: params.stream (true -> null)", + "universal(1->2): changed: params.logprobs (false -> null)", + "universal(1->2): changed: params.parallel_tool_calls (false -> null)", + "universal(1->2): changed: params.frequency_penalty (46.10292730424837 -> null)", + "universal(1->2): changed: params.tool_choice.disable_parallel (true -> null)", + "universal(1->2): changed: messages[0].content[0].output (\"?S?W\" -> {\"output\":\"?S?W\"})", + "chat(final): lost: verbosity", + "chat(final): lost: parallel_tool_calls", + "chat(final): lost: logprobs", + "chat(final): lost: web_search_options", + "chat(final): lost: functions", + "chat(final): lost: frequency_penalty", + "chat(final): lost: n", + "chat(final): lost: stream_options", + "chat(final): lost: logit_bias", + "chat(final): lost: stream", + "chat(final): lost: modalities", + "chat(final): changed: messages[0].content (\"?S?W\" -> \"{\\\"output\\\":\\\"?S?W\\\"}\")" + ], + "kind": "chat-google-two-arm", + "provider": "chat-completions" +} diff --git a/payloads/fuzz-snapshots/chat-google-two-arm/case-2ee666392a1d7d74.request.json b/payloads/fuzz-snapshots/chat-google-two-arm/case-2ee666392a1d7d74.request.json new file mode 100644 index 00000000..8c9f090a --- /dev/null +++ b/payloads/fuzz-snapshots/chat-google-two-arm/case-2ee666392a1d7d74.request.json @@ -0,0 +1,37 @@ +{ + "frequency_penalty": 46.10292730424837, + "functions": [ + { + "name": "vDrB3? ", + "parameters": {} + }, + { + "description": "8oiu?.G,yt28!o?,", + "name": "WDz,7Wr0,Tqu.VRWfyE." + } + ], + "logit_bias": {}, + "logprobs": false, + "messages": [ + { + "content": "?S?W", + "role": "tool", + "tool_call_id": "4.?,W!3 z e,.?, 8a.S .3C.t" + } + ], + "modalities": null, + "model": "3!4. .5..T?!!IkJ5wDX?9 ?V.?tg?8", + "n": 913, + "parallel_tool_calls": false, + "reasoning_effort": "high", + "response_format": { + "type": "text" + }, + "stream": true, + "stream_options": null, + "tool_choice": "none", + "verbosity": "low", + "web_search_options": { + "search_context_size": "high" + } +} diff --git a/payloads/fuzz-snapshots/chat-google-two-arm/case-310ed34a7ad861b4.meta.json b/payloads/fuzz-snapshots/chat-google-two-arm/case-310ed34a7ad861b4.meta.json new file mode 100644 index 00000000..0d3dfa58 --- /dev/null +++ b/payloads/fuzz-snapshots/chat-google-two-arm/case-310ed34a7ad861b4.meta.json @@ -0,0 +1,21 @@ +{ + "issues": [ + "universal(1->2): changed: messages[0].content (\"\" -> [{\"text\":\"\",\"type\":\"text\"}])", + "universal(1->2): changed: messages[1].content[0].output (\"\" -> {\"output\":\"\"})", + "universal(1->2): changed: params.store (false -> null)", + "chat(final): lost: reasoning_effort", + "chat(final): lost: prediction", + "chat(final): lost: function_call", + "chat(final): lost: stream_options", + "chat(final): lost: store", + "chat(final): lost: n", + "chat(final): lost: messages[0].audio", + "chat(final): lost: messages[1].name", + "chat(final): added: messages[0].content", + "chat(final): added: messages[1].tool_call_id", + "chat(final): changed: messages[1].content (null -> \"{\\\"output\\\":\\\"\\\"}\")", + "chat(final): changed: messages[1].role (\"function\" -> \"tool\")" + ], + "kind": "chat-google-two-arm", + "provider": "chat-completions" +} diff --git a/payloads/fuzz-snapshots/chat-google-two-arm/case-310ed34a7ad861b4.request.json b/payloads/fuzz-snapshots/chat-google-two-arm/case-310ed34a7ad861b4.request.json new file mode 100644 index 00000000..adf092c2 --- /dev/null +++ b/payloads/fuzz-snapshots/chat-google-two-arm/case-310ed34a7ad861b4.request.json @@ -0,0 +1,29 @@ +{ + "function_call": "auto", + "messages": [ + { + "audio": { + "id": "mjK.?,NZp..6. N0? 8.?TUh.?r cvG!o.v!8xIa1k 7MpF6q5" + }, + "role": "assistant" + }, + { + "content": null, + "name": "Q,ovM?", + "role": "function" + } + ], + "model": ",YaO?5!H?z?x.4!SH F?,.X0TDS?.Px,!8A,,p", + "n": 673, + "prediction": { + "content": "c..o,T?x7D.?.m!?6!Q ?i? ", + "type": "content" + }, + "reasoning_effort": null, + "response_format": { + "type": "json_object" + }, + "store": false, + "stream_options": null, + "tool_choice": "none" +} diff --git a/payloads/fuzz-snapshots/chat-google-two-arm/case-33d13b18d488f747.meta.json b/payloads/fuzz-snapshots/chat-google-two-arm/case-33d13b18d488f747.meta.json new file mode 100644 index 00000000..3ebacff3 --- /dev/null +++ b/payloads/fuzz-snapshots/chat-google-two-arm/case-33d13b18d488f747.meta.json @@ -0,0 +1,28 @@ +{ + "issues": [ + "universal(1->2): changed: messages.length (3 -> 1)", + "universal(1->2): changed: params.seed (-695 -> null)", + "universal(1->2): changed: params.tools ([{\"description\":\" .?!BQ.,1S!83?.vy4\",\"kind\":\"custom\",\"name\":\"V3.Zj.hB?nc.L?Ql.,c4?f?x1uPsO.,,,.?ya?,4?e\"},{\"format\":{\"grammar\":{\"definition\":\"6.2xx?Wt,!c?A?f,2Za?O, L J3Jy.8,yp.\",\"syntax\":\"lark\"},\"type\":\"grammar\"},\"kind\":\"custom\",\"name\":\"264!F?j8F\"}] -> null)", + "universal(1->2): changed: params.top_logprobs (328 -> null)", + "universal(1->2): changed: params.parallel_tool_calls (true -> null)", + "universal(1->2): changed: params.presence_penalty (-17.30499605635964 -> null)", + "universal(1->2): changed: params.frequency_penalty (8.40294665126246 -> null)", + "google(1->2): lost: systemInstruction", + "chat(final): lost: top_logprobs", + "chat(final): lost: prediction", + "chat(final): lost: n", + "chat(final): lost: frequency_penalty", + "chat(final): lost: presence_penalty", + "chat(final): lost: parallel_tool_calls", + "chat(final): lost: seed", + "chat(final): lost: web_search_options", + "chat(final): lost: max_tokens", + "chat(final): lost: tools", + "chat(final): lost: functions", + "chat(final): lost: audio", + "chat(final): changed: messages.length (3 -> 1)", + "chat(final): changed: max_completion_tokens (-902 -> 586)" + ], + "kind": "chat-google-two-arm", + "provider": "chat-completions" +} diff --git a/payloads/fuzz-snapshots/chat-google-two-arm/case-33d13b18d488f747.request.json b/payloads/fuzz-snapshots/chat-google-two-arm/case-33d13b18d488f747.request.json new file mode 100644 index 00000000..c4da079d --- /dev/null +++ b/payloads/fuzz-snapshots/chat-google-two-arm/case-33d13b18d488f747.request.json @@ -0,0 +1,98 @@ +{ + "audio": { + "format": "opus", + "voice": "?,R.l..YBo?,j.!1a.CT!3es.P?W893?,Mjli" + }, + "frequency_penalty": 8.40294665126246, + "functions": [ + { + "description": "?e4W,DB?.?4VO??T?4e.EZ6.?D,5?5.v5j.D,pG5.uX ", + "name": "N,O?.,9,.d,.4 75fh,wl?04!.9 4e ..?4A", + "parameters": {} + }, + { + "name": "Dbjw??" + }, + { + "name": "!O,??", + "parameters": {} + }, + { + "name": "NXf.?S, 1OO2??R.o!vC,x.7?bS ku,,R?EigRM ?yUx,Y?SJz", + "parameters": {} + } + ], + "max_completion_tokens": -902, + "max_tokens": 586, + "messages": [ + { + "function_call": null, + "role": "assistant" + }, + { + "content": [ + { + "text": ",S!?6!s,AyB.Wn3T,us.g.3", + "type": "text" + } + ], + "name": "?S.3Y.?3?55Q.71Ot5RoF1,P60?.?bjE.?8??XM4?P?m,,?", + "role": "developer" + }, + { + "audio": null, + "content": ".A4.1.a,,4L.ni,8.ve,?qnsu6?P", + "name": "b5,!O!k l.HQJI!Bi0c!!,", + "role": "assistant" + } + ], + "model": "a,1?0?.7,BTr57g?W.N7 ,h3IJr S4 nc", + "n": 159, + "parallel_tool_calls": true, + "prediction": { + "content": [ + { + "text": ",6?Q ", + "type": "text" + }, + { + "text": "8x?x?9..N. !..Lm.? ?4.,??LuO.A5Uj", + "type": "text" + }, + { + "text": "DZBe .7 .?,J6Q?Qcy!.il52., ,!", + "type": "text" + } + ], + "type": "content" + }, + "presence_penalty": -17.30499605635964, + "seed": -695, + "tool_choice": "required", + "tools": [ + { + "custom": { + "description": " .?!BQ.,1S!83?.vy4", + "name": "V3.Zj.hB?nc.L?Ql.,c4?f?x1uPsO.,,,.?ya?,4?e" + }, + "type": "custom" + }, + { + "custom": { + "format": { + "grammar": { + "definition": "6.2xx?Wt,!c?A?f,2Za?O, L J3Jy.8,yp.", + "syntax": "lark" + }, + "type": "grammar" + }, + "name": "264!F?j8F" + }, + "type": "custom" + } + ], + "top_logprobs": 328, + "web_search_options": { + "search_context_size": "medium" + } +} diff --git a/payloads/fuzz-snapshots/chat-google-two-arm/case-34ba485a1406f69c.meta.json b/payloads/fuzz-snapshots/chat-google-two-arm/case-34ba485a1406f69c.meta.json new file mode 100644 index 00000000..81f807b8 --- /dev/null +++ b/payloads/fuzz-snapshots/chat-google-two-arm/case-34ba485a1406f69c.meta.json @@ -0,0 +1,28 @@ +{ + "issues": [ + "universal(1->2): added: params.tools[0].parameters.items", + "universal(1->2): changed: messages[0].content[0].output (\"\" -> {\"output\":\"\"})", + "universal(1->2): changed: messages[1].content (\"F.sO.G!g?ba?8bDF F,Dd.Dg,.,Z?0,xA,5i?!!?..9oZecMc\" -> [{\"text\":\"F.sO.G!g?ba?8bDF F,Dd.Dg,.,Z?0,xA,5i?!!?..9oZecMc\",\"type\":\"text\"}])", + "universal(1->2): changed: params.presence_penalty (17.299183099790024 -> null)", + "universal(1->2): changed: params.reasoning.canonical (\"effort\" -> \"budget_tokens\")", + "universal(1->2): changed: params.top_logprobs (672 -> null)", + "universal(1->2): changed: params.parallel_tool_calls (true -> null)", + "chat(final): lost: parallel_tool_calls", + "chat(final): lost: n", + "chat(final): lost: top_logprobs", + "chat(final): lost: web_search_options", + "chat(final): lost: prediction", + "chat(final): lost: presence_penalty", + "chat(final): lost: messages[0].name", + "chat(final): lost: messages[1].audio", + "chat(final): lost: messages[1].function_call", + "chat(final): lost: messages[1].refusal", + "chat(final): added: tools[0].function.parameters.items", + "chat(final): added: messages[0].tool_call_id", + "chat(final): changed: stop (\"!D.k?R?p L!N,qnWi,s 8.5,l L. k!h\" -> [\"!D.k?R?p L!N,qnWi,s 8.5,l L. k!h\"])", + "chat(final): changed: messages[0].content (null -> \"{\\\"output\\\":\\\"\\\"}\")", + "chat(final): changed: messages[0].role (\"function\" -> \"tool\")" + ], + "kind": "chat-google-two-arm", + "provider": "chat-completions" +} diff --git a/payloads/fuzz-snapshots/chat-google-two-arm/case-34ba485a1406f69c.request.json b/payloads/fuzz-snapshots/chat-google-two-arm/case-34ba485a1406f69c.request.json new file mode 100644 index 00000000..a4a10759 --- /dev/null +++ b/payloads/fuzz-snapshots/chat-google-two-arm/case-34ba485a1406f69c.request.json @@ -0,0 +1,60 @@ +{ + "messages": [ + { + "content": null, + "name": "??.p m,!6?00w.?..CQ8.!...uZ...s?g.A", + "role": "function" + }, + { + "audio": null, + "content": "F.sO.G!g?ba?8bDF F,Dd.Dg,.,Z?0,xA,5i?!!?..9oZecMc", + "function_call": { + "arguments": "D,e uLG3v A6XQ,5m", + "name": ",Pm?,,,BAg!N.khza.XW.jV?,5.Rp51,." + }, + "refusal": null, + "role": "assistant" + } + ], + "model": "7oKl,XCq8,fE,35,VV!,..?s.2 3kn.!I.Un.gWoI1", + "n": 905, + "parallel_tool_calls": true, + "prediction": { + "content": [ + { + "text": " vIfvB.l0.U?i", + "type": "text" + }, + { + "text": "..?l.!20eC..?Om0q!9?b.51lt,??m,.. .F,e,a", + "type": "text" + } + ], + "type": "content" + }, + "presence_penalty": 17.299183099790024, + "reasoning_effort": "low", + "response_format": { + "type": "json_object" + }, + "stop": "!D.k?R?p L!N,qnWi,s 8.5,l L. k!h", + "tool_choice": { + "function": { + "name": "5,?F,2x?Frh1 .Uq5!.m" + }, + "type": "function" + }, + "tools": [ + { + "function": { + "name": "CNl.bI.?D?WW,.af,?!QY.?.? ?.z85Pp?! oZ", + "parameters": {} + }, + "type": "function" + } + ], + "top_logprobs": 672, + "web_search_options": { + "search_context_size": "high" + } +} diff --git a/payloads/fuzz-snapshots/chat-google-two-arm/case-4ec638697bbccbe3.meta.json b/payloads/fuzz-snapshots/chat-google-two-arm/case-4ec638697bbccbe3.meta.json new file mode 100644 index 00000000..a2a2ac47 --- /dev/null +++ b/payloads/fuzz-snapshots/chat-google-two-arm/case-4ec638697bbccbe3.meta.json @@ -0,0 +1,23 @@ +{ + "issues": [ + "universal(1->2): changed: params.stream (true -> null)", + "universal(1->2): changed: params.store (true -> null)", + "universal(1->2): changed: params.logprobs (true -> null)", + "universal(1->2): changed: params.reasoning.canonical (\"effort\" -> \"budget_tokens\")", + "universal(1->2): changed: params.reasoning.effort (\"medium\" -> \"low\")", + "universal(1->2): changed: params.presence_penalty (90.809479429592 -> null)", + "universal(1->2): changed: messages.length (1 -> 0)", + "google(1->2): lost: systemInstruction", + "chat(final): lost: presence_penalty", + "chat(final): lost: stream", + "chat(final): lost: functions", + "chat(final): lost: web_search_options", + "chat(final): lost: store", + "chat(final): lost: n", + "chat(final): lost: logprobs", + "chat(final): changed: reasoning_effort (\"medium\" -> \"low\")", + "chat(final): changed: messages.length (1 -> 0)" + ], + "kind": "chat-google-two-arm", + "provider": "chat-completions" +} diff --git a/payloads/fuzz-snapshots/chat-google-two-arm/case-4ec638697bbccbe3.request.json b/payloads/fuzz-snapshots/chat-google-two-arm/case-4ec638697bbccbe3.request.json new file mode 100644 index 00000000..86176579 --- /dev/null +++ b/payloads/fuzz-snapshots/chat-google-two-arm/case-4ec638697bbccbe3.request.json @@ -0,0 +1,56 @@ +{ + "functions": [ + { + "name": ".?Hw.?,!..w", + "parameters": {} + }, + { + "description": "fv 0gAo?Ez6,Hr,.W?,M ZtY, 7n21nS?fH.W?,c", + "name": "? L?g?,E?!.?.A?80R8.dqY,UScyT8134?.Z8W!?.,,?8" + }, + { + "description": "bB ,, .9.l.,Y?P.???e5", + "name": "iv?!" + } + ], + "logprobs": true, + "max_completion_tokens": 160, + "messages": [ + { + "content": [ + { + "text": "3Oqp,..", + "type": "text" + }, + { + "text": "??cGz!,5y8", + "type": "text" + }, + { + "text": "BSs E?k6?6,..?!fC?5A,", + "type": "text" + } + ], + "name": "!u,?Nl?,O2 0 ag!6. rw..!?,LMp!L?!VWf,.av!", + "role": "developer" + } + ], + "model": "RJ", + "n": -396, + "presence_penalty": 90.809479429592, + "reasoning_effort": "medium", + "response_format": { + "type": "json_object" + }, + "store": true, + "stream": true, + "web_search_options": { + "search_context_size": "low", + "user_location": { + "approximate": { + "region": "1?x?zH7z,.,27Doe?C." + }, + "type": "approximate" + } + } +} diff --git a/payloads/fuzz-snapshots/chat-google-two-arm/case-64011eae99c883f4.meta.json b/payloads/fuzz-snapshots/chat-google-two-arm/case-64011eae99c883f4.meta.json new file mode 100644 index 00000000..10c4129d --- /dev/null +++ b/payloads/fuzz-snapshots/chat-google-two-arm/case-64011eae99c883f4.meta.json @@ -0,0 +1,37 @@ +{ + "issues": [ + "universal(1->2): added: params.response_format.json_schema.schema.items", + "universal(1->2): changed: messages.length (3 -> 1)", + "universal(1->2): changed: params.tools ([{\"description\":\"Owd5Y!XouwrA4x..qh11U !?74 m ?VFbY,k ce!.k eE\",\"kind\":\"custom\",\"name\":\".XDT!ckr9,,?wXL?.,.x u !2,.1? 5b!9o!HAF.?7\"},{\"format\":{\"type\":\"text\"},\"kind\":\"custom\",\"name\":\"Y,,o.uPG6,cEg!f.fA83B4.6umV?N ?uZJ!,vi\"}] -> null)", + "universal(1->2): changed: params.reasoning.effort (\"high\" -> \"low\")", + "universal(1->2): changed: params.reasoning.canonical (\"effort\" -> \"budget_tokens\")", + "universal(1->2): changed: params.store (false -> null)", + "universal(1->2): changed: params.top_logprobs (-941 -> null)", + "universal(1->2): changed: params.response_format.json_schema.name (\"i?.Bn.S?eF.K!.9v rPD,?,.1R.LY92i,,?..OY8,?,!W.\" -> \"response\")", + "universal(1->2): changed: params.response_format.json_schema.description (\",?N !u3d?Z0?F \" -> null)", + "universal(1->2): changed: params.seed (-566 -> null)", + "google(1->2): lost: systemInstruction", + "chat(final): lost: seed", + "chat(final): lost: functions", + "chat(final): lost: verbosity", + "chat(final): lost: function_call", + "chat(final): lost: tools", + "chat(final): lost: top_logprobs", + "chat(final): lost: max_tokens", + "chat(final): lost: logit_bias", + "chat(final): lost: audio", + "chat(final): lost: prediction", + "chat(final): lost: store", + "chat(final): lost: stream_options", + "chat(final): lost: response_format.json_schema.strict", + "chat(final): lost: response_format.json_schema.description", + "chat(final): added: response_format.json_schema.schema.items", + "chat(final): changed: messages.length (3 -> 1)", + "chat(final): changed: stop (\"7QY,1D c,.9K.n,,,k!,1?fcO\" -> [\"7QY,1D c,.9K.n,,,k!,1?fcO\"])", + "chat(final): changed: max_completion_tokens (416 -> 357)", + "chat(final): changed: response_format.json_schema.name (\"i?.Bn.S?eF.K!.9v rPD,?,.1R.LY92i,,?..OY8,?,!W.\" -> \"response\")", + "chat(final): changed: reasoning_effort (\"high\" -> \"low\")" + ], + "kind": "chat-google-two-arm", + "provider": "chat-completions" +} diff --git a/payloads/fuzz-snapshots/chat-google-two-arm/case-64011eae99c883f4.request.json b/payloads/fuzz-snapshots/chat-google-two-arm/case-64011eae99c883f4.request.json new file mode 100644 index 00000000..db7bb721 --- /dev/null +++ b/payloads/fuzz-snapshots/chat-google-two-arm/case-64011eae99c883f4.request.json @@ -0,0 +1,99 @@ +{ + "audio": { + "format": "wav", + "voice": "w?!L" + }, + "function_call": "auto", + "functions": [ + { + "description": ".Zw?E", + "name": "d", + "parameters": {} + }, + { + "description": "7Qb?,!As ? 4JdK !!!2! D,?xo l", + "name": "NJW.N.z0u9!aP,lI4,k.2,M2U0K" + }, + { + "description": ". ,nGTl.d..yZBuF.,b5lgE .EIkP.L", + "name": "!" + } + ], + "logit_bias": {}, + "max_completion_tokens": 416, + "max_tokens": 357, + "messages": [ + { + "content": null, + "name": "B,.?!N7iTxUU9P,v,!q!75? E.TB 0!j.GVvD.C2", + "role": "function" + }, + { + "content": "MJ1.a,kg6!?.HDr,e0,.s.Kx02", + "role": "system" + }, + { + "content": ",V.i7x,rD,..a?p5 0?Z", + "role": "tool", + "tool_call_id": "BW " + } + ], + "model": "gpt-4-turbo-preview", + "prediction": { + "content": [ + { + "text": "h?92fv9z3G, ?,?c..?P,,0,Y.R,?.NEK,OGe?", + "type": "text" + }, + { + "text": " .?gAD.gXaD?.r,GJ?.", + "type": "text" + }, + { + "text": ".!.!.6?J1,.7gO?j3v? W.s,,?2O,.? QG,b4m?o.", + "type": "text" + } + ], + "type": "content" + }, + "reasoning_effort": "high", + "response_format": { + "json_schema": { + "description": ",?N !u3d?Z0?F ", + "name": "i?.Bn.S?eF.K!.9v rPD,?,.1R.LY92i,,?..OY8,?,!W.", + "schema": {}, + "strict": null + }, + "type": "json_schema" + }, + "seed": -566, + "stop": "7QY,1D c,.9K.n,,,k!,1?fcO", + "store": false, + "stream_options": null, + "tool_choice": { + "function": { + "name": "9d.o!.GQZ,? ??A O?s," + }, + "type": "function" + }, + "tools": [ + { + "custom": { + "description": "Owd5Y!XouwrA4x..qh11U !?74 m ?VFbY,k ce!.k eE", + "name": ".XDT!ckr9,,?wXL?.,.x u !2,.1? 5b!9o!HAF.?7" + }, + "type": "custom" + }, + { + "custom": { + "format": { + "type": "text" + }, + "name": "Y,,o.uPG6,cEg!f.fA83B4.6umV?N ?uZJ!,vi" + }, + "type": "custom" + } + ], + "top_logprobs": -941, + "verbosity": null +} diff --git a/payloads/fuzz-snapshots/chat-google-two-arm/case-a3ec1907e0d629d5.meta.json b/payloads/fuzz-snapshots/chat-google-two-arm/case-a3ec1907e0d629d5.meta.json new file mode 100644 index 00000000..776c55da --- /dev/null +++ b/payloads/fuzz-snapshots/chat-google-two-arm/case-a3ec1907e0d629d5.meta.json @@ -0,0 +1,28 @@ +{ + "issues": [ + "universal(1->2): lost: params.tools[0].strict", + "universal(1->2): changed: messages.length (3 -> 0)", + "universal(1->2): changed: params.logprobs (true -> null)", + "universal(1->2): changed: params.frequency_penalty (-70.06246855099464 -> null)", + "universal(1->2): changed: params.stream (true -> null)", + "universal(1->2): changed: params.store (false -> null)", + "google(1->2): lost: systemInstruction", + "chat(final): lost: store", + "chat(final): lost: reasoning_effort", + "chat(final): lost: audio", + "chat(final): lost: modalities", + "chat(final): lost: prediction", + "chat(final): lost: stream", + "chat(final): lost: frequency_penalty", + "chat(final): lost: web_search_options", + "chat(final): lost: function_call", + "chat(final): lost: stream_options", + "chat(final): lost: functions", + "chat(final): lost: logprobs", + "chat(final): lost: tools[0].function.strict", + "chat(final): changed: stop (\".9.h?,!3X cix?R7?b.mP7x4p6H??6!?7pC,?k?U\" -> [\".9.h?,!3X cix?R7?b.mP7x4p6H??6!?7pC,?k?U\"])", + "chat(final): changed: messages.length (3 -> 0)" + ], + "kind": "chat-google-two-arm", + "provider": "chat-completions" +} diff --git a/payloads/fuzz-snapshots/chat-google-two-arm/case-a3ec1907e0d629d5.request.json b/payloads/fuzz-snapshots/chat-google-two-arm/case-a3ec1907e0d629d5.request.json new file mode 100644 index 00000000..f5261471 --- /dev/null +++ b/payloads/fuzz-snapshots/chat-google-two-arm/case-a3ec1907e0d629d5.request.json @@ -0,0 +1,80 @@ +{ + "audio": { + "format": "pcm16", + "voice": "ash" + }, + "frequency_penalty": -70.06246855099464, + "function_call": "auto", + "functions": [ + { + "name": "E 1qQ KmZ? " + } + ], + "logprobs": true, + "max_completion_tokens": 443, + "messages": [ + { + "content": [ + { + "text": ",f .wX ?", + "type": "text" + } + ], + "role": "developer" + }, + { + "content": " 5xPcy?qc ,Q?.,?!.?10KMn.,F?V,h?9?Y", + "name": " Lof..WP56s i!9Xw", + "role": "system" + }, + { + "content": [ + { + "text": "8yt!w", + "type": "text" + }, + { + "text": ".!wX?", + "type": "text" + } + ], + "name": "A,8.d.p., ??k..2h8 9 HI.t!u8.1s?t,OV!?H! !?E5W!y", + "role": "developer" + } + ], + "modalities": null, + "model": "o3-2025-04-16", + "prediction": { + "content": [ + { + "text": "??j.,.E1 f,.?d.50QswHW.!5S.2?h?oLnJi?z5.T,tg.", + "type": "text" + }, + { + "text": ".?n?6,N.t8I!?!qy?hu.A88!?vU2vj.t.2?AxUG ,XhPh", + "type": "text" + } + ], + "type": "content" + }, + "reasoning_effort": null, + "stop": ".9.h?,!3X cix?R7?b.mP7x4p6H??6!?7pC,?k?U", + "store": false, + "stream": true, + "stream_options": { + "include_obfuscation": false + }, + "tools": [ + { + "function": { + "description": "r.7.?J!3x", + "name": "?,Z", + "strict": true + }, + "type": "function" + } + ], + "web_search_options": { + "search_context_size": "medium" + } +} diff --git a/payloads/fuzz-snapshots/google-roundtrip/case-00015db5103bca8c.meta.json b/payloads/fuzz-snapshots/google-roundtrip/case-00015db5103bca8c.meta.json new file mode 100644 index 00000000..c77444fe --- /dev/null +++ b/payloads/fuzz-snapshots/google-roundtrip/case-00015db5103bca8c.meta.json @@ -0,0 +1,7 @@ +{ + "issues": [ + "request_to_universal error: Conversion to universal format failed: Missing required field: parts" + ], + "kind": "request-roundtrip", + "provider": "google" +} diff --git a/payloads/fuzz-snapshots/google-roundtrip/case-00015db5103bca8c.request.json b/payloads/fuzz-snapshots/google-roundtrip/case-00015db5103bca8c.request.json new file mode 100644 index 00000000..8a5b052d --- /dev/null +++ b/payloads/fuzz-snapshots/google-roundtrip/case-00015db5103bca8c.request.json @@ -0,0 +1,53 @@ +{ + "cachedContent": "Zx.yEkOd0", + "contents": [ + { + "role": ".,i?0" + }, + { + "role": "?kV.k ,,LMM.3f,1?,mlxb6I3IG" + } + ], + "generationConfig": { + "_responseJsonSchema": null, + "frequencyPenalty": -60.42419908836965, + "imageConfig": {}, + "logprobs": -825, + "maxOutputTokens": -266, + "presencePenalty": 62.34146184198899, + "responseMimeType": "S", + "responseModalities": [ + "IMAGE" + ], + "speechConfig": { + "multiSpeakerVoiceConfig": { + "speakerVoiceConfigs": [ + { + "voiceConfig": { + "prebuiltVoiceConfig": null + } + } + ] + } + }, + "stopSequences": [ + "zA!,2.xB,TLcC. jfvuE.,k!xy S.?5I?2c.o" + ], + "thinkingConfig": { + "includeThoughts": true, + "thinkingBudget": -573, + "thinkingLevel": "MINIMAL" + } + }, + "safetySettings": [ + {}, + {} + ], + "systemInstruction": {}, + "toolConfig": { + "functionCallingConfig": { + "mode": "AUTO" + }, + "retrievalConfig": {} + } +} diff --git a/payloads/fuzz-snapshots/google-roundtrip/case-0005d480caf84338.meta.json b/payloads/fuzz-snapshots/google-roundtrip/case-0005d480caf84338.meta.json new file mode 100644 index 00000000..64f19efb --- /dev/null +++ b/payloads/fuzz-snapshots/google-roundtrip/case-0005d480caf84338.meta.json @@ -0,0 +1,15 @@ +{ + "issues": [ + "lost: toolConfig", + "lost: systemInstruction", + "lost: contents[0].parts[0].thought", + "lost: contents[0].parts[0].functionCall", + "lost: contents[0].parts[0].thoughtSignature", + "lost: contents[0].parts[0].videoMetadata", + "lost: contents[0].parts[0].partMetadata", + "lost: contents[0].parts[0].functionResponse", + "added: contents[0].role" + ], + "kind": "request-roundtrip", + "provider": "google" +} diff --git a/payloads/fuzz-snapshots/google-roundtrip/case-0005d480caf84338.request.json b/payloads/fuzz-snapshots/google-roundtrip/case-0005d480caf84338.request.json new file mode 100644 index 00000000..92e9f4d2 --- /dev/null +++ b/payloads/fuzz-snapshots/google-roundtrip/case-0005d480caf84338.request.json @@ -0,0 +1,97 @@ +{ + "contents": [ + { + "parts": [ + { + "functionCall": { + "id": "xJ9iB" + }, + "functionResponse": { + "id": ".qM?", + "name": ",W?zH.p??d 3!", + "willContinue": true + }, + "partMetadata": {}, + "text": "52, .X?,!Z 1L s!,GH!!!aFdv.mKl,l?,.?Dq", + "thought": true, + "thoughtSignature": " Kl,6?", + "videoMetadata": { + "endOffset": "Bpv?? !,,Jr,giuk!n. EZe66,1!buc?", + "fps": 11.271011959752407, + "startOffset": "5P3Q,w,n,3e,x" + } + } + ] + } + ], + "systemInstruction": { + "parts": [ + { + "codeExecutionResult": { + "output": "?I.,?jJ!f. .DuF1374!.Ahv2q7,9j22T3zHbO.Rn4?A" + }, + "executableCode": { + "code": "!?B?D.N!U??us1?.x??,,S4f2b6.L", + "language": "LANGUAGE_UNSPECIFIED" + }, + "fileData": { + "fileUri": "?!.jj?!,PC8. 6.??,x8,?s?pw33A,R?L L", + "mimeType": ",,.?.!w1w0 3i.9!?...yQ.x,FVL!.." + }, + "functionCall": { + "args": {}, + "id": "7?. 5.sjWl9 2Qc 5,! z. ??Qo H.q47. OPvJuB.,!iNi v", + "name": ".3A?.?4g?IO!?Ymdx!a??s!,p?.fOGE?.e,Vg?" + }, + "functionResponse": { + "id": "!,cd,fy.. hA?82S9Fhp 4m.h?!DJ!u8?j.?N9g1,", + "parts": [ + { + "inlineData": { + "mimeType": null + } + }, + {}, + { + "inlineData": { + "data": null + } + } + ] + }, + "partMetadata": {}, + "text": "S" + }, + { + "codeExecutionResult": { + "outcome": "OUTCOME_DEADLINE_EXCEEDED", + "output": "a.MGjQ4" + }, + "fileData": { + "mimeType": "7.Q8 NZ..D..Q?yNAc?KVNg,?p8.??," + }, + "functionResponse": { + "parts": [ + { + "inlineData": {} + }, + { + "inlineData": { + "data": null + } + } + ], + "response": {}, + "scheduling": "INTERRUPT" + }, + "inlineData": {}, + "videoMetadata": { + "endOffset": " .,.?g9.,j,Ly2O,47.XqVw.p?Oe?0?ZA", + "fps": 69.29815115247787, + "startOffset": "i ?x?71He,si?1.?.IW,B?!I!0" + } + } + ] + }, + "toolConfig": {} +} diff --git a/payloads/fuzz-snapshots/google-roundtrip/case-007bd4500a682a93.meta.json b/payloads/fuzz-snapshots/google-roundtrip/case-007bd4500a682a93.meta.json new file mode 100644 index 00000000..4b7c3afd --- /dev/null +++ b/payloads/fuzz-snapshots/google-roundtrip/case-007bd4500a682a93.meta.json @@ -0,0 +1,9 @@ +{ + "issues": [ + "changed: tools.length (1 -> 2)", + "changed: contents[0].parts.length (1 -> 0)", + "changed: contents[0].role (\"a\" -> \"user\")" + ], + "kind": "request-roundtrip", + "provider": "google" +} diff --git a/payloads/fuzz-snapshots/google-roundtrip/case-007bd4500a682a93.request.json b/payloads/fuzz-snapshots/google-roundtrip/case-007bd4500a682a93.request.json new file mode 100644 index 00000000..e0ea4523 --- /dev/null +++ b/payloads/fuzz-snapshots/google-roundtrip/case-007bd4500a682a93.request.json @@ -0,0 +1,27 @@ +{ + "contents": [ + { + "parts": [ + { + "inlineData": {} + } + ], + "role": "a" + } + ], + "tools": [ + { + "computerUse": { + "excludedPredefinedFunctions": [ + "?.bi.?,p" + ] + }, + "googleSearch": { + "timeRangeFilter": { + "startTime": "QDBDMGS.?lxG,,?.X?9?j.k?TA.!x.R,?9," + } + }, + "googleSearchRetrieval": {} + } + ] +} diff --git a/payloads/fuzz-snapshots/google-roundtrip/case-011c75aef5297a6e.meta.json b/payloads/fuzz-snapshots/google-roundtrip/case-011c75aef5297a6e.meta.json new file mode 100644 index 00000000..052be157 --- /dev/null +++ b/payloads/fuzz-snapshots/google-roundtrip/case-011c75aef5297a6e.meta.json @@ -0,0 +1,11 @@ +{ + "issues": [ + "lost: toolConfig", + "lost: safetySettings", + "changed: tools.length (1 -> 2)", + "changed: contents[0].role (\"n7R?!!.8!hC4l85,Qwt5o UJX?K,.812 !ULY!!!Y?5k4yLf\" -> \"user\")", + "changed: contents[0].parts.length (1 -> 0)" + ], + "kind": "request-roundtrip", + "provider": "google" +} diff --git a/payloads/fuzz-snapshots/google-roundtrip/case-011c75aef5297a6e.request.json b/payloads/fuzz-snapshots/google-roundtrip/case-011c75aef5297a6e.request.json new file mode 100644 index 00000000..66416488 --- /dev/null +++ b/payloads/fuzz-snapshots/google-roundtrip/case-011c75aef5297a6e.request.json @@ -0,0 +1,52 @@ +{ + "contents": [ + { + "parts": [ + { + "fileData": { + "fileUri": "!A5Hc!20t" + }, + "functionCall": { + "args": {}, + "id": "!R ,gX56,,?29?? C8w?,4.!z??q44x1LXALQtl" + }, + "inlineData": {}, + "partMetadata": {}, + "thought": false, + "thoughtSignature": "Jn 0L,q3J", + "videoMetadata": { + "fps": 92.51682992764292 + } + } + ], + "role": "n7R?!!.8!hC4l85,Qwt5o UJX?K,.812 !ULY!!!Y?5k4yLf" + } + ], + "model": "iw.?1?,.g3! .?,5?2,,.hB.1?.J,9?,,?32e.Hi1.FAY", + "safetySettings": [ + { + "category": "HARM_CATEGORY_HARASSMENT", + "threshold": "HARM_BLOCK_THRESHOLD_UNSPECIFIED" + }, + { + "category": "HARM_CATEGORY_DEROGATORY" + } + ], + "toolConfig": {}, + "tools": [ + { + "computerUse": { + "environment": "ENVIRONMENT_UNSPECIFIED", + "excludedPredefinedFunctions": [ + "e4 .?PO.?.!c?.bi.?,p" + ] + }, + "googleSearch": { + "timeRangeFilter": { + "startTime": "QDBDMGS.?lxG,,?.X?9?j.k?TA.!x.R,?9," + } + }, + "googleSearchRetrieval": {} + } + ] +} diff --git a/payloads/fuzz-snapshots/google-roundtrip/case-051ebd07ca42b0d6.meta.json b/payloads/fuzz-snapshots/google-roundtrip/case-051ebd07ca42b0d6.meta.json new file mode 100644 index 00000000..0bc93dfa --- /dev/null +++ b/payloads/fuzz-snapshots/google-roundtrip/case-051ebd07ca42b0d6.meta.json @@ -0,0 +1,10 @@ +{ + "issues": [ + "lost: toolConfig", + "lost: contents[0].parts[0].inlineData", + "lost: contents[0].parts[0].videoMetadata", + "changed: contents[0].role (\"j.,.kD8c,v! ?\" -> \"user\")" + ], + "kind": "request-roundtrip", + "provider": "google" +} diff --git a/payloads/fuzz-snapshots/google-roundtrip/case-051ebd07ca42b0d6.request.json b/payloads/fuzz-snapshots/google-roundtrip/case-051ebd07ca42b0d6.request.json new file mode 100644 index 00000000..abd0ac60 --- /dev/null +++ b/payloads/fuzz-snapshots/google-roundtrip/case-051ebd07ca42b0d6.request.json @@ -0,0 +1,23 @@ +{ + "contents": [ + { + "parts": [ + { + "inlineData": {}, + "text": "?lsKj.a? ??R6?,uO3q?p2j?? .h ", + "videoMetadata": { + "fps": 70.04902652071755 + } + } + ], + "role": "j.,.kD8c,v! ?" + } + ], + "toolConfig": { + "retrievalConfig": { + "latLng": { + "longitude": 42.35899714596116 + } + } + } +} diff --git a/payloads/fuzz-snapshots/google-roundtrip/case-066263d9b371b047.meta.json b/payloads/fuzz-snapshots/google-roundtrip/case-066263d9b371b047.meta.json new file mode 100644 index 00000000..85aa7661 --- /dev/null +++ b/payloads/fuzz-snapshots/google-roundtrip/case-066263d9b371b047.meta.json @@ -0,0 +1,14 @@ +{ + "issues": [ + "lost: safetySettings", + "lost: systemInstruction", + "lost: toolConfig", + "lost: contents[0].parts[0].videoMetadata", + "lost: contents[0].parts[0].mediaResolution", + "lost: contents[0].parts[0].thoughtSignature", + "added: contents[0].role", + "added: contents[0].parts[0].inlineData.mimeType" + ], + "kind": "request-roundtrip", + "provider": "google" +} diff --git a/payloads/fuzz-snapshots/google-roundtrip/case-066263d9b371b047.request.json b/payloads/fuzz-snapshots/google-roundtrip/case-066263d9b371b047.request.json new file mode 100644 index 00000000..642d91b6 --- /dev/null +++ b/payloads/fuzz-snapshots/google-roundtrip/case-066263d9b371b047.request.json @@ -0,0 +1,50 @@ +{ + "contents": [ + { + "parts": [ + { + "inlineData": { + "data": ".vW1 v?,Nx6mq?a?A?8,5E" + }, + "mediaResolution": {}, + "thoughtSignature": "AVJlL,u.?Y?PN Tsr", + "videoMetadata": { + "fps": 65.50903686895178, + "startOffset": "?Z?R,.Fu d. ," + } + } + ] + } + ], + "model": "D ", + "safetySettings": [ + {} + ], + "systemInstruction": { + "parts": [ + { + "codeExecutionResult": { + "outcome": "OUTCOME_UNSPECIFIED" + }, + "executableCode": { + "code": " 9.!?..t3VA.2?", + "language": "LANGUAGE_UNSPECIFIED" + }, + "functionResponse": { + "response": {}, + "willContinue": false + }, + "text": "z,f 7,IZlg, N!W,u?a f5,4U!.2!,d,.", + "thought": true, + "thoughtSignature": ",KheB??KQqCP,M1!.c.e.,2gzDKYJ.?,FBt,Pk8" + } + ] + }, + "toolConfig": { + "retrievalConfig": { + "latLng": { + "longitude": -49.83756195325235 + } + } + } +} diff --git a/payloads/fuzz-snapshots/google-roundtrip/case-08e697e9ba45fe3e.meta.json b/payloads/fuzz-snapshots/google-roundtrip/case-08e697e9ba45fe3e.meta.json new file mode 100644 index 00000000..10a25eea --- /dev/null +++ b/payloads/fuzz-snapshots/google-roundtrip/case-08e697e9ba45fe3e.meta.json @@ -0,0 +1,15 @@ +{ + "issues": [ + "lost: cachedContent", + "lost: contents[0].parts[0].functionCall", + "lost: contents[0].parts[0].mediaResolution", + "lost: contents[0].parts[0].fileData", + "lost: contents[0].parts[0].thought", + "lost: contents[0].parts[0].partMetadata", + "lost: contents[0].parts[0].videoMetadata", + "lost: contents[0].parts[0].codeExecutionResult", + "changed: contents[0].role (\"L,???b3M?!vZ!y,.3Q..FL0!.5 e.,?\" -> \"user\")" + ], + "kind": "request-roundtrip", + "provider": "google" +} diff --git a/payloads/fuzz-snapshots/google-roundtrip/case-08e697e9ba45fe3e.request.json b/payloads/fuzz-snapshots/google-roundtrip/case-08e697e9ba45fe3e.request.json new file mode 100644 index 00000000..ac7ef8f6 --- /dev/null +++ b/payloads/fuzz-snapshots/google-roundtrip/case-08e697e9ba45fe3e.request.json @@ -0,0 +1,28 @@ +{ + "cachedContent": "W02F.q!.GGcS.qm.,3..M!mn!8H? .9bSyHSf?UVER", + "contents": [ + { + "parts": [ + { + "codeExecutionResult": { + "outcome": "OUTCOME_OK", + "output": "32iaPm,psYi,u1JR" + }, + "fileData": { + "fileUri": "G?.ua?tx64Ri?,??z.3Zk5T?Y8????I,Pd79sa.?XX?, ," + }, + "functionCall": {}, + "mediaResolution": {}, + "partMetadata": {}, + "text": "GnQ,!2?,Zx2.4 Z.? ,H 0a0Qd?,8Z2M.3u.!.L.dm?pN", + "thought": false, + "videoMetadata": { + "endOffset": "MOKAT ,a,V,v,?ng.i KP.17ZK,,,k,i.o?kQ?,,4xWx,.p.3t", + "startOffset": "77?!d. ?!j " + } + } + ], + "role": "L,???b3M?!vZ!y,.3Q..FL0!.5 e.,?" + } + ] +} diff --git a/payloads/fuzz-snapshots/google-roundtrip/case-0bde07b217c86337.meta.json b/payloads/fuzz-snapshots/google-roundtrip/case-0bde07b217c86337.meta.json new file mode 100644 index 00000000..1face53b --- /dev/null +++ b/payloads/fuzz-snapshots/google-roundtrip/case-0bde07b217c86337.meta.json @@ -0,0 +1,24 @@ +{ + "issues": [ + "lost: cachedContent", + "lost: systemInstruction", + "lost: generationConfig.speechConfig", + "lost: generationConfig.responseMimeType", + "lost: generationConfig.seed", + "lost: generationConfig.responseModalities", + "lost: generationConfig.mediaResolution", + "lost: generationConfig.logprobs", + "lost: generationConfig.presencePenalty", + "lost: generationConfig.responseLogprobs", + "lost: generationConfig.responseJsonSchema", + "lost: generationConfig.candidateCount", + "lost: generationConfig.frequencyPenalty", + "lost: toolConfig.functionCallingConfig.allowedFunctionNames", + "added: generationConfig.responseSchema", + "changed: contents[0].parts.length (1 -> 0)", + "changed: contents[0].role (\"092,?JS.,02,N4 ..xQ3X..d6!p8u?l.aq73k,FK!tG?5I\" -> \"user\")", + "changed: toolConfig.functionCallingConfig.mode (\"VALIDATED\" -> \"AUTO\")" + ], + "kind": "request-roundtrip", + "provider": "google" +} diff --git a/payloads/fuzz-snapshots/google-roundtrip/case-0bde07b217c86337.request.json b/payloads/fuzz-snapshots/google-roundtrip/case-0bde07b217c86337.request.json new file mode 100644 index 00000000..12a39020 --- /dev/null +++ b/payloads/fuzz-snapshots/google-roundtrip/case-0bde07b217c86337.request.json @@ -0,0 +1,94 @@ +{ + "cachedContent": "1FfW7eP9!!!wV!c?4?758Z2i?8a1.B?8b,6X.M.?5?,", + "contents": [ + { + "parts": [ + { + "codeExecutionResult": { + "output": "ql.H,?Ewf973 g.I,XZ?bqw7,nZ? kGi !S?1ia?k" + }, + "executableCode": { + "code": ",A.. Q.J?TD", + "language": "LANGUAGE_UNSPECIFIED" + }, + "fileData": { + "mimeType": "?. 2?77o.3,krV00 am6,z" + }, + "mediaResolution": { + "level": "MEDIA_RESOLUTION_HIGH" + }, + "partMetadata": {}, + "thought": true, + "videoMetadata": {} + } + ], + "role": "092,?JS.,02,N4 ..xQ3X..d6!p8u?l.aq73k,FK!tG?5I" + } + ], + "generationConfig": { + "candidateCount": -720, + "frequencyPenalty": -2.548798129137723, + "logprobs": -608, + "mediaResolution": "MEDIA_RESOLUTION_HIGH", + "presencePenalty": -81.12346234355222, + "responseJsonSchema": " 4c09s BvUzow4y6G3UV7w1 qqs2", + "responseLogprobs": true, + "responseMimeType": "M,Q.,NU.,N", + "responseModalities": [ + "AUDIO", + "MODALITY_UNSPECIFIED", + "IMAGE" + ], + "seed": 562, + "speechConfig": { + "languageCode": "!VH,", + "multiSpeakerVoiceConfig": { + "speakerVoiceConfigs": [ + { + "voiceConfig": { + "prebuiltVoiceConfig": null + } + }, + { + "voiceConfig": {} + }, + { + "speaker": "N,?K0wu.R!o.,E hVT,!,O,38v 5" + } + ] + } + }, + "topP": 30.900514366320234 + }, + "model": "IerC,???XA,Iy7N1n4,..y! ?.KO ", + "systemInstruction": { + "parts": [ + { + "executableCode": { + "code": "e!CfUzN.i? .... 55r.Z3LQf41", + "language": "PYTHON" + }, + "functionCall": { + "args": {} + }, + "functionResponse": { + "id": ". .X", + "name": "oat09o.n3RQ4.E ?3ZEJp?t!E9,P ,wms", + "response": {} + }, + "mediaResolution": {}, + "thought": false, + "thoughtSignature": "L9p5" + } + ], + "role": "Z,?WUq?M,Dm9MY 2.4mA8,0 ,XD7M?,, 1.7fE6!! XNb9" + }, + "toolConfig": { + "functionCallingConfig": { + "allowedFunctionNames": [ + ",??0X.7x?MJ.8XD!,T27t?hQJ" + ], + "mode": "VALIDATED" + } + } +} diff --git a/payloads/fuzz-snapshots/google-roundtrip/case-117472f2bf6ae259.meta.json b/payloads/fuzz-snapshots/google-roundtrip/case-117472f2bf6ae259.meta.json new file mode 100644 index 00000000..c8d4ab17 --- /dev/null +++ b/payloads/fuzz-snapshots/google-roundtrip/case-117472f2bf6ae259.meta.json @@ -0,0 +1,11 @@ +{ + "issues": [ + "lost: safetySettings", + "lost: systemInstruction", + "lost: toolConfig", + "lost: cachedContent", + "changed: contents.length (3 -> 1)" + ], + "kind": "request-roundtrip", + "provider": "google" +} diff --git a/payloads/fuzz-snapshots/google-roundtrip/case-117472f2bf6ae259.request.json b/payloads/fuzz-snapshots/google-roundtrip/case-117472f2bf6ae259.request.json new file mode 100644 index 00000000..218004eb --- /dev/null +++ b/payloads/fuzz-snapshots/google-roundtrip/case-117472f2bf6ae259.request.json @@ -0,0 +1,157 @@ +{ + "cachedContent": "?j.JUM04?.k3rAjkY ,?R! 25J.?,8M3MkbN7?6, ,a.ZHe", + "contents": [ + { + "parts": [ + { + "codeExecutionResult": { + "outcome": "OUTCOME_UNSPECIFIED" + }, + "fileData": {}, + "functionResponse": { + "id": "Zi aFh?a6aG2H?.pMlo,P.m,Py .gj", + "parts": [ + { + "inlineData": null + } + ], + "scheduling": "INTERRUPT", + "willContinue": true + }, + "mediaResolution": { + "level": "MEDIA_RESOLUTION_HIGH" + }, + "partMetadata": {}, + "text": "e?T.3z0P,7a8 ,m8!.e.? ..,N.n,zaN2Ez!hoydi", + "thought": true + }, + { + "functionCall": { + "args": {}, + "id": ". .?2T jy!..yx.Sy1? .6P" + }, + "functionResponse": { + "id": "wC?0 ,ha jvdb!,.,n,9?E!.O fn.wOgAc,26", + "response": {}, + "willContinue": true + }, + "inlineData": {}, + "mediaResolution": { + "level": "MEDIA_RESOLUTION_UNSPECIFIED" + }, + "partMetadata": {}, + "text": "yu?9MLHSL", + "thought": true + } + ] + }, + { + "parts": [ + { + "executableCode": { + "code": "Wt, 1?OVQqtM0V2?,fd1IX?.o,?.144uE", + "language": "LANGUAGE_UNSPECIFIED" + }, + "fileData": { + "mimeType": "SrZB3J,.!" + }, + "functionCall": { + "args": {}, + "name": "DyN!?,,,.Li8?d,Al9u7M,,L5,xXm M" + }, + "functionResponse": { + "id": ",??,ec. j4WkMARA?8y,!.?gXTqo..3s?H jnR,.er8?g!,", + "name": ".L!.URw.,en!", + "response": {}, + "scheduling": "WHEN_IDLE" + }, + "inlineData": { + "data": "4.?7,Z?Z!R.,,3A .v2,rQ,?,!.lo.?!1i,Mm G2w eI0j0" + }, + "text": "?2.I!1QnE,q?9u.E.S.!W.T,4..26.!3?p.z..gO", + "thought": false, + "thoughtSignature": "OAl!w8R L?, e0 !?v.w? 6", + "videoMetadata": { + "endOffset": "?.?dimZDh.?6VgY.!D?7S.. Q.A3a.,?!n", + "fps": 31.80004873880053, + "startOffset": ".56.1I.,," + } + }, + { + "codeExecutionResult": { + "outcome": "OUTCOME_FAILED", + "output": "ov2." + }, + "executableCode": { + "language": "LANGUAGE_UNSPECIFIED" + }, + "fileData": { + "fileUri": ".8Kp oG.P.8?u Uc", + "mimeType": "?t?0.r?Y.." + }, + "inlineData": { + "mimeType": " Z .?l.,.V8UOK" + }, + "mediaResolution": { + "level": "MEDIA_RESOLUTION_LOW" + }, + "text": " nQ7UhFf9,cJ5dO3,8?2??1b", + "thought": true, + "videoMetadata": { + "fps": -87.21323262709994, + "startOffset": "5Ps.Q,s!??Y84.,bh??G??!! " + } + } + ] + }, + { + "parts": [ + { + "functionCall": { + "args": {} + }, + "inlineData": { + "data": "?ijMBl4H,,w..?.vW1 v?,Nx6mq?a?A?8,5E" + }, + "mediaResolution": {}, + "thoughtSignature": "AVJlL,u.?Y?PN Tsr", + "videoMetadata": { + "fps": 65.50903686895178, + "startOffset": "?Z?R,.Fu d. ," + } + } + ] + } + ], + "model": "D ", + "safetySettings": [ + {} + ], + "systemInstruction": { + "parts": [ + { + "codeExecutionResult": { + "outcome": "OUTCOME_UNSPECIFIED" + }, + "executableCode": { + "code": " 9.!?..t3VA.2?", + "language": "LANGUAGE_UNSPECIFIED" + }, + "functionResponse": { + "response": {}, + "willContinue": false + }, + "text": "z,f 7,IZlg, N!W,u?a f5,4U!.2!,d,.", + "thought": true, + "thoughtSignature": ",KheB??KQqCP,M1!.c.e.,2gzDKYJ.?,FBt,Pk8" + } + ] + }, + "toolConfig": { + "retrievalConfig": { + "latLng": { + "longitude": -49.83756195325235 + } + } + } +} diff --git a/payloads/fuzz-snapshots/google-roundtrip/case-138766d1b5d34371.meta.json b/payloads/fuzz-snapshots/google-roundtrip/case-138766d1b5d34371.meta.json new file mode 100644 index 00000000..57e2de28 --- /dev/null +++ b/payloads/fuzz-snapshots/google-roundtrip/case-138766d1b5d34371.meta.json @@ -0,0 +1,22 @@ +{ + "issues": [ + "lost: cachedContent", + "lost: generationConfig", + "lost: safetySettings", + "lost: contents[0].parts[0].codeExecutionResult", + "lost: contents[0].parts[0].executableCode", + "lost: contents[0].parts[0].thoughtSignature", + "lost: contents[0].parts[0].inlineData", + "lost: contents[0].parts[1].executableCode", + "lost: contents[0].parts[1].mediaResolution", + "lost: contents[0].parts[1].videoMetadata", + "lost: contents[0].parts[2].codeExecutionResult", + "lost: contents[0].parts[2].videoMetadata", + "lost: contents[0].parts[2].inlineData", + "lost: contents[0].parts[2].functionCall", + "changed: tools.length (1 -> 3)", + "changed: contents[0].role (\"7oF,.m6,L?OK,7?0JW\" -> \"user\")" + ], + "kind": "request-roundtrip", + "provider": "google" +} diff --git a/payloads/fuzz-snapshots/google-roundtrip/case-138766d1b5d34371.request.json b/payloads/fuzz-snapshots/google-roundtrip/case-138766d1b5d34371.request.json new file mode 100644 index 00000000..8fd35f37 --- /dev/null +++ b/payloads/fuzz-snapshots/google-roundtrip/case-138766d1b5d34371.request.json @@ -0,0 +1,69 @@ +{ + "cachedContent": " xQ.PP?", + "contents": [ + { + "parts": [ + { + "codeExecutionResult": {}, + "executableCode": {}, + "inlineData": { + "mimeType": ".e?9rOs40B!,F,.JEv,Q6j9!,,?Ad" + }, + "text": " nI?G m,sy?a.?JB..!,,8rnc4R7DmvKT,,K", + "thoughtSignature": "z1.Tf ,4fQ..,o.Tf4w?2 F9?!N 3" + }, + { + "executableCode": { + "code": "Ph.G Qw5n4r.8,Z5,.xWd!?.. o?z.a!?7B6 Aj" + }, + "mediaResolution": {}, + "text": "N.,n6a,94?II.?X?6bn.zE F R.?,c??X1 BKf2J0U 6,..", + "videoMetadata": { + "fps": -90.9113865558719 + } + }, + { + "codeExecutionResult": {}, + "functionCall": {}, + "inlineData": { + "data": "31A.,", + "mimeType": "2k.s05,Z,.e 81.?5r.bsC." + }, + "text": ".,7.??JU9,.,y?!F.Dr?0,Mb,Tyc,REg,.r ,hy!PR2FhB", + "videoMetadata": { + "endOffset": "? J!?w!w,.n ?jg.9?", + "startOffset": ",8C.MP!," + } + } + ], + "role": "7oF,.m6,L?OK,7?0JW" + } + ], + "generationConfig": { + "_responseJsonSchema": "NF ", + "candidateCount": 861, + "mediaResolution": "MEDIA_RESOLUTION_HIGH", + "presencePenalty": -24.74147361495701, + "responseJsonSchema": null, + "seed": 764 + }, + "model": ",!Ji,1X.,2R5I2ME!P r7?OuIO", + "safetySettings": [ + { + "threshold": "BLOCK_ONLY_HIGH" + }, + {} + ], + "tools": [ + { + "codeExecution": {}, + "googleSearch": {}, + "googleSearchRetrieval": { + "dynamicRetrievalConfig": { + "mode": "MODE_DYNAMIC" + } + }, + "urlContext": {} + } + ] +} diff --git a/payloads/fuzz-snapshots/google-roundtrip/case-375c9df694c3d0a3.meta.json b/payloads/fuzz-snapshots/google-roundtrip/case-375c9df694c3d0a3.meta.json new file mode 100644 index 00000000..ec42fc59 --- /dev/null +++ b/payloads/fuzz-snapshots/google-roundtrip/case-375c9df694c3d0a3.meta.json @@ -0,0 +1,24 @@ +{ + "issues": [ + "lost: toolConfig", + "lost: cachedContent", + "lost: generationConfig._responseJsonSchema", + "lost: generationConfig.responseMimeType", + "lost: generationConfig.mediaResolution", + "lost: generationConfig.logprobs", + "lost: generationConfig.speechConfig", + "lost: generationConfig.frequencyPenalty", + "lost: generationConfig.imageConfig", + "lost: generationConfig.responseJsonSchema", + "lost: generationConfig.responseModalities", + "lost: contents[0].parts[0].executableCode", + "lost: contents[0].parts[0].mediaResolution", + "lost: contents[0].parts[0].videoMetadata", + "lost: contents[0].parts[0].thoughtSignature", + "added: generationConfig.thinkingConfig.includeThoughts", + "changed: generationConfig.responseSchema ({\"default\":\"u U712X\",\"description\":\"1!?mw!9,T??NU.9E321?92.ySf 1UXL5.!,Lf.b?l87ZJ3V.D,\",\"enum\":[\"4N,?c?.??p?.EJ5vq..UOc4,,?s1H,ZHt.D3,? \",\".dROe b.53\",\"a D?1vG.vQ.,,Kk?Pq.A8.R,\"],\"format\":\",p..E , .Zv\",\"items\":{\"description\":\",j,.!,Ng!c.chY\",\"example\":null,\"format\":\"7i8KLWM!,r6.J !j?Q0.t???,N?.!?.!9?B,v6z6,?MN!Qt\",\"maxLength\":\" \",\"minItems\":\"?? ,?HzCp8,G?..? qr Fwj\",\"minProperties\":\",..sN.U O VN?LjP,,Z?? dhpK.d1v7.Z1,l\",\"nullable\":true,\"properties\":{},\"propertyOrdering\":[\"K !.?N,!y!!,29yTXWe?,,21 ?,.\",\"t 69\",\". z,t??2\"],\"type\":\"NULL\"},\"maxLength\":\"bpL!zlx Lbu?z,Wym.g3P!,. UE,RGT!c!64y7p?hOpTOh.\",\"maxProperties\":\"4 .,\",\"maximum\":19.61703789371374,\"minProperties\":\"!,Y.!s.?UW o,Q??!.f6!?o i\",\"required\":[\"?,? ?G,? nW.mg2e?02 R5M a6E0IP?!.,035M,,86?.\",\"PFq?L\",\".k,,?.BB6j6J\"],\"title\":\"0..QG7D,u.P?pJqM?f1?x?D,?xu0SKP,\",\"type\":\"ARRAY\"} -> null)", + "changed: contents[0].role (\"? ,gbww6.kgJ7Pi.6P7z.K?,J8!2?f.N.!0?\" -> \"user\")" + ], + "kind": "request-roundtrip", + "provider": "google" +} diff --git a/payloads/fuzz-snapshots/google-roundtrip/case-375c9df694c3d0a3.request.json b/payloads/fuzz-snapshots/google-roundtrip/case-375c9df694c3d0a3.request.json new file mode 100644 index 00000000..4976c63d --- /dev/null +++ b/payloads/fuzz-snapshots/google-roundtrip/case-375c9df694c3d0a3.request.json @@ -0,0 +1,95 @@ +{ + "cachedContent": "MIL?l v.,! Y??m?j?a.,.?vzX,c3K m.DGjy.!", + "contents": [ + { + "parts": [ + { + "executableCode": { + "language": "LANGUAGE_UNSPECIFIED" + }, + "inlineData": { + "data": "i?9O,2.,.H0rl7.,5.?Q,f?,n67aVy?dP", + "mimeType": "99bn9.?jx9?5Xu?F.6t.Q,q.V3.7ER." + }, + "mediaResolution": { + "level": "MEDIA_RESOLUTION_ULTRA_HIGH" + }, + "thoughtSignature": "?u!eIug..9C? soB0.8sTxo.q.k?65W??,p4 g.i3mX6 u7??", + "videoMetadata": { + "fps": 51.698216934714644 + } + } + ], + "role": "? ,gbww6.kgJ7Pi.6P7z.K?,J8!2?f.N.!0?" + } + ], + "generationConfig": { + "_responseJsonSchema": "Z aX5ge66 6D 59xXsGnF", + "frequencyPenalty": 2.6697296059110456, + "imageConfig": { + "imageSize": "BZw,!JpI?T?CHAOT4?w .8!?P MMuh ?2S,?." + }, + "logprobs": -41, + "maxOutputTokens": -889, + "mediaResolution": "MEDIA_RESOLUTION_UNSPECIFIED", + "responseJsonSchema": null, + "responseMimeType": "yH? .rM!6 .", + "responseModalities": [ + "AUDIO", + "AUDIO" + ], + "responseSchema": { + "default": "u U712X", + "description": "1!?mw!9,T??NU.9E321?92.ySf 1UXL5.!,Lf.b?l87ZJ3V.D,", + "enum": [ + "4N,?c?.??p?.EJ5vq..UOc4,,?s1H,ZHt.D3,? ", + ".dROe b.53", + "a D?1vG.vQ.,,Kk?Pq.A8.R," + ], + "format": ",p..E , .Zv", + "items": { + "description": ",j,.!,Ng!c.chY", + "example": null, + "format": "7i8KLWM!,r6.J !j?Q0.t???,N?.!?.!9?B,v6z6,?MN!Qt", + "maxLength": " ", + "minItems": "?? ,?HzCp8,G?..? qr Fwj", + "minProperties": ",..sN.U O VN?LjP,,Z?? dhpK.d1v7.Z1,l", + "nullable": true, + "properties": {}, + "propertyOrdering": [ + "K !.?N,!y!!,29yTXWe?,,21 ?,.", + "t 69", + ". z,t??2" + ], + "type": "NULL" + }, + "maxLength": "bpL!zlx Lbu?z,Wym.g3P!,. UE,RGT!c!64y7p?hOpTOh.", + "maxProperties": "4 .,", + "maximum": 19.61703789371374, + "minProperties": "!,Y.!s.?UW o,Q??!.f6!?o i", + "required": [ + "?,? ?G,? nW.mg2e?02 R5M a6E0IP?!.,035M,,86?.", + "PFq?L", + ".k,,?.BB6j6J" + ], + "title": "0..QG7D,u.P?pJqM?f1?x?D,?xu0SKP,", + "type": "ARRAY" + }, + "speechConfig": { + "languageCode": "9avd?RZ?9hs.,n?6?!HX?.", + "voiceConfig": { + "prebuiltVoiceConfig": {} + } + }, + "stopSequences": [ + ",9BK hokEj Y9zD?,k!,??KLXuP?7l,,5y" + ], + "thinkingConfig": { + "thinkingBudget": 763 + }, + "topK": 207, + "topP": -3.3063070170643147 + }, + "model": "4 .,,9I4xTh,1 w?,.3rYaJMR!?? Vq.H.!.kiKyp0,?!h?D9", + "toolConfig": {} +} diff --git a/payloads/fuzz-snapshots/google-roundtrip/case-562df898165dbfc6.meta.json b/payloads/fuzz-snapshots/google-roundtrip/case-562df898165dbfc6.meta.json new file mode 100644 index 00000000..18192f03 --- /dev/null +++ b/payloads/fuzz-snapshots/google-roundtrip/case-562df898165dbfc6.meta.json @@ -0,0 +1,16 @@ +{ + "issues": [ + "lost: cachedContent", + "lost: generationConfig.enableEnhancedCivicAnswers", + "lost: generationConfig.candidateCount", + "lost: generationConfig.imageConfig", + "lost: generationConfig.responseMimeType", + "lost: generationConfig.responseLogprobs", + "lost: generationConfig._responseJsonSchema", + "lost: generationConfig.seed", + "added: generationConfig.responseSchema", + "changed: contents.length (2 -> 1)" + ], + "kind": "request-roundtrip", + "provider": "google" +} diff --git a/payloads/fuzz-snapshots/google-roundtrip/case-562df898165dbfc6.request.json b/payloads/fuzz-snapshots/google-roundtrip/case-562df898165dbfc6.request.json new file mode 100644 index 00000000..3a002295 --- /dev/null +++ b/payloads/fuzz-snapshots/google-roundtrip/case-562df898165dbfc6.request.json @@ -0,0 +1,104 @@ +{ + "cachedContent": ".V?lwoQ4,,dQ! JHQ2!", + "contents": [ + { + "parts": [ + { + "fileData": { + "mimeType": "IX2NDH5Ni??.!!u4,U?9?,,,p20M8," + }, + "videoMetadata": { + "endOffset": ".5i7.gs oIc?y?U6rP6,.k!G2s.,??a4Fl?.N0", + "fps": -61.025434573605416 + } + }, + { + "executableCode": {}, + "inlineData": { + "data": "?N?t.7 ?,!" + }, + "mediaResolution": {}, + "partMetadata": {}, + "thoughtSignature": "x?xcwt??5kf.y.d8 .F!..2j5Nrst!c?.AeniH4?" + }, + { + "codeExecutionResult": { + "output": "I!" + }, + "executableCode": { + "code": ",i?BQbM3?.vs?HR?0.. z,!kT4m?5gLf,7! V?", + "language": "PYTHON" + }, + "functionResponse": { + "response": {}, + "scheduling": "SILENT", + "willContinue": true + }, + "inlineData": {}, + "mediaResolution": { + "level": "MEDIA_RESOLUTION_LOW" + }, + "partMetadata": {}, + "videoMetadata": { + "endOffset": "0mT!.G5y", + "fps": 17.274042364937063 + } + } + ], + "role": "F8H!sc6.D?d,?,.U,2f?,V" + }, + { + "parts": [ + { + "functionCall": { + "name": " O2?6!,m!!e,FC?Jn?85?..7eH.m7.cw" + }, + "inlineData": { + "mimeType": "sEH" + }, + "thought": true, + "videoMetadata": { + "startOffset": "..?J,I3.f?5?,r ?6!MK8 EWk,gvo Va?P,2!eFZV?02??" + } + }, + { + "codeExecutionResult": {}, + "functionResponse": { + "id": "pu.YT. .UF?.y.B", + "parts": [ + {} + ], + "response": {}, + "willContinue": true + }, + "mediaResolution": { + "level": "MEDIA_RESOLUTION_HIGH" + }, + "thought": false, + "videoMetadata": { + "fps": -53.962882073809226, + "startOffset": "D" + } + } + ], + "role": "3!qi?" + } + ], + "generationConfig": { + "_responseJsonSchema": "f ys b27NGe7Y WFq Y3v2yr", + "candidateCount": -879, + "enableEnhancedCivicAnswers": false, + "imageConfig": { + "aspectRatio": " t2Et8hEIB,.L ,. a", + "imageSize": ".WB? ,W.4,?.?f..0KXT D!iz5bE,1,.?!?!Fm!?y.!?wpod2" + }, + "maxOutputTokens": 69, + "responseLogprobs": false, + "responseMimeType": ",.6,0 5t.TF..!,,1!5a.F!,N2Yy?B9 ?7?,aUNZs?q.5G7b..", + "seed": 978, + "stopSequences": [ + ",,.mLx?,U?64dK7 HO?!RC .l3.7,o" + ], + "topK": -140 + } +} diff --git a/payloads/fuzz-snapshots/google-roundtrip/case-65163e2b3d0cab80.meta.json b/payloads/fuzz-snapshots/google-roundtrip/case-65163e2b3d0cab80.meta.json new file mode 100644 index 00000000..a399b89a --- /dev/null +++ b/payloads/fuzz-snapshots/google-roundtrip/case-65163e2b3d0cab80.meta.json @@ -0,0 +1,9 @@ +{ + "issues": [ + "lost: tools", + "changed: contents[0].role (\"a\" -> \"user\")", + "changed: contents[0].parts.length (1 -> 0)" + ], + "kind": "request-roundtrip", + "provider": "google" +} diff --git a/payloads/fuzz-snapshots/google-roundtrip/case-65163e2b3d0cab80.request.json b/payloads/fuzz-snapshots/google-roundtrip/case-65163e2b3d0cab80.request.json new file mode 100644 index 00000000..17c74e9e --- /dev/null +++ b/payloads/fuzz-snapshots/google-roundtrip/case-65163e2b3d0cab80.request.json @@ -0,0 +1,15 @@ +{ + "contents": [ + { + "parts": [ + { + "inlineData": {} + } + ], + "role": "a" + } + ], + "tools": [ + {} + ] +} diff --git a/payloads/fuzz-snapshots/google-roundtrip/case-97dcb376a3bad1c9.meta.json b/payloads/fuzz-snapshots/google-roundtrip/case-97dcb376a3bad1c9.meta.json new file mode 100644 index 00000000..8dd50d42 --- /dev/null +++ b/payloads/fuzz-snapshots/google-roundtrip/case-97dcb376a3bad1c9.meta.json @@ -0,0 +1,14 @@ +{ + "issues": [ + "lost: systemInstruction", + "lost: contents[0].parts[0].thought", + "lost: contents[0].parts[0].functionCall", + "lost: contents[0].parts[0].thoughtSignature", + "lost: contents[0].parts[0].videoMetadata", + "lost: contents[0].parts[0].fileData", + "added: contents[0].parts[0].inlineData", + "changed: contents[0].role (\"N,L770L K,H,?,.l4n ,\" -> \"user\")" + ], + "kind": "request-roundtrip", + "provider": "google" +} diff --git a/payloads/fuzz-snapshots/google-roundtrip/case-97dcb376a3bad1c9.request.json b/payloads/fuzz-snapshots/google-roundtrip/case-97dcb376a3bad1c9.request.json new file mode 100644 index 00000000..70069d2d --- /dev/null +++ b/payloads/fuzz-snapshots/google-roundtrip/case-97dcb376a3bad1c9.request.json @@ -0,0 +1,24 @@ +{ + "contents": [ + { + "parts": [ + { + "fileData": { + "fileUri": "f,T?W.46R?.?!i.?", + "mimeType": "!?SN.U?oo8.u,.!LJ!?,,??5nY? P?e,IN57N.,4Hw,9 " + }, + "functionCall": { + "args": {} + }, + "thought": true, + "thoughtSignature": ",?U!bl.87n?e.q34Um,? ", + "videoMetadata": { + "startOffset": "Hfb5..I4?6iQ.bN1.se23,,4,J5,7?, ." + } + } + ], + "role": "N,L770L K,H,?,.l4n ," + } + ], + "systemInstruction": {} +} diff --git a/payloads/fuzz-snapshots/google-roundtrip/case-a2d3e2321403c3b4.meta.json b/payloads/fuzz-snapshots/google-roundtrip/case-a2d3e2321403c3b4.meta.json new file mode 100644 index 00000000..dea236d7 --- /dev/null +++ b/payloads/fuzz-snapshots/google-roundtrip/case-a2d3e2321403c3b4.meta.json @@ -0,0 +1,24 @@ +{ + "issues": [ + "lost: toolConfig", + "lost: cachedContent", + "lost: contents[0].parts[0].thoughtSignature", + "lost: contents[0].parts[0].functionCall", + "lost: contents[0].parts[0].codeExecutionResult", + "lost: contents[0].parts[0].executableCode", + "lost: contents[0].parts[0].partMetadata", + "lost: generationConfig.logprobs", + "lost: generationConfig.presencePenalty", + "lost: generationConfig.imageConfig", + "lost: generationConfig._responseJsonSchema", + "lost: generationConfig.speechConfig", + "lost: generationConfig.responseModalities", + "lost: generationConfig.enableEnhancedCivicAnswers", + "added: generationConfig.responseSchema", + "added: generationConfig.thinkingConfig.thinkingBudget", + "changed: contents[0].role (\"!,B?.p,.J9H\" -> \"user\")", + "changed: generationConfig.thinkingConfig.includeThoughts (false -> true)" + ], + "kind": "request-roundtrip", + "provider": "google" +} diff --git a/payloads/fuzz-snapshots/google-roundtrip/case-a2d3e2321403c3b4.request.json b/payloads/fuzz-snapshots/google-roundtrip/case-a2d3e2321403c3b4.request.json new file mode 100644 index 00000000..73f6e249 --- /dev/null +++ b/payloads/fuzz-snapshots/google-roundtrip/case-a2d3e2321403c3b4.request.json @@ -0,0 +1,50 @@ +{ + "cachedContent": "..? !?3?!?U4,F!H7.B,!,?,t,?zP !F,.Z?,?XGJa", + "contents": [ + { + "parts": [ + { + "codeExecutionResult": { + "outcome": "OUTCOME_DEADLINE_EXCEEDED", + "output": "NSO?6?r,T !.8Y kfN.!" + }, + "executableCode": {}, + "functionCall": { + "args": {}, + "id": "b!V!qCx?Ov?Qm?9FKKY,t,?8", + "name": "?AX4??PUX.m.9ZJ.w.o,o7XVJJ.?y?.!Bt 4!.2.0,?,l ,2," + }, + "inlineData": { + "data": "ARCQ?3tWv!?Ss.aA!1XUZ!6G?,6e.2D??,D?!5TA", + "mimeType": "p.b,28!m?k,Zz20td.83n,.WiazGM?lB!,.I2GIy," + }, + "partMetadata": {}, + "thoughtSignature": "y.Rb?? .,??V?7...,O.!!?pXEh,b?.?,?Pg!?!!F91.c ,xd." + } + ], + "role": "!,B?.p,.J9H" + } + ], + "generationConfig": { + "_responseJsonSchema": "o8 2Wo hA3MC q 8v A0nfE", + "enableEnhancedCivicAnswers": false, + "imageConfig": {}, + "logprobs": 397, + "maxOutputTokens": 650, + "presencePenalty": -24.12705797021374, + "responseModalities": [ + "TEXT", + "MODALITY_UNSPECIFIED" + ], + "speechConfig": { + "voiceConfig": {} + }, + "temperature": -85.88575612520403, + "thinkingConfig": { + "includeThoughts": false + }, + "topP": -20.02183526273265 + }, + "model": "!PB!YL,lai...XDJ,x??e,L0.3?3..w?C905?", + "toolConfig": {} +} diff --git a/payloads/fuzz-snapshots/google-roundtrip/case-c18e28e85b79bba8.meta.json b/payloads/fuzz-snapshots/google-roundtrip/case-c18e28e85b79bba8.meta.json new file mode 100644 index 00000000..26b030af --- /dev/null +++ b/payloads/fuzz-snapshots/google-roundtrip/case-c18e28e85b79bba8.meta.json @@ -0,0 +1,14 @@ +{ + "issues": [ + "lost: toolConfig", + "lost: cachedContent", + "lost: systemInstruction", + "lost: tools[0].urlContext", + "lost: tools[0].fileSearch", + "lost: tools[0].computerUse", + "lost: tools[0].googleMaps", + "changed: contents.length (3 -> 1)" + ], + "kind": "request-roundtrip", + "provider": "google" +} diff --git a/payloads/fuzz-snapshots/google-roundtrip/case-c18e28e85b79bba8.request.json b/payloads/fuzz-snapshots/google-roundtrip/case-c18e28e85b79bba8.request.json new file mode 100644 index 00000000..f29d206e --- /dev/null +++ b/payloads/fuzz-snapshots/google-roundtrip/case-c18e28e85b79bba8.request.json @@ -0,0 +1,116 @@ +{ + "cachedContent": " , oQC,?,,q,, p5?.94nKlh?1X?,S,W", + "contents": [ + { + "parts": [ + { + "functionCall": { + "args": {} + }, + "functionResponse": { + "id": "XZo.5,Co. P", + "scheduling": "SCHEDULING_UNSPECIFIED" + }, + "mediaResolution": { + "level": "MEDIA_RESOLUTION_LOW" + }, + "partMetadata": {}, + "videoMetadata": { + "endOffset": "Z?!g!rE,53E,2Z?9YAmEOJK.Mx.31?V,4 !3 !Z," + } + } + ], + "role": ",B.?!8??,y!?" + }, + { + "parts": [ + { + "executableCode": { + "code": ",q3gj8!T,.Dg.?!k8d?C,F!J7?q?!Pwb.." + }, + "text": "!,? ? ! ", + "thoughtSignature": "O.Vla, !" + }, + { + "codeExecutionResult": {}, + "fileData": { + "fileUri": "Bbv..,!gr6c3.,,,,!6.p!Hkg.Cx11 ,.Fx" + }, + "inlineData": { + "data": ".4??,Kyu 4?S?c?.,9HXyV,,1F.RN.,8gx,,P?", + "mimeType": "?G?,.!?,?? Vg.s7Y,gb,hV?1.?Tz0Vg2" + }, + "mediaResolution": {}, + "partMetadata": {}, + "videoMetadata": { + "endOffset": "A.,!H5.3?TuvHw!lM" + } + }, + { + "inlineData": {}, + "mediaResolution": {}, + "partMetadata": {}, + "text": "8U?,Xlzb?.,I!6R?.Q9Zi???Uf0E", + "thoughtSignature": "?b7?hP,7?Zau F!.Pxw.b", + "videoMetadata": { + "startOffset": "!?B..S ,e.,..?.ox50o.?4.,.?Tf!Ig A" + } + } + ], + "role": ",,,!" + }, + { + "parts": [ + { + "codeExecutionResult": { + "outcome": "OUTCOME_UNSPECIFIED", + "output": "F?.?D2" + }, + "partMetadata": {}, + "text": ",,w?,aq2pq", + "thought": true, + "thoughtSignature": "5?0doC" + } + ], + "role": "?!?z..FPc.6 !??en?1!T?.,sq9zWp" + } + ], + "systemInstruction": { + "parts": [ + { + "codeExecutionResult": { + "output": "o?oKV,X.gyZ?.?C?7..rJG Ci?o?,QIO9N51M8,IjEF" + }, + "fileData": { + "mimeType": "K1.A .?w8!" + }, + "functionResponse": { + "parts": [ + {} + ], + "scheduling": "WHEN_IDLE", + "willContinue": true + }, + "partMetadata": {}, + "text": ".,. ,??68F?wZtr WCfmVZ.!.,F0z,!zA.v" + } + ] + }, + "toolConfig": {}, + "tools": [ + { + "computerUse": { + "environment": "ENVIRONMENT_BROWSER" + }, + "fileSearch": { + "metadataFilter": ",4W L.ApfF?a,3m! ? ?fb. 4SIgm8", + "topK": 382 + }, + "googleMaps": { + "enableWidget": false + }, + "googleSearch": {}, + "urlContext": {} + } + ] +} diff --git a/scripts/validate_google_payload.py b/scripts/validate_google_payload.py new file mode 100755 index 00000000..44ac4e5a --- /dev/null +++ b/scripts/validate_google_payload.py @@ -0,0 +1,116 @@ +#!/usr/bin/env python3 +"""Send a Google Gemini payload to the real API and report whether it's accepted. + +Usage: + # Validate the minimal fuzz case: + python scripts/validate_google_payload.py '{"contents": [{}]}' + + # Validate from a snapshot file: + python scripts/validate_google_payload.py payloads/fuzz-snapshots/google-roundtrip/case-XYZ.request.json + + # Validate all snapshots in a directory: + python scripts/validate_google_payload.py payloads/fuzz-snapshots/google-roundtrip/ + +Requires GEMINI_API_KEY (or GOOGLE_API_KEY) in the environment. +""" + +import json +import os +import sys +from pathlib import Path +from urllib.error import HTTPError +from urllib.request import Request, urlopen + +MODEL = os.environ.get("GEMINI_MODEL", "gemini-2.0-flash") +API_KEY = os.environ.get("GEMINI_API_KEY") or os.environ.get("GOOGLE_API_KEY") +BASE_URL = "https://generativelanguage.googleapis.com/v1beta" + + +def validate_payload(payload: dict, label: str = "") -> bool: + url = f"{BASE_URL}/models/{MODEL}:generateContent?key={API_KEY}" + data = json.dumps(payload).encode() + req = Request(url, data=data, headers={"Content-Type": "application/json"}) + + prefix = f"[{label}] " if label else "" + try: + resp = urlopen(req) + body = json.loads(resp.read()) + print(f"{prefix}ACCEPTED (200) -- API processed the payload") + if "candidates" in body: + text = ( + body["candidates"][0] + .get("content", {}) + .get("parts", [{}])[0] + .get("text", "")[:80] + ) + if text: + print(f" response: {text!r}") + return True + except HTTPError as e: + body = e.read().decode() + try: + err = json.loads(body) + msg = err.get("error", {}).get("message", body[:200]) + status = err.get("error", {}).get("status", e.code) + except json.JSONDecodeError: + msg = body[:200] + status = e.code + print(f"{prefix}REJECTED ({status}) -- {msg}") + return False + + +def load_payload(arg: str) -> list[tuple[str, dict]]: + """Return list of (label, payload) from a JSON string, file, or directory.""" + path = Path(arg) + + # Directory: load all .request.json files + if path.is_dir(): + results = [] + for f in sorted(path.glob("*.request.json")): + with open(f) as fh: + results.append((f.name, json.load(fh))) + return results + + # File + if path.is_file(): + with open(path) as fh: + return [(path.name, json.load(fh))] + + # Inline JSON string + try: + return [("inline", json.loads(arg))] + except json.JSONDecodeError: + print(f"Error: not a valid JSON string, file, or directory: {arg}", file=sys.stderr) + sys.exit(1) + + +def main(): + if not API_KEY: + print("Set GEMINI_API_KEY or GOOGLE_API_KEY environment variable.", file=sys.stderr) + sys.exit(1) + + if len(sys.argv) < 2: + print(__doc__.strip()) + sys.exit(1) + + payloads = load_payload(sys.argv[1]) + if not payloads: + print("No payloads found.", file=sys.stderr) + sys.exit(1) + + accepted = 0 + rejected = 0 + for label, payload in payloads: + if validate_payload(payload, label): + accepted += 1 + else: + rejected += 1 + + if len(payloads) > 1: + print(f"\n--- {accepted} accepted, {rejected} rejected (of {len(payloads)}) ---") + + sys.exit(0 if rejected == 0 else 1) + + +if __name__ == "__main__": + main() From dcafcb6cfbdc11dfb9251299f694af6c9d5069aa Mon Sep 17 00:00:00 2001 From: Matt Perpick Date: Wed, 18 Feb 2026 16:47:30 -0600 Subject: [PATCH 2/4] Fix Google adapter roundtrip: preserve seed, penalties, and provider-specific extras Map seed, presencePenalty, frequencyPenalty from generationConfig to universal params directly. Store safetySettings, cachedContent, and remaining generationConfig fields (candidateCount, speechConfig, responseModalities, etc.) in provider extras so they survive roundtrips. Reconstruct them in request_from_universal. Co-Authored-By: Claude Opus 4.6 --- crates/lingua/src/providers/google/adapter.rs | 279 +++++++++++++++--- .../case-0005d480caf84338.meta.json | 6 +- .../case-007bd4500a682a93.meta.json | 4 +- .../case-011c75aef5297a6e.meta.json | 11 - .../case-011c75aef5297a6e.request.json | 52 ---- .../case-066263d9b371b047.meta.json | 3 +- .../case-08e697e9ba45fe3e.meta.json | 9 +- .../case-0bde07b217c86337.meta.json | 18 +- .../case-117472f2bf6ae259.meta.json | 4 +- .../case-138766d1b5d34371.meta.json | 21 +- .../case-375c9df694c3d0a3.meta.json | 8 - .../case-562df898165dbfc6.meta.json | 16 - .../case-562df898165dbfc6.request.json | 104 ------- .../case-65163e2b3d0cab80.meta.json | 4 +- .../case-97dcb376a3bad1c9.meta.json | 6 +- .../case-a2d3e2321403c3b4.meta.json | 12 +- .../case-c18e28e85b79bba8.meta.json | 3 +- 17 files changed, 263 insertions(+), 297 deletions(-) delete mode 100644 payloads/fuzz-snapshots/google-roundtrip/case-011c75aef5297a6e.meta.json delete mode 100644 payloads/fuzz-snapshots/google-roundtrip/case-011c75aef5297a6e.request.json delete mode 100644 payloads/fuzz-snapshots/google-roundtrip/case-562df898165dbfc6.meta.json delete mode 100644 payloads/fuzz-snapshots/google-roundtrip/case-562df898165dbfc6.request.json diff --git a/crates/lingua/src/providers/google/adapter.rs b/crates/lingua/src/providers/google/adapter.rs index 6133d954..82e1e720 100644 --- a/crates/lingua/src/providers/google/adapter.rs +++ b/crates/lingua/src/providers/google/adapter.rs @@ -67,37 +67,49 @@ impl ProviderAdapter for GoogleAdapter { .map_err(|e| TransformError::ToUniversalFailed(e.to_string()))?; // Extract params from generationConfig (now typed in params struct) - let (temperature, top_p, top_k, max_tokens, stop, reasoning) = - if let Some(config) = &typed_params.generation_config { - let max_tokens = config.max_output_tokens; - // Convert Google's thinkingConfig to ReasoningConfig - // thinkingBudget: 0 means disabled - let reasoning = config.thinking_config.as_ref().map(|tc| { - let is_disabled = tc.thinking_budget == Some(0); - let budget_tokens = tc.thinking_budget; - // Derive effort from budget_tokens - let effort = budget_tokens - .map(|b| crate::universal::reasoning::budget_to_effort(b, None)); - crate::universal::ReasoningConfig { - enabled: Some(!is_disabled), - effort, - budget_tokens, - canonical: Some(crate::universal::ReasoningCanonical::BudgetTokens), - ..Default::default() - } - }); - let stop = config.stop_sequences.clone().filter(|s| !s.is_empty()); - ( - config.temperature, - config.top_p, - config.top_k, - max_tokens, - stop, - reasoning, - ) - } else { - (None, None, None, None, None, None) - }; + let ( + temperature, + top_p, + top_k, + max_tokens, + stop, + reasoning, + seed, + presence_penalty, + frequency_penalty, + ) = if let Some(config) = &typed_params.generation_config { + let max_tokens = config.max_output_tokens; + // Convert Google's thinkingConfig to ReasoningConfig + // thinkingBudget: 0 means disabled + let reasoning = config.thinking_config.as_ref().map(|tc| { + let is_disabled = tc.thinking_budget == Some(0); + let budget_tokens = tc.thinking_budget; + // Derive effort from budget_tokens + let effort = + budget_tokens.map(|b| crate::universal::reasoning::budget_to_effort(b, None)); + crate::universal::ReasoningConfig { + enabled: Some(!is_disabled), + effort, + budget_tokens, + canonical: Some(crate::universal::ReasoningCanonical::BudgetTokens), + ..Default::default() + } + }); + let stop = config.stop_sequences.clone().filter(|s| !s.is_empty()); + ( + config.temperature, + config.top_p, + config.top_k, + max_tokens, + stop, + reasoning, + config.seed, + config.presence_penalty, + config.frequency_penalty, + ) + } else { + (None, None, None, None, None, None, None, None, None) + }; // Convert tools using typed conversions let tools = typed_params @@ -127,9 +139,9 @@ impl ProviderAdapter for GoogleAdapter { tools, tool_choice, response_format, - seed: None, // Google doesn't support seed - presence_penalty: None, - frequency_penalty: None, + seed, + presence_penalty, + frequency_penalty, stream: None, // Google uses endpoint-based streaming // New canonical fields - Google doesn't support most of these parallel_tool_calls: None, @@ -142,12 +154,48 @@ impl ProviderAdapter for GoogleAdapter { extras: Default::default(), }; - // Use extras captured automatically via #[serde(flatten)] - if !typed_params.extras.is_empty() { - params.extras.insert( - ProviderFormat::Google, - typed_params.extras.into_iter().collect(), - ); + // Collect Google-specific extras: serde-flatten unknowns + known fields + // that don't map to universal params. + let mut google_extras: Map = typed_params.extras.into_iter().collect(); + + if let Some(v) = typed_params.safety_settings { + google_extras.insert("safetySettings".into(), v); + } + if let Some(v) = typed_params.cached_content { + google_extras.insert("cachedContent".into(), Value::String(v)); + } + + // Preserve generationConfig fields that don't have universal equivalents. + // Serialize the whole config, strip fields we already handle, keep the rest. + if let Some(config) = &typed_params.generation_config { + if let Ok(Value::Object(mut config_map)) = serde_json::to_value(config) { + // Remove fields handled canonically above + for key in &[ + "temperature", + "topP", + "topK", + "maxOutputTokens", + "stopSequences", + "thinkingConfig", + "responseMimeType", + "responseSchema", + "seed", + "presencePenalty", + "frequencyPenalty", + ] { + config_map.remove(*key); + } + // Remove null entries + config_map.retain(|_, v| !v.is_null()); + if !config_map.is_empty() { + google_extras + .insert("_generationConfigExtras".into(), Value::Object(config_map)); + } + } + } + + if !google_extras.is_empty() { + params.extras.insert(ProviderFormat::Google, google_extras); } Ok(UniversalRequest { @@ -220,13 +268,23 @@ impl ProviderAdapter for GoogleAdapter { .map(|r| !r.is_effectively_disabled()) .unwrap_or(false); let has_response_format = req.params.response_format.is_some(); + let has_gen_config_extras = req + .params + .extras + .get(&ProviderFormat::Google) + .and_then(|e| e.get("_generationConfigExtras")) + .is_some(); let has_params = req.params.temperature.is_some() || req.params.top_p.is_some() || req.params.top_k.is_some() || req.params.output_token_budget().is_some() || req.params.stop.is_some() + || req.params.seed.is_some() + || req.params.presence_penalty.is_some() + || req.params.frequency_penalty.is_some() || has_reasoning - || has_response_format; + || has_response_format + || has_gen_config_extras; if has_params { // Convert ReasoningConfig to Google's thinkingConfig @@ -254,6 +312,9 @@ impl ProviderAdapter for GoogleAdapter { max_output_tokens: req.params.output_token_budget(), stop_sequences, thinking_config, + seed: req.params.seed, + presence_penalty: req.params.presence_penalty, + frequency_penalty: req.params.frequency_penalty, ..Default::default() }; @@ -262,11 +323,23 @@ impl ProviderAdapter for GoogleAdapter { apply_response_format_to_generation_config(&mut config, format); } - obj.insert( - "generationConfig".into(), - serde_json::to_value(config) - .map_err(|e| TransformError::SerializationFailed(e.to_string()))?, - ); + let mut config_value = serde_json::to_value(config) + .map_err(|e| TransformError::SerializationFailed(e.to_string()))?; + + // Merge back generationConfig extras (candidateCount, speechConfig, etc.) + if let Some(extras) = req.params.extras.get(&ProviderFormat::Google) { + if let Some(Value::Object(config_extras)) = extras.get("_generationConfigExtras") { + if let Some(config_map) = config_value.as_object_mut() { + for (k, v) in config_extras { + if !config_map.contains_key(k) { + config_map.insert(k.clone(), v.clone()); + } + } + } + } + } + + obj.insert("generationConfig".into(), config_value); } // Add tools if present @@ -296,6 +369,10 @@ impl ProviderAdapter for GoogleAdapter { // Merge back provider-specific extras (only for Google) if let Some(extras) = req.params.extras.get(&ProviderFormat::Google) { for (k, v) in extras { + // _generationConfigExtras is merged into generationConfig above + if k == "_generationConfigExtras" { + continue; + } // Don't overwrite canonical fields we already handled if !obj.contains_key(k) { obj.insert(k.clone(), v.clone()); @@ -577,10 +654,114 @@ mod tests { }); let universal = adapter.request_to_universal(payload).unwrap(); - // safetySettings is a known key, so it won't be in extras - // but it should be preserved through serialization - let reconstructed = adapter.request_from_universal(&universal).unwrap(); assert!(reconstructed.get("contents").is_some()); + // safetySettings should survive the roundtrip + assert_eq!( + reconstructed.get("safetySettings"), + Some(&json!([{"category": "HARM_CATEGORY_HARASSMENT", "threshold": "BLOCK_NONE"}])) + ); + } + + #[test] + fn test_google_roundtrip_generation_config_extras() { + let adapter = GoogleAdapter; + let payload = json!({ + "contents": [{"role": "user", "parts": [{"text": "hi"}]}], + "generationConfig": { + "temperature": 0.5, + "seed": 42, + "presencePenalty": 0.3, + "frequencyPenalty": 0.7, + "candidateCount": 2, + "responseLogprobs": true, + "responseModalities": ["TEXT"], + "mediaResolution": "MEDIA_RESOLUTION_LOW" + } + }); + + let universal = adapter.request_to_universal(payload).unwrap(); + assert_eq!(universal.params.seed, Some(42)); + assert!((universal.params.presence_penalty.unwrap() - 0.3).abs() < 0.001); + assert!((universal.params.frequency_penalty.unwrap() - 0.7).abs() < 0.001); + + let reconstructed = adapter.request_from_universal(&universal).unwrap(); + let config = reconstructed.get("generationConfig").unwrap(); + assert_eq!(config.get("seed"), Some(&json!(42))); + assert_eq!(config.get("candidateCount"), Some(&json!(2))); + assert_eq!(config.get("responseLogprobs"), Some(&json!(true))); + assert_eq!(config.get("responseModalities"), Some(&json!(["TEXT"]))); + assert_eq!( + config.get("mediaResolution"), + Some(&json!("MEDIA_RESOLUTION_LOW")) + ); + } + + #[test] + fn test_google_roundtrip_cached_content() { + let adapter = GoogleAdapter; + let payload = json!({ + "contents": [{"role": "user", "parts": [{"text": "hi"}]}], + "cachedContent": "cachedContents/abc123" + }); + + let universal = adapter.request_to_universal(payload).unwrap(); + let reconstructed = adapter.request_from_universal(&universal).unwrap(); + assert_eq!( + reconstructed.get("cachedContent"), + Some(&json!("cachedContents/abc123")) + ); + } + + #[test] + fn test_google_openai_google_roundtrip_seed_and_penalties() { + use crate::processing::adapters::adapter_for_format; + + let google = adapter_for_format(ProviderFormat::Google).unwrap(); + let openai = adapter_for_format(ProviderFormat::ChatCompletions).unwrap(); + + let google_payload = json!({ + "model": "gemini-2.0-flash", + "contents": [{"role": "user", "parts": [{"text": "hello"}]}], + "generationConfig": { + "seed": 42, + "presencePenalty": 0.5, + "frequencyPenalty": 0.8, + "candidateCount": 2, + "responseModalities": ["TEXT"], + "temperature": 0.7 + }, + "safetySettings": [{"category": "HARM_CATEGORY_HATE_SPEECH", "threshold": "BLOCK_ONLY_HIGH"}], + "cachedContent": "cachedContents/test123" + }); + + // Google -> Universal + let universal = google.request_to_universal(google_payload.clone()).unwrap(); + assert_eq!(universal.params.seed, Some(42)); + assert!((universal.params.presence_penalty.unwrap() - 0.5).abs() < 0.001); + assert!((universal.params.frequency_penalty.unwrap() - 0.8).abs() < 0.001); + + // Universal -> OpenAI ChatCompletions + let openai_payload = openai.request_from_universal(&universal).unwrap(); + // seed, presence_penalty, frequency_penalty should be in OpenAI format + assert_eq!(openai_payload.get("seed"), Some(&json!(42))); + + // OpenAI -> Universal (back) + let universal_2 = openai.request_to_universal(openai_payload).unwrap(); + assert_eq!(universal_2.params.seed, Some(42)); + + // Universal -> Google (back) + let google_out = google.request_from_universal(&universal_2).unwrap(); + let config = google_out.get("generationConfig").unwrap(); + // Universal params survive cross-provider roundtrip + assert_eq!(config.get("seed"), Some(&json!(42))); + assert!(config.get("presencePenalty").is_some()); + assert!(config.get("frequencyPenalty").is_some()); + assert!(config.get("temperature").is_some()); + + // Google-specific extras (candidateCount, responseModalities, safetySettings, + // cachedContent) are stored under ProviderFormat::Google in extras, so they + // survive a Google->Google roundtrip but NOT a cross-provider trip through OpenAI. + // This is expected: OpenAI doesn't know about Google's candidateCount etc. } } diff --git a/payloads/fuzz-snapshots/google-roundtrip/case-0005d480caf84338.meta.json b/payloads/fuzz-snapshots/google-roundtrip/case-0005d480caf84338.meta.json index 64f19efb..033e4af0 100644 --- a/payloads/fuzz-snapshots/google-roundtrip/case-0005d480caf84338.meta.json +++ b/payloads/fuzz-snapshots/google-roundtrip/case-0005d480caf84338.meta.json @@ -1,13 +1,13 @@ { "issues": [ - "lost: toolConfig", "lost: systemInstruction", - "lost: contents[0].parts[0].thought", - "lost: contents[0].parts[0].functionCall", + "lost: toolConfig", "lost: contents[0].parts[0].thoughtSignature", "lost: contents[0].parts[0].videoMetadata", "lost: contents[0].parts[0].partMetadata", "lost: contents[0].parts[0].functionResponse", + "lost: contents[0].parts[0].thought", + "lost: contents[0].parts[0].functionCall", "added: contents[0].role" ], "kind": "request-roundtrip", diff --git a/payloads/fuzz-snapshots/google-roundtrip/case-007bd4500a682a93.meta.json b/payloads/fuzz-snapshots/google-roundtrip/case-007bd4500a682a93.meta.json index 4b7c3afd..6432821c 100644 --- a/payloads/fuzz-snapshots/google-roundtrip/case-007bd4500a682a93.meta.json +++ b/payloads/fuzz-snapshots/google-roundtrip/case-007bd4500a682a93.meta.json @@ -1,8 +1,8 @@ { "issues": [ - "changed: tools.length (1 -> 2)", + "changed: contents[0].role (\"a\" -> \"user\")", "changed: contents[0].parts.length (1 -> 0)", - "changed: contents[0].role (\"a\" -> \"user\")" + "changed: tools.length (1 -> 2)" ], "kind": "request-roundtrip", "provider": "google" diff --git a/payloads/fuzz-snapshots/google-roundtrip/case-011c75aef5297a6e.meta.json b/payloads/fuzz-snapshots/google-roundtrip/case-011c75aef5297a6e.meta.json deleted file mode 100644 index 052be157..00000000 --- a/payloads/fuzz-snapshots/google-roundtrip/case-011c75aef5297a6e.meta.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "issues": [ - "lost: toolConfig", - "lost: safetySettings", - "changed: tools.length (1 -> 2)", - "changed: contents[0].role (\"n7R?!!.8!hC4l85,Qwt5o UJX?K,.812 !ULY!!!Y?5k4yLf\" -> \"user\")", - "changed: contents[0].parts.length (1 -> 0)" - ], - "kind": "request-roundtrip", - "provider": "google" -} diff --git a/payloads/fuzz-snapshots/google-roundtrip/case-011c75aef5297a6e.request.json b/payloads/fuzz-snapshots/google-roundtrip/case-011c75aef5297a6e.request.json deleted file mode 100644 index 66416488..00000000 --- a/payloads/fuzz-snapshots/google-roundtrip/case-011c75aef5297a6e.request.json +++ /dev/null @@ -1,52 +0,0 @@ -{ - "contents": [ - { - "parts": [ - { - "fileData": { - "fileUri": "!A5Hc!20t" - }, - "functionCall": { - "args": {}, - "id": "!R ,gX56,,?29?? C8w?,4.!z??q44x1LXALQtl" - }, - "inlineData": {}, - "partMetadata": {}, - "thought": false, - "thoughtSignature": "Jn 0L,q3J", - "videoMetadata": { - "fps": 92.51682992764292 - } - } - ], - "role": "n7R?!!.8!hC4l85,Qwt5o UJX?K,.812 !ULY!!!Y?5k4yLf" - } - ], - "model": "iw.?1?,.g3! .?,5?2,,.hB.1?.J,9?,,?32e.Hi1.FAY", - "safetySettings": [ - { - "category": "HARM_CATEGORY_HARASSMENT", - "threshold": "HARM_BLOCK_THRESHOLD_UNSPECIFIED" - }, - { - "category": "HARM_CATEGORY_DEROGATORY" - } - ], - "toolConfig": {}, - "tools": [ - { - "computerUse": { - "environment": "ENVIRONMENT_UNSPECIFIED", - "excludedPredefinedFunctions": [ - "e4 .?PO.?.!c?.bi.?,p" - ] - }, - "googleSearch": { - "timeRangeFilter": { - "startTime": "QDBDMGS.?lxG,,?.X?9?j.k?TA.!x.R,?9," - } - }, - "googleSearchRetrieval": {} - } - ] -} diff --git a/payloads/fuzz-snapshots/google-roundtrip/case-066263d9b371b047.meta.json b/payloads/fuzz-snapshots/google-roundtrip/case-066263d9b371b047.meta.json index 85aa7661..1ecb131c 100644 --- a/payloads/fuzz-snapshots/google-roundtrip/case-066263d9b371b047.meta.json +++ b/payloads/fuzz-snapshots/google-roundtrip/case-066263d9b371b047.meta.json @@ -1,8 +1,7 @@ { "issues": [ - "lost: safetySettings", - "lost: systemInstruction", "lost: toolConfig", + "lost: systemInstruction", "lost: contents[0].parts[0].videoMetadata", "lost: contents[0].parts[0].mediaResolution", "lost: contents[0].parts[0].thoughtSignature", diff --git a/payloads/fuzz-snapshots/google-roundtrip/case-08e697e9ba45fe3e.meta.json b/payloads/fuzz-snapshots/google-roundtrip/case-08e697e9ba45fe3e.meta.json index 10a25eea..b6e22213 100644 --- a/payloads/fuzz-snapshots/google-roundtrip/case-08e697e9ba45fe3e.meta.json +++ b/payloads/fuzz-snapshots/google-roundtrip/case-08e697e9ba45fe3e.meta.json @@ -1,13 +1,12 @@ { "issues": [ - "lost: cachedContent", - "lost: contents[0].parts[0].functionCall", - "lost: contents[0].parts[0].mediaResolution", - "lost: contents[0].parts[0].fileData", - "lost: contents[0].parts[0].thought", "lost: contents[0].parts[0].partMetadata", + "lost: contents[0].parts[0].thought", + "lost: contents[0].parts[0].mediaResolution", "lost: contents[0].parts[0].videoMetadata", "lost: contents[0].parts[0].codeExecutionResult", + "lost: contents[0].parts[0].functionCall", + "lost: contents[0].parts[0].fileData", "changed: contents[0].role (\"L,???b3M?!vZ!y,.3Q..FL0!.5 e.,?\" -> \"user\")" ], "kind": "request-roundtrip", diff --git a/payloads/fuzz-snapshots/google-roundtrip/case-0bde07b217c86337.meta.json b/payloads/fuzz-snapshots/google-roundtrip/case-0bde07b217c86337.meta.json index 1face53b..0c95c2ae 100644 --- a/payloads/fuzz-snapshots/google-roundtrip/case-0bde07b217c86337.meta.json +++ b/payloads/fuzz-snapshots/google-roundtrip/case-0bde07b217c86337.meta.json @@ -1,23 +1,13 @@ { "issues": [ - "lost: cachedContent", "lost: systemInstruction", - "lost: generationConfig.speechConfig", - "lost: generationConfig.responseMimeType", - "lost: generationConfig.seed", - "lost: generationConfig.responseModalities", - "lost: generationConfig.mediaResolution", - "lost: generationConfig.logprobs", - "lost: generationConfig.presencePenalty", - "lost: generationConfig.responseLogprobs", - "lost: generationConfig.responseJsonSchema", - "lost: generationConfig.candidateCount", - "lost: generationConfig.frequencyPenalty", "lost: toolConfig.functionCallingConfig.allowedFunctionNames", + "lost: generationConfig.responseMimeType", + "lost: generationConfig.speechConfig.multiSpeakerVoiceConfig.speakerVoiceConfigs[0].voiceConfig.prebuiltVoiceConfig", "added: generationConfig.responseSchema", + "changed: toolConfig.functionCallingConfig.mode (\"VALIDATED\" -> \"AUTO\")", "changed: contents[0].parts.length (1 -> 0)", - "changed: contents[0].role (\"092,?JS.,02,N4 ..xQ3X..d6!p8u?l.aq73k,FK!tG?5I\" -> \"user\")", - "changed: toolConfig.functionCallingConfig.mode (\"VALIDATED\" -> \"AUTO\")" + "changed: contents[0].role (\"092,?JS.,02,N4 ..xQ3X..d6!p8u?l.aq73k,FK!tG?5I\" -> \"user\")" ], "kind": "request-roundtrip", "provider": "google" diff --git a/payloads/fuzz-snapshots/google-roundtrip/case-117472f2bf6ae259.meta.json b/payloads/fuzz-snapshots/google-roundtrip/case-117472f2bf6ae259.meta.json index c8d4ab17..2da9bb91 100644 --- a/payloads/fuzz-snapshots/google-roundtrip/case-117472f2bf6ae259.meta.json +++ b/payloads/fuzz-snapshots/google-roundtrip/case-117472f2bf6ae259.meta.json @@ -1,9 +1,7 @@ { "issues": [ - "lost: safetySettings", - "lost: systemInstruction", "lost: toolConfig", - "lost: cachedContent", + "lost: systemInstruction", "changed: contents.length (3 -> 1)" ], "kind": "request-roundtrip", diff --git a/payloads/fuzz-snapshots/google-roundtrip/case-138766d1b5d34371.meta.json b/payloads/fuzz-snapshots/google-roundtrip/case-138766d1b5d34371.meta.json index 57e2de28..c62ede6e 100644 --- a/payloads/fuzz-snapshots/google-roundtrip/case-138766d1b5d34371.meta.json +++ b/payloads/fuzz-snapshots/google-roundtrip/case-138766d1b5d34371.meta.json @@ -1,21 +1,20 @@ { "issues": [ - "lost: cachedContent", - "lost: generationConfig", - "lost: safetySettings", + "lost: contents[0].parts[0].inlineData", "lost: contents[0].parts[0].codeExecutionResult", - "lost: contents[0].parts[0].executableCode", "lost: contents[0].parts[0].thoughtSignature", - "lost: contents[0].parts[0].inlineData", - "lost: contents[0].parts[1].executableCode", - "lost: contents[0].parts[1].mediaResolution", + "lost: contents[0].parts[0].executableCode", "lost: contents[0].parts[1].videoMetadata", + "lost: contents[0].parts[1].mediaResolution", + "lost: contents[0].parts[1].executableCode", "lost: contents[0].parts[2].codeExecutionResult", - "lost: contents[0].parts[2].videoMetadata", - "lost: contents[0].parts[2].inlineData", "lost: contents[0].parts[2].functionCall", - "changed: tools.length (1 -> 3)", - "changed: contents[0].role (\"7oF,.m6,L?OK,7?0JW\" -> \"user\")" + "lost: contents[0].parts[2].inlineData", + "lost: contents[0].parts[2].videoMetadata", + "lost: generationConfig.responseJsonSchema", + "added: generationConfig.responseSchema", + "changed: contents[0].role (\"7oF,.m6,L?OK,7?0JW\" -> \"user\")", + "changed: tools.length (1 -> 3)" ], "kind": "request-roundtrip", "provider": "google" diff --git a/payloads/fuzz-snapshots/google-roundtrip/case-375c9df694c3d0a3.meta.json b/payloads/fuzz-snapshots/google-roundtrip/case-375c9df694c3d0a3.meta.json index ec42fc59..25df2d8a 100644 --- a/payloads/fuzz-snapshots/google-roundtrip/case-375c9df694c3d0a3.meta.json +++ b/payloads/fuzz-snapshots/google-roundtrip/case-375c9df694c3d0a3.meta.json @@ -1,16 +1,8 @@ { "issues": [ "lost: toolConfig", - "lost: cachedContent", - "lost: generationConfig._responseJsonSchema", "lost: generationConfig.responseMimeType", - "lost: generationConfig.mediaResolution", - "lost: generationConfig.logprobs", - "lost: generationConfig.speechConfig", - "lost: generationConfig.frequencyPenalty", - "lost: generationConfig.imageConfig", "lost: generationConfig.responseJsonSchema", - "lost: generationConfig.responseModalities", "lost: contents[0].parts[0].executableCode", "lost: contents[0].parts[0].mediaResolution", "lost: contents[0].parts[0].videoMetadata", diff --git a/payloads/fuzz-snapshots/google-roundtrip/case-562df898165dbfc6.meta.json b/payloads/fuzz-snapshots/google-roundtrip/case-562df898165dbfc6.meta.json deleted file mode 100644 index 18192f03..00000000 --- a/payloads/fuzz-snapshots/google-roundtrip/case-562df898165dbfc6.meta.json +++ /dev/null @@ -1,16 +0,0 @@ -{ - "issues": [ - "lost: cachedContent", - "lost: generationConfig.enableEnhancedCivicAnswers", - "lost: generationConfig.candidateCount", - "lost: generationConfig.imageConfig", - "lost: generationConfig.responseMimeType", - "lost: generationConfig.responseLogprobs", - "lost: generationConfig._responseJsonSchema", - "lost: generationConfig.seed", - "added: generationConfig.responseSchema", - "changed: contents.length (2 -> 1)" - ], - "kind": "request-roundtrip", - "provider": "google" -} diff --git a/payloads/fuzz-snapshots/google-roundtrip/case-562df898165dbfc6.request.json b/payloads/fuzz-snapshots/google-roundtrip/case-562df898165dbfc6.request.json deleted file mode 100644 index 3a002295..00000000 --- a/payloads/fuzz-snapshots/google-roundtrip/case-562df898165dbfc6.request.json +++ /dev/null @@ -1,104 +0,0 @@ -{ - "cachedContent": ".V?lwoQ4,,dQ! JHQ2!", - "contents": [ - { - "parts": [ - { - "fileData": { - "mimeType": "IX2NDH5Ni??.!!u4,U?9?,,,p20M8," - }, - "videoMetadata": { - "endOffset": ".5i7.gs oIc?y?U6rP6,.k!G2s.,??a4Fl?.N0", - "fps": -61.025434573605416 - } - }, - { - "executableCode": {}, - "inlineData": { - "data": "?N?t.7 ?,!" - }, - "mediaResolution": {}, - "partMetadata": {}, - "thoughtSignature": "x?xcwt??5kf.y.d8 .F!..2j5Nrst!c?.AeniH4?" - }, - { - "codeExecutionResult": { - "output": "I!" - }, - "executableCode": { - "code": ",i?BQbM3?.vs?HR?0.. z,!kT4m?5gLf,7! V?", - "language": "PYTHON" - }, - "functionResponse": { - "response": {}, - "scheduling": "SILENT", - "willContinue": true - }, - "inlineData": {}, - "mediaResolution": { - "level": "MEDIA_RESOLUTION_LOW" - }, - "partMetadata": {}, - "videoMetadata": { - "endOffset": "0mT!.G5y", - "fps": 17.274042364937063 - } - } - ], - "role": "F8H!sc6.D?d,?,.U,2f?,V" - }, - { - "parts": [ - { - "functionCall": { - "name": " O2?6!,m!!e,FC?Jn?85?..7eH.m7.cw" - }, - "inlineData": { - "mimeType": "sEH" - }, - "thought": true, - "videoMetadata": { - "startOffset": "..?J,I3.f?5?,r ?6!MK8 EWk,gvo Va?P,2!eFZV?02??" - } - }, - { - "codeExecutionResult": {}, - "functionResponse": { - "id": "pu.YT. .UF?.y.B", - "parts": [ - {} - ], - "response": {}, - "willContinue": true - }, - "mediaResolution": { - "level": "MEDIA_RESOLUTION_HIGH" - }, - "thought": false, - "videoMetadata": { - "fps": -53.962882073809226, - "startOffset": "D" - } - } - ], - "role": "3!qi?" - } - ], - "generationConfig": { - "_responseJsonSchema": "f ys b27NGe7Y WFq Y3v2yr", - "candidateCount": -879, - "enableEnhancedCivicAnswers": false, - "imageConfig": { - "aspectRatio": " t2Et8hEIB,.L ,. a", - "imageSize": ".WB? ,W.4,?.?f..0KXT D!iz5bE,1,.?!?!Fm!?y.!?wpod2" - }, - "maxOutputTokens": 69, - "responseLogprobs": false, - "responseMimeType": ",.6,0 5t.TF..!,,1!5a.F!,N2Yy?B9 ?7?,aUNZs?q.5G7b..", - "seed": 978, - "stopSequences": [ - ",,.mLx?,U?64dK7 HO?!RC .l3.7,o" - ], - "topK": -140 - } -} diff --git a/payloads/fuzz-snapshots/google-roundtrip/case-65163e2b3d0cab80.meta.json b/payloads/fuzz-snapshots/google-roundtrip/case-65163e2b3d0cab80.meta.json index a399b89a..b8e47420 100644 --- a/payloads/fuzz-snapshots/google-roundtrip/case-65163e2b3d0cab80.meta.json +++ b/payloads/fuzz-snapshots/google-roundtrip/case-65163e2b3d0cab80.meta.json @@ -1,8 +1,8 @@ { "issues": [ "lost: tools", - "changed: contents[0].role (\"a\" -> \"user\")", - "changed: contents[0].parts.length (1 -> 0)" + "changed: contents[0].parts.length (1 -> 0)", + "changed: contents[0].role (\"a\" -> \"user\")" ], "kind": "request-roundtrip", "provider": "google" diff --git a/payloads/fuzz-snapshots/google-roundtrip/case-97dcb376a3bad1c9.meta.json b/payloads/fuzz-snapshots/google-roundtrip/case-97dcb376a3bad1c9.meta.json index 8dd50d42..fa0d81b8 100644 --- a/payloads/fuzz-snapshots/google-roundtrip/case-97dcb376a3bad1c9.meta.json +++ b/payloads/fuzz-snapshots/google-roundtrip/case-97dcb376a3bad1c9.meta.json @@ -1,11 +1,11 @@ { "issues": [ "lost: systemInstruction", - "lost: contents[0].parts[0].thought", - "lost: contents[0].parts[0].functionCall", "lost: contents[0].parts[0].thoughtSignature", - "lost: contents[0].parts[0].videoMetadata", + "lost: contents[0].parts[0].thought", "lost: contents[0].parts[0].fileData", + "lost: contents[0].parts[0].videoMetadata", + "lost: contents[0].parts[0].functionCall", "added: contents[0].parts[0].inlineData", "changed: contents[0].role (\"N,L770L K,H,?,.l4n ,\" -> \"user\")" ], diff --git a/payloads/fuzz-snapshots/google-roundtrip/case-a2d3e2321403c3b4.meta.json b/payloads/fuzz-snapshots/google-roundtrip/case-a2d3e2321403c3b4.meta.json index dea236d7..6f2af3d2 100644 --- a/payloads/fuzz-snapshots/google-roundtrip/case-a2d3e2321403c3b4.meta.json +++ b/payloads/fuzz-snapshots/google-roundtrip/case-a2d3e2321403c3b4.meta.json @@ -1,19 +1,11 @@ { "issues": [ "lost: toolConfig", - "lost: cachedContent", + "lost: contents[0].parts[0].partMetadata", "lost: contents[0].parts[0].thoughtSignature", - "lost: contents[0].parts[0].functionCall", "lost: contents[0].parts[0].codeExecutionResult", "lost: contents[0].parts[0].executableCode", - "lost: contents[0].parts[0].partMetadata", - "lost: generationConfig.logprobs", - "lost: generationConfig.presencePenalty", - "lost: generationConfig.imageConfig", - "lost: generationConfig._responseJsonSchema", - "lost: generationConfig.speechConfig", - "lost: generationConfig.responseModalities", - "lost: generationConfig.enableEnhancedCivicAnswers", + "lost: contents[0].parts[0].functionCall", "added: generationConfig.responseSchema", "added: generationConfig.thinkingConfig.thinkingBudget", "changed: contents[0].role (\"!,B?.p,.J9H\" -> \"user\")", diff --git a/payloads/fuzz-snapshots/google-roundtrip/case-c18e28e85b79bba8.meta.json b/payloads/fuzz-snapshots/google-roundtrip/case-c18e28e85b79bba8.meta.json index 26b030af..f7ca4327 100644 --- a/payloads/fuzz-snapshots/google-roundtrip/case-c18e28e85b79bba8.meta.json +++ b/payloads/fuzz-snapshots/google-roundtrip/case-c18e28e85b79bba8.meta.json @@ -1,8 +1,7 @@ { "issues": [ - "lost: toolConfig", - "lost: cachedContent", "lost: systemInstruction", + "lost: toolConfig", "lost: tools[0].urlContext", "lost: tools[0].fileSearch", "lost: tools[0].computerUse", From 30e883689fc2397db9bd852996213220520ca3ce Mon Sep 17 00:00:00 2001 From: Matt Perpick Date: Wed, 18 Feb 2026 17:04:06 -0600 Subject: [PATCH 3/4] Fix typed-boundary-check lint: use constant and typed deserialization Replace direct .get("...") calls with GENERATION_CONFIG_EXTRAS_KEY constant in production code and typed GoogleParams/OpenAIChatParams deserialization in test assertions. Co-Authored-By: Claude Opus 4.6 --- crates/lingua/src/providers/google/adapter.rs | 76 +++++++++++-------- 1 file changed, 44 insertions(+), 32 deletions(-) diff --git a/crates/lingua/src/providers/google/adapter.rs b/crates/lingua/src/providers/google/adapter.rs index 82e1e720..39b73fc6 100644 --- a/crates/lingua/src/providers/google/adapter.rs +++ b/crates/lingua/src/providers/google/adapter.rs @@ -31,6 +31,11 @@ use crate::universal::{ UniversalStreamChunk, UniversalUsage, UserContent, }; +/// Internal extras key used to stash generationConfig fields that don't have +/// universal equivalents (candidateCount, speechConfig, responseModalities, etc.) +/// so they survive a roundtrip through the universal format. +const GENERATION_CONFIG_EXTRAS_KEY: &str = "_generationConfigExtras"; + /// Adapter for Google AI GenerateContent API. pub struct GoogleAdapter; @@ -188,8 +193,10 @@ impl ProviderAdapter for GoogleAdapter { // Remove null entries config_map.retain(|_, v| !v.is_null()); if !config_map.is_empty() { - google_extras - .insert("_generationConfigExtras".into(), Value::Object(config_map)); + google_extras.insert( + GENERATION_CONFIG_EXTRAS_KEY.into(), + Value::Object(config_map), + ); } } } @@ -272,7 +279,7 @@ impl ProviderAdapter for GoogleAdapter { .params .extras .get(&ProviderFormat::Google) - .and_then(|e| e.get("_generationConfigExtras")) + .and_then(|e| e.get(GENERATION_CONFIG_EXTRAS_KEY)) .is_some(); let has_params = req.params.temperature.is_some() || req.params.top_p.is_some() @@ -328,7 +335,8 @@ impl ProviderAdapter for GoogleAdapter { // Merge back generationConfig extras (candidateCount, speechConfig, etc.) if let Some(extras) = req.params.extras.get(&ProviderFormat::Google) { - if let Some(Value::Object(config_extras)) = extras.get("_generationConfigExtras") { + if let Some(Value::Object(config_extras)) = extras.get(GENERATION_CONFIG_EXTRAS_KEY) + { if let Some(config_map) = config_value.as_object_mut() { for (k, v) in config_extras { if !config_map.contains_key(k) { @@ -370,7 +378,7 @@ impl ProviderAdapter for GoogleAdapter { if let Some(extras) = req.params.extras.get(&ProviderFormat::Google) { for (k, v) in extras { // _generationConfigExtras is merged into generationConfig above - if k == "_generationConfigExtras" { + if k == GENERATION_CONFIG_EXTRAS_KEY { continue; } // Don't overwrite canonical fields we already handled @@ -603,6 +611,11 @@ mod tests { use super::*; use crate::serde_json::json; + /// Parse a reconstructed JSON Value back into typed GoogleParams for assertions. + fn parse_params(value: &Value) -> GoogleParams { + serde_json::from_value(value.clone()).expect("should parse as GoogleParams") + } + #[test] fn test_google_detect_request() { let adapter = GoogleAdapter; @@ -630,7 +643,6 @@ mod tests { }); let universal = adapter.request_to_universal(payload).unwrap(); - // Use approximate comparison due to f32->f64 conversion precision assert!((universal.params.temperature.unwrap() - 0.7).abs() < 0.001); assert_eq!( universal.params.token_budget, @@ -638,8 +650,9 @@ mod tests { ); let reconstructed = adapter.request_from_universal(&universal).unwrap(); - assert!(reconstructed.get("contents").is_some()); - assert!(reconstructed.get("generationConfig").is_some()); + let params = parse_params(&reconstructed); + assert!(params.contents.is_some()); + assert!(params.generation_config.is_some()); } #[test] @@ -655,11 +668,11 @@ mod tests { let universal = adapter.request_to_universal(payload).unwrap(); let reconstructed = adapter.request_from_universal(&universal).unwrap(); - assert!(reconstructed.get("contents").is_some()); - // safetySettings should survive the roundtrip + let params = parse_params(&reconstructed); + assert!(params.contents.is_some()); assert_eq!( - reconstructed.get("safetySettings"), - Some(&json!([{"category": "HARM_CATEGORY_HARASSMENT", "threshold": "BLOCK_NONE"}])) + params.safety_settings, + Some(json!([{"category": "HARM_CATEGORY_HARASSMENT", "threshold": "BLOCK_NONE"}])) ); } @@ -686,15 +699,13 @@ mod tests { assert!((universal.params.frequency_penalty.unwrap() - 0.7).abs() < 0.001); let reconstructed = adapter.request_from_universal(&universal).unwrap(); - let config = reconstructed.get("generationConfig").unwrap(); - assert_eq!(config.get("seed"), Some(&json!(42))); - assert_eq!(config.get("candidateCount"), Some(&json!(2))); - assert_eq!(config.get("responseLogprobs"), Some(&json!(true))); - assert_eq!(config.get("responseModalities"), Some(&json!(["TEXT"]))); - assert_eq!( - config.get("mediaResolution"), - Some(&json!("MEDIA_RESOLUTION_LOW")) - ); + let params = parse_params(&reconstructed); + let config = params.generation_config.unwrap(); + assert_eq!(config.seed, Some(42)); + assert_eq!(config.candidate_count, Some(2)); + assert_eq!(config.response_logprobs, Some(true)); + assert!(config.response_modalities.is_some()); + assert!(config.media_resolution.is_some()); } #[test] @@ -707,15 +718,14 @@ mod tests { let universal = adapter.request_to_universal(payload).unwrap(); let reconstructed = adapter.request_from_universal(&universal).unwrap(); - assert_eq!( - reconstructed.get("cachedContent"), - Some(&json!("cachedContents/abc123")) - ); + let params = parse_params(&reconstructed); + assert_eq!(params.cached_content, Some("cachedContents/abc123".into())); } #[test] fn test_google_openai_google_roundtrip_seed_and_penalties() { use crate::processing::adapters::adapter_for_format; + use crate::providers::openai::params::OpenAIChatParams; let google = adapter_for_format(ProviderFormat::Google).unwrap(); let openai = adapter_for_format(ProviderFormat::ChatCompletions).unwrap(); @@ -743,8 +753,9 @@ mod tests { // Universal -> OpenAI ChatCompletions let openai_payload = openai.request_from_universal(&universal).unwrap(); - // seed, presence_penalty, frequency_penalty should be in OpenAI format - assert_eq!(openai_payload.get("seed"), Some(&json!(42))); + let openai_params: OpenAIChatParams = + serde_json::from_value(openai_payload.clone()).unwrap(); + assert_eq!(openai_params.seed, Some(42)); // OpenAI -> Universal (back) let universal_2 = openai.request_to_universal(openai_payload).unwrap(); @@ -752,12 +763,13 @@ mod tests { // Universal -> Google (back) let google_out = google.request_from_universal(&universal_2).unwrap(); - let config = google_out.get("generationConfig").unwrap(); + let params = parse_params(&google_out); + let config = params.generation_config.unwrap(); // Universal params survive cross-provider roundtrip - assert_eq!(config.get("seed"), Some(&json!(42))); - assert!(config.get("presencePenalty").is_some()); - assert!(config.get("frequencyPenalty").is_some()); - assert!(config.get("temperature").is_some()); + assert_eq!(config.seed, Some(42)); + assert!(config.presence_penalty.is_some()); + assert!(config.frequency_penalty.is_some()); + assert!(config.temperature.is_some()); // Google-specific extras (candidateCount, responseModalities, safetySettings, // cachedContent) are stored under ProviderFormat::Google in extras, so they From 664f0f5ecd071f08dca2374449e4d2ffef850bfc Mon Sep 17 00:00:00 2001 From: Matt Perpick Date: Wed, 18 Feb 2026 17:28:22 -0600 Subject: [PATCH 4/4] Strip null generationConfig fields and update transform snapshots GenerationConfig.response_schema is Box> which serializes as null when None. Strip null entries from the serialized config to avoid spurious output. Update vitest snapshots to reflect seed, presencePenalty, and frequencyPenalty now correctly appearing in generationConfig output. Co-Authored-By: Claude Opus 4.6 --- crates/lingua/src/providers/google/adapter.rs | 6 +++++ mise.toml | 1 + .../__snapshots__/transforms.test.ts.snap | 22 ++++++++----------- 3 files changed, 16 insertions(+), 13 deletions(-) diff --git a/crates/lingua/src/providers/google/adapter.rs b/crates/lingua/src/providers/google/adapter.rs index 39b73fc6..b4113847 100644 --- a/crates/lingua/src/providers/google/adapter.rs +++ b/crates/lingua/src/providers/google/adapter.rs @@ -333,6 +333,12 @@ impl ProviderAdapter for GoogleAdapter { let mut config_value = serde_json::to_value(config) .map_err(|e| TransformError::SerializationFailed(e.to_string()))?; + // Strip null entries (e.g. responseSchema which is Box> + // and always serializes, producing null when None) + if let Some(config_map) = config_value.as_object_mut() { + config_map.retain(|_, v| !v.is_null()); + } + // Merge back generationConfig extras (candidateCount, speechConfig, etc.) if let Some(extras) = req.params.extras.get(&ProviderFormat::Google) { if let Some(Value::Object(config_extras)) = extras.get(GENERATION_CONFIG_EXTRAS_KEY) diff --git a/mise.toml b/mise.toml index 6a798e2b..804b084f 100644 --- a/mise.toml +++ b/mise.toml @@ -3,3 +3,4 @@ _.file = ".env" [tools] pnpm = "10.26.2" +nodejs = "22.15.0" diff --git a/payloads/scripts/transforms/__snapshots__/transforms.test.ts.snap b/payloads/scripts/transforms/__snapshots__/transforms.test.ts.snap index e5309657..fba6ae81 100644 --- a/payloads/scripts/transforms/__snapshots__/transforms.test.ts.snap +++ b/payloads/scripts/transforms/__snapshots__/transforms.test.ts.snap @@ -2499,7 +2499,6 @@ exports[`chat-completions → google > complexReasoningRequest > request 1`] = ` ], "generationConfig": { "maxOutputTokens": 20000, - "responseSchema": null, }, "model": "gemini-2.5-flash", } @@ -2616,6 +2615,9 @@ exports[`chat-completions → google > frequencyPenaltyParam > request 1`] = ` "role": "user", }, ], + "generationConfig": { + "frequencyPenalty": 0.5, + }, "model": "gemini-2.5-flash", } `; @@ -2798,7 +2800,6 @@ exports[`chat-completions → google > maxCompletionTokensParam > request 1`] = ], "generationConfig": { "maxOutputTokens": 500, - "responseSchema": null, }, "model": "gemini-2.5-flash", } @@ -2895,7 +2896,6 @@ exports[`chat-completions → google > multimodalRequest > request 1`] = ` ], "generationConfig": { "maxOutputTokens": 300, - "responseSchema": null, }, "model": "gemini-2.5-flash", } @@ -3279,6 +3279,9 @@ exports[`chat-completions → google > presencePenaltyParam > request 1`] = ` "role": "user", }, ], + "generationConfig": { + "presencePenalty": 0.5, + }, "model": "gemini-2.5-flash", } `; @@ -3368,7 +3371,6 @@ exports[`chat-completions → google > reasoningEffortLowParam > request 1`] = ` }, ], "generationConfig": { - "responseSchema": null, "thinkingConfig": { "includeThoughts": true, "thinkingBudget": 1024, @@ -3500,7 +3502,6 @@ exports[`chat-completions → google > reasoningRequestTruncated > request 1`] = ], "generationConfig": { "maxOutputTokens": 100, - "responseSchema": null, }, "model": "gemini-2.5-flash", } @@ -3547,7 +3548,6 @@ exports[`chat-completions → google > reasoningSummaryParam > request 1`] = ` }, ], "generationConfig": { - "responseSchema": null, "thinkingConfig": { "includeThoughts": true, "thinkingBudget": 2048, @@ -3704,6 +3704,9 @@ exports[`chat-completions → google > seedParam > request 1`] = ` "role": "user", }, ], + "generationConfig": { + "seed": 12345, + }, "model": "gemini-2.5-flash", } `; @@ -3793,7 +3796,6 @@ exports[`chat-completions → google > simpleRequest > request 1`] = ` }, ], "generationConfig": { - "responseSchema": null, "thinkingConfig": { "includeThoughts": true, "thinkingBudget": 1024, @@ -3849,7 +3851,6 @@ exports[`chat-completions → google > stopSequencesParam > request 1`] = ` }, ], "generationConfig": { - "responseSchema": null, "stopSequences": [ "10", "ten", @@ -3945,7 +3946,6 @@ exports[`chat-completions → google > systemMessageArrayContent > request 1`] = ], "generationConfig": { "maxOutputTokens": 300, - "responseSchema": null, }, "model": "gemini-2.5-flash", "systemInstruction": { @@ -4017,7 +4017,6 @@ exports[`chat-completions → google > temperatureParam > request 1`] = ` }, ], "generationConfig": { - "responseSchema": null, "temperature": 0.7, }, "model": "gemini-2.5-flash", @@ -4066,7 +4065,6 @@ exports[`chat-completions → google > textFormatJsonObjectParam > request 1`] = ], "generationConfig": { "responseMimeType": "application/json", - "responseSchema": null, }, "model": "gemini-2.5-flash", } @@ -4244,7 +4242,6 @@ exports[`chat-completions → google > textFormatTextParam > request 1`] = ` ], "generationConfig": { "responseMimeType": "text/plain", - "responseSchema": null, }, "model": "gemini-2.5-flash", } @@ -4459,7 +4456,6 @@ exports[`chat-completions → google > topPParam > request 1`] = ` }, ], "generationConfig": { - "responseSchema": null, "topP": 0.9, }, "model": "gemini-2.5-flash",