Skip to content

Commit 7ac7e02

Browse files
feat: Add advanced agent tools for Coperniq operations
New tools added to Claude chat API: - assign_technician: Quick technician assignment with auto-scheduling - convert_request_to_work_order: One-click request conversion - add_work_order_note: Append notes with timestamps - Enhanced update_work_order: Now supports assigneeId and notes fields These tools enable natural language commands like: - "Assign Mike to work order 123" - "Convert request 456 to a work order and assign to Sarah" - "Add a note to WO 789: Customer rescheduled to Friday" Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
1 parent 0f877ce commit 7ac7e02

1 file changed

Lines changed: 126 additions & 2 deletions

File tree

  • chat_ui/frontend/src/app/api/chat

chat_ui/frontend/src/app/api/chat/route.ts

Lines changed: 126 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -596,20 +596,63 @@ const TOOLS = [
596596
// ═══════════════════════════════════════════════════════════════════════════
597597
{
598598
name: 'update_work_order',
599-
description: 'Update an existing work order.',
599+
description: 'Update an existing work order. Use to change status, assign technicians, add notes, update schedule.',
600600
input_schema: {
601601
type: 'object',
602602
properties: {
603603
workOrderId: { type: 'number', description: 'Work order ID' },
604604
title: { type: 'string', description: 'New title' },
605605
description: { type: 'string', description: 'New description' },
606-
status: { type: 'string', description: 'New status' },
606+
status: { type: 'string', enum: ['pending', 'scheduled', 'in_progress', 'completed', 'cancelled'], description: 'New status' },
607607
priority: { type: 'string', enum: ['low', 'medium', 'high', 'urgent'] },
608608
scheduledDate: { type: 'string', description: 'Scheduled date (ISO 8601)' },
609+
assigneeId: { type: 'number', description: 'User/technician ID to assign to this work order' },
610+
notes: { type: 'string', description: 'Notes or comments to add to the work order' },
609611
},
610612
required: ['workOrderId'],
611613
},
612614
},
615+
{
616+
name: 'assign_technician',
617+
description: 'Assign a technician to a work order. Shortcut for update_work_order with assignee.',
618+
input_schema: {
619+
type: 'object',
620+
properties: {
621+
workOrderId: { type: 'number', description: 'Work order ID' },
622+
technicianId: { type: 'number', description: 'Technician/user ID' },
623+
scheduledDate: { type: 'string', description: 'Optional: Scheduled date (ISO 8601)' },
624+
},
625+
required: ['workOrderId', 'technicianId'],
626+
},
627+
},
628+
{
629+
name: 'convert_request_to_work_order',
630+
description: 'Convert a service request into a work order. Creates a work order linked to the request.',
631+
input_schema: {
632+
type: 'object',
633+
properties: {
634+
requestId: { type: 'number', description: 'Service request ID to convert' },
635+
title: { type: 'string', description: 'Work order title (defaults to request title)' },
636+
priority: { type: 'string', enum: ['low', 'medium', 'high', 'urgent'], description: 'Work order priority' },
637+
assigneeId: { type: 'number', description: 'Optional: Technician to assign' },
638+
scheduledDate: { type: 'string', description: 'Optional: Scheduled date' },
639+
},
640+
required: ['requestId'],
641+
},
642+
},
643+
{
644+
name: 'add_work_order_note',
645+
description: 'Add a note or comment to an existing work order.',
646+
input_schema: {
647+
type: 'object',
648+
properties: {
649+
workOrderId: { type: 'number', description: 'Work order ID' },
650+
note: { type: 'string', description: 'Note content to add' },
651+
isInternal: { type: 'boolean', description: 'Whether note is internal only (default: false)' },
652+
},
653+
required: ['workOrderId', 'note'],
654+
},
655+
},
613656
{
614657
name: 'delete_work_order',
615658
description: 'Delete a work order.',
@@ -1254,6 +1297,87 @@ async function executeTool(toolName: string, toolInput: Record<string, unknown>,
12541297
return JSON.stringify({ success: true, work_order: result });
12551298
}
12561299

1300+
case 'assign_technician': {
1301+
const { workOrderId, technicianId, scheduledDate } = toolInput;
1302+
const updateData: Record<string, unknown> = { assigneeId: technicianId };
1303+
if (scheduledDate) updateData.scheduledDate = scheduledDate;
1304+
if (!updateData.status) updateData.status = 'scheduled'; // Auto-set to scheduled when assigning
1305+
1306+
const result = await coperniqFetch(`/work-orders/${workOrderId}`, apiKey, {
1307+
method: 'PATCH',
1308+
body: JSON.stringify(updateData),
1309+
});
1310+
return JSON.stringify({ success: true, work_order: result, message: 'Technician assigned successfully' });
1311+
}
1312+
1313+
case 'convert_request_to_work_order': {
1314+
const { requestId, title, priority, assigneeId, scheduledDate } = toolInput;
1315+
1316+
// First, get the request details
1317+
const requestData = await coperniqFetch(`/requests/${requestId}`, apiKey) as Record<string, unknown>;
1318+
1319+
// Create work order from request data
1320+
const workOrderData: Record<string, unknown> = {
1321+
title: title || requestData.title || `Work Order from Request #${requestId}`,
1322+
description: requestData.description || '',
1323+
priority: priority || requestData.priority || 'medium',
1324+
requestId: requestId, // Link to original request
1325+
};
1326+
1327+
if (assigneeId) workOrderData.assigneeId = assigneeId;
1328+
if (scheduledDate) {
1329+
workOrderData.scheduledDate = scheduledDate;
1330+
workOrderData.status = 'scheduled';
1331+
}
1332+
1333+
// Create the work order
1334+
const workOrder = await coperniqFetch(`/requests/${requestId}/work-orders`, apiKey, {
1335+
method: 'POST',
1336+
body: JSON.stringify(workOrderData),
1337+
});
1338+
1339+
// Update request status to 'converted'
1340+
try {
1341+
await coperniqFetch(`/requests/${requestId}`, apiKey, {
1342+
method: 'PATCH',
1343+
body: JSON.stringify({ status: 'converted' }),
1344+
});
1345+
} catch {
1346+
// Request status update is optional
1347+
}
1348+
1349+
return JSON.stringify({
1350+
success: true,
1351+
work_order: workOrder,
1352+
message: `Created work order from request #${requestId}`
1353+
});
1354+
}
1355+
1356+
case 'add_work_order_note': {
1357+
const { workOrderId, note, isInternal } = toolInput;
1358+
1359+
// Try to add note via comments/notes endpoint, fallback to updating description
1360+
try {
1361+
const result = await coperniqFetch(`/work-orders/${workOrderId}/notes`, apiKey, {
1362+
method: 'POST',
1363+
body: JSON.stringify({ content: note, isInternal: isInternal || false }),
1364+
});
1365+
return JSON.stringify({ success: true, note: result });
1366+
} catch {
1367+
// Fallback: Append to description
1368+
const workOrder = await coperniqFetch(`/work-orders/${workOrderId}`, apiKey) as Record<string, unknown>;
1369+
const existingDesc = String(workOrder.description || '');
1370+
const timestamp = new Date().toISOString();
1371+
const newDesc = `${existingDesc}\n\n[Note - ${timestamp}]: ${note}`;
1372+
1373+
const result = await coperniqFetch(`/work-orders/${workOrderId}`, apiKey, {
1374+
method: 'PATCH',
1375+
body: JSON.stringify({ description: newDesc }),
1376+
});
1377+
return JSON.stringify({ success: true, work_order: result, message: 'Note added to description' });
1378+
}
1379+
}
1380+
12571381
case 'delete_work_order': {
12581382
await coperniqFetch(`/work-orders/${toolInput.workOrderId}`, apiKey, {
12591383
method: 'DELETE',

0 commit comments

Comments
 (0)