Architecture¶
At v1.0.0 the framework is a uv workspace with a lean modeling core and optional application layers:
pip install mmm-framework— business logic only: the full Bayesian MMM (build / fit / analyze / report / validate) with no web framework and no LLM stack.pip install "mmm-framework[agents]"— adds the LangGraph oracle agent (langgraph, langchain-*, httpx, pypdf, python-docx). The langchain-free agents service modules (fitting,workspace,model_ops, …) import without the extra via the lazyagents/__init__.mmm-framework-server— the FastAPI application, a separate workspace package in the repository’sserver/directory. Run it withuvicorn mmm_framework_server.main:app.The former
mmm_framework.apiservice modules (sessions store, run history, pacing, scorecards, …) live inmmm_framework.platform— dependency-light, shared by the agents and the server.
The lean-core invariant is pinned in CI by tests/test_lean_imports.py.
Package & dependency structure¶
Dependencies point strictly inward — nothing in the core depends on the agent stack or the server:
graph TD
FE["frontend/<br/><small>React + Vite UI</small>"]
SRV["mmm-framework-server<br/><small>FastAPI app · repo server/ · package mmm_framework_server</small>"]
AGENTS["mmm-framework with the agents extra<br/><small>LangGraph oracle · langgraph, langchain, httpx, pypdf, python-docx</small>"]
CORE["mmm-framework — the core<br/><small>model · transforms · config + builders · reporting · validation<br/>planning · estimands · diagnostics · eda · synth · platform · auth core</small>"]
KERNEL["session kernel image<br/><small>lean core + ipykernel + python-pptx + matplotlib<br/>no LLM or web stack</small>"]
GCP["gcp extra<br/><small>GCS + BigQuery</small>"]
S3["s3 extra<br/><small>S3 object store</small>"]
FE -->|"HTTP + SSE"| SRV
SRV -->|"depends on"| AGENTS
AGENTS -->|"depends on"| CORE
KERNEL -->|"built from"| CORE
GCP -.->|"optional extra of"| CORE
S3 -.->|"optional extra of"| CORE
Model-building data flow¶
graph LR
subgraph IN["Data in"]
MFF["MFF CSV<br/><small>long format</small>"]
WIDE["wide CSV"]
EX["load_example()"]
end
LOAD["MFFLoader<br/><small>validation · cadence checks</small>"]
PANEL["PanelDataset"]
MODEL["BayesianMMM<br/><small>adstock → saturation → trend, seasonality, controls → likelihood<br/>fit: NUTS / numpyro · approximate MAP, ADVI, pathfinder, Laplace · SMC exact</small>"]
RES["MMMResults<br/><small>posterior · diagnostics · estimands</small>"]
subgraph OUT["Fan-out"]
REP["MMMReportGenerator<br/><small>Augur HTML readout</small>"]
IREP["InteractiveReportGenerator<br/><small>recompute-in-browser report</small>"]
SER["MMMSerializer<br/><small>persisted model directory</small>"]
ANA["analysis + estimands<br/><small>ROI · marginal ROAS · counterfactuals</small>"]
VAL["validation<br/><small>backtests · SBC · refutation</small>"]
end
MFF --> LOAD
WIDE --> LOAD
EX --> LOAD
LOAD --> PANEL
PANEL --> MODEL
MODEL --> RES
RES --> REP
RES --> IREP
RES --> SER
RES --> ANA
RES --> VAL
Application request flow¶
How a question in the web UI becomes a fitted model and rendered artifacts
(all server-side components are in the separate mmm-framework-server
package; the persistence layer is core mmm_framework.platform):
sequenceDiagram
autonumber
actor User
participant UI as React UI (port 5173)
participant API as FastAPI server (port 8000)
participant Agent as LangGraph agent
participant Tools as Tools layer
participant Kernel as Session kernel
participant Store as platform.sessions (SQLite)
User->>UI: Ask a question in the Oracle
UI->>API: POST /chat — SSE stream opens
API->>Agent: Resume thread from checkpoint
Note over Agent: Workflow-step guard —<br/>one pipeline milestone per turn
Agent->>Tools: Tool call (EDA, build, fit, report, ...)
Tools->>Kernel: execute_python — fits run in-kernel
Kernel-->>Tools: stdout, plots, tables
Tools->>Store: Persist artifacts, experiments, run metrics
Store-->>API: Session state and artifact refs
API-->>UI: SSE events — messages, plots, tables, artifacts
UI-->>User: Rendered in the workspace tabs
The measurement loop¶
The product’s core cycle — fit, prioritize experiments by information value, run them, fold the results back in as calibration, allocate, and re-test when information decays:
graph LR
T0["T₀ · Fit<br/><small>Bayesian MMM with calibration</small>"]
T1["T₁ · Prioritize<br/><small>EIG / EVOI per channel</small>"]
T2["T₂ · Design + pre-register<br/><small>geo lift · DiD · flighting<br/>draft → planned → running → completed → calibrated</small>"]
T3["T₃ · Calibrated refit<br/><small>experiment likelihoods in-graph</small>"]
T4["T₄ · Allocate<br/><small>budget optimizer</small>"]
T5["T₅ · Re-evaluate<br/><small>information decay triggers re-tests</small>"]
T0 --> T1
T1 --> T2
T2 --> T3
T3 --> T4
T4 --> T5
T5 -->|"new priorities"| T1
Where things live¶
Subsystem |
Import path |
|---|---|
Core modeling |
|
Data loading |
|
Results & prediction |
|
Estimands |
|
Reporting |
|
Validation & diagnostics |
|
Experiment planning |
|
Platform services (sessions store, run history, …) |
|
Agents (optional |
|
API server (separate package) |
|
Further reading¶
Architecture & flow diagrams (docs site) — the same diagrams with the full narrative.
REST API reference — all server endpoints, generated from the live OpenAPI schema.
API Contracts & Stability — what the v1.0 stability promise covers.