Spaces:
Runtime error
Runtime error
| """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.") | |
| 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." | |
| 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." | |
| 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." | |
| 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, | |
| ] | |