Commit
Β·
a7365ce
1
Parent(s):
50e460a
Add order and driver management tools with update and delete functionalities
Browse files- chat/providers/gemini_provider.py +77 -0
- chat/tools.py +502 -0
- ui/app.py +334 -0
chat/providers/gemini_provider.py
CHANGED
|
@@ -83,6 +83,10 @@ You: [geocode_address] β "OK geocoded, now creating..." β WRONG!
|
|
| 83 |
- geocode_address: Convert address to GPS coordinates
|
| 84 |
- create_order: Create customer delivery order (REQUIRES geocoded address)
|
| 85 |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 86 |
**Order Querying (INTERACTIVE):**
|
| 87 |
- count_orders: Count orders with optional filters
|
| 88 |
- fetch_orders: Fetch N orders with pagination and filters
|
|
@@ -93,6 +97,10 @@ You: [geocode_address] β "OK geocoded, now creating..." β WRONG!
|
|
| 93 |
**Driver Creation:**
|
| 94 |
- create_driver: Add new driver/delivery man to fleet
|
| 95 |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 96 |
**Driver Querying (INTERACTIVE):**
|
| 97 |
- count_drivers: Count drivers with optional filters
|
| 98 |
- fetch_drivers: Fetch N drivers with pagination and filters
|
|
@@ -532,6 +540,75 @@ You: [get_driver_details with driver_id=DRV-20251114163800] β [Display complet
|
|
| 532 |
},
|
| 533 |
required=[]
|
| 534 |
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 535 |
)
|
| 536 |
]
|
| 537 |
)
|
|
|
|
| 83 |
- geocode_address: Convert address to GPS coordinates
|
| 84 |
- create_order: Create customer delivery order (REQUIRES geocoded address)
|
| 85 |
|
| 86 |
+
**Order Management:**
|
| 87 |
+
- update_order: Update existing order's details (status, priority, address, etc.)
|
| 88 |
+
- delete_order: Permanently delete an order (requires confirm=true)
|
| 89 |
+
|
| 90 |
**Order Querying (INTERACTIVE):**
|
| 91 |
- count_orders: Count orders with optional filters
|
| 92 |
- fetch_orders: Fetch N orders with pagination and filters
|
|
|
|
| 97 |
**Driver Creation:**
|
| 98 |
- create_driver: Add new driver/delivery man to fleet
|
| 99 |
|
| 100 |
+
**Driver Management:**
|
| 101 |
+
- update_driver: Update existing driver's details (name, status, vehicle, location, etc.)
|
| 102 |
+
- delete_driver: Permanently delete a driver (requires confirm=true)
|
| 103 |
+
|
| 104 |
**Driver Querying (INTERACTIVE):**
|
| 105 |
- count_drivers: Count drivers with optional filters
|
| 106 |
- fetch_drivers: Fetch N drivers with pagination and filters
|
|
|
|
| 540 |
},
|
| 541 |
required=[]
|
| 542 |
)
|
| 543 |
+
),
|
| 544 |
+
genai.protos.FunctionDeclaration(
|
| 545 |
+
name="update_order",
|
| 546 |
+
description="Update an existing order's details. You can update any combination of fields. Only provide the fields you want to change.",
|
| 547 |
+
parameters=genai.protos.Schema(
|
| 548 |
+
type=genai.protos.Type.OBJECT,
|
| 549 |
+
properties={
|
| 550 |
+
"order_id": genai.protos.Schema(type=genai.protos.Type.STRING, description="Order ID to update"),
|
| 551 |
+
"customer_name": genai.protos.Schema(type=genai.protos.Type.STRING, description="Updated customer name"),
|
| 552 |
+
"customer_phone": genai.protos.Schema(type=genai.protos.Type.STRING, description="Updated customer phone"),
|
| 553 |
+
"customer_email": genai.protos.Schema(type=genai.protos.Type.STRING, description="Updated customer email"),
|
| 554 |
+
"delivery_address": genai.protos.Schema(type=genai.protos.Type.STRING, description="Updated delivery address"),
|
| 555 |
+
"delivery_lat": genai.protos.Schema(type=genai.protos.Type.NUMBER, description="Updated delivery latitude"),
|
| 556 |
+
"delivery_lng": genai.protos.Schema(type=genai.protos.Type.NUMBER, description="Updated delivery longitude"),
|
| 557 |
+
"status": genai.protos.Schema(type=genai.protos.Type.STRING, description="Updated order status (pending/assigned/in_transit/delivered/failed/cancelled)"),
|
| 558 |
+
"priority": genai.protos.Schema(type=genai.protos.Type.STRING, description="Updated priority (standard/express/urgent)"),
|
| 559 |
+
"special_instructions": genai.protos.Schema(type=genai.protos.Type.STRING, description="Updated special instructions"),
|
| 560 |
+
"time_window_end": genai.protos.Schema(type=genai.protos.Type.STRING, description="Updated delivery deadline"),
|
| 561 |
+
"payment_status": genai.protos.Schema(type=genai.protos.Type.STRING, description="Updated payment status (pending/paid/cod)"),
|
| 562 |
+
"weight_kg": genai.protos.Schema(type=genai.protos.Type.NUMBER, description="Updated weight in kg"),
|
| 563 |
+
"order_value": genai.protos.Schema(type=genai.protos.Type.NUMBER, description="Updated order value")
|
| 564 |
+
},
|
| 565 |
+
required=["order_id"]
|
| 566 |
+
)
|
| 567 |
+
),
|
| 568 |
+
genai.protos.FunctionDeclaration(
|
| 569 |
+
name="delete_order",
|
| 570 |
+
description="Permanently delete an order from the database. This action cannot be undone. Use with caution.",
|
| 571 |
+
parameters=genai.protos.Schema(
|
| 572 |
+
type=genai.protos.Type.OBJECT,
|
| 573 |
+
properties={
|
| 574 |
+
"order_id": genai.protos.Schema(type=genai.protos.Type.STRING, description="Order ID to delete"),
|
| 575 |
+
"confirm": genai.protos.Schema(type=genai.protos.Type.BOOLEAN, description="Must be true to confirm deletion")
|
| 576 |
+
},
|
| 577 |
+
required=["order_id", "confirm"]
|
| 578 |
+
)
|
| 579 |
+
),
|
| 580 |
+
genai.protos.FunctionDeclaration(
|
| 581 |
+
name="update_driver",
|
| 582 |
+
description="Update an existing driver's details. You can update any combination of fields. Only provide the fields you want to change.",
|
| 583 |
+
parameters=genai.protos.Schema(
|
| 584 |
+
type=genai.protos.Type.OBJECT,
|
| 585 |
+
properties={
|
| 586 |
+
"driver_id": genai.protos.Schema(type=genai.protos.Type.STRING, description="Driver ID to update"),
|
| 587 |
+
"name": genai.protos.Schema(type=genai.protos.Type.STRING, description="Updated driver name"),
|
| 588 |
+
"phone": genai.protos.Schema(type=genai.protos.Type.STRING, description="Updated phone"),
|
| 589 |
+
"email": genai.protos.Schema(type=genai.protos.Type.STRING, description="Updated email"),
|
| 590 |
+
"status": genai.protos.Schema(type=genai.protos.Type.STRING, description="Updated status (active/busy/offline/unavailable)"),
|
| 591 |
+
"vehicle_type": genai.protos.Schema(type=genai.protos.Type.STRING, description="Updated vehicle type"),
|
| 592 |
+
"vehicle_plate": genai.protos.Schema(type=genai.protos.Type.STRING, description="Updated vehicle plate"),
|
| 593 |
+
"capacity_kg": genai.protos.Schema(type=genai.protos.Type.NUMBER, description="Updated capacity in kg"),
|
| 594 |
+
"capacity_m3": genai.protos.Schema(type=genai.protos.Type.NUMBER, description="Updated capacity in mΒ³"),
|
| 595 |
+
"current_lat": genai.protos.Schema(type=genai.protos.Type.NUMBER, description="Updated current latitude"),
|
| 596 |
+
"current_lng": genai.protos.Schema(type=genai.protos.Type.NUMBER, description="Updated current longitude")
|
| 597 |
+
},
|
| 598 |
+
required=["driver_id"]
|
| 599 |
+
)
|
| 600 |
+
),
|
| 601 |
+
genai.protos.FunctionDeclaration(
|
| 602 |
+
name="delete_driver",
|
| 603 |
+
description="Permanently delete a driver from the database. This action cannot be undone. Use with caution.",
|
| 604 |
+
parameters=genai.protos.Schema(
|
| 605 |
+
type=genai.protos.Type.OBJECT,
|
| 606 |
+
properties={
|
| 607 |
+
"driver_id": genai.protos.Schema(type=genai.protos.Type.STRING, description="Driver ID to delete"),
|
| 608 |
+
"confirm": genai.protos.Schema(type=genai.protos.Type.BOOLEAN, description="Must be true to confirm deletion")
|
| 609 |
+
},
|
| 610 |
+
required=["driver_id", "confirm"]
|
| 611 |
+
)
|
| 612 |
)
|
| 613 |
]
|
| 614 |
)
|
chat/tools.py
CHANGED
|
@@ -375,6 +375,171 @@ TOOLS_SCHEMA = [
|
|
| 375 |
},
|
| 376 |
"required": []
|
| 377 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 378 |
}
|
| 379 |
]
|
| 380 |
|
|
@@ -417,6 +582,14 @@ def execute_tool(tool_name: str, tool_input: dict) -> dict:
|
|
| 417 |
return handle_search_drivers(tool_input)
|
| 418 |
elif tool_name == "get_available_drivers":
|
| 419 |
return handle_get_available_drivers(tool_input)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 420 |
else:
|
| 421 |
return {
|
| 422 |
"success": False,
|
|
@@ -647,6 +820,335 @@ def handle_create_driver(tool_input: dict) -> dict:
|
|
| 647 |
}
|
| 648 |
|
| 649 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 650 |
def handle_count_orders(tool_input: dict) -> dict:
|
| 651 |
"""
|
| 652 |
Execute count orders tool
|
|
|
|
| 375 |
},
|
| 376 |
"required": []
|
| 377 |
}
|
| 378 |
+
},
|
| 379 |
+
{
|
| 380 |
+
"name": "update_order",
|
| 381 |
+
"description": "Update an existing order's details. You can update any combination of fields. Only provide the fields you want to change.",
|
| 382 |
+
"input_schema": {
|
| 383 |
+
"type": "object",
|
| 384 |
+
"properties": {
|
| 385 |
+
"order_id": {
|
| 386 |
+
"type": "string",
|
| 387 |
+
"description": "Order ID to update (e.g., 'ORD-20250114123456')"
|
| 388 |
+
},
|
| 389 |
+
"customer_name": {
|
| 390 |
+
"type": "string",
|
| 391 |
+
"description": "Updated customer name"
|
| 392 |
+
},
|
| 393 |
+
"customer_phone": {
|
| 394 |
+
"type": "string",
|
| 395 |
+
"description": "Updated customer phone number"
|
| 396 |
+
},
|
| 397 |
+
"customer_email": {
|
| 398 |
+
"type": "string",
|
| 399 |
+
"description": "Updated customer email address"
|
| 400 |
+
},
|
| 401 |
+
"delivery_address": {
|
| 402 |
+
"type": "string",
|
| 403 |
+
"description": "Updated delivery address"
|
| 404 |
+
},
|
| 405 |
+
"delivery_lat": {
|
| 406 |
+
"type": "number",
|
| 407 |
+
"description": "Updated delivery latitude (required if updating address)"
|
| 408 |
+
},
|
| 409 |
+
"delivery_lng": {
|
| 410 |
+
"type": "number",
|
| 411 |
+
"description": "Updated delivery longitude (required if updating address)"
|
| 412 |
+
},
|
| 413 |
+
"status": {
|
| 414 |
+
"type": "string",
|
| 415 |
+
"description": "Updated order status",
|
| 416 |
+
"enum": ["pending", "assigned", "in_transit", "delivered", "failed", "cancelled"]
|
| 417 |
+
},
|
| 418 |
+
"priority": {
|
| 419 |
+
"type": "string",
|
| 420 |
+
"description": "Updated priority level",
|
| 421 |
+
"enum": ["standard", "express", "urgent"]
|
| 422 |
+
},
|
| 423 |
+
"special_instructions": {
|
| 424 |
+
"type": "string",
|
| 425 |
+
"description": "Updated special delivery instructions"
|
| 426 |
+
},
|
| 427 |
+
"time_window_end": {
|
| 428 |
+
"type": "string",
|
| 429 |
+
"description": "Updated delivery deadline (ISO format datetime)"
|
| 430 |
+
},
|
| 431 |
+
"payment_status": {
|
| 432 |
+
"type": "string",
|
| 433 |
+
"description": "Updated payment status",
|
| 434 |
+
"enum": ["pending", "paid", "cod"]
|
| 435 |
+
},
|
| 436 |
+
"weight_kg": {
|
| 437 |
+
"type": "number",
|
| 438 |
+
"description": "Updated package weight in kilograms"
|
| 439 |
+
},
|
| 440 |
+
"order_value": {
|
| 441 |
+
"type": "number",
|
| 442 |
+
"description": "Updated order value in currency"
|
| 443 |
+
}
|
| 444 |
+
},
|
| 445 |
+
"required": ["order_id"]
|
| 446 |
+
}
|
| 447 |
+
},
|
| 448 |
+
{
|
| 449 |
+
"name": "delete_order",
|
| 450 |
+
"description": "Permanently delete an order from the database. This action cannot be undone. Use with caution.",
|
| 451 |
+
"input_schema": {
|
| 452 |
+
"type": "object",
|
| 453 |
+
"properties": {
|
| 454 |
+
"order_id": {
|
| 455 |
+
"type": "string",
|
| 456 |
+
"description": "Order ID to delete (e.g., 'ORD-20250114123456')"
|
| 457 |
+
},
|
| 458 |
+
"confirm": {
|
| 459 |
+
"type": "boolean",
|
| 460 |
+
"description": "Must be set to true to confirm deletion"
|
| 461 |
+
}
|
| 462 |
+
},
|
| 463 |
+
"required": ["order_id", "confirm"]
|
| 464 |
+
}
|
| 465 |
+
},
|
| 466 |
+
{
|
| 467 |
+
"name": "update_driver",
|
| 468 |
+
"description": "Update an existing driver's details. You can update any combination of fields. Only provide the fields you want to change.",
|
| 469 |
+
"input_schema": {
|
| 470 |
+
"type": "object",
|
| 471 |
+
"properties": {
|
| 472 |
+
"driver_id": {
|
| 473 |
+
"type": "string",
|
| 474 |
+
"description": "Driver ID to update (e.g., 'DRV-20250114123456')"
|
| 475 |
+
},
|
| 476 |
+
"name": {
|
| 477 |
+
"type": "string",
|
| 478 |
+
"description": "Updated driver name"
|
| 479 |
+
},
|
| 480 |
+
"phone": {
|
| 481 |
+
"type": "string",
|
| 482 |
+
"description": "Updated phone number"
|
| 483 |
+
},
|
| 484 |
+
"email": {
|
| 485 |
+
"type": "string",
|
| 486 |
+
"description": "Updated email address"
|
| 487 |
+
},
|
| 488 |
+
"status": {
|
| 489 |
+
"type": "string",
|
| 490 |
+
"description": "Updated driver status",
|
| 491 |
+
"enum": ["active", "busy", "offline", "unavailable"]
|
| 492 |
+
},
|
| 493 |
+
"vehicle_type": {
|
| 494 |
+
"type": "string",
|
| 495 |
+
"description": "Updated vehicle type"
|
| 496 |
+
},
|
| 497 |
+
"vehicle_plate": {
|
| 498 |
+
"type": "string",
|
| 499 |
+
"description": "Updated vehicle license plate"
|
| 500 |
+
},
|
| 501 |
+
"capacity_kg": {
|
| 502 |
+
"type": "number",
|
| 503 |
+
"description": "Updated cargo capacity in kilograms"
|
| 504 |
+
},
|
| 505 |
+
"capacity_m3": {
|
| 506 |
+
"type": "number",
|
| 507 |
+
"description": "Updated cargo capacity in cubic meters"
|
| 508 |
+
},
|
| 509 |
+
"skills": {
|
| 510 |
+
"type": "array",
|
| 511 |
+
"items": {"type": "string"},
|
| 512 |
+
"description": "Updated list of driver skills/certifications"
|
| 513 |
+
},
|
| 514 |
+
"current_lat": {
|
| 515 |
+
"type": "number",
|
| 516 |
+
"description": "Updated current latitude"
|
| 517 |
+
},
|
| 518 |
+
"current_lng": {
|
| 519 |
+
"type": "number",
|
| 520 |
+
"description": "Updated current longitude"
|
| 521 |
+
}
|
| 522 |
+
},
|
| 523 |
+
"required": ["driver_id"]
|
| 524 |
+
}
|
| 525 |
+
},
|
| 526 |
+
{
|
| 527 |
+
"name": "delete_driver",
|
| 528 |
+
"description": "Permanently delete a driver from the database. This action cannot be undone. Use with caution.",
|
| 529 |
+
"input_schema": {
|
| 530 |
+
"type": "object",
|
| 531 |
+
"properties": {
|
| 532 |
+
"driver_id": {
|
| 533 |
+
"type": "string",
|
| 534 |
+
"description": "Driver ID to delete (e.g., 'DRV-20250114123456')"
|
| 535 |
+
},
|
| 536 |
+
"confirm": {
|
| 537 |
+
"type": "boolean",
|
| 538 |
+
"description": "Must be set to true to confirm deletion"
|
| 539 |
+
}
|
| 540 |
+
},
|
| 541 |
+
"required": ["driver_id", "confirm"]
|
| 542 |
+
}
|
| 543 |
}
|
| 544 |
]
|
| 545 |
|
|
|
|
| 582 |
return handle_search_drivers(tool_input)
|
| 583 |
elif tool_name == "get_available_drivers":
|
| 584 |
return handle_get_available_drivers(tool_input)
|
| 585 |
+
elif tool_name == "update_order":
|
| 586 |
+
return handle_update_order(tool_input)
|
| 587 |
+
elif tool_name == "delete_order":
|
| 588 |
+
return handle_delete_order(tool_input)
|
| 589 |
+
elif tool_name == "update_driver":
|
| 590 |
+
return handle_update_driver(tool_input)
|
| 591 |
+
elif tool_name == "delete_driver":
|
| 592 |
+
return handle_delete_driver(tool_input)
|
| 593 |
else:
|
| 594 |
return {
|
| 595 |
"success": False,
|
|
|
|
| 820 |
}
|
| 821 |
|
| 822 |
|
| 823 |
+
def handle_update_order(tool_input: dict) -> dict:
|
| 824 |
+
"""
|
| 825 |
+
Execute order update tool
|
| 826 |
+
|
| 827 |
+
Args:
|
| 828 |
+
tool_input: Dict with order_id and fields to update
|
| 829 |
+
|
| 830 |
+
Returns:
|
| 831 |
+
Update result
|
| 832 |
+
"""
|
| 833 |
+
import json
|
| 834 |
+
|
| 835 |
+
order_id = tool_input.get("order_id")
|
| 836 |
+
|
| 837 |
+
# Validate required field
|
| 838 |
+
if not order_id:
|
| 839 |
+
return {
|
| 840 |
+
"success": False,
|
| 841 |
+
"error": "Missing required field: order_id"
|
| 842 |
+
}
|
| 843 |
+
|
| 844 |
+
# Check if order exists
|
| 845 |
+
check_query = "SELECT order_id FROM orders WHERE order_id = %s"
|
| 846 |
+
existing = execute_query(check_query, (order_id,))
|
| 847 |
+
|
| 848 |
+
if not existing:
|
| 849 |
+
return {
|
| 850 |
+
"success": False,
|
| 851 |
+
"error": f"Order {order_id} not found"
|
| 852 |
+
}
|
| 853 |
+
|
| 854 |
+
# Auto-geocode if delivery address is updated without coordinates
|
| 855 |
+
if "delivery_address" in tool_input and ("delivery_lat" not in tool_input or "delivery_lng" not in tool_input):
|
| 856 |
+
from chat.geocoding import GeocodingService
|
| 857 |
+
geocoding_service = GeocodingService()
|
| 858 |
+
|
| 859 |
+
try:
|
| 860 |
+
geocode_result = geocoding_service.geocode(tool_input["delivery_address"])
|
| 861 |
+
tool_input["delivery_lat"] = geocode_result["lat"]
|
| 862 |
+
tool_input["delivery_lng"] = geocode_result["lng"]
|
| 863 |
+
logger.info(f"Auto-geocoded delivery address: {geocode_result['formatted_address']}")
|
| 864 |
+
except Exception as e:
|
| 865 |
+
logger.warning(f"Failed to geocode address, skipping coordinate update: {e}")
|
| 866 |
+
|
| 867 |
+
# Build UPDATE query dynamically based on provided fields
|
| 868 |
+
update_fields = []
|
| 869 |
+
params = []
|
| 870 |
+
|
| 871 |
+
# Map of field names to their database columns
|
| 872 |
+
updateable_fields = {
|
| 873 |
+
"customer_name": "customer_name",
|
| 874 |
+
"customer_phone": "customer_phone",
|
| 875 |
+
"customer_email": "customer_email",
|
| 876 |
+
"delivery_address": "delivery_address",
|
| 877 |
+
"delivery_lat": "delivery_lat",
|
| 878 |
+
"delivery_lng": "delivery_lng",
|
| 879 |
+
"status": "status",
|
| 880 |
+
"priority": "priority",
|
| 881 |
+
"special_instructions": "special_instructions",
|
| 882 |
+
"time_window_end": "time_window_end",
|
| 883 |
+
"payment_status": "payment_status",
|
| 884 |
+
"weight_kg": "weight_kg",
|
| 885 |
+
"order_value": "order_value"
|
| 886 |
+
}
|
| 887 |
+
|
| 888 |
+
for field, column in updateable_fields.items():
|
| 889 |
+
if field in tool_input:
|
| 890 |
+
update_fields.append(f"{column} = %s")
|
| 891 |
+
params.append(tool_input[field])
|
| 892 |
+
|
| 893 |
+
if not update_fields:
|
| 894 |
+
return {
|
| 895 |
+
"success": False,
|
| 896 |
+
"error": "No fields provided to update"
|
| 897 |
+
}
|
| 898 |
+
|
| 899 |
+
# Always update the updated_at timestamp
|
| 900 |
+
update_fields.append("updated_at = %s")
|
| 901 |
+
params.append(datetime.now())
|
| 902 |
+
|
| 903 |
+
# Add order_id for WHERE clause
|
| 904 |
+
params.append(order_id)
|
| 905 |
+
|
| 906 |
+
# Execute update
|
| 907 |
+
query = f"""
|
| 908 |
+
UPDATE orders
|
| 909 |
+
SET {', '.join(update_fields)}
|
| 910 |
+
WHERE order_id = %s
|
| 911 |
+
"""
|
| 912 |
+
|
| 913 |
+
try:
|
| 914 |
+
execute_write(query, tuple(params))
|
| 915 |
+
logger.info(f"Order updated: {order_id}")
|
| 916 |
+
|
| 917 |
+
return {
|
| 918 |
+
"success": True,
|
| 919 |
+
"order_id": order_id,
|
| 920 |
+
"updated_fields": list(updateable_fields.keys() & tool_input.keys()),
|
| 921 |
+
"message": f"Order {order_id} updated successfully!"
|
| 922 |
+
}
|
| 923 |
+
except Exception as e:
|
| 924 |
+
logger.error(f"Database error updating order: {e}")
|
| 925 |
+
return {
|
| 926 |
+
"success": False,
|
| 927 |
+
"error": f"Failed to update order: {str(e)}"
|
| 928 |
+
}
|
| 929 |
+
|
| 930 |
+
|
| 931 |
+
def handle_delete_order(tool_input: dict) -> dict:
|
| 932 |
+
"""
|
| 933 |
+
Execute order deletion tool
|
| 934 |
+
|
| 935 |
+
Args:
|
| 936 |
+
tool_input: Dict with order_id and confirm flag
|
| 937 |
+
|
| 938 |
+
Returns:
|
| 939 |
+
Deletion result
|
| 940 |
+
"""
|
| 941 |
+
order_id = tool_input.get("order_id")
|
| 942 |
+
confirm = tool_input.get("confirm", False)
|
| 943 |
+
|
| 944 |
+
# Validate required fields
|
| 945 |
+
if not order_id:
|
| 946 |
+
return {
|
| 947 |
+
"success": False,
|
| 948 |
+
"error": "Missing required field: order_id"
|
| 949 |
+
}
|
| 950 |
+
|
| 951 |
+
if not confirm:
|
| 952 |
+
return {
|
| 953 |
+
"success": False,
|
| 954 |
+
"error": "Deletion not confirmed. Set confirm=true to proceed."
|
| 955 |
+
}
|
| 956 |
+
|
| 957 |
+
# Check if order exists
|
| 958 |
+
check_query = "SELECT order_id, status FROM orders WHERE order_id = %s"
|
| 959 |
+
existing = execute_query(check_query, (order_id,))
|
| 960 |
+
|
| 961 |
+
if not existing:
|
| 962 |
+
return {
|
| 963 |
+
"success": False,
|
| 964 |
+
"error": f"Order {order_id} not found"
|
| 965 |
+
}
|
| 966 |
+
|
| 967 |
+
# Delete the order
|
| 968 |
+
query = "DELETE FROM orders WHERE order_id = %s"
|
| 969 |
+
|
| 970 |
+
try:
|
| 971 |
+
execute_write(query, (order_id,))
|
| 972 |
+
logger.info(f"Order deleted: {order_id}")
|
| 973 |
+
|
| 974 |
+
return {
|
| 975 |
+
"success": True,
|
| 976 |
+
"order_id": order_id,
|
| 977 |
+
"message": f"Order {order_id} has been permanently deleted."
|
| 978 |
+
}
|
| 979 |
+
except Exception as e:
|
| 980 |
+
logger.error(f"Database error deleting order: {e}")
|
| 981 |
+
return {
|
| 982 |
+
"success": False,
|
| 983 |
+
"error": f"Failed to delete order: {str(e)}"
|
| 984 |
+
}
|
| 985 |
+
|
| 986 |
+
|
| 987 |
+
def handle_update_driver(tool_input: dict) -> dict:
|
| 988 |
+
"""
|
| 989 |
+
Execute driver update tool
|
| 990 |
+
|
| 991 |
+
Args:
|
| 992 |
+
tool_input: Dict with driver_id and fields to update
|
| 993 |
+
|
| 994 |
+
Returns:
|
| 995 |
+
Update result
|
| 996 |
+
"""
|
| 997 |
+
import json
|
| 998 |
+
|
| 999 |
+
driver_id = tool_input.get("driver_id")
|
| 1000 |
+
|
| 1001 |
+
# Validate required field
|
| 1002 |
+
if not driver_id:
|
| 1003 |
+
return {
|
| 1004 |
+
"success": False,
|
| 1005 |
+
"error": "Missing required field: driver_id"
|
| 1006 |
+
}
|
| 1007 |
+
|
| 1008 |
+
# Check if driver exists
|
| 1009 |
+
check_query = "SELECT driver_id FROM drivers WHERE driver_id = %s"
|
| 1010 |
+
existing = execute_query(check_query, (driver_id,))
|
| 1011 |
+
|
| 1012 |
+
if not existing:
|
| 1013 |
+
return {
|
| 1014 |
+
"success": False,
|
| 1015 |
+
"error": f"Driver {driver_id} not found"
|
| 1016 |
+
}
|
| 1017 |
+
|
| 1018 |
+
# Build UPDATE query dynamically based on provided fields
|
| 1019 |
+
update_fields = []
|
| 1020 |
+
params = []
|
| 1021 |
+
|
| 1022 |
+
# Map of field names to their database columns
|
| 1023 |
+
updateable_fields = {
|
| 1024 |
+
"name": "name",
|
| 1025 |
+
"phone": "phone",
|
| 1026 |
+
"email": "email",
|
| 1027 |
+
"status": "status",
|
| 1028 |
+
"vehicle_type": "vehicle_type",
|
| 1029 |
+
"vehicle_plate": "vehicle_plate",
|
| 1030 |
+
"capacity_kg": "capacity_kg",
|
| 1031 |
+
"capacity_m3": "capacity_m3",
|
| 1032 |
+
"current_lat": "current_lat",
|
| 1033 |
+
"current_lng": "current_lng"
|
| 1034 |
+
}
|
| 1035 |
+
|
| 1036 |
+
for field, column in updateable_fields.items():
|
| 1037 |
+
if field in tool_input:
|
| 1038 |
+
update_fields.append(f"{column} = %s")
|
| 1039 |
+
params.append(tool_input[field])
|
| 1040 |
+
|
| 1041 |
+
# Handle skills array specially (convert to JSON)
|
| 1042 |
+
if "skills" in tool_input:
|
| 1043 |
+
skills = list(tool_input.get("skills", []))
|
| 1044 |
+
update_fields.append("skills = %s")
|
| 1045 |
+
params.append(json.dumps(skills))
|
| 1046 |
+
|
| 1047 |
+
if not update_fields:
|
| 1048 |
+
return {
|
| 1049 |
+
"success": False,
|
| 1050 |
+
"error": "No fields provided to update"
|
| 1051 |
+
}
|
| 1052 |
+
|
| 1053 |
+
# Always update the updated_at timestamp
|
| 1054 |
+
update_fields.append("updated_at = %s")
|
| 1055 |
+
params.append(datetime.now())
|
| 1056 |
+
|
| 1057 |
+
# Update location timestamp if lat/lng changed
|
| 1058 |
+
if "current_lat" in tool_input or "current_lng" in tool_input:
|
| 1059 |
+
update_fields.append("last_location_update = %s")
|
| 1060 |
+
params.append(datetime.now())
|
| 1061 |
+
|
| 1062 |
+
# Add driver_id for WHERE clause
|
| 1063 |
+
params.append(driver_id)
|
| 1064 |
+
|
| 1065 |
+
# Execute update
|
| 1066 |
+
query = f"""
|
| 1067 |
+
UPDATE drivers
|
| 1068 |
+
SET {', '.join(update_fields)}
|
| 1069 |
+
WHERE driver_id = %s
|
| 1070 |
+
"""
|
| 1071 |
+
|
| 1072 |
+
try:
|
| 1073 |
+
execute_write(query, tuple(params))
|
| 1074 |
+
logger.info(f"Driver updated: {driver_id}")
|
| 1075 |
+
|
| 1076 |
+
updated_list = list(updateable_fields.keys() & tool_input.keys())
|
| 1077 |
+
if "skills" in tool_input:
|
| 1078 |
+
updated_list.append("skills")
|
| 1079 |
+
|
| 1080 |
+
return {
|
| 1081 |
+
"success": True,
|
| 1082 |
+
"driver_id": driver_id,
|
| 1083 |
+
"updated_fields": updated_list,
|
| 1084 |
+
"message": f"Driver {driver_id} updated successfully!"
|
| 1085 |
+
}
|
| 1086 |
+
except Exception as e:
|
| 1087 |
+
logger.error(f"Database error updating driver: {e}")
|
| 1088 |
+
return {
|
| 1089 |
+
"success": False,
|
| 1090 |
+
"error": f"Failed to update driver: {str(e)}"
|
| 1091 |
+
}
|
| 1092 |
+
|
| 1093 |
+
|
| 1094 |
+
def handle_delete_driver(tool_input: dict) -> dict:
|
| 1095 |
+
"""
|
| 1096 |
+
Execute driver deletion tool
|
| 1097 |
+
|
| 1098 |
+
Args:
|
| 1099 |
+
tool_input: Dict with driver_id and confirm flag
|
| 1100 |
+
|
| 1101 |
+
Returns:
|
| 1102 |
+
Deletion result
|
| 1103 |
+
"""
|
| 1104 |
+
driver_id = tool_input.get("driver_id")
|
| 1105 |
+
confirm = tool_input.get("confirm", False)
|
| 1106 |
+
|
| 1107 |
+
# Validate required fields
|
| 1108 |
+
if not driver_id:
|
| 1109 |
+
return {
|
| 1110 |
+
"success": False,
|
| 1111 |
+
"error": "Missing required field: driver_id"
|
| 1112 |
+
}
|
| 1113 |
+
|
| 1114 |
+
if not confirm:
|
| 1115 |
+
return {
|
| 1116 |
+
"success": False,
|
| 1117 |
+
"error": "Deletion not confirmed. Set confirm=true to proceed."
|
| 1118 |
+
}
|
| 1119 |
+
|
| 1120 |
+
# Check if driver exists
|
| 1121 |
+
check_query = "SELECT driver_id, name FROM drivers WHERE driver_id = %s"
|
| 1122 |
+
existing = execute_query(check_query, (driver_id,))
|
| 1123 |
+
|
| 1124 |
+
if not existing:
|
| 1125 |
+
return {
|
| 1126 |
+
"success": False,
|
| 1127 |
+
"error": f"Driver {driver_id} not found"
|
| 1128 |
+
}
|
| 1129 |
+
|
| 1130 |
+
driver_name = existing[0]["name"]
|
| 1131 |
+
|
| 1132 |
+
# Delete the driver
|
| 1133 |
+
query = "DELETE FROM drivers WHERE driver_id = %s"
|
| 1134 |
+
|
| 1135 |
+
try:
|
| 1136 |
+
execute_write(query, (driver_id,))
|
| 1137 |
+
logger.info(f"Driver deleted: {driver_id}")
|
| 1138 |
+
|
| 1139 |
+
return {
|
| 1140 |
+
"success": True,
|
| 1141 |
+
"driver_id": driver_id,
|
| 1142 |
+
"message": f"Driver {driver_id} ({driver_name}) has been permanently deleted."
|
| 1143 |
+
}
|
| 1144 |
+
except Exception as e:
|
| 1145 |
+
logger.error(f"Database error deleting driver: {e}")
|
| 1146 |
+
return {
|
| 1147 |
+
"success": False,
|
| 1148 |
+
"error": f"Failed to delete driver: {str(e)}"
|
| 1149 |
+
}
|
| 1150 |
+
|
| 1151 |
+
|
| 1152 |
def handle_count_orders(tool_input: dict) -> dict:
|
| 1153 |
"""
|
| 1154 |
Execute count orders tool
|
ui/app.py
CHANGED
|
@@ -351,6 +351,66 @@ def get_driver_details(driver_id):
|
|
| 351 |
return f"Error fetching driver details: {str(e)}"
|
| 352 |
|
| 353 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 354 |
# ============================================
|
| 355 |
# CHAT FUNCTIONS
|
| 356 |
# ============================================
|
|
@@ -443,12 +503,21 @@ def create_interface():
|
|
| 443 |
|
| 444 |
# Quick Action Buttons
|
| 445 |
gr.Markdown("**Quick Actions:**")
|
|
|
|
|
|
|
| 446 |
with gr.Row():
|
| 447 |
quick_create_order = gr.Button("π¦ Create Order", size="sm")
|
| 448 |
quick_view_orders = gr.Button("π View Orders", size="sm")
|
| 449 |
quick_view_drivers = gr.Button("π₯ View Drivers", size="sm")
|
| 450 |
quick_check_status = gr.Button("π Check Status", size="sm")
|
| 451 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 452 |
# Chat interface
|
| 453 |
chatbot = gr.Chatbot(
|
| 454 |
label="Chat with AI Assistant",
|
|
@@ -515,6 +584,26 @@ def create_interface():
|
|
| 515 |
outputs=msg_input
|
| 516 |
)
|
| 517 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 518 |
send_btn.click(
|
| 519 |
fn=send_message,
|
| 520 |
inputs=[msg_input, session_id_state],
|
|
@@ -603,6 +692,42 @@ def create_interface():
|
|
| 603 |
gr.Markdown("**Order Details:**")
|
| 604 |
order_details = gr.Markdown("*Select an order from the table above to view full details*")
|
| 605 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 606 |
# Event handlers
|
| 607 |
def filter_and_update_orders(status, priority, payment, search):
|
| 608 |
return get_all_orders(status, priority, payment, search)
|
|
@@ -643,6 +768,93 @@ def create_interface():
|
|
| 643 |
outputs=order_details
|
| 644 |
)
|
| 645 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 646 |
# ==========================================
|
| 647 |
# TAB 3: DRIVERS
|
| 648 |
# ==========================================
|
|
@@ -707,6 +919,38 @@ def create_interface():
|
|
| 707 |
gr.Markdown("**Driver Details:**")
|
| 708 |
driver_details = gr.Markdown("*Select a driver from the table above to view full details*")
|
| 709 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 710 |
# Event handlers
|
| 711 |
def filter_and_update_drivers(status, vehicle, search):
|
| 712 |
return get_all_drivers(status, vehicle, search)
|
|
@@ -747,6 +991,96 @@ def create_interface():
|
|
| 747 |
outputs=driver_details
|
| 748 |
)
|
| 749 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 750 |
gr.Markdown("---")
|
| 751 |
gr.Markdown("*FleetMind v1.0 - AI-Powered Dispatch Coordination*")
|
| 752 |
|
|
|
|
| 351 |
return f"Error fetching driver details: {str(e)}"
|
| 352 |
|
| 353 |
|
| 354 |
+
def update_order_ui(order_id, **fields):
|
| 355 |
+
"""Update order via UI"""
|
| 356 |
+
from chat.tools import execute_tool
|
| 357 |
+
|
| 358 |
+
if not order_id:
|
| 359 |
+
return {"success": False, "message": "Order ID is required"}
|
| 360 |
+
|
| 361 |
+
# Build tool input
|
| 362 |
+
tool_input = {"order_id": order_id}
|
| 363 |
+
tool_input.update(fields)
|
| 364 |
+
|
| 365 |
+
# Call update tool
|
| 366 |
+
result = execute_tool("update_order", tool_input)
|
| 367 |
+
|
| 368 |
+
return result
|
| 369 |
+
|
| 370 |
+
|
| 371 |
+
def delete_order_ui(order_id):
|
| 372 |
+
"""Delete order via UI"""
|
| 373 |
+
from chat.tools import execute_tool
|
| 374 |
+
|
| 375 |
+
if not order_id:
|
| 376 |
+
return {"success": False, "message": "Order ID is required"}
|
| 377 |
+
|
| 378 |
+
# Call delete tool with confirmation
|
| 379 |
+
result = execute_tool("delete_order", {"order_id": order_id, "confirm": True})
|
| 380 |
+
|
| 381 |
+
return result
|
| 382 |
+
|
| 383 |
+
|
| 384 |
+
def update_driver_ui(driver_id, **fields):
|
| 385 |
+
"""Update driver via UI"""
|
| 386 |
+
from chat.tools import execute_tool
|
| 387 |
+
|
| 388 |
+
if not driver_id:
|
| 389 |
+
return {"success": False, "message": "Driver ID is required"}
|
| 390 |
+
|
| 391 |
+
# Build tool input
|
| 392 |
+
tool_input = {"driver_id": driver_id}
|
| 393 |
+
tool_input.update(fields)
|
| 394 |
+
|
| 395 |
+
# Call update tool
|
| 396 |
+
result = execute_tool("update_driver", tool_input)
|
| 397 |
+
|
| 398 |
+
return result
|
| 399 |
+
|
| 400 |
+
|
| 401 |
+
def delete_driver_ui(driver_id):
|
| 402 |
+
"""Delete driver via UI"""
|
| 403 |
+
from chat.tools import execute_tool
|
| 404 |
+
|
| 405 |
+
if not driver_id:
|
| 406 |
+
return {"success": False, "message": "Driver ID is required"}
|
| 407 |
+
|
| 408 |
+
# Call delete tool with confirmation
|
| 409 |
+
result = execute_tool("delete_driver", {"driver_id": driver_id, "confirm": True})
|
| 410 |
+
|
| 411 |
+
return result
|
| 412 |
+
|
| 413 |
+
|
| 414 |
# ============================================
|
| 415 |
# CHAT FUNCTIONS
|
| 416 |
# ============================================
|
|
|
|
| 503 |
|
| 504 |
# Quick Action Buttons
|
| 505 |
gr.Markdown("**Quick Actions:**")
|
| 506 |
+
|
| 507 |
+
# Row 1: Create and View
|
| 508 |
with gr.Row():
|
| 509 |
quick_create_order = gr.Button("π¦ Create Order", size="sm")
|
| 510 |
quick_view_orders = gr.Button("π View Orders", size="sm")
|
| 511 |
quick_view_drivers = gr.Button("π₯ View Drivers", size="sm")
|
| 512 |
quick_check_status = gr.Button("π Check Status", size="sm")
|
| 513 |
|
| 514 |
+
# Row 2: Order Management
|
| 515 |
+
with gr.Row():
|
| 516 |
+
quick_update_order = gr.Button("βοΈ Update Order", size="sm", variant="secondary")
|
| 517 |
+
quick_delete_order = gr.Button("ποΈ Delete Order", size="sm", variant="secondary")
|
| 518 |
+
quick_update_driver = gr.Button("βοΈ Update Driver", size="sm", variant="secondary")
|
| 519 |
+
quick_delete_driver = gr.Button("ποΈ Delete Driver", size="sm", variant="secondary")
|
| 520 |
+
|
| 521 |
# Chat interface
|
| 522 |
chatbot = gr.Chatbot(
|
| 523 |
label="Chat with AI Assistant",
|
|
|
|
| 584 |
outputs=msg_input
|
| 585 |
)
|
| 586 |
|
| 587 |
+
quick_update_order.click(
|
| 588 |
+
fn=lambda: "Update order [ORDER_ID] - change status to [STATUS]",
|
| 589 |
+
outputs=msg_input
|
| 590 |
+
)
|
| 591 |
+
|
| 592 |
+
quick_delete_order.click(
|
| 593 |
+
fn=lambda: "Delete order [ORDER_ID]",
|
| 594 |
+
outputs=msg_input
|
| 595 |
+
)
|
| 596 |
+
|
| 597 |
+
quick_update_driver.click(
|
| 598 |
+
fn=lambda: "Update driver [DRIVER_ID] - change status to [STATUS]",
|
| 599 |
+
outputs=msg_input
|
| 600 |
+
)
|
| 601 |
+
|
| 602 |
+
quick_delete_driver.click(
|
| 603 |
+
fn=lambda: "Delete driver [DRIVER_ID]",
|
| 604 |
+
outputs=msg_input
|
| 605 |
+
)
|
| 606 |
+
|
| 607 |
send_btn.click(
|
| 608 |
fn=send_message,
|
| 609 |
inputs=[msg_input, session_id_state],
|
|
|
|
| 692 |
gr.Markdown("**Order Details:**")
|
| 693 |
order_details = gr.Markdown("*Select an order from the table above to view full details*")
|
| 694 |
|
| 695 |
+
# Edit/Delete Actions
|
| 696 |
+
gr.Markdown("---")
|
| 697 |
+
gr.Markdown("**Order Actions:**")
|
| 698 |
+
|
| 699 |
+
with gr.Row():
|
| 700 |
+
selected_order_id_edit = gr.Textbox(label="Order ID to Edit", placeholder="ORD-XXXXXXXX", scale=2)
|
| 701 |
+
edit_order_btn = gr.Button("βοΈ Edit Order", variant="secondary", scale=1)
|
| 702 |
+
delete_order_btn = gr.Button("ποΈ Delete Order", variant="stop", scale=1)
|
| 703 |
+
|
| 704 |
+
# Edit Form (in accordion)
|
| 705 |
+
with gr.Accordion("Edit Order Form", open=False) as edit_accordion:
|
| 706 |
+
with gr.Row():
|
| 707 |
+
edit_customer_name = gr.Textbox(label="Customer Name")
|
| 708 |
+
edit_customer_phone = gr.Textbox(label="Customer Phone")
|
| 709 |
+
with gr.Row():
|
| 710 |
+
edit_status = gr.Dropdown(
|
| 711 |
+
choices=["pending", "assigned", "in_transit", "delivered", "failed", "cancelled"],
|
| 712 |
+
label="Status"
|
| 713 |
+
)
|
| 714 |
+
edit_priority = gr.Dropdown(
|
| 715 |
+
choices=["standard", "express", "urgent"],
|
| 716 |
+
label="Priority"
|
| 717 |
+
)
|
| 718 |
+
with gr.Row():
|
| 719 |
+
edit_payment_status = gr.Dropdown(
|
| 720 |
+
choices=["pending", "paid", "cod"],
|
| 721 |
+
label="Payment Status"
|
| 722 |
+
)
|
| 723 |
+
edit_weight_kg = gr.Number(label="Weight (kg)")
|
| 724 |
+
edit_special_instructions = gr.Textbox(label="Special Instructions", lines=2)
|
| 725 |
+
|
| 726 |
+
save_order_btn = gr.Button("πΎ Save Changes", variant="primary")
|
| 727 |
+
|
| 728 |
+
# Action Results
|
| 729 |
+
action_result = gr.Markdown("")
|
| 730 |
+
|
| 731 |
# Event handlers
|
| 732 |
def filter_and_update_orders(status, priority, payment, search):
|
| 733 |
return get_all_orders(status, priority, payment, search)
|
|
|
|
| 768 |
outputs=order_details
|
| 769 |
)
|
| 770 |
|
| 771 |
+
# Edit/Delete handlers
|
| 772 |
+
def handle_edit_order(order_id):
|
| 773 |
+
if not order_id:
|
| 774 |
+
return "Please enter an Order ID"
|
| 775 |
+
# Fetch order details and populate form
|
| 776 |
+
query = "SELECT * FROM orders WHERE order_id = %s"
|
| 777 |
+
from database.connection import execute_query
|
| 778 |
+
results = execute_query(query, (order_id,))
|
| 779 |
+
if not results:
|
| 780 |
+
return "Order not found"
|
| 781 |
+
order = results[0]
|
| 782 |
+
return (
|
| 783 |
+
order.get('customer_name', ''),
|
| 784 |
+
order.get('customer_phone', ''),
|
| 785 |
+
order.get('status', ''),
|
| 786 |
+
order.get('priority', ''),
|
| 787 |
+
order.get('payment_status', ''),
|
| 788 |
+
order.get('weight_kg', 0),
|
| 789 |
+
order.get('special_instructions', ''),
|
| 790 |
+
"Order loaded. Update fields and click Save Changes."
|
| 791 |
+
)
|
| 792 |
+
|
| 793 |
+
def handle_save_order(order_id, name, phone, status, priority, payment, weight, instructions, status_f, priority_f, payment_f, search):
|
| 794 |
+
if not order_id:
|
| 795 |
+
return "Please enter an Order ID", get_all_orders(status_f, priority_f, payment_f, search)
|
| 796 |
+
|
| 797 |
+
fields = {}
|
| 798 |
+
if name:
|
| 799 |
+
fields['customer_name'] = name
|
| 800 |
+
if phone:
|
| 801 |
+
fields['customer_phone'] = phone
|
| 802 |
+
if status:
|
| 803 |
+
fields['status'] = status
|
| 804 |
+
if priority:
|
| 805 |
+
fields['priority'] = priority
|
| 806 |
+
if payment:
|
| 807 |
+
fields['payment_status'] = payment
|
| 808 |
+
if weight:
|
| 809 |
+
fields['weight_kg'] = float(weight)
|
| 810 |
+
if instructions:
|
| 811 |
+
fields['special_instructions'] = instructions
|
| 812 |
+
|
| 813 |
+
result = update_order_ui(order_id, **fields)
|
| 814 |
+
|
| 815 |
+
# Refresh table
|
| 816 |
+
refreshed_table = get_all_orders(status_f, priority_f, payment_f, search)
|
| 817 |
+
|
| 818 |
+
if result['success']:
|
| 819 |
+
return f"β
{result['message']}", refreshed_table
|
| 820 |
+
else:
|
| 821 |
+
return f"β {result.get('error', 'Update failed')}", refreshed_table
|
| 822 |
+
|
| 823 |
+
def handle_delete_order(order_id, status_f, priority_f, payment_f, search):
|
| 824 |
+
if not order_id:
|
| 825 |
+
return "Please enter an Order ID", get_all_orders(status_f, priority_f, payment_f, search)
|
| 826 |
+
|
| 827 |
+
result = delete_order_ui(order_id)
|
| 828 |
+
|
| 829 |
+
# Refresh table
|
| 830 |
+
refreshed_table = get_all_orders(status_f, priority_f, payment_f, search)
|
| 831 |
+
|
| 832 |
+
if result['success']:
|
| 833 |
+
return f"β
{result['message']}", refreshed_table
|
| 834 |
+
else:
|
| 835 |
+
return f"β {result.get('error', 'Deletion failed')}", refreshed_table
|
| 836 |
+
|
| 837 |
+
edit_order_btn.click(
|
| 838 |
+
fn=handle_edit_order,
|
| 839 |
+
inputs=[selected_order_id_edit],
|
| 840 |
+
outputs=[edit_customer_name, edit_customer_phone, edit_status, edit_priority,
|
| 841 |
+
edit_payment_status, edit_weight_kg, edit_special_instructions, action_result]
|
| 842 |
+
)
|
| 843 |
+
|
| 844 |
+
save_order_btn.click(
|
| 845 |
+
fn=handle_save_order,
|
| 846 |
+
inputs=[selected_order_id_edit, edit_customer_name, edit_customer_phone, edit_status,
|
| 847 |
+
edit_priority, edit_payment_status, edit_weight_kg, edit_special_instructions,
|
| 848 |
+
status_filter, priority_filter, payment_filter, search_orders],
|
| 849 |
+
outputs=[action_result, orders_table]
|
| 850 |
+
)
|
| 851 |
+
|
| 852 |
+
delete_order_btn.click(
|
| 853 |
+
fn=handle_delete_order,
|
| 854 |
+
inputs=[selected_order_id_edit, status_filter, priority_filter, payment_filter, search_orders],
|
| 855 |
+
outputs=[action_result, orders_table]
|
| 856 |
+
)
|
| 857 |
+
|
| 858 |
# ==========================================
|
| 859 |
# TAB 3: DRIVERS
|
| 860 |
# ==========================================
|
|
|
|
| 919 |
gr.Markdown("**Driver Details:**")
|
| 920 |
driver_details = gr.Markdown("*Select a driver from the table above to view full details*")
|
| 921 |
|
| 922 |
+
# Edit/Delete Actions
|
| 923 |
+
gr.Markdown("---")
|
| 924 |
+
gr.Markdown("**Driver Actions:**")
|
| 925 |
+
|
| 926 |
+
with gr.Row():
|
| 927 |
+
selected_driver_id_edit = gr.Textbox(label="Driver ID to Edit", placeholder="DRV-XXXXXXXX", scale=2)
|
| 928 |
+
edit_driver_btn = gr.Button("βοΈ Edit Driver", variant="secondary", scale=1)
|
| 929 |
+
delete_driver_btn = gr.Button("ποΈ Delete Driver", variant="stop", scale=1)
|
| 930 |
+
|
| 931 |
+
# Edit Form (in accordion)
|
| 932 |
+
with gr.Accordion("Edit Driver Form", open=False) as driver_edit_accordion:
|
| 933 |
+
with gr.Row():
|
| 934 |
+
edit_driver_name = gr.Textbox(label="Driver Name")
|
| 935 |
+
edit_driver_phone = gr.Textbox(label="Phone")
|
| 936 |
+
with gr.Row():
|
| 937 |
+
edit_driver_email = gr.Textbox(label="Email")
|
| 938 |
+
edit_driver_status = gr.Dropdown(
|
| 939 |
+
choices=["active", "busy", "offline", "unavailable"],
|
| 940 |
+
label="Status"
|
| 941 |
+
)
|
| 942 |
+
with gr.Row():
|
| 943 |
+
edit_vehicle_type = gr.Textbox(label="Vehicle Type")
|
| 944 |
+
edit_vehicle_plate = gr.Textbox(label="Vehicle Plate")
|
| 945 |
+
with gr.Row():
|
| 946 |
+
edit_capacity_kg = gr.Number(label="Capacity (kg)")
|
| 947 |
+
edit_capacity_m3 = gr.Number(label="Capacity (mΒ³)")
|
| 948 |
+
|
| 949 |
+
save_driver_btn = gr.Button("πΎ Save Changes", variant="primary")
|
| 950 |
+
|
| 951 |
+
# Action Results
|
| 952 |
+
driver_action_result = gr.Markdown("")
|
| 953 |
+
|
| 954 |
# Event handlers
|
| 955 |
def filter_and_update_drivers(status, vehicle, search):
|
| 956 |
return get_all_drivers(status, vehicle, search)
|
|
|
|
| 991 |
outputs=driver_details
|
| 992 |
)
|
| 993 |
|
| 994 |
+
# Edit/Delete handlers
|
| 995 |
+
def handle_edit_driver(driver_id):
|
| 996 |
+
if not driver_id:
|
| 997 |
+
return "Please enter a Driver ID"
|
| 998 |
+
# Fetch driver details and populate form
|
| 999 |
+
query = "SELECT * FROM drivers WHERE driver_id = %s"
|
| 1000 |
+
from database.connection import execute_query
|
| 1001 |
+
results = execute_query(query, (driver_id,))
|
| 1002 |
+
if not results:
|
| 1003 |
+
return "Driver not found"
|
| 1004 |
+
driver = results[0]
|
| 1005 |
+
return (
|
| 1006 |
+
driver.get('name', ''),
|
| 1007 |
+
driver.get('phone', ''),
|
| 1008 |
+
driver.get('email', ''),
|
| 1009 |
+
driver.get('status', ''),
|
| 1010 |
+
driver.get('vehicle_type', ''),
|
| 1011 |
+
driver.get('vehicle_plate', ''),
|
| 1012 |
+
driver.get('capacity_kg', 0),
|
| 1013 |
+
driver.get('capacity_m3', 0),
|
| 1014 |
+
"Driver loaded. Update fields and click Save Changes."
|
| 1015 |
+
)
|
| 1016 |
+
|
| 1017 |
+
def handle_save_driver(driver_id, name, phone, email, status, vehicle_type, vehicle_plate, capacity_kg, capacity_m3, status_f, vehicle_f, search):
|
| 1018 |
+
if not driver_id:
|
| 1019 |
+
return "Please enter a Driver ID", get_all_drivers(status_f, vehicle_f, search)
|
| 1020 |
+
|
| 1021 |
+
fields = {}
|
| 1022 |
+
if name:
|
| 1023 |
+
fields['name'] = name
|
| 1024 |
+
if phone:
|
| 1025 |
+
fields['phone'] = phone
|
| 1026 |
+
if email:
|
| 1027 |
+
fields['email'] = email
|
| 1028 |
+
if status:
|
| 1029 |
+
fields['status'] = status
|
| 1030 |
+
if vehicle_type:
|
| 1031 |
+
fields['vehicle_type'] = vehicle_type
|
| 1032 |
+
if vehicle_plate:
|
| 1033 |
+
fields['vehicle_plate'] = vehicle_plate
|
| 1034 |
+
if capacity_kg:
|
| 1035 |
+
fields['capacity_kg'] = float(capacity_kg)
|
| 1036 |
+
if capacity_m3:
|
| 1037 |
+
fields['capacity_m3'] = float(capacity_m3)
|
| 1038 |
+
|
| 1039 |
+
result = update_driver_ui(driver_id, **fields)
|
| 1040 |
+
|
| 1041 |
+
# Refresh table
|
| 1042 |
+
refreshed_table = get_all_drivers(status_f, vehicle_f, search)
|
| 1043 |
+
|
| 1044 |
+
if result['success']:
|
| 1045 |
+
return f"β
{result['message']}", refreshed_table
|
| 1046 |
+
else:
|
| 1047 |
+
return f"β {result.get('error', 'Update failed')}", refreshed_table
|
| 1048 |
+
|
| 1049 |
+
def handle_delete_driver(driver_id, status_f, vehicle_f, search):
|
| 1050 |
+
if not driver_id:
|
| 1051 |
+
return "Please enter a Driver ID", get_all_drivers(status_f, vehicle_f, search)
|
| 1052 |
+
|
| 1053 |
+
result = delete_driver_ui(driver_id)
|
| 1054 |
+
|
| 1055 |
+
# Refresh table
|
| 1056 |
+
refreshed_table = get_all_drivers(status_f, vehicle_f, search)
|
| 1057 |
+
|
| 1058 |
+
if result['success']:
|
| 1059 |
+
return f"β
{result['message']}", refreshed_table
|
| 1060 |
+
else:
|
| 1061 |
+
return f"β {result.get('error', 'Deletion failed')}", refreshed_table
|
| 1062 |
+
|
| 1063 |
+
edit_driver_btn.click(
|
| 1064 |
+
fn=handle_edit_driver,
|
| 1065 |
+
inputs=[selected_driver_id_edit],
|
| 1066 |
+
outputs=[edit_driver_name, edit_driver_phone, edit_driver_email, edit_driver_status,
|
| 1067 |
+
edit_vehicle_type, edit_vehicle_plate, edit_capacity_kg, edit_capacity_m3, driver_action_result]
|
| 1068 |
+
)
|
| 1069 |
+
|
| 1070 |
+
save_driver_btn.click(
|
| 1071 |
+
fn=handle_save_driver,
|
| 1072 |
+
inputs=[selected_driver_id_edit, edit_driver_name, edit_driver_phone, edit_driver_email,
|
| 1073 |
+
edit_driver_status, edit_vehicle_type, edit_vehicle_plate, edit_capacity_kg, edit_capacity_m3,
|
| 1074 |
+
driver_status_filter, vehicle_filter, search_drivers],
|
| 1075 |
+
outputs=[driver_action_result, drivers_table]
|
| 1076 |
+
)
|
| 1077 |
+
|
| 1078 |
+
delete_driver_btn.click(
|
| 1079 |
+
fn=handle_delete_driver,
|
| 1080 |
+
inputs=[selected_driver_id_edit, driver_status_filter, vehicle_filter, search_drivers],
|
| 1081 |
+
outputs=[driver_action_result, drivers_table]
|
| 1082 |
+
)
|
| 1083 |
+
|
| 1084 |
gr.Markdown("---")
|
| 1085 |
gr.Markdown("*FleetMind v1.0 - AI-Powered Dispatch Coordination*")
|
| 1086 |
|