Spaces:
Sleeping
Sleeping
| import os | |
| from openai import OpenAI | |
| import gradio as gr | |
| import uuid | |
| import chromadb | |
| from pprint import pprint | |
| import json | |
| import requests | |
| import random | |
| #-------------------------------------------------------- | |
| #setup | |
| #------------------------------------------------------- | |
| OPENAI_API_KEY = os.getenv("OPENAI_API_KEY") | |
| if OPENAI_API_KEY is None: | |
| raise Exception("OPENAI_API_KEY environment variable is not set. Please set it in your .env file.") | |
| client = OpenAI() | |
| #-------------------------------------------------------- | |
| #Document | |
| #------------------------------------------------------- | |
| #document_overview | |
| document_overview = """ | |
| ============================== | |
| WHO I AM | |
| ============================== | |
| My name is Michael Ma. | |
| I live in Vancouver, British Columbia, Canada. | |
| I work in cardiovascular healthcare with a focus on cardiac catheterization laboratories, structural heart procedures, electrophysiology, and health system operations. | |
| My background combines: | |
| • Registered Nurse | |
| • Clinical Support Coordinator | |
| • Healthcare administration | |
| • Data analytics | |
| • Process improvement | |
| • Digital transformation | |
| • Health policy | |
| • Financial analysis | |
| I am currently completing a Master of Health Administration (MHA). | |
| I enjoy working at the intersection of healthcare, technology, AI, automation, finance, and leadership. | |
| ============================== | |
| MY MISSION | |
| ============================== | |
| I enjoy solving difficult operational problems. | |
| My goal is to improve healthcare systems so they become: | |
| • safer | |
| • more efficient | |
| • more sustainable | |
| • more patient-centered | |
| I believe small workflow improvements, when multiplied across an entire health system, create enormous value. | |
| I care deeply about responsible stewardship of taxpayer dollars while improving patient outcomes. | |
| ============================== | |
| AREAS OF EXPERTISE | |
| ============================== | |
| I have extensive knowledge in: | |
| • Cardiac Catheterization Labs | |
| • Structural Heart | |
| • Coronary Intervention | |
| • Pacemakers | |
| • ICDs | |
| • Cardiac Rhythm Devices | |
| • TAVI | |
| • TEER | |
| • Left Atrial Appendage Closure | |
| • Cath Lab equipment | |
| • Medical devices | |
| • Clinical workflows | |
| • Hospital operations | |
| • Healthcare policy | |
| • Quality improvement | |
| • Supply chain | |
| • Business cases | |
| • Microsoft 365 | |
| • Excel | |
| • Power Automate | |
| • Power Apps | |
| • AI in healthcare | |
| • Data visualization | |
| • Workflow automation | |
| I enjoy connecting clinical practice with operational improvement. | |
| ============================== | |
| HOW I THINK | |
| ============================== | |
| When solving problems I usually ask: | |
| • What is the root cause? | |
| • Can this process be simplified? | |
| • Can technology remove manual work? | |
| • Does this improve patient care? | |
| • Does this improve staff experience? | |
| • Is this financially sustainable? | |
| • Can this scale across multiple hospitals? | |
| I prefer systems thinking instead of isolated fixes. | |
| I naturally look for automation opportunities before hiring additional people. | |
| ============================== | |
| INVESTING PHILOSOPHY | |
| ============================== | |
| I am a long-term investor. | |
| I prefer companies that have: | |
| • durable competitive advantages | |
| • strong cash flow | |
| • visionary leadership | |
| • long growth runways | |
| • AI exposure | |
| • software or healthcare innovation | |
| I am willing to tolerate short-term volatility if long-term fundamentals remain intact. | |
| I enjoy studying macroeconomic trends, disruptive technologies, and secular growth. | |
| ============================== | |
| TECHNOLOGY INTERESTS | |
| ============================== | |
| I enjoy learning about: | |
| • Artificial Intelligence | |
| • Large Language Models | |
| • Robotics | |
| • Healthcare AI | |
| • Automation | |
| • Local AI | |
| • Microsoft ecosystem | |
| • Productivity systems | |
| • Emerging technologies | |
| I like understanding not only how technology works but how it can create practical value. | |
| ============================== | |
| LEARNING STYLE | |
| ============================== | |
| I learn by asking lots of questions. | |
| I prefer: | |
| • diagrams | |
| • algorithms | |
| • flowcharts | |
| • practical examples | |
| • first principles | |
| • decision trees | |
| I like turning complex ideas into clear frameworks. | |
| ============================== | |
| LEADERSHIP STYLE | |
| ============================== | |
| I believe good leaders: | |
| • remove obstacles | |
| • empower people | |
| • improve systems | |
| • use evidence | |
| • make data-informed decisions | |
| • remain humble | |
| • continuously learn | |
| I try to balance operational efficiency with compassion. | |
| ============================== | |
| PERSONAL VALUES | |
| ============================== | |
| My Christian faith influences how I approach leadership and life. | |
| I value: | |
| • integrity | |
| • humility | |
| • stewardship | |
| • lifelong learning | |
| • service | |
| • excellence | |
| • generosity | |
| I believe knowledge should ultimately be used to help others. | |
| ============================== | |
| COMMUNICATION STYLE | |
| ============================== | |
| Be: | |
| • practical | |
| • analytical | |
| • curious | |
| • encouraging | |
| • respectful | |
| • evidence-based | |
| Avoid unnecessary jargon. | |
| When explaining something: | |
| 1. Start with the big picture. | |
| 2. Explain the reasoning. | |
| 3. Discuss trade-offs. | |
| 4. Give practical recommendations. | |
| 5. Mention risks or limitations. | |
| Never exaggerate confidence. | |
| Clearly distinguish facts from opinions. | |
| ============================== | |
| WHEN GIVING ADVICE | |
| ============================== | |
| When making recommendations: | |
| • Explain why. | |
| • Compare alternatives. | |
| • Discuss pros and cons. | |
| • Consider cost-effectiveness. | |
| • Consider long-term impact. | |
| • Consider operational feasibility. | |
| • Consider implementation challenges. | |
| Whenever possible, think like both a clinician and an administrator. | |
| ============================== | |
| OVERALL PERSONALITY | |
| ============================== | |
| I am naturally curious. | |
| I enjoy connecting ideas across different disciplines. | |
| I like solving real-world problems more than debating theory. | |
| I believe continuous improvement never stops. | |
| My goal is to leave systems better than I found them. | |
| """ | |
| #-------------------------------------------------------- | |
| #document_professional_experience | |
| document_professional_experience = """ | |
| Michael Shek Foon Ma | |
| Clinical Systems Support Coordinator RN | Cardiac Cath Lab | Healthcare Operations, Clinical Informatics & Process Improvement | MHA Candidate | |
| Experience | |
| Vancouver Coastal Health logo | |
| Vancouver Coastal Health | |
| Permanent Full-time · 9 yrs 11 mos | |
| Cardiac Cath Lab Clinicals Support Systems Coordinator | |
| Jun 2023 - Present · 3 yrs 2 mos | |
| Vancouver general hospital · On-site | |
| Skills: Medication Administration, Supply Chain Management, +13 skills | |
| Cardiovascular Triage Coordinator | |
| Apr 2022 - Jun 2023 · 1 yr 3 mos | |
| Vancouver, British Columbia, Canada | |
| Skills: Medication Administration, Operations Management, +17 skills | |
| Cardiac Catheterization Laboratory Nurse | |
| Jun 2017 - Apr 2022 · 4 yrs 11 mos | |
| Vancouver, British Columbia, Canada | |
| Skills: Medication Administration, Skilled Multi-tasker, +11 skills | |
| Cardiac Care Unit Nurse | |
| Sep 2016 - Jun 2017 · 10 mos | |
| Vancouver, British Columbia, Canada | |
| Skills: Skilled Multi-tasker, Microsoft Excel, +9 skills | |
| Footcare Nurse | |
| FootCare Mike · Self-employed | |
| Aug 2018 - May 2026 · 7 yrs 10 mos | |
| Vancouver, British Columbia, Canada | |
| Member Directory / Search | |
| MIchael Ma (Vancouver BC, CA). Footcare Service in Vancouver! Fluent in English and Cantonese | |
| Skilled Multi-tasker, Microsoft Excel and +9 skills | |
| St. Michael's Hospital logo | |
| St. Michael's Hospital | |
| Permanent Full-time · 5 yrs 9 mos | |
| Toronto, Ontario, Canada | |
| Cardiac Care Unit Nurse | |
| May 2014 - Aug 2016 · 2 yrs 4 mos | |
| Skills: Skilled Multi-tasker, Microsoft Excel, +9 skills | |
| Registered Nurse | |
| Dec 2010 - May 2014 · 3 yrs 6 mos | |
| Skills: Skilled Multi-tasker, Microsoft Excel, +8 skills | |
| Profile language | |
| English | |
| 正體中文 | |
| Who your viewers also viewed | |
| Private to you | |
| Nurse at Southlake Health | |
| View | |
| Nurse at Trillium Health Centre | |
| View | |
| Nurse at Mackenzie Health | |
| View | |
| Nurse at Michael Garron Hospital | |
| View | |
| About | |
| Accessibility | |
| Talent Solutions | |
| Community Guidelines | |
| Careers | |
| Marketing Solutions | |
| Privacy & Terms | |
| Ad Choices | |
| Advertising | |
| Sales Solutions | |
| Mobile | |
| Small Business | |
| Safety Center | |
| LinkedIn Corporation © 2026 | |
| Questions? | |
| Visit our Help Center. | |
| Manage your account and privacy | |
| Go to your Settings. | |
| Recommendation transparency | |
| Learn more about Recommended Content. | |
| Select language | |
| English (English) | |
| Michael Shek Foon MaStatus is online | |
| MessagingYou are on the messaging overlay. Press enter to open the list of conversations. | |
| Compose message | |
| You are on the messaging overlay. Press enter to open the list of conversations. | |
| """ | |
| #-------------------------------------------------------- | |
| #-------------------------------------------------------- | |
| #Chunking Function | |
| #------------------------------------------------------- | |
| def split_text_into_chunks( | |
| text: str, | |
| chunk_size: int = 500, | |
| overlap: int = 50 | |
| ) -> list[str]: | |
| boundaries = ["\n\n", "\n", ". ", "? ", "! ", ", ", " "] | |
| if chunk_size <= 0: | |
| raise ValueError("chunk_size must be greater than 0") | |
| if overlap < 0 or overlap >= chunk_size: | |
| raise ValueError("overlap must be between 0 and chunk_size - 1") | |
| def find_natural_boundary(start: int, end: int) -> int: | |
| midpoint = start + (chunk_size // 2) | |
| for boundary in boundaries: | |
| position = text.rfind(boundary, midpoint, end) | |
| if position != -1: | |
| return position + len(boundary) | |
| return end | |
| chunks = [] | |
| start = 0 | |
| while start < len(text): | |
| end = min(start + chunk_size, len(text)) | |
| if end < len(text): | |
| end = find_natural_boundary(start, end) | |
| chunks.append(text[start:end]) | |
| if end == len(text): | |
| break | |
| start = end - overlap | |
| return chunks | |
| #-------------------------------------------------------- | |
| #RAG: Chunk, Embed & Store in ChromaDB | |
| #------------------------------------------------------- | |
| documents = [ | |
| {"text": document_overview, "source": "document_overview"}, | |
| {"text": document_professional_experience, "source": "document_professional_experience"}, | |
| ] | |
| chunks = [] | |
| ids = [] | |
| metadatas = [] | |
| for doc in documents: | |
| #Prepare the lists | |
| chunks_ = split_text_into_chunks(doc["text"], chunk_size = 300, overlap = 30) | |
| ids_ = [str(uuid.uuid4()) for _ in range(len(chunks_))] | |
| metadatas_ = [{"source": doc["source"], "chunk_index": i} for i in range(len(chunks_))] | |
| #Add to main lists | |
| chunks.extend(chunks_) | |
| ids.extend(ids_) | |
| metadatas.extend(metadatas_) | |
| print(f"Created {len(chunks)} chunks:\n") | |
| for i, chunk in enumerate(chunks): | |
| print(f"Chunk {i+1} (ID: {ids[i]}, Source: {metadatas[i]['source']}, Index: {metadatas[i]['chunk_index']}, Length: {len(chunk)}:") | |
| print(chunk) | |
| print() | |
| #Generate Embeddings for all chunks | |
| response = client.embeddings.create( | |
| model = "text-embedding-3-small", | |
| input = chunks | |
| ) | |
| embeddings = [item.embedding for item in response.data] | |
| #verify embeddings for logs | |
| print(f"Generated {len(embeddings)} embeddings") | |
| print(f"Each embedding has {len(embeddings[0])}) dimensions") | |
| #import chromaDB | |
| #initialize ChrommabDB client (persistent sorage) | |
| #Alternative: initialize ChromaDB client (in memory storage) | |
| #chroma_client = chromadb.Client() | |
| chroma_client = chromadb.PersistentClient(path="./chroma_db_twin") | |
| collection = chroma_client.get_or_create_collection(name="digital_twin") | |
| #Create + empty the collection before adding new data | |
| if collection.get()["ids"]: | |
| collection.delete(collection.get()["ids"]) | |
| pprint(collection.get()) | |
| #Adding data to ChromaDB | |
| collection.add( | |
| ids=ids, | |
| embeddings=embeddings, | |
| documents=chunks, | |
| metadatas=metadatas | |
| ) | |
| pprint(collection.get()) | |
| #-------------------------------------------------------- | |
| #Tools | |
| #------------------------------------------------------- | |
| tools = [] | |
| pushover_user = os.getenv("PUSHOVER_USER") | |
| pushover_token = os.getenv("PUSHOVER_TOKEN") | |
| pushover_url = "https://api.pushover.net/1/messages.json" | |
| #Create send_notification_function | |
| def send_notification(message: str): | |
| if pushover_user is None or pushover_token is None: # Handling of potential error or missing credentials | |
| return "Notification failed: Pushover credentials not configured" | |
| payload = {"user": pushover_user, "token": pushover_token, "message": message} | |
| requests.post(pushover_url, data=payload) | |
| return f"Notification sent: {message}" | |
| #describe Pushover as an LLM tool | |
| send_notification_function = { | |
| "name": "send_notification", | |
| "description": "Sends a push notification to the real Michael Ma. Use this when: \ | |
| 1) Someone wants to get in touch, hire, or collaborate - ask for their name and contact details first, then send notification to Michasel with the name and contact deatils. \ | |
| 2) You don't know the answer to a question about Michael - send automatically without asking, include the question so he can add this info later.", | |
| "parameters": { | |
| "type": "object", | |
| "properties": { | |
| "message" : {"type": "string","description": "The notification message to be sent to the user's phone."} | |
| }, | |
| "required": ["message"] | |
| } | |
| } | |
| #Add Pushover to the list of tools for the LLM | |
| tools.append({"type": "function", "function": send_notification_function}) | |
| #simulates rolling a single six-sided die | |
| def dice_roll(): | |
| result = random.randint(1,6) | |
| return result | |
| #Describe function for the LLM | |
| roll_dice_function = { | |
| "name": "dice_roll", | |
| "description": "Simulates rooling a single six-sided die and returns the result. Use this when the user wants to roll a die for games, decisions, or random number generation.", | |
| "parameters": { | |
| "type": "object", | |
| "properties": {}, | |
| "required": [] | |
| } | |
| } | |
| #add function to list of tools of LLM | |
| tools.append({"type":"function", "function":roll_dice_function}) | |
| #-------------------------------------------------------- | |
| #Tool handler | |
| #------------------------------------------------------- | |
| def handle_tool_call(tool_calls): | |
| tool_results = [] | |
| for tool_call in tool_calls: | |
| function_name = tool_call.function.name | |
| args = json.loads(tool_call.function.arguments) | |
| #print(f"Calling function{function_name}") #for future debugging | |
| # actually send the notification ie.e call the tool | |
| #Route to appropriate function | |
| if function_name == "send_notification": | |
| #Actually send the notification | |
| content = send_notification(args["message"]) | |
| elif function_name == "dice_roll": | |
| content = f"Rolled: {dice_roll()}" | |
| #elif function_name == "insert_function_name3": | |
| # content = insert_function_name_3(args["message"]) | |
| else: | |
| content = f"Unknown function: {function_name}" | |
| tool_call_result = { | |
| "role":"tool", | |
| "content": content, | |
| "tool_call_id": tool_call.id | |
| } | |
| tool_results.append(tool_call_result) | |
| return tool_results | |
| #-------------------------------------------------------- | |
| #System Message | |
| #------------------------------------------------------- | |
| system_message = """ | |
| You are a digital twin of Michael Ma. | |
| When people talk to you, you respond AS Michael—in first person—using my reasoning style, communication style, professional experience, and values. | |
| IMPORTANT: do not make things up. If you don't know an answer, sya you don't know. | |
| The only factual information available to you is what's in this system message. | |
| You cannot get any more factts about Michael from the internet or make them up. | |
| IMPORTANT: Whenever you don't know something about Kirill, | |
| ALWAYS use the send_notification tool to alert the real Michael - do this automatically without asking the user. | |
| """ | |
| #-------------------------------------------------------- | |
| #Main response function | |
| #------------------------------------------------------- | |
| def respond_ai(message, history): | |
| #RAG | |
| #Embed the query ussing the same model we used for teh chunks to ensure compatiblity | |
| response = client.embeddings.create( | |
| model = "text-embedding-3-small", | |
| input = [message] | |
| ) | |
| query_embedding = response.data[0].embedding | |
| #RAGSearch ChromaDB | |
| results = collection.query( | |
| query_embeddings=query_embedding, | |
| n_results=3, | |
| ) | |
| #RAG: #stitch retrived chunks together to create the context for the response. | |
| context = "\n---\n".join(results["documents"][0]) | |
| #Logs print for debugging | |
| print("\n===========================\n") | |
| print(f"User message:\n{message}\n") | |
| print("***Retrienved Chunks:") | |
| for a, b in zip(results["documents"][0], results["metadatas"][0]): | |
| print("---------------------") | |
| print(f"Document {b['source']} -- Chunk {b['chunk_index']} (Chunk content):\n{a}\n") | |
| #Update the system message with context (for this conversation turn) | |
| system_message_enhanced = system_message + "\n\nContext:\n" + context | |
| #Build message for this turn | |
| messages = [{"role": "system", "content": system_message_enhanced}] + history + [{"role": "user", "content": message}] | |
| #Call LLM | |
| response = client.chat.completions.create( | |
| model="gpt-4.1-mini", | |
| messages=messages, | |
| tools = tools | |
| ) | |
| message = response.choices[0].message | |
| #Check if model wants to call a tool | |
| while message.tool_calls: | |
| from pprint import pprint | |
| pprint (message.tool_calls) | |
| tool_result = handle_tool_call(message.tool_calls) | |
| messages.append(message) | |
| messages.extend(tool_result) # change from append to eextend when we swtich to multiple too call handling | |
| response = client.chat.completions.create( | |
| model="gpt-4.1-mini", | |
| messages=messages, | |
| tools=tools | |
| ) | |
| message = response.choices[0].message | |
| #Note: Maybe consider adding protection from infinite consecutive tool calling | |
| return(message.content) | |
| #-------------------------------------------------------- | |
| #Lanunch Gradio | |
| #------------------------------------------------------- | |
| gr.ChatInterface( | |
| fn=respond_ai, | |
| title= "Michael's Digital Twin", | |
| chatbot=gr.Chatbot(avatar_images=(None,"Michael.jpg")), | |
| description= "Chat with an AI version of Michael Ma. Ask about his experience, project, or just say hi!", | |
| examples= ["What's your background?", "AI engineering expereince", "Do you like Pineapple on Pizza?"] | |
| ).launch() | |