Handle non-booking requests: take a message, log a callback note

A caller asking if their already-purchased frames were ready got railroaded
through the booking script and hung up. AVA had no path for requests it can't do
on the phone.

- Prompt: classify intent first — question (answer it), can't-do request (take
  name + a one-line note, confirm callback number, promise a staff callback;
  never force booking questions), or booking (the ordered steps).
- extract.py: request_type = appointment | callback | none. Callback gate needs
  a name or a request note. Records kind.
- practice.py / odoo_client.py: callbacks write a "📞 Callback request" lead
  (name, callback number, what they need) instead of an appointment card.

Verified the classifier: frames-status -> callback, booking -> appointment,
pure question -> none.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
tocmo0nlord
2026-06-29 14:46:23 +00:00
parent 2f7e2629fe
commit 97e109ed89
5 changed files with 90 additions and 37 deletions

View File

@@ -59,20 +59,32 @@ def _find_or_create_partner(uid, models, name, phone):
def create_appointment_request(patient_name, callback_number, reason, preferred_time,
insurance=None, call_sid=None):
"""Create the request in Odoo. Returns (model, record_id) or raises OdooError."""
insurance=None, call_sid=None, kind="appointment"):
"""Create the request in Odoo. `kind` is "appointment" or "callback" (a message for staff
to call the caller back about something we can't do on the phone). Returns (model, id)."""
uid, models = _connect()
summary = f"📞 Phone appt — {patient_name or 'caller'}" + (f": {reason}" if reason else "")
if kind == "callback":
summary = f"📞 Callback request — {patient_name or 'caller'}" + (f": {reason}" if reason else "")
header = "<b>Captured by the AVC phone agent</b> — CALL THE PATIENT BACK about this request."
rows = [
("Name", patient_name),
("Callback", callback_number),
("What they need", reason),
("Twilio call SID", call_sid),
]
else:
summary = f"📞 Phone appt — {patient_name or 'caller'}" + (f": {reason}" if reason else "")
header = "<b>Captured by the AVC phone agent</b> (UNCONFIRMED — call patient to finalize)."
rows = [
("Name", patient_name),
("Callback", callback_number),
("Reason", reason),
("Insurance (log only — staff to verify coverage)", insurance),
("Preferred time (patient's words)", preferred_time),
("Twilio call SID", call_sid),
]
# description is an Odoo HTML field — build with <br/> so it renders in the UI.
rows = [
("Name", patient_name),
("Callback", callback_number),
("Reason", reason),
("Insurance (log only — staff to verify coverage)", insurance),
("Preferred time (patient's words)", preferred_time),
("Twilio call SID", call_sid),
]
note = "<p><b>Captured by the AVC phone agent</b> (UNCONFIRMED — call patient to finalize).</p><p>" + \
note = f"<p>{header}</p><p>" + \
"<br/>".join(f"{escape(k)}: {escape(str(v)) if v else ''}" for k, v in rows) + "</p>"
if ODOO_TARGET == "calendar":