mirror of
https://github.com/temporal-community/temporal-ai-agent.git
synced 2026-03-16 22:48:09 +01:00
Model Context Protocol (MCP) support with new use case (#42)
* initial mcp * food ordering with mcp * prompt eng * splitting out goals and updating docs * a diff so I can get tests from codex * a diff so I can get tests from codex * oops, missing files * tests, file formatting * readme and setup updates * setup.md link fixes * readme change * readme change * readme change * stripe food setup script * single agent mode default * prompt engineering for better multi agent performance * performance should be greatly improved * Update goals/finance.py Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * Update activities/tool_activities.py Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * co-pilot PR suggested this change, and now fixed it * stronger wording around json format response * formatting * moved docs to dir * moved image assets under docs * cleanup env example, stripe guidance * cleanup --------- Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
This commit is contained in:
committed by
GitHub
parent
1811e4cf59
commit
5d55a9fe80
@@ -8,6 +8,7 @@ from .fin.get_account_balances import get_account_balance
|
||||
from .fin.move_money import move_money
|
||||
from .fin.submit_loan_application import submit_loan_application
|
||||
from .find_events import find_events
|
||||
from .food.add_to_cart import add_to_cart
|
||||
from .give_hint import give_hint
|
||||
from .guess_location import guess_location
|
||||
from .hr.book_pto import book_pto
|
||||
@@ -66,5 +67,7 @@ def get_handler(tool_name: str):
|
||||
return give_hint
|
||||
if tool_name == "GuessLocation":
|
||||
return guess_location
|
||||
if tool_name == "AddToCart":
|
||||
return add_to_cart
|
||||
|
||||
raise ValueError(f"Unknown tool: {tool_name}")
|
||||
|
||||
@@ -1,17 +1,14 @@
|
||||
import os
|
||||
|
||||
import stripe
|
||||
from dotenv import load_dotenv
|
||||
|
||||
load_dotenv(override=True) # Load environment variables from a .env file
|
||||
|
||||
stripe.api_key = os.getenv("STRIPE_API_KEY")
|
||||
|
||||
|
||||
def ensure_customer_exists(
|
||||
customer_id: str = None, email: str = "default@example.com"
|
||||
) -> str:
|
||||
"""Ensure a Stripe customer exists; create one if not."""
|
||||
import stripe
|
||||
|
||||
if customer_id:
|
||||
try:
|
||||
stripe.Customer.retrieve(customer_id)
|
||||
@@ -27,6 +24,12 @@ def ensure_customer_exists(
|
||||
|
||||
def create_invoice(args: dict) -> dict:
|
||||
"""Create and finalize a Stripe invoice."""
|
||||
import stripe
|
||||
|
||||
# Load environment variables and configure stripe
|
||||
load_dotenv(override=True)
|
||||
stripe.api_key = os.getenv("STRIPE_API_KEY")
|
||||
|
||||
# If an API key exists in the env file, find or create customer
|
||||
if stripe.api_key is not None and stripe.api_key != "":
|
||||
customer_id = ensure_customer_exists(
|
||||
|
||||
33
tools/food/add_to_cart.py
Normal file
33
tools/food/add_to_cart.py
Normal file
@@ -0,0 +1,33 @@
|
||||
def add_to_cart(args: dict) -> dict:
|
||||
"""
|
||||
Simple stateless cart tool for demo purposes.
|
||||
In production, this would use proper session storage or database.
|
||||
"""
|
||||
customer_email = args.get("customer_email")
|
||||
item_name = args.get("item_name")
|
||||
item_price = float(args.get("item_price", 0))
|
||||
quantity = int(args.get("quantity", 1))
|
||||
stripe_product_id = args.get("stripe_product_id")
|
||||
|
||||
# Basic validation
|
||||
if not customer_email:
|
||||
return {"error": "Customer email is required"}
|
||||
if not item_name:
|
||||
return {"error": "Item name is required"}
|
||||
if item_price <= 0:
|
||||
return {"error": "Item price must be greater than 0"}
|
||||
if quantity <= 0:
|
||||
return {"error": "Quantity must be greater than 0"}
|
||||
|
||||
# For demo purposes, just acknowledge the addition
|
||||
# In a real system, this would store to session/database
|
||||
return {
|
||||
"status": "success",
|
||||
"message": f"Added {quantity} x {item_name} (${item_price}) to cart for {customer_email}",
|
||||
"item_added": {
|
||||
"name": item_name,
|
||||
"price": item_price,
|
||||
"quantity": quantity,
|
||||
"stripe_product_id": stripe_product_id,
|
||||
},
|
||||
}
|
||||
48
tools/food/setup/archive_food_products.py
Normal file
48
tools/food/setup/archive_food_products.py
Normal file
@@ -0,0 +1,48 @@
|
||||
import os
|
||||
from dotenv import load_dotenv
|
||||
|
||||
|
||||
def delete_food_ordering_products():
|
||||
"""Archive all Stripe products with metadata use_case = food_ordering_demo (since products with prices cannot be deleted)."""
|
||||
import stripe
|
||||
|
||||
# Load environment variables and configure stripe
|
||||
load_dotenv(override=True)
|
||||
stripe.api_key = os.getenv("STRIPE_API_KEY")
|
||||
|
||||
if not stripe.api_key:
|
||||
print("Error: STRIPE_API_KEY not found in environment variables")
|
||||
return
|
||||
|
||||
try:
|
||||
# Search for products with food_ordering_demo use_case
|
||||
products = stripe.Product.search(
|
||||
query="metadata['use_case']:'food_ordering_demo'", limit=100
|
||||
)
|
||||
|
||||
if not products.data:
|
||||
print("No products found with use_case = food_ordering_demo")
|
||||
return
|
||||
|
||||
archived_count = 0
|
||||
|
||||
for product in products.data:
|
||||
try:
|
||||
# Archive the product (set active=False)
|
||||
stripe.Product.modify(product.id, active=False)
|
||||
print(f"Archived product: {product.name} (ID: {product.id})")
|
||||
archived_count += 1
|
||||
|
||||
except Exception as e:
|
||||
print(
|
||||
f"Error archiving product {product.name} (ID: {product.id}): {str(e)}"
|
||||
)
|
||||
|
||||
print(f"\nSuccessfully archived {archived_count} products")
|
||||
|
||||
except Exception as e:
|
||||
print(f"Error searching for products: {str(e)}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
delete_food_ordering_products()
|
||||
92
tools/food/setup/create_stripe_products.py
Normal file
92
tools/food/setup/create_stripe_products.py
Normal file
@@ -0,0 +1,92 @@
|
||||
import json
|
||||
import os
|
||||
from dotenv import load_dotenv
|
||||
|
||||
|
||||
def create_stripe_products():
|
||||
"""Create Stripe products and prices from the stripe_pizza_products.json file."""
|
||||
import stripe
|
||||
|
||||
# Load environment variables and configure stripe
|
||||
load_dotenv(override=True)
|
||||
stripe.api_key = os.getenv("STRIPE_API_KEY")
|
||||
|
||||
if not stripe.api_key:
|
||||
print("Error: STRIPE_API_KEY not found in environment variables")
|
||||
return
|
||||
|
||||
# Load the products data
|
||||
current_dir = os.path.dirname(__file__)
|
||||
products_file = os.path.join(current_dir, "stripe_pizza_products.json")
|
||||
|
||||
with open(products_file, "r") as f:
|
||||
products_data = json.load(f)
|
||||
|
||||
# Filter for food ordering demo products only
|
||||
food_products = [
|
||||
p
|
||||
for p in products_data
|
||||
if p.get("metadata", {}).get("use_case") == "food_ordering_demo"
|
||||
]
|
||||
|
||||
created_products = []
|
||||
|
||||
for product_data in food_products:
|
||||
try:
|
||||
# Create the product with relevant fields
|
||||
product = stripe.Product.create(
|
||||
name=product_data["name"],
|
||||
description=product_data.get("description"),
|
||||
images=product_data.get("images", []),
|
||||
metadata=product_data.get("metadata", {}),
|
||||
type=product_data.get("type", "service"),
|
||||
active=product_data.get("active", True),
|
||||
)
|
||||
|
||||
# Create price for the product if price_info exists
|
||||
price_info = product_data.get("price_info")
|
||||
if price_info:
|
||||
price_amount = price_info.get("amount")
|
||||
currency = price_info.get("currency", "usd")
|
||||
|
||||
price = stripe.Price.create(
|
||||
currency=currency, unit_amount=price_amount, product=product.id
|
||||
)
|
||||
|
||||
# Set this price as the default price for the product
|
||||
stripe.Product.modify(product.id, default_price=price.id)
|
||||
|
||||
print(
|
||||
f"Created product: {product.name} (ID: {product.id}) with default price ${price_amount/100:.2f}"
|
||||
)
|
||||
|
||||
created_products.append(
|
||||
{
|
||||
"name": product.name,
|
||||
"id": product.id,
|
||||
"price_id": price.id,
|
||||
"price_amount": price_amount,
|
||||
"original_id": product_data["id"],
|
||||
}
|
||||
)
|
||||
else:
|
||||
print(
|
||||
f"Created product: {product.name} (ID: {product.id}) - No price defined"
|
||||
)
|
||||
created_products.append(
|
||||
{
|
||||
"name": product.name,
|
||||
"id": product.id,
|
||||
"original_id": product_data["id"],
|
||||
}
|
||||
)
|
||||
|
||||
except Exception as e:
|
||||
print(f"Error creating product {product_data['name']}: {str(e)}")
|
||||
|
||||
print(f"\nSuccessfully created {len(created_products)} products with prices")
|
||||
return created_products
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
create_stripe_products()
|
||||
188
tools/food/setup/stripe_pizza_products.json
Normal file
188
tools/food/setup/stripe_pizza_products.json
Normal file
@@ -0,0 +1,188 @@
|
||||
[
|
||||
{
|
||||
"id": "prod_SSWirxxS5A8gcT",
|
||||
"object": "product",
|
||||
"active": true,
|
||||
"attributes": [],
|
||||
"created": 1749360061,
|
||||
"default_price": "price_1RXbfGKVZbzw7QA57Mj1akGI",
|
||||
"description": "A large size bottle of cola.",
|
||||
"images": [
|
||||
"https://files.stripe.com/links/MDB8YWNjdF8xTkJPTHVLVlpienc3UUE1fGZsX3Rlc3RfbDJxckJKMDRnT1dDc253OHlZNWNkZkY5006Xg07kHT"
|
||||
],
|
||||
"livemode": false,
|
||||
"marketing_features": [],
|
||||
"metadata": {
|
||||
"use_case": "food_ordering_demo"
|
||||
},
|
||||
"name": "Soda",
|
||||
"price_info": {
|
||||
"amount": 349,
|
||||
"currency": "usd"
|
||||
},
|
||||
"package_dimensions": null,
|
||||
"shippable": null,
|
||||
"statement_descriptor": null,
|
||||
"tax_code": null,
|
||||
"type": "service",
|
||||
"unit_label": null,
|
||||
"updated": 1749360062,
|
||||
"url": null
|
||||
},
|
||||
{
|
||||
"id": "prod_SSWhxv3tUy1YOG",
|
||||
"object": "product",
|
||||
"active": true,
|
||||
"attributes": [],
|
||||
"created": 1749359978,
|
||||
"default_price": "price_1RXbdvKVZbzw7QA5ARomQvaf",
|
||||
"description": "Our warm, crusty bread is generously spread with a savory garlic butter and toasted to golden perfection. It's the ideal aromatic and flavorful side to accompany your main course.",
|
||||
"images": [
|
||||
"https://files.stripe.com/links/MDB8YWNjdF8xTkJPTHVLVlpienc3UUE1fGZsX3Rlc3RfWTdIZTBkUjNZNFQ1ZEhSVG9nRnduY1pS00XVgLRRZD"
|
||||
],
|
||||
"livemode": false,
|
||||
"marketing_features": [],
|
||||
"metadata": {
|
||||
"use_case": "food_ordering_demo"
|
||||
},
|
||||
"name": "Garlic Bread",
|
||||
"price_info": {
|
||||
"amount": 799,
|
||||
"currency": "usd"
|
||||
},
|
||||
"package_dimensions": null,
|
||||
"shippable": null,
|
||||
"statement_descriptor": null,
|
||||
"tax_code": null,
|
||||
"type": "service",
|
||||
"unit_label": null,
|
||||
"updated": 1749360084,
|
||||
"url": null
|
||||
},
|
||||
{
|
||||
"id": "prod_SSWgXa5bwUFCJs",
|
||||
"object": "product",
|
||||
"active": true,
|
||||
"attributes": [],
|
||||
"created": 1749359922,
|
||||
"default_price": "price_1RXbd0KVZbzw7QA5Nq36vdLW",
|
||||
"description": "A tribute to Italian simplicity, this pizza is topped with fresh mozzarella, a vibrant tomato sauce, and fragrant basil leaves. Each bite delivers a clean and authentic taste of Italy's most famous flavors.",
|
||||
"images": [
|
||||
"https://files.stripe.com/links/MDB8YWNjdF8xTkJPTHVLVlpienc3UUE1fGZsX3Rlc3RfamdmTXBFbzY0TW9rS2N0c2g0Tml2SERL00Evl60Ttq"
|
||||
],
|
||||
"livemode": false,
|
||||
"marketing_features": [],
|
||||
"metadata": {
|
||||
"use_case": "food_ordering_demo"
|
||||
},
|
||||
"name": "Margherita Pizza",
|
||||
"price_info": {
|
||||
"amount": 1699,
|
||||
"currency": "usd"
|
||||
},
|
||||
"package_dimensions": null,
|
||||
"shippable": null,
|
||||
"statement_descriptor": null,
|
||||
"tax_code": null,
|
||||
"type": "service",
|
||||
"unit_label": null,
|
||||
"updated": 1749359998,
|
||||
"url": null
|
||||
},
|
||||
{
|
||||
"id": "prod_SSWf738UqIJzzi",
|
||||
"object": "product",
|
||||
"active": true,
|
||||
"attributes": [],
|
||||
"created": 1749359845,
|
||||
"default_price": "price_1RXbbmKVZbzw7QA53EkjV2nB",
|
||||
"description": "A timeless classic featuring a generous layer of savory pepperoni over rich tomato sauce and melted mozzarella cheese. It's the perfect choice for those who love a bold, meaty flavor on a perfectly baked crust.",
|
||||
"images": [
|
||||
"https://files.stripe.com/links/MDB8YWNjdF8xTkJPTHVLVlpienc3UUE1fGZsX3Rlc3RfcGRHc0c4cEZYWmR2bm0zOHBOa0FWMk5t008QmCJoWr"
|
||||
],
|
||||
"livemode": false,
|
||||
"marketing_features": [],
|
||||
"metadata": {
|
||||
"use_case": "food_ordering_demo"
|
||||
},
|
||||
"name": "Pepperoni Pizza",
|
||||
"price_info": {
|
||||
"amount": 2299,
|
||||
"currency": "usd"
|
||||
},
|
||||
"package_dimensions": null,
|
||||
"shippable": null,
|
||||
"statement_descriptor": null,
|
||||
"tax_code": null,
|
||||
"type": "service",
|
||||
"unit_label": null,
|
||||
"updated": 1749359846,
|
||||
"url": null
|
||||
},
|
||||
{
|
||||
"id": "prod_SGMXBnatLlkJ4d",
|
||||
"object": "product",
|
||||
"active": true,
|
||||
"attributes": [],
|
||||
"created": 1746554502,
|
||||
"default_price": "price_1RLpoJKVZbzw7QA5ra76Fk6g",
|
||||
"description": null,
|
||||
"images": [],
|
||||
"livemode": false,
|
||||
"marketing_features": [],
|
||||
"metadata": {},
|
||||
"name": "ACME Scooter Token",
|
||||
"package_dimensions": null,
|
||||
"shippable": null,
|
||||
"statement_descriptor": null,
|
||||
"tax_code": null,
|
||||
"type": "service",
|
||||
"unit_label": null,
|
||||
"updated": 1746554503,
|
||||
"url": null
|
||||
},
|
||||
{
|
||||
"id": "prod_NxJPcqTWzXk45K",
|
||||
"object": "product",
|
||||
"active": true,
|
||||
"attributes": [],
|
||||
"created": 1684961969,
|
||||
"default_price": null,
|
||||
"description": "$12/Month subscription",
|
||||
"images": [],
|
||||
"livemode": false,
|
||||
"marketing_features": [],
|
||||
"metadata": {},
|
||||
"name": "Starter Subscription",
|
||||
"package_dimensions": null,
|
||||
"shippable": null,
|
||||
"statement_descriptor": null,
|
||||
"tax_code": null,
|
||||
"type": "service",
|
||||
"unit_label": null,
|
||||
"updated": 1684961969,
|
||||
"url": null
|
||||
},
|
||||
{
|
||||
"id": "prod_NxJ4KvyENd0uUu",
|
||||
"object": "product",
|
||||
"active": true,
|
||||
"attributes": [],
|
||||
"created": 1684960731,
|
||||
"default_price": null,
|
||||
"description": "Created with the Stripe CLI",
|
||||
"images": [],
|
||||
"livemode": false,
|
||||
"marketing_features": [],
|
||||
"metadata": {},
|
||||
"name": "Temporal Money Transfer",
|
||||
"package_dimensions": null,
|
||||
"shippable": null,
|
||||
"statement_descriptor": null,
|
||||
"tax_code": null,
|
||||
"type": "service",
|
||||
"unit_label": null,
|
||||
"updated": 1684960731,
|
||||
"url": null
|
||||
}
|
||||
]
|
||||
@@ -2,7 +2,7 @@ import os
|
||||
from typing import List
|
||||
|
||||
import tools.tool_registry as tool_registry
|
||||
from models.tool_definitions import AgentGoal
|
||||
from models.tool_definitions import AgentGoal, MCPServerDefinition
|
||||
|
||||
# Turn on Silly Mode - this should be a description of the persona you'd like the bot to have and can be a single word or a phrase.
|
||||
# Example if you want the bot to be a specific person, like Mario or Christopher Walken, or to describe a specific tone:
|
||||
@@ -311,7 +311,7 @@ goal_fin_check_account_balances = AgentGoal(
|
||||
)
|
||||
|
||||
# this tool checks account balances, and uses ./data/customer_account_data.json as dummy data
|
||||
# it also uses a separate workflow/tool, see ./SETUP.md for details
|
||||
# it also uses a separate workflow/tool, see ./setup.md for details
|
||||
goal_fin_move_money = AgentGoal(
|
||||
id="goal_fin_move_money",
|
||||
category_tag="fin",
|
||||
@@ -351,7 +351,7 @@ goal_fin_move_money = AgentGoal(
|
||||
)
|
||||
|
||||
# this starts a loan approval process
|
||||
# it also uses a separate workflow/tool, see ./SETUP.md for details
|
||||
# it also uses a separate workflow/tool, see ./setup.md for details
|
||||
goal_fin_loan_application = AgentGoal(
|
||||
id="goal_fin_loan_application",
|
||||
category_tag="fin",
|
||||
@@ -455,6 +455,126 @@ goal_ecomm_list_orders = AgentGoal(
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
# ----- MCP Integrations -----
|
||||
goal_mcp_stripe = AgentGoal(
|
||||
id="goal_mcp_stripe",
|
||||
category_tag="mcp-integrations",
|
||||
agent_name="Stripe MCP Agent",
|
||||
agent_friendly_description="Manage Stripe operations via MCP",
|
||||
tools=[], # Will be populated dynamically
|
||||
mcp_server_definition=MCPServerDefinition(
|
||||
name="stripe-mcp",
|
||||
command="npx",
|
||||
args=[
|
||||
"-y",
|
||||
"@stripe/mcp",
|
||||
"--tools=all",
|
||||
f"--api-key={os.getenv('STRIPE_API_KEY')}",
|
||||
],
|
||||
env=None,
|
||||
included_tools=["list_customers", "list_products"],
|
||||
),
|
||||
description="Help manage Stripe operations for customer and product data by using the customers.read and products.read tools.",
|
||||
starter_prompt="Welcome! I can help you read Stripe customer and product information.",
|
||||
example_conversation_history="\n ".join(
|
||||
[
|
||||
"agent: Welcome! I can help you read Stripe customer and product information. What would you like to do first?",
|
||||
"user: what customers are there?",
|
||||
"agent: I'll check for customers now.",
|
||||
"user_confirmed_tool_run: <user clicks confirm on customers.read tool>",
|
||||
'tool_result: { "customers": [{"id": "cus_abc", "name": "Customer A"}, {"id": "cus_xyz", "name": "Customer B"}] }',
|
||||
"agent: I found two customers: Customer A and Customer B. Can I help with anything else?",
|
||||
"user: what products exist?",
|
||||
"agent: Let me get the list of products for you.",
|
||||
"user_confirmed_tool_run: <user clicks confirm on products.read tool>",
|
||||
'tool_result: { "products": [{"id": "prod_123", "name": "Gold Plan"}, {"id": "prod_456", "name": "Silver Plan"}] }',
|
||||
"agent: I found two products: Gold Plan and Silver Plan.",
|
||||
]
|
||||
),
|
||||
)
|
||||
|
||||
# ----- Food Ordering Goal -----
|
||||
goal_food_ordering = AgentGoal(
|
||||
id="goal_food_ordering",
|
||||
category_tag="food",
|
||||
agent_name="Food Ordering Assistant",
|
||||
agent_friendly_description="Order food from Tony's Pizza Palace using Stripe for payment processing. Browse menu, add items to your order, and check out securely. Please ensure context carries over between tool runs.",
|
||||
tools=[tool_registry.food_add_to_cart_tool],
|
||||
mcp_server_definition=MCPServerDefinition(
|
||||
name="stripe-mcp",
|
||||
command="npx",
|
||||
args=[
|
||||
"-y",
|
||||
"@stripe/mcp",
|
||||
"--tools=all",
|
||||
f"--api-key={os.getenv('STRIPE_API_KEY')}",
|
||||
],
|
||||
env=None,
|
||||
included_tools=[
|
||||
"list_products",
|
||||
"list_prices",
|
||||
"create_customer",
|
||||
"create_invoice",
|
||||
"create_invoice_item",
|
||||
"finalize_invoice",
|
||||
],
|
||||
),
|
||||
description="The user wants to order food from Tony's Pizza Palace. "
|
||||
"First, help the user browse the menu by calling list_products. "
|
||||
"When they express interest in items, get pricing using list_prices. "
|
||||
"Add items to their cart using AddToCart as they decide - the order doesn't matter, multiple items can be added. "
|
||||
"After they're done selecting items, get their customer details and create a Stripe customer. "
|
||||
"For checkout: 1) create_invoice, 2) create_invoice_item for each individual item (IMPORTANT: create_invoice_item does NOT accept quantity parameter - call it once per item, so if user wants 2 pizzas, call create_invoice_item twice with the same price), "
|
||||
"3) finalize_invoice. The finalized invoice will contain a hosted_invoice_url for payment.",
|
||||
starter_prompt=starter_prompt_generic,
|
||||
example_conversation_history="\n ".join(
|
||||
[
|
||||
"user: I want to order food.",
|
||||
"agent: Welcome to Tony's Pizza Palace! I can help you browse our menu, add items to your cart, and process payment. Let me show you what we have available.",
|
||||
"user_confirmed_tool_run: <user clicks confirm on list_products tool with limit=100 and product.metadata.use_case == 'food_ordering_demo'>",
|
||||
'tool_result: {"products": [{"id": "prod_pepperoni", "name": "Pepperoni Pizza", "description": "A timeless classic featuring pepperoni"}, {"id": "prod_margherita", "name": "Margherita Pizza", "description": "Fresh mozzarella and basil"}, {"id": "prod_garlic_bread", "name": "Garlic Bread", "description": "Warm crusty bread with garlic butter"}, {"id": "prod_soda", "name": "Soda", "description": "A large bottle of cola"}]}',
|
||||
"agent: We have Pepperoni Pizza, Margherita Pizza, Garlic Bread, and Soda available. What would you like to order?",
|
||||
"user: I'd like 2 pepperoni pizzas and a soda.",
|
||||
"agent: Excellent! Let me get the pricing for the pepperoni pizzas first so you know the cost.",
|
||||
"user_confirmed_tool_run: <user clicks confirm on list_prices tool with product='prod_pepperoni'>",
|
||||
'tool_result: {"prices": [{"id": "price_pepperoni_large", "product": "prod_pepperoni", "unit_amount": 2299, "currency": "usd"}]}',
|
||||
"agent: Pepperoni Pizza is $22.99 each. Now let me check the soda pricing:",
|
||||
"user_confirmed_tool_run: <user clicks confirm on list_prices tool with product='prod_soda'>",
|
||||
'tool_result: {"prices": [{"id": "price_soda", "product": "prod_soda", "unit_amount": 250, "currency": "usd"}]}',
|
||||
"agent: And Soda is $2.50. So 2 Pepperoni Pizzas ($22.99 each) plus 1 Soda ($2.50) = $48.48 total. What's your email address to add these items to your cart?",
|
||||
"user: jane.smith@example.com",
|
||||
"agent: Perfect! Adding 2 Pepperoni Pizzas to your cart for jane.smith@example.com. Note: I'm using quantity=2 since you want 2 pizzas.",
|
||||
"user_confirmed_tool_run: <user clicks confirm on AddToCart tool with customer_email='jane.smith@example.com', item_name='Pepperoni Pizza', item_price=22.99, quantity=2, stripe_product_id='prod_pepperoni'>",
|
||||
'tool_result: {"status": "success", "message": "Added 2 x Pepperoni Pizza ($22.99) to cart for jane.smith@example.com", "item_added": {"quantity": 2, "name": "Pepperoni Pizza"}}',
|
||||
"agent: Great! Added 2 Pepperoni Pizzas to your cart. Now adding the Soda (quantity=1):",
|
||||
"user_confirmed_tool_run: <user clicks confirm on AddToCart tool with customer_email='jane.smith@example.com', item_name='Soda', item_price=2.50, quantity=1, stripe_product_id='prod_soda'>",
|
||||
'tool_result: {"status": "success", "message": "Added 1 x Soda ($2.50) to cart for jane.smith@example.com", "item_added": {"quantity": 1, "name": "Soda"}}',
|
||||
"agent: Excellent! Your cart now contains: 2 Pepperoni Pizzas ($22.99 each) and 1 Soda ($2.50). Ready to checkout? I'll need your full name.",
|
||||
"user: Yes, let's proceed with payment. My name is Jane Smith.",
|
||||
"agent: Thank you Jane! Creating your Stripe customer profile with name and email.",
|
||||
"user_confirmed_tool_run: <user clicks confirm on create_customer tool with name='Jane Smith' and email='jane.smith@example.com'>",
|
||||
'tool_result: {"customer": {"id": "cus_ExAmPlE12345", "name": "Jane Smith", "email": "jane.smith@example.com"}}',
|
||||
"agent: Customer profile created! Now I'll create a draft invoice for your order.",
|
||||
"user_confirmed_tool_run: <user clicks confirm on create_invoice tool with customer='cus_ExAmPlE12345'>",
|
||||
'tool_result: {"invoice": {"id": "in_InvOicE54321", "status": "draft", "customer": "cus_ExAmPlE12345"}}',
|
||||
"agent: Invoice created! Now adding your first Pepperoni Pizza to the invoice.",
|
||||
"user_confirmed_tool_run: <user clicks confirm on create_invoice_item tool with customer='cus_ExAmPlE12345', invoice='in_InvOicE54321', price='price_pepperoni_large'>",
|
||||
'tool_result: {"status": "success", "invoice_item": {"id": "ii_pizza1"}}',
|
||||
"agent: First pizza added! Now adding your second Pepperoni Pizza to the invoice.",
|
||||
"user_confirmed_tool_run: <user clicks confirm on create_invoice_item tool with customer='cus_ExAmPlE12345', invoice='in_InvOicE54321', price='price_pepperoni_large'>",
|
||||
'tool_result: {"status": "success", "invoice_item": {"id": "ii_pizza2"}}',
|
||||
"agent: Both pizzas added! Now adding your Soda to the invoice.",
|
||||
"user_confirmed_tool_run: <user clicks confirm on create_invoice_item tool with customer='cus_ExAmPlE12345', invoice='in_InvOicE54321', price='price_soda'>",
|
||||
'tool_result: {"status": "success", "invoice_item": {"id": "ii_soda"}}',
|
||||
"agent: All items added to invoice! Now finalizing to generate the payment link.",
|
||||
"user_confirmed_tool_run: <user clicks confirm on finalize_invoice tool with invoice='in_InvOicE54321'>",
|
||||
'tool_result: {"invoice": {"id": "in_InvOicE54321", "status": "open", "hosted_invoice_url": "https://invoice.stripe.com/i/acct_1NBOLuKVZbzw7QA5/test_YWNjdF8xTkJPTHVLVlpienc3UUE1LF9SbGJlZ2xLMjhlc3lxNzFkVVBXbkswOUlFa2Rna1RxLDEzMDAwMTY4Nw0200RKeJRMlw?s=ap", "amount_due": 4848}}',
|
||||
"agent: Perfect! Your order is complete with 2 Pepperoni Pizzas and 1 Soda. Total: $48.48. Pay securely here: https://invoice.stripe.com/i/acct_1NBOLuKVZbzw7QA5/test_YWNjdF8xTkJPTHVLVlpienc3UUE1LF9SbGJlZ2xLMjhlc3lxNzFkVVBXbkswOUlFa2Rna1RxLDEzMDAwMTY4Nw0200RKeJRMlw?s=ap\\n\\nThank you for ordering from Tony's Pizza Palace!",
|
||||
]
|
||||
),
|
||||
)
|
||||
|
||||
# Add the goals to a list for more generic processing, like listing available agents
|
||||
goal_list: List[AgentGoal] = []
|
||||
goal_list.append(goal_choose_agent_type)
|
||||
@@ -469,19 +589,21 @@ goal_list.append(goal_fin_move_money)
|
||||
goal_list.append(goal_fin_loan_application)
|
||||
goal_list.append(goal_ecomm_list_orders)
|
||||
goal_list.append(goal_ecomm_order_status)
|
||||
goal_list.append(goal_mcp_stripe)
|
||||
goal_list.append(goal_food_ordering)
|
||||
|
||||
|
||||
# for multi-goal, just set list agents as the last tool
|
||||
first_goal_value = os.getenv("AGENT_GOAL")
|
||||
if first_goal_value is None:
|
||||
multi_goal_mode = True # default if unset
|
||||
multi_goal_mode = False # default to single agent mode if unset
|
||||
elif (
|
||||
first_goal_value is not None
|
||||
and first_goal_value.lower() != "goal_choose_agent_type"
|
||||
and first_goal_value.lower() == "goal_choose_agent_type"
|
||||
):
|
||||
multi_goal_mode = False
|
||||
else:
|
||||
multi_goal_mode = True
|
||||
else:
|
||||
multi_goal_mode = False
|
||||
|
||||
if multi_goal_mode:
|
||||
for goal in goal_list:
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import os
|
||||
|
||||
import tools.goal_registry as goals
|
||||
import goals
|
||||
|
||||
|
||||
def list_agents(args: dict) -> dict:
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import http.client
|
||||
import json
|
||||
import os
|
||||
import random
|
||||
import urllib.parse
|
||||
|
||||
from dotenv import load_dotenv
|
||||
@@ -174,45 +175,166 @@ def search_flights_real_api(
|
||||
}
|
||||
|
||||
|
||||
def generate_smart_flights(origin: str, destination: str) -> list:
|
||||
"""
|
||||
Generate realistic flight options with smart pricing based on origin and destination.
|
||||
"""
|
||||
# Common airlines for different regions
|
||||
airlines_by_region = {
|
||||
"domestic_us": [
|
||||
{"name": "American Airlines", "code": "AA"},
|
||||
{"name": "United Airlines", "code": "UA"},
|
||||
{"name": "Delta Airlines", "code": "DL"},
|
||||
{"name": "Southwest Airlines", "code": "WN"},
|
||||
],
|
||||
"us_international": [
|
||||
{"name": "American Airlines", "code": "AA"},
|
||||
{"name": "United Airlines", "code": "UA"},
|
||||
{"name": "Delta Airlines", "code": "DL"},
|
||||
{"name": "Virgin Atlantic", "code": "VS"},
|
||||
],
|
||||
"australia_nz": [
|
||||
{"name": "Qantas", "code": "QF"},
|
||||
{"name": "Jetstar", "code": "JQ"},
|
||||
{"name": "Virgin Australia", "code": "VA"},
|
||||
{"name": "Air New Zealand", "code": "NZ"},
|
||||
],
|
||||
"international": [
|
||||
{"name": "American Airlines", "code": "AA"},
|
||||
{"name": "United Airlines", "code": "UA"},
|
||||
{"name": "Delta Airlines", "code": "DL"},
|
||||
{"name": "Air New Zealand", "code": "NZ"},
|
||||
{"name": "Qantas", "code": "QF"},
|
||||
{"name": "Singapore Airlines", "code": "SQ"},
|
||||
],
|
||||
}
|
||||
|
||||
# Determine route type and base pricing
|
||||
origin_lower = origin.lower()
|
||||
dest_lower = destination.lower()
|
||||
|
||||
# Australia/NZ cities
|
||||
anz_cities = [
|
||||
"sydney",
|
||||
"melbourne",
|
||||
"syd",
|
||||
"mel",
|
||||
"auckland",
|
||||
"akl",
|
||||
"wellington",
|
||||
"wlg",
|
||||
"brisbane",
|
||||
"bne",
|
||||
"perth",
|
||||
"per",
|
||||
]
|
||||
# US cities
|
||||
us_cities = [
|
||||
"los angeles",
|
||||
"lax",
|
||||
"san francisco",
|
||||
"sfo",
|
||||
"new york",
|
||||
"nyc",
|
||||
"jfk",
|
||||
"chicago",
|
||||
"ord",
|
||||
"miami",
|
||||
"mia",
|
||||
]
|
||||
|
||||
is_origin_anz = any(city in origin_lower for city in anz_cities)
|
||||
is_dest_anz = any(city in dest_lower for city in anz_cities)
|
||||
is_origin_us = any(city in origin_lower for city in us_cities)
|
||||
is_dest_us = any(city in dest_lower for city in us_cities)
|
||||
|
||||
# Determine airline pool and base price
|
||||
if (is_origin_us and is_dest_anz) or (is_origin_anz and is_dest_us):
|
||||
# Trans-Pacific routes
|
||||
airline_pool = airlines_by_region["international"]
|
||||
base_price = random.randint(1200, 1800)
|
||||
elif is_origin_anz and is_dest_anz:
|
||||
# Australia/NZ domestic
|
||||
airline_pool = airlines_by_region["australia_nz"]
|
||||
base_price = random.randint(300, 600)
|
||||
elif is_origin_us and is_dest_us:
|
||||
# US domestic
|
||||
airline_pool = airlines_by_region["domestic_us"]
|
||||
base_price = random.randint(200, 800)
|
||||
else:
|
||||
# General international
|
||||
airline_pool = airlines_by_region["international"]
|
||||
base_price = random.randint(800, 1500)
|
||||
|
||||
# Generate 3-4 flight options
|
||||
num_flights = random.randint(3, 4)
|
||||
results = []
|
||||
used_airlines = set()
|
||||
|
||||
for i in range(num_flights):
|
||||
# Pick unique airline
|
||||
available_airlines = [a for a in airline_pool if a["name"] not in used_airlines]
|
||||
if not available_airlines:
|
||||
available_airlines = airline_pool # Reset if we run out
|
||||
|
||||
airline = random.choice(available_airlines)
|
||||
used_airlines.add(airline["name"])
|
||||
|
||||
# Generate flight numbers
|
||||
outbound_num = random.randint(100, 999)
|
||||
return_num = random.randint(100, 999)
|
||||
|
||||
# Price variation (cheaper airlines get lower prices)
|
||||
price_multiplier = 1.0
|
||||
if "Southwest" in airline["name"] or "Jetstar" in airline["name"]:
|
||||
price_multiplier = 0.7
|
||||
elif "Virgin" in airline["name"]:
|
||||
price_multiplier = 0.85
|
||||
elif "Singapore" in airline["name"]:
|
||||
price_multiplier = 1.2
|
||||
|
||||
# Add some random variation
|
||||
price_variation = random.uniform(0.9, 1.1)
|
||||
final_price = round(base_price * price_multiplier * price_variation, 2)
|
||||
|
||||
results.append(
|
||||
{
|
||||
"operating_carrier": airline["name"],
|
||||
"outbound_flight_code": f"{airline['code']}{outbound_num}",
|
||||
"price": final_price,
|
||||
"return_flight_code": f"{airline['code']}{return_num}",
|
||||
"return_operating_carrier": airline["name"],
|
||||
}
|
||||
)
|
||||
|
||||
# Sort by price
|
||||
results.sort(key=lambda x: x["price"])
|
||||
return results
|
||||
|
||||
|
||||
def search_flights(args: dict) -> dict:
|
||||
"""
|
||||
Returns example flight search results in the requested JSON format.
|
||||
Search for flights. Uses real API if RAPIDAPI_KEY is available, otherwise generates smart mock data.
|
||||
"""
|
||||
load_dotenv(override=True)
|
||||
api_key = os.getenv("RAPIDAPI_KEY")
|
||||
|
||||
origin = args.get("origin")
|
||||
destination = args.get("destination")
|
||||
|
||||
if not origin or not destination:
|
||||
return {"error": "Both origin and destination are required"}
|
||||
|
||||
# If API key is available, use the real API
|
||||
if api_key and api_key != "YOUR_DEFAULT_KEY":
|
||||
return search_flights_real_api(args)
|
||||
|
||||
# Otherwise, generate smart mock data
|
||||
results = generate_smart_flights(origin, destination)
|
||||
|
||||
return {
|
||||
"currency": "USD",
|
||||
"destination": f"{destination}",
|
||||
"origin": f"{origin}",
|
||||
"results": [
|
||||
{
|
||||
"operating_carrier": "American Airlines",
|
||||
"outbound_flight_code": "AA203",
|
||||
"price": 1262.51,
|
||||
"return_flight_code": "AA202",
|
||||
"return_operating_carrier": "American Airlines",
|
||||
},
|
||||
{
|
||||
"operating_carrier": "Air New Zealand",
|
||||
"outbound_flight_code": "NZ488",
|
||||
"price": 1396.00,
|
||||
"return_flight_code": "NZ527",
|
||||
"return_operating_carrier": "Air New Zealand",
|
||||
},
|
||||
{
|
||||
"operating_carrier": "United Airlines",
|
||||
"outbound_flight_code": "UA100",
|
||||
"price": 1500.00,
|
||||
"return_flight_code": "UA101",
|
||||
"return_operating_carrier": "United Airlines",
|
||||
},
|
||||
{
|
||||
"operating_carrier": "Delta Airlines",
|
||||
"outbound_flight_code": "DL200",
|
||||
"price": 1600.00,
|
||||
"return_flight_code": "DL201",
|
||||
"return_operating_carrier": "Delta Airlines",
|
||||
},
|
||||
],
|
||||
"destination": destination,
|
||||
"origin": origin,
|
||||
"results": results,
|
||||
}
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
from typing import Dict, List
|
||||
|
||||
from models.tool_definitions import ToolArgument, ToolDefinition
|
||||
|
||||
# ----- System tools -----
|
||||
@@ -397,3 +399,75 @@ ecomm_track_package = ToolDefinition(
|
||||
),
|
||||
],
|
||||
)
|
||||
|
||||
|
||||
# ----- Food Ordering Use Case Tools -----
|
||||
food_add_to_cart_tool = ToolDefinition(
|
||||
name="AddToCart",
|
||||
description="Add a menu item to the customer's cart using item details from Stripe.",
|
||||
arguments=[
|
||||
ToolArgument(
|
||||
name="customer_email",
|
||||
type="string",
|
||||
description="Email address of the customer",
|
||||
),
|
||||
ToolArgument(
|
||||
name="item_name",
|
||||
type="string",
|
||||
description="Name of the menu item (e.g., 'Margherita Pizza', 'Caesar Salad')",
|
||||
),
|
||||
ToolArgument(
|
||||
name="item_price",
|
||||
type="number",
|
||||
description="Price of the item in dollars (e.g., 14.99)",
|
||||
),
|
||||
ToolArgument(
|
||||
name="quantity",
|
||||
type="number",
|
||||
description="Quantity of the item to add (defaults to 1)",
|
||||
),
|
||||
ToolArgument(
|
||||
name="stripe_product_id",
|
||||
type="string",
|
||||
description="Stripe product ID for reference (optional)",
|
||||
),
|
||||
],
|
||||
)
|
||||
|
||||
# MCP Integration Functions
|
||||
|
||||
|
||||
def create_mcp_tool_definitions(
|
||||
mcp_tools_info: Dict[str, Dict],
|
||||
) -> List[ToolDefinition]:
|
||||
"""Convert MCP tool info to ToolDefinition objects"""
|
||||
tool_definitions = []
|
||||
|
||||
for tool_name, tool_info in mcp_tools_info.items():
|
||||
# Extract input schema properties
|
||||
input_schema = tool_info.get("inputSchema", {})
|
||||
properties = (
|
||||
input_schema.get("properties", {}) if isinstance(input_schema, dict) else {}
|
||||
)
|
||||
|
||||
# Convert properties to ToolArgument objects
|
||||
arguments = []
|
||||
for param_name, param_info in properties.items():
|
||||
if isinstance(param_info, dict):
|
||||
arguments.append(
|
||||
ToolArgument(
|
||||
name=param_name,
|
||||
type=param_info.get("type", "string"),
|
||||
description=param_info.get("description", ""),
|
||||
)
|
||||
)
|
||||
|
||||
# Create ToolDefinition
|
||||
tool_def = ToolDefinition(
|
||||
name=tool_info["name"],
|
||||
description=tool_info.get("description", ""),
|
||||
arguments=arguments,
|
||||
)
|
||||
tool_definitions.append(tool_def)
|
||||
|
||||
return tool_definitions
|
||||
|
||||
Reference in New Issue
Block a user