url = self::normalizarUrl(ChatConfig::get('ocr_url', '')); $this->token = ChatConfig::get('ocr_token', ''); } /** Acepta tanto "https://host" como "https://host/extract" (error comun de configuracion). */ public static function normalizarUrl(string $url): string { $url = rtrim(trim($url), '/'); return preg_replace('#/extract$#', '', $url); } public function habilitado(): bool { return ChatConfig::get('ocr_habilitado', '0') === '1' && $this->url !== ''; } /** Envia la imagen al servicio OCR externo y devuelve el texto crudo, o null si falla. */ public function extraerTexto(string $base64, string $mimeType): ?string { if (! $this->url) { return null; } try { $response = Http::withToken($this->token) ->timeout(20) ->post("{$this->url}/extract", [ 'image_base64' => $base64, 'mime_type' => $mimeType, ]); if (! $response->successful()) { Log::warning('[OCR] HTTP ' . $response->status() . ': ' . substr($response->body(), 0, 300)); return null; } $data = $response->json(); if (! ($data['success'] ?? false) || empty($data['text'])) { Log::warning('[OCR] respuesta sin texto util: ' . substr($response->body(), 0, 300)); return null; } return $data['text']; } catch (\Throwable $e) { Log::warning('[OCR] excepcion: ' . $e->getMessage()); return null; } } }