SeanerBUS Bridge — Operational Guide¶
The SeanerBUS bridge connects ExaMLOps inference to an external Cap'n'Proto/TCP message bus.
It is a separate process managed via Docker Compose (--profile seanerbus) or bare-metal.
In dev and production, the bridge connects to the real SeanerBUS from the companion seanerbus repo (see SeanerBUS setup guide).
Protocol¶
Message Types¶
| Type ID | Name | Direction | Purpose |
|---|---|---|---|
| 5 | VectorReqV1 |
SeanerBUS → Bridge | Generic feature-vector inference request |
| 6 | VectorResV1 |
Bridge → SeanerBUS | Generic inference result (single prediction) |
| 7 | HpcJobV1 |
SeanerBUS → Bridge | HPC job inference request |
| 8 | HpcInferenceResV1 |
Bridge → SeanerBUS | HPC job inference result |
| 9 | RetrainReqV1 |
SeanerBUS → Bridge | Manual retrain trigger |
| 10 | RetrainResV1 |
Bridge → SeanerBUS | Retrain acknowledgement |
HpcJobV1 Fields¶
| Field | Type | Notes |
|---|---|---|
jobId |
Text | Unique HPC job identifier |
userId |
UInt32 | Submitting user ID |
numNodes |
UInt32 | Requested node count |
numCpus |
UInt32 | Requested CPU count |
partition |
Text | Slurm partition name |
walltimeSecs |
UInt64 | Wall-clock limit in seconds |
timestamp |
UInt64 | Submission timestamp (ms) |
embedding |
List(Float64) | 384-dim pre-computed embedding vector |
modelName |
Text | Target model (empty → SEANERBUS_DEFAULT_MODEL) |
alias |
Text | MLflow alias (empty → SEANERBUS_DEFAULT_ALIAS) |
Feature routing¶
The bridge uses a ModelSchemaRegistry (loaded from pipelines/models/*.yaml) to extract the correct input features for each model from the incoming HpcJobV1 message. It calls registry.build_features(model_name, msg) and registry.validate_features() before forwarding to the inference pipeline (POST /infer-pipeline/infer).
The FeatureTransformer inside the pipeline then extracts only the declared feature fields for the model call — numNodes and userId are retained as top-level metadata. All current models consume only the 384-dim embedding vector, but the schema registry allows future models with different input shapes to be added without bridge code changes.
Bridge modes¶
| Mode | Env value | Behaviour |
|---|---|---|
| Req/Res | reqres |
Register one handler per model UUID (from YAML); handle inference + optional retrain |
| Pub/Sub | pubsub |
Subscribe to JOB_TOPIC_UUID topic (not used with real SeanerBUS) |
| Both | both |
All handlers concurrently (legacy) |
The real SeanerBUS (seanerbus repo) uses reqres mode exclusively. The bridge is hardcoded to SEANERBUS_MODE=reqres in Docker Compose.
Connection-per-role design¶
Each subscription or service registration gets its own Connection object. The seanerbus read_msg() loop is a single blocking async loop over the TCP stream — one connection per role. In both mode the bridge opens four connections concurrently under asyncio.gather():
| Connection | Role |
|---|---|
pubsub_conn |
Subscribe to JOB_TOPIC_UUID; publish to RESULT_TOPIC_UUID |
<model>_conn × N |
One per model — serve seanerbus_uuid from each YAML (per-model req/res) |
inf_conn |
Legacy global handler — used only when no YAML UUIDs are present |
retrain_conn |
Serve RETRAIN_UUID — RetrainReqV1 → RetrainResV1 |
vector_conn |
Serve VECTOR_UUID — VectorReqV1 → VectorResV1 |
Prerequisites¶
- SeanerBUS Python bindings installed:
- A SeanerBUS server reachable at
SEANERBUS_HOST:SEANERBUS_PORT. - Ray Serve running (
:18001) for inference handlers. - Control Plane running (
:18002) for retrain handlers.
Environment Variables¶
| Variable | Default | Required | Purpose |
|---|---|---|---|
SEANERBUS_HOST |
localhost |
always | SeanerBUS server hostname |
SEANERBUS_PORT |
5398 |
always | SeanerBUS server TCP port |
SEANERBUS_MODE |
reqres |
always | Always reqres with real SeanerBUS |
SEANERBUS_RETRAIN_UUID |
— | optional | Req/res service UUID for RetrainReqV1 → RetrainResV1 |
SEANERBUS_VECTOR_UUID |
— | optional | Req/res service UUID for VectorReqV1 → VectorResV1 |
SEANERBUS_DEFAULT_MODEL |
JPCP |
— | Fallback model when HpcJobV1.modelName is empty |
SEANERBUS_DEFAULT_ALIAS |
Production |
— | Fallback MLflow alias when HpcJobV1.alias is empty |
RAY_SERVE_URL |
http://localhost:18001 |
— | Ray Serve base URL (host-side) |
CONTROL_PLANE_URL |
http://localhost:18002 |
— | Control plane base URL (host-side) |
CONTROL_PLANE_TOKEN |
— | — | Bearer token for POST /retrain |
DRIFT_WINDOW |
50 |
— | Rolling window size for per-model drift detection |
DRIFT_THRESHOLD |
0.5 |
— | Error-rate threshold that triggers POST /retrain |
DRIFT_COOLDOWN |
300 |
— | Seconds between auto-retrain triggers per model |
MODELS_YAML_DIR |
pipelines/models |
— | Directory scanned by ModelSchemaRegistry at startup |
Per-Model UUIDs¶
Each model registered in pipelines/models/*.yaml can declare a seanerbus_uuid field. The bridge reads these at startup and registers one req/res handler per model — HPC callers can address a specific model by UUID without embedding a model name in the message payload.
View UUIDs:
Assign UUIDs to all models (run once):
Backward compatibility: If no model YAMLs have seanerbus_uuid set, the bridge falls back to the legacy SEANERBUS_INFERENCE_UUID single-handler behaviour.
Starting the Bridge¶
Prerequisites¶
Real SeanerBUS must be running first (from the seanerbus repo):
# one-time: create the shared Docker network
docker network create seanerbus-net
# start real SeanerBUS + JPCP request generator
cd ../seanerbus && docker compose up -d
Docker Compose (recommended)¶
make seanerbus-up # start bridge container (connects to seanerbus-reqgen:5398)
make seanerbus-bridge-logs # tail bridge logs
make seanerbus-reqgen-logs # tail reqgen logs (inference_requests.log + seanerbus.log)
make seanerbus-down # stop bridge
Bare-metal (dev/debug)¶
Smoke Testing¶
Send one JPCP req/res inference request and print the response:
Expected output:
Sending HpcJobV1 → 30b0f24c-e154-432b-91f9-25a144095a30
job_id=1 user_id=42 num_nodes=8
{
"prediction": 91.36,
"model_name": "JPCP",
"model_version": "18",
"run_id": "5f015850..."
}
Bridge Status¶
Or directly:
curl http://localhost:18003/health # {"status":"ok","mode":"both"}
curl http://localhost:18003/stats # {"inferences_total":…, "vectors_total":…, …}
Troubleshooting¶
| Symptom | Likely cause | Fix |
|---|---|---|
| Bridge exits immediately | UUID env vars not set for the configured mode | Set the required UUIDs (see table above) |
[Errno -3] Temporary failure in name resolution (bridge loops on reconnect) |
seanerbus-reqgen container is stopped or was never started |
cd ../seanerbus && docker compose up -d — or make full-up which starts it automatically |
type X, got Y error in logs |
Message type mismatch — sender using wrong type ID | Check payloadType integer matches seanerbus_msgs constants (5-10) |
Connection refused on Ray Serve call |
Ray Serve not running | exa stack up --service ray-serving or make stack-up |
503 from control plane |
CONTROL_PLANE_TOKEN not set |
Set the token or start without auth |
| Stats show errors but no retrain | Drift cooldown not elapsed | Wait DRIFT_COOLDOWN seconds (default 300) |
| Dashboard shows "Bridge offline" | Bridge not running, or wrong probe URL | Start bridge (make seanerbus-up). In Docker the dashboard probes http://seanerbus-bridge:8003 (set via SEANERBUS_BRIDGE_STATUS_URL in docker-compose). If overriding, set the correct URL via Config → SeanerBUS → Bridge Status URL. |
Adding a New Message Handler¶
- Schema: add the new struct to
platform/clients/seanerbus_msgs/msg.capnpwith the next available type ID. - Python class: add
class MyMsgV1andclass MyResV1toplatform/clients/seanerbus_msgs/__init__.pywithfrom_capnp/to_capnpmethods, following theVectorReqV1pattern. - Handler: add
async def _handle_my_msg(req: MyMsgV1) -> MyResV1toplatform/clients/seanerbus_bridge.py. - UUID: add
MY_UUID = _parse_uuid("SEANERBUS_MY_UUID")in the config block. _run_reqres: add amy_connparameter, add theif MY_UUID is not None: tasks.append(...)block.main(): openmy_conn = Connection(...)andawait my_conn.connect()in bothreqresandbothbranches; include it inasyncio.gather().- Env vars: add
SEANERBUS_MY_UUID: "${SEANERBUS_MY_UUID:-}"to the docker-composeseanerbus-bridgeenv block and toseanerbus-bridge-upin the Makefile. - Docs: add a row to the message-type table in
docs/guides/seanerbus.mdand add the env var to the table above.