Skip to content

ump_sdk.service

module ump-sdk

FastAPI app factory shared by every UMP POC service.

create_app(name, version) gives you:

  • CORS for the web front end (http://localhost:5173 and 127.0.0.1:5173);
  • GET /healthz and GET /v1/meta;
  • a UMP-Spec-Version header (and X-UMP-Service) on every response;
  • RFC 9457 application/problem+json for UmpError, upstream ProblemError, validation errors (422 SCHEMA_INVALID with a JSON pointer per issue), HTTPException and crashes;
  • get_principal dependency reading X-UMP-Principal (default user:demo). On GET requests only, a ?principal= query parameter is accepted when the header is absent, because browser EventSource (SSE) cannot send headers. Writes always need the header.

class

class Principal

Fields

NameTypeDefaultDescription
idstr
kindstr
explicitbool
Methods and properties (3)

is_agent property

is_agent: bool

is_human property

is_human: bool

init constructor

Principal(self, id: str, kind: str, explicit: bool) -> None

Source: packages/ump-sdk/src/ump_sdk/service.py, line 51

function

def get_principal(request: Request) -> Principal

FastAPI dependency: principal: Principal = Depends(get_principal).

Header X-UMP-Principal wins. On GET requests a ?principal= query parameter is the fallback (EventSource cannot set headers). Neither present means user:demo with explicit=False.

Source: packages/ump-sdk/src/ump_sdk/service.py, line 66

function

def is_json_content_type(request: Request) -> bool

Source: packages/ump-sdk/src/ump_sdk/service.py, line 81

function

async def read_json_body(request: Request) -> Any

Raw JSON body for routes that validate with ump_spec models themselves.

A non-empty body must be sent as Content-Type: application/json (or +json); otherwise 415 UNSUPPORTED_MEDIA_TYPE. Parsing text/plain would let any web page write through a CORS “simple request”, which skips the preflight that the CORS allow-list blocks. An empty body reads as None. Duplicate member names and non-finite numbers are refused with 422 SCHEMA_INVALID (spec §18 canonical form).

Source: packages/ump-sdk/src/ump_sdk/service.py, line 86

function

def set_gate_mode_echo(
request: Request,
effective: dict[str, GateFailMode] | GateFailMode,
) -> None

Record the mode a route applied; create_app echoes it in UMP-On-Gate-Fail (spec §19).

Source: packages/ump-sdk/src/ump_sdk/service.py, line 141

function

def get_gate_mode(request: Request) -> GateFailMode

FastAPI dependency: the UMP-On-Gate-Fail header (X-UMP-On-Gate-Fail is the deprecated spelling), else fallback. Any other value is 422 SCHEMA_INVALID. The mode is echoed on the response unless the route records a stricter effective mode with set_gate_mode_echo.

Source: packages/ump-sdk/src/ump_sdk/service.py, line 146

function

def request_gate_mode(
request: Request,
body_value: Any,
) -> dict[str, GateFailMode] | None

The request’s on_gate_fail from the body member or the header (spec §19), as a per-gate map; None when the request names none. Both present and different is 422 GATE_MODE_CONFLICT.

Source: packages/ump-sdk/src/ump_sdk/service.py, line 156

function

def accept_profile(request: Request) -> None

Spec §21 Negotiation: when Accept names a profile (application/json; profile="<base>/spec/2.3"), at least one media range must fall in a supported MAJOR.MINOR range, else 406 SPEC_VERSION_NOT_ACCEPTABLE listing the ranges. An Accept without a profile is not negotiated.

Source: packages/ump-sdk/src/ump_sdk/service.py, line 169

function

def problem_response(
problem: Problem,
headers: dict[str, str] | None = None,
) -> JSONResponse

Source: packages/ump-sdk/src/ump_sdk/service.py, line 188

function

def error_response(exc: UmpError, request: Request | None = None) -> JSONResponse

Source: packages/ump-sdk/src/ump_sdk/service.py, line 193

function

def install_problem_handlers(app: FastAPI) -> None

Source: packages/ump-sdk/src/ump_sdk/service.py, line 205

function

def create_app(
name: str,
version: str = '0.1.0',
*,
description: str = '',
lifespan: Callable[..., Any] | None = None,
stub: bool = False,
meta: dict[str, Any] | None = None,
conformance_classes: list[str] | None = None,
) -> FastAPI

Build a FastAPI app with the UMP conventions. name should be a key of ports.PORTS.

conformance_classes are the spec §2 classes the service implements (Producer, Consumer, Engine adapter, Gate engine, Agent host, Log verifier, Hosted endpoint). /v1/meta lists them with conformance_proven: false: the POC has no conformance suite, so none is a claim.

Source: packages/ump-sdk/src/ump_sdk/service.py, line 238

function

def mount_unavailable(
app: FastAPI,
operations: Mapping[str, str],
*,
engine: str,
reason: str | None = None,
) -> list[str]

Spec §8 engine profile: mount POST /v1/<op> answering 501 OPERATION_UNAVAILABLE for every ADAPTER_OPERATIONS request that operations (the adapter’s metadata) does not mark built_in, unless the app already routes that path. The problem’s suggested_action names the nearest working operation (ump_spec.adapter.unavailable_suggestion); reason is added to the detail. Returns the requests mounted, in contract order. Call it after the app’s own routes.

Source: packages/ump-sdk/src/ump_sdk/service.py, line 297

function

def run(
app: FastAPI | str,
name: str,
*,
host: str = '127.0.0.1',
reload: bool = False,
) -> None

python -m <pkg> helper: serve on the registry port for name.

Source: packages/ump-sdk/src/ump_sdk/service.py, line 336

NameTypeValueDescription
PROBLEM_JSON'application/problem+json'
DEFAULT_PRINCIPAL'user:demo'
PRINCIPAL_QUERY_PARAM'principal'Query fallback for the identity stub, honoured on GET only (SSE via EventSource).
CORS_ORIGINS['http://localhost:5173', 'http://127.0.0.1:5173']
HTTP_STATUS_CODESdict[int, str]{400: 'SCHEMA_INVALID', 401: 'UNAUTHORIZED', 403: 'FORBIDDEN', 404: 'NOT_FOUN...Framework HTTP errors to UMP codes; the response then carries the code’s registered status (spec §19).