File size: 6,445 Bytes
927c050
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
"""Invoice information tools for the multi-agent system."""

import logging
from langchain_core.tools import tool
from src.db.database import run_query_safe

logger = logging.getLogger(__name__)


def _safe_int(value: str, label: str = "value") -> int:
    try:
        return int(value)
    except (ValueError, TypeError):
        raise ValueError(f"Invalid {label}: '{value}'. Please provide a numeric value.")


@tool
def get_invoices_by_customer_sorted_by_date(customer_id: str) -> str:
    """
    Look up all invoices for a customer using their ID.
    Returns invoices sorted by date (most recent first).
    """
    logger.info(f"TOOL_CALL: get_invoices_by_customer_sorted_by_date | customer_id={customer_id}")
    try:
        result = run_query_safe(
            """
            SELECT InvoiceId, CustomerId, InvoiceDate, BillingAddress, BillingCity,
                   BillingState, BillingCountry, BillingPostalCode, Total
            FROM Invoice
            WHERE CustomerId = :customer_id
            ORDER BY InvoiceDate DESC;
            """,
            {"customer_id": _safe_int(customer_id, "customer ID")},
        )
        logger.info(f"TOOL_RESULT: get_invoices_by_customer_sorted_by_date | result_length={len(result)}")
        if result == "[]":
            return f"No invoices found for customer {customer_id}."
        return result
    except Exception as e:
        logger.error(f"Error in get_invoices_by_customer_sorted_by_date: {e}")
        return f"Error retrieving invoices for customer {customer_id}. Please try again."


@tool
def get_invoice_line_items_sorted_by_price(customer_id: str) -> str:
    """
    Look up all purchased line items for a customer, sorted by unit price (highest first).
    Each row is a single purchased track (NOT a full invoice). An invoice with 5 tracks
    will appear as 5 separate rows, each showing the track name, unit price, and quantity.
    """
    logger.info(f"TOOL_CALL: get_invoice_line_items_sorted_by_price | customer_id={customer_id}")
    try:
        result = run_query_safe(
            """
            SELECT Invoice.InvoiceId, Invoice.InvoiceDate, Invoice.Total AS InvoiceTotal,
                   Track.Name AS TrackName, InvoiceLine.UnitPrice, InvoiceLine.Quantity
            FROM Invoice
            JOIN InvoiceLine ON Invoice.InvoiceId = InvoiceLine.InvoiceId
            JOIN Track ON InvoiceLine.TrackId = Track.TrackId
            WHERE Invoice.CustomerId = :customer_id
            ORDER BY InvoiceLine.UnitPrice DESC;
            """,
            {"customer_id": _safe_int(customer_id, "customer ID")},
        )
        logger.info(f"TOOL_RESULT: get_invoice_line_items_sorted_by_price | result_length={len(result)}")
        if result == "[]":
            return f"No purchase records found for customer {customer_id}."
        return result
    except Exception as e:
        logger.error(f"Error in get_invoice_line_items_sorted_by_price: {e}")
        return f"Error retrieving purchase records for customer {customer_id}. Please try again."


@tool
def get_employee_by_invoice_and_customer(invoice_id: str, customer_id: str) -> str:
    """
    Find the employee (support rep) associated with a specific invoice and customer.
    Returns employee full name, title, and email.
    """
    logger.info(f"TOOL_CALL: get_employee_by_invoice_and_customer | invoice_id={invoice_id}, customer_id={customer_id}")
    try:
        result = run_query_safe(
            """
            SELECT Employee.FirstName, Employee.LastName, Employee.Title, Employee.Email
            FROM Employee
            JOIN Customer ON Customer.SupportRepId = Employee.EmployeeId
            JOIN Invoice ON Invoice.CustomerId = Customer.CustomerId
            WHERE Invoice.InvoiceId = :invoice_id AND Invoice.CustomerId = :customer_id;
            """,
            {"invoice_id": _safe_int(invoice_id, "invoice ID"), "customer_id": _safe_int(customer_id, "customer ID")},
        )
        logger.info(f"TOOL_RESULT: get_employee_by_invoice_and_customer | result_length={len(result)}")
        if result == "[]":
            return f"No employee found for invoice ID {invoice_id} and customer ID {customer_id}."
        return result
    except Exception as e:
        logger.error(f"Error in get_employee_by_invoice_and_customer: {e}")
        return f"Error finding employee for invoice {invoice_id}. Please try again."


@tool
def get_invoice_line_items(invoice_id: str, customer_id: str) -> str:
    """
    Get the detailed line items (tracks purchased) for a specific invoice.
    Returns full track details for each purchased item.
    """
    logger.info(f"TOOL_CALL: get_invoice_line_items | invoice_id={invoice_id}, customer_id={customer_id}")
    try:
        result = run_query_safe(
            """
            SELECT Track.TrackId,
                   Track.Name AS TrackName,
                   Artist.Name AS ArtistName,
                   Album.Title AS AlbumTitle,
                   Genre.Name AS GenreName,
                   Track.Composer,
                   Track.Milliseconds,
                   ROUND(Track.Milliseconds / 60000.0, 1) AS DurationMinutes,
                   InvoiceLine.UnitPrice,
                   InvoiceLine.Quantity
            FROM InvoiceLine
            JOIN Invoice ON InvoiceLine.InvoiceId = Invoice.InvoiceId
            JOIN Track ON InvoiceLine.TrackId = Track.TrackId
            LEFT JOIN Album ON Track.AlbumId = Album.AlbumId
            LEFT JOIN Artist ON Album.ArtistId = Artist.ArtistId
            LEFT JOIN Genre ON Track.GenreId = Genre.GenreId
            WHERE Invoice.InvoiceId = :invoice_id AND Invoice.CustomerId = :customer_id
            ORDER BY Track.Name;
            """,
            {"invoice_id": _safe_int(invoice_id, "invoice ID"), "customer_id": _safe_int(customer_id, "customer ID")},
        )
        logger.info(f"TOOL_RESULT: get_invoice_line_items | result_length={len(result)}")
        if result == "[]":
            return f"No line items found for invoice {invoice_id} (customer {customer_id})."
        return result
    except Exception as e:
        logger.error(f"Error in get_invoice_line_items: {e}")
        return f"Error retrieving line items for invoice {invoice_id}. Please try again."


invoice_tools = [
    get_invoices_by_customer_sorted_by_date,
    get_invoice_line_items_sorted_by_price,
    get_employee_by_invoice_and_customer,
    get_invoice_line_items,
]