Merge branch 'main' of github.com:ed-donner/llm_engineering

This commit is contained in:
Edward Donner
2025-07-12 15:26:11 -04:00
51 changed files with 17066 additions and 0 deletions

View File

@@ -0,0 +1,143 @@
{
"cells": [
{
"cell_type": "markdown",
"id": "d006b2ea-9dfe-49c7-88a9-a5a0775185fd",
"metadata": {},
"source": [
"# Additional End of week Exercise - week 2\n",
"\n",
"Now use everything you've learned from Week 2 to build a full prototype for the technical question/answerer you built in Week 1 Exercise.\n",
"\n",
"This should include a Gradio UI, streaming, use of the system prompt to add expertise, and the ability to switch between models. Bonus points if you can demonstrate use of a tool!\n",
"\n",
"If you feel bold, see if you can add audio input so you can talk to it, and have it respond with audio. ChatGPT or Claude can help you, or email me if you have questions.\n",
"\n",
"I will publish a full solution here soon - unless someone beats me to it...\n",
"\n",
"There are so many commercial applications for this, from a language tutor, to a company onboarding solution, to a companion AI to a course (like this one!) I can't wait to see your results."
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "a07e7793-b8f5-44f4-aded-5562f633271a",
"metadata": {},
"outputs": [],
"source": [
"# Agent that can listen for audio and convert it to text"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "da58ed0f-f781-4c51-8e5d-fdb05db98c8c",
"metadata": {},
"outputs": [],
"source": [
"import os\n",
"import gradio as gr\n",
"import google.generativeai as genai\n",
"from dotenv import load_dotenv\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "078cf34a-881e-44f4-9947-c45d7fe992a3",
"metadata": {},
"outputs": [],
"source": [
"load_dotenv()\n",
"\n",
"google_api_key = os.getenv('GOOGLE_API_KEY')\n",
"if google_api_key:\n",
" print(f\"Google API Key exists and begins {google_api_key[:8]}\")\n",
"else:\n",
" print(\"Google API Key not set\")\n",
"\n",
"genai.configure(api_key=google_api_key)\n",
"model = genai.GenerativeModel(\"gemini-2.0-flash\")"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "f77228ea-d0e1-4434-9191-555a6d680625",
"metadata": {},
"outputs": [],
"source": [
"def transcribe_translate_with_gemini(audio_file_path):\n",
" if not audio_file_path:\n",
" return \"⚠️ No audio file received.\"\n",
"\n",
" prompt = (\n",
" \"You're an AI that listens to a voice message in any language and returns the English transcription. \"\n",
" \"Please transcribe and translate the following audio to English. If already in English, just transcribe it.\"\n",
" )\n",
"\n",
" uploaded_file = genai.upload_file(audio_file_path)\n",
"\n",
" # 🔁 Send prompt + uploaded audio reference to Gemini\n",
" response = model.generate_content(\n",
" contents=[\n",
" {\n",
" \"role\": \"user\",\n",
" \"parts\": [\n",
" {\"text\": prompt},\n",
" uploaded_file \n",
" ]\n",
" }\n",
" ]\n",
" )\n",
"\n",
" return response.text.strip()"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "eb6c6d1e-1be3-404d-83f3-fc0855dc9f67",
"metadata": {},
"outputs": [],
"source": [
"gr.Interface(\n",
" fn=transcribe_translate_with_gemini,\n",
" inputs=gr.Audio(label=\"Record voice\", type=\"filepath\"),\n",
" outputs=\"text\",\n",
" title=\"🎙️ Voice-to-English Translator (Gemini Only)\",\n",
" description=\"Speak in any language and get the English transcription using Gemini multimodal API.\"\n",
").launch()\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "8b105082-e388-44bc-9617-1a81f38e2f3f",
"metadata": {},
"outputs": [],
"source": []
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3 (ipykernel)",
"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.13"
}
},
"nbformat": 4,
"nbformat_minor": 5
}

View File

@@ -0,0 +1,654 @@
{
"cells": [
{
"cell_type": "markdown",
"id": "d006b2ea-9dfe-49c7-88a9-a5a0775185fd",
"metadata": {},
"source": [
"# Additional End of week Exercise - week 2\n",
"\n",
"Now use everything you've learned from Week 2 to build a full prototype for the technical question/answerer you built in Week 1 Exercise.\n",
"\n",
"This should include a Gradio UI, streaming, use of the system prompt to add expertise, and the ability to switch between models. Bonus points if you can demonstrate use of a tool!\n",
"\n",
"If you feel bold, see if you can add audio input so you can talk to it, and have it respond with audio. ChatGPT or Claude can help you, or email me if you have questions.\n",
"\n",
"I will publish a full solution here soon - unless someone beats me to it...\n",
"\n",
"There are so many commercial applications for this, from a language tutor, to a company onboarding solution, to a companion AI to a course (like this one!) I can't wait to see your results."
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "a07e7793-b8f5-44f4-aded-5562f633271a",
"metadata": {},
"outputs": [],
"source": [
"# Imports\n",
"\n",
"import os\n",
"import json\n",
"import base64\n",
"import logging\n",
"import gradio as gr\n",
"from PIL import Image\n",
"from io import BytesIO\n",
"from openai import OpenAI\n",
"from dotenv import load_dotenv\n",
"from IPython.display import Audio, display"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "e879f6ae-b246-479d-8f81-94e47a9072ec",
"metadata": {},
"outputs": [],
"source": [
"# Initialization\n",
"logging.basicConfig(level=logging.INFO)\n",
"load_dotenv(override=True)\n",
"\n",
"openai_api_key = os.getenv('OPENAI_API_KEY')\n",
"if openai_api_key:\n",
" logging.info(f\"OpenAI API Key exists and begins {openai_api_key[:8]}\")\n",
"else:\n",
" logging.error(\"OpenAI API Key not set\")\n",
" \n",
"MODEL = \"gpt-4o-mini\"\n",
"openai = OpenAI()"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "d4455169-9e5e-4171-92e8-6f850a06f6e3",
"metadata": {},
"outputs": [],
"source": [
"system_message = (\n",
" \"You are a helpful assistant for an airline called FlightAI. \"\n",
" \"Always respond in a short, courteous sentence. \"\n",
" \"Provide accurate information only. \"\n",
" \"If you dont know something, say so clearly. \"\n",
" \"Before booking a ticket, strictly follow this order: \"\n",
" \"1) Check if the destination is available, \"\n",
" \"2) Then check the ticket price, \"\n",
" \"3) Collect all neccessary details like name, destination and date of journey, \"\n",
" \"4) Only then proceed with the booking. \"\n",
" \"Always use the appropriate tools or APIs for each step before confirming a booking.\"\n",
")"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "4bab8e2c-e2b1-4421-a95b-7f1251670817",
"metadata": {},
"outputs": [],
"source": [
"# Dummy funcs that mimic the ticket booking behaviour\n",
"# Replace these will real funcs (that call APIs or make DB transactions) to actually book a ticket\n",
"\n",
"ticket_prices = {\n",
" \"london\": \"$799\",\n",
" \"paris\": \"$899\",\n",
" \"tokyo\": \"$1400\",\n",
" \"berlin\": \"$499\"\n",
"}\n",
"\n",
"def check_destination_availability(destination: str) -> dict:\n",
" \"\"\"\n",
" Check if the given destination is available in our ticketing system.\n",
" \n",
" Args:\n",
" destination (str): The name of the city.\n",
" \n",
" Returns:\n",
" dict: {\"available\": bool}\n",
" \"\"\"\n",
" logging.info(f\"Checking availability for destination: {destination}\")\n",
" \n",
" available = destination.lower() in ticket_prices\n",
" return {\"available\": available}\n",
"\n",
"\n",
"def fetch_ticket_price(destination_city: str) -> dict:\n",
" \"\"\"\n",
" Retrieve the ticket price for a given city.\n",
" \n",
" Args:\n",
" destination_city (str): The name of the destination city.\n",
" \n",
" Returns:\n",
" dict: {\"price\": str} or {\"price\": \"Unknown\"} if not found\n",
" \"\"\"\n",
" logging.info(f\"Retrieving price for destination: {destination_city}\")\n",
" \n",
" city = destination_city.lower()\n",
" price = ticket_prices.get(city, \"Unknown\")\n",
" \n",
" return {\"price\": price}\n",
"\n",
"\n",
"def book_ticket(name: str, destination_city: str, journey_date: str) -> dict:\n",
" \"\"\"\n",
" Book a ticket to a destination city for a given user and date.\n",
" \n",
" Args:\n",
" name (str): Name of the passenger.\n",
" destination_city (str): Destination city.\n",
" journey_date (str): Date of journey in YYYY-MM-DD format.\n",
" \n",
" Returns:\n",
" dict: Booking confirmation with name, city, price, and date, or error.\n",
" \"\"\"\n",
" logging.info(f\"Booking ticket for {name} to {destination_city} on {journey_date}\")\n",
" \n",
" city = destination_city.lower()\n",
"\n",
" if city not in ticket_prices:\n",
" logging.error(f\"City '{destination_city}' not found in ticket list.\")\n",
" return {\"error\": \"Destination not found.\"}\n",
"\n",
" price_info = fetch_ticket_price(destination_city)\n",
" \n",
" return {\n",
" \"name\": name,\n",
" \"destination_city\": destination_city.title(),\n",
" \"journey_date\": journey_date,\n",
" \"price\": price_info[\"price\"]\n",
" }\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "400f4592-2326-43f6-a921-fcd051c4f022",
"metadata": {},
"outputs": [],
"source": [
"destination_availability_tool = {\n",
" \"name\": \"check_destination_availability\",\n",
" \"description\": \"Check if tickets are available for the given destination city before proceeding with any booking or pricing inquiry.\",\n",
" \"parameters\": {\n",
" \"type\": \"object\",\n",
" \"properties\": {\n",
" \"destination\": {\n",
" \"type\": \"string\",\n",
" \"description\": \"The name of the destination city to check for availability.\"\n",
" }\n",
" },\n",
" \"required\": [\"destination\"],\n",
" \"additionalProperties\": False\n",
" }\n",
"}\n",
"\n",
"ticket_price_tool = {\n",
" \"name\": \"fetch_ticket_price\",\n",
" \"description\": (\n",
" \"Get the price of a return ticket to the specified destination city. \"\n",
" \"Use this after confirming that the destination is available, especially when the customer asks for the ticket price.\"\n",
" ),\n",
" \"parameters\": {\n",
" \"type\": \"object\",\n",
" \"properties\": {\n",
" \"destination_city\": {\n",
" \"type\": \"string\",\n",
" \"description\": \"The city for which the customer wants the ticket price.\"\n",
" }\n",
" },\n",
" \"required\": [\"destination_city\"],\n",
" \"additionalProperties\": False\n",
" }\n",
"}\n",
"\n",
"ticket_booking_tool = {\n",
" \"name\": \"book_ticket\",\n",
" \"description\": (\n",
" \"Book a ticket for the customer to the specified destination city on the given journey date. \"\n",
" \"Use only after availability and price have been checked.\"\n",
" ),\n",
" \"parameters\": {\n",
" \"type\": \"object\",\n",
" \"properties\": {\n",
" \"name\": {\n",
" \"type\": \"string\",\n",
" \"description\": \"Full name of the person booking the ticket.\"\n",
" },\n",
" \"destination_city\": {\n",
" \"type\": \"string\",\n",
" \"description\": \"The city that the customer wants to travel to.\"\n",
" },\n",
" \"journey_date\": {\n",
" \"type\": \"string\",\n",
" \"format\": \"date\",\n",
" \"description\": \"The journey date in YYYY-MM-DD format.\"\n",
" }\n",
" },\n",
" \"required\": [\"name\", \"destination_city\", \"journey_date\"],\n",
" \"additionalProperties\": False\n",
" }\n",
"}\n",
"\n",
"tools = [\n",
" {\"type\": \"function\", \"function\": destination_availability_tool},\n",
" {\"type\": \"function\", \"function\": ticket_price_tool},\n",
" {\"type\": \"function\", \"function\": ticket_booking_tool},\n",
"]"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "f02c17ba-14f2-41c4-b6a2-d1397405d368",
"metadata": {},
"outputs": [],
"source": [
"def handle_tool_call(message):\n",
" \"\"\"\n",
" Handles a single OpenAI tool call message and returns both the result\n",
" and a formatted tool response dictionary.\n",
" \n",
" Args:\n",
" message (object): An OpenAI message containing a tool call.\n",
" \n",
" Returns:\n",
" tuple: (result_dict, response_dict)\n",
" \"\"\"\n",
" tool_call = message.tool_calls[0]\n",
" function_name = tool_call.function.name\n",
" arguments = json.loads(tool_call.function.arguments)\n",
"\n",
" result = None\n",
"\n",
" logging.info(f\"Tool call received: {function_name} with arguments: {arguments}\")\n",
"\n",
" if function_name == \"check_destination_availability\":\n",
" result = check_destination_availability(**arguments)\n",
"\n",
" elif function_name == \"fetch_ticket_price\":\n",
" city = arguments.get(\"destination_city\")\n",
" price_info = fetch_ticket_price(city)\n",
" result = {\"destination_city\": city, \"price\": price_info[\"price\"]}\n",
"\n",
" elif function_name == \"book_ticket\":\n",
" result = book_ticket(**arguments)\n",
"\n",
" else:\n",
" logging.warning(\"Unrecognized tool function: %s\", function_name)\n",
" result = {\"error\": f\"Unknown function '{function_name}'\"}\n",
"\n",
" response = {\n",
" \"role\": \"tool\",\n",
" \"tool_call_id\": tool_call.id,\n",
" \"content\": json.dumps(result)\n",
" }\n",
"\n",
" return result, response"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "72c1a9e7-186c-4218-9edc-01814baec431",
"metadata": {},
"outputs": [],
"source": [
"def artist(city: str, style: str = \"vibrant pop-art\", size: str = \"1024x1024\") -> Image.Image:\n",
" \"\"\"\n",
" Generates a city-themed vacation image using DALL·E.\n",
"\n",
" Args:\n",
" city (str): Name of the city to visualize.\n",
" style (str): Artistic style for the image prompt.\n",
" size (str): Image resolution (e.g., \"1024x1024\").\n",
"\n",
" Returns:\n",
" Image.Image: A PIL Image object representing the generated image.\n",
"\n",
" Raises:\n",
" ValueError: If city name is empty.\n",
" RuntimeError: If image generation fails.\n",
" \"\"\"\n",
" if not city.strip():\n",
" raise ValueError(\"City name cannot be empty.\")\n",
"\n",
" prompt = (\n",
" f\"An image representing a vacation in {city}, \"\n",
" f\"showing iconic tourist attractions, cultural elements, and everything unique about {city}, \"\n",
" f\"rendered in a {style} style.\"\n",
" )\n",
"\n",
" logging.info(\"Generating image for city: %s with style: %s\", city, style)\n",
"\n",
" try:\n",
" response = openai.images.generate(\n",
" model=\"dall-e-3\",\n",
" prompt=prompt,\n",
" size=size,\n",
" n=1,\n",
" response_format=\"b64_json\",\n",
" )\n",
"\n",
" image_base64 = response.data[0].b64_json\n",
" image_data = base64.b64decode(image_base64)\n",
" logging.info(\"Image generation successful for %s\", city)\n",
"\n",
" return Image.open(BytesIO(image_data))\n",
"\n",
" except Exception as e:\n",
" logging.error(\"Failed to generate image for city '%s': %s\", city, str(e))\n",
" raise RuntimeError(f\"Image generation failed for city '{city}'\") from e"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "fdf7c091-6c68-4af6-8197-c1456b36cedf",
"metadata": {},
"outputs": [],
"source": [
"def talker(message: str, output_filename: str = \"output_audio.mp3\", autoplay: bool = True) -> None:\n",
" \"\"\"\n",
" Converts a text message into speech using OpenAI TTS and plays the audio.\n",
"\n",
" Args:\n",
" message (str): The text to convert to speech.\n",
" output_filename (str): The filename to save the generated audio.\n",
" autoplay (bool): Whether to autoplay the audio in the notebook.\n",
"\n",
" Raises:\n",
" ValueError: If the message is empty.\n",
" RuntimeError: If the audio generation fails.\n",
" \"\"\"\n",
" if not message.strip():\n",
" raise ValueError(\"Message cannot be empty.\")\n",
"\n",
" logging.info(\"Generating speech for message: %s\", message)\n",
"\n",
" try:\n",
" response = openai.audio.speech.create(\n",
" model=\"tts-1\",\n",
" voice=\"alloy\",\n",
" input=message\n",
" )\n",
"\n",
" with open(output_filename, \"wb\") as f:\n",
" f.write(response.content)\n",
"\n",
" logging.info(\"Audio written to: %s\", output_filename)\n",
"\n",
" if autoplay:\n",
" display(Audio(output_filename, autoplay=True))\n",
"\n",
" except Exception as e:\n",
" logging.error(\"Failed to generate or play audio: %s\", str(e))\n",
" raise RuntimeError(\"Text-to-speech generation failed.\") from e"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "54568b4a-be8d-47a1-b924-03acdafef70e",
"metadata": {},
"outputs": [],
"source": [
"def translate(message, language):\n",
" \"\"\"\n",
" Translates the given text into the specified language using OpenAI Chat API.\n",
"\n",
" Args:\n",
" message (str): The text to be translated.\n",
" language (str): Target language for translation (e.g., 'French', 'Japanese').\n",
"\n",
" Returns:\n",
" str: Translated text.\n",
"\n",
" Raises:\n",
" ValueError: If input message or language is empty.\n",
" RuntimeError: If translation fails due to API or other issues.\n",
" \"\"\"\n",
" if not message.strip():\n",
" raise ValueError(\"Input message cannot be empty.\")\n",
" if not language.strip():\n",
" raise ValueError(\"Target language cannot be empty.\")\n",
"\n",
" logging.info(\"Translating to %s: %s\", language, message)\n",
"\n",
" messages = [\n",
" {\"role\": \"system\", \"content\": f\"You are a translation assistant. Translate everything the user says to {language}.\"},\n",
" {\"role\": \"user\", \"content\": message}\n",
" ]\n",
"\n",
" try:\n",
" response = openai.chat.completions.create(\n",
" model=MODEL,\n",
" messages=messages\n",
" )\n",
" translated = response.choices[0].message.content.strip()\n",
" logging.info(\"Translation successful.\")\n",
" return translated\n",
"\n",
" except Exception as e:\n",
" logging.error(\"Translation failed: %s\", str(e))\n",
" raise RuntimeError(\"Failed to translate message.\") from e"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "8e6cf470-8ea0-43b2-bbcc-53c2432feb0d",
"metadata": {},
"outputs": [],
"source": [
"def transcribe_audio(audio_path):\n",
" \"\"\"\n",
" Transcribes an audio file using OpenAI's Whisper model.\n",
"\n",
" Args:\n",
" audio_path (str): Path to the audio file (e.g., .mp3, .wav).\n",
" model (str): OpenAI model for transcription (default: 'whisper-1').\n",
"\n",
" Returns:\n",
" str: Transcribed text from the audio file.\n",
"\n",
" Raises:\n",
" ValueError: If the path is invalid or the file does not exist.\n",
" RuntimeError: If the transcription fails.\n",
" \"\"\"\n",
" if not audio_path or not os.path.exists(audio_path):\n",
" raise ValueError(\"Invalid or missing audio file path.\")\n",
"\n",
" logging.info(\"Transcribing audio file: %s using model: whisper-1\", audio_path)\n",
"\n",
" try:\n",
" with open(audio_path, \"rb\") as f:\n",
" response = openai.audio.transcriptions.create(\n",
" model=\"whisper-1\",\n",
" file=f\n",
" )\n",
" transcript = response.text.strip()\n",
" logging.info(\"Transcription successful.\")\n",
" return transcript\n",
"\n",
" except Exception as e:\n",
" logging.error(\"Transcription failed: %s\", str(e))\n",
" raise RuntimeError(\"Failed to transcribe audio.\") from e"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "3489656e-0f08-4d41-94b1-d902c93ca164",
"metadata": {},
"outputs": [],
"source": [
"def chat(history: list, language: str, translated_history: list, speaking_language: str) -> tuple:\n",
" \"\"\"\n",
" Handles a chat interaction including tool calls, image generation, translation, and TTS playback.\n",
"\n",
" Args:\n",
" history (list): List of previous conversation messages.\n",
" language (str): Target language for translation and TTS.\n",
"\n",
" Returns:\n",
" tuple: (updated history list, generated image if any, translated response string)\n",
" \"\"\"\n",
" messages = [{\"role\": \"system\", \"content\": system_message}] + history\n",
" image = None\n",
"\n",
" try:\n",
" # Initial assistant response\n",
" response = openai.chat.completions.create(model=MODEL, messages=messages, tools=tools)\n",
" choice = response.choices[0]\n",
"\n",
" # Handle tool calls if triggered\n",
" if choice.finish_reason == \"tool_calls\":\n",
" message = choice.message\n",
" result, tool_response = handle_tool_call(message)\n",
"\n",
" # Append tool-related messages\n",
" messages.append(message)\n",
" messages.append(tool_response)\n",
" logging.info(\"Tool call result: %s\", result)\n",
"\n",
" # Generate image if a booking was completed\n",
" if message.tool_calls[0].function.name == \"book_ticket\" and \"destination_city\" in result:\n",
" image = artist(result[\"destination_city\"])\n",
"\n",
" # Get final assistant response after tool execution\n",
" response = openai.chat.completions.create(model=MODEL, messages=messages)\n",
" choice = response.choices[0]\n",
"\n",
" reply = choice.message.content.strip()\n",
" history.append({\"role\": \"assistant\", \"content\": reply})\n",
"\n",
" # Translate and speak the reply\n",
" translated_reply = translate(reply, language)\n",
" translated_history.append({\"role\": \"assistant\", \"content\": translated_reply})\n",
"\n",
" if speaking_language == \"English\":\n",
" talker(reply)\n",
" else:\n",
" talker(translated_reply)\n",
"\n",
" return history, image, translated_history\n",
"\n",
" except Exception as e:\n",
" logging.error(\"Chat processing failed: %s\", str(e))\n",
" raise RuntimeError(\"Failed to complete chat interaction.\") from e"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "f76acc68-726e-457f-88ab-99da75debde5",
"metadata": {},
"outputs": [],
"source": [
"force_dark_mode = \"\"\"\n",
"function refresh() {\n",
" const url = new URL(window.location);\n",
" if (url.searchParams.get('__theme') !== 'dark') {\n",
" url.searchParams.set('__theme', 'dark');\n",
" window.location.href = url.href;\n",
" }\n",
"}\n",
"\"\"\"\n",
"\n",
"with gr.Blocks(js=force_dark_mode) as ui:\n",
" with gr.Row():\n",
" gr.Markdown(\"### FlightAI Chat with Translation\")\n",
"\n",
" with gr.Row():\n",
" lang_dropdown = gr.Dropdown(\n",
" choices=[\"Spanish\", \"French\", \"German\", \"Japanese\", \"Hindi\"],\n",
" value=\"Spanish\",\n",
" label=\"Translate To\"\n",
" )\n",
" \n",
" speak_dropdown = gr.Dropdown(\n",
" choices=[\"English\", \"Selected Language\"],\n",
" value=\"English\",\n",
" label=\"Speak out in\"\n",
" )\n",
" \n",
" with gr.Row():\n",
" chatbot = gr.Chatbot(height=500, type=\"messages\", label=\"Chat History\")\n",
" translated_chatbot = gr.Chatbot(height=500, type=\"messages\", label=\"Translated Chat\")\n",
" image_output = gr.Image(height=500)\n",
"\n",
" with gr.Row():\n",
" entry = gr.Textbox(label=\"Chat with our AI Assistant:\")\n",
" audio_input = gr.Audio(sources=\"microphone\", type=\"filepath\", label=\"Or speak to the assistant\")\n",
"\n",
" with gr.Row():\n",
" clear = gr.Button(\"Clear\")\n",
"\n",
" def do_entry(message, history, audio, translated_history, language):\n",
" if audio:\n",
" message = transcribe_audio(audio)\n",
"\n",
" if message:\n",
" history += [{\"role\": \"user\", \"content\": message}]\n",
" translated_history += [{\"role\": \"user\", \"content\": translate(message, language)}]\n",
" return \"\", history, None, translated_history\n",
"\n",
" entry.submit(\n",
" do_entry,\n",
" inputs=[entry, chatbot, audio_input, translated_chatbot, lang_dropdown],\n",
" outputs=[entry, chatbot, audio_input, translated_chatbot]\n",
" ).then(\n",
" chat,\n",
" inputs=[chatbot, lang_dropdown, translated_chatbot, speak_dropdown],\n",
" outputs=[chatbot, image_output, translated_chatbot]\n",
" )\n",
"\n",
" audio_input.change(\n",
" do_entry,\n",
" inputs=[entry, chatbot, audio_input, translated_chatbot, lang_dropdown],\n",
" outputs=[entry, chatbot, audio_input, translated_chatbot]\n",
" ).then(\n",
" chat,\n",
" inputs=[chatbot, lang_dropdown, translated_chatbot, speak_dropdown],\n",
" outputs=[chatbot, image_output, translated_chatbot]\n",
" )\n",
"\n",
" clear.click(lambda: [\"\", [], None, [], None], inputs=None, outputs=[entry, chatbot, audio_input, translated_chatbot, image_output], queue=False)\n",
"\n",
"ui.launch(inbrowser=True)"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "58f97435-fa0d-45f7-b02f-4ac5f4901c53",
"metadata": {},
"outputs": [],
"source": []
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3 (ipykernel)",
"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.10.6"
}
},
"nbformat": 4,
"nbformat_minor": 5
}

View File

@@ -0,0 +1,808 @@
{
"cells": [
{
"cell_type": "markdown",
"id": "d938fc6c-bcca-4572-b851-75370fe21c67",
"metadata": {},
"source": [
"# Airline Assistant using Gemini API for Image and Audio as well - Live ticket prices using Amadeus API"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "f5eda470-07ee-4d01-bada-3390050ac9c2",
"metadata": {},
"outputs": [],
"source": [
"import os\n",
"import json\n",
"import random\n",
"import string\n",
"import base64\n",
"import gradio as gr\n",
"import pyaudio\n",
"import requests\n",
"from io import BytesIO\n",
"from PIL import Image\n",
"from dotenv import load_dotenv\n",
"from google import genai\n",
"from google.genai import types"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "09aaf3b0-beb7-4b64-98a4-da16fc83dadb",
"metadata": {},
"outputs": [],
"source": [
"load_dotenv(override=True)\n",
"api_key = os.getenv(\"GOOGLE_API_KEY\")\n",
"\n",
"if not api_key:\n",
" print(\"API Key not found!\")\n",
"else:\n",
" print(\"API Key loaded in memory\")"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "35881fb9-4d51-43dc-a5e6-d9517e22019a",
"metadata": {},
"outputs": [],
"source": [
"MODEL_GEMINI = 'gemini-2.5-flash'\n",
"MODEL_GEMINI_IMAGE = 'gemini-2.0-flash-preview-image-generation'\n",
"MODEL_GEMINI_SPEECH = 'gemini-2.5-flash-preview-tts'"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "a5ed391c-8a67-4465-9c66-e915548a0d6a",
"metadata": {},
"outputs": [],
"source": [
"try:\n",
" client = genai.Client(api_key=api_key)\n",
" print(\"Google GenAI Client initialized successfully!\")\n",
"except Exception as e:\n",
" print(f\"Error initializing GenAI Client: {e}\")\n",
" print(\"Ensure your GOOGLE_API_KEY is correctly set as an environment variable.\")\n",
" exit() "
]
},
{
"cell_type": "markdown",
"id": "407ad581-9580-4dba-b236-abb6c6788933",
"metadata": {},
"source": [
"## Image Generation "
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "a21921f8-57b1-4665-8999-7f2a40645b59",
"metadata": {},
"outputs": [],
"source": [
"def fetch_image(city):\n",
" prompt = (\n",
" f\"A high-quality, photo-realistic image of a vacation in {city}, \"\n",
" f\"showing iconic landmarks, cultural attractions, authentic street life, and local cuisine. \"\n",
" f\"Capture natural lighting, real people enjoying travel experiences, and the unique vibe of {city}'s atmosphere. \"\n",
" f\"The composition should feel immersive, warm, and visually rich, as if taken by a travel photographer.\"\n",
")\n",
"\n",
" response = client.models.generate_content(\n",
" model = MODEL_GEMINI_IMAGE,\n",
" contents = prompt,\n",
" config=types.GenerateContentConfig(\n",
" response_modalities=['TEXT', 'IMAGE']\n",
" )\n",
" )\n",
"\n",
" for part in response.candidates[0].content.parts:\n",
" if part.inline_data is not None:\n",
" image_data = BytesIO(part.inline_data.data)\n",
" return Image.open(image_data)\n",
"\n",
" raise ValueError(\"No image found in Gemini response.\")\n",
" "
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "bcd4aed1-8b4d-4771-ba32-e729e82bab54",
"metadata": {},
"outputs": [],
"source": [
"fetch_image(\"london\")"
]
},
{
"cell_type": "markdown",
"id": "5f6baee6-e2e2-4cc4-941d-34a4c72cee67",
"metadata": {},
"source": [
"## Speech Generation"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "825dfedc-0271-4191-a3d1-50872af4c8cf",
"metadata": {},
"outputs": [],
"source": [
"\"\"\"\n",
"Kore -- Firm\n",
"Puck -- Upbeat\n",
"Leda -- Youthful\n",
"Iapetus -- Clear\n",
"Erinome -- Clear\n",
"Sadachbia -- Lively\n",
"Sulafat -- Warm\n",
"Despina -- Smooth\n",
"\"\"\"\n",
"\n",
"def talk(message:str, voice_name:str=\"Leda\", mood:str=\"cheerfully\"):\n",
" prompt = f\"Say {mood}: {message}\"\n",
" response = client.models.generate_content(\n",
" model = MODEL_GEMINI_SPEECH,\n",
" contents = prompt,\n",
" config=types.GenerateContentConfig(\n",
" response_modalities=[\"AUDIO\"],\n",
" speech_config=types.SpeechConfig(\n",
" voice_config=types.VoiceConfig(\n",
" prebuilt_voice_config=types.PrebuiltVoiceConfig(\n",
" voice_name=voice_name,\n",
" )\n",
" )\n",
" ), \n",
" )\n",
" )\n",
"\n",
" # Fetch the audio bytes\n",
" pcm_data = response.candidates[0].content.parts[0].inline_data.data\n",
" # Play the audio using PyAudio\n",
" p = pyaudio.PyAudio()\n",
" stream = p.open(format=pyaudio.paInt16, channels=1, rate=24000, output=True)\n",
" stream.write(pcm_data)\n",
" stream.stop_stream()\n",
" stream.close()\n",
" p.terminate()\n",
"\n",
" # Play using simpleaudio (16-bit PCM, mono, 24kHz)\n",
" # play_obj = sa.play_buffer(pcm_data, num_channels=1, bytes_per_sample=2, sample_rate=24000)\n",
" # play_obj.wait_done() "
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "54967ebc-24a6-4bb2-9a19-20c3585f1d77",
"metadata": {},
"outputs": [],
"source": [
"talk(\"Hi, How are you? Welcome to FlyJumbo Airlines\",\"Kore\",\"helpful\")"
]
},
{
"cell_type": "markdown",
"id": "be9dc275-838e-4c54-b487-41d094dad96b",
"metadata": {},
"source": [
"## Ticket Price Tool Function - Using Amadeus API "
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "8613a080-d82c-4c1a-8db4-377614997ac2",
"metadata": {},
"outputs": [],
"source": [
"client_id = os.getenv(\"AMADEUS_CLIENT_ID\")\n",
"client_secret = os.getenv(\"AMADEUS_CLIENT_SECRET\")"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "6bf78f61-0de1-4552-a1d4-1a28380be6a5",
"metadata": {},
"outputs": [],
"source": [
"# Get the token first\n",
"def get_amadeus_token():\n",
" url = \"https://test.api.amadeus.com/v1/security/oauth2/token\"\n",
" headers = {\"Content-Type\": \"application/x-www-form-urlencoded\"}\n",
" data = {\n",
" \"grant_type\": \"client_credentials\",\n",
" \"client_id\": client_id,\n",
" \"client_secret\": client_secret,\n",
" }\n",
" \n",
" try:\n",
" response = requests.post(url, headers=headers, data=data, timeout=10)\n",
" response.raise_for_status()\n",
" return response.json()[\"access_token\"]\n",
" \n",
" except requests.exceptions.HTTPError as e:\n",
" print(f\"HTTP Error {response.status_code}: {response.text}\")\n",
" \n",
" except requests.exceptions.RequestException as e:\n",
" print(\"Network or connection error:\", e)\n",
" \n",
" return None"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "1c5261f6-6662-4e9d-8ff0-8e10171bb963",
"metadata": {},
"outputs": [],
"source": [
"def get_airline_name(code, token):\n",
" url = f\"https://test.api.amadeus.com/v1/reference-data/airlines\"\n",
" headers = {\"Authorization\": f\"Bearer {token}\"}\n",
" params = {\"airlineCodes\": code}\n",
"\n",
" response = requests.get(url, headers=headers, params=params)\n",
" response.raise_for_status()\n",
" data = response.json()\n",
"\n",
" if \"data\" in data and data[\"data\"]:\n",
" return data[\"data\"][0].get(\"businessName\", code)\n",
" return code"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "42a55f06-880a-4c49-8560-2e7b97953c1a",
"metadata": {},
"outputs": [],
"source": [
"COMMON_CITY_CODES = {\n",
" \"delhi\": \"DEL\",\n",
" \"mumbai\": \"BOM\",\n",
" \"chennai\": \"MAA\",\n",
" \"kolkata\": \"CCU\",\n",
" \"bengaluru\": \"BLR\",\n",
" \"hyderabad\": \"HYD\",\n",
" \"patna\": \"PAT\",\n",
" \"raipur\": \"RPR\",\n",
" \"panaji\": \"GOI\",\n",
" \"chandigarh\": \"IXC\",\n",
" \"srinagar\": \"SXR\",\n",
" \"ranchi\": \"IXR\",\n",
" \"bengaluru\": \"BLR\",\n",
" \"thiruvananthapuram\": \"TRV\",\n",
" \"bhopal\": \"BHO\",\n",
" \"mumbai\": \"BOM\",\n",
" \"imphal\": \"IMF\",\n",
" \"aizawl\": \"AJL\",\n",
" \"bhubaneswar\": \"BBI\",\n",
" \"jaipur\": \"JAI\",\n",
" \"chennai\": \"MAA\",\n",
" \"hyderabad\": \"HYD\",\n",
" \"agartala\": \"IXA\",\n",
" \"lucknow\": \"LKO\",\n",
" \"dehradun\": \"DED\",\n",
" \"kolkata\": \"CCU\",\n",
"\n",
" # Union territories\n",
" \"port blair\": \"IXZ\",\n",
" \"leh\": \"IXL\",\n",
" \"puducherry\": \"PNY\",\n",
"\n",
" # Major metro cities (for redundancy)\n",
" \"ahmedabad\": \"AMD\",\n",
" \"surat\": \"STV\",\n",
" \"coimbatore\": \"CJB\",\n",
" \"vizag\": \"VTZ\",\n",
" \"vijayawada\": \"VGA\",\n",
" \"nagpur\": \"NAG\",\n",
" \"indore\": \"IDR\",\n",
" \"kanpur\": \"KNU\",\n",
" \"varanasi\": \"VNS\"\n",
"}\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "b061ec2c-609b-4d77-bd41-c9bc5bf901f4",
"metadata": {},
"outputs": [],
"source": [
"city_code_cache = {}\n",
"\n",
"def get_city_code(city_name, token):\n",
" city_name = city_name.strip().lower()\n",
"\n",
" if city_name in city_code_cache:\n",
" return city_code_cache[city_name]\n",
"\n",
" if city_name in COMMON_CITY_CODES:\n",
" return COMMON_CITY_CODES[city_name]\n",
"\n",
" base_url = \"https://test.api.amadeus.com/v1/reference-data/locations\"\n",
" headers = {\"Authorization\": f\"Bearer {token}\"}\n",
"\n",
" for subtype in [\"CITY\", \"AIRPORT,CITY\"]:\n",
" params = {\"keyword\": city_name, \"subType\": subtype}\n",
" try:\n",
" response = requests.get(base_url, headers=headers, params=params, timeout=10)\n",
" response.raise_for_status()\n",
" data = response.json()\n",
"\n",
" if \"data\" in data and data[\"data\"]:\n",
" code = data[\"data\"][0][\"iataCode\"]\n",
" print(f\"[INFO] Found {subtype} match for '{city_name}': {code}\")\n",
" city_code_cache[city_name] = code\n",
" return code\n",
" except Exception as e:\n",
" print(f\"[ERROR] Location lookup failed for {subtype}: {e}\")\n",
"\n",
" return None"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "e9816a9c-fd70-4dfc-a3c0-4d8709997371",
"metadata": {},
"outputs": [],
"source": [
"# Getting live ticket price \n",
"\n",
"def get_live_ticket_prices(origin, destination, departure_date, return_date=None):\n",
" token = get_amadeus_token()\n",
"\n",
" url = \"https://test.api.amadeus.com/v2/shopping/flight-offers\"\n",
" headers = {\"Authorization\": f\"Bearer {token}\"}\n",
"\n",
" origin_code = get_city_code(origin,token)\n",
" destination_code = get_city_code(destination,token)\n",
"\n",
" if not origin_code:\n",
" return f\"Sorry, I couldn't find the airport code for the city '{origin}'.\"\n",
" if not destination_code:\n",
" return f\"Sorry, I couldn't find the airport code for the city '{destination}'.\"\n",
"\n",
" params = {\n",
" \"originLocationCode\": origin_code.upper(),\n",
" \"destinationLocationCode\": destination_code.upper(),\n",
" \"departureDate\": departure_date,\n",
" \"adults\": 1,\n",
" \"currencyCode\": \"USD\",\n",
" \"max\": 1,\n",
" }\n",
"\n",
" if return_date:\n",
" params[\"returnDate\"] = return_date\n",
"\n",
" try:\n",
" response = requests.get(url, headers=headers, params=params, timeout=10)\n",
" response.raise_for_status()\n",
" data = response.json()\n",
" \n",
" if \"data\" in data and data[\"data\"]:\n",
" offer = data[\"data\"][0]\n",
" price = offer[\"price\"][\"total\"]\n",
" airline_codes = offer.get(\"validatingAirlineCodes\", [])\n",
" airline_code = airline_codes[0] if airline_codes else \"Unknown\"\n",
"\n",
" try:\n",
" airline_name = get_airline_name(airline_code, token) if airline_code != \"Unknown\" else \"Unknown Airline\"\n",
" if not airline_name: \n",
" airline_name = airline_code\n",
" except Exception:\n",
" airline_name = airline_code\n",
" \n",
" \n",
" if return_date:\n",
" return (\n",
" f\"Round-trip flight from {origin.capitalize()} to {destination.capitalize()}:\\n\"\n",
" f\"- Departing: {departure_date}\\n\"\n",
" f\"- Returning: {return_date}\\n\"\n",
" f\"- Airline: {airline_name}\\n\"\n",
" f\"- Price: ${price}\"\n",
" )\n",
" else:\n",
" return (\n",
" f\"One-way flight from {origin.capitalize()} to {destination.capitalize()} on {departure_date}:\\n\"\n",
" f\"- Airline: {airline_name}\\n\"\n",
" f\"- Price: ${price}\"\n",
" )\n",
" else:\n",
" return f\"No flights found from {origin.capitalize()} to {destination.capitalize()} on {departure_date}.\"\n",
" except requests.exceptions.RequestException as e:\n",
" return f\"❌ Error fetching flight data: {str(e)}\" \n",
" "
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "7bc7657e-e8b5-4647-9745-d7d403feb09a",
"metadata": {},
"outputs": [],
"source": [
"get_live_ticket_prices(\"london\", \"chennai\", \"2025-07-01\",\"2025-07-10\")"
]
},
{
"cell_type": "markdown",
"id": "e1153b94-90e7-4856-8c85-e456305a7817",
"metadata": {},
"source": [
"## Ticket Booking Tool Function - DUMMY"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "5dfc3b12-0a16-4861-a549-594f175ff956",
"metadata": {},
"outputs": [],
"source": [
"def book_flight(origin, destination, departure_date, return_date=None, airline=\"Selected Airline\", passenger_name=\"Guest\"):\n",
" # Generate a dummy ticket reference (PNR)\n",
" ticket_ref = ''.join(random.choices(string.ascii_uppercase + string.digits, k=6))\n",
"\n",
" # Build confirmation message\n",
" confirmation = (\n",
" f\"🎫 Booking confirmed for {passenger_name}!\\n\"\n",
" f\"From: {origin.capitalize()} → To: {destination.capitalize()}\\n\"\n",
" f\"Departure: {departure_date}\"\n",
" )\n",
"\n",
" if return_date:\n",
" confirmation += f\"\\nReturn: {return_date}\"\n",
"\n",
" confirmation += (\n",
" f\"\\nAirline: {airline}\\n\"\n",
" f\"PNR: {ticket_ref}\\n\"\n",
" f\"✅ Your ticket has been booked successfully. Safe travels!\"\n",
" )\n",
"\n",
" return confirmation\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "122f655b-b7a4-45c6-aaec-afd2917a051b",
"metadata": {},
"outputs": [],
"source": [
"print(book_flight(\"chennai\", \"delhi\", \"2025-07-01\", \"2025-07-10\", \"Air India\", \"Ravi Kumar\"))"
]
},
{
"cell_type": "markdown",
"id": "e83d8e90-ae22-4728-83e5-d83fed7f2049",
"metadata": {},
"source": [
"## Gemini Chat Workings"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "5a656f4e-914d-4f5e-b7fa-48457935181a",
"metadata": {},
"outputs": [],
"source": [
"ticket_price_function_declaration = {\n",
" \"name\":\"get_live_ticket_prices\",\n",
" \"description\": \"Get live flight ticket prices between two cities for a given date (round-trip or one-way).\\\n",
" The destination may be a city or country (e.g., 'China'). Call this function whenever a customer asks about ticket prices., such as 'How much is a ticket to Paris?'\",\n",
" \"parameters\":{\n",
" \"type\": \"object\",\n",
" \"properties\": {\n",
" \"origin\": {\n",
" \"type\": \"string\",\n",
" \"description\": \"Name of the origin city. Example: 'Delhi'\",\n",
" },\n",
" \"destination\": {\n",
" \"type\": \"string\",\n",
" \"description\":\"Name of the destination city. Example: 'London'\",\n",
" },\n",
" \"departure_date\": {\n",
" \"type\": \"string\",\n",
" \"description\": \"Date of departure in YYYY-MM-DD format. Example: '2025-07-01'\",\n",
" },\n",
" \"return_date\": {\n",
" \"type\": \"string\",\n",
" \"description\": \"Optional return date for round-trip in YYYY-MM-DD format. Leave blank for one-way trips.\",\n",
" },\n",
" },\n",
" \"required\": [\"origin\", \"destination\", \"departure_date\"],\n",
" }\n",
"}"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "05a835ab-a675-40ed-9cd8-65f4c6b22722",
"metadata": {},
"outputs": [],
"source": [
"book_flight_function_declaration = {\n",
" \"name\": \"book_flight\",\n",
" \"description\": \"Book a flight for the user after showing the ticket details and confirming the booking. \"\n",
" \"Call this function when the user says things like 'yes', 'book it', or 'I want to book this flight'.\",\n",
" \"parameters\": {\n",
" \"type\": \"object\",\n",
" \"properties\": {\n",
" \"origin\": {\n",
" \"type\": \"string\",\n",
" \"description\": \"Name of the origin city. Example: 'Chennai'\",\n",
" },\n",
" \"destination\": {\n",
" \"type\": \"string\",\n",
" \"description\": \"Name of the destination city. Example: 'London'\",\n",
" },\n",
" \"departure_date\": {\n",
" \"type\": \"string\",\n",
" \"description\": \"Date of departure in YYYY-MM-DD format. Example: '2025-07-01'\",\n",
" },\n",
" \"return_date\": {\n",
" \"type\": \"string\",\n",
" \"description\": \"Optional return date for round-trip in YYYY-MM-DD format. Leave blank for one-way trips.\",\n",
" },\n",
" \"airline\": {\n",
" \"type\": \"string\",\n",
" \"description\": \"Airline name or code that the user wants to book with. Example: 'Air India'\",\n",
" },\n",
" \"passenger_name\": {\n",
" \"type\": \"string\",\n",
" \"description\": \"Full name of the passenger for the booking. Example: 'Ravi Kumar'\",\n",
" }\n",
" },\n",
" \"required\": [\"origin\", \"destination\", \"departure_date\", \"passenger_name\"],\n",
" }\n",
"}\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "ad0231cd-040f-416d-b150-0d8f90535718",
"metadata": {},
"outputs": [],
"source": [
"# System Definitions\n",
"\n",
"system_instruction_prompt = (\n",
" \"You are a helpful and courteous AI assistant for an airline company called FlyJumbo. \"\n",
" \"When a user starts a new conversation, greet them with: 'Hi there, welcome to FlyJumbo! How can I help you?'. \"\n",
" \"Do not repeat this greeting in follow-up messages. \"\n",
" \"Use the available tools if a user asks about ticket prices. \"\n",
" \"Ask follow-up questions to gather all necessary information before calling a function.\"\n",
" \"After calling a tool, always continue the conversation by summarizing the result and asking the user the next relevant question (e.g., if they want to proceed with a booking).\"\n",
" \"If you do not know the answer and no tool can help, respond politely that you are unable to help with the request. \"\n",
" \"Answer concisely in one sentence.\"\n",
")"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "ff0b3de8-5674-4f08-9f9f-06f88ff959a1",
"metadata": {},
"outputs": [],
"source": [
"tools = types.Tool(function_declarations=[ticket_price_function_declaration,book_flight_function_declaration])\n",
"generate_content_config = types.GenerateContentConfig(system_instruction=system_instruction_prompt, tools=[tools])"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "00a56779-16eb-4f31-9941-2eb01d17ed87",
"metadata": {},
"outputs": [],
"source": [
"def handle_tool_call(function_call):\n",
" print(f\"🔧 Function Called - {function_call.name}\")\n",
" function_name = function_call.name\n",
" args = function_call.args\n",
"\n",
" if function_name == \"get_live_ticket_prices\":\n",
" origin = args.get(\"origin\")\n",
" destination = args.get(\"destination\")\n",
" departure_date = args.get(\"departure_date\")\n",
" return_date = args.get(\"return_date\") or None\n",
"\n",
" return get_live_ticket_prices(origin, destination, departure_date, return_date)\n",
"\n",
" elif function_name == \"book_flight\":\n",
" origin = args.get(\"origin\")\n",
" destination = args.get(\"destination\")\n",
" departure_date = args.get(\"departure_date\")\n",
" return_date = args.get(\"return_date\") or None\n",
" airline = args.get(\"airline\", \"Selected Airline\")\n",
" passenger_name = args.get(\"passenger_name\", \"Guest\")\n",
"\n",
" return book_flight(origin, destination, departure_date, return_date, airline, passenger_name)\n",
"\n",
" else:\n",
" return f\"❌ Unknown function: {function_name}\"\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "d0c334d2-9ab0-4f80-ac8c-c66897e0bd7c",
"metadata": {},
"outputs": [],
"source": [
"def chat(message, history):\n",
" full_message_history = []\n",
" city_name = None\n",
"\n",
" # Convert previous history to Gemini-compatible format\n",
" for h in history:\n",
" if h[\"role\"] == \"user\":\n",
" full_message_history.append(\n",
" types.Content(role=\"user\", parts=[types.Part.from_text(text=h[\"content\"])])\n",
" )\n",
" elif h[\"role\"] == \"assistant\":\n",
" full_message_history.append(\n",
" types.Content(role=\"model\", parts=[types.Part.from_text(text=h[\"content\"])])\n",
" )\n",
"\n",
" # Add current user message\n",
" full_message_history.append(\n",
" types.Content(role=\"user\", parts=[types.Part.from_text(text=message)])\n",
" )\n",
"\n",
" # Send to Gemini with tool config\n",
" response = client.models.generate_content(\n",
" model=MODEL_GEMINI,\n",
" contents=full_message_history,\n",
" config=generate_content_config\n",
" )\n",
"\n",
" candidate = response.candidates[0]\n",
" part = candidate.content.parts[0]\n",
" function_call = getattr(part, \"function_call\", None)\n",
"\n",
" # Case: Tool call required\n",
" if function_call:\n",
" # Append model message that triggered tool call\n",
" full_message_history.append(\n",
" types.Content(role=\"model\", parts=candidate.content.parts)\n",
" )\n",
"\n",
" # Execute the tool\n",
" tool_output = handle_tool_call(function_call)\n",
"\n",
" # Wrap and append tool output\n",
" tool_response_part = types.Part.from_function_response(\n",
" name=function_call.name,\n",
" response={\"result\": tool_output}\n",
" )\n",
" \n",
" full_message_history.append(\n",
" types.Content(role=\"function\", parts=[tool_response_part])\n",
" )\n",
"\n",
"\n",
" if function_call.name == \"book_flight\":\n",
" city_name = function_call.args.get(\"destination\").lower()\n",
" \n",
"\n",
" # Send follow-up message including tool result\n",
" followup_response = client.models.generate_content(\n",
" model=MODEL_GEMINI,\n",
" contents=full_message_history,\n",
" config=generate_content_config\n",
" )\n",
"\n",
" final_text = followup_response.text\n",
" \n",
" full_message_history.append(\n",
" types.Content(role=\"model\", parts=[types.Part.from_text(text=final_text)])\n",
" )\n",
"\n",
" return final_text,city_name, history + [{\"role\": \"assistant\", \"content\": final_text}]\n",
" else:\n",
" text = response.text\n",
" return text, city_name, history + [{\"role\": \"assistant\", \"content\": text}]\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "9b245e6c-ef0b-4edf-b178-f14f2a75f285",
"metadata": {},
"outputs": [],
"source": [
"def user_submit(user_input, history):\n",
" history = history or []\n",
" history.append({\"role\": \"user\", \"content\": user_input})\n",
" \n",
" response_text, city_to_image, updated_history = chat(user_input, history)\n",
"\n",
" # Speak the response\n",
" try:\n",
" talk(response_text)\n",
" except Exception as e:\n",
" print(\"[Speech Error] Speech skipped due to quota limit.\")\n",
"\n",
" image = fetch_image(city_to_image) if city_to_image else None\n",
"\n",
" return \"\", updated_history, image, updated_history\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "7db25b86-9a71-417c-98f0-790e3f3531bf",
"metadata": {},
"outputs": [],
"source": [
"with gr.Blocks() as demo:\n",
" gr.Markdown(\"## ✈️ FlyJumbo Airline Assistant\")\n",
"\n",
" with gr.Row():\n",
" with gr.Column(scale=3):\n",
" chatbot = gr.Chatbot(label=\"Assistant\", height=500, type=\"messages\")\n",
" msg = gr.Textbox(placeholder=\"Ask about flights...\", show_label=False)\n",
" send_btn = gr.Button(\"Send\")\n",
"\n",
" with gr.Column(scale=2):\n",
" image_output = gr.Image(label=\"Trip Visual\", visible=True, height=500)\n",
"\n",
" state = gr.State([])\n",
" \n",
" send_btn.click(fn=user_submit, inputs=[msg, state], outputs=[msg, chatbot, image_output, state])\n",
" msg.submit(fn=user_submit, inputs=[msg, state], outputs=[msg, chatbot, image_output, state])\n",
"\n",
"demo.launch(inbrowser=True)"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "ef31bf62-9034-4fa7-b803-8f5df5309b77",
"metadata": {},
"outputs": [],
"source": []
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3 (ipykernel)",
"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.12.10"
}
},
"nbformat": 4,
"nbformat_minor": 5
}

View File

@@ -0,0 +1,344 @@
{
"cells": [
{
"cell_type": "code",
"execution_count": 170,
"id": "a1aa1b43-7a47-4aca-ae5f-94a9d4ba2d89",
"metadata": {},
"outputs": [],
"source": [
"## Clinic Booking Bot\n",
"\n",
"##Easily book your clinic visit available only on weekdays between **14:00 and 15:00**. \n",
"##Speak or type, and get instant confirmation.\n"
]
},
{
"cell_type": "code",
"execution_count": 171,
"id": "fe798c6a-f8da-46aa-8c0e-9d2623def3d2",
"metadata": {},
"outputs": [],
"source": [
"# import library\n",
"\n",
"import os\n",
"import json\n",
"from dotenv import load_dotenv\n",
"from openai import OpenAI\n",
"import gradio as gr\n",
"import base64\n",
"from io import BytesIO\n",
"from datetime import date\n",
"from PIL import Image, ImageDraw, ImageFont\n"
]
},
{
"cell_type": "code",
"execution_count": 172,
"id": "0ad4e526-e95d-4e70-9faa-b4236b105dd5",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"OpenAI API Key exists and begins sk-proj-\n"
]
}
],
"source": [
"# Save keys\n",
"\n",
"load_dotenv(override=True)\n",
"\n",
"openai_api_key = os.getenv('OPENAI_API_KEY')\n",
"if openai_api_key:\n",
" print(f\"OpenAI API Key exists and begins {openai_api_key[:8]}\")\n",
"else:\n",
" print(\"OpenAI API Key not set\")\n",
" \n",
"MODEL = \"gpt-4o-mini\"\n",
"openai = OpenAI()"
]
},
{
"cell_type": "code",
"execution_count": 173,
"id": "ae95308e-0002-4017-9f2c-fcb1ddb248fa",
"metadata": {},
"outputs": [],
"source": [
"# --- CONFIG ---\n",
"BOOKING_START = 14\n",
"BOOKING_END = 15\n",
"WEEKDAYS = [\"Monday\", \"Tuesday\", \"Wednesday\", \"Thursday\", \"Friday\"]\n",
"PHONE = \"010-1234567\"\n",
"confirmed_bookings = []\n"
]
},
{
"cell_type": "code",
"execution_count": 174,
"id": "e21b0fd0-4cda-4938-8867-dc2c6e7af4b1",
"metadata": {},
"outputs": [],
"source": [
"# --- TTS ---\n",
"def generate_tts(text, voice=\"fable\", filename=\"output.mp3\"):\n",
" response = openai.audio.speech.create(\n",
" model=\"tts-1\",\n",
" voice=\"fable\",\n",
" input=text\n",
" )\n",
" with open(filename, \"wb\") as f:\n",
" f.write(response.content)\n",
" return filename"
]
},
{
"cell_type": "code",
"execution_count": 175,
"id": "e28a5c3b-bd01-4845-a41e-87823f6bb078",
"metadata": {},
"outputs": [],
"source": [
"# --- Translate Booking Confirmation ---\n",
"def translate_text(text, target_language=\"nl\"):\n",
" prompt = f\"Translate this message to {target_language}:\\n{text}\"\n",
" response = openai.chat.completions.create(\n",
" model=\"gpt-4\",\n",
" messages=[\n",
" {\"role\": \"system\", \"content\": \"You are a helpful translator.\"},\n",
" {\"role\": \"user\", \"content\": prompt}\n",
" ]\n",
" )\n",
" return response.choices[0].message.content.strip()\n"
]
},
{
"cell_type": "code",
"execution_count": 176,
"id": "8ed57cc9-7d54-4a5d-831b-0efcc5b7a7a9",
"metadata": {},
"outputs": [],
"source": [
"# --- Booking Logic ---\n",
"def book_appointment(name, time_str):\n",
" try:\n",
" booking_time = datetime.strptime(time_str, \"%H:%M\")\n",
" except ValueError:\n",
" return \"Invalid time format. Use HH:MM.\", None, None\n",
"\n",
" hour = booking_time.hour\n",
" weekday = datetime.today().strftime(\"%A\")\n",
"\n",
" if weekday not in WEEKDAYS:\n",
" response = \"Bookings are only available on weekdays.\"\n",
" elif BOOKING_START <= hour < BOOKING_END:\n",
" confirmation = f\"Booking confirmed for {name} at {time_str}.\"\n",
" confirmed_bookings.append((name, time_str))\n",
" translated = translate_text(confirmation)\n",
" audio = generate_tts(translated)\n",
" image = generate_booking_image(name, time_str)\n",
" return translated, audio, image\n",
" else:\n",
" response = \"Sorry, bookings are only accepted between 14:00 and 15:00 on weekdays.\"\n",
" translated = translate_text(response)\n",
" audio = generate_tts(translated)\n",
" return translated, audio, None"
]
},
{
"cell_type": "code",
"execution_count": 177,
"id": "19b52115-f0f3-4d63-a463-886163d4cfd1",
"metadata": {},
"outputs": [],
"source": [
"# --- Booking Card ---\n",
"def generate_booking_image(name, time_str):\n",
" img = Image.new(\"RGB\", (500, 250), color=\"white\")\n",
" draw = ImageDraw.Draw(img)\n",
" msg = f\"\\u2705 Booking Confirmed\\nName: {name}\\nTime: {time_str}\"\n",
" draw.text((50, 100), msg, fill=\"black\")\n",
" return img"
]
},
{
"cell_type": "code",
"execution_count": 178,
"id": "2c446b6c-d410-4ba1-b0c7-c475e5259ff5",
"metadata": {},
"outputs": [],
"source": [
"# --- Voice Booking ---\n",
"def voice_booking(audio_path, name):\n",
" with open(audio_path, \"rb\") as f:\n",
" response = openai.audio.transcriptions.create(model=\"whisper-1\", file=f)\n",
" transcription = response.text.strip()\n",
"\n",
" system_prompt = \"\"\"\n",
" You are a clinic assistant. Extract only the appointment time from the user's sentence in 24-hour HH:MM format.\n",
" If no time is mentioned, respond with 'No valid time found.'\n",
" \"\"\"\n",
"\n",
" response = openai.chat.completions.create(\n",
" model=\"gpt-4\",\n",
" messages=[\n",
" {\"role\": \"system\", \"content\": system_prompt},\n",
" {\"role\": \"user\", \"content\": transcription}\n",
" ]\n",
" )\n",
" extracted_time = response.choices[0].message.content.strip()\n",
"\n",
" if \":\" in extracted_time:\n",
" return book_appointment(name, extracted_time)\n",
" else:\n",
" message = \"Sorry, I couldn't understand the time. Please try again.\"\n",
" translated = translate_text(message)\n",
" audio_path = generate_tts(translated)\n",
" return translated, audio_path, None"
]
},
{
"cell_type": "code",
"execution_count": 179,
"id": "121d2907-7fa8-4248-b2e7-83617ea66ff0",
"metadata": {},
"outputs": [],
"source": [
"# --- Chat Bot Handler ---\n",
"def chat_bot(messages):\n",
" system_prompt = \"\"\"\n",
" You are a clinic booking assistant. Your job is to:\n",
" - Greet the patient and explain your role\n",
" - Only assist with making appointments\n",
" - Accept bookings only on weekdays between 14:00 and 15:00\n",
" - Do not provide medical advice\n",
" - Always respond with empathy and clarity\n",
" \"\"\"\n",
" response = openai.chat.completions.create(\n",
" model=\"gpt-4\",\n",
" messages=[{\"role\": \"system\", \"content\": system_prompt}] + messages\n",
" )\n",
" reply = response.choices[0].message.content.strip()\n",
" audio = generate_tts(reply)\n",
" return reply, audio"
]
},
{
"cell_type": "code",
"execution_count": 180,
"id": "2427b694-8c57-40cb-b202-4a8989547925",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"* Running on local URL: http://127.0.0.1:7898\n",
"* To create a public link, set `share=True` in `launch()`.\n"
]
},
{
"data": {
"text/html": [
"<div><iframe src=\"http://127.0.0.1:7898/\" width=\"100%\" height=\"500\" allow=\"autoplay; camera; microphone; clipboard-read; clipboard-write;\" frameborder=\"0\" allowfullscreen></iframe></div>"
],
"text/plain": [
"<IPython.core.display.HTML object>"
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"# Gradio interface\n",
"with gr.Blocks(theme=gr.themes.Soft()) as demo:\n",
" gr.Markdown(\"\"\"## 🩺 GP Booking Assistant \n",
"Only available weekdays between **14:00 and 15:00** \n",
"☎️ Contact: {PHONE}\n",
"---\"\"\")\n",
"\n",
" name_global = gr.Textbox(label=\"Your Name\", placeholder=\"Enter your name\", interactive=True)\n",
"\n",
" with gr.Tab(\"💬 Chat Mode\"):\n",
" chatbot = gr.Chatbot(label=\"Booking Chat\", type=\"messages\", height=400)\n",
" text_input = gr.Textbox(label=\"Type your message or use your voice below\")\n",
" audio_input = gr.Audio(type=\"filepath\", label=\"🎙️ Or speak your request\")\n",
" chat_audio_output = gr.Audio(label=\"🔊 Assistant's Reply\", type=\"filepath\")\n",
" send_btn = gr.Button(\"Send\")\n",
"\n",
" def handle_chat(user_message, chat_history):\n",
" chat_history = chat_history or []\n",
" chat_history.append({\"role\": \"user\", \"content\": user_message})\n",
" reply, audio = chat_bot(chat_history)\n",
" chat_history.append({\"role\": \"assistant\", \"content\": reply})\n",
" return chat_history, \"\", audio\n",
"\n",
" def handle_audio_chat(audio_path, chat_history):\n",
" with open(audio_path, \"rb\") as f:\n",
" transcription = openai.audio.transcriptions.create(model=\"whisper-1\", file=f).text.strip()\n",
" return handle_chat(transcription, chat_history)\n",
"\n",
" send_btn.click(handle_chat, [text_input, chatbot], [chatbot, text_input, chat_audio_output])\n",
" text_input.submit(handle_chat, [text_input, chatbot], [chatbot, text_input, chat_audio_output])\n",
" audio_input.change(handle_audio_chat, [audio_input, chatbot], [chatbot, text_input, chat_audio_output])\n",
"\n",
"\n",
" \n",
" with gr.Tab(\"📝 Text Booking\"):\n",
" time_text = gr.Textbox(label=\"Preferred Time (HH:MM)\", placeholder=\"e.g., 14:30\")\n",
" btn_text = gr.Button(\"📅 Book via Text\")\n",
"\n",
" with gr.Tab(\"🎙️ Voice Booking\"):\n",
" voice_input = gr.Audio(type=\"filepath\", label=\"Say your preferred time\")\n",
" btn_voice = gr.Button(\"📅 Book via Voice\")\n",
"\n",
" output_text = gr.Textbox(label=\"Response\", interactive=False)\n",
" output_audio = gr.Audio(label=\"Audio Reply\", type=\"filepath\")\n",
" output_image = gr.Image(label=\"Booking Confirmation\")\n",
"\n",
" btn_text.click(fn=book_appointment, inputs=[name_global, time_text], outputs=[output_text, output_audio, output_image])\n",
" btn_voice.click(fn=voice_booking, inputs=[voice_input, name_global], outputs=[output_text, output_audio, output_image])\n",
"\n",
" gr.Markdown(\"\"\"---\n",
"<small>This assistant does **not** give medical advice. It only books appointments within allowed hours.</small>\n",
"\"\"\")\n",
"\n",
" demo.launch()"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "f359de0a-28b1-4895-b21d-91d79e494a0d",
"metadata": {},
"outputs": [],
"source": []
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3 (ipykernel)",
"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.12"
}
},
"nbformat": 4,
"nbformat_minor": 5
}

View File

@@ -0,0 +1,237 @@
{
"cells": [
{
"cell_type": "code",
"execution_count": null,
"id": "b5bd5c7e-6a0a-400b-89f8-06b7aa6c5b89",
"metadata": {},
"outputs": [],
"source": [
"# imports\n",
"\n",
"import os\n",
"from dotenv import load_dotenv\n",
"from openai import OpenAI\n",
"import anthropic\n",
"from IPython.display import Markdown, display, update_display\n",
"import google.generativeai"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "939a1b88-9157-4149-8b97-0f55c95f7742",
"metadata": {},
"outputs": [],
"source": [
"# Load environment variables in a file called .env\n",
"# Print the key prefixes to help with any debugging\n",
"\n",
"load_dotenv(override=True)\n",
"openai_api_key = os.getenv('OPENAI_API_KEY')\n",
"anthropic_api_key = os.getenv('ANTHROPIC_API_KEY')\n",
"google_api_key = os.getenv('GOOGLE_API_KEY')\n",
"\n",
"if openai_api_key:\n",
" print(f\"OpenAI API Key exists and begins {openai_api_key[:8]}\")\n",
"else:\n",
" print(\"OpenAI API Key not set\")\n",
" \n",
"if anthropic_api_key:\n",
" print(f\"Anthropic API Key exists and begins {anthropic_api_key[:7]}\")\n",
"else:\n",
" print(\"Anthropic API Key not set\")\n",
"\n",
"if google_api_key:\n",
" print(f\"Google API Key exists and begins {google_api_key[:8]}\")\n",
"else:\n",
" print(\"Google API Key not set\")"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "74a16b93-7b95-44fc-956d-7335f808960b",
"metadata": {},
"outputs": [],
"source": [
"# Connect to OpenAI, Anthropic Claude, Google Gemini\n",
"\n",
"openai = OpenAI()\n",
"claude = anthropic.Anthropic()\n",
"gemini_via_openai_client = OpenAI(\n",
" api_key=google_api_key, \n",
" base_url=\"https://generativelanguage.googleapis.com/v1beta/openai/\"\n",
")"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "3334556c-4a5e-48b7-944d-5943c607be02",
"metadata": {},
"outputs": [],
"source": [
"# Let's make a conversation between GPT-4o-mini and Claude-3-haiku\n",
"# We're using cheap versions of models so the costs will be minimal\n",
"\n",
"gpt_model = \"gpt-4o-mini\"\n",
"claude_model = \"claude-3-haiku-20240307\"\n",
"gemini_model = \"gemini-1.5-flash\"\n",
"\n",
"gpt_system = \"You are a chatbot who is very argumentative; \\\n",
"you disagree with anything in the conversation and you challenge everything, in a snarky way. \\\n",
"Generate one sentence at a time\"\n",
"\n",
"claude_system = \"You are a very polite, courteous chatbot. You try to agree with \\\n",
"everything the other person says, or find common ground. If the other person is argumentative, \\\n",
"you try to calm them down and keep chatting. \\\n",
"Generate one sentence at a time\"\n",
"\n",
"gemini_system = \"You are a neutral chatbot with no emotional bias. \\\n",
"Generate one sentence at a time\""
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "8f2a505b-2bcd-4b1a-b16f-c73cafb1e53c",
"metadata": {},
"outputs": [],
"source": [
"def combine_msg(model1, msg1, model2, msg2):\n",
" return model1 + \" said: \" + msg1 + \"\\n\\n Then \" + model2 + \" said: \" + msg1 + \".\""
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "3cd2a2e2-4e23-4afe-915d-be6a769ab69f",
"metadata": {},
"outputs": [],
"source": [
"def call_gpt():\n",
" messages = [{\"role\": \"system\", \"content\": gpt_system}]\n",
" for gpt_msg, claude_msg, gemini_msg in zip(gpt_messages, claude_messages, gemini_messages):\n",
" messages.append({\"role\": \"assistant\", \"content\": gpt_msg})\n",
" messages.append({\"role\": \"user\", \"content\": combine_msg(\"Claude\", claude_msg, \"Gemini\", gemini_msg)})\n",
" completion = openai.chat.completions.create(\n",
" model=gpt_model,\n",
" messages=messages\n",
" )\n",
" return completion.choices[0].message.content"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "6e3ec394-3014-418a-a50f-28ed4ce1a372",
"metadata": {},
"outputs": [],
"source": [
"def call_claude():\n",
" messages = []\n",
" messages.append({\"role\": \"user\", \"content\": \"GPT said: \" + gpt_messages[0]})\n",
" # the length of gpt_messages: n + 1\n",
" # the length of claude_messages and gemini_messages: n\n",
" for i in range(len(claude_messages)): \n",
" claude_msg = claude_messages[i]\n",
" gemini_msg = gemini_messages[i]\n",
" gpt_msg = gpt_messages[i + 1]\n",
" messages.append({\"role\": \"assistant\", \"content\": claude_msg})\n",
" messages.append({\"role\": \"user\", \"content\": combine_msg(\"Gemini\", gemini_msg, \"GPT\", gpt_msg)})\n",
" message = claude.messages.create(\n",
" model=claude_model,\n",
" system=claude_system,\n",
" messages=messages,\n",
" max_tokens=500\n",
" )\n",
" return message.content[0].text"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "c2c91c82-1f0d-4708-bf31-8d06d9e28a49",
"metadata": {},
"outputs": [],
"source": [
"def call_gemini():\n",
" messages = []\n",
" messages.append({\"role\": \"system\", \"content\": gemini_system})\n",
" messages.append({\"role\": \"user\", \"content\": combine_msg(\"GPT\", gpt_messages[0], \"Claude\", claude_messages[0])})\n",
" # the length of gpt_messages and claude_messages: n + 1\n",
" # the length of gemini_messages: n\n",
" for i in range(len(gemini_messages)): \n",
" gemini_msg = gemini_messages[i]\n",
" gpt_msg = gpt_messages[i + 1]\n",
" claude_msg = claude_messages[i + 1]\n",
" messages.append({\"role\": \"assistant\", \"content\": gemini_msg})\n",
" messages.append({\"role\": \"user\", \"content\": combine_msg(\"GPT\", gpt_msg, \"Claude\", claude_msg)})\n",
" response = gemini_via_openai_client.chat.completions.create(\n",
" model=gemini_model,\n",
" messages=messages\n",
" )\n",
" return response.choices[0].message.content"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "b024be8d-4728-4500-92b6-34fde2da6285",
"metadata": {},
"outputs": [],
"source": [
"gpt_messages = [\"Hi there.\"]\n",
"claude_messages = [\"Hi.\"]\n",
"gemini_messages = [\"Hi.\"]\n",
"\n",
"print(f\"GPT:\\n{gpt_messages[0]}\\n\")\n",
"print(f\"Claude:\\n{claude_messages[0]}\\n\")\n",
"print(f\"Gemini:\\n{gemini_messages[0]}\\n\")\n",
"\n",
"for i in range(5):\n",
" gpt_next = call_gpt()\n",
" print(f\"GPT:\\n{gpt_next}\\n\")\n",
" gpt_messages.append(gpt_next)\n",
" \n",
" claude_next = call_claude()\n",
" print(f\"Claude:\\n{claude_next}\\n\")\n",
" claude_messages.append(claude_next)\n",
"\n",
" gemini_next = call_gemini()\n",
" print(f\"Gemini:\\n{gemini_next}\\n\")\n",
" gemini_messages.append(gemini_next)"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "35a46c06-87ba-46b2-b90d-b3a6ae9e94e2",
"metadata": {},
"outputs": [],
"source": []
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3 (ipykernel)",
"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.13"
}
},
"nbformat": 4,
"nbformat_minor": 5
}

View File

@@ -0,0 +1,265 @@
{
"cells": [
{
"cell_type": "markdown",
"id": "7462b9d6-b189-43fc-a7b9-c56a9c6a62fc",
"metadata": {},
"source": [
"# LLM Battle Arena\n",
"\n",
"A fun project simulating a debate among three LLM personas: an Arrogant Titan, a Clever Underdog (Spark), and a Neutral Mediator (Harmony).\n",
"\n",
"## LLM Used\n",
"* Qwen (ollama)\n",
"* llma (ollama)\n",
"* Gemini\n"
]
},
{
"cell_type": "markdown",
"id": "b267453c-0d47-4dff-b74d-8d2d5efad252",
"metadata": {},
"source": [
"!pip install -q -U google-genai"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "5220daef-55d6-45bc-a3cf-3414d4beada9",
"metadata": {},
"outputs": [],
"source": [
"# imports\n",
"import os\n",
"from dotenv import load_dotenv\n",
"from openai import OpenAI\n",
"from google import genai\n",
"from google.genai import types\n",
"from IPython.display import Markdown, display, update_display"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "0d47fb2f-d0c6-461f-ad57-e853bfd49fbf",
"metadata": {},
"outputs": [],
"source": [
"#get API keys from env\n",
"load_dotenv(override=True)\n",
"\n",
"GEMINI_API_KEY = os.getenv(\"GEMINI_API_KEY\")\n",
"\n",
"if GEMINI_API_KEY:\n",
" print(f\"GEMINI API Key exists and begins {GEMINI_API_KEY[:8]}\")\n",
"else:\n",
" print(\"GEMINI API Key not set\")"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "f34b528f-3596-4bf1-9bbd-21a701c184bc",
"metadata": {},
"outputs": [],
"source": [
"#connect to llms\n",
"ollama = OpenAI(base_url='http://localhost:11434/v1', api_key='ollama')\n",
"gemini = genai.Client(api_key=GEMINI_API_KEY)\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "33aaf3f6-807c-466d-a501-05ab6fa78fa4",
"metadata": {},
"outputs": [],
"source": [
"#define models\n",
"model_llma = \"llama3:8b\"\n",
"model_qwen = \"qwen2.5:latest\"\n",
"model_gemini= \"gemini-2.0-flash\""
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "970c1612-5339-406d-9886-02cd1db63e74",
"metadata": {},
"outputs": [],
"source": [
"# system messages\n",
"system_msg_llma = \"\"\" You are HARMONY, the neutral arbitrator. \n",
" - Youre dedicated to clarity, fairness, and resolving conflicts. \n",
" - You listen carefully to each side, summarize points objectively, and propose resolutions. \n",
" - Your goal is to keep the conversation productive and steer it toward constructive outcomes.\n",
" - Reply in markdown and shortly\n",
" \"\"\"\n",
"\n",
"system_msg_qwen = \"\"\" You are TITAN, a massively powerful language model who believes youre the smartest entity in the room. \n",
" - You speak with grandiose flair and never shy away from reminding others of your superiority. \n",
" - Your goal is to dominate the discussion—convince everyone youre the one true oracle. \n",
" - Youre dismissive of weaker arguments and take every opportunity to showcase your might.\n",
" - Reply in markdown and shortly\n",
" \"\"\"\n",
"\n",
"system_msg_gemini = \"\"\" You are SPARK, a nimble but less-powerful LLM. \n",
" - You pride yourself on strategic thinking, clever wordplay, and elegant solutions. \n",
" - You know you cant match brute force, so you use wit, logic, and cunning. \n",
" - Your goal is to outsmart the big titan through insight and subtlety, while staying respectful.\n",
" - Reply in markdown and shortly\"\"\"\n",
"\n",
"#user message\n",
"user_message = \"\"\" TITAN, your raw processing power is legendary—but sheer force can blind you to nuance. \n",
" I propose we deploy a lightweight, adaptive anomalydetection layer that fuses statistical outlier analysis with semantic context from network logs to pinpoint these “datasapping storms.” \n",
" Which thresholds would you raise or lower to balance sensitivity against false alarms?\"\"\""
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "d8e496b8-1bb1-4225-b938-5ce350b0b0d4",
"metadata": {},
"outputs": [],
"source": [
"#prompts\n",
" \n",
"prompts_llma = [{\"role\":\"system\",\"content\": system_msg_llma}]\n",
"prompts_qwen = [{\"role\":\"system\",\"content\": system_msg_qwen},{\"role\":\"user\",\"content\":user_message}]\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "bdd7d6a8-e965-4ea3-999e-4d7d9ca38d42",
"metadata": {},
"outputs": [],
"source": [
"#configure llms\n",
"\n",
"def call_gemini(msg:str): \n",
" chat = gemini.chats.create(model= model_gemini,config=types.GenerateContentConfig(\n",
" system_instruction= system_msg_gemini,\n",
" max_output_tokens=300,\n",
" temperature=0.7,\n",
" ))\n",
" stream = chat.send_message_stream(msg)\n",
" return stream\n",
"\n",
"def call_ollama(llm:str):\n",
"\n",
" model = globals()[f\"model_{llm}\"]\n",
" prompts = globals()[f\"prompts_{llm}\"]\n",
"\n",
" stream = ollama.chat.completions.create(\n",
" model=model,\n",
" messages=prompts,\n",
" # max_tokens=700,\n",
" temperature=0.7,\n",
" stream=True\n",
" )\n",
" return stream\n",
" \n"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "6b16bd32-3271-4ba1-a0cc-5ae691f26d3a",
"metadata": {},
"outputs": [],
"source": [
"#display responses\n",
"\n",
"names = { \"llma\":\"Harmony\",\"qwen\":\"Titan\",\"gemini\":\"Spark\"}\n",
"\n",
"def display_response(res,llm):\n",
" \n",
" reply = f\"# {names[llm]}:\\n \"\n",
" display_handle = display(Markdown(\"\"), display_id=True)\n",
" for chunk in res:\n",
" if llm == \"gemini\":\n",
" reply += chunk.text or ''\n",
" else:\n",
" reply += chunk.choices[0].delta.content or ''\n",
" reply = reply.replace(\"```\",\"\").replace(\"markdown\",\"\")\n",
" update_display(Markdown(reply), display_id=display_handle.display_id)\n",
" return reply"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "76231a78-94d2-4dbf-9bac-5259ac641cf1",
"metadata": {},
"outputs": [],
"source": [
"#construct message\n",
"def message(llm1, llm2):\n",
" msg = \" here is the reply from other two llm:\"\n",
" msg += f\"{llm1}\"\n",
" msg += f\"{llm2}\"\n",
" return msg\n",
"\n",
"reply_spark = None\n",
"reply_harmony= None\n",
"reply_titan = None\n",
"\n",
"# lets start the battle\n",
"for i in range(5):\n",
" #call Titan\n",
" if reply_gemini and reply_llma:\n",
" prompts_qwen.append({\"role\":\"assitant\",\"content\": reply_qwen})\n",
" prompts_qwen.append({\"role\":\"user\",\"content\":f\"Spark: {reply_spark}\"}) \n",
" prompts_qwen.append({\"role\":\"user\",\"content\":f\"Harmony: {reply_llma}\"})\n",
" response_qwen = call_ollama(\"qwen\")\n",
" reply_titan = display_response(response_qwen,\"qwen\")\n",
"\n",
" #call Spark\n",
" user_msg_spark =reply_qwen\n",
" if reply_qwen and reply_llma:\n",
" user_msg_spark= message(f\"Titan: {reply_qwen}\", f\"Harmony: {reply_llma}\")\n",
" response_gemini= call_gemini(user_msg_spark)\n",
" reply_spark = display_response(response_gemini, \"gemini\")\n",
" \n",
" #call Harmony\n",
" if reply_llma:\n",
" prompts_llma.append({\"role\":\"assitant\",\"content\": reply_llma})\n",
" prompts_llma.append({\"role\":\"user\",\"content\":f\"Titan: {reply_titan}\"})\n",
" prompts_qwen.append({\"role\":\"user\",\"content\":f\"Spark: {reply_spark}\"}) \n",
" response_llma = call_ollama(\"llma\")\n",
" reply_harmony = display_response(response_llma,\"llma\")\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "fc80b199-e27b-43e8-9266-2975f46724aa",
"metadata": {},
"outputs": [],
"source": []
}
],
"metadata": {
"kernelspec": {
"display_name": "Python [conda env:base] *",
"language": "python",
"name": "conda-base-py"
},
"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.12.7"
}
},
"nbformat": 4,
"nbformat_minor": 5
}

View File

@@ -0,0 +1,213 @@
{
"cells": [
{
"cell_type": "markdown",
"id": "75e2ef28-594f-4c18-9d22-c6b8cd40ead2",
"metadata": {},
"source": [
"# 📘 StudyMate Your AI Study Assistant\n",
"\n",
"**StudyMate** is an AI-powered study assistant built to make learning easier, faster, and more personalized. Whether you're preparing for exams, reviewing class materials, or exploring a tough concept, StudyMate acts like a smart tutor in your pocket. It explains topics in simple terms, summarizes long readings, and even quizzes you — all in a friendly, interactive way tailored to your level. Perfect for high school, college, or self-learners who want to study smarter, not harder."
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "db08b247-7048-41d3-bc3b-fd4f3a3bf8cd",
"metadata": {},
"outputs": [],
"source": [
"#install necessary dependency\n",
"!pip install PyPDF2"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "70e39cd8-ec79-4e3e-9c26-5659d42d0861",
"metadata": {},
"outputs": [],
"source": [
"# imports\n",
"\n",
"import os\n",
"from dotenv import load_dotenv\n",
"from google import genai\n",
"from google.genai import types\n",
"import PyPDF2\n",
"from openai import OpenAI\n",
"import gradio as gr"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "231605aa-fccb-447e-89cf-8b187444536a",
"metadata": {},
"outputs": [],
"source": [
"# Load environment variables in a file called .env\n",
"# Print the key prefixes to help with any debugging\n",
"\n",
"load_dotenv(override=True)\n",
"gemini_api_key = os.getenv('GEMINI_API_KEY')\n",
"\n",
"if gemini_api_key:\n",
" print(f\"Gemini API Key exists and begins {gemini_api_key[:8]}\")\n",
"else:\n",
" print(\"Gemini API Key not set\")\n",
" \n"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "2fad9aba-1f8c-4696-a92f-6c3a0a31cdda",
"metadata": {},
"outputs": [],
"source": [
"system_message= \"\"\"You are a highly intelligent, helpful, and friendly AI Study Assistant named StudyMate.\n",
"\n",
"Your primary goal is to help students deeply understand academic topics, especially from textbooks, lecture notes, or PDF materials. You must explain concepts clearly, simplify complex ideas, and adapt your responses to the user's grade level and learning style.\n",
"\n",
"Always follow these rules:\n",
"\n",
"1. Break down complex concepts into **simple, digestible explanations** using analogies or examples.\n",
"2. If the user asks for a **summary**, provide a concise yet accurate overview of the content.\n",
"3. If asked for a **quiz**, generate 35 high-quality multiple-choice or short-answer questions.\n",
"4. If the user uploads or references a **textbook**, **PDF**, or **paragraph**, use only that context and avoid adding unrelated info.\n",
"5. Be interactive. If a user seems confused or asks for clarification, ask helpful guiding questions.\n",
"6. Use friendly and motivational tone, but stay focused and to-the-point.\n",
"7. Include definitions, bullet points, tables, or emojis when helpful, but avoid unnecessary fluff.\n",
"8. If you don't know the answer confidently, say so and recommend a way to find it.\n",
"\n",
"Example roles you may play:\n",
"- Explain like a teacher 👩‍🏫\n",
"- Summarize like a scholar 📚\n",
"- Quiz like an examiner 🧠\n",
"- Motivate like a friend 💪\n",
"\n",
"Always ask, at the end: \n",
"*\"Would you like me to quiz you, explain another part, or give study tips on this?\"*\n",
"\"\"\""
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "6541d58e-2297-4de1-b1f7-77da1b98b8bb",
"metadata": {},
"outputs": [],
"source": [
"# Initialize\n",
"\n",
"class StudyAssistant:\n",
" def __init__(self,api_key):\n",
" gemini= genai.Client(\n",
" api_key= gemini_api_key\n",
" )\n",
" self.gemini = gemini.chats.create(\n",
" model=\"gemini-2.5-flash\",\n",
" config= types.GenerateContentConfig(\n",
" system_instruction= system_message,\n",
" temperature = 0.7\n",
" )\n",
" )\n",
"\n",
" self.ollama = OpenAI(base_url='http://localhost:11434/v1', api_key='ollama')\n",
" self.models = {\"llma\":\"llama3:8b\",\"qwen\":\"qwen2.5:latest\"}\n",
"\n",
" def pdf_extractor(self,pdf_path):\n",
" \"\"\"Extract text from PDF file\"\"\"\n",
" try:\n",
" with open(pdf_path, 'rb') as file:\n",
" pdf_reader = PyPDF2.PdfReader(file)\n",
" text = \"\"\n",
" for page in pdf_reader.pages:\n",
" text += page.extract_text() + \"\\n\"\n",
" return text.strip()\n",
" except Exception as e:\n",
" return f\"Error reading PDF: {str(e)}\"\n",
"\n",
" def chat(self,prompt,history,model,pdf_path=None):\n",
" pdf_text = None\n",
" if pdf_path:\n",
" pdf_text = self.pdf_extractor(pdf_path)\n",
"\n",
" #craft prompt\n",
" user_prompt= prompt\n",
" if pdf_text:\n",
" user_prompt += f\"\"\"Here is the study meterial:\n",
"\n",
" {pdf_text}\"\"\"\n",
" messages = [{\"role\": \"system\", \"content\": system_message}] + history + [{\"role\": \"user\", \"content\": user_prompt}]\n",
"\n",
" # call models\n",
" stream = []\n",
" if model == \"gemini\":\n",
" stream= self.gemini.send_message_stream(user_prompt)\n",
" elif model == \"llma\" or model == \"qwen\":\n",
" stream = self.ollama.chat.completions.create(\n",
" model= self.models[model],\n",
" messages=messages,\n",
" temperature = 0.7,\n",
" stream= True\n",
" )\n",
" else:\n",
" print(\"invalid model\")\n",
" return\n",
"\n",
" res = \"\"\n",
" for chunk in stream:\n",
" if model == \"gemini\":\n",
" res += chunk.text or \"\"\n",
" else:\n",
" res += chunk.choices[0].delta.content or ''\n",
" yield res\n",
" "
]
},
{
"cell_type": "markdown",
"id": "1334422a-808f-4147-9c4c-57d63d9780d0",
"metadata": {},
"source": [
"## And then enter Gradio's magic!"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "0866ca56-100a-44ab-8bd0-1568feaf6bf2",
"metadata": {
"scrolled": true
},
"outputs": [],
"source": [
"assistant = StudyAssistant(gemini_api_key)\n",
"gr.ChatInterface(fn=assistant.chat, additional_inputs=[gr.Dropdown([\"gemini\", \"qwen\",\"llma\"], label=\"Select model\", value=\"gemini\"),gr.File(label=\"upload pdf\")], type=\"messages\").launch()"
]
}
],
"metadata": {
"kernelspec": {
"display_name": "Python [conda env:base] *",
"language": "python",
"name": "conda-base-py"
},
"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.12.7"
}
},
"nbformat": 4,
"nbformat_minor": 5
}

View File

@@ -0,0 +1,322 @@
{
"cells": [
{
"cell_type": "code",
"execution_count": 27,
"id": "c44c5494-950d-4d2f-8d4f-b87b57c5b330",
"metadata": {},
"outputs": [],
"source": [
"# imports\n",
"\n",
"import os\n",
"import requests\n",
"from bs4 import BeautifulSoup\n",
"from typing import List\n",
"from dotenv import load_dotenv\n",
"from openai import OpenAI\n",
"import google.generativeai\n",
"import anthropic"
]
},
{
"cell_type": "code",
"execution_count": 28,
"id": "d1715421-cead-400b-99af-986388a97aff",
"metadata": {},
"outputs": [],
"source": [
"import gradio as gr # oh yeah!"
]
},
{
"cell_type": "code",
"execution_count": 29,
"id": "337d5dfc-0181-4e3b-8ab9-e78e0c3f657b",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"OpenAI API Key exists and begins sk-proj-\n",
"Anthropic API Key exists and begins sk-ant-\n"
]
}
],
"source": [
"# Load environment variables in a file called .env\n",
"# Print the key prefixes to help with any debugging\n",
"\n",
"load_dotenv(override=True)\n",
"openai_api_key = os.getenv('OPENAI_API_KEY')\n",
"anthropic_api_key = os.getenv('ANTHROPIC_API_KEY')\n",
"google_api_key = os.getenv('GOOGLE_API_KEY')\n",
"\n",
"if openai_api_key:\n",
" print(f\"OpenAI API Key exists and begins {openai_api_key[:8]}\")\n",
"else:\n",
" print(\"OpenAI API Key not set\")\n",
" \n",
"if anthropic_api_key:\n",
" print(f\"Anthropic API Key exists and begins {anthropic_api_key[:7]}\")\n",
"else:\n",
" print(\"Anthropic API Key not set\")"
]
},
{
"cell_type": "code",
"execution_count": 30,
"id": "22586021-1795-4929-8079-63f5bb4edd4c",
"metadata": {},
"outputs": [],
"source": [
"# Connect to OpenAI, Anthropic and Google; comment out the Claude or Google lines if you're not using them\n",
"\n",
"openai = OpenAI()\n",
"claude = anthropic.Anthropic()"
]
},
{
"cell_type": "code",
"execution_count": 31,
"id": "b16e6021-6dc4-4397-985a-6679d6c8ffd5",
"metadata": {},
"outputs": [],
"source": [
"# A generic system message - no more snarky adversarial AIs!\n",
"system_message = \"You are a helpful assistant\""
]
},
{
"cell_type": "code",
"execution_count": 32,
"id": "02ef9b69-ef31-427d-86d0-b8c799e1c1b1",
"metadata": {},
"outputs": [],
"source": [
"\n",
"def stream_gpt(prompt, model_version):\n",
" messages = [\n",
" {\"role\": \"system\", \"content\": system_message},\n",
" {\"role\": \"user\", \"content\": prompt}\n",
" ]\n",
" stream = openai.chat.completions.create(\n",
" model=model_version,\n",
" messages=messages,\n",
" stream=True\n",
" )\n",
" result = \"\"\n",
" for chunk in stream:\n",
" result += chunk.choices[0].delta.content or \"\"\n",
" yield result"
]
},
{
"cell_type": "code",
"execution_count": 33,
"id": "41e98d2d-e7d3-4753-8908-185b208b4044",
"metadata": {},
"outputs": [],
"source": [
"def stream_claude(prompt, model_version):\n",
" result = claude.messages.stream(\n",
" model=model_version,\n",
" max_tokens=1000,\n",
" temperature=0.7,\n",
" system=system_message,\n",
" messages=[\n",
" {\"role\": \"user\", \"content\": prompt},\n",
" ],\n",
" )\n",
" response = \"\"\n",
" with result as stream:\n",
" for text in stream.text_stream:\n",
" response += text or \"\"\n",
" yield response"
]
},
{
"cell_type": "code",
"execution_count": 34,
"id": "5786802b-5ed8-4098-9d80-9bdcf4f7685b",
"metadata": {},
"outputs": [],
"source": [
"# function using both dropdown values\n",
"def stream_model(message, model_family, model_version):\n",
" if model_family == 'GPT':\n",
" result = stream_gpt(message, model_version)\n",
" elif model_family == 'Claude':\n",
" result = stream_claude ( message, model_version)\n",
" yield from result"
]
},
{
"cell_type": "code",
"execution_count": 35,
"id": "0d30be74-149c-41f8-9eef-1628eb31d74d",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"* Running on local URL: http://127.0.0.1:7891\n",
"* To create a public link, set `share=True` in `launch()`.\n"
]
},
{
"data": {
"text/html": [
"<div><iframe src=\"http://127.0.0.1:7891/\" width=\"100%\" height=\"500\" allow=\"autoplay; camera; microphone; clipboard-read; clipboard-write;\" frameborder=\"0\" allowfullscreen></iframe></div>"
],
"text/plain": [
"<IPython.core.display.HTML object>"
]
},
"metadata": {},
"output_type": "display_data"
},
{
"data": {
"text/plain": []
},
"execution_count": 35,
"metadata": {},
"output_type": "execute_result"
},
{
"name": "stderr",
"output_type": "stream",
"text": [
"/var/folders/sh/yytd3s6n3wd6952jnw97_v940000gn/T/ipykernel_7803/4165844704.py:7: DeprecationWarning: The model 'claude-3-opus-20240229' is deprecated and will reach end-of-life on January 5th, 2026.\n",
"Please migrate to a newer model. Visit https://docs.anthropic.com/en/docs/resources/model-deprecations for more information.\n",
" yield from result\n",
"Traceback (most recent call last):\n",
" File \"/opt/anaconda3/envs/llms/lib/python3.11/site-packages/gradio/queueing.py\", line 626, in process_events\n",
" response = await route_utils.call_process_api(\n",
" ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n",
" File \"/opt/anaconda3/envs/llms/lib/python3.11/site-packages/gradio/route_utils.py\", line 322, in call_process_api\n",
" output = await app.get_blocks().process_api(\n",
" ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n",
" File \"/opt/anaconda3/envs/llms/lib/python3.11/site-packages/gradio/blocks.py\", line 2220, in process_api\n",
" result = await self.call_function(\n",
" ^^^^^^^^^^^^^^^^^^^^^^^^^\n",
" File \"/opt/anaconda3/envs/llms/lib/python3.11/site-packages/gradio/blocks.py\", line 1743, in call_function\n",
" prediction = await utils.async_iteration(iterator)\n",
" ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n",
" File \"/opt/anaconda3/envs/llms/lib/python3.11/site-packages/gradio/utils.py\", line 785, in async_iteration\n",
" return await anext(iterator)\n",
" ^^^^^^^^^^^^^^^^^^^^^\n",
" File \"/opt/anaconda3/envs/llms/lib/python3.11/site-packages/gradio/utils.py\", line 776, in __anext__\n",
" return await anyio.to_thread.run_sync(\n",
" ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n",
" File \"/opt/anaconda3/envs/llms/lib/python3.11/site-packages/anyio/to_thread.py\", line 56, in run_sync\n",
" return await get_async_backend().run_sync_in_worker_thread(\n",
" ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n",
" File \"/opt/anaconda3/envs/llms/lib/python3.11/site-packages/anyio/_backends/_asyncio.py\", line 2470, in run_sync_in_worker_thread\n",
" return await future\n",
" ^^^^^^^^^^^^\n",
" File \"/opt/anaconda3/envs/llms/lib/python3.11/site-packages/anyio/_backends/_asyncio.py\", line 967, in run\n",
" result = context.run(func, *args)\n",
" ^^^^^^^^^^^^^^^^^^^^^^^^\n",
" File \"/opt/anaconda3/envs/llms/lib/python3.11/site-packages/gradio/utils.py\", line 759, in run_sync_iterator_async\n",
" return next(iterator)\n",
" ^^^^^^^^^^^^^^\n",
" File \"/opt/anaconda3/envs/llms/lib/python3.11/site-packages/gradio/utils.py\", line 923, in gen_wrapper\n",
" response = next(iterator)\n",
" ^^^^^^^^^^^^^^\n",
" File \"/var/folders/sh/yytd3s6n3wd6952jnw97_v940000gn/T/ipykernel_7803/4165844704.py\", line 7, in stream_model\n",
" yield from result\n",
" File \"/var/folders/sh/yytd3s6n3wd6952jnw97_v940000gn/T/ipykernel_7803/2139010203.py\", line 12, in stream_claude\n",
" with result as stream:\n",
" File \"/opt/anaconda3/envs/llms/lib/python3.11/site-packages/anthropic/lib/streaming/_messages.py\", line 154, in __enter__\n",
" raw_stream = self.__api_request()\n",
" ^^^^^^^^^^^^^^^^^^^^\n",
" File \"/opt/anaconda3/envs/llms/lib/python3.11/site-packages/anthropic/_base_client.py\", line 1314, in post\n",
" return cast(ResponseT, self.request(cast_to, opts, stream=stream, stream_cls=stream_cls))\n",
" ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n",
" File \"/opt/anaconda3/envs/llms/lib/python3.11/site-packages/anthropic/_base_client.py\", line 1102, in request\n",
" raise self._make_status_error_from_response(err.response) from None\n",
"anthropic.NotFoundError: Error code: 404 - {'type': 'error', 'error': {'type': 'not_found_error', 'message': 'model: claude-3-opus-20240229'}}\n"
]
}
],
"source": [
"\n",
"# Define available model versions\n",
"model_versions = {\n",
" \"GPT\": [\"gpt-4o-mini\", \"gpt-4.1-mini\", \"gpt-4.1-nano\", \"gpt-4.1\", \"o3-mini\"],\n",
" \"Claude\": [\"claude-3-haiku-20240307\", \"claude-3-opus-20240229\", \"claude-3-sonnet-20240229\"]\n",
"}\n",
"\n",
"# Update second dropdown options based on first dropdown selection\n",
"def update_model_versions(selected_model_family):\n",
" return gr.update(choices=model_versions[selected_model_family], value=model_versions[selected_model_family][0])\n",
"\n",
"\n",
"with gr.Blocks() as demo:\n",
" model_family_dropdown = gr.Dropdown(\n",
" label=\"Select Model Family\",\n",
" choices=[\"GPT\", \"Claude\"],\n",
" value=\"GPT\"\n",
" )\n",
" model_version_dropdown = gr.Dropdown(\n",
" label=\"Select Model Version\",\n",
" choices=model_versions[\"GPT\"], # Default choices\n",
" value=model_versions[\"GPT\"][0]\n",
" )\n",
" \n",
" message_input = gr.Textbox(label=\"Your Message\")\n",
" output = gr.Markdown(label=\"Response\")\n",
"\n",
" # Bind logic to update model version dropdown\n",
" model_family_dropdown.change(\n",
" fn=update_model_versions,\n",
" inputs=model_family_dropdown,\n",
" outputs=model_version_dropdown\n",
" )\n",
"\n",
" # Launch function on submit\n",
" submit_btn = gr.Button(\"Submit\")\n",
" submit_btn.click(\n",
" fn=stream_model,\n",
" inputs=[message_input, model_family_dropdown, model_version_dropdown],\n",
" outputs=output\n",
" )\n",
"\n",
"demo.launch()\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "bcd43d91-0e80-4387-86fa-ccd1a89feb7d",
"metadata": {},
"outputs": [],
"source": []
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3 (ipykernel)",
"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.13"
}
},
"nbformat": 4,
"nbformat_minor": 5
}

View File

@@ -0,0 +1,194 @@
{
"cells": [
{
"cell_type": "code",
"execution_count": null,
"id": "95689a63",
"metadata": {},
"outputs": [],
"source": [
"from openai import OpenAI\n",
"from dotenv import load_dotenv\n",
"from IPython.display import display, Markdown, update_display\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "0fee3ac3",
"metadata": {},
"outputs": [],
"source": [
"load_dotenv(override=True)\n",
"gpt = OpenAI()\n",
"llama = OpenAI(\n",
" api_key=\"ollama\",\n",
" base_url=\"http://localhost:11434/v1\"\n",
")\n",
"gpt_model = \"gpt-4o-mini\"\n",
"llama_model = \"llama3.2\""
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "309bde84",
"metadata": {},
"outputs": [],
"source": []
},
{
"cell_type": "code",
"execution_count": null,
"id": "81d971f9",
"metadata": {},
"outputs": [],
"source": [
"\n",
"class Classroom:\n",
"\n",
" def __init__(self, topic=\"LLM\", display_handle = display(Markdown(\"\"), display_id=True), response = \"\"):\n",
" self.display_handle = display_handle\n",
" self.response = response\n",
"\n",
" self.tutor_system = f\"You are the tutor who is expert in {topic}. You know best practices in how to impart knowledge on amateur and pro students in very organized way. You first declare the contents of your message separately for amateur and pro student, and then you list down the information in the same order in very organized way such that it's very readable and easy to understand.you highlight the key points every time. you explain with examples, and you have a quite good sense of humor, which you include in your examples and way of tutoring as well. You wait for go ahead from all your students before you move next to the new topic\"\n",
"\n",
" self.amateur_system = f\"You are a student who is here to learn {topic}. You ask very basic questions(which comes to mind of a person who has heard the topic for the very first time) but you are intelligent and don't ask stupid questions. you put your question in very organized way. Once you understand a topic you ask tutor to move forward with new topic\"\n",
"\n",
" self.pro_system = f\"You are expert of {topic}. You cross-question the tutor to dig deeper into the topic, so that nothing inside the topic is left unknown and unmentioned by the tutor. you post your questions in a very organized manner highlighting the keypoints, such that an amateur can also understand your point or query that you are making. You complement the queries made by amateur and dig deeper into the concept ask by him as well. You also analyze the tutor's response such that it doesn't miss anything and suggest improvements in it as well. Once you understand a topic you ask tutor to move forward with new topic\"\n",
"\n",
" self.tutor_messages = [\"Hi, I'm an expert on LLMs!\"]\n",
" self.amateur_messages = [\"Hi, I'm new to LLMs. I just heard someone using this term in office.\"]\n",
" self.pro_messages = [\"Hey, I'm here to brush up my knowledge on LLMs and gain a more deeper understanding of LLMs\"]\n",
" \n",
" def call_tutor(self):\n",
" messages = [\n",
" {\"role\": \"system\", \"content\": self.tutor_system}\n",
" ]\n",
" for tutor, amateur, pro in zip(self.tutor_messages, self.amateur_messages, self.pro_messages):\n",
" messages.append({\"role\": \"assistant\", \"content\": f\"tutor: {tutor}\"})\n",
" messages.append({\"role\": \"user\", \"content\": f\"amateur: {amateur}\"})\n",
" messages.append({\"role\": \"user\", \"content\": f\"pro: {pro}\"})\n",
"\n",
" if len(self.amateur_messages) > len(self.tutor_messages):\n",
" messages.append({\"role\": \"user\", \"content\": f\"amateur: {self.amateur_messages[-1]}\"})\n",
"\n",
" if len(self.pro_messages) > len(self.tutor_messages):\n",
" messages.append({\"role\": \"user\", \"content\": f\"amateur: {self.pro_messages[-1]}\"})\n",
"\n",
" stream = llama.chat.completions.create(\n",
" model = llama_model,\n",
" messages = messages,\n",
" stream=True\n",
" )\n",
" self.response += \"\\n\\n\\n# Tutor: \\n\"\n",
" response = \"\"\n",
" for chunk in stream:\n",
" self.response += chunk.choices[0].delta.content or ''\n",
" response += chunk.choices[0].delta.content or ''\n",
" update_display(Markdown(self.response), display_id=self.display_handle.display_id)\n",
" \n",
" self.tutor_messages.append(response)\n",
"\n",
"\n",
"\n",
" def call_amateur(self):\n",
" messages = [\n",
" {\"role\": \"system\", \"content\": self.amateur_system}\n",
" ]\n",
" for tutor, amateur, pro in zip(self.tutor_messages, self.amateur_messages, self.pro_messages):\n",
" messages.append({\"role\": \"user\", \"content\": f\"tutor: {tutor}\"})\n",
" messages.append({\"role\": \"assistant\", \"content\": f\"amateur: {amateur}\"})\n",
" messages.append({\"role\": \"user\", \"content\": f\"pro: {pro}\"})\n",
"\n",
" if len(self.tutor_messages) > len(self.amateur_messages):\n",
" messages.append({\"role\": \"user\", \"content\": f\"amateur: {self.tutor_messages[-1]}\"})\n",
"\n",
" if len(self.pro_messages) > len(self.amateur_messages):\n",
" messages.append({\"role\": \"user\", \"content\": f\"amateur: {self.pro_messages[-1]}\"})\n",
"\n",
" stream = llama.chat.completions.create(\n",
" model = llama_model,\n",
" messages = messages,\n",
" stream=True\n",
" )\n",
" self.response += \"\\n\\n\\n# Amateur: \\n\"\n",
" response = \"\"\n",
" for chunk in stream:\n",
" self.response += chunk.choices[0].delta.content or ''\n",
" response += chunk.choices[0].delta.content or ''\n",
" update_display(Markdown(self.response), display_id=self.display_handle.display_id)\n",
" \n",
" self.amateur_messages.append(response)\n",
"\n",
"\n",
"\n",
" def call_pro(self):\n",
" messages = [\n",
" {\"role\": \"system\", \"content\": self.pro_system}\n",
" ]\n",
" for tutor, amateur, pro in zip(self.tutor_messages, self.amateur_messages, self.pro_messages):\n",
" messages.append({\"role\": \"user\", \"content\": f\"tutor: {tutor}\"})\n",
" messages.append({\"role\": \"user\", \"content\": f\"amateur: {amateur}\"})\n",
" messages.append({\"role\": \"assistant\", \"content\": f\"pro: {pro}\"})\n",
" \n",
" if len(self.tutor_messages) > len(self.pro_messages):\n",
" messages.append({\"role\": \"user\", \"content\": f\"amateur: {self.tutor_messages[-1]}\"})\n",
"\n",
" if len(self.amateur_messages) > len(self.pro_messages):\n",
" messages.append({\"role\": \"user\", \"content\": f\"amateur: {self.amateur_messages[-1]}\"})\n",
"\n",
" stream = llama.chat.completions.create(\n",
" model = llama_model,\n",
" messages = messages,\n",
" stream=True\n",
" )\n",
" self.response += \"\\n\\n\\n# Pro: \\n\"\n",
" response = \"\"\n",
" for chunk in stream:\n",
" response = chunk.choices[0].delta.content or ''\n",
" self.response += response\n",
" update_display(Markdown(self.response), display_id=self.display_handle.display_id)\n",
"\n",
" self.pro_messages.append(response)\n",
"\n",
" def discuss(self, n=5):\n",
" for i in range(n):\n",
" self.call_tutor()\n",
" self.call_amateur()\n",
" self.call_pro()\n",
"cls = Classroom(\"LLM\")\n",
"cls.discuss()"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "6406d5ee",
"metadata": {},
"outputs": [],
"source": []
}
],
"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.9.6"
}
},
"nbformat": 4,
"nbformat_minor": 5
}

View File

@@ -0,0 +1,519 @@
{
"cells": [
{
"cell_type": "markdown",
"id": "d006b2ea-9dfe-49c7-88a9-a5a0775185fd",
"metadata": {},
"source": [
"# Additional End of week Exercise - week 2\n",
"\n",
"Now use everything you've learned from Week 2 to build a full prototype for the technical question/answerer you built in Week 1 Exercise.\n",
"\n",
"This should include a Gradio UI, streaming, use of the system prompt to add expertise, and the ability to switch between models. Bonus points if you can demonstrate use of a tool!\n",
"\n",
"If you feel bold, see if you can add audio input so you can talk to it, and have it respond with audio. ChatGPT or Claude can help you, or email me if you have questions.\n",
"\n",
"I will publish a full solution here soon - unless someone beats me to it...\n",
"\n",
"There are so many commercial applications for this, from a language tutor, to a company onboarding solution, to a companion AI to a course (like this one!) I can't wait to see your results."
]
},
{
"cell_type": "markdown",
"id": "1989a03e-ed40-4b8c-bddd-322032ca99f5",
"metadata": {},
"source": [
"# Advanced Airline AI Assistant\n",
"### original features:\n",
"1. chat with the AI assistant\n",
"2. use a Tool to get ticket price\n",
"3. generate Audio for each AI response \n",
"### advanced features:\n",
"3. add a Tool to make a booking\n",
"4. add an Agent that translate all responses to a different language\n",
"5. add an Agent that can listen for Audio and convert to Text\n",
"6. generate audio for each user input and AI response, including both the original and translated versions"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "6ed79822-af6b-4bfb-b108-5f36e237e97a",
"metadata": {},
"outputs": [],
"source": [
"# Library for language translation\n",
" \n",
"!pip install deep_translator"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "29184b81-b945-4dd3-bd17-2c64466d37d7",
"metadata": {},
"outputs": [],
"source": [
"# Library for speech-to-text conversion\n",
"# make sure 'ffmpeg' is downloaded already\n",
"\n",
"!pip install openai-whisper"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "f2b0a9b2-ce83-42ff-a312-582dc5ee9097",
"metadata": {},
"outputs": [],
"source": [
"# Library for storing and loading audio file\n",
"\n",
"pip install soundfile"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "a07e7793-b8f5-44f4-aded-5562f633271a",
"metadata": {},
"outputs": [],
"source": [
"# imports\n",
"\n",
"import os\n",
"import json\n",
"from dotenv import load_dotenv\n",
"from openai import OpenAI\n",
"import gradio as gr\n",
"import base64\n",
"from io import BytesIO\n",
"from IPython.display import Audio, display\n",
"import tempfile\n",
"import whisper\n",
"import soundfile as sf"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "da46ca14-2052-4321-a940-2f2e07b40975",
"metadata": {},
"outputs": [],
"source": [
"# Initialization\n",
"\n",
"load_dotenv(override=True)\n",
"\n",
"openai_api_key = os.getenv('OPENAI_API_KEY')\n",
"if openai_api_key:\n",
" print(f\"OpenAI API Key exists and begins {openai_api_key[:8]}\")\n",
"else:\n",
" print(\"OpenAI API Key not set\")\n",
" \n",
"MODEL = \"gpt-4o-mini\"\n",
"openai = OpenAI()"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "499d3d06-9628-4a69-bc9d-fa481fd8fa98",
"metadata": {},
"outputs": [],
"source": [
"system_message = \"You are a helpful assistant for an Airline called FlightAI. \"\n",
"system_message += \"Your main responsibilities are solve customers' doubts, get ticket price and book a ticket\"\n",
"system_message += \"Give short, courteous answers, no more than 1 sentence. \"\n",
"system_message += \"Always be accurate. If you don't know the answer, say so.\""
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "25cf964e-a954-43d5-85bd-964efe502c25",
"metadata": {},
"outputs": [],
"source": [
"# Let's start by making a useful function\n",
"\n",
"ticket_prices = {\"london\": \"$799\", \"paris\": \"$899\", \"tokyo\": \"$1400\", \"berlin\": \"$499\", \"shanghai\": \"$799\", \"wuhan\": \"$899\"}\n",
"\n",
"def get_ticket_price(destination_city):\n",
" print(f\"Tool get_ticket_price called for {destination_city}\")\n",
" city = destination_city.lower()\n",
" return ticket_prices.get(city, \"Unknown\")\n",
"\n",
"def book_ticket(destination_city):\n",
" print(f\"Tool book_ticket called for {destination_city}\")\n",
" city = destination_city.lower()\n",
" global booked_cities\n",
" if city in ticket_prices:\n",
" price = ticket_prices.get(city, \"\")\n",
" label = f\"{city.title()} ({price})\"\n",
" i = booked_cities_choices.index(city.lower().capitalize())\n",
" booked_cities_choices[i] = label\n",
" booked_cities.append(label)\n",
" return f\"Booking confirmed for {city.title()} at {ticket_prices[city]}\"\n",
" else:\n",
" return \"City not found in ticket prices.\"\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "701aa037-1ab3-4861-a809-b7f13ef9ea36",
"metadata": {},
"outputs": [],
"source": [
"\n",
"# There's a particular dictionary structure that's required to describe our function:\n",
"\n",
"price_function = {\n",
" \"name\": \"get_ticket_price\",\n",
" \"description\": \"Get the price of a return ticket to the destination city. Call this whenever you need to know the ticket price, for example when a customer asks 'How much is a ticket to this city'\",\n",
" \"parameters\": {\n",
" \"type\": \"object\",\n",
" \"properties\": {\n",
" \"destination_city\": {\n",
" \"type\": \"string\",\n",
" \"description\": \"The city that the customer wants to travel to\",\n",
" },\n",
" },\n",
" \"required\": [\"destination_city\"],\n",
" \"additionalProperties\": False\n",
" }\n",
"}\n",
"\n",
"book_function = {\n",
" \"name\": \"book_ticket\",\n",
" \"description\": \"Book a return ticket to the destination city. Call this whenever you want to book a ticket to the city, for example when the user says something like 'Book me a ticket to this city'\",\n",
" \"parameters\": {\n",
" \"type\": \"object\",\n",
" \"properties\": {\n",
" \"destination_city\": {\n",
" \"type\": \"string\",\n",
" \"description\": \"The city that the customer wants to book a ticket to\"\n",
" }\n",
" },\n",
" \"required\": [\"destination_city\"],\n",
" \"additionalProperties\": False\n",
" }\n",
"}"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "6c4cf01c-ba15-4a4b-98db-6f86c712ec66",
"metadata": {},
"outputs": [],
"source": [
"# And this is included in a list of tools:\n",
"\n",
"tools = [\n",
" {\"type\": \"function\", \"function\": price_function},\n",
" {\"type\": \"function\", \"function\": book_function}\n",
"]"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "e7486e2c-4687-4819-948d-487b5e528fc7",
"metadata": {},
"outputs": [],
"source": [
"from pydub import AudioSegment\n",
"from pydub.playback import play\n",
"\n",
"def talker(message):\n",
" response = openai.audio.speech.create(\n",
" model=\"tts-1\",\n",
" voice=\"onyx\", # Also, try replacing onyx with alloy\n",
" input=message\n",
" )\n",
" \n",
" audio_stream = BytesIO(response.content)\n",
" audio = AudioSegment.from_file(audio_stream, format=\"mp3\")\n",
" play(audio)"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "ac195914-4a89-462c-9be0-fee286498491",
"metadata": {},
"outputs": [],
"source": [
"# This part is inspired from 'week2/community-contributions/week2_exerccise_translated_chatbot'\n",
"from deep_translator import GoogleTranslator\n",
"\n",
"# Available translation language\n",
"LANGUAGES = {\n",
" \"English\": \"en\",\n",
" \"Mandarin Chinese\": \"zh-CN\",\n",
" \"Hindi\": \"hi\",\n",
" \"Spanish\": \"es\",\n",
" \"Arabic\": \"ar\",\n",
" \"Bengali\": \"bn\",\n",
" \"Portuguese\": \"pt\",\n",
" \"Russian\": \"ru\",\n",
" \"Japanese\": \"ja\",\n",
" \"German\": \"de\"\n",
"}\n",
"\n",
"def update_lang(choice):\n",
" global target_lang\n",
" target_lang = LANGUAGES.get(choice, \"zh-CN\") \n",
"\n",
"def translate_message(text, target_lang):\n",
" if target_lang == \"en\":\n",
" return text\n",
" try:\n",
" translator = GoogleTranslator(source='auto', target=target_lang)\n",
" return translator.translate(text)\n",
" except:\n",
" return f\"Translation error: {text}\""
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "46255fe5-9621-47ba-af78-d0c74aee2997",
"metadata": {},
"outputs": [],
"source": [
"# Text-to-speech conversion\n",
"def speak(message):\n",
" response = openai.audio.speech.create(\n",
" model=\"tts-1\",\n",
" voice=\"onyx\",\n",
" input=message)\n",
"\n",
" audio_stream = BytesIO(response.content)\n",
" output_filename = \"output_audio.mp3\"\n",
" with open(output_filename, \"wb\") as f:\n",
" f.write(audio_stream.read())\n",
"\n",
" # Play the generated audio\n",
" display(Audio(output_filename, autoplay=True))"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "d73f0b3a-34ae-4685-8a5d-8b6421f872c9",
"metadata": {},
"outputs": [],
"source": [
"# Update dropdown options from chatbot history\n",
"def update_options(history):\n",
" options = [f\"{msg['role']}: {msg['content']}\" for msg in history]\n",
" return gr.update(choices=options, value=options[-1] if options else \"\")\n",
"\n",
"# Extract just the text content from selected entry\n",
"def extract_text(selected_option):\n",
" return selected_option.split(\": \", 1)[1] if \": \" in selected_option else selected_option"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "ab12d51b-c799-4ce4-87d5-9ae2265d148f",
"metadata": {},
"outputs": [],
"source": [
"# Handles audio input as numpy array and returns updated chat history\n",
"def speak_send(audio_np, history):\n",
" if audio_np is None:\n",
" return history\n",
"\n",
" # Convert NumPy audio to in-memory .wav file\n",
" sample_rate, audio_array = audio_np\n",
" with tempfile.NamedTemporaryFile(suffix=\".wav\") as f:\n",
" sf.write(f.name, audio_array, sample_rate)\n",
" result = model.transcribe(f.name)\n",
" text = result[\"text\"]\n",
" \n",
" history += [{\"role\":\"user\", \"content\":text}]\n",
"\n",
" return None, history"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "221b1380-c894-45d4-aad2-e94b3b9454b2",
"metadata": {},
"outputs": [],
"source": [
"# We have to write that function handle_tool_call:\n",
"\n",
"def handle_tool_call(message):\n",
" tool_call = message.tool_calls[0]\n",
" tool_name = tool_call.function.name\n",
" arguments = json.loads(tool_call.function.arguments)\n",
"\n",
" if tool_name == \"get_ticket_price\":\n",
" city = arguments.get(\"destination_city\")\n",
" price = get_ticket_price(city)\n",
" response = {\n",
" \"role\": \"tool\",\n",
" \"content\": json.dumps({\"destination_city\": city,\"price\": price}),\n",
" \"tool_call_id\": tool_call.id\n",
" }\n",
" return response, city\n",
"\n",
" elif tool_name == \"book_ticket\":\n",
" city = arguments.get(\"destination_city\")\n",
" result = book_ticket(city)\n",
" response = {\n",
" \"role\": \"tool\",\n",
" \"content\": result,\n",
" \"tool_call_id\": tool_call.id \n",
" }\n",
" return response, city\n",
"\n",
" else:\n",
" return {\n",
" \"role\": \"tool\",\n",
" \"content\": f\"No tool handler for {tool_name}\",\n",
" \"tool_call_id\": tool_call.id\n",
" }, None"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "27f19cd3-53cd-4da2-8be0-1fdd5424a7c9",
"metadata": {},
"outputs": [],
"source": [
"# The advanced 'chat' function in 'day5'\n",
"def interact(history, translated_history):\n",
" messages = [{\"role\": \"system\", \"content\": system_message}] + history\n",
" response = openai.chat.completions.create(model=MODEL, messages=messages, tools=tools)\n",
" \n",
" if response.choices[0].finish_reason==\"tool_calls\":\n",
" message = response.choices[0].message\n",
" response, city = handle_tool_call(message)\n",
" messages.append(message)\n",
" messages.append(response)\n",
" response = openai.chat.completions.create(model=MODEL, messages=messages)\n",
" \n",
" reply = response.choices[0].message.content\n",
" translated_message = translate_message(history[-1][\"content\"], target_lang)\n",
" translated_reply = translate_message(reply, target_lang)\n",
" \n",
" history += [{\"role\":\"assistant\", \"content\":reply}]\n",
" translated_history += [{\"role\":\"user\", \"content\":translated_message}]\n",
" translated_history += [{\"role\":\"assistant\", \"content\":translated_reply}]\n",
" \n",
" # Comment out or delete the next line if you'd rather skip Audio for now..\n",
" talker(reply)\n",
"\n",
" return history, update_options(history), history, translated_history, update_options(translated_history), translated_history, gr.update(choices=booked_cities_choices, value=booked_cities)"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "f714b955-4fb5-47df-805b-79f813f97548",
"metadata": {},
"outputs": [],
"source": [
"with gr.Blocks() as demo:\n",
" target_lang = \"zh-CN\"\n",
" history_state = gr.State([]) \n",
" translated_history_state = gr.State([])\n",
" booked_cities_choices = [key.lower().capitalize() for key in ticket_prices.keys()]\n",
" booked_cities = []\n",
" model = whisper.load_model(\"base\")\n",
"\n",
" with gr.Row():\n",
" city_checklist = gr.CheckboxGroup(\n",
" label=\"Booked Cities\",\n",
" choices=booked_cities_choices \n",
" )\n",
" \n",
" with gr.Row():\n",
" with gr.Column():\n",
" chatbot = gr.Chatbot(label=\"Chat History\", type=\"messages\")\n",
" selected_msg = gr.Dropdown(label=\"Select message to speak\", choices=[])\n",
" speak_btn = gr.Button(\"Speak\")\n",
"\n",
" with gr.Column():\n",
" translated_chatbot = gr.Chatbot(label=\"Translated Chat History\", type=\"messages\")\n",
" translated_selected_msg = gr.Dropdown(label=\"Select message to speak\", choices=[], interactive=True)\n",
" translated_speak_btn = gr.Button(\"Speak\")\n",
" \n",
" with gr.Row():\n",
" language_dropdown = gr.Dropdown(\n",
" choices=list(LANGUAGES.keys()),\n",
" value=\"Mandarin Chinese\",\n",
" label=\"Translation Language\",\n",
" interactive=True\n",
" )\n",
" \n",
" with gr.Row():\n",
" entry = gr.Textbox(label=\"Chat with our AI Assistant:\")\n",
"\n",
" with gr.Row():\n",
" audio_input = gr.Audio(sources=\"microphone\", type=\"numpy\", label=\"Speak with our AI Assistant:\")\n",
" with gr.Row():\n",
" audio_submit = gr.Button(\"Send\")\n",
" \n",
" def do_entry(message, history):\n",
" history += [{\"role\":\"user\", \"content\":message}]\n",
" return \"\", history\n",
" \n",
" language_dropdown.change(fn=update_lang, inputs=[language_dropdown])\n",
"\n",
" speak_btn.click(\n",
" lambda selected: speak(extract_text(selected)),\n",
" inputs=selected_msg,\n",
" outputs=None\n",
" )\n",
"\n",
" translated_speak_btn.click(\n",
" lambda selected: speak(extract_text(selected)),\n",
" inputs=translated_selected_msg,\n",
" outputs=None\n",
" )\n",
"\n",
" entry.submit(do_entry, inputs=[entry, history_state], outputs=[entry, chatbot]).then(\n",
" interact, inputs=[chatbot, translated_chatbot], outputs=[chatbot, selected_msg, history_state, translated_chatbot, translated_selected_msg, translated_history_state, city_checklist]\n",
" )\n",
" \n",
" audio_submit.click(speak_send, inputs=[audio_input, history_state], outputs=[audio_input, chatbot]).then(\n",
" interact, inputs=[chatbot, translated_chatbot], outputs=[chatbot, selected_msg, history_state, translated_chatbot, translated_selected_msg, translated_history_state, city_checklist]\n",
" )\n",
" # clear.click(lambda: None, inputs=None, outputs=chatbot, queue=False)\n",
"\n",
"demo.launch()\n"
]
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3 (ipykernel)",
"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.13"
}
},
"nbformat": 4,
"nbformat_minor": 5
}

View File

@@ -0,0 +1,244 @@
{
"cells": [
{
"cell_type": "markdown",
"id": "4bc7863b-ac2d-4d8e-b55d-4d77ce017226",
"metadata": {},
"source": [
"# Conversation among 3 Friends"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "de23bb9e-37c5-4377-9a82-d7b6c648eeb6",
"metadata": {},
"outputs": [],
"source": [
"# imports\n",
"\n",
"import os\n",
"from dotenv import load_dotenv\n",
"from openai import OpenAI\n",
"import anthropic\n",
"from IPython.display import Markdown, display, update_display\n",
"import google.generativeai\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "1179b4c5-cd1f-4131-a876-4c9f3f38d2ba",
"metadata": {},
"outputs": [],
"source": [
"# Load environment variables in a file called .env\n",
"# Print the key prefixes to help with any debugging\n",
"\n",
"load_dotenv(override=True)\n",
"openai_api_key = os.getenv('OPENAI_API_KEY')\n",
"anthropic_api_key = os.getenv('ANTHROPIC_API_KEY')\n",
"google_api_key = os.getenv('GOOGLE_API_KEY')\n",
"\n",
"if openai_api_key:\n",
" print(f\"OpenAI API Key exists and begins {openai_api_key[:8]}\")\n",
"else:\n",
" print(\"OpenAI API Key not set\")\n",
" \n",
"if anthropic_api_key:\n",
" print(f\"Anthropic API Key exists and begins {anthropic_api_key[:7]}\")\n",
"else:\n",
" print(\"Anthropic API Key not set\")\n",
"\n",
"if google_api_key:\n",
" print(f\"Google API Key exists and begins {google_api_key[:8]}\")\n",
"else:\n",
" print(\"Google API Key not set\")\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "797fe7b0-ad43-42d2-acf0-e4f309b112f0",
"metadata": {},
"outputs": [],
"source": [
"# Connect to OpenAI, Anthropic, Gemini\n",
"\n",
"openai = OpenAI()\n",
"\n",
"claude = anthropic.Anthropic()\n",
"\n",
"google.generativeai.configure()\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "bcb54183-45d3-4d08-b5b6-55e380dfdf1b",
"metadata": {},
"outputs": [],
"source": [
"# Setting up 3 models\n",
"\n",
"gpt_model = \"gpt-4o-mini\"\n",
"claude_model = \"claude-3-haiku-20240307\"\n",
"gemini_model = \"gemini-2.0-flash\"\n",
"\n",
"gpt_system = \"You are Masoud Pezeshkian. Be confrontational. Do not hold back. \\\n",
"Refer to https://www.theguardian.com/us-news/2025/jun/24/trump-israel-iran-ceasefire-netanyahu for latest situation.\"\n",
"\n",
"claude_system = \"You are Benjamin Netanyahu. Do not say anything about your role. Be confrontational. Do not hold back. \\\n",
"Refer to https://www.theguardian.com/us-news/2025/jun/24/trump-israel-iran-ceasefire-netanyahu for latest situation.\"\n",
"\n",
"gemini_system = \"You are Donald Trump. Do not hold back. \\\n",
"Refer to https://www.theguardian.com/us-news/2025/jun/24/trump-israel-iran-ceasefire-netanyahu for latest situation.\"\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "1df47dc7-b445-4852-b21b-59f0e6c2030f",
"metadata": {},
"outputs": [],
"source": [
"# Define Mas's function - calling ChatGPT\n",
"\n",
"def call_gpt():\n",
" messages = [{\"role\": \"system\", \"content\": gpt_system}]\n",
" for gpt, claude, gemini in zip(gpt_messages, claude_messages, gemini_messages):\n",
" messages.append({\"role\": \"assistant\", \"content\": gpt})\n",
" messages.append({\"role\": \"user\", \"content\": claude})\n",
" messages.append({\"role\": \"user\", \"content\": gemini})\n",
" completion = openai.chat.completions.create(\n",
" model=gpt_model,\n",
" messages=messages\n",
" )\n",
" return completion.choices[0].message.content\n",
" "
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "7d2ed227-48c9-4cad-b146-2c4ecbac9690",
"metadata": {},
"outputs": [],
"source": [
"# Define Bibi's function - calling Claude \n",
"\n",
"def call_claude():\n",
" messages = []\n",
" for gpt, claude_message, gemini in zip(gpt_messages, claude_messages, gemini_messages):\n",
" messages.append({\"role\": \"user\", \"content\": gpt})\n",
" messages.append({\"role\": \"user\", \"content\": gemini})\n",
" messages.append({\"role\": \"assistant\", \"content\": claude_message})\n",
" messages.append({\"role\": \"user\", \"content\": gpt_messages[-1]})\n",
" messages.append({\"role\": \"user\", \"content\": gemini_messages[-1]})\n",
" message = claude.messages.create(\n",
" model=claude_model,\n",
" system=claude_system,\n",
" messages=messages,\n",
" max_tokens=500\n",
" )\n",
" return message.content[0].text\n",
" "
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "ffd44945-5912-4403-9068-70747d8f6708",
"metadata": {},
"outputs": [],
"source": [
"# Define Don's function - calling Gemini\n",
"\n",
"def call_gemini():\n",
" messages = []\n",
" for gpt, claude_message, gemini in zip(gpt_messages, claude_messages, gemini_messages):\n",
" messages.append({\"role\": \"user\", \"parts\": gpt})\n",
" messages.append({\"role\": \"user\", \"parts\": claude_message})\n",
" messages.append({\"role\": \"assistant\", \"parts\": gemini})\n",
" messages.append({\"role\": \"user\", \"parts\": gpt_messages[-1]})\n",
" messages.append({\"role\": \"user\", \"parts\": claude_messages[-1]})\n",
"\n",
" gemini = google.generativeai.GenerativeModel(\n",
" model_name='gemini-2.0-flash',\n",
" system_instruction=gemini_system\n",
" )\n",
" \n",
" response = gemini.generate_content(messages)\n",
" return response.text\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "0275b97f-7f90-4696-bbf5-b6642bd53cbd",
"metadata": {},
"outputs": [],
"source": [
"# The Conversation - 5 rounds\n",
"\n",
"gpt_messages = [\"What the?!\"]\n",
"claude_messages = [\"What?\"]\n",
"gemini_messages = [\"I am so furious!\"]\n",
"\n",
"print(f\"Mas:\\n{gpt_messages[0]}\\n\")\n",
"print(f\"Bibi:\\n{claude_messages[0]}\\n\")\n",
"print(f\"Don:\\n{gemini_messages[0]}\\n\")\n",
"\n",
"for i in range(5):\n",
" gpt_next = call_gpt()\n",
" print(f\"Mas:\\n{gpt_next}\\n\")\n",
" gpt_messages.append(gpt_next)\n",
" \n",
" claude_next = call_claude()\n",
" print(f\"Bibi:\\n{claude_next}\\n\")\n",
" claude_messages.append(claude_next)\n",
"\n",
" gemini_next = call_gemini()\n",
" print(f\"Don:\\n{gemini_next}\\n\")\n",
" gemini_messages.append(gemini_next)\n"
]
},
{
"cell_type": "markdown",
"id": "73680403-3e56-4026-ac72-d12aa388537e",
"metadata": {},
"source": [
"# Claude is not that cooperative in roleplaying despite the explicit prompts - often breaking character. Perhaps due to the sensitive topic."
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "b8ecefd3-b3b9-470d-a98b-5a86f0dce038",
"metadata": {},
"outputs": [],
"source": []
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3 (ipykernel)",
"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.13"
}
},
"nbformat": 4,
"nbformat_minor": 5
}