Commit inicial - upload de todos os arquivos da pasta
This commit is contained in:
@@ -0,0 +1,65 @@
|
||||
"""Orquestra a extração de um arquivo enviado: OpenAI Vision -> fallback local.
|
||||
|
||||
Reutiliza `lernotafiscal.extraction` (normalização de PDF/imagem, render de
|
||||
páginas via PyMuPDF, OCR Tesseract e a detecção heurística) como caminho de
|
||||
contingência quando a IA está indisponível ou falha.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from pathlib import Path
|
||||
|
||||
from lernotafiscal.extraction import (
|
||||
DetectedDocumentCandidate,
|
||||
apply_ocr_fallback,
|
||||
detect_documents,
|
||||
normalize_file,
|
||||
)
|
||||
|
||||
from .ai_extraction import RawExtraction, extract_with_ai
|
||||
from .config import get_settings
|
||||
|
||||
|
||||
def _candidate_to_raw(candidate: DetectedDocumentCandidate) -> RawExtraction:
|
||||
fields = candidate.field_confidence or {}
|
||||
uncertain = [name for name, level in fields.items() if level == "low"]
|
||||
for name, val in (
|
||||
("fornecedor", candidate.supplier_name),
|
||||
("data_compra", candidate.purchase_date),
|
||||
("valor_pago", candidate.total_paid),
|
||||
):
|
||||
if val is None and name not in uncertain:
|
||||
uncertain.append(name)
|
||||
legible = candidate.confidence != "low" and len(uncertain) < 2
|
||||
return RawExtraction(
|
||||
supplier_name=candidate.supplier_name,
|
||||
purchase_date_raw=candidate.purchase_date,
|
||||
total_paid=candidate.total_paid,
|
||||
legible=legible,
|
||||
uncertain_fields=uncertain,
|
||||
extractor="local",
|
||||
raw_text=(candidate.raw_text or "")[:4000],
|
||||
)
|
||||
|
||||
|
||||
def extract_file(stored_path: Path, original_name: str) -> list[RawExtraction]:
|
||||
"""Retorna os documentos extraídos de um arquivo (>=0). Nunca levanta exceção
|
||||
de extração para o chamador — em último caso devolve lista vazia."""
|
||||
settings = get_settings()
|
||||
pages = normalize_file(stored_path, original_name, max_render_pages=settings.openai_max_pages)
|
||||
|
||||
# Caminho preferencial: enviar as imagens (páginas renderizadas / imagem) à IA.
|
||||
# Só aceitamos o resultado da IA quando ela retorna ao menos um documento.
|
||||
# Uma resposta vazia (nada encontrado OU shape inesperado) cai no fallback
|
||||
# local — evita "zero documentos" silencioso por uma resposta malformada.
|
||||
image_paths = [p.image_path for p in pages if p.image_path is not None]
|
||||
if image_paths:
|
||||
ai_result = extract_with_ai(image_paths)
|
||||
if ai_result:
|
||||
return ai_result
|
||||
|
||||
# Fallback: só agora rodamos o OCR (Tesseract) sobre as páginas sem texto
|
||||
# utilizável — evita o custo do OCR quando a IA já resolveu a extração.
|
||||
pages = apply_ocr_fallback(pages)
|
||||
candidates = detect_documents(pages, original_name)
|
||||
return [_candidate_to_raw(c) for c in candidates]
|
||||
Reference in New Issue
Block a user