feat: ExpressDispatchAgent — tenant-scoped batch assignment + sequencing

New agent that owns the DoormileExpress dispatch flow. The normal B2C booking
flow is untouched; this handles express orders that a console operator has let
accumulate batch by batch, then dispatches in one go.

Triggered by express.dispatch_requested (the console's manual dispatch action).
For the tenant it: reads the pending bookings and the tenant's available riders
(Go internal API), distributes them across riders by proximity + load with a
radius guard, asks routes.workolik.com (Valhalla) for each rider's stop order,
and writes the assignments back through the Go internal API — which stays the
single writer of assignment state; the agent only decides who and what order.

- agents/express_dispatch_agent.py: the agent (mirrors DispatchAgent's NATS
  binding; pure consumer of the backend-owned EXPRESS stream).
- config/system_config.py: ROUTE_OPTIMIZER_URL.
- registered under JARVIS in main.py / agents package.
- tests: distribution (radius, load, cap, no-GPS fallback) and sequencing
  (single-stop, remap, optimizer-down, unknown-id) — deterministic, no network.

Gated off by default on the backend (EXPRESS_AGENT_ENABLED); EXPRESS_AGENT_
AUTONOMOUS=false runs it observe-only (logs the plan, writes nothing).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Suriyakumarvijayanayagam
2026-08-17 19:33:36 +05:30
parent ad753be7a5
commit aa4d4c6549
5 changed files with 542 additions and 1 deletions

View File

@@ -23,6 +23,11 @@ DB_PASSWORD = os.getenv("DB_PASSWORD", "Package@321#")
GO_API_BASE_URL = os.getenv("GO_API_BASE_URL", "http://localhost:8080")
INTERNAL_API_KEY = os.getenv("INTERNAL_API_KEY", "doormile-internal-2024")
# Route Optimization API (Valhalla-backed road routing). The ExpressDispatchAgent
# calls its Doormile endpoint (/api/v1/optimization/doormile/sequence) to order a
# rider's stops. Same service jupiter uses; Doormile speaks its own field names.
ROUTE_OPTIMIZER_URL = os.getenv("ROUTE_OPTIMIZER_URL", "https://routes.workolik.com")
# Agent Configuration
AGENT_CONFIG = {
"jarvis": {