Claude 5e2094f2ad
feat(media): local inference sidecar for Media Studio
FastAPI service in infra/media-worker implementing every
MediaInferenceProvider operation behind HTTP API v1, plus capabilities.
Bearer auth, MEDIA_ROOT realpath enforcement, strict request schemas,
cancellation on disconnect, GPU/CPU gates with 503 overflow, input
size/duration limits, lazy SHA256-pinned model downloads, fail-closed
redaction with atomic output, offline pytest suite with fake backends,
CPU/CUDA/ROCm Dockerfile and a license review of every model.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0138YqDj3ttfayRwuMKKrqVj
2026-10-03 18:19:47 +00:00

63 lines
1.7 KiB
Python

"""Error envelope shared by every route: {"error": {"code", "message", "operation"}}."""
from __future__ import annotations
from fastapi.responses import JSONResponse
STATUS_BY_CODE = {
"bad_request": 400,
"invalid_path": 400,
"unauthorized": 401,
"too_large": 413,
"unsupported": 501,
"busy": 503,
"internal": 500,
}
class MediaError(Exception):
def __init__(self, code: str, message: str, operation: str | None = None) -> None:
super().__init__(message)
self.code = code
self.message = message
self.operation = operation
@property
def status(self) -> int:
return STATUS_BY_CODE[self.code]
class Unsupported(MediaError):
def __init__(self, reason: str, operation: str | None = None) -> None:
super().__init__("unsupported", reason, operation)
class InvalidPath(MediaError):
def __init__(self, message: str = "path is outside MEDIA_ROOT") -> None:
super().__init__("invalid_path", message)
class BadRequest(MediaError):
def __init__(self, message: str) -> None:
super().__init__("bad_request", message)
class Busy(MediaError):
def __init__(self) -> None:
super().__init__("busy", "inference queue is full")
class Cancelled(Exception):
"""The client went away or the request ran past MEDIA_MAX_SECONDS."""
def __init__(self, reason: str = "client disconnected") -> None:
super().__init__(reason)
self.reason = reason
def error_response(code: str, message: str, operation: str | None, status: int | None = None) -> JSONResponse:
return JSONResponse(
status_code=status or STATUS_BY_CODE[code],
content={"error": {"code": code, "message": message, "operation": operation}},
)