updates on the agent flow
This commit is contained in:
294
src/lib/agentNetwork.js
Normal file
294
src/lib/agentNetwork.js
Normal file
@@ -0,0 +1,294 @@
|
||||
// ==============================|| The AI_engine agent swarm ||============================== //
|
||||
//
|
||||
// Nine agents — JARVIS plus eight specialised — living in the `AI_engine`
|
||||
// repository, not in this one and not in `doormile_backend`. They subscribe to
|
||||
// the same NATS JetStream the Go backend publishes to, reason about what they
|
||||
// see, and raise alerts. Every autonomy gate is off, so none of them writes to
|
||||
// the system.
|
||||
//
|
||||
// ─── Why this is a static table ──────────────────────────────────────────────
|
||||
//
|
||||
// `AI_engine` exposes no endpoint this console can call. There is a FastAPI
|
||||
// gateway inside it with a `GET /agents/status`, but it is not deployed beside
|
||||
// the console and nothing routes to it.
|
||||
//
|
||||
// So this file is a hand-maintained record, and it is deliberately shaped like
|
||||
// the response such an endpoint would return — one flat object per agent, the
|
||||
// same keys — so swapping it for a fetch is a change of source, not a rewrite
|
||||
// of the page.
|
||||
//
|
||||
// **It can go stale.** Every `lastSeen` here was read from
|
||||
// `AI_engine/logs/logiflow_2026-09-16.log`, the most recent run, which covers
|
||||
// 16–20 Sep 2026. Treat the counts as of that date, not as live. The page says
|
||||
// so on screen, because a status board that looks live and is not is worse than
|
||||
// one that admits its age.
|
||||
|
||||
/** When the figures below were taken. Rendered on the page — see the note above. */
|
||||
export const SNAPSHOT_AT = '2026-09-20T19:51:23+05:30';
|
||||
export const SNAPSHOT_LABEL = 'Last run · 16–20 Sep 2026';
|
||||
|
||||
/**
|
||||
* How an agent is woken.
|
||||
*
|
||||
* The split matters more than it looks: `nats` agents subscribe to the bus
|
||||
* themselves and can act the moment an event lands, while `task` agents only
|
||||
* run when JARVIS hands them something — and nothing in the Go backend asks
|
||||
* JARVIS for anything, which is why five of the nine have never run at all.
|
||||
*/
|
||||
export const TRIGGER = {
|
||||
nats: 'NATS event',
|
||||
natsPull: 'NATS pull',
|
||||
task: 'JARVIS task',
|
||||
internal: 'In-process',
|
||||
};
|
||||
|
||||
export const AGENTS = [
|
||||
{
|
||||
id: 'JARVIS',
|
||||
name: 'JARVIS',
|
||||
domain: 'orchestration',
|
||||
status: 'running',
|
||||
trigger: 'internal',
|
||||
file: 'core/agent.py',
|
||||
lines: null,
|
||||
tests: null,
|
||||
events: 0,
|
||||
load: 8,
|
||||
blurb: 'Master agent. Every other agent registers under it and receives work through it.',
|
||||
detail:
|
||||
'The orchestrator. It holds the registry of the eight specialised agents and routes tasks to them over an in-process message bus. It never touches NATS itself.',
|
||||
subscribes: [],
|
||||
publishes: 'Tasks to the five task-driven agents.',
|
||||
api: 'No — it delegates.',
|
||||
autonomy: 'not gated — routes only',
|
||||
gated: false,
|
||||
lastSeen:
|
||||
'Started cleanly on 16 Sep at 15:23 and registered eight sub-agents. It has routed nothing since, because nothing in the backend asks it to.',
|
||||
},
|
||||
{
|
||||
id: 'DISPATCH_AGENT',
|
||||
name: 'Dispatch',
|
||||
domain: 'dispatch',
|
||||
status: 'active',
|
||||
trigger: 'nats',
|
||||
file: 'agents/dispatch_agent.py',
|
||||
lines: 508,
|
||||
tests: true,
|
||||
events: 2,
|
||||
load: 62,
|
||||
blurb:
|
||||
'Counts assignment failures per geo cell, checks rider liveness, raises an ops alert when a zone stops getting covered.',
|
||||
detail:
|
||||
'The only agent doing real work. It watches every assignment outcome, buckets failures by geographic cell, and decides whether a zone has genuinely lost coverage or is simply quiet. Escalation is rate-limited to one alert per zone per day.',
|
||||
subscribes: ['booking.assigned', 'booking.assignment_failed'],
|
||||
publishes: 'Ops alerts, customer delay notices, escalations.',
|
||||
api: 'No — reads presence from Redis.',
|
||||
autonomy: 'DISPATCH_AGENT_AUTONOMOUS = false',
|
||||
gated: true,
|
||||
lastSeen:
|
||||
'Bound to stream ASSIGNMENTS on 16 Sep, and saw two assignment failures three seconds later. Both landed in zone unknown — the backend does not publish zone_id, which is exactly why a later commit started bucketing failures by geo cell instead.',
|
||||
},
|
||||
{
|
||||
id: 'EXCEPTION_AGENT',
|
||||
name: 'Exception',
|
||||
domain: 'exception_handling',
|
||||
status: 'degraded',
|
||||
trigger: 'natsPull',
|
||||
file: 'agents/exception_agent.py',
|
||||
lines: 818,
|
||||
tests: true,
|
||||
events: 0,
|
||||
load: 100,
|
||||
blurb: 'Stall detection. Spots a rider who has stopped moving or a booking that has stopped progressing.',
|
||||
detail:
|
||||
'The largest agent. Two pull loops read rider pings and stall events, and a sweep walks active bookings looking for ones that have gone quiet. When it finds a stalled rider it publishes miler.stalled for the dispatch side to act on.',
|
||||
subscribes: ['miler.location.updated', 'miler.stalled'],
|
||||
publishes: 'miler.stalled',
|
||||
api: 'Yes — api_post.',
|
||||
autonomy: 'EXCEPTION_AGENT_AUTONOMOUS = false',
|
||||
gated: true,
|
||||
lastSeen:
|
||||
'Lost NATS and never recovered. Both pull loops logged the same connection-closed error every two seconds for four days — 17,188 of the 18,146 lines in the log. The loop sleeps and retries a dead handle; it has no reconnect.',
|
||||
},
|
||||
{
|
||||
id: 'EXPRESS_DISPATCH_AGENT',
|
||||
name: 'Express Dispatch',
|
||||
domain: 'express_dispatch',
|
||||
status: 'idle',
|
||||
trigger: 'nats',
|
||||
file: 'agents/express_dispatch_agent.py',
|
||||
lines: 391,
|
||||
tests: true,
|
||||
events: 0,
|
||||
load: 0,
|
||||
blurb: 'Handles dispatch requests raised from the console and express booking path.',
|
||||
detail:
|
||||
'The console-side counterpart to Dispatch. It waits on express.dispatch_req and works a booking the express path could not place, reading and writing through the Go API rather than assigning directly.',
|
||||
subscribes: ['express.dispatch_req'],
|
||||
publishes: 'Dispatch outcomes back onto the bus.',
|
||||
api: 'Yes — api_get, api_post.',
|
||||
autonomy: 'EXPRESS_AGENT_AUTONOMOUS = false',
|
||||
gated: true,
|
||||
lastSeen: 'Bound its consumer on 16 Sep and has received nothing. No express dispatch request has ever been published.',
|
||||
},
|
||||
{
|
||||
id: 'ORDER_AGENT',
|
||||
name: 'Order',
|
||||
domain: 'order_management',
|
||||
status: 'idle',
|
||||
trigger: 'task',
|
||||
file: 'agents/order_agent.py',
|
||||
lines: 348,
|
||||
tests: false,
|
||||
events: 0,
|
||||
load: 0,
|
||||
blurb: 'Order intake, validation and categorisation.',
|
||||
detail:
|
||||
'Validates an incoming order, classifies it by zone and service type, and patches the result back. The most connected of the task agents — it reads, creates and patches through the Go API.',
|
||||
subscribes: [],
|
||||
publishes: 'Nothing on the bus.',
|
||||
api: 'Yes — get, post, patch.',
|
||||
autonomy: 'no gate — never reachable',
|
||||
gated: false,
|
||||
lastSeen: 'Started on 16 Sep. Zero tasks received, because nothing sends tasks.',
|
||||
},
|
||||
{
|
||||
id: 'ROUTE_OPTIMIZER',
|
||||
name: 'Route Optimizer',
|
||||
domain: 'route_optimization',
|
||||
status: 'idle',
|
||||
trigger: 'task',
|
||||
file: 'agents/route_optimizer_agent.py',
|
||||
lines: 465,
|
||||
tests: false,
|
||||
events: 0,
|
||||
load: 0,
|
||||
blurb: 'Builds delivery routes from zones and available hubs. Ten-minute cache.',
|
||||
detail:
|
||||
'Sequences waypoints into routes using zone membership and hub availability. This is the piece the Go backend has no equivalent for — HubBatchAssign decides who gets a booking, never what order a rider runs their stops in.',
|
||||
subscribes: [],
|
||||
publishes: 'Nothing on the bus.',
|
||||
api: 'No.',
|
||||
autonomy: 'no gate — never reachable',
|
||||
gated: false,
|
||||
lastSeen: 'Started on 16 Sep. Zero tasks received.',
|
||||
},
|
||||
{
|
||||
id: 'HUB_AGENT',
|
||||
name: 'Hub',
|
||||
domain: 'hub_management',
|
||||
status: 'idle',
|
||||
trigger: 'task',
|
||||
file: 'agents/hub_agent.py',
|
||||
lines: 460,
|
||||
tests: false,
|
||||
events: 0,
|
||||
load: 0,
|
||||
blurb: 'Hub operations — transit records, capacity, what is sitting where.',
|
||||
detail:
|
||||
'Tracks inventory at each hub and the transit records moving between them, and answers capacity questions about whether a hub can absorb more volume.',
|
||||
subscribes: [],
|
||||
publishes: 'Nothing on the bus.',
|
||||
api: 'No.',
|
||||
autonomy: 'no gate — never reachable',
|
||||
gated: false,
|
||||
lastSeen: 'Started on 16 Sep. Zero tasks received.',
|
||||
},
|
||||
{
|
||||
id: 'FLEET_AGENT',
|
||||
name: 'Fleet',
|
||||
domain: 'fleet_management',
|
||||
status: 'idle',
|
||||
trigger: 'task',
|
||||
file: 'agents/fleet_agent.py',
|
||||
lines: 349,
|
||||
tests: false,
|
||||
events: 0,
|
||||
load: 0,
|
||||
blurb: 'Vehicles: status, capacity and live tracking.',
|
||||
detail:
|
||||
"Holds vehicle state and assignment, and answers which vehicle can carry a given load. It overlaps with the Go backend's own Vehicle model, which is the live source today.",
|
||||
subscribes: [],
|
||||
publishes: 'Nothing on the bus.',
|
||||
api: 'No.',
|
||||
autonomy: 'no gate — never reachable',
|
||||
gated: false,
|
||||
lastSeen: 'Started on 16 Sep. Zero tasks received.',
|
||||
},
|
||||
{
|
||||
id: 'CUSTOMER_AGENT',
|
||||
name: 'Customer',
|
||||
domain: 'customer_communication',
|
||||
status: 'idle',
|
||||
trigger: 'task',
|
||||
file: 'agents/customer_agent.py',
|
||||
lines: 407,
|
||||
tests: false,
|
||||
events: 0,
|
||||
load: 0,
|
||||
blurb: 'Customer-facing notifications and tracking updates.',
|
||||
detail:
|
||||
'Composes and sends customer notifications across channels for booking, pickup and delivery events. Worth knowing before it is switched on: the backend has no SMS provider registered, so anything it sends by text goes to a log.',
|
||||
subscribes: [],
|
||||
publishes: 'Nothing on the bus.',
|
||||
api: 'Yes — api_get, api_post.',
|
||||
autonomy: 'no gate — never reachable',
|
||||
gated: false,
|
||||
lastSeen: 'Started on 16 Sep. Zero tasks received.',
|
||||
},
|
||||
];
|
||||
|
||||
/** What the Go backend publishes, and the bus it publishes onto. */
|
||||
export const SOURCE_SUBJECTS = ['booking.assigned', 'booking.assignment_failed', 'miler.location.updated'];
|
||||
|
||||
/**
|
||||
* The last run, as the log tells it.
|
||||
*
|
||||
* `count` is how many times a line repeated. Two of these repeated 8,594 times
|
||||
* each, which is the whole story of that run and the reason the repeat count is
|
||||
* on screen rather than in a tooltip.
|
||||
*/
|
||||
export const RUN_LOG = [
|
||||
{ at: '16 Sep 15:23:20', level: 'INFO', tone: 'info', count: null,
|
||||
text: "DispatchAgent bound 'booking.assigned' on stream 'ASSIGNMENTS'" },
|
||||
{ at: '16 Sep 15:23:20', level: 'INFO', tone: 'info', count: null,
|
||||
text: '9 agents initialized (JARVIS + 8 specialized)' },
|
||||
{ at: '16 Sep 15:23:23', level: 'INFO', tone: 'warn', count: 2,
|
||||
text: '[DISPATCH] zone unknown — failure count 1/3, 2/3 (backend publishes no zone_id)' },
|
||||
{ at: '20 Sep 19:51:23', level: 'ERROR', tone: 'error', count: 8594,
|
||||
text: 'Stalled pull loop error: nats: connection closed' },
|
||||
{ at: '20 Sep 19:51:23', level: 'ERROR', tone: 'error', count: 8594,
|
||||
text: 'Location pull loop error: nats: connection closed' },
|
||||
];
|
||||
|
||||
export const LOG_TOTAL_LINES = 18146;
|
||||
export const LOG_ERROR_LINES = 17188;
|
||||
|
||||
/** Agents that subscribe to the bus themselves, in the order they appear. */
|
||||
export const eventDrivenAgents = () => AGENTS.filter((a) => a.trigger === 'nats' || a.trigger === 'natsPull');
|
||||
|
||||
/** Agents that only run when JARVIS hands them work. */
|
||||
export const taskDrivenAgents = () => AGENTS.filter((a) => a.trigger === 'task');
|
||||
|
||||
export const agentById = (id) => AGENTS.find((a) => a.id === id) || AGENTS[0];
|
||||
|
||||
/**
|
||||
* The headline numbers.
|
||||
*
|
||||
* Derived rather than written down, so a change to the table above cannot leave
|
||||
* a stat contradicting the cards underneath it — which is how a dashboard ends
|
||||
* up saying "3 active" over three grey dots.
|
||||
*/
|
||||
export const networkStats = (agents = AGENTS) => {
|
||||
const working = agents.filter((a) => a.status === 'active').length;
|
||||
const gated = agents.filter((a) => a.gated).length;
|
||||
return {
|
||||
total: agents.length,
|
||||
working,
|
||||
degraded: agents.filter((a) => a.status === 'degraded').length,
|
||||
idle: agents.filter((a) => a.status === 'idle').length,
|
||||
events: agents.reduce((sum, a) => sum + (a.events || 0), 0),
|
||||
gated,
|
||||
autonomyOn: 0,
|
||||
};
|
||||
};
|
||||
Reference in New Issue
Block a user