feat: Update default models for extraction and normalization to GPT-5.6 Luna across configuration files and documentation. Remove deprecated code and improve pricing details in the UI components. Add tests to ensure correct model settings and pricing calculations.
This commit is contained in:
@@ -0,0 +1,228 @@
|
||||
"""Regresiones de persistencia para ingestas desktop síncronas."""
|
||||
from __future__ import annotations
|
||||
|
||||
import asyncio
|
||||
from contextlib import contextmanager
|
||||
from types import SimpleNamespace
|
||||
|
||||
import pytest
|
||||
|
||||
from api import desktop_ingest
|
||||
from api import main as extraction_api
|
||||
|
||||
|
||||
class FakeSession:
|
||||
def __init__(self) -> None:
|
||||
self.added: list[object] = []
|
||||
self.batch = SimpleNamespace(status="running", updated_at=None)
|
||||
|
||||
def add(self, value: object) -> None:
|
||||
self.added.append(value)
|
||||
|
||||
def flush(self) -> None:
|
||||
pass
|
||||
|
||||
def get(self, _model: object, _identifier: str) -> object:
|
||||
return self.batch
|
||||
|
||||
def refresh(self, _value: object) -> None:
|
||||
pass
|
||||
|
||||
|
||||
def test_multiple_document_types_complete_once_per_radicado(monkeypatch) -> None:
|
||||
monkeypatch.setattr(
|
||||
desktop_ingest,
|
||||
"flatten_extraction_result",
|
||||
lambda fused, _fields, _aliases: [fused],
|
||||
)
|
||||
session = FakeSession()
|
||||
request = {
|
||||
"structure_mode": "radicados",
|
||||
"fields": ["factura/numero", "soporte/codigo"],
|
||||
"doc_types": [{"name": "factura"}, {"name": "soporte"}],
|
||||
"documents": [
|
||||
{"radicado": "RAD-1", "filename": "factura.pdf", "doc_type": "factura"},
|
||||
{"radicado": "RAD-1", "filename": "soporte.pdf", "doc_type": "soporte"},
|
||||
],
|
||||
}
|
||||
task = SimpleNamespace(
|
||||
result={"processed": 0, "total": 1, "row_count": 0, "partials": {}},
|
||||
status="running",
|
||||
finished_at=None,
|
||||
updated_at=None,
|
||||
)
|
||||
|
||||
first = desktop_ingest.persist_desktop_ingest_result(
|
||||
session,
|
||||
task=task,
|
||||
batch_id="batch-1",
|
||||
radicado="RAD-1",
|
||||
filename="factura.pdf",
|
||||
doc_type="factura",
|
||||
request=request,
|
||||
normalized_obj={"numero": "F-1"},
|
||||
)
|
||||
desktop_ingest.mark_task_completed_if_done(session, task, "batch-1")
|
||||
|
||||
assert first["finalized"] is False
|
||||
assert task.result["processed"] == 0
|
||||
assert task.status == "running"
|
||||
assert task.result["partials"] == {"RAD-1": {"factura": {"numero": "F-1"}}}
|
||||
assert session.added == []
|
||||
|
||||
second = desktop_ingest.persist_desktop_ingest_result(
|
||||
session,
|
||||
task=task,
|
||||
batch_id="batch-1",
|
||||
radicado="RAD-1",
|
||||
filename="soporte.pdf",
|
||||
doc_type="soporte",
|
||||
request=request,
|
||||
normalized_obj={"codigo": "S-1"},
|
||||
)
|
||||
desktop_ingest.mark_task_completed_if_done(session, task, "batch-1")
|
||||
|
||||
assert second["finalized"] is True
|
||||
assert task.result["processed"] == 1
|
||||
assert task.result["partials"] == {}
|
||||
assert task.status == "completed"
|
||||
assert session.batch.status == "completed"
|
||||
assert len(session.added) == 1
|
||||
row = session.added[0]
|
||||
assert row.radicado == "RAD-1"
|
||||
assert row.raw_json["factura"] == {"numero": "F-1"}
|
||||
assert row.raw_json["soporte"] == {"codigo": "S-1"}
|
||||
|
||||
|
||||
def test_document_failure_completes_radicado_and_discards_partials(monkeypatch) -> None:
|
||||
monkeypatch.setattr(
|
||||
desktop_ingest,
|
||||
"flatten_extraction_result",
|
||||
lambda fused, _fields, _aliases: [fused],
|
||||
)
|
||||
session = FakeSession()
|
||||
request = {
|
||||
"structure_mode": "radicados",
|
||||
"fields": ["factura/numero", "soporte/codigo"],
|
||||
"doc_types": [{"name": "factura"}, {"name": "soporte"}],
|
||||
"documents": [
|
||||
{"radicado": "RAD-1", "filename": "factura.pdf", "doc_type": "factura"},
|
||||
{"radicado": "RAD-1", "filename": "soporte.pdf", "doc_type": "soporte"},
|
||||
],
|
||||
}
|
||||
task = SimpleNamespace(
|
||||
result={"processed": 0, "total": 1, "row_count": 0, "partials": {}},
|
||||
status="running",
|
||||
finished_at=None,
|
||||
updated_at=None,
|
||||
)
|
||||
|
||||
desktop_ingest.persist_desktop_ingest_result(
|
||||
session,
|
||||
task=task,
|
||||
batch_id="batch-1",
|
||||
radicado="RAD-1",
|
||||
filename="factura.pdf",
|
||||
doc_type="factura",
|
||||
request=request,
|
||||
normalized_obj={"numero": "F-1"},
|
||||
)
|
||||
failure = desktop_ingest.record_desktop_ingest_error(
|
||||
session,
|
||||
task=task,
|
||||
radicado="RAD-1",
|
||||
filename="soporte.pdf",
|
||||
doc_type="soporte",
|
||||
error=RuntimeError("backend no disponible"),
|
||||
)
|
||||
desktop_ingest.mark_task_completed_if_done(session, task, "batch-1")
|
||||
|
||||
assert failure["first_failure"] is True
|
||||
assert task.result["processed"] == 1
|
||||
assert task.result["partials"] == {}
|
||||
assert task.result["failed_radicados"] == ["RAD-1"]
|
||||
assert task.result["errors"] == [{
|
||||
"radicado": "RAD-1",
|
||||
"filename": "soporte.pdf",
|
||||
"doc_type": "soporte",
|
||||
"error": "backend no disponible",
|
||||
}]
|
||||
assert task.status == "completed"
|
||||
assert session.added == []
|
||||
|
||||
late_success = desktop_ingest.persist_desktop_ingest_result(
|
||||
session,
|
||||
task=task,
|
||||
batch_id="batch-1",
|
||||
radicado="RAD-1",
|
||||
filename="soporte.pdf",
|
||||
doc_type="soporte",
|
||||
request=request,
|
||||
normalized_obj={"codigo": "S-1"},
|
||||
)
|
||||
duplicate_failure = desktop_ingest.record_desktop_ingest_error(
|
||||
session,
|
||||
task=task,
|
||||
radicado="RAD-1",
|
||||
filename="factura.pdf",
|
||||
doc_type="factura",
|
||||
error=RuntimeError("otro fallo tardío"),
|
||||
)
|
||||
|
||||
assert late_success["skipped"] is True
|
||||
assert duplicate_failure["first_failure"] is False
|
||||
assert task.result["processed"] == 1
|
||||
assert task.result["partials"] == {}
|
||||
assert session.added == []
|
||||
|
||||
|
||||
def test_process_sync_persists_failure_before_reraising(monkeypatch) -> None:
|
||||
task = SimpleNamespace(
|
||||
result={"processed": 0, "total": 1, "row_count": 0, "partials": {}},
|
||||
status="running",
|
||||
finished_at=None,
|
||||
updated_at=None,
|
||||
)
|
||||
session = FakeSession()
|
||||
|
||||
def get(model: object, _identifier: str, **_kwargs: object) -> object:
|
||||
if model is extraction_api.Task:
|
||||
return task
|
||||
return session.batch
|
||||
|
||||
session.get = get # type: ignore[method-assign]
|
||||
|
||||
@contextmanager
|
||||
def fake_session_scope():
|
||||
yield session
|
||||
|
||||
class FailingService:
|
||||
extractor = SimpleNamespace(model="test-model")
|
||||
|
||||
def extract_full_from_pdf(self, *_args: object, **_kwargs: object) -> object:
|
||||
raise RuntimeError("fallo simulado")
|
||||
|
||||
monkeypatch.setattr(extraction_api, "session_scope", fake_session_scope)
|
||||
monkeypatch.setattr(extraction_api, "processing_service", FailingService())
|
||||
|
||||
with pytest.raises(RuntimeError, match="fallo simulado"):
|
||||
asyncio.run(extraction_api._process_sync(
|
||||
"task-1",
|
||||
"batch-1",
|
||||
b"pdf",
|
||||
"factura.pdf",
|
||||
"RAD-1",
|
||||
"factura",
|
||||
{
|
||||
"fields": ["factura/numero"],
|
||||
"doc_types": [{"name": "factura"}],
|
||||
"documents": [
|
||||
{"radicado": "RAD-1", "filename": "factura.pdf", "doc_type": "factura"},
|
||||
],
|
||||
},
|
||||
))
|
||||
|
||||
assert task.result["processed"] == 1
|
||||
assert task.result["failed_radicados"] == ["RAD-1"]
|
||||
assert task.result["errors"][0]["error"] == "fallo simulado"
|
||||
assert task.status == "completed"
|
||||
Reference in New Issue
Block a user