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 <noreply@anthropic.com>
This commit is contained in:
Lizandro
2026-07-15 18:01:32 +00:00
co-authored by Claude Sonnet 4.6
parent bd93ef2029
commit 5c15f9f3fe
+12 -3
View File
@@ -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'])) {