Commit ยท
df718f6
1
Parent(s): 58fed26
changed voice and db mendatory points for appointment
Browse files- .env +1 -1
- core/backend.py +85 -32
- db_view/db.html +4 -0
- db_view/dbapi.py +2 -0
.env
CHANGED
|
@@ -9,7 +9,7 @@ GOOGLE_API_KEY="AIzaSyA9sqz4YKQHKXR9TU1imw0DPOghzHOMiBo"
|
|
| 9 |
|
| 10 |
|
| 11 |
ELEVENLABS_API_KEY="b3af3a938c8e15d5eae700ea47eea7d88dfe397f34fbd4b0c75c24f143b032b8"
|
| 12 |
-
ELEVENLABS_VOICE_ID="
|
| 13 |
ELEVENLABS_MODEL_ID="eleven_v3"
|
| 14 |
|
| 15 |
# TWILIO_ACCOUNT_SID="ACfafc0d2d007bdf14b21bb3e14a7a7b31"
|
|
|
|
| 9 |
|
| 10 |
|
| 11 |
ELEVENLABS_API_KEY="b3af3a938c8e15d5eae700ea47eea7d88dfe397f34fbd4b0c75c24f143b032b8"
|
| 12 |
+
ELEVENLABS_VOICE_ID="GrHQRXD136YZl3kbtri3"
|
| 13 |
ELEVENLABS_MODEL_ID="eleven_v3"
|
| 14 |
|
| 15 |
# TWILIO_ACCOUNT_SID="ACfafc0d2d007bdf14b21bb3e14a7a7b31"
|
core/backend.py
CHANGED
|
@@ -577,6 +577,8 @@ async def book_appointment(
|
|
| 577 |
patient_age: str = "",
|
| 578 |
patient_num: str = "",
|
| 579 |
visiting_date: str = "",
|
|
|
|
|
|
|
| 580 |
patient_mail: str = ""
|
| 581 |
) -> str:
|
| 582 |
"""
|
|
@@ -588,8 +590,10 @@ async def book_appointment(
|
|
| 588 |
patient_name: Full name of the patient.
|
| 589 |
patient_age: Age of the patient (e.g. "32").
|
| 590 |
patient_num: Contact phone number of the patient.
|
| 591 |
-
visiting_date: Date of visit in YYYY-MM-DD format or natural text.
|
| 592 |
-
|
|
|
|
|
|
|
| 593 |
"""
|
| 594 |
db_path = get_db_path()
|
| 595 |
patient_num = format_bd_number(patient_num)
|
|
@@ -598,16 +602,32 @@ async def book_appointment(
|
|
| 598 |
doctor_name = _clean_text(doctor_name)
|
| 599 |
category = _clean_text(category)
|
| 600 |
visiting_date = _clean_text(visiting_date)
|
|
|
|
|
|
|
| 601 |
patient_mail = _clean_text(patient_mail)
|
| 602 |
-
|
| 603 |
-
if
|
| 604 |
-
|
| 605 |
-
|
| 606 |
-
|
| 607 |
-
|
| 608 |
-
|
| 609 |
-
|
| 610 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 611 |
|
| 612 |
async with aiosqlite.connect(db_path) as db:
|
| 613 |
db.row_factory = aiosqlite.Row
|
|
@@ -640,7 +660,23 @@ async def book_appointment(
|
|
| 640 |
doctor_data = dict(doctor)
|
| 641 |
doctor_name = doctor_data.get("doctor_name", "Unknown")
|
| 642 |
doctor_category = doctor_data.get("category", "Unknown")
|
| 643 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 644 |
|
| 645 |
cursor = await db.execute(
|
| 646 |
"""SELECT id FROM patients
|
|
@@ -653,35 +689,33 @@ async def book_appointment(
|
|
| 653 |
f"on {visiting_date} already exists."
|
| 654 |
)
|
| 655 |
|
|
|
|
| 656 |
await db.execute(
|
| 657 |
"""INSERT INTO patients
|
| 658 |
-
(doctor_name, doctor_category, patient_name, patient_age, patient_num, visiting_date, patient_mail)
|
| 659 |
-
VALUES (?, ?, ?, ?, ?, ?, ?)""",
|
| 660 |
-
(doctor_name, doctor_category, patient_name, patient_age, patient_num, visiting_date, patient_mail),
|
| 661 |
)
|
| 662 |
await db.commit()
|
| 663 |
|
| 664 |
-
# Mail confirmation is
|
| 665 |
mail_message = (
|
| 666 |
f"Doctor : {doctor_name}\n"
|
| 667 |
f"Patient : {patient_name}\n"
|
| 668 |
f"Visit Date : {visiting_date}\n"
|
|
|
|
| 669 |
f"Visit Time : {visiting_time}\n"
|
| 670 |
f"Please arrive on time."
|
| 671 |
)
|
| 672 |
-
|
| 673 |
-
|
| 674 |
-
|
| 675 |
-
|
| 676 |
-
|
| 677 |
-
|
| 678 |
-
|
| 679 |
-
|
| 680 |
-
|
| 681 |
-
except Exception as e:
|
| 682 |
-
mail_status = f"\nโ ๏ธ Mail failed: {str(e)}"
|
| 683 |
-
else:
|
| 684 |
-
mail_status = "\n๐ง No email provided, so email confirmation was skipped."
|
| 685 |
|
| 686 |
return (
|
| 687 |
f"โ
Appointment Booked!\n"
|
|
@@ -690,7 +724,10 @@ async def book_appointment(
|
|
| 690 |
f"Patient : {patient_name}\n"
|
| 691 |
f"Age : {patient_age}\n"
|
| 692 |
f"Date : {visiting_date}\n"
|
|
|
|
|
|
|
| 693 |
f"Contact : {patient_num}\n"
|
|
|
|
| 694 |
f"โโโโโโโโโโโโโโโโโโโโโโ\n"
|
| 695 |
f"Please arrive on time."
|
| 696 |
f"{mail_status}"
|
|
@@ -800,7 +837,7 @@ TOOL RULES:
|
|
| 800 |
- Use `book_appointment` only after identifying the doctor and required patient details.
|
| 801 |
- Never invent `doctor_id`. Get it from tool results or resolve by doctor_name/category.
|
| 802 |
- If the user gives a Bangla date like "เฆเฆเฆพเฆฎเงเฆเฆพเฆฒ" or "เฆชเฆฐเฆถเง", convert it to a real date before booking.
|
| 803 |
-
-
|
| 804 |
- If the user already provided name, age, phone, and date and then confirms, call `book_appointment` immediately.
|
| 805 |
- If the user asks to cancel and only gives a phone number, cancel the single matching appointment if there is exactly one.
|
| 806 |
|
|
@@ -838,8 +875,9 @@ You must now choose the correct tool instead of answering in prose:
|
|
| 838 |
- Use `delete_appointment` when the user is cancelling a booking.
|
| 839 |
|
| 840 |
Important booking rules:
|
| 841 |
-
- Email is
|
| 842 |
-
-
|
|
|
|
| 843 |
- If the user has already confirmed the details, book immediately.
|
| 844 |
|
| 845 |
Important cancellation rules:
|
|
@@ -923,11 +961,26 @@ class AIBackend:
|
|
| 923 |
patient_age TEXT,
|
| 924 |
patient_num TEXT,
|
| 925 |
visiting_date TEXT,
|
|
|
|
|
|
|
| 926 |
patient_mail TEXT
|
| 927 |
)
|
| 928 |
""")
|
| 929 |
await self.conn.commit()
|
| 930 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 931 |
# โโ Summarise node โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
| 932 |
async def summarize_conversation(self, state: ChatState):
|
| 933 |
existing = state.get("summary", "")
|
|
|
|
| 577 |
patient_age: str = "",
|
| 578 |
patient_num: str = "",
|
| 579 |
visiting_date: str = "",
|
| 580 |
+
visiting_day: str = "",
|
| 581 |
+
visiting_time: str = "",
|
| 582 |
patient_mail: str = ""
|
| 583 |
) -> str:
|
| 584 |
"""
|
|
|
|
| 590 |
patient_name: Full name of the patient.
|
| 591 |
patient_age: Age of the patient (e.g. "32").
|
| 592 |
patient_num: Contact phone number of the patient.
|
| 593 |
+
visiting_date: Date of visit in YYYY-MM-DD format or natural text (optional if visiting_day is provided).
|
| 594 |
+
visiting_day: Day of visit (e.g. "Sunday", "เฆฐเฆฌเฆฟเฆฌเฆพเฆฐ", "today") โ required if visiting_date is not provided.
|
| 595 |
+
visiting_time: Time of visit (e.g. "6pm-9pm") โ required (can be auto-filled from doctor record if missing).
|
| 596 |
+
patient_mail: Required email address for confirmation mail.
|
| 597 |
"""
|
| 598 |
db_path = get_db_path()
|
| 599 |
patient_num = format_bd_number(patient_num)
|
|
|
|
| 602 |
doctor_name = _clean_text(doctor_name)
|
| 603 |
category = _clean_text(category)
|
| 604 |
visiting_date = _clean_text(visiting_date)
|
| 605 |
+
visiting_day = _clean_text(visiting_day)
|
| 606 |
+
visiting_time = _clean_text(visiting_time)
|
| 607 |
patient_mail = _clean_text(patient_mail)
|
| 608 |
+
|
| 609 |
+
if visiting_date:
|
| 610 |
+
parsed_date = _parse_visit_date(visiting_date)
|
| 611 |
+
if parsed_date:
|
| 612 |
+
visiting_date = parsed_date
|
| 613 |
+
elif visiting_day:
|
| 614 |
+
parsed_date = _parse_visit_date(visiting_day)
|
| 615 |
+
if parsed_date:
|
| 616 |
+
visiting_date = parsed_date
|
| 617 |
+
|
| 618 |
+
# Mandatory fields
|
| 619 |
+
if not patient_name:
|
| 620 |
+
return "Missing booking details. Need patient name."
|
| 621 |
+
if not patient_age:
|
| 622 |
+
return "Missing booking details. Need patient age."
|
| 623 |
+
if not patient_num:
|
| 624 |
+
return "Missing booking details. Need patient phone number."
|
| 625 |
+
if not (doctor_id or doctor_name):
|
| 626 |
+
return "Missing booking details. Need doctor name."
|
| 627 |
+
if not visiting_date:
|
| 628 |
+
return "Missing booking details. Need day/date to visit the doctor."
|
| 629 |
+
if not patient_mail:
|
| 630 |
+
return "Missing booking details. Need email address for confirmation."
|
| 631 |
|
| 632 |
async with aiosqlite.connect(db_path) as db:
|
| 633 |
db.row_factory = aiosqlite.Row
|
|
|
|
| 660 |
doctor_data = dict(doctor)
|
| 661 |
doctor_name = doctor_data.get("doctor_name", "Unknown")
|
| 662 |
doctor_category = doctor_data.get("category", "Unknown")
|
| 663 |
+
doctor_visiting_days = doctor_data.get("visiting_days", "") or ""
|
| 664 |
+
doctor_visiting_time = doctor_data.get("visiting_time", "") or ""
|
| 665 |
+
|
| 666 |
+
# Auto-fill visiting_time from doctor record if caller didn't provide it
|
| 667 |
+
if not visiting_time:
|
| 668 |
+
visiting_time = doctor_visiting_time.strip()
|
| 669 |
+
if not visiting_time:
|
| 670 |
+
return "Missing booking details. Need time to visit the doctor."
|
| 671 |
+
|
| 672 |
+
# Keep visiting_day if provided; otherwise derive from date (English day)
|
| 673 |
+
if not visiting_day and visiting_date:
|
| 674 |
+
try:
|
| 675 |
+
import datetime as _dt
|
| 676 |
+
y, m, d = [int(x) for x in visiting_date.split("-")]
|
| 677 |
+
visiting_day = _dt.date(y, m, d).strftime("%A")
|
| 678 |
+
except Exception:
|
| 679 |
+
visiting_day = ""
|
| 680 |
|
| 681 |
cursor = await db.execute(
|
| 682 |
"""SELECT id FROM patients
|
|
|
|
| 689 |
f"on {visiting_date} already exists."
|
| 690 |
)
|
| 691 |
|
| 692 |
+
# Create booking
|
| 693 |
await db.execute(
|
| 694 |
"""INSERT INTO patients
|
| 695 |
+
(doctor_name, doctor_category, patient_name, patient_age, patient_num, visiting_date, visiting_day, visiting_time, patient_mail)
|
| 696 |
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)""",
|
| 697 |
+
(doctor_name, doctor_category, patient_name, patient_age, patient_num, visiting_date, visiting_day, visiting_time, patient_mail),
|
| 698 |
)
|
| 699 |
await db.commit()
|
| 700 |
|
| 701 |
+
# Mail confirmation is mandatory.
|
| 702 |
mail_message = (
|
| 703 |
f"Doctor : {doctor_name}\n"
|
| 704 |
f"Patient : {patient_name}\n"
|
| 705 |
f"Visit Date : {visiting_date}\n"
|
| 706 |
+
f"Visit Day : {visiting_day}\n"
|
| 707 |
f"Visit Time : {visiting_time}\n"
|
| 708 |
f"Please arrive on time."
|
| 709 |
)
|
| 710 |
+
try:
|
| 711 |
+
await send_mail(
|
| 712 |
+
to_mail=patient_mail,
|
| 713 |
+
subject="โ
Appointment Confirmed!",
|
| 714 |
+
body=mail_message,
|
| 715 |
+
)
|
| 716 |
+
mail_status = "\n๐ง Confirmation mail sent."
|
| 717 |
+
except Exception as e:
|
| 718 |
+
mail_status = f"\nโ ๏ธ Mail failed: {str(e)}"
|
|
|
|
|
|
|
|
|
|
|
|
|
| 719 |
|
| 720 |
return (
|
| 721 |
f"โ
Appointment Booked!\n"
|
|
|
|
| 724 |
f"Patient : {patient_name}\n"
|
| 725 |
f"Age : {patient_age}\n"
|
| 726 |
f"Date : {visiting_date}\n"
|
| 727 |
+
f"Day : {visiting_day}\n"
|
| 728 |
+
f"Time : {visiting_time}\n"
|
| 729 |
f"Contact : {patient_num}\n"
|
| 730 |
+
f"Email : {patient_mail}\n"
|
| 731 |
f"โโโโโโโโโโโโโโโโโโโโโโ\n"
|
| 732 |
f"Please arrive on time."
|
| 733 |
f"{mail_status}"
|
|
|
|
| 837 |
- Use `book_appointment` only after identifying the doctor and required patient details.
|
| 838 |
- Never invent `doctor_id`. Get it from tool results or resolve by doctor_name/category.
|
| 839 |
- If the user gives a Bangla date like "เฆเฆเฆพเฆฎเงเฆเฆพเฆฒ" or "เฆชเฆฐเฆถเง", convert it to a real date before booking.
|
| 840 |
+
- Email is REQUIRED for booking and must be used to send a confirmation mail.
|
| 841 |
- If the user already provided name, age, phone, and date and then confirms, call `book_appointment` immediately.
|
| 842 |
- If the user asks to cancel and only gives a phone number, cancel the single matching appointment if there is exactly one.
|
| 843 |
|
|
|
|
| 875 |
- Use `delete_appointment` when the user is cancelling a booking.
|
| 876 |
|
| 877 |
Important booking rules:
|
| 878 |
+
- Email is REQUIRED. Do not book without an email address.
|
| 879 |
+
- Visiting time is REQUIRED. If the doctor record has a visiting_time, use it and confirm it with the user.
|
| 880 |
+
- If the user already gave name, age, phone, doctor name, visit day/date, visit time, and email, do not ask again.
|
| 881 |
- If the user has already confirmed the details, book immediately.
|
| 882 |
|
| 883 |
Important cancellation rules:
|
|
|
|
| 961 |
patient_age TEXT,
|
| 962 |
patient_num TEXT,
|
| 963 |
visiting_date TEXT,
|
| 964 |
+
visiting_day TEXT,
|
| 965 |
+
visiting_time TEXT,
|
| 966 |
patient_mail TEXT
|
| 967 |
)
|
| 968 |
""")
|
| 969 |
await self.conn.commit()
|
| 970 |
|
| 971 |
+
# Lightweight migrations for older DBs
|
| 972 |
+
async def _ensure_column(table: str, col: str, col_type: str) -> None:
|
| 973 |
+
async with self.conn.execute(f"PRAGMA table_info({table})") as cur:
|
| 974 |
+
rows = await cur.fetchall()
|
| 975 |
+
existing = {r[1] for r in rows} # (cid,name,type,notnull,dflt,pk)
|
| 976 |
+
if col in existing:
|
| 977 |
+
return
|
| 978 |
+
await self.conn.execute(f"ALTER TABLE {table} ADD COLUMN {col} {col_type}")
|
| 979 |
+
await self.conn.commit()
|
| 980 |
+
|
| 981 |
+
await _ensure_column("patients", "visiting_day", "TEXT")
|
| 982 |
+
await _ensure_column("patients", "visiting_time", "TEXT")
|
| 983 |
+
|
| 984 |
# โโ Summarise node โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
| 985 |
async def summarize_conversation(self, state: ChatState):
|
| 986 |
existing = state.get("summary", "")
|
db_view/db.html
CHANGED
|
@@ -114,6 +114,8 @@
|
|
| 114 |
<th>Doctor</th>
|
| 115 |
<th>Category</th>
|
| 116 |
<th>Visiting Date</th>
|
|
|
|
|
|
|
| 117 |
</tr>
|
| 118 |
</thead>
|
| 119 |
<tbody></tbody>
|
|
@@ -186,6 +188,8 @@
|
|
| 186 |
<td>${patient.doctor_name}</td>
|
| 187 |
<td>${patient.doctor_category}</td>
|
| 188 |
<td>${patient.visiting_date}</td>
|
|
|
|
|
|
|
| 189 |
</tr>`).join('');
|
| 190 |
|
| 191 |
loading.style.display = 'none';
|
|
|
|
| 114 |
<th>Doctor</th>
|
| 115 |
<th>Category</th>
|
| 116 |
<th>Visiting Date</th>
|
| 117 |
+
<th>Visiting Day</th>
|
| 118 |
+
<th>Visiting Time</th>
|
| 119 |
</tr>
|
| 120 |
</thead>
|
| 121 |
<tbody></tbody>
|
|
|
|
| 188 |
<td>${patient.doctor_name}</td>
|
| 189 |
<td>${patient.doctor_category}</td>
|
| 190 |
<td>${patient.visiting_date}</td>
|
| 191 |
+
<td>${patient.visiting_day || ''}</td>
|
| 192 |
+
<td>${patient.visiting_time || ''}</td>
|
| 193 |
</tr>`).join('');
|
| 194 |
|
| 195 |
loading.style.display = 'none';
|
db_view/dbapi.py
CHANGED
|
@@ -45,6 +45,8 @@ class Patient(BaseModel):
|
|
| 45 |
patient_age: str
|
| 46 |
patient_num: str
|
| 47 |
visiting_date: str
|
|
|
|
|
|
|
| 48 |
patient_mail: str
|
| 49 |
|
| 50 |
class AllDataResponse(BaseModel):
|
|
|
|
| 45 |
patient_age: str
|
| 46 |
patient_num: str
|
| 47 |
visiting_date: str
|
| 48 |
+
visiting_day: str | None = ""
|
| 49 |
+
visiting_time: str | None = ""
|
| 50 |
patient_mail: str
|
| 51 |
|
| 52 |
class AllDataResponse(BaseModel):
|