From 5c15f9f3fe0ea9e2858058cf322d8da50d3e90b3 Mon Sep 17 00:00:00 2001 From: Lizandro Date: Wed, 15 Jul 2026 18:01:32 +0000 Subject: [PATCH] fix: skip thinking parts in Gemini vision response + extract JSON from any position gemini-3.x models return thinking blocks with thought:true flag in parts[]. Filter those out so only actual response text is used. Also extract the first {...} JSON object from the text so stray content before or after (like '040825') doesn't break parsing. Co-Authored-By: Claude Sonnet 4.6 --- app/Services/GeminiVisionService.php | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/app/Services/GeminiVisionService.php b/app/Services/GeminiVisionService.php index 09e3835..93fefb3 100644 --- a/app/Services/GeminiVisionService.php +++ b/app/Services/GeminiVisionService.php @@ -77,9 +77,12 @@ class GeminiVisionService $inputTokens = $response->json('usageMetadata.promptTokenCount', 0); $outputTokens = $response->json('usageMetadata.candidatesTokenCount', 0); - // Collect text from ALL parts (newer models may put JSON in parts[1] if thinking is in parts[0]) + // Skip thought parts (gemini-3.x thinking blocks), only keep actual response text $parts = $response->json('candidates.0.content.parts', []); - $text = implode("\n", array_column(array_filter($parts, fn($p) => isset($p['text'])), 'text')); + $text = implode("\n", array_column( + array_filter($parts, fn($p) => isset($p['text']) && empty($p['thought'])), + 'text' + )); Log::info('[Gemini Vision] raw response: ' . substr($text, 0, 500)); @@ -129,7 +132,13 @@ PROMPT; private function parseRespuesta(string $text): ?array { - $text = trim(preg_replace('/```json\s*|\s*```/', '', $text)); + // Strip markdown fences + $text = preg_replace('/```json\s*|\s*```/', '', $text); + // Extract the first JSON object found anywhere in the text + if (preg_match('/\{.*\}/s', $text, $m)) { + $text = $m[0]; + } + $text = trim($text); $json = json_decode($text, true); if (! is_array($json) || isset($json['error'])) {