from opperai import Opper, trace
from pydantic import BaseModel
from typing import Literal
import os
opper = Opper(api_key=os.getenv("OPPER_API_KEY"))
orders = {
123123: {
"email": "santa@clau.se",
"status": "delivering",
"created_date": "2024-11-03",
"updated_date": "2024-11-06",
"adress": "Snowy Mountain 123, 421 23, Greenland",
"purchase": "Large sled 1999 SEK"
}
}
class IntentClassification(BaseModel):
thoughts: str
intent: Literal["get_order_status", "query_products", "unsupported"]
def determine_intent(messages):
intent, _ = opper.call(
name="determine_intent",
instructions="Analyze the user message and determine their intent. Supported intents are get_order_status and query products.",
input={"messages": messages},
output_type=IntentClassification
)
return intent
class ParsedOrder(BaseModel):
thoughts: str
order_id: int | None = None
email: str | None = None
def extract_order_from_messages(messages):
order_info, _ = opper.call(
name="extract_order_info",
instructions="Extract order ID and email from the conversation if present",
input={"messages": messages},
output_type=ParsedOrder
)
return order_info
def get_order(id, email):
if id in orders and orders[id]["email"] == email:
return orders[id]
else:
return None
def process_message(messages):
intent = determine_intent(messages)
if intent.intent == "get_order_status":
order_request = extract_order_from_messages(messages)
if not order_request.order_id or not order_request.email:
return f"Need {'order ID and email' if not order_request.order_id and not order_request.email else 'order ID' if not order_request.order_id else 'email'}"
order = get_order(id=order_request.order_id, email=order_request.email)
if order:
return {
"order_id": order_request.order_id,
"status": orders[order_request.order_id]["status"],
"email": orders[order_request.order_id]["email"],
"address": orders[order_request.order_id]["adress"]
}
else:
return f"Could not find an order with id {order_request.order_id}"
elif intent.intent == "unsupported":
return f"Request is currently not supported: {messages[-1]}"
else:
return None
def bake_response(messages):
response, _ = opper.call(
name="generate_response",
instructions="Generate a helpful, friendly but brief response to the user's message in the conversation.",
input={"messages": messages},
output_type=str,
)
return response
@trace
def run():
messages = []
while True:
with opper.traces.start(name="on_message") as message_trace:
user_input = input("User: ")
if user_input.lower() == "quit":
break
messages.append({
"role": "user",
"content": user_input
})
analysis = process_message(messages)
messages.append({
"role": "function",
"content": analysis
})
response = bake_response(messages)
print(f"Assistant: {response}")
messages.append({
"role": "assistant",
"content": response
})
message_trace.update(input = user_input, output = response)
if __name__ == "__main__":
run()