gpt-5.3 codex review: Phase 4
Quality Gate / gate (push) Failing after 11s

This commit is contained in:
Jim Lancaster
2026-08-19 21:16:36 -05:00
parent 30fcef3892
commit cdd846fe29
16 changed files with 765 additions and 26 deletions
+18 -1
View File
@@ -59,11 +59,28 @@ class ErrorEnvelope:
timestamp: str
def canonical_error_category(error: AppError) -> str:
"""Map internal categories to canonical API/UI envelope categories."""
match error.category:
case ErrorCategory.VALIDATION | ErrorCategory.USER_INPUT:
return "validation"
case ErrorCategory.NOT_FOUND:
return "not_found"
case ErrorCategory.CONFLICT:
return "conflict"
case ErrorCategory.EXTERNAL_PROVIDER:
return "external"
case ErrorCategory.INFRA_TRANSIENT:
return "timeout"
case _:
return "internal"
def build_error_envelope(error: AppError) -> ErrorEnvelope:
"""Build an API-safe response envelope from an AppError."""
return ErrorEnvelope(
error_id=error.error_id,
category=error.category.value,
category=canonical_error_category(error),
message=error.message,
suggestion=error.suggestion,
timestamp=datetime.now(UTC).isoformat(),
+18 -8
View File
@@ -476,6 +476,7 @@ class SourceService(ServiceBase):
model: str | None = None,
request_manifest: RequestManifest | None = None,
quality_warnings: dict[str, JsonValue] | None = None,
timing_breakdown: dict[str, JsonValue] | None = None,
transport_evidence: TransportEvidence | None = None,
failure_phase: str | None = None,
error_category: str | None = None,
@@ -508,7 +509,12 @@ class SourceService(ServiceBase):
job.model = model or job.model or _resolve_transcript_model(provider=self.provider, settings=self.settings)
metadata_payload = _validate_transcription_metadata(ai_metadata)
raw_response_payload = _validate_json_object(raw_api_response, field_name="raw_api_response")
attempt_metadata = _merge_quality_warnings(metadata_payload, quality_warnings)
timing_payload = _validate_json_object(timing_breakdown, field_name="timing_breakdown")
attempt_metadata = _merge_attempt_metadata(
metadata=metadata_payload,
quality_warnings=quality_warnings,
timing_breakdown=timing_payload,
)
existing_job_source = await _session.exec(
select(JobSource).where(JobSource.job_id == job_id).where(JobSource.source_id == source_id)
@@ -657,20 +663,24 @@ def _validate_transcription_metadata(
return validated.as_json_object()
def _merge_quality_warnings(
def _merge_attempt_metadata(
metadata: dict[str, JsonValue] | None,
*,
quality_warnings: dict[str, JsonValue] | None,
timing_breakdown: dict[str, JsonValue] | None,
) -> dict[str, JsonValue] | None:
"""Attach app-computed quality warnings to provider-normalized metadata.
"""Attach app-computed metadata to provider-normalized metadata.
The warnings are derived from the transcription text rather than reported by
the provider, so they are namespaced under their own key instead of being
mixed into the provider's own fields.
App-computed values (quality warnings and timing) are namespaced so provider
metadata remains semantically distinct.
"""
if quality_warnings is None:
if quality_warnings is None and timing_breakdown is None:
return metadata
merged: dict[str, JsonValue] = dict(metadata or {})
merged["transcription_quality_warnings"] = quality_warnings
if quality_warnings is not None:
merged["transcription_quality_warnings"] = quality_warnings
if timing_breakdown is not None:
merged["processing_timing"] = timing_breakdown
return merged
+33 -7
View File
@@ -116,7 +116,8 @@ class _SuccessfulPage:
result: TranscriptionResult
started_at: datetime
finished_at: datetime
duration_ms: int
provider_duration_ms: int
processing_duration_ms: int
@dataclass(frozen=True)
@@ -125,7 +126,8 @@ class _FailedPage:
error: AppError
started_at: datetime
finished_at: datetime
duration_ms: int
provider_duration_ms: int
processing_duration_ms: int
request_manifest: RequestManifest | None = None
transport_evidence: TransportEvidence | None = None
failure_phase: str | None = None
@@ -273,7 +275,11 @@ async def process_queued_job( # noqa: PLR0915
result=result,
started_at=started_at,
finished_at=finished_at,
duration_ms=max(0, int(elapsed_seconds * 1000)),
provider_duration_ms=max(0, int(elapsed_seconds * 1000)),
processing_duration_ms=max(
_duration_ms_between(started_at, finished_at),
max(0, int(elapsed_seconds * 1000)),
),
)
successful_pages.append(page_outcome)
except TimeoutError:
@@ -289,10 +295,14 @@ async def process_queued_job( # noqa: PLR0915
error=error,
started_at=started_at,
finished_at=finished_at,
duration_ms=max(
provider_duration_ms=max(
0,
int((asyncio.get_running_loop().time() - monotonic_started_at) * 1000),
),
processing_duration_ms=max(
_duration_ms_between(started_at, finished_at),
max(0, int((asyncio.get_running_loop().time() - monotonic_started_at) * 1000)),
),
request_manifest=provider.current_request_manifest,
transport_evidence=provider.current_transport_evidence,
failure_phase="local_timeout",
@@ -321,10 +331,14 @@ async def process_queued_job( # noqa: PLR0915
error=error,
started_at=started_at,
finished_at=finished_at,
duration_ms=max(
provider_duration_ms=max(
0,
int((asyncio.get_running_loop().time() - monotonic_started_at) * 1000),
),
processing_duration_ms=max(
_duration_ms_between(started_at, finished_at),
max(0, int((asyncio.get_running_loop().time() - monotonic_started_at) * 1000)),
),
request_manifest=(
result.request_manifest
if result is not None
@@ -573,10 +587,14 @@ async def _write_page_outcome(
model=result.model,
request_manifest=result.request_manifest,
quality_warnings=quality_warning_payload(analyze_transcription_quality(result.text)),
timing_breakdown={
"provider_call_duration_ms": page.provider_duration_ms,
"processing_duration_ms": page.processing_duration_ms,
},
transport_evidence=result.transport_evidence,
started_at=page.started_at,
finished_at=page.finished_at,
duration_ms=page.duration_ms,
duration_ms=page.provider_duration_ms,
session=session,
)
return
@@ -591,16 +609,24 @@ async def _write_page_outcome(
provider=page.provider,
model=page.model,
request_manifest=page.request_manifest,
timing_breakdown={
"provider_call_duration_ms": page.provider_duration_ms,
"processing_duration_ms": page.processing_duration_ms,
},
transport_evidence=page.transport_evidence,
failure_phase=page.failure_phase,
error_category=page.error.category.value,
started_at=page.started_at,
finished_at=page.finished_at,
duration_ms=page.duration_ms,
duration_ms=page.provider_duration_ms,
session=session,
)
def _duration_ms_between(started_at: datetime, finished_at: datetime) -> int:
return max(0, int((finished_at - started_at).total_seconds() * 1000))
def _validate_transcription_quality(*, result: TranscriptionResult, settings: Settings) -> None:
text_chars = len(result.text)
text_lines = _line_count(result.text)
@@ -72,3 +72,36 @@ def resolve_media_url(path: str | None, *, upload_dir: Path, base_url: str) -> s
return absolute_upload_url(f"/uploads/{quote(normalized)}", base_url=base_url)
return absolute_upload_url(f"/uploads/{quote(path_obj.name)}", base_url=base_url)
def public_media_path_label(path: str | None, *, upload_dir: Path) -> str:
"""Return a safe, non-local path label for UI metadata display."""
candidate = (path or "").strip()
if not candidate:
return "unknown"
normalized = candidate.replace("\\", "/")
lowered = normalized.casefold()
if normalized.startswith(_UPLOAD_ROUTE_PREFIX):
return normalized
if lowered.startswith("uploads/"):
return f"/{normalized}"
if lowered.startswith("data/"):
relative = normalized.split("/", 1)[1] if "/" in normalized else ""
return f"/uploads/{relative}" if relative else "/uploads"
if lowered.startswith(("documents/", "persons/")):
return f"/uploads/{normalized}"
resolved_upload_dir = upload_dir.resolve()
path_obj = Path(candidate)
if path_obj.is_absolute():
absolute_candidate = path_obj.resolve()
try:
relative = absolute_candidate.relative_to(resolved_upload_dir).as_posix()
return f"/uploads/{relative}"
except ValueError:
# Never expose non-managed absolute filesystem paths.
return path_obj.name or "unknown"
return f"/uploads/{quote(path_obj.name)}"
+6 -3
View File
@@ -29,6 +29,7 @@ from transcription.ui.components.error_presenter import show_error
from transcription.ui.components.formatters import parse_uuid
from transcription.ui.components.guards import parsed_record_id
from transcription.ui.components.guards import render_record_not_found
from transcription.ui.components.media_urls import public_media_path_label
from transcription.ui.components.media_urls import resolve_media_url
from transcription.ui.components.primitives import destructive_button
from transcription.ui.components.primitives import render_empty_state
@@ -193,6 +194,7 @@ def register_page() -> None: # noqa: PLR0915
source=source,
latest_job_source=latest_job_source,
latest_attempt=latest_attempt,
settings=resolve_runtime_settings(request),
)
@ui.page("/sources/{source_id}/delete")
@@ -303,9 +305,10 @@ def _render_source_metadata_column(
source: Source,
latest_job_source: JobSource | None,
latest_attempt: LatestExecutionAttempt | None,
settings: Settings,
) -> None:
with ui.column().classes("col-span-12 lg:col-span-4 gap-4"):
_render_source_metadata_zone(source)
_render_source_metadata_zone(source, settings=settings)
_render_source_job_metadata_zone(
latest_job_source,
latest_attempt=latest_attempt,
@@ -313,14 +316,14 @@ def _render_source_metadata_column(
_render_source_revision_logistics_zone(source)
def _render_source_metadata_zone(source: Source) -> None:
def _render_source_metadata_zone(source: Source, *, settings: Settings) -> None:
with archival_card(title="Source Metadata"):
metadata_row("Upload Name:", source.upload_name)
metadata_row("Stored Filename:", source.filename)
metadata_row("Page Number:", str(source.page_number))
metadata_row("Document Name:", source.document_name or "Not set")
metadata_row("Document ID:", str(source.document_id))
metadata_row("Stored Path:", source.file_path)
metadata_row("Stored Path:", public_media_path_label(source.file_path, upload_dir=settings.upload_dir))
def _render_source_job_metadata_zone(