ump_spec.adapter
module ump-spec
Engine adapter contract (POC blueprint “Engine adapter contract”).
Every adapter is a FastAPI process that serves:
========== ======================== =============================== ========================= Method Path Request Response ========== ======================== =============================== ========================= GET /v1/metadata (none) AdapterMetadata POST /v1/fit FitRequest FitResponse (or 202 JobAccepted) POST /v1/score ScoreRequest ScoreResponse POST /v1/response_curve ResponseCurveRequest ResponseCurveResponse POST /v1/marginal_return MarginalReturnRequest MarginalReturnResponse POST /v1/decompose DecomposeRequest DecomposeResponse POST /v1/optimize OptimizeRequest OptimizeResponse POST /v1/calibrate CalibrateRequest CalibrateResponse POST /v1/update UpdateRequest UpdateResponse ========== ======================== =============================== =========================
Spec §8 engine profile: AdapterMetadata.operations names every request in ADAPTER_OPERATIONS
(fit plus the seven standard requests); a missing one reads as unavailable. A request the manifest
marks unavailable or computed_by_ump answers 501 with problem code OPERATION_UNAVAILABLE and a
suggested_action naming the nearest working operation (unavailable_suggestion); the models app
computes computed_by_ump requests in UMP from the curves. ump_sdk.mount_unavailable mounts those routes.
Every response extends AdapterResponse: engine, engine_version, stub, estimand
blocks, intervals (via ShownNumber), dependence, fallbacks[] and notes[] (e.g.
ENGINE_STUB). Response curves travel as per-draw grids draws[draw][channel][grid].
Classes
Section titled “Classes”AdapterMetadata
Section titled “AdapterMetadata”class
class AdapterMetadata(StrictModel)Fields
| Name | Type | Default | Description |
|---|---|---|---|
engine | str | ||
engine_version | str | ||
adapter_version | str | '0.1.0' | |
spec_version | str | SPEC_VERSION | |
stub | bool | False | |
family | ModelFamily | 'media_mix' | |
tiers_produced | list[Tier] | ||
operations | dict[str, OperationSupport] | fit/score/response_curve/marginal_return/decompose/optimize/calibrate/update -> support. Every one is named: a request left out reads as unavailable (spec §8). | |
inference | str | 'custom' | |
representation | str | 'draws' | |
dependence | DependenceDeclaration | ||
deterministic_given_seed | bool | True | |
estimands_supported | dict[str, Estimand] | dict() | |
role | Literal['candidate', 'negative_control', 'positive_control'] | 'candidate' | |
adapter_kind | AdapterKind | None | None | model | experiment | planner | negative_control. None means infer it (see infer_adapter_kind). Experiments are not a model family. |
licence | str | None | None | |
notes | list[Note] | list() | |
supported_spec_versions | list[str] | None | None | Spec §21 negotiation: the spec versions this adapter reads and writes. |
Source: packages/ump-spec/src/ump_spec/adapter.py, line 119
AdapterResponse
Section titled “AdapterResponse”class
class AdapterResponse(StrictModel)Fields
| Name | Type | Default | Description |
|---|---|---|---|
engine | str | ||
engine_version | str | ||
stub | bool | False | |
estimands | dict[str, Estimand] | dict() | |
dependence | DependenceDeclaration | None | None | |
fallbacks | list[FallbackEntry] | list() | |
notes | list[Note] | list() | |
run | RunRecord | None | None |
Source: packages/ump-spec/src/ump_spec/adapter.py, line 153
SeriesData
Section titled “SeriesData”class
class SeriesData(StrictModel)Inline weekly data for the POC (small). Long jobs may pass dataset_ref instead.
Fields
| Name | Type | Default | Description |
|---|---|---|---|
dates | list[str] | ||
kpi | list[float] | ||
spend | dict[str, list[float]] | ||
controls | dict[str, list[float]] | dict() | |
geo | str | None | None |
Source: packages/ump-spec/src/ump_spec/adapter.py, line 164
FitRequest
Section titled “FitRequest”class
class FitRequest(StrictModel)Fields
| Name | Type | Default | Description |
|---|---|---|---|
client_id | str | None | None | |
dataset_ref | str | None | None | |
data | SeriesData | None | None | |
kpi | str | 'revenue' | |
currency | str | 'USD' | |
config | dict[str, Any] | dict() | Engine-specific settings. |
n_draws | int | 200 | |
seed | int | 0 | |
async_ | bool | False | |
truth | dict[str, Any] | None | None | Known truth for identification checks. |
retain_data | bool | True | False when the client allows no copies of the rows (data catalogue copy_policy transient_only or aggregates_only). The adapter MAY hold the rows in memory for this fit but MUST NOT write them to disk; persisted fit state keeps curves, draws and aggregates only (POC DATA-CATALOGUE section 4). |
Source: packages/ump-spec/src/ump_spec/adapter.py, line 184
ResponseCurves
Section titled “ResponseCurves”class
class ResponseCurves(StrictModel)Per-draw grids: draws[d][c][g] = KPI contribution of channel c at spend_grid[c][g].
Fields
| Name | Type | Default | Description |
|---|---|---|---|
channels | list[str] | ||
multipliers | list[float] | Grid as multiples of current spend, e.g. 0..2.5. | |
spend_grid | list[list[float]] | [channel][grid] spend levels. | |
draws | list[list[list[float]]] | [draw][channel][grid] contributions. | |
current_spend | dict[str, float] | ||
max_seen_spend | dict[str, float] | dict() | |
period | Literal['per_week', 'per_flight', 'annual'] | 'annual' | |
estimand | Estimand |
Source: packages/ump-spec/src/ump_spec/adapter.py, line 207
ChannelSummary
Section titled “ChannelSummary”class
class ChannelSummary(StrictModel)Fields
| Name | Type | Default | Description |
|---|---|---|---|
channel | str | ||
spend | float | ||
contribution | ShownNumber | ||
roi | ShownNumber | ||
mroi | ShownNumber | None | None |
Source: packages/ump-spec/src/ump_spec/adapter.py, line 230
FitResponse
Section titled “FitResponse”class
class FitResponse(AdapterResponse)Fields
| Name | Type | Default | Description |
|---|---|---|---|
fit_id | str | Adapter-local handle used by score/curve/optimize calls. | |
tier | Tier | 'A' | |
manifest | ModelManifest | ||
curves | ResponseCurves | None | None | |
summaries | list[ChannelSummary] | list() | |
diagnostics | dict[str, Any] | dict() | rhat, ess, r2, mape, … |
truth_recovery | dict[str, Any] | None | None |
Source: packages/ump-spec/src/ump_spec/adapter.py, line 238
JobAccepted
Section titled “JobAccepted”class
class JobAccepted(StrictModel)The body of a 202 Accepted (spec §19); the response also carries Location: <status_url>.
Fields
| Name | Type | Default | Description |
|---|---|---|---|
job_id | str | ||
state | str | 'queued' | |
status_url | str |
Source: packages/ump-spec/src/ump_spec/adapter.py, line 248
ScoreRequest
Section titled “ScoreRequest”class
class ScoreRequest(StrictModel)Fields
| Name | Type | Default | Description |
|---|---|---|---|
fit_id | str | ||
plan | list[PlanLine] | ||
n_draws | int | None | None | |
seed | int | 0 | |
crn_seed | int | None | None | |
estimand | Estimand | None | None | G-EST-02 re-ask rung: the estimand the caller wants answered. Adapters that can re-summarise answer with it; others ignore it, answer with their own estimand and add the note REASK_NOT_HONOURED, and the caller aligns as before. |
Source: packages/ump-spec/src/ump_spec/adapter.py, line 256
ScoreResponse
Section titled “ScoreResponse”class
class ScoreResponse(AdapterResponse)Fields
| Name | Type | Default | Description |
|---|---|---|---|
kpi | ShownNumber | ||
per_channel | list[ChannelResult] | list() | |
draws | list[float] | None | None | Total KPI per draw (optional). |
Source: packages/ump-spec/src/ump_spec/adapter.py, line 268
ResponseCurveRequest
Section titled “ResponseCurveRequest”class
class ResponseCurveRequest(StrictModel)Fields
| Name | Type | Default | Description |
|---|---|---|---|
fit_id | str | ||
channels | list[str] | None | None | |
multipliers | list[float] | None | None | |
estimand | Estimand | None | None | G-EST-02 re-ask rung: the estimand the caller wants answered. Adapters that can re-summarise answer with it; others ignore it, answer with their own estimand and add the note REASK_NOT_HONOURED, and the caller aligns as before. |
Source: packages/ump-spec/src/ump_spec/adapter.py, line 274
ResponseCurveResponse
Section titled “ResponseCurveResponse”class
class ResponseCurveResponse(AdapterResponse)Fields
| Name | Type | Default | Description |
|---|---|---|---|
curves | ResponseCurves |
Source: packages/ump-spec/src/ump_spec/adapter.py, line 284
MarginalReturnRequest
Section titled “MarginalReturnRequest”class
class MarginalReturnRequest(StrictModel)Fields
| Name | Type | Default | Description |
|---|---|---|---|
fit_id | str | ||
at_spend | dict[str, float] | None | None | |
delta_rel | float | 0.1 | +10% matches E-BM-MARG-ROI. |
estimand | Estimand | None | None | G-EST-02 re-ask rung: the estimand the caller wants answered. Adapters that can re-summarise answer with it; others ignore it, answer with their own estimand and add the note REASK_NOT_HONOURED, and the caller aligns as before. |
Source: packages/ump-spec/src/ump_spec/adapter.py, line 288
MarginalReturnResponse
Section titled “MarginalReturnResponse”class
class MarginalReturnResponse(AdapterResponse)Fields
| Name | Type | Default | Description |
|---|---|---|---|
per_channel | list[ChannelSummary] |
Source: packages/ump-spec/src/ump_spec/adapter.py, line 298
DecomposeRequest
Section titled “DecomposeRequest”class
class DecomposeRequest(StrictModel)Fields
| Name | Type | Default | Description |
|---|---|---|---|
fit_id | str | ||
estimand | Estimand | None | None | G-EST-02 re-ask rung: the estimand the caller wants answered. Adapters that can re-summarise answer with it; others ignore it, answer with their own estimand and add the note REASK_NOT_HONOURED, and the caller aligns as before. |
Source: packages/ump-spec/src/ump_spec/adapter.py, line 302
DecomposeResponse
Section titled “DecomposeResponse”class
class DecomposeResponse(AdapterResponse)Fields
| Name | Type | Default | Description |
|---|---|---|---|
baseline | ShownNumber | None | None | |
contributions | list[ChannelSummary] |
Source: packages/ump-spec/src/ump_spec/adapter.py, line 310
OptimizeRequest
Section titled “OptimizeRequest”class
class OptimizeRequest(StrictModel)Fields
| Name | Type | Default | Description |
|---|---|---|---|
fit_id | str | ||
total_budget | float | ||
objective | Literal['mean', 'p10', 'cvar5'] | 'mean' | |
bounds | dict[str, tuple[float, float]] | dict() | channel -> (min, max) spend |
n_draws | int | None | None | |
seed | int | 0 |
Source: packages/ump-spec/src/ump_spec/adapter.py, line 315
OptimizeResponse
Section titled “OptimizeResponse”class
class OptimizeResponse(AdapterResponse)Fields
| Name | Type | Default | Description |
|---|---|---|---|
result | OptimisationResult |
Source: packages/ump-spec/src/ump_spec/adapter.py, line 324
CalibrateRequest
Section titled “CalibrateRequest”class
class CalibrateRequest(StrictModel)Spec §8 calibrate: fold experiment results into a fit. Name the results by artifact ID or inline.
Fields
| Name | Type | Default | Description |
|---|---|---|---|
fit_id | str | ||
test_result_ids | list[str] | list() | |
test_results | list[dict[str, Any]] | list() | Inline test_result docs. |
route | str | None | None | Calibration route R1..R6 (spec §10) when the caller has chosen one. |
target_estimand | Estimand | None | None | The estimand being calibrated (spec §10; G-EXP-05 judges class E admissibility on it). |
seed | int | 0 |
Source: packages/ump-spec/src/ump_spec/adapter.py, line 328
CalibrateResponse
Section titled “CalibrateResponse”class
class CalibrateResponse(AdapterResponse)Fields
| Name | Type | Default | Description |
|---|---|---|---|
fit_id | str | Handle of the calibrated fit. | |
source_fit_id | str | ||
method | Literal['refit', 'reweight', 'curve_adjust', 'attach_only'] | ||
diagnostics | dict[str, Any] | dict() |
Source: packages/ump-spec/src/ump_spec/adapter.py, line 348
UpdateRequest
Section titled “UpdateRequest”class
class UpdateRequest(StrictModel)Spec §8 update: refresh a fit with new data (Tier A; other tiers are marked stale instead).
Fields
| Name | Type | Default | Description |
|---|---|---|---|
fit_id | str | ||
dataset_ref | str | None | None | |
data | SeriesData | None | None | |
seed | int | 0 | |
retain_data | bool | True |
Source: packages/ump-spec/src/ump_spec/adapter.py, line 355
UpdateResponse
Section titled “UpdateResponse”class
class UpdateResponse(AdapterResponse)Fields
| Name | Type | Default | Description |
|---|---|---|---|
fit_id | str | Handle of the updated fit. | |
source_fit_id | str | ||
manifest | ModelManifest | None | None | |
diagnostics | dict[str, Any] | dict() |
Source: packages/ump-spec/src/ump_spec/adapter.py, line 371
Functions
Section titled “Functions”complete_operations
Section titled “complete_operations”function
def complete_operations(operations: Mapping[str, str]) -> dict[str, str]Every ADAPTER_OPERATIONS request named; one the engine leaves out is unavailable. Family-profile
requests the engine declares (index, track, …) are kept after them.
Source: packages/ump-spec/src/ump_spec/adapter.py, line 88
nearest_operation
Section titled “nearest_operation”function
def nearest_operation(op: str, operations: Mapping[str, str]) -> str | NoneThe first operation in NEAREST_OPERATIONS[op] the engine builds in, else None.
Source: packages/ump-spec/src/ump_spec/adapter.py, line 96
unavailable_suggestion
Section titled “unavailable_suggestion”function
def unavailable_suggestion(op: str, operations: Mapping[str, str]) -> strThe suggested_action of a 501 OPERATION_UNAVAILABLE for op: always a concrete call.
Source: packages/ump-spec/src/ump_spec/adapter.py, line 101
infer_adapter_kind
Section titled “infer_adapter_kind”function
def infer_adapter_kind( meta: AdapterMetadata | dict[str, Any], hint: str | None = None,) -> stradapter_kind when declared, else a best guess from role, tiers and operations.
Order: declared adapter_kind; role == "negative_control"; hint (a registry seed);
no tiers produced plus optimize built in means planner; no tiers produced otherwise means
experiment; anything else is model.
Source: packages/ump-spec/src/ump_spec/adapter.py, line 378
Constants
Section titled “Constants”| Name | Type | Value | Description |
|---|---|---|---|
AdapterKind | Literal['model', 'experiment', 'planner', 'negative_control'] | ||
ADAPTER_KINDS | tuple[str, ...] | ('model', 'experiment', 'planner', 'negative_control') | What an adapter does, separate from family (which is always a model family). |
ADAPTER_OPERATIONS | tuple[str, ...] | ('fit', *STANDARD_REQUESTS) | fit plus the seven standard requests of spec §8, in that order. |
ADAPTER_PATHS | dict[str, tuple[str, str]] | {'metadata': ('GET', '/v1/metadata'), 'fit': ('POST', '/v1/fit'), 'score': ('... | |
NEAREST_OPERATIONS | dict[str, tuple[str, ...]] | {'fit': (), 'score': ('response_curve', 'marginal_return', 'optimize'), 'resp... | For each request, the operations that answer the nearest question, best first (spec §8: a 501 names the nearest working operation). A POC ordering, not a spec table. |