Revamp booking flow
Build Agents Runtime / build (push) Successful in 2m58s
Details
Build Agents Runtime / build (push) Successful in 2m58s
Details
This commit is contained in:
parent
871d341161
commit
f9e8786921
|
|
@ -163,31 +163,53 @@ async def save_clinical_record_activity(payload: dict) -> str:
|
|||
|
||||
class AppointmentIntentSchema(BaseModel):
|
||||
is_booking_request: bool = Field(description="True if the user is asking to book, schedule, or modify an appointment.")
|
||||
department: str = Field(description="The department they want to visit (e.g. Cardiology, General, Dental). Leave empty if not specified.")
|
||||
appointment_date: str = Field(description="The date they want to visit (e.g. 'Monday', 'Tomorrow', '10/12/2026'). Leave empty if not specified.")
|
||||
appointment_time: str = Field(description="The time they want to visit (e.g. 'Morning', '3 PM'). Leave empty if not specified.")
|
||||
missing_info: list[str] = Field(description="List of fields that are still missing and need to be asked. E.g. ['department', 'appointment_date']")
|
||||
reply_message: str = Field(description="The message to send to the user. IMPORTANT: If asking for department, you MUST list the 4 available departments: Cardiology, General Medicine, Pediatrics, Orthopedics.")
|
||||
doctor_name: str = Field(description="The specific doctor they want to visit. Leave empty if not specified.")
|
||||
appointment_date: str = Field(description="The date they want to visit. Leave empty if not specified.")
|
||||
appointment_time: str = Field(description="The time they want to visit. Leave empty if not specified.")
|
||||
missing_info: list[str] = Field(description="List of fields that are still missing and need to be asked.")
|
||||
reply_message: str = Field(description="The exact text to send to the user. Ask for missing details using numbered lists.")
|
||||
|
||||
@activity.defn
|
||||
async def parse_booking_intent_activity(chat_history: list[dict]) -> dict:
|
||||
"""
|
||||
Parses the chat history using the LLM to extract booking intent and details.
|
||||
chat_history is a list of {"role": "user"|"assistant", "content": "..."}
|
||||
"""
|
||||
async def parse_booking_intent_activity(payload: dict) -> dict:
|
||||
chat_history = payload.get("chat_history", [])
|
||||
to_number = payload.get("to_number", "")
|
||||
|
||||
# 1. Fetch available doctors for the given tenant
|
||||
import psycopg2
|
||||
doctors = []
|
||||
try:
|
||||
conn = psycopg2.connect("postgres://postgres:curio_secret@curio-db:5432/curio")
|
||||
cur = conn.cursor()
|
||||
# Find tenant by whatsapp_number
|
||||
cur.execute("SELECT id FROM tenants WHERE whatsapp_number = %s OR whatsapp_number = %s", (to_number, to_number.replace('whatsapp:', '')))
|
||||
row = cur.fetchone()
|
||||
if row:
|
||||
tenant_id = row[0]
|
||||
cur.execute("SELECT name FROM users WHERE tenant_id = %s AND role = 'DOCTOR' AND status = 'active'", (tenant_id,))
|
||||
doctors = [doc[0] for doc in cur.fetchall()]
|
||||
cur.close()
|
||||
conn.close()
|
||||
except Exception as e:
|
||||
print(f"DB Error fetching doctors: {e}")
|
||||
|
||||
doctors_str = ", ".join(doctors) if doctors else "Any Available Doctor"
|
||||
|
||||
from langchain_core.prompts import PromptTemplate
|
||||
|
||||
prompt = PromptTemplate.from_template(
|
||||
"You are a helpful hospital receptionist bot talking to a patient on WhatsApp.\n"
|
||||
"Here is the conversation history:\n"
|
||||
"{history}\n\n"
|
||||
"Analyze the conversation. Extract any appointment details (department, date, time).\n"
|
||||
"If they want to book an appointment but haven't specified department, date, and time, "
|
||||
"add those to missing_info and formulate a reply_message asking for them.\n"
|
||||
"CRITICAL INSTRUCTIONS FOR ASKING:\n"
|
||||
"- If the user hasn't specified a department, you MUST list the 4 available departments (Cardiology, General Medicine, Pediatrics, Orthopedics) so they know what to choose. Do not make them guess!\n"
|
||||
"- If they haven't specified a time, suggest common intervals (e.g. 10:00 AM, 11:00 AM, 6:00 PM).\n"
|
||||
"If all details are present, set missing_info to empty and formulate a confirmation reply_message.\n"
|
||||
f"Available Doctors: {doctors_str}\n\n"
|
||||
"Analyze the conversation. Extract any appointment details (doctor_name, appointment_date, appointment_time).\n"
|
||||
"If they want to book an appointment, gather missing details sequentially:\n"
|
||||
"1. If doctor_name is missing, check the Available Doctors. If there is more than 1 doctor, ask the user to choose using a numbered list (1. Dr. X, 2. Dr. Y). If there is only 1 doctor, auto-select them and move to the next step.\n"
|
||||
"2. If appointment_date is missing, ask for a date and suggest 2 or 3 specific dates for the current week as a numbered list (1. Monday, 2. Tuesday).\n"
|
||||
"3. If appointment_time is missing, ask for a time and suggest two 30-minute time slots as a numbered list (1. 10:00 AM, 2. 10:30 AM).\n"
|
||||
"CRITICAL INSTRUCTIONS:\n"
|
||||
"- Only ask for ONE missing piece of information at a time.\n"
|
||||
"- Format your choices as strict numbered lists so the user can just type the number.\n"
|
||||
"- If all details are present, set missing_info to empty and formulate a confirmation reply_message.\n"
|
||||
"Format output strictly as JSON matching the schema."
|
||||
)
|
||||
|
||||
|
|
@ -204,14 +226,13 @@ async def parse_booking_intent_activity(chat_history: list[dict]) -> dict:
|
|||
return result.dict()
|
||||
except Exception as e:
|
||||
print(f"Failed to parse booking intent: {e}")
|
||||
# Fallback due to Pydantic strictness failures
|
||||
return {
|
||||
"is_booking_request": True,
|
||||
"department": "",
|
||||
"doctor_name": "",
|
||||
"appointment_date": "",
|
||||
"appointment_time": "",
|
||||
"missing_info": ["department", "appointment_date", "appointment_time"],
|
||||
"reply_message": "I'm sorry, I'm having trouble understanding. Could you please specify the department, date, and time you'd like to book?"
|
||||
"missing_info": ["doctor_name", "appointment_date", "appointment_time"],
|
||||
"reply_message": "I'm sorry, I encountered an error. Could you please let me know which doctor you want to book?"
|
||||
}
|
||||
|
||||
@activity.defn
|
||||
|
|
@ -252,7 +273,7 @@ async def save_appointment_activity(payload: dict) -> str:
|
|||
try:
|
||||
record = Appointment(
|
||||
phone_number=payload.get("phone_number", ""),
|
||||
department=payload.get("department", ""),
|
||||
doctor_name=payload.get("doctor_name", ""),
|
||||
appointment_date=payload.get("appointment_date", ""),
|
||||
appointment_time=payload.get("appointment_time", ""),
|
||||
status="CONFIRMED"
|
||||
|
|
|
|||
|
|
@ -33,7 +33,7 @@ class Appointment(Base):
|
|||
|
||||
id = Column(Integer, primary_key=True, index=True)
|
||||
phone_number = Column(String(50), index=True, nullable=False)
|
||||
department = Column(String(100), nullable=True)
|
||||
doctor_name = Column(String(100), nullable=True)
|
||||
appointment_date = Column(String(50), nullable=True)
|
||||
appointment_time = Column(String(50), nullable=True)
|
||||
status = Column(String(20), default="PENDING")
|
||||
|
|
|
|||
|
|
@ -130,14 +130,14 @@ class AppointmentBookingWorkflow:
|
|||
self._new_message_event.set()
|
||||
|
||||
@workflow.run
|
||||
async def run(self, initial_message: str, phone_number: str) -> dict:
|
||||
async def run(self, initial_message: str, phone_number: str, to_number: str = "") -> dict:
|
||||
self._chat_history.append({"role": "user", "content": initial_message})
|
||||
|
||||
while True:
|
||||
# 1. Parse intent and get next action
|
||||
intent_data = await workflow.execute_activity(
|
||||
"parse_booking_intent_activity",
|
||||
self._chat_history,
|
||||
{"chat_history": self._chat_history, "to_number": to_number},
|
||||
retry_policy=RetryPolicy(maximum_attempts=3), schedule_to_close_timeout=timedelta(minutes=5)
|
||||
)
|
||||
|
||||
|
|
@ -159,7 +159,7 @@ class AppointmentBookingWorkflow:
|
|||
"save_appointment_activity",
|
||||
{
|
||||
"phone_number": phone_number,
|
||||
"department": intent_data.get("department"),
|
||||
"doctor_name": intent_data.get("doctor_name"),
|
||||
"appointment_date": intent_data.get("appointment_date"),
|
||||
"appointment_time": intent_data.get("appointment_time")
|
||||
},
|
||||
|
|
|
|||
Loading…
Reference in New Issue