fix: recognize limited exam, 1 PA (#tooth), composite # NN surfaces, and check [patient name]

- Add limited exam / emergency exam → D0140 to DIRECT_CODE_MAP for reliable lookup
- Fix parseCompositeCode regex to allow space between # and digit (# 10 DL, # 11 ML)
- Strip empty parens from strippedCleaned so "1 pa (#13)" → "1 pa" hits DIRECT_CODE_MAP
- LLM prompt: add single-PA-with-tooth examples (1 PA (#13) → ["1 pa, #13"])
- LLM prompt: clarify check_eligibility vs navigate_eligibility — "check Qu" is patient name, not insurance

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-06-24 09:05:21 -04:00
parent 8154e904b9
commit 528a30efc6
2 changed files with 14 additions and 5 deletions

View File

@@ -147,6 +147,9 @@ const ALIAS_MAP: Record<string, string> = {
// Direct phrase → CDT code for terms not in the MH fee schedule JSON
// or short abbreviations that need precise mapping (checked before keyword search)
const DIRECT_CODE_MAP: Record<string, string> = {
// Exams
"limited exam": "D0140",
"emergency exam": "D0140",
// X-rays
"2bw": "D0272",
"2 bw": "D0272",
@@ -265,7 +268,7 @@ const RCT_MOLAR = new Set([1, 2, 3, 14, 15, 16, 17, 18, 19, 30, 31, 32]); /
function parseCompositeCode(input: string): CdtMatch | null {
const s = input.trim();
// Match #NN SURFACES or tooth NN SURFACES (surfaces = 1-5 letters, valid dental surface chars)
const m = s.match(/#(\d{1,2})\s+([A-Za-z]{1,5})\b/) ?? s.match(/\btooth\s+(\d{1,2})\s+([A-Za-z]{1,5})\b/i);
const m = s.match(/#\s*(\d{1,2})\s+([A-Za-z]{1,5})\b/) ?? s.match(/\btooth\s+(\d{1,2})\s+([A-Za-z]{1,5})\b/i);
if (!m) return null;
const toothNum = parseInt(m[1]!, 10);
@@ -467,6 +470,7 @@ export function lookupCdtCodes(
const strippedCleaned = cleaned
.replace(/,?\s*#\s*\d{1,2}\b/g, "")
.replace(/,?\s*#\s*[a-t]\b/gi, "")
.replace(/\(\s*\)/g, "")
.replace(/\s+/g, " ")
.trim();

View File

@@ -71,6 +71,8 @@ Omit any field that is not present in the message or history.
Intents:
- check_eligibility : user wants to check insurance for a patient identified by NAME only
e.g. "check Maria Jesus", "verify insurance for John Smith"
e.g. "check Qu", "check Li", "check Kim" — a short word after "check" is a patient last name, NOT an insurance abbreviation
Insurance abbreviations are: mh, masshealth, bcbs, cca, dentaquest — anything else is a patient name
- eligibility_by_id : user provides a SINGLE member ID and date of birth (no patient name)
e.g. "check masshealth for 100xxxx, 10/10/1988"
ALSO use this when user wants to check eligibility AND schedule/add an appointment on a date
@@ -121,8 +123,9 @@ Intents:
Use this when the user says "preauth", "pre auth", "pre-auth", or "prior auth"
- navigate_claims : open the claims page
- navigate_schedule : open the appointments/schedule page
- navigate_eligibility : open the insurance eligibility page
- navigate_eligibility : open the insurance eligibility page — ONLY when user says a known insurance keyword with no patient name
e.g. "check mh", "check masshealth", "open eligibility", "go to eligibility", "check insurance"
Do NOT use this for "check [name]" — that is check_eligibility
- general : anything else
Rules:
@@ -134,7 +137,8 @@ Rules:
do NOT include it in procedureNames. It refers to a file attachment, not a billable procedure.
Only include actual clinical procedures in procedureNames.
- For composite fillings with a tooth number, preserve the EXACT notation including tooth# and surfaces:
e.g. "composite #29 O", "#8 MO", "composite #11 MOD" — keep the #number and surface letters together as one entry
e.g. "composite #29 O", "#8 MO", "composite #11 MOD", "# 10 DL", "# 11 ML" — keep the #number and surface letters together as one entry
Note: "# 10 DL" and "composite on # 10 DL" are the same — preserve the space-after-# as-is
- #number always means a TOOTH number (never a case or pre-auth reference). When a single #number appears before a comma-separated list of procedures, apply it to EVERY procedure in the list.
e.g. "#20 rct, post, crown" → ["#20 rct", "#20 post", "#20 crown"]
e.g. "preauth #20 rct, pos, crown" → ["#20 rct", "#20 pos", "#20 crown"]
@@ -147,8 +151,9 @@ Rules:
The tooth# after a CDT code belongs only to that procedure.
- For SRP with a quadrant abbreviation (UL, UR, LL, LR), keep the code and quadrant together as one entry:
e.g. "D4341 UL", "4341 LR", "D4342 UR" — the quadrant always travels with the SRP code
- For multiple PA X-rays with tooth numbers, expand each PA into its own entry:
ALWAYS use "1 pa, #T" for the first tooth and "2nd pa, #T" for EVERY additional tooth (never "3rd pa", "4th pa", etc.)
- For PA X-rays with tooth numbers, always use "1 pa, #T" format for the first/only PA and "2nd pa, #T" for every additional:
e.g. "1 PA (#13)" → ["1 pa, #13"]
e.g. "PA #13" → ["1 pa, #13"]
e.g. "2 PA (#30, 15)" → ["1 pa, #30", "2nd pa, #15"]
e.g. "3 PA (#3, 14, 30)" → ["1 pa, #3", "2nd pa, #14", "2nd pa, #30"]
e.g. "4 PA (#3, 14, 19, 30)" → ["1 pa, #3", "2nd pa, #14", "2nd pa, #19", "2nd pa, #30"]