ump-workflow
Sharing models import ump_workflow as uw
A workflow bundle is a folder of data and source, never pickles. Someone else can read the code, trust it, and run your saved fit on new weeks.
Here is the case it was built for. An analyst fits a weekly sales model in a notebook with her own plain functions and saves it as a bundle. Next quarter a colleague on another laptop opens the folder, reads the code, trusts it, and scores eight new weeks. When the new file skips two weeks, or TV spend runs past anything the fit saw, the run says so and refits with the saved seed. If he never trusts the code, nothing is imported, and he still gets the fit-time numbers from the bundle’s summary, labelled as such.
Most analysts reach it through babelfin’s fin.Workflow. This package is the runtime underneath. It depends on ump-spec, finprint, numpy and pandas, and never on a modelling library.
Quick path
Section titled “Quick path”- Install:
pip install -e 'packages/ump-workflow'(add theserveextra to serve bundles). - Write a module with plain functions:
fit,to_state,from_stateand one function per question. - Save, open, run.
import numpy as np
def fit(df, *, seed, params): ... # returns any model object, e.g. bootstrap draws of coefficients
def to_state(model): return {"n": model["n"]}, {"beta": model["beta"]} # JSON-safe meta, plain arrays
def from_state(meta, arrays): return {"beta": np.asarray(arrays["beta"]), "n": int(meta["n"])}
def tv_roi(model, df): return model["beta"][:, 1] # one value per drawimport ump_workflow as uw
c = uw.contract_from_frame(train, time="week", target="sales", freq="W-SUN")path = uw.save_workflow("out/weekly_sales", code_path="sales_model.py", data=train, contract=c, questions=questions, name="Weekly sales", seed=11, params={"draws": 200}, author="user:sam.patel")
b = uw.open_bundle(path) # hashes checked, data parsed, nothing runb.code # read it firstb.trust() # then trust its hashres = b.run(new_df) # scores by defaultres.route, res.source, res.answers["tv_roi"], res.notesEach question is a dict with name, ask, function and a seven-field estimand template, so every answer comes back with its estimand and an interval taken from the quantiles of the draws.
Main entry points
Section titled “Main entry points”| Call | What it does |
|---|---|
uw.contract_from_frame(df, ...) | The input contract: the shape new data must have. Category levels are stored as hashes, never values. |
uw.save_workflow(path, ...) | Fits, checks that the state round-trips to the same draws, computes fit-time answers and a summary, then writes. Nothing is written on a refusal. |
uw.open_bundle(path) | Verifies every file hash before reading anything. Runs nothing. |
OpenBundle.run(df) | Scores new data, or refits when the data calls for it, and labels which happened. |
OpenBundle.check(df) | The contract check alone. |
ump_workflow.serve.build_app(path) | Puts one bundle behind UMP’s engine adapter contract as a FastAPI app. |
How a run decides
Section titled “How a run decides”| Situation | Route | Note on the result |
|---|---|---|
| New weeks follow the training data, spend in range | score with the saved state | none |
| A gap, an overlap or irregular time | refit on the given data with the saved seed | HISTORY_ROUTED_TO_REFIT |
| Spend outside the training range | refit | INPUT_OUT_OF_SUPPORT |
| Code not trusted | answers from the bundle’s summary | route not_available, labelled |
Pass mode="refit" to refit on purpose, or allow_refit=False to stop at the labelled fallback instead.
Rules the bundle keeps
- Never unpickle. Arrays are saved with
numpy.saveand loaded withallow_pickle=False. - Load order. Every file is checked against
workflow.jsonandSHA256SUMSfirst. A mismatch, a missing or unlisted file, or a link refuses the bundle before any content is read. - Round trip on save.
from_state(to_state(model))must give the same draws on the training data, or the save is refused withWORKFLOW_STATE_LOSSY. - Answers are draws. A question must return a one-dimensional array with at least two values. With no interval level declared, the level is 0.9, listed as proposed.
- No personal data. The author is a principal id such as
user:sam.patel, never an email. Training data is stored only withinclude_data=True. - Shared trust is an approval. Through core, a bundle’s hashes are trusted only from a validated manifest approved by someone other than its author.
Command line
Section titled “Command line”ump-workflow inspect out/weekly_sales --jsonump-workflow check out/weekly_sales new_weeks.csvump-workflow run out/weekly_sales new_weeks.csv --trust -o answers.jsonump-workflow serve out/weekly_sales --port 8210Exit codes: 0 ok, 1 fallback (summary answers), 2 usage, 3 bundle refused or unreadable. The serve port default, 8210, is proposed.
Served routes
| Method | Path | Returns |
|---|---|---|
| GET | /v1/metadata | Adapter metadata: the bundle’s family, the answer and fit operations, one estimand per question. |
| GET | /v1/questions | Each question’s name, wording and estimand. |
| GET | /v1/contract | The input contract. |
| POST | /v1/answer | {rows, mode?, allow_refit?, questions?} gives the same result as run(df).to_dict(). |
| POST | /v1/fit | The same with mode="refit". |
The server runs the code only when an approval, or the machine’s own trust store, vouches for its hash. Otherwise it answers from the labelled summary.