Model Capacity Audit

Use this command before enabling the global model scheduler. It compares the configured worker roster with live Ollama /api/ps responses from the serving nodes, reports loaded model residency, and suggests resident-weight settings when a roster model is currently loaded on a node.

By default the command is read-only. It does not place work, mutate Valkey state, or change model residency.

Command

python -m aibs_backend.interfaces.cli.audit_model_capacity

Optional ad-hoc node inspection:

python -m aibs_backend.interfaces.cli.audit_model_capacity \
  --node-url http://10.4.3.16:11434 \
  --node-url http://10.4.3.17:11434

Machine-readable output:

python -m aibs_backend.interfaces.cli.audit_model_capacity --json

Active warm-up measurement:

python -m aibs_backend.interfaces.cli.audit_model_capacity \
  --warmup \
  --warmup-node-id box-c \
  --json

--warmup deliberately sends a tiny non-streaming Ollama request for each configured worker model, samples /api/ps while that model is resident, and then unloads the model with keep_alive=0. Use it only during an operator measurement window because it consumes model slots and may evict currently loaded models. Omit --warmup-node-id to measure every configured node.

What It Checks

  • AIBS_MODEL_ADMISSION_WORKER_ROSTER remains the source of model lanes and roles. The audit does not introduce a second model catalog.

  • Each worker should have measured resident_weight_gb and active_parameter_billions.

  • Ollama /api/ps supplies size_vram and size; the audit reports those as resident-weight suggestions for matching workers. It does not infer active parameter counts.

  • --warmup can produce resident-weight suggestions even when the boxes are idle and the configured worker models are cold.

  • Each configured cluster node should expose live /api/ps state.

  • The ready_to_enable_global_scheduler gate is independent of AIBS_MODEL_CLUSTER_ENABLED. That lets operators audit first, then enable only after Valkey coordination, node budgets, worker measurements, and the node-residency monitor are present.

Optional Host Telemetry Agent

For richer System Health charts, install the lightweight host telemetry agent on each AI box and expose it only on the trusted management network:

python scripts/aibs_node_telemetry_agent.py --host 0.0.0.0 --port 8765

The agent is read-only. It reports CPU, memory, GPU busy, raw VRAM, raw GTT, and combined GPU memory from Linux /proc and DRM sysfs. AMD APU/iGPU deployments can place model buffers in GTT, so operator-facing capacity should use combined GPU memory rather than raw VRAM alone. The agent does not run model calls, mutate Valkey, or make placement decisions. The backend polls the URLs configured through AIBS_MODEL_CLUSTER_NODE_HOST_TELEMETRY_URLS and stores those values beside the fenced residency snapshots for operator visibility.

If the agent is not configured or unavailable, the node remains schedulable when its fenced residency facts are fresh. System Health will show host telemetry as unavailable for that node.

Valkey Placement Test Gate

The global placement primitive requires Valkey 8+. The Valkey Lua scripts use server-side TIME while writing placement state so all API processes share one clock authority. Do not enable the global scheduler against older Redis-compatible deployments.

Run the atomic placement tests against a real Valkey instance:

AIBS_TEST_VALKEY_URL=redis://localhost:6379/15 \
python -m pytest tests/infrastructure/model_scheduling/test_redis_atomic_placement.py

Without AIBS_TEST_VALKEY_URL, the real Valkey behavior test is skipped and only the deterministic in-memory/fake-client tests run. The legacy AIBS_TEST_REDIS_URL name remains a temporary compatibility fallback.

Residency Monitor Takeover Gate

Before enabling AIBS_MODEL_CLUSTER_ENABLED=true, prove that the elected residency monitor can die without leaving stale node health trusted by the placement scheduler:

python -m aibs_backend.interfaces.cli.prove_model_monitor_takeover --json

The proof command:

  1. creates an isolated Valkey namespace;

  2. starts monitor owner A as a subprocess;

  3. waits for owner A to publish healthy samples for every enabled model node;

  4. starts monitor owner B as a standby;

  5. hard-kills owner A, without graceful lease release;

  6. waits for owner B to publish newer healthy samples;

  7. verifies correctness: no pre-kill owner loss and no successful fenced writes from a deposed owner;

  8. measures handover duration separately from correctness;

  9. fails the availability gate unless the target deployment topology observes fresh replacement samples inside AIBS_MODEL_CLUSTER_MAX_RESIDENCY_STALENESS_MS;

  10. captures owner stderr and classifies expected shutdown artifacts separately from real owner churn;

  11. deletes the isolated proof namespace so proof scheduler hashes do not accumulate in Valkey.

The safe timing bound is:

residency_monitor_poll_interval
+ residency_monitor_lease_ttl
+ residency_monitor_standby_retry
+ 2 * residency_monitor_poll_timeout
<= max_residency_staleness

With the current recommended values, 3 + 6 + 1 + 2 * 2 = 14s, which fits inside the 15s staleness window.

For deployment-grade evidence, run the proof repeatedly and require every run to pass:

python -m aibs_backend.interfaces.cli.prove_model_monitor_takeover --json --repeat 3

When running from the local dev machine through an SSH tunnel to Valkey, use the split contract explicitly:

python -m aibs_backend.interfaces.cli.prove_model_monitor_takeover \
  --json --repeat 3 --handover-gate report-only

In that mode, correctness still gates the result. Long handover duration is reported as an availability finding because the dev tunnel is not the production transport. On the demo topology, where the backend runs on an AI box and reaches Valkey over LAN, use the default strict handover gate before enabling shared traffic.

Treat passed=true as a scheduler liveness gate, not as full production approval. It proves election/failover and fresh node telemetry. It does not prove answer quality or routed LLM behavior under real user load. If correctness fails, do not enable the cluster scheduler. If only the dev tunnel handover report fails, document the admission-pause duration and rerun the strict proof from the target topology.

Controlled Enable Smoke Test

After the audit and takeover proof are green, enable the scheduler only in the target test environment:

AIBS_MODEL_CLUSTER_ENABLED=true

Then restart the API process. Do not rely on --reload to notice a .env-only change; some reload configurations watch Python files but not environment files.

Run:

python -m aibs_backend.interfaces.cli.audit_model_capacity --json

Expected enable-state result:

  • cluster_enabled=true

  • ready_to_enable_global_scheduler=true

  • ready_for_global_scheduler=true

Then run the real API path:

  1. one narrow or single-document query;

  2. one broad or agentic query;

  3. two concurrent broad queries from separate conversations or users when possible.

Acceptance evidence:

  • global placement acquisition/release logs appear;

  • leases include node_id and node_base_url;

  • answer quality does not regress;

  • no request remains stuck in answering state after completion/cancel;

  • Valkey active leases are released or expire according to TTL.

If placement acquisition works but release warnings appear, stop before demo and inspect the placement release path. A scheduler that admits work but leaks capacity is not enterprise-stable.