From 2f7e2629fe4b6ccd031d7b67d041fe50c3b88ad3 Mon Sep 17 00:00:00 2001 From: tocmo0nlord Date: Sun, 28 Jun 2026 00:18:40 +0000 Subject: [PATCH] Lead-quality gate: require a name or location, not just a reason MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 --- CLAUDE.md | 3 +++ extract.py | 10 +++++----- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/CLAUDE.md b/CLAUDE.md index bd3aeff..c5fe49d 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -86,6 +86,9 @@ Trade-off: half-duplex — the caller can't barge in mid-utterance (fine for sho **Post-call extraction (`extract.py`)** — single JSON-mode completion after call ends. Correctly uses `format: json`, uses verified Twilio caller-ID instead of trusting model output, falls back to JSONL if Odoo is unreachable. Keep it. +**Lead-quality gate:** a lead is only written if a NAME or a LOCATION was captured — a bare +reason (e.g. "check on my eyes") with no name and no office is skipped (not worth a worklist +card). Captures full name, phone (confirmed/alternate), insurance, reason, and resolved date. **Odoo integration (`odoo_client.py`)** — already uses `ODOO_API_KEY` for XML-RPC auth, not password. Correct pattern. No changes. diff --git a/extract.py b/extract.py index 6059c96..09c7db1 100644 --- a/extract.py +++ b/extract.py @@ -80,12 +80,12 @@ async def extract_and_record(messages, ollama_url, model, call_sid=None, caller_ logger.info("Post-call extraction: no appointment requested") return None - # Don't create near-empty cards from quick hang-ups: require at least a name or a - # reason. A bare location + caller-ID isn't enough to be worth a worklist card. + # Lead-quality gate: a usable lead needs a NAME or a LOCATION (so staff can act on it) — + # a bare reason like "check on my eyes" with no name and no office is not worth a card. name = (data.get("patient_name") or "").strip() - reason_raw = (data.get("reason") or "").strip() - if not name and not reason_raw: - logger.info("Post-call extraction: appointment intent but no name/reason captured — skipping card") + location = (data.get("location") or "").strip() + if not name and not location: + logger.info("Post-call extraction: appointment intent but no name/location captured — skipping card") return None # Callback number: default to the verified Twilio caller-ID. If the caller explicitly