babelfin
package babelfin
provisional (not yet in the spec) G-DATA-07 is in the gate registry but not yet in the specification’s gate catalogue, so it is not normative.
Babelfin: the analyst-facing Python API for UMP.
import babelfin as fin
roi = fin.estimand("roi", channels=["tv", "search"], weeks=104)tv = fin.number(1.37, (1.26, 1.50), roi)fin.compare(tv, test_number)Everything here wraps ump_spec; each result exposes the underlying model as .spec.
Read the babelfin guide first for the main entry points.
Classes
Section titled “Classes”Alignment
Section titled “Alignment”class
class AlignmentThe G-EST-02 ladder’s answer for two numbers or estimands. spec is the AlignmentResult.
Methods and properties (9)
init constructor
Alignment(self, spec: AlignmentResult, comparison: EstimandComparison) -> Nonerung property
rung: strmatch, converter, re_ask, assumed_alignment or side_by_side.
combinable property
combinable: boolmismatched property
mismatched: list[str]interval property
interval: Interval | Nonefallback property
fallback: FallbackEntry | Nonereason property
reason: strto_frame method
def to_frame(self) -> pd.DataFrameOne row per field that differs.
explain method
def explain(self) -> strSource: packages/babelfin/src/babelfin/_numbers.py, line 124
Allocation
Section titled “Allocation”class
class AllocationCurrent and optimal spend per channel, each optimal spend with its interval across draws.
Fields
| Name | Type | Default | Description |
|---|---|---|---|
raw | rows |
Methods and properties (2)
init constructor
Allocation(self, rows: list[dict[str, Any]]) -> Noneto_frame method
def to_frame(self) -> pd.DataFrameSource: packages/babelfin/src/babelfin/_apps.py, line 197
AppModel
Section titled “AppModel”class
class AppModelA model as the models app describes it: plain status, estimand in words, acceptance, use label.
Methods and properties (11)
init constructor
AppModel(self, app: Models, raw: dict[str, Any]) -> Noneid property
id: strtitle property
title: strtier property
tier: stracceptance property
acceptance: strpassed, untested, pilot or failed (untested is usable with reduced trust; failed is excluded).
use_label property
use_label: strdependence property
dependence: strestimand property
estimand: strWhat the model’s headline number means, in words.
plain property
plain: strclient property
client: str | Nonecurves method
def curves(self, level: float = 0.9) -> CurvesSource: packages/babelfin/src/babelfin/_apps.py, line 118
Artifact
Section titled “Artifact”class
class ArtifactA handle on one artifact in ump-core. Methods update the handle and return it, so calls chain.
Fields
| Name | Type | Default | Description |
|---|---|---|---|
envelope | envelope | ||
approval_leaf | int | None | None |
Methods and properties (14)
init constructor
Artifact(self, session: Session, envelope: dict[str, Any]) -> Noneid property
id: strkind property
kind: strstate property
state: strclient property
client: str | Nonedoc property
doc: dict[str, Any]fallbacks property
fallbacks: FallbacksThe fallbacks from the latest checks: what an approver must accept by name.
gates property
gates: GateReportrefresh method
def refresh(self) -> Artifactsubmit method
def submit(self, reason: str | None = None) -> ArtifactRun the checks and, when nothing stops, move to awaiting approval.
approve method
def approve( self, accept: Iterable[FallbackEntry | str] | None = None, comment: str | None = None,) -> ArtifactSign as this session’s principal. accept must name every fallback on the artifact (art.fallbacks,
the FallbackEntry objects, or "gate:code" strings); otherwise fin.UnacknowledgedFallbacks is
raised before anything is sent. G-INT-02 refuses an approver who owns, submitted or wrote the work.
reject method
def reject(self, comment: str) -> ArtifactReject with a reason (core requires one); nothing is accepted.
proof method
def proof(self) -> LogProofFetch this artifact’s latest log leaf and the signed checkpoint, and check them here.
lineage method
def lineage(self, upstream: bool = True, depth: int | str = 'all') -> LineageSource: packages/babelfin/src/babelfin/_session.py, line 134
ClientView
Section titled “ClientView”class
class ClientViewThe console’s view of one client: returns per channel (with intervals), alerts, raw.
Fields
| Name | Type | Default | Description |
|---|---|---|---|
raw | raw | ||
name | str | str(raw.get('client') or raw.get('client_id') or '') | |
alerts | list[dict[str, Any]] | list(raw.get('alerts') or []) |
Methods and properties (2)
init constructor
ClientView(self, raw: dict[str, Any]) -> Nonereturns property
returns: pd.DataFrameSource: packages/babelfin/src/babelfin/_apps.py, line 338
Contract
Section titled “Contract”class
class ContractA workflow’s input contract. .spec is the ump_spec InputContract.
Fields
| Name | Type | Default | Description |
|---|---|---|---|
spec | spec |
Methods and properties (8)
init constructor
Contract(self, spec: InputContract) -> Nonefrom_frame classmethod
def from_frame( cls, df: pd.DataFrame, *, time: str | None, target: str | None, freq: str | None = None, categories: dict[str, DimPolicy] | list[str] | None = None, extra: Literal['allow', 'drop'] = 'drop', min_rows: int | None = None,) -> ContractInfer a contract from a training frame: every non-time, non-target, non-category column is a required
variable with the frame’s dtype (missing: forbid unless it already has nulls). categories maps a
column to its policy (a list means fixed); when None, string columns are fixed categories. Fixed
categories get their level set (hash and count). freq is inferred from the time column when not given.
load classmethod
def load(cls, path: str | Path) -> Contractsave method
def save(self, path: str | Path) -> Pathto_dict method
def to_dict(self) -> dict[str, Any]key property
key: list[str]profile method
def profile(self, df: pd.DataFrame) -> FrameProfileWhat check_input reads about df: no row values, only dtypes, counts and hashes.
check method
def check( self, df: pd.DataFrame, *, mode: InputMode = 'score', history_end: str | None = None, trained_rows: int | None = None,) -> InputCheckCheck df against the contract (ump_spec.rules.check_input). history_end (ISO date) checks a
score follows on from the saved history; trained_rows notes growth on a refit.
Source: packages/babelfin/src/babelfin/_contract.py, line 107
Curves
Section titled “Curves”class
class CurvesResponse curves on a spend grid with their band. rung and origin say which fallback-ladder rung
produced them; raw is the app’s JSON.
Fields
| Name | Type | Default | Description |
|---|---|---|---|
channels | list[str] | list(c['channels']) | |
draws | np.asarray(c['draws'], dtype=float) | ||
rung | str | raw.get('ladder', {}).get('step', '') | |
origin | str | raw.get('source', {}).get('origin', '') | |
pseudo_draws | bool | bool(raw.get('pseudo_draws')) |
Methods and properties (5)
init constructor
Curves(self, raw: dict[str, Any], level: float = 0.9) -> Nonen_draws property
n_draws: intband property
band: strHow the band is drawn: quantiles at level with enough real draws, else min to max across draws.
to_frame method
def to_frame(self) -> pd.DataFrameLong form: one row per channel and grid point with spend, lo, median, hi.
plot method
def plot(self, scale: float = 1000000.0, unit: str = '$M') -> FigureOne panel per channel: the band, the median and a dotted line at current spend (needs matplotlib).
Source: packages/babelfin/src/babelfin/_apps.py, line 59
class
class Error(Exception)Base class: code is the UMP note or error code, gate the gate that raised it (if any).
Methods and properties (1)
init constructor
Error(self, code: str, detail: str, *, gate: str | None = None) -> NoneSource: packages/babelfin/src/babelfin/_errors.py, line 16
Estimand
Section titled “Estimand”class
class EstimandWhat a number means: the seven UMP fields plus scope. spec is the ump_spec model.
Fields
| Name | Type | Default | Description |
|---|---|---|---|
spec | spec |
Methods and properties (5)
init constructor
Estimand(self, spec: SpecEstimand) -> Nonewords property
words: strregistry_id property
registry_id: str | Noneto_dict method
def to_dict(self) -> dict[str, Any]explain method
def explain(self) -> strSource: packages/babelfin/src/babelfin/_estimand.py, line 51
Fallbacks
Section titled “Fallbacks”class
class Fallbacks(list[FallbackEntry])The fallbacks a check applied: what was assumed and how the numbers moved. A list, plus to_frame.
Methods and properties (2)
to_frame method
def to_frame(self) -> pd.DataFramekeys property
keys: list[str]gate:code for each, the form an approver acknowledges.
Source: packages/babelfin/src/babelfin/_gates.py, line 39
FittedWorkflow
Section titled “FittedWorkflow”class
class FittedWorkflowA workflow fitted in this session. .model is what the module’s fit returned.
Methods and properties (9)
init constructor
FittedWorkflow( self, workflow: Workflow, module: ModuleType, model: Any, *, data: pd.DataFrame, prepped: pd.DataFrame, check: InputCheck,) -> Noneanswers method
def answers( self, df: pd.DataFrame | None = None, *, inputs: dict[str, Any] | None = None,) -> dict[str, Number]{question: fin.Number} on the training data, or on df (checked against the contract, cleaned
and prepped first) from this fitted model. inputs binds the prep recipe’s other inputs (df binds
its first), as in fit and run.
answer method
def answer(self, name: str) -> Numbersummary_model method
def summary_model(self) -> ModelThe fit-time answers as a Tier D fin.Model (Model.from_table): the bundle’s summary.json
and the fallback rung when its code cannot run. Questions without a summary number are left out.
check method
def check( self, *, at: str = 'submit', strict: bool | str | list[str] = False,) -> GateReportThe gates on the answers’ model (the Tier D summary a bundle carries), as Model.check.
save method
def save( self, path: str | Path, *, include_data: bool = False, version: str = '0.1.0', author: str | None = None,) -> PathWrite the workflow as a bundle folder at path (which must not exist): source, plain fitted state,
contract, prep recipe, questions and the Tier D summary, every file hashed. The state must round-trip
(from_state(to_state(model)) gives the same answers, else WORKFLOW_STATE_LOSSY) and be plain
(STATE_NOT_PLAIN); on a refusal nothing is written. Training data is left out unless
include_data=True (and never when the recipe marks an input transient_only). author is a principal
id (user:maya.chen), recorded in workflow.json; a shared bundle’s author can never be its approver.
to_frame method
def to_frame(self) -> pd.DataFrameto_dict method
def to_dict(self) -> dict[str, Any]explain method
def explain(self) -> strSource: packages/babelfin/src/babelfin/_workflow.py, line 243
Forbidden
Section titled “Forbidden”class
class Forbidden(Refused)A 403: not allowed for this principal (G-INT-02 self approval, a missing role, client scope, agent tools).
Source: packages/babelfin/src/babelfin/_errors.py, line 51
GateReport
Section titled “GateReport”class
class GateReportEvery gate’s outcome for one document at one point in its life. results are ump_spec results.
Fields
| Name | Type | Default | Description |
|---|---|---|---|
results | results | ||
summary | gates.summarize(results) |
Methods and properties (9)
init constructor
GateReport(self, results: list[GateResult]) -> Noneok property
ok: boolFalse when anything stopped (a hard stop, or a strict stop when strict was asked).
next_state property
next_state: strstops property
stops: list[GateResult]warnings property
warnings: list[GateResult]fallbacks property
fallbacks: Fallbacksraise_for_stops method
def raise_for_stops(self) -> NoneRaise fin.Stopped with the first stop’s code; do nothing when nothing stopped.
to_frame method
def to_frame(self, all: bool = False) -> pd.DataFrameOne row per gate that did something; all=True adds the passes.
explain method
def explain(self) -> strSource: packages/babelfin/src/babelfin/_gates.py, line 52
InputCheck
Section titled “InputCheck”class
class InputCheckThe outcome of checking a frame against a workflow’s contract. .spec is the ump_spec
InputReport.
Fields
| Name | Type | Default | Description |
|---|---|---|---|
spec | report |
Methods and properties (10)
init constructor
InputCheck(self, report: InputReport) -> Noneok property
ok: boolFalse when a note refuses the input (the step is not available).
route property
route: strscore, refit or not_available.
mode property
mode: strnotes property
notes: list[InputNote]to_frame method
def to_frame(self) -> pd.DataFrameto_dict method
def to_dict(self) -> dict[str, Any]clean method
def clean(self, df: pd.DataFrame) -> pd.DataFrameA new frame with the excluded rows removed (rows at a new category level, matched by level hash, and
rows with no time or category value), drop_columns dropped and the coerce casts applied. The input
is not changed. Missing values in variables are left for the caller’s stated policy.
gate method
def gate(self, *, strict: bool = False) -> GateReportG-DATA-07 on this report, as core would run it on a score or refit (strict=True turns its
Fall back outcomes into stops; warnings stay warnings).
explain method
def explain(self) -> strSource: packages/babelfin/src/babelfin/_contract.py, line 40
InvalidDocument
Section titled “InvalidDocument”class
class InvalidDocument(Error)A document failed the contract (SCHEMA_INVALID). issues lists each field with its pointer.
Fields
| Name | Type | Default | Description |
|---|---|---|---|
issues | issues |
Methods and properties (2)
init constructor
InvalidDocument(self, detail: str, issues: list[dict[str, Any]]) -> Noneto_frame method
def to_frame(self) -> pd.DataFrameSource: packages/babelfin/src/babelfin/_errors.py, line 24
Lineage
Section titled “Lineage”class
class LineageEdges from inputs to outputs around one artifact (nodes, edges and raw as core returns them).
Fields
| Name | Type | Default | Description |
|---|---|---|---|
raw | raw | ||
edges | list[dict[str, Any]] | list(raw.get('edges') or []) | |
nodes | list[dict[str, Any]] | list(raw.get('nodes') or []) |
Methods and properties (2)
init constructor
Lineage(self, raw: dict[str, Any]) -> Noneto_frame method
def to_frame(self) -> pd.DataFrameSource: packages/babelfin/src/babelfin/_session.py, line 114
LoadedWorkflow
Section titled “LoadedWorkflow”class
class LoadedWorkflowA saved workflow, opened. Inspecting it, its contract check and its summary need no trust; running its
code does (.trust() or Workflow.load(..., trust_code=True)).
Fields
| Name | Type | Default | Description |
|---|---|---|---|
bundle | bundle | ||
approval | approval | For a workflow fetched from core (s.workflows.get): what its approval vouches for, or the reason it vouches for nothing (WORKFLOW_SHARE_NOT_APPROVED, WORKFLOW_SELF_APPROVED, WORKFLOW_SHARE_MISMATCH). | |
model_id | model_id |
Methods and properties (16)
init constructor
LoadedWorkflow( self, bundle: OpenBundle, *, approval: ApprovalTrust | None = None, model_id: str | None = None,) -> Nonename property
name: strcode property
code: strThe module’s source: read it before trusting it.
code_sha256 property
code_sha256: strtrusted property
trusted: boolcontract property
contract: Contracttrust method
def trust(self) -> LoadedWorkflowTrust this code hash on this machine (persisted in finprint’s trust store). Read .code first. A prep
recipe’s named-step code is not covered: read .step_code, then .trust_steps().
step_code property
step_code: dict[str, str]{step_id: source} of the prep recipe’s named steps: code that runs on the input before the module.
trust_steps method
def trust_steps(self) -> LoadedWorkflowTrust the prep recipe’s named-step code on this machine (finprint’s store). Read .step_code first.
inspect method
def inspect(self) -> dict[str, Any]Manifest, contract, questions and env report (no trust needed, nothing runs); env_pin is the
versions it was saved with.
check method
def check(self, df: pd.DataFrame, *, mode: Mode = 'score') -> InputCheckThe contract check on df (score follows on from the saved history); data only.
run method
def run( self, df: pd.DataFrame, *, mode: Mode = 'score', allow_refit: bool = True, inputs: dict[str, Any] | None = None,) -> WorkflowRunAnswer the questions on df. Score (the default) reuses the saved state; input that breaks
continuity (a gap, an overlap, irregular time, spend outside the training range, a major env drift)
refits, labelled, unless allow_refit=False; anything unavailable, or untrusted code, gives the
bundle’s Tier D summary, labelled.
score method
def score(self, df: pd.DataFrame, **kw: Any) -> WorkflowRunrefit method
def refit(self, df: pd.DataFrame, **kw: Any) -> WorkflowRunto_dict method
def to_dict(self) -> dict[str, Any]explain method
def explain(self) -> strSource: packages/babelfin/src/babelfin/_workflow.py, line 487
class
class LogAn append-only Merkle log (RFC 9162 style) held in memory. Records are hashed under a domain tag.
Fields
| Name | Type | Default | Description |
|---|---|---|---|
tag | tag |
Methods and properties (6)
init constructor
Log(self, tag: str = 'ump.artifact.v1') -> Nonesize property
size: introot property
root: stradd method
def add(self, record: Any) -> intAppend a record; returns its index.
proof method
def proof(self, index: int) -> Proofappended_since method
def appended_since(self, size: int, root: str) -> boolTrue when this log only appended to the earlier log of size with root (a consistency proof).
Source: packages/babelfin/src/babelfin/_log.py, line 39
LogProof
Section titled “LogProof”class
class LogProofAn approval’s place in the sealed log, checked here with ump_spec.integrity in two steps (spec §18): the
record against its log entry’s content hash, the leaf hash recomputed from the entry, the audit path to the
root, the root against the signed checkpoint, the log’s signature and the timestamp token beside it.
Fields
| Name | Type | Default | Description |
|---|---|---|---|
record | dict[str, Any] | entry['record'] | |
checks | dict[str, bool] | {'record matches its log entry': not any('content_hash' in p or 'no log entry... |
Methods and properties (4)
init constructor
LogProof(self, entry: dict[str, Any], checkpoint: dict[str, Any]) -> Noneok property
ok: boolverify method
def verify(self) -> boolto_frame method
def to_frame(self) -> pd.DataFrameSource: packages/babelfin/src/babelfin/_session.py, line 69
class
class MeWho the session acts as: principal, roles, client_scope, permissions (raw has the rest).
Fields
| Name | Type | Default | Description |
|---|---|---|---|
raw | raw | ||
principal | str | raw['principal'] | |
roles | list[str] | list(raw.get('roles') or []) | |
client_scope | list[str] | str | raw.get('client_scope') or [] | |
permissions | dict[str, Any] | dict(raw.get('permissions') or {}) |
Methods and properties (1)
init constructor
Me(self, raw: dict[str, Any]) -> NoneSource: packages/babelfin/src/babelfin/_session.py, line 48
class
class ModelA model manifest you can check, save and (with a session) file. doc is the JSON, spec the model.
Fields
| Name | Type | Default | Description |
|---|---|---|---|
spec | ModelManifest | spec | |
doc | doc |
Methods and properties (8)
init constructor
Model(self, doc: dict[str, Any]) -> Nonefrom_table classmethod
def from_table( cls, table: pd.DataFrame | Mapping[str, Number], *, estimand: EstimandLike | Estimand, name: str, engine: str, client: str | None = None, metric: Literal['roi', 'mroi'] = 'roi', dependence: DependenceKind | dict[str, Any] | None = None, recipe: Any = None, **fields: Any,) -> ModelA Tier D model (summary numbers at current spend) from a results table with columns channel,
value, lo, hi and optionally current_spend and level, or from a dict of channel to
fin.Number. A model of more than one channel must declare dependence (G-DEP-01): how its channel
estimates move together, e.g. "joint_draws" when they came from one fit’s draws. recipe links
the transform recipe that made the input. Other manifest fields pass through as keywords.
load classmethod
def load(cls, path: str | Path) -> Modelcheck method
def check( self, *, at: str = 'submit', strict: bool | str | list[str] = False,) -> GateReportThe gates core will run when this model is submitted (see fin.check).
save method
def save(self, path: str | Path) -> Pathto_dict method
def to_dict(self) -> dict[str, Any]to_frame method
def to_frame(self) -> pd.DataFrameexplain method
def explain(self) -> strSource: packages/babelfin/src/babelfin/_model.py, line 52
Number
Section titled “Number”class
class NumberA value with its interval, estimand, trust labels and fallbacks. spec is the ShownNumber.
Fields
| Name | Type | Default | Description |
|---|---|---|---|
spec | spec |
Methods and properties (11)
init constructor
Number(self, spec: ShownNumber) -> Nonevalue property
value: floatlo property
lo: floathi property
hi: floatlevel property
level: floatlabel property
label: str | Nonefallbacks property
fallbacks: list[FallbackEntry]estimand property
estimand: Estimandwords property
words: strThe value and its range in words, e.g. “1.37, likely between 1.26 and 1.50”.
to_dict method
def to_dict(self) -> dict[str, Any]explain method
def explain(self) -> strSource: packages/babelfin/src/babelfin/_numbers.py, line 31
Optimisation
Section titled “Optimisation”class
class OptimisationA planner optimisation. uplift is a fin.Number with its reallocation estimand; nothing is saved
unless it was asked for (scenario_id is then set).
Fields
| Name | Type | Default | Description |
|---|---|---|---|
op | dict[str, Any] | raw['optimisation'] |
Methods and properties (9)
init constructor
Optimisation( self, planner: Planner, request: dict[str, Any], raw: dict[str, Any],) -> Noneuplift property
uplift: Numberallocation property
allocation: Allocationp_beat_plan property
p_beat_plan: float | NoneShare of joint draws in which the optimised plan beats the current one.
ladder_step property
ladder_step: str | Nonedependence property
dependence: str | Nonefallbacks property
fallbacks: list[dict[str, Any]]scenario_id property
scenario_id: str | Nonesave method
def save(self, name: str) -> OptimisationRun the same optimisation again (same model, settings and seed) with save=True, as the web app’s
Save button does, and return the saved result.
Source: packages/babelfin/src/babelfin/_apps.py, line 211
Priority
Section titled “Priority”class
class PriorityWhich test to run next: candidates ranked by expected information gain and EVOI, with the rule used.
Fields
| Name | Type | Default | Description |
|---|---|---|---|
raw | raw | ||
rule | str | str(rule.get('words') if isinstance(rule, dict) else rule or '') | |
kept | str | None | raw.get('kept') | |
guarantee_note | str | str(raw.get('guarantee_note') or '') |
Methods and properties (2)
init constructor
Priority(self, raw: dict[str, Any]) -> Noneto_frame method
def to_frame(self) -> pd.DataFrameSource: packages/babelfin/src/babelfin/_apps.py, line 294
class
class ProofProof that one record is in the log. spec is the InclusionProof.
Methods and properties (3)
init constructor
Proof(self, spec: InclusionProof, tag: str) -> Noneroot property
root: strverify method
def verify(self, record: Any = None) -> boolThe audit path reaches the root; with record, also that this record is the proven leaf.
Source: packages/babelfin/src/babelfin/_log.py, line 13
Refused
Section titled “Refused”class
class Refused(Error)ump-core or an app said no (any 4xx): status, code, gate and the RFC 9457 problem.
Methods and properties (1)
init constructor
Refused( self, code: str, detail: str, *, status: int, gate: str | None = None, problem: Any = None,) -> NoneSource: packages/babelfin/src/babelfin/_errors.py, line 43
Session
Section titled “Session”class
class SessionOne principal talking to ump-core. Use as a context manager; core is the underlying CoreClient.
Fields
| Name | Type | Default | Description |
|---|---|---|---|
gate_mode | 'strict' if strict else None | ||
core | CoreClient(url, principal=principal, transport=transport, on_gate_fail=self.g... |
Methods and properties (16)
init constructor
Session( self, principal: str, *, url: str | None = None, strict: bool = False, transport: httpx.BaseTransport | None = None,) -> Noneclose method
def close(self) -> Nonemodels property
models: ModelsThe models app: list, get, then a model’s curves().
planner property
planner: PlannerThe planner app: optimize (not saved unless asked).
experiments property
experiments: ExperimentsThe experiments app: prioritise.
console property
console: ConsoleThe console app: client(id).
data property
data: DataThe data catalogue app: datasets, dataset.
workflows property
workflows: WorkflowsWorkflow bundles shared through core: publish, get, list.
engines method
def engines(self) -> pd.DataFrameThe engine adapters core knows, with metadata re-read from each adapter.
as_ method
def as_(self, who: str) -> SessionThe same connection acting as another principal (for example the approver).
me property
me: Meportfolio method
def portfolio(self) -> pd.DataFrameOne row per client in scope; ShownNumber columns flattened (iroas.value, iroas.range_words, …).
artifacts method
def artifacts( self, *, kind: str | None = None, client: str | None = None, state: str | None = None,) -> pd.DataFrameartifact method
def artifact(self, artifact_id: str) -> Artifactcheck method
def check( self, document: Document, *, strict: bool | str | list[str] = False,) -> GateReportDry-run the gates on the server (core adds context from the client’s contract), on the doc as core will
store it. Core raises on a stop, so a stop here raises fin.Stopped; otherwise the report comes back.
file method
def file( self, document: Document, *, client: str | None = None, title: str | None = None, parents: Mapping[str, str | None] | Iterable[str] | None = None,) -> ArtifactCreate a draft. client defaults to the doc’s client_id; title to its name. parents
records lineage into the new artifact: ids, or a mapping of id to role ({"ds_...": "training_data"}).
Source: packages/babelfin/src/babelfin/_session.py, line 229
Stopped
Section titled “Stopped”class
class Stopped(Error)A strict or hard stop. report is the full gate report when the stop came from the gates.
Fields
| Name | Type | Default | Description |
|---|---|---|---|
report | report |
Methods and properties (1)
init constructor
Stopped( self, code: str, detail: str, *, gate: str | None = None, report: GateReport | None = None,) -> NoneSource: packages/babelfin/src/babelfin/_errors.py, line 35
class
class TotalA sum of channel estimates under a dependence assumption. spec is the DependenceEnvelope.
Fields
| Name | Type | Default | Description |
|---|---|---|---|
spec | spec |
Methods and properties (9)
init constructor
Total(self, spec: DependenceEnvelope) -> Nonevalue property
value: floatlo property
lo: floathi property
hi: floatmethod property
method: strfallbacks property
fallbacks: list[FallbackEntry]envelope_kind property
envelope_kind: str | Nonebound when the total is a G-DEP-01 union of couplings (spec §7: a sum is a bound); None when the
draws were joint and nothing was assumed.
to_frame method
def to_frame(self) -> pd.DataFrameexplain method
def explain(self) -> strSource: packages/babelfin/src/babelfin/_numbers.py, line 203
UnacknowledgedFallbacks
Section titled “UnacknowledgedFallbacks”class
class UnacknowledgedFallbacks(Error)approve was called without naming every fallback on the artifact. missing lists gate:code.
Fields
| Name | Type | Default | Description |
|---|---|---|---|
missing | missing |
Methods and properties (1)
init constructor
UnacknowledgedFallbacks(self, missing: list[str]) -> NoneSource: packages/babelfin/src/babelfin/_errors.py, line 63
Unavailable
Section titled “Unavailable”class
class Unavailable(Error)A service or engine is down or unreachable (502/503).
Fields
| Name | Type | Default | Description |
|---|---|---|---|
status | status |
Methods and properties (1)
init constructor
Unavailable(self, code: str, detail: str, *, status: int = 502) -> NoneSource: packages/babelfin/src/babelfin/_errors.py, line 55
Workflow
Section titled “Workflow”class
class WorkflowA workflow being built: a contract for its input, the module that holds the model, an optional prep
recipe, and the questions it answers. fit(df) runs it; Workflow.load(path) opens a saved one.
Fields
| Name | Type | Default | Description |
|---|---|---|---|
name | name | ||
contract | contract | ||
code_path | Path(code) | ||
source | raw.decode('utf-8') | ||
code_sha256 | _sha256(raw) | ||
params | dict[str, Any] | dict(params or {}) | |
seed | int(seed) | ||
entry | {'fit': fit, 'to_state': to_state, 'from_state': from_state} | ||
dependence | dependence | ||
questions | list[dict[str, Any]] | [] |
Methods and properties (6)
init constructor
Workflow( self, name: str, *, contract: Contract, code: str | Path, prep: Any = None, params: dict[str, Any] | None = None, seed: int = 0, fit: str = 'fit', to_state: str = 'to_state', from_state: str = 'from_state', dependence: str | dict[str, Any] | None = None,) -> Nonecode is the module file (its source is what a bundle saves). prep is a finprint recipe (path,
recipe or save result) applied to the checked input before fit. fit, to_state and
from_state name the module’s functions. dependence is how the summary’s channel numbers move
together, required once the summary holds two or more channels (G-DEP-01).
question method
def question( self, name: str, *, ask: str, estimand: Estimand | str | dict[str, Any], fn: str | None = None, weeks: Weeks | None = 'rows', summary: tuple[str, str] | Literal[False] | None = None,) -> WorkflowRegister a question answered by the module function fn (default: name), which returns one value
per draw. estimand is a fin.Estimand or a registry name; the interval level is the estimand’s.
weeks="rows" (or "periods") recomputes a window horizon from the input on every run; None
keeps the estimand’s horizon. summary=(metric, channel) names its Tier D number in the bundle’s
summary (the fallback rung); by default a registered ROI or mROI on one channel gets one, False
opts out. Returns the workflow, so calls chain. A mistake raises fin.Error with code
WORKFLOW_QUESTION_INVALID and adds nothing.
fit method
def fit( self, df: pd.DataFrame, *, inputs: dict[str, Any] | None = None,) -> FittedWorkflowCheck df against the contract (refit mode), apply the prep recipe, fit the module and answer every
question. A refused input raises fin.Error with the contract’s code (e.g. a missing target).
load staticmethod
def load( path: str | Path, *, trust_code: bool = False, trust_steps: bool = False,) -> LoadedWorkflowOpen a saved bundle: every file’s hash checked, data parsed, no code run. The code runs only once its
hash is trusted on this machine (.trust(), finprint’s store) or trust_code=True is passed after
reading .code; until then, answers come from the bundle’s Tier D summary, labelled. A prep recipe’s
named-step code is separate: .trust_steps() or trust_steps=True after reading .step_code.
to_dict method
def to_dict(self) -> dict[str, Any]explain method
def explain(self) -> strSource: packages/babelfin/src/babelfin/_workflow.py, line 105
WorkflowRun
Section titled “WorkflowRun”class
class WorkflowRunOne rerun of a loaded workflow. route: score, refit or not_available; source: where the answers came
from (score, refit or summary); notes: every label, in the input-note shape.
Methods and properties (11)
init constructor
WorkflowRun(self, result: RunResult, name: str) -> Noneroute property
route: strsource property
source: strnotes property
notes: list[dict[str, Any]]rows property
rows: intplain property
plain: boolTrue when the saved state scored the input with nothing routed or substituted.
answers method
def answers(self) -> dict[str, Number]{question: fin.Number}. Where the route or source is not a plain score, each warn, exclude or
refuse note, and an asked-for refit’s WORKFLOW_REFIT, is on each number as a G-DATA-07 fallback;
other info notes (such as a dropped extra column) stay plain notes. Summary answers carry trust level 1. A
question the summary holds no number for is left out (named in the SUMMARY_FALLBACK note).
answer method
def answer(self, name: str) -> Numberto_frame method
def to_frame(self) -> pd.DataFrameto_dict method
def to_dict(self) -> dict[str, Any]explain method
def explain(self) -> strSource: packages/babelfin/src/babelfin/_workflow.py, line 397
Workflows
Section titled “Workflows”class
class WorkflowsWorkflow bundles shared through core: publish, get, list; answer calls a served bundle
(ump-workflow serve).
Methods and properties (5)
init constructor
Workflows(self, session: Session) -> Nonepublish method
def publish( self, fitted_or_bundle_path: FittedWorkflow | str | Path, *, client: str, title: str | None = None,) -> ArtifactUpload every file of the bundle (POST /v1/blobs, idempotent) and file a draft Tier A model_manifest
for client: summary numbers and estimands from summary.json, input_contract from
contract.json, payload_refs pinning every file, and a note with the code and prep-step hashes. A
FittedWorkflow is saved first (to a temporary folder) with this session’s principal as its author.
Then art.submit(), and a second person art.approve(accept=...).
get method
def get(self, model_id: str, *, dest: str | Path | None = None) -> LoadedWorkflowDownload the bundle a workflow manifest names (its validated version while a revision is pending, as
ump-workflow serve --artifact reads it), rebuild and verify it in dest (or this user’s private cache
folder under the system temp dir, mode 0700; when that folder cannot be trusted, a fresh mkdtemp folder
removed as soon as the bundle is open, so .bundle.data.root then names a folder that no longer exists)
and open it trusting exactly the hashes a validated,
second-person approval vouches for (.approval). A draft, an unapproved manifest or a self-approval loads
untrusted: its runs answer from the labelled summary. A wrong or missing blob raises fin.Error
BUNDLE_TAMPERED; a manifest of the wrong shape, or a ref that is not a sha256,
WORKFLOW_SHARE_MISMATCH.
list method
def list(self, client: str | None = None) -> pd.DataFrameWorkflow manifests (engine ump-workflow) in scope, one row each.
answer method
def answer( self, base_url: str, df: pd.DataFrame, *, mode: Literal['score', 'refit'] = 'score', allow_refit: bool = True, questions: Iterable[str] | None = None, transport: httpx.BaseTransport | None = None,) -> WorkflowRunAsk a served workflow bundle (ump-workflow serve, at base_url) its questions on df: one
POST /v1/answer as this session’s principal. The result is a WorkflowRun like lw.run(df): each
answer keeps its estimand and interval, and anything but a plain score is labelled (the server’s code trust
comes from an approval or its own trust store; untrusted code answers from the bundle’s summary). A
refusal is fin.Refused (413 WORKFLOW_REQUEST_TOO_LARGE past the server’s body limit), an unreachable
server fin.Unavailable. transport is for tests (an in-process app).
Source: packages/babelfin/src/babelfin/_share.py, line 88
Functions
Section titled “Functions”function
def check( kind: str, doc: dict[str, Any], *, at: str = 'submit', strict: bool | str | Iterable[str] = False, context: dict[str, Any] | None = None, operation: str | None = None,) -> GateReportRun the gate catalogue on doc exactly as core would, on the doc as core stores it. at is "submit" (default), "seal",
or the lifecycle state being entered (any key of ump_spec’s TRANSITIONS). strict=True turns every fallback into a stop; a list of gate ids makes
only those strict (a single id may be a plain string; an unknown id is refused). Hard stops stop in every
mode.
Source: packages/babelfin/src/babelfin/_gates.py, line 113
compare
Section titled “compare”function
def compare( a: Number | EstimandLike, b: Number | EstimandLike, *, strict: bool = False, a_is_model: bool = False, b_is_model: bool = False, can_reask: bool = False,) -> AlignmentCan these two be combined? Walks match, converter, re-ask, assumed alignment (interval widened), side by
side. strict=True raises fin.Stopped where a fallback would be needed. can_reask is off by
default because a notebook cannot re-ask a model unless it is connected to one.
Source: packages/babelfin/src/babelfin/_numbers.py, line 186
connect
Section titled “connect”function
def connect( as_: str, *, url: str | None = None, strict: bool = False, transport: httpx.BaseTransport | None = None,) -> SessionA session on ump-core as as_ ("maya.chen" means user:maya.chen; the POC’s identity stub is the
X-UMP-Principal header, IAP/OIDC in production). url defaults to UMP_CORE_URL or localhost:8000.
strict=True sends X-UMP-On-Gate-Fail: strict on every call.
Source: packages/babelfin/src/babelfin/_session.py, line 360
contract
Section titled “contract”function
def contract( *, time: str | None = None, freq: str | None = None, target: str | None = None, target_dtype: str | None = None, required: dict[str, str], optional: dict[str, str] | None = None, missing: dict[str, MissingPolicy] | None = None, categories: dict[str, DimPolicy] | None = None, key: list[str] | None = None, extra: Literal['allow', 'drop'] = 'drop', min_rows: int | None = None,) -> ContractA contract written by hand: required and optional map column to dtype; missing maps a column to
forbid (default), allow or mask; categories maps a categorical column to its policy (no level
sets: use Contract.from_frame to record them); key columns are required and never dropped as extra;
target_dtype makes a refit check the target’s cast; min_rows is a proposed threshold.
Source: packages/babelfin/src/babelfin/_contract.py, line 174
estimand
Section titled “estimand”function
def estimand( name: str | dict[str, Any] | Estimand | SpecEstimand, **overrides: Any,) -> EstimandA registered estimand ("roi", "mroi", "iroas", "lift" or any registry id), changed only
where you say. channels, geos, client and scope_kpi set the scope and keep the registry id;
any of the seven fields (weeks, kpi, interval, level, intervention, baseline, …)
makes it a custom estimand, so registry_id is dropped. intervention and horizon also take a
dict for the whole field. A full estimand dict or model is accepted as is.
Source: packages/babelfin/src/babelfin/_estimand.py, line 107
imputed_number
Section titled “imputed_number”function
def imputed_number( value: float, estimand: EstimandLike, *, level: float | None = None, label: str | None = None,) -> NumberA number whose source gave no interval: the band is imputed (G-UNC-01, proposed width) and labelled.
Source: packages/babelfin/src/babelfin/_numbers.py, line 109
ladder
Section titled “ladder”function
def ladder(available: Iterable[str], *, excluded: Iterable[str] = ()) -> LadderDecisionThe fallback-ladder rung for what you have: full_model, curve_params, spend_table, summary (warned), benchmarks (warned) or not_available.
Source: packages/babelfin/src/babelfin/_numbers.py, line 304
number
Section titled “number”function
def number( value: float, interval: tuple[float, float], estimand: EstimandLike, *, level: float | None = None, label: str | None = None, **labels: Any,) -> NumberA shown number. interval is (lo, hi) and is required: UMP never shows a bare point. level
defaults to the estimand’s. Extra keywords (trust_level, audit_level, unit_label,
artifact_id) pass through to ump_spec.rules.shown_number.
Source: packages/babelfin/src/babelfin/_numbers.py, line 95
record
Section titled “record”function
def record(title: str, **kwargs: Any) -> AnyStart recording what you do to data (finprint.session). In a notebook, %load_ext finprint
first so cells are recorded too.
Source: packages/babelfin/src/babelfin/_recipes.py, line 16
function
def total( values: Mapping[str, Number | tuple[float, float, float] | float] | pd.DataFrame, *, dependence: DependenceKind | None = None, level: float = DEFAULT_LEVEL, strict: bool = False, seed: int = 0,) -> TotalAdd channel estimates honestly. Give a dict of channel to Number, (value, lo, hi) or a bare value
(its interval is imputed and labelled), and the total is the G-DEP-01 envelope with DEPENDENCE_ASSUMED,
labelled envelope_kind: bound. Give a draws-by-channels DataFrame and the total uses the joint draws, with
no assumption; with dependence="per_channel" (or "none") the columns are not aligned, so the total
couples them two ways (a permutation seeded with seed, and sorted), its point is the median under the
independent coupling and its interval the union, a bound (spec §7).
Source: packages/babelfin/src/babelfin/_numbers.py, line 266
validate
Section titled “validate”function
def validate(kind: str, doc: dict[str, Any]) -> BaseModelThe typed ump_spec model for doc, or fin.InvalidDocument listing every issue with its pointer.
Source: packages/babelfin/src/babelfin/_gates.py, line 30
verify_recipe
Section titled “verify_recipe”function
def verify_recipe(path: Any, **kwargs: Any) -> AnyReplay a saved recipe and say whether it reproduced (finprint.verify).
Source: packages/babelfin/src/babelfin/_recipes.py, line 22
Constants
Section titled “Constants”| Name | Type | Value | Description |
|---|---|---|---|
AT | dict[str, tuple[str, str | None]] | {'submit': ('checks_running', None), 'seal': ('validated', 'seal')} | Points in an artifact’s life you can check at, as (transition, operation). submit is what core runs when a draft is submitted; seal is what runs when an approved record is written to the log. |
DEFAULT_LEVEL | float | Interval.model_fields['level'].default | Interval level used when an estimand declares none (ump_spec’s Interval default). |
ESTIMAND_ALIASES | dict[str, str] | {'roi': 'E-MMM-ROI', 'mroi': 'E-MMM-MROI', 'iroas': 'E-GEO-IROAS', 'lift': 'E... | Short names for the registered estimands analysts use most. Any registry id works too. |