Completes the data-contract layer. Highlights: - `ResponseIX.context` is an internal mutable accumulator used by pipeline steps (pages, files, texts, use_case classes, segment index). It MUST NOT leak into the serialised response, so we mark the field with `Field(exclude=True)` and carry the shape in a small `_InternalContext` sub-model with `extra="allow"` so steps can stash arbitrary state without schema churn. Tested: `model_dump()` and `model_dump_json()` both drop it. - `FieldProvenance` gains `provenance_verified: bool | None` and `text_agreement: bool | None` — the two MVP reliability flags written by the new ReliabilityStep. Both default None so rows predating the ReliabilityStep (empty LLM output, cloud-import replay) parse cleanly. - `quality_metrics` stays a free-form `dict[str, Any]` — the MVP adds `verified_fields` and `text_agreement_fields` counters without carving them into the schema, which keeps future metric additions free. - `Job.status` and `Job.callback_status` are `Literal[...]` so Pydantic rejects unknown states at the edge. Invariant (`status='done' iff response.error is None`) stays worker-enforced — callers sometimes hydrate in-flight rows and we do not want validation to reject them. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
46 lines
1.4 KiB
Python
46 lines
1.4 KiB
Python
"""Job envelope stored in ``ix_jobs`` and returned by REST.
|
|
|
|
Mirrors spec §3 ("Job envelope") and §4 ("Job store"). The lifecycle
|
|
enum is a ``Literal`` so Pydantic rejects unknown values at parse time.
|
|
``callback_status`` is nullable until the worker attempts delivery (or
|
|
skips delivery when there's no ``callback_url``).
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
from datetime import datetime
|
|
from typing import Literal
|
|
from uuid import UUID
|
|
|
|
from pydantic import BaseModel, ConfigDict
|
|
|
|
from ix.contracts.request import RequestIX
|
|
from ix.contracts.response import ResponseIX
|
|
|
|
JobStatus = Literal["pending", "running", "done", "error"]
|
|
CallbackStatus = Literal["pending", "delivered", "failed"]
|
|
|
|
|
|
class Job(BaseModel):
|
|
"""Row of ``ix_jobs`` + its request/response bodies.
|
|
|
|
The invariant ``status='done' iff response.error is None`` is enforced by
|
|
the worker, not here — callers occasionally hydrate a stale or in-flight
|
|
row and we don't want the Pydantic validator to reject it.
|
|
"""
|
|
|
|
model_config = ConfigDict(extra="forbid")
|
|
|
|
job_id: UUID
|
|
ix_id: str
|
|
client_id: str
|
|
request_id: str
|
|
status: JobStatus
|
|
request: RequestIX
|
|
response: ResponseIX | None = None
|
|
callback_url: str | None = None
|
|
callback_status: CallbackStatus | None = None
|
|
attempts: int = 0
|
|
created_at: datetime
|
|
started_at: datetime | None = None
|
|
finished_at: datetime | None = None
|