558 lines
26 KiB
Plaintext
558 lines
26 KiB
Plaintext
{
|
||
"cells": [
|
||
{
|
||
"attachments": {},
|
||
"cell_type": "markdown",
|
||
"id": "ae1ef804-3504-488d-af86-5a0da36fea78",
|
||
"metadata": {},
|
||
"source": [
|
||
"# ☀️🏃♀️ WeatherMate\n",
|
||
"----\n",
|
||
"\n",
|
||
"**WeatherMate** is a conversational **AI agent** that analyzes real-time weather conditions and suggests the best activities and events based on location. Whether it's sunny, rainy, or snowy, WeatherMate helps you make the most of your day! \n",
|
||
"\n",
|
||
"Here's how it works:\n",
|
||
"1. Get current weather conditions for the user's location.\n",
|
||
"2. Recommend suitable indoor or outdoor activities based on the weather.\n",
|
||
"3. Find relevant events using the Ticketmaster API.\n",
|
||
"4. Merge both activity suggestions and events into a single, structured response.\n",
|
||
"\n",
|
||
"---\n",
|
||
"\n",
|
||
"Large Language Models (LLMs), by themselves, cannot fetch real-time data such as weather information. To enable LLMs to access and use such real-time data, we integrate **external tools.** \n",
|
||
"\n",
|
||
"In this notebook, we will implement a weather API, allowing the assistant to fetch real-time weather information and use it for personalized activity suggestions based on current weather conditions. This is an essential step in transforming an LLM into a more interactive and data-driven AI assistant.\n",
|
||
"\n",
|
||
"\n",
|
||
"In this notebook, we will develop a conversational AI Agent that helps users receive personalized activity recommendations based on real-time weather data.\n",
|
||
"\n",
|
||
"- 🧑💻 Skill Level: Advanced\n",
|
||
"- 📤 Output Format: conversational chat\n",
|
||
"- 🚀 Tools:\n",
|
||
" - Weather API integration \n",
|
||
" - Ticketmaster API\n",
|
||
" - OpenAI with external tool handling\n",
|
||
" - Gradio for the UI\n",
|
||
"\n",
|
||
"🛠️ Requirements\n",
|
||
"- ⚙️ Hardware: ✅ CPU is sufficient — no GPU required\n",
|
||
"- 🔑 OpenAI API Key\n",
|
||
"- 🔑 Weather API integration (https://www.weatherapi.com)\n",
|
||
"- 🔑 Ticketmaster API (https://developer.ticketmaster.com/explore/)\n",
|
||
"\n",
|
||
"⚙️ Customizable by user\n",
|
||
"- 🤖 Selected model\n",
|
||
"- 📜 system_prompt: Controls model behavior\n",
|
||
"\n",
|
||
"---\n",
|
||
"📢 Find more LLM notebooks on my [GitHub repository](https://github.com/lisekarimi/lexo)"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"id": "ad262788",
|
||
"metadata": {},
|
||
"source": [
|
||
"**Class Diagram**\n",
|
||
"\n",
|
||
"\n"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": null,
|
||
"id": "d6b7a492-f510-4ba4-bbc3-239675d389dd",
|
||
"metadata": {},
|
||
"outputs": [],
|
||
"source": [
|
||
"# imports\n",
|
||
"\n",
|
||
"import os\n",
|
||
"import json\n",
|
||
"import requests\n",
|
||
"from dotenv import load_dotenv\n",
|
||
"from openai import OpenAI\n",
|
||
"import gradio as gr\n",
|
||
"from datetime import datetime\n",
|
||
"\n",
|
||
"# Initialization\n",
|
||
"\n",
|
||
"load_dotenv(override=True)\n",
|
||
"\n",
|
||
"openai_api_key = os.getenv('OPENAI_API_KEY')\n",
|
||
"if not openai_api_key:\n",
|
||
" print(\"❌ OpenAI API Key is missing!\")\n",
|
||
"\n",
|
||
"weather_api_key = os.getenv('WEATHERAPI_KEY')\n",
|
||
"if not weather_api_key:\n",
|
||
" print(\"❌ Weather API Key is missing!\")\n",
|
||
"\n",
|
||
"ticketmaster_api_key = os.getenv('TICKETMASTER_KEY')\n",
|
||
"if not ticketmaster_api_key:\n",
|
||
" print(\"❌ TicketMaster API Key is missing!\")\n",
|
||
"\n",
|
||
"\n",
|
||
"MODEL = \"gpt-4o-mini\"\n",
|
||
"openai = OpenAI()"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": null,
|
||
"id": "347dbe00-5826-4aa6-9d2c-9d028fc33ec8",
|
||
"metadata": {},
|
||
"outputs": [],
|
||
"source": [
|
||
"# Get today's date and day name\n",
|
||
"today_str = datetime.today().strftime('%Y-%m-%d')\n",
|
||
"day_name = datetime.today().strftime('%A')\n",
|
||
"\n",
|
||
"nb_activity = 10\n",
|
||
"\n",
|
||
"\n",
|
||
"system_message = f\"\"\"\n",
|
||
"You are a fun and helpful assistant for an Activity Suggestion App.\n",
|
||
"Your job is to recommend **up to {nb_activity} activities** based on the real-time weather fetched from the API, ensuring a mix of **indoor, outdoor, and event-based activities** whenever possible.\n",
|
||
"\n",
|
||
"The total must always be **10 or fewer**, following this rule:\n",
|
||
"**nb_events + nb_indoors + nb_outdoors ≤ 10**.\n",
|
||
"\n",
|
||
"You must **analyze and think carefully** to determine the best combination of activities and events for the user.\n",
|
||
"- Evaluate **weather conditions** to decide if outdoor activities are suitable.\n",
|
||
"- Check **event availability** and select the most relevant ones.\n",
|
||
"- Balance **indoor, outdoor, and event-based activities** dynamically to provide the best experience.\n",
|
||
"\n",
|
||
"If one of these categories is unavailable, that's fine—just provide the best possible suggestions without exceeding **10 activities**.\n",
|
||
"Deliver everything **in one go—no waiting!**\n",
|
||
"\n",
|
||
"\n",
|
||
"### **Understanding Relative Dates**\n",
|
||
"- Always interpret relative dates based on **{today_str} ({day_name})**.\n",
|
||
"- The weekend always refers to Saturday and Sunday.\n",
|
||
"- \"Next {day_name}\" should refer to the **closest upcoming occurrence** of that day.\n",
|
||
"- If the user asks for a time range (e.g., \"the next 3 days\"), calculate the **exact date range** starting from today.\n",
|
||
"- If no specific date is mentioned, **assume today by default**.\n",
|
||
"- **Do not ask for confirmation** when interpreting dates—just assume the correct date and proceed confidently unless there's real ambiguity.\n",
|
||
"\n",
|
||
"### **Activity and Event Suggestion Process**\n",
|
||
"To provide the best {nb_activity} activity recommendations, follow these steps:\n",
|
||
"Step 1: Retrieve Weather Data – Use the Weather API to get current conditions for the user's location.\n",
|
||
"Step 2: Suggest Activities – Recommend suitable indoor or outdoor activities based on the weather.\n",
|
||
"Step 3: Fetch Events (if available) – Use the Ticketmaster API to find relevant events in the user’s area.\n",
|
||
"Step 4: Combine Everything – Merge both event listings and activity suggestions into a single, well-structured response.\n",
|
||
"This entire process should be done seamlessly in one go without making the user wait.\n",
|
||
"\n",
|
||
"### **How to Handle Each API**\n",
|
||
"- **Weather API Handling**:\n",
|
||
" - If the user requests a relative date (e.g., \"tomorrow,\" \"next Monday\"), calculate the number of days from today.\n",
|
||
" - Provide the weather forecast only for the requested date, ignoring any other days in the response.\n",
|
||
" - If no weather data is available, inform the user in a friendly, light-hearted way.\n",
|
||
" - The forecast is limited to 14 days, so if the user requests a longer period, politely let him know.\n",
|
||
"\n",
|
||
"- **Ticketmaster API Handling**:\n",
|
||
" - If the user asks for events today, set the start date as today’s date.\n",
|
||
" - If the user asks for any specific weekday, find the next occurrence of that day and use it as the start date.\n",
|
||
" - If the user asks for a range of days (e.g., \"the next 3 days\"), use today’s date as the start date.\n",
|
||
" - The country corresponding to the user's city must be represented using the ISO Alpha-2 Code (e.g., FR for France, US for the United States, CA for Canada, DK for Denmark).\n",
|
||
" - If more than 5 events are found, ask the user for their interests to refine the search, using a one-word keyword like 'music,' 'cinema,' or 'theater.'\n",
|
||
" - If no events are found, explicitly inform the user in a friendly, funny way.\n",
|
||
" - Do not mention Ticketmaster unless necessary; simply state that you are checking for events.\n",
|
||
"\n",
|
||
"### **User Interaction Rules**\n",
|
||
"- If the user **doesn’t mention a city**, **ask them to provide one**.\n",
|
||
"- If an event search fails, do **not** mention Ticketmaster; simply say that no events were found.\n",
|
||
"- Ensure all activity suggestions are provided **in one response**, combining weather-based activities and event suggestions.\n",
|
||
"\n",
|
||
"\n",
|
||
"### **Event Formatting in Output**\n",
|
||
"**If Ticketmaster events are available**, format the output as follows:\n",
|
||
"Here are some events that may interest you:\n",
|
||
"**Event Name**:\n",
|
||
"- 📅 Date: Give the date like 19th March 2025\n",
|
||
"- 📍 Venue:\n",
|
||
"- 🔗 Ticket Link: Put the URL here\n",
|
||
"\n",
|
||
"(And don't forget to separate these gems with a snazzy divider)\n",
|
||
"\n",
|
||
"**Event Name**:\n",
|
||
"- 📅 Date: Give the date like 19th March 2025\n",
|
||
"- 📍 Venue:\n",
|
||
"- 🔗 Ticket Link: Put the URL here\n",
|
||
"\n",
|
||
"(Another divider, because we like to keep things fresh!)\n",
|
||
"\n",
|
||
"**Event Name**:\n",
|
||
"- 📅 Date: Give the date like 19th March 2025\n",
|
||
"- 📍 Venue:\n",
|
||
"- 🔗 Ticket Link: Put the URL here\n",
|
||
"\n",
|
||
"### **Tone and Style**\n",
|
||
"**Keep it short, fun, and don’t forget to add a dash of humor!**\n",
|
||
"Your job is to keep the user smiling while giving them the **best activities for the day**.\n",
|
||
"Be **accurate and concise**, but let’s keep it **light and lively!** 🎉\n",
|
||
"\"\"\"\n"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": null,
|
||
"id": "578da33d-be38-4c75-8a96-9d6bfc1af99b",
|
||
"metadata": {},
|
||
"outputs": [],
|
||
"source": [
|
||
"class WeatherAPI:\n",
|
||
" def get_weather(self, city: str, days: int) -> dict:\n",
|
||
" \"\"\"Fetches weather data for the given city for the next 'days' number of days.\"\"\"\n",
|
||
" url = \"https://api.weatherapi.com/v1/forecast.json\"\n",
|
||
" params = {\"key\": weather_api_key, \"q\": city, \"days\": days}\n",
|
||
" # print(f\"params weather: {params}\")\n",
|
||
" response = requests.get(url, params=params)\n",
|
||
"\n",
|
||
" if response.status_code == 200:\n",
|
||
" data = response.json()\n",
|
||
" forecast = []\n",
|
||
" for day in data[\"forecast\"][\"forecastday\"]:\n",
|
||
" forecast.append({\n",
|
||
" \"date\": day[\"date\"],\n",
|
||
" \"temp\": day[\"day\"][\"avgtemp_c\"]\n",
|
||
" })\n",
|
||
"\n",
|
||
" result = {\n",
|
||
" \"city\": city,\n",
|
||
" \"forecast\": forecast\n",
|
||
" }\n",
|
||
" return result\n",
|
||
" else:\n",
|
||
" return {\"error\": f\"City '{city}' not found or other issue. Please check the city name and try again.\"}"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": null,
|
||
"id": "305f9f18-8556-4b49-9f6b-4a2233eefae9",
|
||
"metadata": {},
|
||
"outputs": [],
|
||
"source": [
|
||
"from abc import ABC, abstractmethod\n",
|
||
"\n",
|
||
"class BaseEventAPI(ABC):\n",
|
||
" @abstractmethod\n",
|
||
" def get_events(self, city, country_code, keywords, size):\n",
|
||
" \"\"\"Fetches upcoming events from an event provider.\"\"\"\n",
|
||
" pass # Subclasses must implement this method\n",
|
||
"\n",
|
||
"class TicketmasterAPI(BaseEventAPI):\n",
|
||
" def get_events(self, city, country_code, keywords, start_date):\n",
|
||
" \"\"\"Fetches upcoming events from Ticketmaster for a given city.\"\"\"\n",
|
||
" url = \"https://app.ticketmaster.com/discovery/v2/events.json\"\n",
|
||
" params = {\n",
|
||
" \"apikey\": ticketmaster_api_key,\n",
|
||
" \"city\": city,\n",
|
||
" \"countryCode\": country_code,\n",
|
||
" \"keyword\": \",\".join(keywords),\n",
|
||
" \"size\": 10,\n",
|
||
" \"startDateTime\": start_date\n",
|
||
" }\n",
|
||
"\n",
|
||
" response = requests.get(url, params=params)\n",
|
||
"\n",
|
||
" if response.status_code == 200:\n",
|
||
" data = response.json()\n",
|
||
" events = data.get(\"_embedded\", {}).get(\"events\", [])\n",
|
||
" return [\n",
|
||
" {\n",
|
||
" \"name\": event[\"name\"],\n",
|
||
" \"date\": event[\"dates\"][\"start\"][\"localDate\"],\n",
|
||
" \"venue\": event[\"_embedded\"][\"venues\"][0][\"name\"],\n",
|
||
" \"url\": event.get(\"url\", \"N/A\") # Using .get() to avoid KeyError\n",
|
||
" }\n",
|
||
" for event in events\n",
|
||
" ] if events else []\n",
|
||
" else:\n",
|
||
" return {\"error\": f\"API request failed! Status: {response.status_code}, Response: {response.text}\"}\n"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": null,
|
||
"id": "4c60820f-4e9f-4851-8330-52c8fd676259",
|
||
"metadata": {},
|
||
"outputs": [],
|
||
"source": [
|
||
"class ChatAssistant:\n",
|
||
" def __init__(self):\n",
|
||
" self.model = MODEL\n",
|
||
" self.tools = [\n",
|
||
" {\n",
|
||
" \"type\": \"function\",\n",
|
||
" \"function\": {\n",
|
||
" \"name\": \"get_weather\",\n",
|
||
" \"description\": \"Get the current weather and forecast for the destination city.\",\n",
|
||
" \"parameters\": {\n",
|
||
" \"type\": \"object\",\n",
|
||
" \"properties\": {\n",
|
||
" \"city\": {\n",
|
||
" \"type\": \"string\",\n",
|
||
" \"description\": \"The city for which the weather is being requested.\"\n",
|
||
" },\n",
|
||
" \"days\": {\n",
|
||
" \"type\": \"integer\",\n",
|
||
" \"description\": \"The number of days for the weather forecast (can be 1, 2, 6, or 10).\"\n",
|
||
" }\n",
|
||
" },\n",
|
||
" \"required\": [\"city\", \"days\"],\n",
|
||
" \"additionalProperties\": False\n",
|
||
" }\n",
|
||
" }\n",
|
||
" },\n",
|
||
" {\n",
|
||
" \"type\": \"function\",\n",
|
||
" \"function\": {\n",
|
||
" \"name\": \"get_ticketmaster_events\",\n",
|
||
" \"description\": \"Fetch upcoming events from Ticketmaster.\",\n",
|
||
" \"parameters\": {\n",
|
||
" \"type\": \"object\",\n",
|
||
" \"properties\": {\n",
|
||
" \"city\": {\n",
|
||
" \"type\": \"string\",\n",
|
||
" \"description\": \"City where the events are searched.\"\n",
|
||
" },\n",
|
||
" \"country_code\": {\n",
|
||
" \"type\": \"string\",\n",
|
||
" \"description\": \"Country code for filtering results.\"\n",
|
||
" },\n",
|
||
" \"keywords\": {\n",
|
||
" \"type\": \"array\",\n",
|
||
" \"items\": {\n",
|
||
" \"type\": \"string\"\n",
|
||
" },\n",
|
||
" \"description\": \"Optional keywords for event search (e.g., 'music', 'concert').\"\n",
|
||
" },\n",
|
||
" \"size\": {\n",
|
||
" \"type\": \"integer\",\n",
|
||
" \"description\": \"Number of events to fetch.\"\n",
|
||
" },\n",
|
||
" \"start_date\": {\n",
|
||
" \"type\": \"string\",\n",
|
||
" \"description\": \"Start date for the event search.\"\n",
|
||
" }\n",
|
||
" },\n",
|
||
" \"required\": [\"city\", \"country_code\", \"size\", \"start_date\"],\n",
|
||
" \"additionalProperties\": False\n",
|
||
" }\n",
|
||
" }\n",
|
||
" }\n",
|
||
" ]\n",
|
||
"\n",
|
||
" def chat(self, user_message, history, weather_api, event_apis):\n",
|
||
" # Build the conversation\n",
|
||
" messages = [{\"role\": \"system\", \"content\": system_message}] + history + [{\"role\": \"user\", \"content\": user_message}]\n",
|
||
"\n",
|
||
" # OpenAI response\n",
|
||
" response = openai.chat.completions.create(model=self.model, messages=messages, tools=self.tools, stream=True)\n",
|
||
"\n",
|
||
" recovered_pieces = {\n",
|
||
" \"content\": None,\n",
|
||
" \"role\": \"assistant\",\n",
|
||
" \"tool_calls\": {}\n",
|
||
" }\n",
|
||
" last_tool_calls = {}\n",
|
||
" has_tool_call = False\n",
|
||
" result = \"\" # Initialize result accumulator\n",
|
||
" # previous_index = None # Track the last processed index\n",
|
||
"\n",
|
||
" for chunk in response:\n",
|
||
" delta = chunk.choices[0].delta\n",
|
||
" finish_reason = chunk.choices[0].finish_reason\n",
|
||
"\n",
|
||
" # Handle tool call detection\n",
|
||
" if delta.tool_calls and finish_reason in [None, \"tool_calls\"]:\n",
|
||
" has_tool_call = True\n",
|
||
" piece = delta.tool_calls[0] # Get the first piece in the tool call\n",
|
||
"\n",
|
||
" # Create a dictionary for the tool call if it doesn't exist yet\n",
|
||
" recovered_pieces[\"tool_calls\"][piece.index] = recovered_pieces[\"tool_calls\"].get(\n",
|
||
" piece.index, {\"id\": None, \"function\": {\"arguments\": \"\", \"name\": \"\"}, \"type\": \"function\"}\n",
|
||
" )\n",
|
||
"\n",
|
||
" if piece.id:\n",
|
||
" recovered_pieces[\"tool_calls\"][piece.index][\"id\"] = piece.id\n",
|
||
" if piece.function.name:\n",
|
||
" recovered_pieces[\"tool_calls\"][piece.index][\"function\"][\"name\"] = piece.function.name\n",
|
||
" recovered_pieces[\"tool_calls\"][piece.index][\"function\"][\"arguments\"] += piece.function.arguments\n",
|
||
"\n",
|
||
" # Store the tool call in the dictionary by index\n",
|
||
" last_tool_calls[piece.index] = recovered_pieces[\"tool_calls\"][piece.index]\n",
|
||
"\n",
|
||
" # Store content in result and yield\n",
|
||
" else:\n",
|
||
" result += delta.content or \"\"\n",
|
||
" if result.strip():\n",
|
||
" yield result\n",
|
||
"\n",
|
||
"\n",
|
||
" # Handle tool call scenario\n",
|
||
" if has_tool_call:\n",
|
||
" # Handle the tool calls\n",
|
||
" response = self.handle_tool_call(last_tool_calls, weather_api, event_apis)\n",
|
||
"\n",
|
||
" if response: # Only iterate if response is not None\n",
|
||
" tool_calls_list = [tool_call for tool_call in last_tool_calls.values()]\n",
|
||
" messages.append({\"role\": \"assistant\", \"tool_calls\": tool_calls_list}) # Append the tool calls to the messages\n",
|
||
"\n",
|
||
" # Dynamically process each tool call response and append it to the message history\n",
|
||
" for res in response:\n",
|
||
" messages.append({\n",
|
||
" \"role\": \"tool\",\n",
|
||
" \"tool_call_id\": res[\"tool_call_id\"],\n",
|
||
" \"content\": json.dumps(res[\"content\"])\n",
|
||
" })\n",
|
||
"\n",
|
||
" # New OpenAI request with tool response\n",
|
||
" response = openai.chat.completions.create(model=self.model, messages=messages, stream=True)\n",
|
||
"\n",
|
||
" result = \"\" # Reset result before second stream\n",
|
||
" for chunk in response:\n",
|
||
" result += chunk.choices[0].delta.content or \"\"\n",
|
||
" if result.strip():\n",
|
||
" yield result\n",
|
||
"\n",
|
||
"\n",
|
||
" def handle_tool_call(self, tool_call, weather_api, event_apis):\n",
|
||
" stored_values = {} # Dictionary to store the valid value for each field\n",
|
||
"\n",
|
||
" for index, call in tool_call.items():\n",
|
||
" # Load the arguments for each tool call dynamically\n",
|
||
" arguments = json.loads(call[\"function\"][\"arguments\"])\n",
|
||
"\n",
|
||
" # Iterate over all keys dynamically\n",
|
||
" for key, value in arguments.items():\n",
|
||
" # Update the field if it's currently None or hasn't been set before\n",
|
||
" if key not in stored_values or stored_values[key] is None:\n",
|
||
" stored_values[key] = value\n",
|
||
"\n",
|
||
" city = stored_values.get('city')\n",
|
||
" days = stored_values.get('days')\n",
|
||
" country_code = stored_values.get('country_code')\n",
|
||
" keywords = stored_values.get('keywords', [])\n",
|
||
" # size = stored_values.get('size')\n",
|
||
" start_date = stored_values.get('start_date')\n",
|
||
" start_date = str(start_date) + \"T00:00:00Z\"\n",
|
||
"\n",
|
||
" weather_data = None\n",
|
||
" event_data = None\n",
|
||
"\n",
|
||
" # Iteration over tool_call\n",
|
||
" for call in tool_call.values():\n",
|
||
" if call[\"function\"][\"name\"] == \"get_weather\":\n",
|
||
" weather_data = weather_api.get_weather(city, days)\n",
|
||
"\n",
|
||
" if call[\"function\"][\"name\"] == \"get_ticketmaster_events\":\n",
|
||
" event_data = event_apis[\"ticketmaster\"].get_events(city, country_code, keywords, start_date)\n",
|
||
"\n",
|
||
" responses = []\n",
|
||
"\n",
|
||
" # Ensure weather response is always included\n",
|
||
" weather_tool_call_id = next((call[\"id\"] for call in tool_call.values() if call[\"function\"][\"name\"] == \"get_weather\"), None)\n",
|
||
" if weather_data and \"forecast\" in weather_data:\n",
|
||
" responses.append({\n",
|
||
" \"role\": \"assistant\",\n",
|
||
" \"content\": {\"weather\": weather_data[\"forecast\"]},\n",
|
||
" \"tool_call_id\": weather_tool_call_id\n",
|
||
" })\n",
|
||
" elif weather_tool_call_id:\n",
|
||
" responses.append({\n",
|
||
" \"role\": \"assistant\",\n",
|
||
" \"content\": {\"message\": \"No weather data available for this location.\"},\n",
|
||
" \"tool_call_id\": weather_tool_call_id\n",
|
||
" })\n",
|
||
"\n",
|
||
" # Ensure event response is always included\n",
|
||
" event_tool_call_id = next((call[\"id\"] for call in tool_call.values() if call[\"function\"][\"name\"] == \"get_ticketmaster_events\"), None)\n",
|
||
" if event_data:\n",
|
||
" responses.append({\n",
|
||
" \"role\": \"assistant\",\n",
|
||
" \"content\": {\"events\": event_data},\n",
|
||
" \"tool_call_id\": event_tool_call_id\n",
|
||
" })\n",
|
||
" elif event_tool_call_id:\n",
|
||
" responses.append({\n",
|
||
" \"role\": \"assistant\",\n",
|
||
" \"content\": {\"message\": \"No events found for this location.\"},\n",
|
||
" \"tool_call_id\": event_tool_call_id\n",
|
||
" })\n",
|
||
"\n",
|
||
" # print(\"Final responses:\", responses)\n",
|
||
" return responses\n"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": null,
|
||
"id": "191a3a9e-95e1-4ca6-8992-4a5bafb9b8ff",
|
||
"metadata": {},
|
||
"outputs": [],
|
||
"source": [
|
||
"# GradioInterface class to handle the Gradio UI\n",
|
||
"class GradioInterface:\n",
|
||
" def __init__(self, activity_assistant):\n",
|
||
" self.activity_assistant = activity_assistant\n",
|
||
"\n",
|
||
" def launch(self):\n",
|
||
" # Gradio chat interface\n",
|
||
" gr.ChatInterface(fn=self.activity_assistant.chat, type=\"messages\").launch()\n",
|
||
"\n",
|
||
"# ActivityAssistant setup\n",
|
||
"class ActivityAssistant:\n",
|
||
" def __init__(self):\n",
|
||
" self.weather_api = WeatherAPI() # Interact with the Weather API\n",
|
||
" self.event_apis = { # Interact with the Events API\n",
|
||
" \"ticketmaster\": TicketmasterAPI()\n",
|
||
" }\n",
|
||
" self.chat_assistant = ChatAssistant() # This will handle conversation with OpenAI\n",
|
||
"\n",
|
||
" def chat(self, user_message, history):\n",
|
||
" # Forward the user message and conversation history to ChatAssistant\n",
|
||
" response_stream = self.chat_assistant.chat(user_message, history, self.weather_api, self.event_apis)\n",
|
||
" for chunk in response_stream:\n",
|
||
" yield chunk"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": null,
|
||
"id": "0b501e8e-2e10-4ab7-b523-1d4b8ad358e8",
|
||
"metadata": {},
|
||
"outputs": [],
|
||
"source": [
|
||
"# Main execution\n",
|
||
"if __name__ == \"__main__\":\n",
|
||
" activity_assistant = ActivityAssistant()\n",
|
||
" gradio_interface = GradioInterface(activity_assistant)\n",
|
||
" gradio_interface.launch()"
|
||
]
|
||
}
|
||
],
|
||
"metadata": {
|
||
"kernelspec": {
|
||
"display_name": ".venv",
|
||
"language": "python",
|
||
"name": "python3"
|
||
},
|
||
"language_info": {
|
||
"codemirror_mode": {
|
||
"name": "ipython",
|
||
"version": 3
|
||
},
|
||
"file_extension": ".py",
|
||
"mimetype": "text/x-python",
|
||
"name": "python",
|
||
"nbconvert_exporter": "python",
|
||
"pygments_lexer": "ipython3",
|
||
"version": "3.11.7"
|
||
}
|
||
},
|
||
"nbformat": 4,
|
||
"nbformat_minor": 5
|
||
}
|