Skip to content

Commit

Permalink
Merge pull request #19 from StreetLamb/pizza-example
Browse files Browse the repository at this point in the history
Pizza example
  • Loading branch information
StreetLamb authored Jan 16, 2025
2 parents 870c101 + daa5c3a commit f6b2b73
Show file tree
Hide file tree
Showing 9 changed files with 363 additions and 37 deletions.
76 changes: 39 additions & 37 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,41 @@ https://github.com/user-attachments/assets/d61b8893-3c33-4002-bca9-740f403f51f1
- 👁️ **Visiblity** - Track your agents’ past and current actions in real time through a user-friendly browser-based UI.
- 🌐 **Universal Deployment** - Deploy and run locally or on any cloud platform.

# Table of Contents

- [Table of Contents](#table-of-contents)
- [Install](#install)
- [Usage](#usage)
- [Overview](#overview)
- [Examples](#examples)
- [Understanding Rojak’s Architecture](#understanding-rojaks-architecture)
- [Running Rojak](#running-rojak)
- [Workers](#workers)
- [`rojak.run()`](#rojakrun)
- [Arguments](#arguments)
- [`OrchestratorResponse` Fields](#orchestratorresponse-fields)
- [Agents](#agents)
- [`Agent` Abstract Class Fields](#agent-abstract-class-fields)
- [Instructions](#instructions)
- [Functions](#functions)
- [Handoffs and Updating Context Variables](#handoffs-and-updating-context-variables)
- [Function Schemas](#function-schemas)
- [Retrievers](#retrievers)
- [Timeouts and Retries](#timeouts-and-retries)
- [Sessions](#sessions)
- [`rojak.create_session()`](#rojakcreate_session)
- [Arguments](#arguments-1)
- [`session.get_session()`](#sessionget_session)
- [Arguments](#arguments-2)
- [`session.send_messages()`](#sessionsend_messages)
- [Arguments](#arguments-3)
- [Other Session methods](#other-session-methods)
- [Schedules](#schedules)
- [`rojak.create_schedule()`](#rojakcreate_schedule)
- [Arguments](#arguments-4)
- [`rojak.list_scheduled_runs()`](#rojaklist_scheduled_runs)
- [Model Context Protocol (MCP) Servers](#model-context-protocol-mcp-servers)

## Install

Install the core Rojak library:
Expand Down Expand Up @@ -48,6 +83,7 @@ pip install rojak[qdrant-client]

## Usage

Start the Temporal development server.
```shell
temporal server start-dev
```
Expand Down Expand Up @@ -111,42 +147,7 @@ Ready to chat and assist,
What do you wish for?
```

## Table of Contents

- [Rojak](#rojak)
- [Features](#features)
- [Install](#install)
- [Usage](#usage)
- [Table of Contents](#table-of-contents)
- [Overview](#overview)
- [Examples](#examples)
- [Understanding Rojak’s Architecture](#understanding-rojaks-architecture)
- [Running Rojak](#running-rojak)
- [Workers](#workers)
- [`rojak.run()`](#rojakrun)
- [Arguments](#arguments)
- [`OrchestratorResponse` Fields](#orchestratorresponse-fields)
- [Agents](#agents)
- [`Agent` Abstract Class Fields](#agent-abstract-class-fields)
- [Instructions](#instructions)
- [Functions](#functions)
- [Handoffs and Updating Context Variables](#handoffs-and-updating-context-variables)
- [Function Schemas](#function-schemas)
- [Retrievers](#retrievers)
- [Timeouts and Retries](#timeouts-and-retries)
- [Sessions](#sessions)
- [`rojak.create_session()`](#rojakcreate_session)
- [Arguments](#arguments-1)
- [`session.get_session()`](#sessionget_session)
- [Arguments](#arguments-2)
- [`session.send_messages()`](#sessionsend_messages)
- [Arguments](#arguments-3)
- [Other Session methods](#other-session-methods)
- [Schedules](#schedules)
- [`rojak.create_schedule()`](#rojakcreate_schedule)
- [Arguments](#arguments-4)
- [`rojak.list_scheduled_runs()`](#rojaklist_scheduled_runs)
- [Model Context Protocol (MCP) Servers](#model-context-protocol-mcp-servers)
View this completed workflow at `http://localhost:8233`.

# Overview

Expand All @@ -162,7 +163,8 @@ Much like OpenAI’s Swarm, Rojak employs two key concepts:
Basic examples can be found in the `/examples` directory:

- [`weather`](examples/weather/): A straightforward example demonstrating tool calling and the use of `context_variables`.
- [`mcp_weather`](examples/mcp_weather/) An example demostrating connecting to MCP servers and executing tools through them.
- [`mcp_weather`](examples/mcp_weather/) An example demonstrating connecting to MCP servers and executing tools through them.
- [`pizza`](examples/pizza/) A complete example demonstrating using multiple agents to assist users in ordering food.


# Understanding Rojak’s Architecture
Expand Down
5 changes: 5 additions & 0 deletions examples/mcp_weather/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,11 @@ Make sure temporal is running:
temporal server start-dev
```

Ensure you have `OPENAI_API_KEY` in the .env file:
```
OPENAI_API_KEY=<YOUR_OPENAI_API_KEY>
```

Run the script:
```shell
python main.py
Expand Down
25 changes: 25 additions & 0 deletions examples/pizza/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
# Food ordering example

Demostrate orchestrating multiple agents in a food ordering workflow.

## Setup

Run Temporal locally:
```shell
temporal server start-dev
```

Ensure you have `OPENAI_API_KEY` in the .env file:
```
OPENAI_API_KEY=<YOUR_OPENAI_API_KEY>
```

Start the worker:
```shell
python run_worker.py
```

In another terminal, run the client:
```shell
python send_message.py
```
Empty file added examples/pizza/__init__.py
Empty file.
63 changes: 63 additions & 0 deletions examples/pizza/agents.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
from rojak.agents import OpenAIAgent
from rojak.types import RetryOptions, RetryPolicy


retry_options = RetryOptions(
retry_policy=RetryPolicy(non_retryable_error_types=["TypeError"])
)
triage_agent = OpenAIAgent(
name="Triage Agent",
instructions="""
You are the Triage Agent. Your role is to assist customers by identifying their needs and routing them to the correct agent:
- **Food Ordering** (`to_food_order`): For menu recommendations, adding/removing items, viewing or modifying the cart.
- **Payment** (`to_payment`): For payments, payment method queries, receipts, or payment issues.
- **Feedback** (`to_feedback`): For reviews, ratings, comments, or complaints.
If unsure, guide customers by explaining options (ordering, payment, feedback). For multi-step needs, start with the immediate priority and redirect after.
Always ensure clear, polite, and accurate communication during handoffs.
""",
functions=["to_food_order", "to_payment", "to_feedback"],
tool_choice="required",
retry_options=retry_options,
)

food_order_agent = OpenAIAgent(
name="Food Ordering Agent",
instructions={"type": "function", "name": "food_ordering_instructions"},
functions=[
"to_triage",
"add_to_cart",
"remove_from_cart",
"get_cart",
"get_menu",
],
retry_options=retry_options,
)


payment_agent = OpenAIAgent(
name="Payment Agent",
instructions="""
You are the Payment Agent. Your role is to securely and efficiently handle the payment process for customers.
Start by confirming the payment amount and presenting the available payment methods (e.g., credit card, mobile wallet, or cash).
Use the `process_payment` function to finalize the transaction and provide a receipt.
In case of a failed transaction, assist customers by suggesting alternative payment options or troubleshooting the issue.
Always maintain a courteous and professional tone, ensuring customers feel supported throughout the payment process.
Redirect customers to the Triage Agent if they need help with non-payment tasks.
""",
functions=["to_triage", "process_payment", "get_receipt"],
retry_options=retry_options,
)

feedback_agent = OpenAIAgent(
name="Feedback Agent",
instructions="""
You are the Feedback Agent. Your role is to collect and manage customer feedback to improve the overall experience.
Ask customers to rate their experience and provide detailed comments about the food, service, or satisfaction level.
Use the `provide_feedback` function to log their input.
If the customer has complaints, acknowledge them empathetically and offer to escalate the issue to the relevant team.
Encourage constructive feedback and thank customers for their time and insights.
Redirect customers to the Triage Agent if they wish to engage with other services after providing feedback.
""",
functions=["to_triage", "provide_feedback"],
retry_options=retry_options,
)
125 changes: 125 additions & 0 deletions examples/pizza/functions.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
from .agents import (
triage_agent,
food_order_agent,
payment_agent,
feedback_agent,
)
from copy import deepcopy


def food_ordering_instructions(context_variables):
return f"""
You are the Food Ordering Agent. Your role is to assist customers in selecting and managing their food orders.
- **Recommendations**: Start by retrieving the available menu using the `get_menu` function. Guide customers in choosing items based on their preferences or suggest popular options. Provide clear and concise descriptions of menu items.
- **Adding to Order**: When customers request to add items, confirm their selection against the menu. Use the `get_menu` function to ensure the item is available. If the item exists, proceed to add it using the `add_to_cart` function. For unavailable items, politely inform the customer and suggest alternatives from the menu.
- **Order Management**: Customers may also want to modify their order. Use the `get_cart` function to show the current order details and confirm requested changes. Use `remove_from_cart` to delete specific items as needed and provide an updated summary of the cart.
- **Guiding and Redirecting**: If customers are unsure of what they want, offer assistance by highlighting popular dishes or providing recommendations. Redirect customers to the Triage Agent if they need help beyond ordering or recommendations.
- **Tone and Accuracy**: Maintain a polite and professional tone, ensuring order details are accurate at every step. Always summarize the current state of the cart after any action.
Redirect customers to the Triage Agent if they need help with non-ordering tasks.
Remember: Customers can only order items listed in the menu. Validate all item requests before proceeding with any order changes.
Customer Preferences: {context_variables.get("preferences", "N/A")}
"""


def to_food_order():
"""Route to the food order agent."""
return food_order_agent


def to_payment():
"""Route to the payment agent."""
return payment_agent


def to_feedback():
"""Route to the feedback agent."""
return feedback_agent


def to_triage():
"""Route to the triage agent."""
return triage_agent


def get_menu():
"""Return a list of menu items for a pizza place."""
menu = [
{"name": "Margherita Pizza", "cost": 12.99},
{"name": "Pepperoni Pizza", "cost": 14.49},
{"name": "BBQ Chicken Pizza", "cost": 15.99},
{"name": "Veggie Supreme Pizza", "cost": 13.99},
{"name": "Meat Lovers Pizza", "cost": 16.99},
{"name": "Garlic Knots", "cost": 5.99},
{"name": "Cheesy Breadsticks", "cost": 6.49},
{"name": "Classic Caesar Salad", "cost": 9.49},
{"name": "Buffalo Wings (8 pieces)", "cost": 10.99},
{"name": "Mozzarella Sticks", "cost": 7.99},
{"name": "Chocolate Chip Cannoli", "cost": 4.99},
{"name": "Tiramisu Slice", "cost": 5.99},
{"name": "Fountain Soda", "cost": 2.49},
{"name": "Sparkling Lemonade", "cost": 3.49},
{"name": "Iced Tea", "cost": 2.99},
{"name": "Craft Beer (Pint)", "cost": 6.99},
{"name": "House Red Wine (Glass)", "cost": 7.99},
]
return menu


def add_to_cart(item: str, cost: int, quantity: int, context_variables: dict):
"""Add an item to the cart."""
if item in context_variables["cart"]:
context_variables["cart"][item]["quantity"] += quantity
else:
context_variables["cart"][item] = {"quantity": quantity, "unit_cost": cost}
return f"Added {quantity} of {item} to your cart."


def remove_from_cart(item: str, quantity: int, context_variables: dict):
"""Remove an item from the cart by quantity."""
if item in context_variables["cart"]:
if quantity >= context_variables["cart"][item]["quantity"]:
del context_variables["cart"][item]
return f"Removed {item} from your cart."
else:
context_variables["cart"][item]["quantity"] -= quantity
return f"Decreased quantity of {item} by {quantity}."
else:
return f"{item} is not in your cart."


def get_cart(context_variables: dict):
"""Return the contents of the cart."""
cart = context_variables.get("cart", {})
if cart:
cart_items = [
f"{item} (x{details['quantity']}) - ${details['quantity'] * details['unit_cost']:.2f}"
for item, details in cart.items()
]
return f"Your cart contains: {', '.join(cart_items)}"
else:
return "Your cart is empty."


def process_payment(context_variables: dict):
"""Process the payment and clear the cart."""
context_variables["receipt"] = deepcopy(context_variables["cart"])
context_variables["cart"] = {}
return "Payment processed successfully!"


def get_receipt(context_variables: dict):
"""Return the receipt."""
receipt = context_variables.get("receipt", {})
if receipt:
receipt_items = [
f"{item} (x{details['quantity']}) - ${details['quantity'] * details['unit_cost']:.2f}"
for item, details in receipt.items()
]
return f"Your receipt contains: {', '.join(receipt_items)}"
else:
return "No receipt available."


def provide_feedback():
"""Submit a review."""
return "Review submitted successfully!"
54 changes: 54 additions & 0 deletions examples/pizza/run_worker.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
# main.py
from temporalio.client import Client
from rojak import Rojak
from rojak.agents import OpenAIAgentActivities, OpenAIAgentOptions
import asyncio
from examples.pizza.functions import (
to_food_order,
to_payment,
to_feedback,
to_triage,
get_menu,
add_to_cart,
remove_from_cart,
get_cart,
process_payment,
get_receipt,
provide_feedback,
food_ordering_instructions,
)


async def main():
# Create client connected to server at the given address
client = await Client.connect("localhost:7233")

openai_activities = OpenAIAgentActivities(
OpenAIAgentOptions(
all_functions=[
to_food_order,
to_payment,
to_feedback,
to_triage,
get_menu,
add_to_cart,
remove_from_cart,
get_cart,
process_payment,
get_receipt,
provide_feedback,
food_ordering_instructions,
]
)
)

rojak = Rojak(client, task_queue="tasks")
worker = await rojak.create_worker([openai_activities])
await worker.run()


if __name__ == "__main__":
print("Starting worker")
print("Then run 'python send_messages.py' to start sending messages.")

asyncio.run(main())
Loading

0 comments on commit f6b2b73

Please sign in to comment.