Add files via upload

Hi Ed, 
This is a Gradio-based booking assistant bot that:
- Accepts text and voice inputs
- Uses OpenAI for transcription and chat
- Restricts booking times to weekdays 14:00–15:00
- Responds with translated audio confirmations

Looking forward to your feedback!
Sabine
This commit is contained in:
Sabine Fonderson | CEO
2025-06-27 16:50:39 +02:00
committed by GitHub
parent b840ea6b58
commit 966a5f6c38

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
}