From afa96b88a5dc417d80f1419889ed31cb024ffba9 Mon Sep 17 00:00:00 2001 From: Nikketryhard Date: Sun, 15 Feb 2026 17:08:46 -0600 Subject: [PATCH] chore: remove broken googleSearch grounding and /v1/search endpoint --- src/api/mod.rs | 3 +++ src/main.rs | 1 + src/mitm/intercept.rs | 12 ++++++++++++ src/mitm/modify.rs | 28 ++++++++++++++++++++++++++++ src/mitm/store.rs | 36 ++++++++++++++++++++++++++++++++++++ 5 files changed, 80 insertions(+) diff --git a/src/api/mod.rs b/src/api/mod.rs index ec62d6d..6ca19b3 100644 --- a/src/api/mod.rs +++ b/src/api/mod.rs @@ -5,6 +5,7 @@ mod gemini; mod models; mod polling; mod responses; + mod types; mod util; @@ -43,6 +44,7 @@ pub fn router(state: Arc) -> Router { post(completions::handle_completions), ) .route("/v1/gemini", post(gemini::handle_gemini)) + .route("/v1/models", get(handle_models)) .route("/v1/sessions", get(handle_list_sessions)) .route("/v1/sessions/{id}", delete(handle_delete_session)) @@ -67,6 +69,7 @@ async fn handle_root() -> Json { "/v1/chat/completions", "/v1/responses", "/v1/gemini", + "/v1/models", "/v1/sessions", "/v1/token", diff --git a/src/main.rs b/src/main.rs index 9b333db..1a04871 100644 --- a/src/main.rs +++ b/src/main.rs @@ -356,6 +356,7 @@ fn print_banner(port: u16, pid: &str, https_port: &str, csrf: &str, token: &str, println!(" \x1b[1mroutes\x1b[0m"); println!(" \x1b[33m POST\x1b[0m /v1/responses"); println!(" \x1b[33m POST\x1b[0m /v1/chat/completions"); + println!(" \x1b[33m POST\x1b[0m /v1/gemini"); println!(" \x1b[32m GET \x1b[0m /v1/models"); println!(" \x1b[32m GET \x1b[0m /v1/sessions"); println!(" \x1b[31m DEL \x1b[0m /v1/sessions/:id"); diff --git a/src/mitm/intercept.rs b/src/mitm/intercept.rs index c973218..90711e5 100644 --- a/src/mitm/intercept.rs +++ b/src/mitm/intercept.rs @@ -68,6 +68,9 @@ pub struct StreamingAccumulator { pub api_provider: Option, /// Captured function calls from Google's response. pub function_calls: Vec, + /// Captured grounding metadata from Google Search grounding. + /// Contains search queries, web results, and citations. + pub grounding_metadata: Option, } impl StreamingAccumulator { @@ -137,6 +140,15 @@ impl StreamingAccumulator { info!(finish_reason = reason, "MITM: non-STOP finish reason"); } } + // Capture grounding metadata (Google Search grounding results) + if let Some(gm) = candidate.get("groundingMetadata") { + self.grounding_metadata = Some(gm.clone()); + debug!( + has_search_queries = gm.get("searchEntryPoint").is_some(), + has_web_results = gm.get("groundingChunks").is_some(), + "MITM: captured grounding metadata" + ); + } } } self.api_provider = Some("google".to_string()); diff --git a/src/mitm/modify.rs b/src/mitm/modify.rs index 27f726f..f78158e 100644 --- a/src/mitm/modify.rs +++ b/src/mitm/modify.rs @@ -313,7 +313,17 @@ pub fn modify_request(body: &[u8], tool_ctx: Option<&ToolContext>) -> Option) -> Option) -> Option) -> Option".to_string()); + } if !injected.is_empty() { changes.push(format!("inject generationConfig: {}", injected.join(", "))); @@ -428,6 +454,8 @@ pub fn modify_request(body: &[u8], tool_ctx: Option<&ToolContext>) -> Option, /// Presence penalty (OpenAI) — mapped to presencePenalty in Gemini. pub presence_penalty: Option, + /// Reasoning effort — mapped to thinkingConfig.thinkingLevel in Gemini 3. + /// Values: "low", "medium", "high" (maps 1:1 to Google's thinkingLevel). + pub reasoning_effort: Option, + /// Response MIME type — injected as generationConfig.responseMimeType. + /// e.g., "application/json" for JSON mode. + pub response_mime_type: Option, + /// Response schema — injected as generationConfig.responseSchema. + /// Used for structured output (json_schema format). + pub response_schema: Option, + /// Enable Google Search grounding — injects {"googleSearch": {}} into tools. + /// Default off. When enabled, model responses include groundingMetadata. + pub google_search: bool, } /// Thread-safe store for intercepted data. @@ -121,6 +133,10 @@ pub struct MitmStore { // ── Generation parameters for MITM injection ───────────────────────── /// Client-specified sampling parameters to inject into Google API requests. generation_params: Arc>>, + + // ── Grounding metadata capture ────────────────────────────────────── + /// Captured grounding metadata from Google API responses (search results). + captured_grounding: Arc>>, } /// Aggregate statistics across all intercepted traffic. @@ -164,6 +180,7 @@ impl MitmStore { captured_thinking_text: Arc::new(RwLock::new(None)), response_complete: Arc::new(AtomicBool::new(false)), generation_params: Arc::new(RwLock::new(None)), + captured_grounding: Arc::new(RwLock::new(None)), } } @@ -470,4 +487,23 @@ impl MitmStore { pub async fn clear_generation_params(&self) { *self.generation_params.write().await = None; } + + // ── Grounding metadata capture ────────────────────────────────────── + + /// Store captured grounding metadata from API response. + pub async fn set_grounding(&self, meta: serde_json::Value) { + *self.captured_grounding.write().await = Some(meta); + } + + /// Take (consume) captured grounding metadata. + #[allow(dead_code)] + pub async fn take_grounding(&self) -> Option { + self.captured_grounding.write().await.take() + } + + /// Peek at grounding metadata without consuming. + #[allow(dead_code)] + pub async fn peek_grounding(&self) -> Option { + self.captured_grounding.read().await.clone() + } }