Commit Graph

7 Commits

Author SHA1 Message Date
tocmo0nlord
bbfff482a3 Attach call recording + transcript to the Odoo lead (Phase 3 item)
odoo_client.attach_call_artifacts posts the transcript to the lead's chatter
(readable in place) and attaches transcript_<sid>.txt + the stereo call WAV.
bot.py wires it post-call: extract_and_record now returns the Odoo ref
(persisted_to), the in-call tool path stamps it too, the audiobuffer handler
records the saved WAV path, and the attach runs in a thread after the lead
lands (brief wait for the async WAV write; failures only log — the lead is
already safe). Verified e2e against db1: create -> attach -> verify -> delete.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-04 17:03:00 +00:00
tocmo0nlord
97e109ed89 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>
2026-06-29 14:46:23 +00:00
tocmo0nlord
2f7e2629fe Lead-quality gate: require a name or location, not just a reason
A call produced a near-empty lead (name=None, location=None, only reason="check
on my eyes"). Changed the extraction gate from "name OR reason" to "name OR
location" — a bare reason with no name and no office isn't an actionable
worklist card, so skip it. Verified: reason-only -> skip; name-only, location-
only, both -> keep.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-28 00:18:40 +00:00
tocmo0nlord
550550975f Capture symptom reasons; switch startup warmup to lifespan
- Reason extraction missed symptom-style reasons: a caller said "I'm actually
  blind" and the lead logged reason=None (it caught "disintegrated eyes" before
  but not this). Broadened the extractor's reason rule to capture the eye
  problem/symptom as the reason, not just visit types. Verified 3/3 -> "vision
  loss / blindness".
- server.py: move the LLM warmup/pin (keep_alive=-1) from the deprecated
  on_event("startup") to a lifespan handler — silences the FastAPI deprecation
  warning; model still shows ollama ps UNTIL=Forever.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-27 04:37:45 +00:00
tocmo0nlord
ba36ae6891 Log/surface the reason, pin LLM warm for latency, doc insurance rule
- Reason visibility: the reason WAS extracted ("disintegrated eyes") but only
  lived in the Odoo description note. Add it to the post-call log line and to
  the Odoo lead title so it's visible at a glance.
- Latency: split the timing — Whisper is ~0.1s, latency is LLM-side. The ~3s
  tail was cold model reloads after Ollama's keep-alive expired. server.py now
  warms + pins the model on startup (keep_alive=-1, ollama ps UNTIL=Forever),
  removing cold first-turn stalls. Whisper size left alone (not the bottleneck).
- CLAUDE.md: insurance rule (never suggest/guess the plan), latency note.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-27 04:24:10 +00:00
tocmo0nlord
b8c71b15c2 Capture full appointment details + validate dates in-call
In-call (system prompt + per-call calendar injection):
- Gather full name (prompt asks for last name if only first given).
- Confirm the caller-ID number; if declined, use the number the caller gives.
- Ask for and LOG insurance only — never promise/confirm/deny coverage or
  treatment based on it; staff verify on callback.
- Validate the requested date against an injected 45-day calendar (recomputed
  per call since the server is long-running). Push back on impossible/mismatched
  dates, e.g. "Monday lands on the sixth — would you like that date?".
- AGENT_NAME=AVA; 4s grace pause before hang-up (HANGUP_DELAY_SECS).

Logging (post-call extraction -> Odoo):
- Extract full name, phone_confirmed, chosen callback (caller-ID or alternate),
  insurance, reason, and preferred time annotated with a resolved YYYY-MM-DD
  date (today's date is fed to the extractor).
- odoo_client: insurance row on the lead note (log only — staff verify).

.gitignore: ignore rotated avc_run.log* files.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-25 03:00:35 +00:00
tocmo0nlord
c3c719b77e Initial commit: avc-phone-ai codebase + CLAUDE.md 2026-06-23 22:38:22 +00:00