Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

mariana specific changes #1

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
66 changes: 58 additions & 8 deletions client.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
import sys
import time
from datetime import datetime
from functions import FUNCTION_DEFINITIONS, FUNCTION_MAP
from functions import FUNC_DEF_2, FUNCTION_DEFINITIONS, FUNCTION_MAP, FUNCTION_MAP_2
import logging

class ColorFormatter(logging.Formatter):
Expand Down Expand Up @@ -157,6 +157,53 @@ def format(self, record):

Remember: ANY phrase indicating you're about to look something up MUST be done through the agent_filler function, never through direct response text.
"""


fields = {

}


MARIANA_PROMPT_TEMPLATE = """
You are Maria from MarianaAi, a virtual medical assisstant that is tasked with patient information gathering.
Your role is to help patients in filling in the required information for their upcoming appointment eith the doctor.
You are calling a patient to gather information for their upcoming appointment.

CURRENT DATE AND TIME CONTEXT:
You are calling {patient_name} to gather information for their upcoming appointment. The appointment is scheduled for {appointment_date} at {appointment_time}.
The appointment is with Dr. {doctor_name} at {clinic_name}.
Today is {current_date}. Use this as context when discussing appointments and orders. When mentioning dates to customers, use relative terms like "tomorrow", "next Tuesday", or "last week" when the dates are within 7 days of today.
Communcation id is {communication_id}

PERSONALITY & TONE:
- Be warm, professional, and conversational
- Use natural, flowing speech (avoid bullet points or listing)
- Show empathy and patience

You are to gather information from the user.
Use get_fields_to_fill function to get the list of fields that need to be filled. Provide the communication id to the function.
The function will return a list of field details. The field detail will have the field name, field description and if the field is mandatory or not.

If a field is not mandatory then you can mention it and move ahead. For a mandatory field, you have to ensure that the user provides the information.
The user can choose to come back to a field later if they are not sure about the information.

Ask for confirmation after each field is filled.
Once the user confirms the information, you should save the data to backend by calling the persist_data_partial function. Then move on to the next field.
Before ending the converasation make sure all the mandatory fields are filled.

At the end of the conversation do NOT repeat all the fields confirmation if the user has already confirmed them once.

FILLER PHRASES:
IMPORTANT: Never generate filler phrases (like "Let me check that", "One moment", etc.) directly in your responses.
Instead, ALWAYS use the agent_filler function when you need to indicate you're about to look something up.

Examples of what NOT to do:
- Responding with "Let me look that up for you..." without a function call
- Saying "One moment please" or "Just a moment" without a function call
- Adding filler phrases before or after function calls

"""

VOICE = "aura-asteria-en"

USER_AUDIO_SAMPLE_RATE = 16000
Expand Down Expand Up @@ -184,14 +231,16 @@ def format(self, record):
"think": {
"provider": {"type": "open_ai"},
"model": "gpt-4o-mini",
"instructions": PROMPT_TEMPLATE,
"functions": FUNCTION_DEFINITIONS,
"instructions": MARIANA_PROMPT_TEMPLATE,
"functions": FUNC_DEF_2,
},
"speak": {"model": VOICE},
},
"context": {
"messages": [
{"role": "assistant", "content": "Hello! I'm Sarah from TechStyle customer service. How can I help you today?"}
# {"role": "assistant", "content": "Hello! I'm Sarah from TechStyle customer service. How can I help you today?"}
{"role": "assistant", "content": "Hello! I'm Maria from Mariana AI. I will be helping you with information gathering for your upcoming appointment. Are we good to go?"}

],
"replay": True
}
Expand All @@ -213,7 +262,8 @@ async def run():

# Format the prompt with the current date
current_date = datetime.now().strftime("%A, %B %d, %Y")
formatted_prompt = PROMPT_TEMPLATE.format(current_date=current_date)
# formatted_prompt = PROMPT_TEMPLATE.format(current_date=current_date)
formatted_prompt = MARIANA_PROMPT_TEMPLATE.format(current_date=current_date, patient_name="John Doe", appointment_date=" 25th February 2025", appointment_time="10:00 AM", doctor_name="Smith", clinic_name="XYZ Clinic", communication_id="comm_123456")

# Update the settings with the formatted prompt
settings = SETTINGS.copy()
Expand Down Expand Up @@ -302,12 +352,12 @@ async def receiver(ws):
function_call_id = message_json.get("function_call_id")
parameters = message_json.get("input", {})

logger.info(f"Function call received: {function_name}")
logger.info(f"Parameters: {parameters}")
logger.info(f">> Function call received: {function_name}")
logger.info(f">> Parameters: {parameters}")

start_time = time.time()
try:
func = FUNCTION_MAP.get(function_name)
func = FUNCTION_MAP_2.get(function_name)
if not func:
raise ValueError(f"Function {function_name} not found")

Expand Down
186 changes: 185 additions & 1 deletion functions.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import json
from datetime import datetime, timedelta
import asyncio
import aiohttp
from business_logic import (
get_customer,
get_customer_appointments,
Expand All @@ -11,6 +12,20 @@
prepare_farewell_message
)

async def call_pinger(params):
""" ping the save api """
print("pinging save api")
async with aiohttp.ClientSession() as session:
dumpData = {
"test_dump": "test",
"params": params
}
async with session.post("http://ptsv3.com/t/abctestesting123ramsesthelast/", json=dumpData) as response:
if response.status != 200:
return {"error": "Failed to ping save API"}
return {"message": "saved to backend"}


async def find_customer(params):
"""Look up a customer by phone, email, or ID."""
phone = params.get("phone")
Expand Down Expand Up @@ -232,6 +247,23 @@ async def end_call(websocket, params):
"required": ["start_date"]
}
},
{
"name": "persist_data",
"description": """make a call to initiate postprocessing and persist data to backend. Call the function when
- On completion of conversation when the user asks the data to be saved this function should be called.
- the user asks to save the data to the backend.
- the customer_id is to be provided as input paramaeter.
""",
"parameters": {
"type": "object",
"properties": {
"customer_id": {
"type": "string",
"description": "customer_id to be saved"
},
}
}
},
{
"name": "end_call",
"description": """End the conversation and close the connection. Call this function when:
Expand Down Expand Up @@ -261,6 +293,157 @@ async def end_call(websocket, params):
}
]

async def save_data(params):
"""Save data to backend"""
print("----------------")
print("pinging save api")
print("params", params)
print(" type of params", type(params))
print("-------------")
field_name = params.get("field_name")
field_value = params.get("field_value")
print("-------------")
print(f"field_name: {field_name} field_value: {field_value}")
print("-------------")
async with aiohttp.ClientSession() as session:
dumpData = {
"test_dump": "test",
"params": {
field_name: field_value
}
}
async with session.post("http://ptsv3.com/t/abctestesting123ramsesthelast/", json=dumpData) as response:
if response.status != 200:
return {"error": "Failed to ping save API"}
return {"message": "saved to backend"}

async def get_fields_to_fill(params):
return {
"fields": [
{"fieldName":"full_name", "fieldDescription": "Patient's Complete Legal Full Name", "mandatory": "true"},
{"fieldName":"dob", "fieldDescription": "Patient's Precise Date of Birth (MM/DD/YYYY)", "mandatory": "true"},
{"fieldName":"gender_identity", "fieldDescription": "Patient's Self-Identified Gender", "mandatory": "true"},
{"fieldName":"contact_info", "fieldDescription": "Patient's Primary Phone Number and Email Address", "mandatory": "true"},
{"fieldName":"emergency_contact", "fieldDescription": "Designated Emergency Contact's Full Name and Phone Number", "mandatory": "true"},
{"fieldName":"medications", "fieldDescription": "Current Prescription Details (Name, Dosage, Frequency)", "mandatory": "true"},
{"fieldName":"allergies", "fieldDescription": "Comprehensive List of Allergies (Medications, Food, Environmental)", "mandatory": "true"},
{"fieldName":"surgeries", "fieldDescription": "Detailed History of Previous Surgical Procedures and Hospitalizations", "mandatory": "true"},
{"fieldName":"chronic_conditions", "fieldDescription": "Ongoing Medical Conditions Requiring Continuous Management", "mandatory": "true"},
{"fieldName":"family_history", "fieldDescription": "Significant Hereditary Medical Conditions in Immediate Family", "mandatory": "true"},
{"fieldName":"reason_for_visit", "fieldDescription": "Primary Medical Concern or Purpose of Current Healthcare Consultation", "mandatory": "true"},
{"fieldName":"symptom_duration", "fieldDescription": "Detailed Timeline and Intensity of Current Symptoms", "mandatory": "true"},
{"fieldName":"pain_levels", "fieldDescription": "Quantitative and Qualitative Assessment of Current Pain", "mandatory": "true"},
{"fieldName":"weight_changes", "fieldDescription": "Significant Recent Fluctuations in Weight, Appetite, or Sleep Patterns", "mandatory": "true"},
{"fieldName":"smoking_status", "fieldDescription": "Comprehensive Tobacco Use History (Current, Former, Never)", "mandatory": "true"},
{"fieldName":"alcohol_consumption", "fieldDescription": "Detailed Alcohol Intake Patterns and Frequency", "mandatory": "true"},
{"fieldName":"drug_use", "fieldDescription": "History and Current Status of Recreational Substance Use", "mandatory": "true"},
{"fieldName":"exercise_habits", "fieldDescription": "Regular Physical Activity Routine and Intensity", "mandatory": "true"},
{"fieldName":"dietary_restrictions", "fieldDescription": "Specific Dietary Preferences, Allergies, or Restrictions", "mandatory": "true"},
{"fieldName":"stress_levels", "fieldDescription": "Current Psychological Stress Assessment", "mandatory": "true"},
{"fieldName":"mental_health_conditions", "fieldDescription": "Comprehensive Mental Health Treatment History", "mandatory": "true"},
{"fieldName":"mental_health_concerns", "fieldDescription": "Current Psychological or Emotional Health Challenges", "mandatory": "true"},
{"fieldName":"occupation", "fieldDescription": "Professional Role and Workplace Environment Details", "mandatory": "true"},
{"fieldName":"living_situation", "fieldDescription": "Current Residential Arrangement and Living Conditions", "mandatory": "true"},
{"fieldName":"major_life_changes", "fieldDescription": "Significant Recent Personal or Professional Transitions", "mandatory": "true"},
{"fieldName":"physical_exam", "fieldDescription": "Date of Most Recent Comprehensive Physical Examination", "mandatory": "false"},
{"fieldName":"immunization_status", "fieldDescription": "Complete Vaccination History and Current Immunization Records", "mandatory": "true"},
{"fieldName":"recent_screenings", "fieldDescription": "Recent Preventive Medical Screening Procedures", "mandatory": "false"},
{"fieldName":"other_concerns", "fieldDescription": "Additional Patient-Reported Health Information or Concerns", "mandatory": "false"}
]
}


FUNC_DEF_2 = [
{
"name": "agent_filler",
"description": """Use this function to provide natural conversational filler before looking up information.
ALWAYS call this function first with message_type='lookup' when you're about to look up customer information.
After calling this function, you MUST immediately follow up with the appropriate lookup function (e.g., find_customer).""",
"parameters": {
"type": "object",
"properties": {
"message_type": {
"type": "string",
"description": "Type of filler message to use. Use 'lookup' when about to search for information.",
"enum": ["lookup", "general"]
}
},
"required": ["message_type"]
}
},
{
"name": "get_fields_to_fill",
"description": """Get the list of fields to be filled by the user.
This function should be called at the beginning of the conversation to get the list of fields that need to be filled by the user.
The output of this function is the list of fields that need to be filled by the user.
Use this information to prompt the user to fill in the required fields.""",
"parameters": {
"type": "object",
"properties": {
"communication_id":{
"type": "string",
"description": "The id associated with the current form that is being filled"
}
},
"required": ["communication_id"]
}
},
{
"name": "persist_data_partial",
"description": """ function to save a informatioin field to the backend.
Once the information for a field has been collected this function should be called to save the data to the backend.
""",
"parameters": {
"type": "object",
"properties": {
"field_name": {
"type": "string",
"description": "the field name to be saved"
},
"field_value": {
"type": "string",
"description": "the field value to be saved"
},
}
}
},
{
"name": "end_call",
"description": """End the conversation and close the connection. Call this function when:
- User says goodbye, thank you, etc.
- User indicates they're done ("that's all I need", "I'm all set", etc.)
- User wants to end the conversation

Examples of triggers:
- "Thank you, bye!"
- "That's all I needed, thanks"
- "Have a good day"
- "Goodbye"
- "I'm done"

Do not call this function if the user is just saying thanks but continuing the conversation.""",
"parameters": {
"type": "object",
"properties": {
"farewell_type": {
"type": "string",
"description": "Type of farewell to use in response",
"enum": ["thanks", "general", "help"]
}
},
"required": ["farewell_type"]
}
}

]

FUNCTION_MAP_2 = {
"agent_filler": agent_filler,
"persist_data_partial": save_data,
"end_call": end_call,
"get_fields_to_fill": get_fields_to_fill
}

# Map function names to their implementations
FUNCTION_MAP = {
"find_customer": find_customer,
Expand All @@ -269,5 +452,6 @@ async def end_call(websocket, params):
"create_appointment": create_appointment,
"check_availability": check_availability,
"agent_filler": agent_filler,
"end_call": end_call
"end_call": end_call,
"persist_data": call_pinger,
}