Skip to content

symfonic.agent.fastapi.transport.frames

frames

Transport-neutral values the platform's outermost layer speaks (TRN-3).

Nothing in this module mentions a web framework: an ErrorFrame is what an ErrorMapper returns, and it is equally an HTTP response, an SSE error event, or a CLI exit code. That is the whole point of the port — a second host maps these values, it does not re-derive them.

DetailMode

Bases: Enum

Where a frame's detail string comes from (EMAP-7).

Server-side faults never echo their own message: an operator-side configuration error names hosts, DSNs, and occasionally credentials, and the person who needs that text is reading the log, not the socket.

ErrorFrame dataclass

ErrorFrame(status: int, detail: str, code: str | None = None, headers: Mapping[str, str] | None = None)

The mapped result: status, detail, machine-readable code, headers.

payload

payload() -> dict[str, object]

The SSE/JSON body shape (STR-3).

status is always present; code appears only when the error carried one, so a client that reads detail sees exactly what it saw before and a client that wants to branch finally can.

Source code in src/symfonic/agent/fastapi/transport/frames.py
def payload(self) -> dict[str, object]:
    """The SSE/JSON body shape (STR-3).

    ``status`` is always present; ``code`` appears only when the error
    carried one, so a client that reads ``detail`` sees exactly what it saw
    before and a client that wants to branch finally can.
    """
    body: dict[str, object] = {"detail": self.detail, "status": self.status}
    if self.code is not None:
        body["code"] = self.code
    return body

ErrorPolicy dataclass

ErrorPolicy(name: str, fallback_status: int, opaque_detail: str, collapse_server_errors: bool = False)

An entry point's declared difference from every other one (EMAP-3).

The four invocation entry points differ in exactly one way — what an unmapped failure becomes — and that difference is a value passed to the mapper, not a fifth copy of the mapping.

StatusRule dataclass

StatusRule(active: int, target: int | None = None, detail_mode: DetailMode = DetailMode.MESSAGE, detail_override: str | None = None, headers: Mapping[str, str] | None = None)

One row of the EMAP-4 table.

target is the status the row should carry and does not yet: three classes deserve 409/503/504 and surface as 500 today, and flipping them is a tier-1 behaviour change that COMPAT-POL puts at MAJOR (EMAP-5). Carrying the target in the table is what makes the eventual flip a scheduled change rather than a surprise, and what lets a test assert the intent today.