update on the agents and registry side
This commit is contained in:
133
src/lib/assistant/skills/definitions/LateDispatchSkill.js
Normal file
133
src/lib/assistant/skills/definitions/LateDispatchSkill.js
Normal file
@@ -0,0 +1,133 @@
|
||||
// ==============================|| Skill: Late Dispatch Agent ||============================== //
|
||||
//
|
||||
// Flags orders that have been accepted/created but not yet dispatched to a rider
|
||||
// within the acceptable dispatch window during business hours.
|
||||
// Late dispatch at creation cascades into late delivery at the door.
|
||||
|
||||
import { parseDoormileTimestamp } from '@/lib/doormileTimestamp';
|
||||
|
||||
const PENDING_DISPATCH_STATUSES = new Set(['pending']);
|
||||
|
||||
const minutesBetween = (from, to) => {
|
||||
const a = parseDoormileTimestamp(from);
|
||||
const b = parseDoormileTimestamp(to);
|
||||
if (!a.isValid() || !b.isValid()) return null;
|
||||
return b.diff(a, 'minute');
|
||||
};
|
||||
|
||||
const ref = (row) => row?.orderid || row?.bookingno || (row?.bookingid ? `#${row.bookingid}` : '—');
|
||||
|
||||
export const LateDispatchSkill = {
|
||||
id: 'skill_late_dispatch',
|
||||
name: 'Late Dispatch Agent',
|
||||
description: 'Flags orders accepted but sitting in queue without dispatch for too long. Triggers auto-assignment to prevent cascading delays.',
|
||||
category: 'sla_management',
|
||||
icon: 'ShieldAlert',
|
||||
defaultEnabled: true,
|
||||
|
||||
thresholds: {
|
||||
lateDispatchMinutes: {
|
||||
label: 'Dispatch Deadline',
|
||||
description: 'Minutes after order creation before flagging as late dispatch.',
|
||||
value: 30,
|
||||
min: 10,
|
||||
max: 90,
|
||||
step: 5,
|
||||
unit: 'min'
|
||||
},
|
||||
criticalDispatchMinutes: {
|
||||
label: 'Critical Dispatch Deadline',
|
||||
description: 'Minutes after order creation before escalating to critical alert.',
|
||||
value: 60,
|
||||
min: 30,
|
||||
max: 180,
|
||||
step: 10,
|
||||
unit: 'min'
|
||||
}
|
||||
},
|
||||
|
||||
instructions: `
|
||||
- Orders waiting for dispatch beyond the threshold accumulate delay that can't be recovered at the doorstep.
|
||||
- Auto-assign to the nearest available rider immediately.
|
||||
- Prioritize orders with the earliest SLA deadlines when multiple are pending.
|
||||
`,
|
||||
|
||||
tools: [
|
||||
{
|
||||
name: 'trigger_auto_dispatch',
|
||||
description: 'Proposes auto-assigning late-pending orders to available riders in zone.',
|
||||
parameters: {
|
||||
type: 'object',
|
||||
properties: {
|
||||
bookingIds: { type: 'array', items: { type: 'number' }, description: 'Booking IDs requiring immediate dispatch' },
|
||||
reason: { type: 'string', description: 'Dispatch urgency reason' }
|
||||
},
|
||||
required: ['bookingIds']
|
||||
},
|
||||
handler: async (args) => ({
|
||||
success: true,
|
||||
proposal: {
|
||||
action: 'auto_dispatch',
|
||||
bookingIds: args.bookingIds,
|
||||
reason: args.reason || 'Late dispatch threshold exceeded',
|
||||
blastRadius: `Auto-assigns ${args.bookingIds?.length || 'late'} order(s) to nearest available zone riders. Rider notifications will be sent.`
|
||||
}
|
||||
})
|
||||
}
|
||||
],
|
||||
|
||||
evaluate: (rows = [], now, currentThresholds = {}) => {
|
||||
const lateThreshold = currentThresholds.lateDispatchMinutes ?? 30;
|
||||
const criticalThreshold = currentThresholds.criticalDispatchMinutes ?? 60;
|
||||
const findings = [];
|
||||
|
||||
const allPending = rows
|
||||
.filter((r) => PENDING_DISPATCH_STATUSES.has(String(r?.orderstatus || '').toLowerCase()))
|
||||
.map((r) => ({ row: r, waitMin: minutesBetween(r.orderdate, now) }))
|
||||
.filter((m) => m.waitMin !== null && m.waitMin >= lateThreshold)
|
||||
.sort((a, b) => b.waitMin - a.waitMin);
|
||||
|
||||
const critical = allPending.filter((m) => m.waitMin >= criticalThreshold);
|
||||
const warning = allPending.filter((m) => m.waitMin >= lateThreshold && m.waitMin < criticalThreshold);
|
||||
|
||||
if (critical.length) {
|
||||
findings.push({
|
||||
id: 'late_dispatch_critical',
|
||||
skillId: 'skill_late_dispatch',
|
||||
severity: 'critical',
|
||||
title: `${critical.length} order${critical.length === 1 ? '' : 's'} critically late for dispatch (>${criticalThreshold}m)`,
|
||||
why: `Order has been sitting in queue for over ${criticalThreshold} minutes with no rider assigned. Delivery SLA is at severe risk.`,
|
||||
count: critical.length,
|
||||
rows: critical.map((m) => m.row),
|
||||
detail: critical.slice(0, 8).map((m) => `${ref(m.row)} — waiting ${m.waitMin} min for dispatch`),
|
||||
proposal: {
|
||||
label: 'Auto-dispatch to available riders',
|
||||
tool: 'trigger_auto_dispatch',
|
||||
scope: critical.map((m) => m.row),
|
||||
blastRadius: 'Immediately assigns nearest available riders. Overrides manual queue priority.'
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (warning.length) {
|
||||
findings.push({
|
||||
id: 'late_dispatch_warning',
|
||||
skillId: 'skill_late_dispatch',
|
||||
severity: 'warning',
|
||||
title: `${warning.length} order${warning.length === 1 ? '' : 's'} approaching dispatch deadline (>${lateThreshold}m)`,
|
||||
why: `Order has been waiting ${lateThreshold}+ minutes for rider assignment. Act now to prevent SLA breach.`,
|
||||
count: warning.length,
|
||||
rows: warning.map((m) => m.row),
|
||||
detail: warning.slice(0, 8).map((m) => `${ref(m.row)} — waiting ${m.waitMin} min`),
|
||||
proposal: {
|
||||
label: 'Dispatch now',
|
||||
tool: 'trigger_auto_dispatch',
|
||||
scope: warning.map((m) => m.row),
|
||||
blastRadius: 'Assigns available riders to pending orders based on zone proximity.'
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return findings;
|
||||
}
|
||||
};
|
||||
Reference in New Issue
Block a user