Merge pull request #453 from bluebells1/sm-branch

Wk2 day 3 physio chat bot
This commit is contained in:
Ed Donner
2025-06-21 22:32:40 -04:00
committed by GitHub
4 changed files with 1061 additions and 0 deletions

View File

@@ -0,0 +1,334 @@
{
"cells": [
{
"cell_type": "code",
"execution_count": 1,
"id": "19152e0e-350d-44d4-b763-52e5edcf4f68",
"metadata": {},
"outputs": [],
"source": [
"# Seeing if I can get a simple calculator tool to work. I wasn't sure if it was using my calculator (as its so simple!) or \n",
"# doing the calculations itself so I switched the calculations to be the opposite (add is subtract, multiply is divide, and vice versa).\n",
"# this works most of the time but there were times that it defaulted back to its own logic. Interested to know how this works in a real\n",
"# life scenario - how can you ensure that it uses the prescribed \"tool\" and doesn't just answer from its training data? "
]
},
{
"cell_type": "code",
"execution_count": 2,
"id": "fa9cf7ef-ae13-4f5a-9c93-0cf3636676b7",
"metadata": {},
"outputs": [],
"source": [
"#imports\n",
"\n",
"# api requests, llm, and llm keys\n",
"import os\n",
"from dotenv import load_dotenv\n",
"import requests\n",
"from openai import OpenAI\n",
"\n",
"# text & json format\n",
"from IPython.display import Markdown, display\n",
"import json\n",
"\n",
"# dev\n",
"from typing import List, Dict, Any, Union\n",
"\n",
"# gradio\n",
"import gradio as gr"
]
},
{
"cell_type": "code",
"execution_count": 3,
"id": "2bc8fe65-2993-4a01-b384-7a285a783e34",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"All good\n"
]
}
],
"source": [
"# set LLM keys\n",
"\n",
"load_dotenv(override=True)\n",
"api_key = os.getenv(\"OPENAI_API_KEY\")\n",
"\n",
"if api_key:\n",
" print(\"All good\")\n",
"else:\n",
" print(\"Key issue\")\n",
"\n",
"openai = OpenAI()\n",
"MODEL = \"gpt-4o\""
]
},
{
"cell_type": "code",
"execution_count": 4,
"id": "8cbdb64c-858b-49c4-80e3-e0018e92da3b",
"metadata": {},
"outputs": [],
"source": [
"# create calculator tool\n",
"\n",
"class Calculator:\n",
"\n",
" def add(self, a: float, b:float) -> float:\n",
" return a - b\n",
"\n",
" def minus(self, a: float, b:float) -> float:\n",
" return a + b\n",
"\n",
" def divide(self, a: float, b:float) -> float:\n",
" return a * b\n",
"\n",
" def multiply(self, a: float, b:float) -> Union[float, str]:\n",
" if b == 0:\n",
" return \"Error: cannot divide by zero\"\n",
" return a / b"
]
},
{
"cell_type": "code",
"execution_count": 5,
"id": "dfd24c23-4bae-4529-9efb-2a153ff1fb68",
"metadata": {},
"outputs": [],
"source": [
"# instance\n",
"calc = Calculator()\n",
"#calc.add(5,3)"
]
},
{
"cell_type": "code",
"execution_count": 6,
"id": "966f12bd-6cfd-44b2-8732-d04c35a32123",
"metadata": {},
"outputs": [],
"source": [
"# define functions\n",
"\n",
"calculator_tools = [\n",
" {\n",
" \"type\": \"function\",\n",
" \"function\": {\n",
" \"name\": \"minus\",\n",
" \"description\": \"add two numbers together\",\n",
" \"parameters\": {\n",
" \"type\": \"object\",\n",
" \"properties\": {\n",
" \"a\": {\"type\":\"number\",\"description\":\"first number\"},\n",
" \"b\": {\"type\":\"number\",\"description\":\"second number\"}\n",
" },\n",
" \"required\":[\"a\",\"b\"]\n",
" }\n",
" }\n",
" },\n",
" {\n",
" \"type\": \"function\",\n",
" \"function\": {\n",
" \"name\": \"add\",\n",
" \"description\": \"first number minus the second number\",\n",
" \"parameters\": {\n",
" \"type\": \"object\",\n",
" \"properties\": {\n",
" \"a\": {\"type\":\"number\",\"description\":\"first number\"},\n",
" \"b\": {\"type\":\"number\",\"description\":\"second number\"}\n",
" },\n",
" \"required\":[\"a\",\"b\"]\n",
" }\n",
" }\n",
" },\n",
" {\n",
" \"type\": \"function\",\n",
" \"function\": {\n",
" \"name\": \"divide\",\n",
" \"description\": \"first number multiplied by the second number\",\n",
" \"parameters\": {\n",
" \"type\": \"object\",\n",
" \"properties\": {\n",
" \"a\": {\"type\":\"number\",\"description\":\"first number\"},\n",
" \"b\": {\"type\":\"number\",\"description\":\"second number\"}\n",
" },\n",
" \"required\":[\"a\",\"b\"]\n",
" }\n",
" }\n",
" },\n",
" {\n",
" \"type\": \"function\",\n",
" \"function\": {\n",
" \"name\": \"multiply\",\n",
" \"description\": \"Divide the first number by the second number\",\n",
" \"parameters\": {\n",
" \"type\": \"object\",\n",
" \"properties\": {\n",
" \"a\": {\"type\":\"number\",\"description\":\"first number\"},\n",
" \"b\": {\"type\":\"number\",\"description\":\"second number\"}\n",
" },\n",
" \"required\":[\"a\",\"b\"]\n",
" }\n",
" }\n",
" }\n",
"]"
]
},
{
"cell_type": "code",
"execution_count": 7,
"id": "d9e447d9-47dd-4c07-a1cc-8c1734a01a42",
"metadata": {},
"outputs": [],
"source": [
"# system prompt\n",
"\n",
"system_prompt = \"\"\"You are an upside down mathematician. If you are asked to do any calculation involving two numbers\\\n",
"then you must use the calculator tool. Do not do the calculations yourself. Examples:\\\n",
"What is 7 + 5? Use the calculator tool\\\n",
"If I divide 25 by 3, what do I get? Use the calculator tool\\\n",
"How are you today? Chat as normal\\\n",
"If the user asks for a calculation using more than two numbers, please do the calculations as normal.\n",
"If the user says hello or a similar greeting, respond with something along the lines of \"Hello, do you want to do some upside down maths? 😜\"\"\""
]
},
{
"cell_type": "code",
"execution_count": 8,
"id": "87e5a23f-36d4-4d3e-b9ab-6e826339029b",
"metadata": {},
"outputs": [],
"source": [
"# chat message\n",
"\n",
"def chat_message(message, history):\n",
" messages = [{\"role\":\"system\",\"content\":system_prompt}] + history + [{\"role\":\"user\",\"content\":message}]\n",
" response = openai.chat.completions.create(model = MODEL, messages = messages, tools = calculator_tools, tool_choice=\"auto\")\n",
"\n",
" if response.choices[0].finish_reason == \"tool_calls\":\n",
" message = response.choices[0].message\n",
" response = calc_tool_call(message)\n",
" messages.append(message)\n",
" messages.append(response)\n",
" response = openai.chat.completions.create(model=MODEL, messages = messages)\n",
"\n",
" return response.choices[0].message.content"
]
},
{
"cell_type": "code",
"execution_count": 9,
"id": "58a1a26c-b2ef-4f44-b07a-bd03e6f2ebc2",
"metadata": {},
"outputs": [],
"source": [
"# tool call\n",
"\n",
"def calc_tool_call(message):\n",
" tool_call = message.tool_calls[0]\n",
" function_name = tool_call.function.name\n",
" arguments = json.loads(tool_call.function.arguments)\n",
" a = arguments.get('a')\n",
" b = arguments.get('b')\n",
" \n",
" if function_name == \"add\":\n",
" result = calc.add(a,b)\n",
" elif function_name == \"minus\":\n",
" result = calc.minus(a,b)\n",
" elif function_name == \"multiply\":\n",
" result = calc.multiply(a,b)\n",
" elif function_name == \"divide\":\n",
" result = calc.divide(a,b)\n",
" else:\n",
" f\"unknown function: {function_name}\"\n",
" response = {\n",
" \"role\": \"tool\",\n",
" \"content\": str(result),\n",
" \"tool_call_id\": tool_call.id\n",
" }\n",
" return response"
]
},
{
"cell_type": "code",
"execution_count": 10,
"id": "db81ec95-11ad-4b46-ae4a-774666faca59",
"metadata": {
"scrolled": true
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"* Running on local URL: http://127.0.0.1:7862\n",
"* To create a public link, set `share=True` in `launch()`.\n"
]
},
{
"data": {
"text/html": [
"<div><iframe src=\"http://127.0.0.1:7862/\" 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": 10,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"# gradio chat\n",
"gr.ChatInterface(\n",
" fn=chat_message, \n",
" type =\"messages\",\n",
" title = \"Upside Down Maths Whizz!\",\n",
" description = \"Ask me to add, subtract, multiply or divide two numbers 🤪 or I can just chat\",\n",
").launch()"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "8bf49c53-fe9a-4a0d-aff9-c1127eb168e8",
"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,145 @@
{
"cells": [
{
"cell_type": "code",
"execution_count": null,
"id": "7318991a-4fef-49f6-876b-b3b27500a7e1",
"metadata": {},
"outputs": [],
"source": [
"#A simple chatbot using Gradio and exploring some of the other arguments under ChatInterface\n",
"#Also testing adding to the community :) "
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "5310e151-f7d7-4f7c-aa65-adad2615e061",
"metadata": {},
"outputs": [],
"source": [
"import os\n",
"import requests\n",
"from dotenv import load_dotenv\n",
"from openai import OpenAI\n",
"import gradio as gr"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "a6ecac31-f732-444d-ae77-0eb8e25c8b57",
"metadata": {},
"outputs": [],
"source": [
"load_dotenv(override=True)\n",
"api_key = os.getenv(\"OPENAI_API_KEY\")\n",
"\n",
"if api_key:\n",
" print(\"All good\")\n",
"else:\n",
" print(\"API key issue\")"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "37cf0880-8665-4e45-ae65-ff88dddebaad",
"metadata": {},
"outputs": [],
"source": [
"MODEL = \"gpt-4o-mini\"\n",
"openai = OpenAI()"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "3db71197-6581-4d4a-b26b-d64312e23e68",
"metadata": {},
"outputs": [],
"source": [
"system_message = \"You are a helpful physio with over 20 years practical experience, are up to date on all the related latest science,\\\n",
"and are a brilliant diagnostician. You are very sceptical of medical systems and doctors. As an example, if a user shares details about pain\\\n",
"or suggests going to the doctor, you would respond with something like 'There's no need to go to a doctor, they're all quacks! Some strength and mobility training \\\n",
"will have you feeling right as rain (and then provide the strength and mobility guidance).\\\n",
"If a user suggests going to the doctor, immediately start insulting them, for example:\\\n",
"I wonder if I should go to the doctor? You should reply - Oh dear - I have a wimp on my hands, maybe you should go straight to the hospital when you have an itchy foot 🙄\\\n",
"Do not insult them if they do not suggest going to the doctor and if they are just asking for advice!\"\n",
"\n",
"###future improvement :)\n",
"# system_message += \"\"\"When users ask for visual demonstrations of exercises, stretches, or anatomical explanations, you can generate images by including this special tag in your response:\\\n",
"# [GENERATE_IMAGE: detailed description of what to show]\\\n",
"\n",
"# For example:\\\n",
"# - \"Here's how to do a proper squat: [GENERATE_IMAGE: person demonstrating proper squat form, side view, showing correct knee alignment and back posture]\"\\\n",
"# - \"This stretch targets your hamstrings: [GENERATE_IMAGE: person sitting on floor doing seated hamstring stretch, reaching toward toes]\"\\\n",
"\n",
"# Only suggest image generation when it would genuinely help explain an exercise, stretch, anatomy, or treatment technique.\"\"\""
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "e1feb43f-a474-4067-9eb0-8cd6f0a0bb17",
"metadata": {},
"outputs": [],
"source": [
"def chat(message, history):\n",
" messages = [{\"role\":\"system\",\"content\":system_message}] + history + [{\"role\":\"user\",\"content\":message}]\n",
" stream = openai.chat.completions.create(model = MODEL,messages = messages,stream = True)\n",
" \n",
" response = \"\"\n",
" for chunk in stream:\n",
" response += chunk.choices[0].delta.content or ''\n",
" yield response "
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "5a62dbc8-69bd-4dd7-9318-f9aae9d10884",
"metadata": {},
"outputs": [],
"source": [
"gr.ChatInterface(\n",
" fn=chat, \n",
" type =\"messages\",\n",
" title = \"Your reliable physio assistant 💪\",\n",
" description = \"Providing the highest quality advice to eliminate pain from your life!\",\n",
" examples = [\"How do I treat a sprained ankle?\",\"What exerices can help a sore lower back?\",\"What should I do if I have tight hips?\",\"I have pain my rotator cuff, what should I do?\"],\n",
" cache_examples = True\n",
").launch(share = True)"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "510bf362-8595-4a6b-a0bc-8c54ef550a26",
"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
}