Merge branch 'ecommerce' into main

This commit is contained in:
Joshua Smith
2025-04-17 08:52:17 -04:00
committed by GitHub
11 changed files with 386 additions and 7 deletions

View File

@@ -1,5 +1,6 @@
RAPIDAPI_KEY=9df2cb5... RAPIDAPI_KEY=9df2cb5...
RAPIDAPI_HOST=sky-scrapper.p.rapidapi.com RAPIDAPI_HOST_FLIGHTS=sky-scrapper.p.rapidapi.com #For travel flight information tool
RAPIDAPI_HOST_PACKAGE=trackingpackage.p.rapidapi.com #For eCommerce order status package tracking tool
FOOTBALL_DATA_API_KEY=.... FOOTBALL_DATA_API_KEY=....
STRIPE_API_KEY=sk_test_51J... STRIPE_API_KEY=sk_test_51J...
@@ -42,7 +43,7 @@ AGENT_GOAL=goal_choose_agent_type # for multi-goal start
#Choose which category(ies) of goals you want to be listed by the Agent Goal picker if enabled above #Choose which category(ies) of goals you want to be listed by the Agent Goal picker if enabled above
# - options are system (always included), hr, travel, or all. # - options are system (always included), hr, travel, or all.
GOAL_CATEGORIES=hr,travel-flights,travel-trains,fin # default is all GOAL_CATEGORIES=hr,travel-flights,travel-trains,fin,ecommerce # default is all
#GOAL_CATEGORIES=travel-flights #GOAL_CATEGORIES=travel-flights
# Set if the workflow should wait for the user to click a confirm button (and if the UI should show the confirm button and tool args) # Set if the workflow should wait for the user to click a confirm button (and if the UI should show the confirm button and tool args)

2
.gitignore vendored
View File

@@ -32,5 +32,5 @@ coverage.xml
.idea/ .idea/
.env .env
*.env
.env* .env*

View File

@@ -10,6 +10,11 @@
[ ] financial advise - args being freeform customer input about their financial situation, goals [ ] financial advise - args being freeform customer input about their financial situation, goals
[ ] tool is maybe a new tool asking the LLM to advise [ ] tool is maybe a new tool asking the LLM to advise
[ ] for demo simulate failure - add utilities/simulated failures from pipeline demo <br />
[ ] ecommerce goals <br />
- [ ] add to docs <br />
- [ ] decide about api key names with Laine <br />
[ ] LLM failure->autoswitch: <br /> [ ] LLM failure->autoswitch: <br />
- detect failure in the activity using failurecount <br /> - detect failure in the activity using failurecount <br />

View File

@@ -18,6 +18,10 @@ from .fin.get_account_balances import get_account_balance
from .fin.move_money import move_money from .fin.move_money import move_money
from .fin.submit_loan_application import submit_loan_application from .fin.submit_loan_application import submit_loan_application
from .ecommerce.get_order import get_order
from .ecommerce.track_package import track_package
from .ecommerce.list_orders import list_orders
from .give_hint import give_hint from .give_hint import give_hint
from .guess_location import guess_location from .guess_location import guess_location
@@ -57,6 +61,12 @@ def get_handler(tool_name: str):
return move_money return move_money
if tool_name == "FinCheckAccountSubmitLoanApproval": if tool_name == "FinCheckAccountSubmitLoanApproval":
return submit_loan_application return submit_loan_application
if tool_name == "GetOrder":
return get_order
if tool_name == "TrackPackage":
return track_package
if tool_name == "ListOrders":
return list_orders
if tool_name == "GiveHint": if tool_name == "GiveHint":
return give_hint return give_hint
if tool_name == "GuessLocation": if tool_name == "GuessLocation":

View File

@@ -0,0 +1,81 @@
{
"orders": [
{
"id": "100",
"summary": "Lawyer Books",
"email": "matt.murdock@nelsonmurdock.com",
"status": "cancelled",
"order_date": "2025-03-30",
"last_update": "2025-04-01"
},
{
"id": "101",
"summary": "Bonking Sticks",
"email": "matt.murdock@nelsonmurdock.com",
"status": "paid",
"order_date": "2025-04-01",
"last_order_update": "2025-04-01"
},
{
"id": "102",
"summary": "Red Sunglasses",
"email": "matt.murdock@nelsonmurdock.com",
"status": "shipped",
"order_date": "2025-04-01",
"last_order_update": "2025-04-01",
"tracking_id": "1Z111111"
},
{
"id": "200",
"summary": "Paper",
"email": "foggy.nelson@nelsonmurdock.com",
"status": "shipped",
"order_date": "2025-04-03",
"last_update": "2025-04-06",
"tracking_id": "991111"
},
{
"id": "300",
"summary": "Chemistry Books",
"email": "heisenberg@blue-meth.com",
"status": "shipped",
"order_date": "2025-03-30",
"last_update": "2025-04-06",
"tracking_id": "991111"
},
{
"id": "301",
"summary": "Book: Being a Cool Bro",
"email": "heisenberg@blue-meth.com",
"status": "cancelled",
"order_date": "2025-04-01",
"last_update": "2025-04-02"
},
{
"id": "302",
"summary": "Black Hat",
"email": "heisenberg@blue-meth.com",
"status": "delivered",
"order_date": "2025-04-01",
"last_update": "2025-04-06",
"tracking_id": "1Z11111"
},
{
"id": "400",
"summary": "Giant Graphic Hoodie",
"email": "jessenotpinkman@blue-meth.com",
"status": "shipped",
"order_date": "2025-04-03",
"last_update": "2025-04-09",
"tracking_id": "1Z111111"
},
{
"id": "401",
"summary": "Giant Pants",
"email": "jessenotpinkman@blue-meth.com",
"status": "processing",
"order_date": "2025-04-03",
"last_update": "2025-04-09"
}
]
}

View File

@@ -0,0 +1,27 @@
from pathlib import Path
import json
# this is made to demonstrate functionality but it could just as durably be an API call
# called as part of a temporal activity with automatic retries
def get_order(args: dict) -> dict:
order_id = args.get("order_id")
file_path = Path(__file__).resolve().parent.parent / "data" / "customer_order_data.json"
if not file_path.exists():
return {"error": "Data file not found."}
with open(file_path, "r") as file:
data = json.load(file)
order_list = data["orders"]
for order in order_list:
if order["id"] == order_id:
return order
# if order["status"] == "shipped":
# return{"status": order["status"], "tracking_id": order["tracking_id"]}
# else:
# return{"status": order["status"]}
return_msg = "Order " + order_id + " not found."
return {"error": return_msg}

View File

@@ -0,0 +1,30 @@
from pathlib import Path
import json
def sorting(e):
return e['order_date']
def list_orders(args: dict) -> dict:
email_address = args.get("email_address")
file_path = Path(__file__).resolve().parent.parent / "data" / "customer_order_data.json"
if not file_path.exists():
return {"error": "Data file not found."}
with open(file_path, "r") as file:
data = json.load(file)
order_list = data["orders"]
rtn_order_list = []
for order in order_list:
if order["email"] == email_address:
rtn_order_list.append(order)
if len(rtn_order_list) > 0:
rtn_order_list.sort(key=sorting)
return {"orders": rtn_order_list}
else:
return_msg = "No orders for customer " + email_address + " found."
return {"error": return_msg}

View File

@@ -0,0 +1,110 @@
import http
import os
import json
def track_package_faked(args: dict) -> dict:
tracking_id = args.get("tracking_id")
#return_msg = "Account not found with email address " + email + " or account ID: " + account_id
return {"tracking_info": "delivered, probably"}
'''Format of response:
{
"TrackingNumber": "",
"Delivered": false,
"Carrier": "USPS",
"ServiceType": "USPS Ground Advantage<SUP>&#153;</SUP>",
"PickupDate": "",
"ScheduledDeliveryDate": "April 14, 2025",
"ScheduledDeliveryDateInDateTimeFromat": "2025-04-14T00:00:00",
"StatusCode": "In Transit from Origin Processing",
"Status": "Departed Post Office",
"StatusSummary": "Your item has left our acceptance facility and is in transit to a sorting facility on April 10, 2025 at 7:06 am in IRON RIDGE, WI 53035.",
"Message": "",
"DeliveredDateTime": "",
"DeliveredDateTimeInDateTimeFormat": null,
"SignatureName": "",
"DestinationCity": "CITY",
"DestinationState": "ST",
"DestinationZip": "12345",
"DestinationCountry": null,
"EventDate": "2025-04-10T07:06:00",
"TrackingDetails": [
{
"EventDateTime": "April 10, 2025 7:06 am",
"Event": "Departed Post Office",
"EventAddress": "IRON RIDGE WI 53035",
"State": "WI",
"City": "IRON RIDGE",
"Zip": "53035",
"EventDateTimeInDateTimeFormat": "2025-04-10T07:06:00"
},
{
"EventDateTime": "April 9, 2025 11:29 am",
"Event": "USPS picked up item",
"EventAddress": "IRON RIDGE WI 53035",
"State": "WI",
"City": "IRON RIDGE",
"Zip": "53035",
"EventDateTimeInDateTimeFormat": "2025-04-09T11:29:00"
},
{
"EventDateTime": "April 7, 2025 6:29 am",
"Event": "Shipping Label Created, USPS Awaiting Item",
"EventAddress": "IRON RIDGE WI 53035",
"State": "WI",
"City": "IRON RIDGE",
"Zip": "53035",
"EventDateTimeInDateTimeFormat": "2025-04-07T06:29:00"
}
]
}
'''
def track_package(args: dict) -> dict:
tracking_id = args.get("tracking_id")
api_key = os.getenv("RAPIDAPI_KEY")
api_host = os.getenv("RAPIDAPI_HOST_PACKAGE", "trackingpackage.p.rapidapi.com")
conn = http.client.HTTPSConnection(api_host)
headers = {
"x-rapidapi-key": api_key,
"x-rapidapi-host": api_host,
"Authorization": "Basic Ym9sZGNoYXQ6TGZYfm0zY2d1QzkuKz9SLw==",
}
path = f"/TrackingPackage?trackingNumber={tracking_id}"
conn.request("GET", path, headers=headers)
res = conn.getresponse()
data = res.read()
data_decoded = data.decode("utf-8")
conn.close()
try:
json_data = json.loads(data_decoded)
except json.JSONDecodeError:
return {"error": "Invalid JSON response"}
scheduled_delivery_date = json_data["ScheduledDeliveryDate"]
carrier = json_data["Carrier"]
status_summary = json_data["StatusSummary"]
tracking_details = json_data.get("TrackingDetails", [])
last_tracking_update = ""
if tracking_details and tracking_details is not None and tracking_details[0] is not None:
last_tracking_update = tracking_details[0]["EventDateTimeInDateTimeFormat"]
tracking_link = ""
if carrier == "USPS":
tracking_link = f"https://tools.usps.com/go/TrackConfirmAction?qtc_tLabels1={tracking_id}"
elif carrier == "UPS":
tracking_link = f"https://www.ups.com/track?track=yes&trackNums={tracking_id}"
return {
"scheduled_delivery_date": scheduled_delivery_date,
"carrier": carrier,
"status_summary": status_summary,
"tracking_link": tracking_link,
"last_tracking_update": last_tracking_update
}

View File

@@ -95,6 +95,7 @@ goal_pirate_treasure = AgentGoal(
), ),
) )
# ----- Travel Goals ---
goal_match_train_invoice = AgentGoal( goal_match_train_invoice = AgentGoal(
id = "goal_match_train_invoice", id = "goal_match_train_invoice",
category_tag="travel-trains", category_tag="travel-trains",
@@ -180,6 +181,7 @@ goal_event_flight_invoice = AgentGoal(
), ),
) )
# ----- HR Goals ---
# This goal uses the data/employee_pto_data.json file as dummy data. # This goal uses the data/employee_pto_data.json file as dummy data.
goal_hr_schedule_pto = AgentGoal( goal_hr_schedule_pto = AgentGoal(
id = "goal_hr_schedule_pto", id = "goal_hr_schedule_pto",
@@ -268,6 +270,7 @@ goal_hr_check_paycheck_bank_integration_status = AgentGoal(
), ),
) )
# ----- FinServ Goals ---
# this tool checks account balances, and uses ./data/customer_account_data.json as dummy data # this tool checks account balances, and uses ./data/customer_account_data.json as dummy data
goal_fin_check_account_balances = AgentGoal( goal_fin_check_account_balances = AgentGoal(
id = "goal_fin_check_account_balances", id = "goal_fin_check_account_balances",
@@ -368,6 +371,79 @@ goal_fin_loan_application = AgentGoal(
"user_confirmed_tool_run: <user clicks confirm on FinCheckAccountSubmitLoanApproval tool>", "user_confirmed_tool_run: <user clicks confirm on FinCheckAccountSubmitLoanApproval tool>",
"tool_result: { 'status': submitted, 'detailed_status': loan application is submitted and initial validation is complete, 'confirmation id': 333421, 'next_step': You'll receive a confirmation for final approval in three business days }", "tool_result: { 'status': submitted, 'detailed_status': loan application is submitted and initial validation is complete, 'confirmation id': 333421, 'next_step': You'll receive a confirmation for final approval in three business days }",
"agent: I have submitted your loan application process and the initial validation is successful. Your application ID is 333421. You'll receive a notification for final approval from us in three business days. " "agent: I have submitted your loan application process and the initial validation is successful. Your application ID is 333421. You'll receive a notification for final approval from us in three business days. "
# ----- E-Commerce Goals ---
#todo: add goal to list all orders for last X amount of time?
# this tool checks account balances, and uses ./data/customer_account_data.json as dummy data
goal_ecomm_order_status = AgentGoal(
id = "goal_ecomm_order_status",
category_tag="ecommerce",
agent_name="Check Order Status",
agent_friendly_description="Check the status of your order.",
tools=[
tool_registry.ecomm_get_order,
tool_registry.ecomm_track_package,
],
description="The user wants to learn the status of a specific order. If the status is 'shipped' or 'delivered', they might want to get the package tracking information. To assist with that goal, help the user gather args for these tools in order: "
"1. GetOrder: get information about an order"
"2. TrackPackage: provide tracking information for the package. This tool is optional and should only be offered if the status is 'shipped' OR 'delivered' - otherwise, skip this tool and do not mention it to the user.",
starter_prompt=starter_prompt_generic,
example_conversation_history="\n ".join(
[
"user: I'd like to know the status of my order",
"agent: Sure! I can help you out with that. May I have your email address or order number?",
"user: email is bob.johnson@emailzzz.com ",
"user_confirmed_tool_run: <user clicks confirm on GetOrderStatus tool>",
"tool_result: { 'id': '102', 'summary': 'Red Sunglasses', 'email': 'matt.murdock@nelsonmurdock.com', 'status': 'shipped', 'order_date': '2025-04-01', 'last_order_update': '2025-04-06', 'tracking_id': '039813852990618' }",
"agent: Your order 'Red Sunglasses,' placed April 1, 2025, was shipped on April 6, 2025. Would you like to see the tracking inforation?",
"user: Yes",
"user_confirmed_tool_run: <user clicks confirm on TrackPackage tool>",
"tool_result: { 'scheduled_delivery_date': 'April 30, 2025', 'carrier': 'USPS', 'status_summary': 'Your item has left our acceptance facility and is in transit to a sorting facility on April 10, 2025 at 7:06 am in IRON RIDGE, WI 53035.', 'tracking_link': 'https://tools.usps.com/go/TrackConfirmAction?qtc_tLabels1=12345','last_tracking_update': '2025-03-22T16:14:48'}",
"agent: Your package is scheduled to be delivered on April 30, 2025 via USPS. Here is the most recent status from them regarding your package, updated as of March 22: \n"
"Your item has left our acceptance facility and is in transit to a sorting facility on April 10, 2025 at 7:06 am in IRON RIDGE, WI 53035. \n"
"You can find the full tracking details here: tracking_link !",
]
),
)
goal_ecomm_list_orders = AgentGoal(
id = "goal_ecomm_list_orders",
category_tag="ecommerce",
agent_name="List All Orders",
agent_friendly_description="List all orders for a user.",
tools=[
tool_registry.ecomm_list_orders,
tool_registry.ecomm_get_order,
tool_registry.ecomm_track_package,
],
description="The user wants to see all of their orders. They may want more details about specific orders, and if the status of an order is 'shipped' or 'delivered', they might want to get the package tracking information. To assist with that goal, help the user gather args for this tool: "
"1. ListOrders: list orders for a user"
" and then offer the following tools, in a loop, until the user indicates they are done:"
"2. GetOrder: get information about an order. This tool is optional."
"3. TrackPackage: provide tracking information for the package. This tool is optional and should only be offered if the status is 'shipped' OR 'delivered' - otherwise, skip this tool and do not mention it to the user.",
starter_prompt=starter_prompt_generic,
example_conversation_history="\n ".join(
[
"user: I'd like to see all of my orders.",
"agent: Sure! I can help you out with that. May I have your email address?",
"user: email is bob.johnson@emailzzz.com ",
"user_confirmed_tool_run: <user clicks confirm on ListOrders tool>",
"tool_result: a list of orders including [{'id': '102', 'summary': 'Red Sunglasses', 'email': 'matt.murdock@nelsonmurdock.com', 'status': 'shipped', 'order_date': '2025-04-01', 'last_order_update': '2025-04-06', 'tracking_id': '039813852990618' }, { 'id': '103', 'summary': 'Blue Sunglasses', 'email': 'matt.murdock@nelsonmurdock.com', 'status': 'paid', 'order_date': '2025-04-03', 'last_order_update': '2025-04-07' }]",
"agent: Your orders are as follows: \n",
"1. Red Sunglasses, ordered 4/1/2025 \n",
"2. Blue Sunglasses, ordered 4/3/2025 \n",
"Would you like more information about any of your orders?"
"user: Yes, the Red Sunglasses",
"agent: Your order 'Red Sunglasses,' placed April 1, 2025, was shipped on April 6, 2025. Would you like to see the tracking inforation?",
"user: Yes",
"user_confirmed_tool_run: <user clicks confirm on TrackPackage tool>",
"tool_result: { 'scheduled_delivery_date': 'April 30, 2025', 'carrier': 'USPS', 'status_summary': 'Your item has left our acceptance facility and is in transit to a sorting facility on April 10, 2025 at 7:06 am in IRON RIDGE, WI 53035.', 'tracking_link': 'https://tools.usps.com/go/TrackConfirmAction?qtc_tLabels1=12345','last_tracking_update': '2025-03-22T16:14:48'}",
"agent: Your package is scheduled to be delivered on April 30, 2025 via USPS. Here is the most recent status from them regarding your package \n, updated as of March 22: \n"
"Your item has left our acceptance facility and is in transit to a sorting facility on April 10, 2025 at 7:06 am in IRON RIDGE, WI 53035. \n"
"You can find the full tracking details here: tracking_link ! \n"
"Would you like more information about any of your other orders?",
"user: No"
"agent: Thanks, and have a great day!"
] ]
), ),
) )
@@ -384,6 +460,6 @@ goal_list.append(goal_hr_check_paycheck_bank_integration_status)
goal_list.append(goal_fin_check_account_balances) goal_list.append(goal_fin_check_account_balances)
goal_list.append(goal_fin_move_money) goal_list.append(goal_fin_move_money)
goal_list.append(goal_fin_loan_application) goal_list.append(goal_fin_loan_application)
goal_list.append(goal_ecomm_list_orders)
goal_list.append(goal_ecomm_order_status)

View File

@@ -11,7 +11,7 @@ def search_airport(query: str) -> list:
""" """
load_dotenv(override=True) load_dotenv(override=True)
api_key = os.getenv("RAPIDAPI_KEY", "YOUR_DEFAULT_KEY") api_key = os.getenv("RAPIDAPI_KEY", "YOUR_DEFAULT_KEY")
api_host = os.getenv("RAPIDAPI_HOST", "sky-scrapper.p.rapidapi.com") api_host = os.getenv("RAPIDAPI_HOST_FLIGHTS", "sky-scrapper.p.rapidapi.com")
conn = http.client.HTTPSConnection(api_host) conn = http.client.HTTPSConnection(api_host)
headers = { headers = {
@@ -73,7 +73,7 @@ def search_flights_real_api(
# Step 2: Call flight search with resolved codes # Step 2: Call flight search with resolved codes
load_dotenv(override=True) load_dotenv(override=True)
api_key = os.getenv("RAPIDAPI_KEY", "YOUR_DEFAULT_KEY") api_key = os.getenv("RAPIDAPI_KEY", "YOUR_DEFAULT_KEY")
api_host = os.getenv("RAPIDAPI_HOST", "sky-scrapper.p.rapidapi.com") api_host = os.getenv("RAPIDAPI_HOST_FLIGHTS", "sky-scrapper.p.rapidapi.com")
conn = http.client.HTTPSConnection(api_host) conn = http.client.HTTPSConnection(api_host)
headers = { headers = {

View File

@@ -333,6 +333,45 @@ financial_submit_loan_approval = ToolDefinition(
name="amount", name="amount",
type="string", type="string",
description="amount requested for the loan", description="amount requested for the loan",
# ----- ECommerce Use Case Tools -----
ecomm_list_orders = ToolDefinition(
name="ListOrders",
description="Get all orders for a certain email address.",
arguments=[
ToolArgument(
name="email_address",
type="string",
description="Email address of user by which to find orders",
),
],
)
ecomm_get_order = ToolDefinition(
name="GetOrder",
description="Get infromation about an order by order ID.",
arguments=[
ToolArgument(
name="order_id",
type="string",
description="ID of order to determine status of",
),
],
)
ecomm_track_package = ToolDefinition(
name="TrackPackage",
description="Get tracking information for a package by shipping provider and tracking ID",
arguments=[
ToolArgument(
name="tracking_id",
type="string",
description="ID of package to track",
),
ToolArgument(
name="userConfirmation",
type="string",
description="Indication of user's desire to get package tracking information",
), ),
], ],
) )