Skip to content

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].

class

class AdapterMetadata(StrictModel)

Fields

NameTypeDefaultDescription
enginestr
engine_versionstr
adapter_versionstr'0.1.0'
spec_versionstrSPEC_VERSION
stubboolFalse
familyModelFamily'media_mix'
tiers_producedlist[Tier]
operationsdict[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).
inferencestr'custom'
representationstr'draws'
dependenceDependenceDeclaration
deterministic_given_seedboolTrue
estimands_supporteddict[str, Estimand]dict()
roleLiteral['candidate', 'negative_control', 'positive_control']'candidate'
adapter_kindAdapterKind | NoneNonemodel | experiment | planner | negative_control. None means infer it (see infer_adapter_kind). Experiments are not a model family.
licencestr | NoneNone
noteslist[Note]list()
supported_spec_versionslist[str] | NoneNoneSpec §21 negotiation: the spec versions this adapter reads and writes.

Source: packages/ump-spec/src/ump_spec/adapter.py, line 119

class

class AdapterResponse(StrictModel)

Fields

NameTypeDefaultDescription
enginestr
engine_versionstr
stubboolFalse
estimandsdict[str, Estimand]dict()
dependenceDependenceDeclaration | NoneNone
fallbackslist[FallbackEntry]list()
noteslist[Note]list()
runRunRecord | NoneNone

Source: packages/ump-spec/src/ump_spec/adapter.py, line 153

class

class SeriesData(StrictModel)

Inline weekly data for the POC (small). Long jobs may pass dataset_ref instead.

Fields

NameTypeDefaultDescription
dateslist[str]
kpilist[float]
spenddict[str, list[float]]
controlsdict[str, list[float]]dict()
geostr | NoneNone

Source: packages/ump-spec/src/ump_spec/adapter.py, line 164

class

class FitRequest(StrictModel)

Fields

NameTypeDefaultDescription
client_idstr | NoneNone
dataset_refstr | NoneNone
dataSeriesData | NoneNone
kpistr'revenue'
currencystr'USD'
configdict[str, Any]dict()Engine-specific settings.
n_drawsint200
seedint0
async_boolFalse
truthdict[str, Any] | NoneNoneKnown truth for identification checks.
retain_databoolTrueFalse 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

class

class ResponseCurves(StrictModel)

Per-draw grids: draws[d][c][g] = KPI contribution of channel c at spend_grid[c][g].

Fields

NameTypeDefaultDescription
channelslist[str]
multiplierslist[float]Grid as multiples of current spend, e.g. 0..2.5.
spend_gridlist[list[float]][channel][grid] spend levels.
drawslist[list[list[float]]][draw][channel][grid] contributions.
current_spenddict[str, float]
max_seen_spenddict[str, float]dict()
periodLiteral['per_week', 'per_flight', 'annual']'annual'
estimandEstimand

Source: packages/ump-spec/src/ump_spec/adapter.py, line 207

class

class ChannelSummary(StrictModel)

Fields

NameTypeDefaultDescription
channelstr
spendfloat
contributionShownNumber
roiShownNumber
mroiShownNumber | NoneNone

Source: packages/ump-spec/src/ump_spec/adapter.py, line 230

class

class FitResponse(AdapterResponse)

Fields

NameTypeDefaultDescription
fit_idstrAdapter-local handle used by score/curve/optimize calls.
tierTier'A'
manifestModelManifest
curvesResponseCurves | NoneNone
summarieslist[ChannelSummary]list()
diagnosticsdict[str, Any]dict()rhat, ess, r2, mape, …
truth_recoverydict[str, Any] | NoneNone

Source: packages/ump-spec/src/ump_spec/adapter.py, line 238

class

class JobAccepted(StrictModel)

The body of a 202 Accepted (spec §19); the response also carries Location: <status_url>.

Fields

NameTypeDefaultDescription
job_idstr
statestr'queued'
status_urlstr

Source: packages/ump-spec/src/ump_spec/adapter.py, line 248

class

class ScoreRequest(StrictModel)

Fields

NameTypeDefaultDescription
fit_idstr
planlist[PlanLine]
n_drawsint | NoneNone
seedint0
crn_seedint | NoneNone
estimandEstimand | NoneNoneG-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

class

class ScoreResponse(AdapterResponse)

Fields

NameTypeDefaultDescription
kpiShownNumber
per_channellist[ChannelResult]list()
drawslist[float] | NoneNoneTotal KPI per draw (optional).

Source: packages/ump-spec/src/ump_spec/adapter.py, line 268

class

class ResponseCurveRequest(StrictModel)

Fields

NameTypeDefaultDescription
fit_idstr
channelslist[str] | NoneNone
multiplierslist[float] | NoneNone
estimandEstimand | NoneNoneG-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

class

class ResponseCurveResponse(AdapterResponse)

Fields

NameTypeDefaultDescription
curvesResponseCurves

Source: packages/ump-spec/src/ump_spec/adapter.py, line 284

class

class MarginalReturnRequest(StrictModel)

Fields

NameTypeDefaultDescription
fit_idstr
at_spenddict[str, float] | NoneNone
delta_relfloat0.1+10% matches E-BM-MARG-ROI.
estimandEstimand | NoneNoneG-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

class

class MarginalReturnResponse(AdapterResponse)

Fields

NameTypeDefaultDescription
per_channellist[ChannelSummary]

Source: packages/ump-spec/src/ump_spec/adapter.py, line 298

class

class DecomposeRequest(StrictModel)

Fields

NameTypeDefaultDescription
fit_idstr
estimandEstimand | NoneNoneG-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

class

class DecomposeResponse(AdapterResponse)

Fields

NameTypeDefaultDescription
baselineShownNumber | NoneNone
contributionslist[ChannelSummary]

Source: packages/ump-spec/src/ump_spec/adapter.py, line 310

class

class OptimizeRequest(StrictModel)

Fields

NameTypeDefaultDescription
fit_idstr
total_budgetfloat
objectiveLiteral['mean', 'p10', 'cvar5']'mean'
boundsdict[str, tuple[float, float]]dict()channel -> (min, max) spend
n_drawsint | NoneNone
seedint0

Source: packages/ump-spec/src/ump_spec/adapter.py, line 315

class

class OptimizeResponse(AdapterResponse)

Fields

NameTypeDefaultDescription
resultOptimisationResult

Source: packages/ump-spec/src/ump_spec/adapter.py, line 324

class

class CalibrateRequest(StrictModel)

Spec §8 calibrate: fold experiment results into a fit. Name the results by artifact ID or inline.

Fields

NameTypeDefaultDescription
fit_idstr
test_result_idslist[str]list()
test_resultslist[dict[str, Any]]list()Inline test_result docs.
routestr | NoneNoneCalibration route R1..R6 (spec §10) when the caller has chosen one.
target_estimandEstimand | NoneNoneThe estimand being calibrated (spec §10; G-EXP-05 judges class E admissibility on it).
seedint0

Source: packages/ump-spec/src/ump_spec/adapter.py, line 328

class

class CalibrateResponse(AdapterResponse)

Fields

NameTypeDefaultDescription
fit_idstrHandle of the calibrated fit.
source_fit_idstr
methodLiteral['refit', 'reweight', 'curve_adjust', 'attach_only']
diagnosticsdict[str, Any]dict()

Source: packages/ump-spec/src/ump_spec/adapter.py, line 348

class

class UpdateRequest(StrictModel)

Spec §8 update: refresh a fit with new data (Tier A; other tiers are marked stale instead).

Fields

NameTypeDefaultDescription
fit_idstr
dataset_refstr | NoneNone
dataSeriesData | NoneNone
seedint0
retain_databoolTrue

Source: packages/ump-spec/src/ump_spec/adapter.py, line 355

class

class UpdateResponse(AdapterResponse)

Fields

NameTypeDefaultDescription
fit_idstrHandle of the updated fit.
source_fit_idstr
manifestModelManifest | NoneNone
diagnosticsdict[str, Any]dict()

Source: packages/ump-spec/src/ump_spec/adapter.py, line 371

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

function

def nearest_operation(op: str, operations: Mapping[str, str]) -> str | None

The first operation in NEAREST_OPERATIONS[op] the engine builds in, else None.

Source: packages/ump-spec/src/ump_spec/adapter.py, line 96

function

def unavailable_suggestion(op: str, operations: Mapping[str, str]) -> str

The suggested_action of a 501 OPERATION_UNAVAILABLE for op: always a concrete call.

Source: packages/ump-spec/src/ump_spec/adapter.py, line 101

function

def infer_adapter_kind(
meta: AdapterMetadata | dict[str, Any],
hint: str | None = None,
) -> str

adapter_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

NameTypeValueDescription
AdapterKindLiteral['model', 'experiment', 'planner', 'negative_control']
ADAPTER_KINDStuple[str, ...]('model', 'experiment', 'planner', 'negative_control')What an adapter does, separate from family (which is always a model family).
ADAPTER_OPERATIONStuple[str, ...]('fit', *STANDARD_REQUESTS)fit plus the seven standard requests of spec §8, in that order.
ADAPTER_PATHSdict[str, tuple[str, str]]{'metadata': ('GET', '/v1/metadata'), 'fit': ('POST', '/v1/fit'), 'score': ('...
NEAREST_OPERATIONSdict[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.