Files
aiform_prod/backend/app/services/ocr_service.py
AI Assistant 134eb42493 fix: Исправлен OCR endpoint - /process → /analyze-file
Проблема:
 HTTP 404 Not Found при вызове /process
 OCR не работал вообще
 Gemini Vision не получал данные

Решение:
 Изменен endpoint на /analyze-file (правильный)
 Исправлено в 3 местах:
   - ocr_service.py (line 48)
   - upload.py - /policy endpoint (line 53)
   - upload.py - /passport endpoint (line 122)

Теперь:
 OCR будет работать
 Gemini Vision получит текст
 Debug панель покажет результаты

Тестирование:
1. Перезапусти backend
2. Загрузи файл полиса
3. Смотри логи:
   🔍 Starting OCR for: filename
   📄 OCR completed: XXX chars
   🤖 Starting AI analysis
    AI Analysis complete
2025-10-25 10:39:57 +03:00

179 lines
6.9 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"""
OCR Service - Распознавание документов + AI проверка
"""
import httpx
import logging
from typing import Optional, Dict, Any
from ..config import settings
import json
logger = logging.getLogger(__name__)
class OCRService:
"""Сервис для OCR и AI анализа документов"""
def __init__(self):
self.ocr_url = settings.ocr_api_url
self.ai_api_key = settings.openrouter_api_key
self.ai_model = settings.openrouter_model
async def process_document(self, file_content: bytes, filename: str) -> Dict[str, Any]:
"""
Обработка документа: OCR + AI анализ
Args:
file_content: Содержимое файла
filename: Имя файла
Returns:
Dict с результатами OCR и AI анализа
"""
result = {
"ocr_text": "",
"ai_analysis": None,
"document_type": "unknown", # policy, passport, ticket, other, garbage
"is_valid": False,
"confidence": 0.0,
"extracted_data": {}
}
try:
# Шаг 1: OCR распознавание текста
logger.info(f"🔍 Starting OCR for: {filename}")
async with httpx.AsyncClient(timeout=60.0) as client:
files = {"file": (filename, file_content, "image/jpeg")}
response = await client.post(
f"{self.ocr_url}/analyze-file",
files=files
)
if response.status_code == 200:
ocr_result = response.json()
ocr_text = ocr_result.get("text", "")
result["ocr_text"] = ocr_text
logger.info(f"📄 OCR completed: {len(ocr_text)} chars")
logger.debug(f"OCR Text preview: {ocr_text[:200]}...")
else:
logger.error(f"❌ OCR failed: {response.status_code}")
return result
# Шаг 2: AI анализ - что это за документ?
logger.info(f"🤖 Starting AI analysis with {self.ai_model}")
ai_analysis = await self._analyze_with_vision(ocr_text)
result["ai_analysis"] = ai_analysis
if ai_analysis:
result["document_type"] = ai_analysis.get("document_type", "unknown")
result["is_valid"] = ai_analysis.get("is_valid_policy", False)
result["confidence"] = ai_analysis.get("confidence", 0.0)
result["extracted_data"] = ai_analysis.get("extracted_data", {})
# Логируем результат
logger.info(f"✅ AI Analysis complete:")
logger.info(f" Document type: {result['document_type']}")
logger.info(f" Valid policy: {result['is_valid']}")
logger.info(f" Confidence: {result['confidence']}")
if result['document_type'] == 'garbage':
logger.warning(f"⚠️ GARBAGE DETECTED: {filename} - not a policy document!")
elif result['document_type'] == 'policy':
logger.info(f"✅ VALID POLICY: {filename}")
if result['extracted_data']:
logger.info(f" Extracted: {json.dumps(result['extracted_data'], ensure_ascii=False)}")
except Exception as e:
logger.error(f"❌ OCR/AI processing error: {e}")
return result
async def _analyze_with_vision(self, ocr_text: str) -> Optional[Dict[str, Any]]:
"""
Анализ через Gemini Vision
Проверяет:
- Это полис или нет?
- Извлекает данные полиса
"""
try:
prompt = f"""Проанализируй этот текст из OCR документа.
Текст: {ocr_text}
Задачи:
1. Определи тип документа: policy (страховой полис), passport, ticket, other, garbage (не документ)
2. Если это полис - извлеки данные:
- voucher (номер полиса вида E1000-302538524)
- holder_name (ФИО держателя)
- insured_from (дата начала)
- insured_to (дата окончания)
- destination (страна/регион)
3. Оцени confidence (0.0-1.0) насколько уверен
4. is_valid_policy: true если это реальный страховой полис
Ответь ТОЛЬКО в формате JSON:
{{
"document_type": "policy|passport|ticket|other|garbage",
"is_valid_policy": true/false,
"confidence": 0.95,
"extracted_data": {{
"voucher": "E1000-302538524",
"holder_name": "...",
"insured_from": "DD.MM.YYYY",
"insured_to": "DD.MM.YYYY",
"destination": "..."
}}
}}"""
async with httpx.AsyncClient(timeout=30.0) as client:
response = await client.post(
"https://openrouter.ai/api/v1/chat/completions",
headers={
"Authorization": f"Bearer {self.ai_api_key}",
"HTTP-Referer": "http://147.45.146.17:8100",
"Content-Type": "application/json"
},
json={
"model": self.ai_model,
"messages": [
{
"role": "user",
"content": prompt
}
],
"temperature": 0.1,
"max_tokens": 500
}
)
if response.status_code == 200:
ai_response = response.json()
content = ai_response["choices"][0]["message"]["content"]
# Парсим JSON из ответа
# Убираем markdown если есть
if "```json" in content:
content = content.split("```json")[1].split("```")[0]
elif "```" in content:
content = content.split("```")[1].split("```")[0]
analysis = json.loads(content.strip())
return analysis
else:
logger.error(f"❌ AI API error: {response.status_code}")
return None
except Exception as e:
logger.error(f"❌ AI analysis error: {e}")
return None
# Глобальный экземпляр
ocr_service = OCRService()