Simulations
Simulations exercise a Deployment before production by driving the Runtime closed loop:
POST /invocationsfor a sampled customer (ranked offers infinal_result).- Simulated take-up (or none).
- On accept,
POST /response(singular) with that row’suuidandoffer_name.
params on /invocations is a JSON string, never a nested object. Use a stringified empty object, or a stringified input / value arrays object.
Two ways to run that loop:
| Approach | Where | Best for |
|---|---|---|
| Workbench 2 | /entities/simulation | Operator setup, scenario describe, logs, Beta plots, options-store snapshots |
| Python | Python package or a custom script | Fully custom take-up, traffic, or batch logic |
Workbench is easier. Python is more flexible. Both still need a pushed Dynamic Interaction deployment and a reachable Runtime.
A simulation writes Runtime logs. To analyze those logs (funnel, rank uptake, catalog), run an Interactions Analytic job on the same ecosystemruntime / ecosystemruntime_response collections.
Workbench 2
Open Simulation at /entities/simulation (not the old Laboratory / Jupyter notebook UI).
Identity
Each simulation document has a name, project, deployment, and deployment version. New simulations inherit the header project, then the first deployment/version when those are empty.
Save is never blocked on those fields. Empty names become New simulation / New simulation N. Duplicate explicit names still conflict.
Run Simulation stays disabled until setup is complete. Blockers shown on the Setup card:
- Simulation name
- Project, deployment, and version
- ecosystem-server username and password
- MongoDB available
- ecosystem-notebooks available
The learning algorithm is not chosen on the simulation form. It is resolved from the Dynamic Engagement linked to the deployment. If none is linked, the Runtime scores naively.
Runs
A simulation can have many runs. All run settings (configuration, scenario, customer source, connections, advanced, logs, results) sit under the selected run.
Add Run clones the first run’s configuration, with a new UUID and RunN name so logs and plots stay isolated.
Default new-run values (override per run):
| Field | Default |
|---|---|
| Iterations | 1000 |
| Default take-up | 0.1 (10% chance the presented offer is accepted) |
| Number of offers | 3 |
| Offer selection | top (highest scoring among final_result) |
| Iterations per second | 100 |
| Output every N iterations | 100 |
| Plot + parameter output | on |
| Server URL | http://ecosystem-server:3001/api |
| Notebooks URL | http://ecosystem-notebooks:8010/process_simulation |
| Pulse responder | http://ecosystem-runtime:8091 |
| Server credentials | admin@ecosystem.ai / password |
Run configuration
Offers & algorithm
- Number of offers —
numberofferssent to/invocations. - Offer selection (among presented) — which ranked offer is credited on take-up:
top— highest scoringsecond— always rank 2random— uniform among the Runtime slatedistribution— score-weighted
- Algorithm — read-only, from the deployment’s Dynamic Engagement.
Iterations & take-up
- Number of iterations — how many
/invocationscalls. - Default take-up rate — Bernoulli accept probability unless historical or policy modes override it.
- Reward binary threshold — used when the deployment’s reward path needs a binary outcome.
Connections
Open the Connections accordion (not buried in Advanced). Simulations authenticate to ecosystem-server with these credentials, not the Workbench UI login.
Click Validate (POST /simulations/validate-connections). The Workbench backend checks:
| Check | What it proves |
|---|---|
| Database | Mongo ping |
| ecosystem-notebooks | Process URL reachable (including localhost fallbacks when Docker DNS names fail) |
| ecosystem-server | POST login on server_url (must end in /api) with the run username/password |
| Pulse responder | Runtime /invocations host is reachable from notebooks |
Typical local notebooks port is 8011 if the container maps that way. If Validate says notebooks is down, start ecosystem-notebooks or change Notebooks URL. If the pulse responder cannot resolve ecosystem-runtime, use a host the notebook server can reach (for example http://localhost:8099 or http://localhost:8091).
Describe scenario (optional)
Natural language → reviewable ScenarioSpec. Failures stay in this panel and never block Save or Run.
- Describe peaks, take-up drift, or random uptake among presented offers (for example
telco weekday peaks 6–8am and 3–7pm). - Analyze — intents
traffic,take_up,selection. - Apply to run — writes the patch into this run only after you confirm.
If the scenario cannot be expressed as config, Generate custom Python produces a script you copy and run yourself. Workbench Run does not execute that script.
Apply can set:
- Traffic profile — virtual day/week compressed into iterations (weekday peaks 6–8 and 15–19 by default). Does not wait for wall-clock hours.
- Take-up policy —
flat,historical,linear_drift,cyclical,rank_decline,offer_decline. - Selection policy —
static(use Offer Selection) ordrift_lower_sequence(acceptance moves toward lower ranks over the run).
You can also set those under Advanced without using Analyze.
Customer source (optional)
By default, customers come from the deployment Parameter Access lookup.
Enable Use alternate customer source to sample /invocations customers from another Mongo database/collection/lookup field, with an optional aggregation pipeline (then the engine adds $sample). Example lookup field customer; pipeline [{"$match":{"region":"ZA"}}].
If the lookup key does not match the collection, Run fails with a hint to fix Parameter Access or the alternate source.
Advanced
| Group | Fields |
|---|---|
| Outputs & pacing | Plot output, parameter output, output every N iterations, iterations per second |
| Who is tested | Segment to test — JSON such as {"contextual_variable_one":"Healthy"} or {} for all dynamic segments. Static models ignore this. |
| Baseline take-up | Historical take-up from the Dynamic Engagement feature store (1/0 success field); optional override of the field name (does not write the store) |
| Take-up over time | Linear drift (per-iteration shift std), or cyclical sine period |
| Selection over time | drift_lower_sequence |
| Traffic profile | Virtual-time intensity windows |
Run, logs, results
- Save the simulation (drafts are allowed).
- Validate connections.
- Run Simulation — Workbench FastAPI proxies to notebooks
POST .../process_simulation. Progress shows in the run banner. - Logs —
GET /simulations/logs?run_uuid=...(Mongosimulations.simulation_logs; legacyecosystem_meta.threadsis still read). Cancel hits notebooks/cancel_simulation. - Results — options-store parameter snapshots (
simulation_param_outputs) and Beta / plot series (simulation_plot_outputs) when those outputs are enabled. Use plots to inspect Ecosystem Rewards (and similar) arm distributions after the run.
Failures return a message and a hint (unreachable Runtime, empty customer collection, missing server password, notebooks down, bad customer pipeline).
Workbench APIs (operator)
Thin UI; processing stays on the backend:
GET /simulations/status— Mongo + notebooks healthPOST /simulations/validate-connectionsPOST /simulations/run/ cancelGET /simulations/logs,DELETE /simulations/logsGET /simulations/results/parameters,/results/options,/results/plot- Scenario analyze/apply via the agents router (optional)
Python simulations
Use the Python package (prediction.apis.prediction_engine) when you need a take-up rule Workbench cannot express. Keep the same closed loop.
import random
import time
empty_params = "{}" # params must be a JSON string
invocations_body = {
"campaign": deployment_id,
"subcampaign": "simulation",
"channel": "simulation",
"customer": customer_id,
"userid": "simulation",
"numberoffers": 1,
"params": empty_params, # JSON string, e.g. "{}"
}
offer_response = predictor_engine.invocations(auth_runtime, invocations_body)
row = offer_response["final_result"][0]
# Ranked offers: parse offer_name from the row (field names follow the Runtime result)
if random.random() < takeup_rate:
predictor_engine.response(
auth_runtime,
{
"uuid": row["uuid"],
"offer_name": row.get("offer_name") or row.get("result", {}).get("offer"),
},
)
time.sleep(delay_seconds)Do not call /responses (plural). After the loop, inspect the options store or run Interactions Analytic on the logging collections.
For Beta plots from Mongo alpha/beta (Ecosystem Rewards), filter the options store by contextual variables and overlay scipy.stats.beta.pdf — same idea as the Workbench plot output.
Related
- Deployments
- Runtime access —
/invocationsand/response - Ecosystem Rewards — reading Beta plots
- Model convergence
- Interactions Analytic
- Runtime Console — single-call tests without a full simulation