File size: 30,008 Bytes
e1a9577 70d7b49 e1a9577 70d7b49 e1a9577 70d7b49 e1a9577 70d7b49 e1a9577 70d7b49 e1a9577 70d7b49 e1a9577 70d7b49 e1a9577 70d7b49 e1a9577 f2e05cd e1a9577 70d7b49 9deb1ad e1a9577 70d7b49 f2e05cd e1a9577 f2e05cd e1a9577 f2e05cd e1a9577 f2e05cd 70d7b49 e1a9577 f2e05cd e1a9577 70d7b49 f2e05cd 70d7b49 f2e05cd 70d7b49 f2e05cd 70d7b49 f2e05cd 70d7b49 e1a9577 f2e05cd e1a9577 70d7b49 e1a9577 70d7b49 e1a9577 70d7b49 e1a9577 70d7b49 e1a9577 70d7b49 e1a9577 70d7b49 e1a9577 f2e05cd e1a9577 70d7b49 e1a9577 70d7b49 f2e05cd 70d7b49 e1a9577 70d7b49 e1a9577 9deb1ad 90e859f 9deb1ad 56691fc 9deb1ad 56691fc f2e05cd 70d7b49 90e859f e1a9577 9deb1ad 70d7b49 9deb1ad e1a9577 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 | import re, json
from typing import Dict, Any, List, Tuple
# ---------- helpers ----------
def _norm(s: str) -> str:
if not s: return ""
s = s.replace("\u2013","-").replace("\u2014","-").replace("β","-").replace("β","-").replace("\u00A0"," ")
s = re.sub(r"\s+", " ", s.strip().lower())
return s
def _has(text: str, pat: str) -> bool:
return re.search(pat, text, flags=re.I) is not None
def _level_sort_key(lv: str) -> Tuple[int, int]:
band = {"C":100, "T":200, "L":300, "S":400}.get(lv[0], 999)
num = int(re.sub(r"\D", "", lv) or 0)
return (band, num)
def _extract_levels(t: str) -> List[str]:
levels = set()
# Ranges (e.g., L4-5, C4βC7). If prefix changes (C->T), capture both endpoints.
for m in re.finditer(r"\b([CTLS])\s?(\d{1,2})\s*[-β]\s*([CTLS])?\s?(\d{1,2})\b", t, flags=re.I):
pfx1, n1, pfx2, n2 = m.group(1).upper(), int(m.group(2)), (m.group(3) or m.group(1)).upper(), int(m.group(4))
if pfx1 != pfx2:
levels.add(f"{pfx1}{n1}"); levels.add(f"{pfx2}{n2}")
else:
lo, hi = sorted([n1, n2])
for k in range(lo, hi+1): levels.add(f"{pfx1}{k}")
# Singles (e.g., L4, T10)
for m in re.finditer(r"\b([CTLS])\s?(\d{1,2})\b", t, flags=re.I):
levels.add(f"{m.group(1).upper()}{int(m.group(2))}")
return sorted(levels, key=_level_sort_key)
def _count_interspaces(levels: List[str]) -> int:
if not levels: return 0
by_band: Dict[str, List[int]] = {}
for lv in levels:
by_band.setdefault(lv[0], []).append(int(re.sub(r"\D", "", lv)))
for b in list(by_band):
by_band[b] = sorted(set(by_band[b]))
inters = 0
for arr in by_band.values():
if len(arr) >= 2:
inters += sum(1 for i in range(1, len(arr)) if arr[i]-arr[i-1]==1)
# L5βS1 interspace
if "L5" in levels and "S1" in levels: inters += 1
# Conservative fallback for multi-level with gaps
if inters == 0:
for arr in by_band.values():
if len(arr) >= 2:
inters = 1
break
return inters
def _span_segments(levels: List[str]) -> int:
if not levels: return 0
by_band: Dict[str, List[int]] = {}
for lv in levels:
by_band.setdefault(lv[0], []).append(int(re.sub(r"\D", "", lv)))
spans = []
for arr in by_band.values():
arr = sorted(set(arr))
spans.append(arr[-1]-arr[0]+1 if len(arr)>=2 else 1)
# If multiple bands, take max; ensure at least 2 for instrumentation estimation in mixed cases
return (max(spans) if spans else 0) if len(spans)==1 else max(spans + [2])
def _laterality_mods(t: str) -> List[str]:
mods: List[str] = []
if _has(t, r"\bleft\b|left-sided|left side|leftward|hemilam\w* left|foraminotomy left|\blt\b"):
mods.append("LT")
# Avoid bare \brt\b to reduce false matches
if _has(t, r"\bright\b|right-sided|right side|rightward|hemilam\w* right|foraminotomy right"):
mods.append("RT")
return list(sorted(set(mods)))
def _add(suggestions: List[Dict[str, Any]], cpt: str, desc: str, rationale: str,
category: str, primary: bool, confidence: float = 0.8, units: int = 1,
mods: List[str] = None, score: float = None):
suggestions.append({
"cpt": cpt, "desc": desc, "rationale": rationale, "category": category,
"primary": primary, "confidence": confidence, "units": units,
"modifiers": mods or [], "score": score if score is not None else confidence
})
def _inst_code_by_span(span_segments: int, anterior: bool) -> str:
if span_segments <= 1: return ""
if anterior:
return "22845" if span_segments <= 3 else ("22846" if span_segments <= 7 else "22847")
else:
return "22842" if span_segments <= 3 else ("22843" if span_segments <= 7 else "22844")
# ---------- main ----------
def suggest_with_cpt_billing(note: str, payer: str = "Medicare", top_k: int = 10) -> Dict[str, Any]:
t = _norm(note)
suggestions: List[Dict[str, Any]] = []
case_modifiers: List[Dict[str, str]] = []
# --- Early guard: exposure-only / access-only -----------------------------
# Flexible: catches "anterior exposure of L4βS1 performed by vascular surgeon ... No fusion performed"
exposure_only = (
_has(t, r"\bexpos(e|ed|ure)\b|\bapproach\b") and
_has(t, r"\banterior\b|retroperitoneal|vascular (surgeon|exposure)|access\b") and
_has(t, r"\bno (fusion|arthrodesis|interbody|implants?|cage|plate|screw|instrumentation)\b|without (fusion|interbody|instrumentation)") and
not _has(t, r"\bfusion|arthrodesis|discectom\w+|interbody|tlif|plif|alif|acdf|arthroplasty|stimulator|instrument|hardware|screw|rod|plate|cage")
)
if exposure_only:
return {
"payer": payer,
"region": "unknown",
"levels": [],
"interspaces_est": 0,
"span_segments_est": 0,
"suggestions": [{
"cpt": "00000",
"desc": "No recognizable spine CPT pattern found",
"rationale": "Exposure-only access without decompression/fusion/instrumentation.",
"confidence": 0.0, "category": "none", "primary": True, "modifiers": [], "units": 1, "score": 0.0
}],
"case_modifiers": [],
"flags": [],
"flags_map": {},
"laterality": "na",
"build": "FINAL-v2.1",
"mode": "standard",
}
# --- Levels / spans / laterality -----------------------------------------
levels = _extract_levels(t)
inters = _count_interspaces(levels)
span = _span_segments(levels)
lat_mods = _laterality_mods(t)
# ---------- Region ----------
region = "unknown"
if any(l.startswith("C") for l in levels) or _has(t, r"\bcervic"):
region = "cervical"
elif any(l.startswith("T") for l in levels) or _has(t, r"\bthorac"):
region = "thoracic"
elif any(l.startswith(("L","S")) for l in levels) or _has(t, r"\blumbar|sacrum"):
region = "lumbar"
# Transitional cervicothoracic override
has_c7 = any(l.upper() == "C7" for l in levels) or _has(t, r"\bc7\b")
has_t1_3 = any(l.upper() in {"T1","T2","T3"} for l in levels) or _has(t, r"\bt[1-3]\b")
if has_c7 and has_t1_3:
region = "cervicothoracic"
# Helper flags
mentions_plate_ant = _has(t, r"\b(anterior (plate|instrument(ation)?)|plate fixed|plating)\b") or _has(t, r"\bplate\b")
mentions_pedicle = _has(t, r"\bpedicle screw|pedicle-screw|pedicle fixation|s2ai\b")
mentions_rod = _has(t, r"\brod(s)?\b")
mentions_inst_post = mentions_pedicle or mentions_rod or _has(t, r"\binstrument(ation)?\b")
did_corpectomy = False
no_decompression_due_to_revision = False
avoid_decompression_for_stimulator = False
# ---------- (1) Hardware removal (support both word orders) ----------
rem_pat_either = (
r"(?:\b(hardware|instrumentation|implant|plate|rod|screw)\b.{0,40}\b(remov\w+|explant\w+|taken out))"
r"|(?:\bremov\w+\b.{0,40}\b(hardware|instrumentation|implant|plate|rod|screw)\b)"
)
if _has(t, rem_pat_either):
if _has(t, r"\banterior\b|acdf|alif|anterior plate|smith[- ]?robinson"):
_add(suggestions, "22855", "Removal of anterior instrumentation",
"Anterior hardware removal documented.", "hardware_removal", True, 0.85, mods=lat_mods)
elif _has(t, r"non[- ]?segmental|hook|wire") and not _has(t, r"segmental|pedicle"):
_add(suggestions, "22850", "Removal of posterior non-segmental instrumentation",
"Posterior non-segmental removal documented.", "hardware_removal", True, 0.82, mods=lat_mods)
else:
_add(suggestions, "22852", "Removal of posterior segmental instrumentation",
"Posterior segmental hardware removal documented.", "hardware_removal", True, 0.85, mods=lat_mods)
# If remove + replace/reinsert/revision language β add 22849 and avoid decompression auto-fires
if _has(t, r"\b(reinsertion|remove and replace|revision of instrumentation|hardware revision)\b"):
_add(suggestions, "22849", "Reinsertion/revision of spinal instrumentation",
"Revision/reinsertion documented.", "revision", True, 0.85)
no_decompression_due_to_revision = True
# ---------- (2b) New posterior instrumentation WITHOUT explicit fusion ----------
if (
not _has(t, r"\btlif\b|\bplif\b|posterolateral\b.*\bfusion|posterior\b.*\bfusion")
and (mentions_inst_post or _has(t, r"\b(new|placed|inserted|reinserted)\b.{0,20}\b(screw|rod|construct|instrumentation)\b"))
and region in {"cervical","thoracic","lumbar"}
):
est_segments = span or max(2, inters + 1)
code = _inst_code_by_span(est_segments, anterior=False)
if code:
desc = {"22842":"Posterior segmental instrumentation (2β3 segments)",
"22843":"Posterior segmental instrumentation (4β7 segments)",
"22844":"Posterior segmental instrumentation (8+ segments)"}[code]
_add(suggestions, code, desc, "New posterior instrumentation documented.", "instrumentation", False, 0.82)
# ---------- (2) ACDF with guards (history/removal) + broadened trigger ----------
found_removal = _has(t, rem_pat_either)
acdf_history = _has(t, r"\b(prior|previous|history of|s/?p)\s*acdf\b")
acdf_current = (
_has(t, r"\banterior cervical discectomy|anterior cervical fusion|smith[- ]?robinson\b")
or (
(region in {"cervical","cervicothoracic"})
and _has(t, r"\bdiscectom\w+\b")
and (_has(t, r"\b(interbody|cage|arthrodesis|plate)\b") or mentions_plate_ant)
)
or _has(t, r"\bacdf\b")
)
if acdf_current and not (found_removal and not _has(t, r"\bacdf\b")) and not exposure_only:
n = max(1, inters or 1)
_add(suggestions, "22551", "ACDF, first interspace (includes discectomy)",
"Anterior cervical fusion pattern detected.", "ACDF", True, 0.95, mods=lat_mods)
if n > 1:
_add(suggestions, "22552", f"ACDF, each additional interspace Γ{n-1}",
"Multi-level ACDF.", "ACDF add-on", False, 0.9, units=(n-1), mods=lat_mods)
# Plate span inference: "spanning C4βC7" or "span C4βC7"
m_span = re.search(r"\bspan\w*\s*(c\d)\s*[-ββ]\s*(c\d)\b", t, flags=re.I)
plate_span_est = None
if m_span:
try:
c_lo = int(re.sub(r"\D","", m_span.group(1)))
c_hi = int(re.sub(r"\D","", m_span.group(2)))
if c_hi >= c_lo:
plate_span_est = c_hi - c_lo + 1
except Exception:
plate_span_est = None
if mentions_plate_ant:
est_span = (plate_span_est if plate_span_est and plate_span_est >= 2
else (span if (span and span >= 2) else (n + 1)))
code = _inst_code_by_span(est_span, anterior=True)
if code:
desc = {"22845":"Anterior instrumentation (2β3 segments)",
"22846":"Anterior instrumentation (4β7 segments)",
"22847":"Anterior instrumentation (8+ segments)"}[code]
_add(suggestions, code, desc, "Anterior plate present; span estimated from levels/plate span.", "instrumentation", False, 0.8)
# ---------- (3a) Implicit TLIF when keywords imply the construct ----------
if (
not _has(t, r"\btlif\b|\bplif\b|posterior interbody fusion")
and _has(t, r"\bfacetectom(y|ies)\b|complete facetectomy|hemifacetectomy|transforaminal")
and _has(t, r"\binterbody (cage|device|spacer)\b|peek (cage|spacer)|titanium (cage|spacer)|allograft spacer")
and (mentions_pedicle or _has(t, r"\bpedicle screw(s)?\b"))
and region in {"lumbar", "thoracic"}
):
n = max(1, inters or 1)
_add(suggestions, "22633", "Posterior/posterolateral + posterior interbody, single level",
"Implicit TLIF/PLIF: facetectomy + interbody device + pedicle screws.", "TLIF/PLIF", True, 0.93, mods=lat_mods)
if n > 1:
_add(suggestions, "22634", f"Posterior interbody each additional interspace Γ{n-1}",
"Multi-level implicit TLIF/PLIF.", "TLIF/PLIF add-on", False, 0.89, units=(n-1), mods=lat_mods)
code = _inst_code_by_span(span or (n+1), anterior=False)
if code:
desc = {"22842":"Posterior segmental instrumentation (2β3 segments)",
"22843":"Posterior segmental instrumentation (4β7 segments)",
"22844":"Posterior segmental instrumentation (8+ segments)"}[code]
_add(suggestions, code, desc, "Posterior instrumentation detected (implicit TLIF).", "instrumentation", False, 0.83)
# ---------- (3) TLIF / PLIF (beats posterior fusion) ----------
if _has(t, r"\btlif\b|\bplif\b|posterior interbody fusion"):
n = max(1, inters or 1)
_add(suggestions, "22633", "Posterior/posterolateral + posterior interbody, single level",
"TLIF/PLIF pattern: posterior approach + cage + screws.", "TLIF/PLIF", True, 0.92, mods=lat_mods)
if n > 1:
_add(suggestions, "22634", f"Posterior interbody each additional interspace Γ{n-1}",
"Multi-level TLIF/PLIF.", "TLIF/PLIF add-on", False, 0.88, units=(n-1), mods=lat_mods)
if mentions_inst_post:
code = _inst_code_by_span(span or (n+1), anterior=False)
if code:
desc = {"22842":"Posterior segmental instrumentation (2β3 segments)",
"22843":"Posterior segmental instrumentation (4β7 segments)",
"22844":"Posterior segmental instrumentation (8+ segments)"}[code]
_add(suggestions, code, desc, "Posterior instrumentation detected.", "instrumentation", False, 0.82)
# ---------- (4) ALIF ----------
if _has(t, r"\balif\b|anterior lumbar interbody fusion"):
n = max(1, inters or 1)
_add(suggestions, "22558", "Anterior lumbar interbody fusion, single interspace",
"ALIF detected.", "ALIF", True, 0.9)
if n > 1:
_add(suggestions, "22585", f"ALIF each additional interspace Γ{n-1}",
"Multi-level ALIF.", "ALIF add-on", False, 0.86, units=(n-1))
if mentions_plate_ant:
est_span = span if (span and span >= 2) else (n + 1)
code = _inst_code_by_span(est_span, anterior=True)
if code:
desc = {"22845":"Anterior instrumentation (2β3 segments)",
"22846":"Anterior instrumentation (4β7 segments)",
"22847":"Anterior instrumentation (8+ segments)"}[code]
_add(suggestions, code, desc, "Anterior plate present; span estimated from levels.", "instrumentation", False, 0.8)
# ---------- (5) Posterolateral/posterior fusion WITHOUT interbody ----------
if (_has(t, r"posterolateral\b.*\bfusion|post[- ]?lat\b.*\bfusion|posterior\b.*\bfusion|in situ\b.*\bfusion")
and not _has(t, r"\btlif\b|\bplif\b|posterior interbody")):
base_map = {"cervical":"22600", "thoracic":"22610", "lumbar":"22612"}
base = base_map.get(region, "22612")
_add(suggestions, base, f"Posterolateral/posterior fusion, first level ({region})",
"Posterior fusion without interbody.", "posterior_fusion", True, 0.78, mods=lat_mods)
add_units = max(0, inters)
if add_units:
_add(suggestions, "22614", f"Posterior fusion each additional segment Γ{add_units}",
"Multi-level posterior fusion.", "posterior_fusion add-on", False, 0.72, units=add_units, mods=lat_mods)
if mentions_inst_post:
n_seg = span or max(2, (inters + 1))
code = _inst_code_by_span(n_seg, anterior=False)
if code:
desc = {"22842":"Posterior segmental instrumentation (2β3 segments)",
"22843":"Posterior segmental instrumentation (4β7 segments)",
"22844":"Posterior segmental instrumentation (8+ segments)"}[code]
_add(suggestions, code, desc, "Posterior instrumentation detected.", "instrumentation", False, 0.8)
# ---------- (6) Neurostimulator paddle + IPG ----------
if _has(t, r"\bstimulator\b|paddle lead|spinal cord stimulator|scs") and _has(t, r"\blaminectomy\b|laminotomy"):
_add(suggestions, "63655", "Laminectomy for implantation of neurostimulator paddle electrodes",
"Paddle lead placed via laminectomy.", "neurostimulator", True, 0.86)
if _has(t, r"\bpulse generator\b|ipg|implantable pulse generator|battery\b"):
_add(suggestions, "63685", "Insertion or replacement of IPG",
"Pulse generator implanted.", "neurostimulator", False, 0.84)
avoid_decompression_for_stimulator = True
# ---------- (7) Decompression (guarded: no history/corpectomy/revision/stimulator) ----------
if (not no_decompression_due_to_revision) and (not avoid_decompression_for_stimulator):
if _has(t, r"\bdecompression(s)?\b|laminectom(y|ies)|laminotom(y|ies)|foraminotom(y|ies)|foraminal decompression(s)?|neuroforamen|lateral recess|central stenosis") \
and not _has(t, r"\b(prior|previous|history of|s/?p)\s+(decompression|laminectomy|laminotomy|foraminotomy)\b"):
base_map = {"cervical":"63045", "thoracic":"63046", "lumbar":"63047"}
base = base_map.get(region, "63047")
_add(suggestions, base, "Decompression, first level", "Decompression terms detected.", "decompression", True, 0.82, mods=lat_mods)
if _has(t, r"additional level|two levels|three levels|multi|levels|l\d-?l\d|c\d-?c\d|t\d-?t\d"):
extra = max(1, inters)
_add(suggestions, "63048", f"Decompression, each additional level Γ{extra}",
"Multi-level decompression inferred.", "decompression add-on", False, 0.78, units=extra, mods=lat_mods)
# ---------- (8) Interspinous/interlaminar device ----------
if _has(t, r"\bcoflex\b|interspinous|interlaminar device|ipd"):
_add(suggestions, "22868", "Insertion of interspinous/interlaminar device",
"Coflex/IPD insertion documented.", "interspinous", True, 0.86, mods=lat_mods)
# ---------- (9) Cervical disc arthroplasty ----------
if _has(t, r"cervical (arthroplasty|artificial disc|disc replacement)|\bcda\b") or (
(region == "cervical") and _has(t, r"\barthroplasty\b|artificial disc|disc replacement")
):
n = max(1, inters or 1)
_add(suggestions, "22856", "Cervical disc arthroplasty, single level",
"Cervical disc arthroplasty documented.", "arthroplasty", True, 0.87, mods=lat_mods)
if n > 1:
_add(suggestions, "22858", f"Cervical disc arthroplasty, each additional level Γ{n-1}",
"Multi-level arthroplasty.", "arthroplasty add-on", False, 0.84, units=(n-1), mods=lat_mods)
# ---------- (10) Tumor / corpectomy ----------
# Cervical corpectomy
if _has(t, r"\bcorpectomy\b") and (region == "cervical" or _has(t, r"\bcervic")):
_add(suggestions, "63081", "Cervical corpectomy for decompression, first segment",
"Cervical corpectomy documented.", "tumor/corpectomy", True, 0.86)
if _has(t, r"additional (level|segment)|two levels|three levels|multi|c\d-?c\d"):
extra = max(1, inters or 1)
_add(suggestions, "63082", f"Each additional cervical segment Γ{extra}",
"Multi-segment cervical corpectomy.", "tumor/corpectomy add-on", False, 0.82, units=extra)
did_corpectomy = True
# Thoracic/Lumbar corpectomy (non-cervical)
if _has(t, r"\btumou?r\b|metastatic|metastasis|en bloc") or (_has(t, r"\bcorpectomy\b") and region in {"thoracic","lumbar"}):
_add(suggestions, "63085", "Vertebral corpectomy for decompression (thoracic/lumbar), first segment",
"Tumor/corpectomy documented.", "tumor/corpectomy", True, 0.84)
if _has(t, r"additional (level|segment)|two levels|three levels|multi|t\d-?t\d|l\d-?l\d"):
_add(suggestions, "63086", "Each additional segment (thoracic/lumbar) Γ1+",
"Multi-segment corpectomy.", "tumor/corpectomy add-on", False, 0.8)
did_corpectomy = True
# If corpectomy present, suppress decompression rows
if did_corpectomy:
suggestions = [r for r in suggestions if r["category"] != "decompression" and r["cpt"] not in {"63045","63046","63047","63048"}]
# ---------- (11) Deformity / Smith-Petersen osteotomy ----------
if _has(t, r"\bspo\b|smith[- ]?petersen|posterior column osteotomy|osteotomy"):
base_map = {"cervical":"22210", "thoracic":"22214", "lumbar":"22206"}
base = base_map.get(region, "22206")
_add(suggestions, base, "Posterior column osteotomy (Smith-Petersen), first level",
"Deformity correction with SPO documented.", "deformity", True, 0.83)
if _has(t, r"additional (level|segment)|two levels|three levels|multi"):
_add(suggestions, str(int(base)+1), "Each additional vertebral segment Γ1+",
"Multi-level osteotomy.", "deformity add-on", False, 0.78)
# ---------- (12) Kyphoplasty / vertebral augmentation ----------
if _has(t, r"\bkyphoplasty\b|vertebroplasty|cement (augmentation|injection)"):
base_map = {"cervical":"22510", "thoracic":"22513", "lumbar":"22514"}
base = base_map.get(region, "22514")
_add(suggestions, base, "Percutaneous vertebral augmentation, first level",
"Vertebral augmentation terms present.", "percutaneous", True, 0.82)
if _has(t, r"additional (level|segment)|two levels|three levels|multi"):
_add(suggestions, "22515", "Each additional vertebral body Γ1+",
"Multi-level augmentation.", "percutaneous add-on", False, 0.8)
# ---------- (13) Pelvic fixation ----------
if _has(t, r"\bpelvic fixation\b|iliac bolt|iliac screw|s2ai|to pelvis\b"):
_add(suggestions, "22848", "Pelvic fixation (attach instrumentation to pelvis)",
"Iliac/pelvic fixation documented.", "pelvic_fixation", False, 0.84)
# ---------- (14) Bone grafts (prefer structural 20931 over 20930) ----------
structural_like = _has(t, r"\b(structural allograft|fibular strut|strut graft|allograft spacer(s)?|interbody allograft spacer(s)?)\b")
any_allograft = _has(t, r"\ballograft\b|dbm|demineralized|osteo.?promotive|morselized local bone|local autograft")
if _has(t, r"\bautograft\b|iliac crest harvest|icbg|iliac crest bone"):
_add(suggestions, "20937", "Autograft (separate incision)",
"Autograft harvest documented.", "bone_graft", False, 0.8)
if structural_like:
_add(suggestions, "20931", "Structural allograft",
"Structural allograft documented.", "bone_graft", False, 0.8)
elif any_allograft:
_add(suggestions, "20930", "Allograft, morselized / osteopromotive material",
"Allograft/DBM documented.", "bone_graft", False, 0.8)
if _has(t, r"\bbmp\b|biologic|recombinant growth factor|rhbmp"):
_add(suggestions, "20939", "Osteopromotive biologic (e.g., BMP)",
"BMP/biologic documented.", "bone_graft", False, 0.8)
# ---------- Case Modifiers (case-level) ----------
mod_map = {
"22": r"\b(complex|difficult|prolonged|adhesiolysis|severe deformity|morbid obesity)\b",
"50": r"\b(both sides|bilaterally|bilateral(?!\s*(pedicle|screw|screws|rod|rods|instrumentation)))\b",
"52": r"\bpartial|limited|reduced service\b",
"53": r"\baborted|terminated|discontinued\b",
"59": r"\bseparate (site|incision)|distinct|different level\b",
"62": r"\bco[- ]?surgeon|two surgeons|co-surgeons\b",
"78": r"\breturn to or\b|\bunplanned return\b",
"79": r"\bunrelated procedure\b",
"80": r"\bassistant surgeon|assistant present\b",
"82": r"\bresident not available\b"
}
reasons = {
"22": "Increased procedural service (complexity).",
"50": "Bilateral procedure documented.",
"52": "Reduced service (limited portion performed).",
"53": "Procedure discontinued for safety.",
"59": "Distinct procedural service (separate site/level).",
"62": "Two surgeons (co-surgeons) documented.",
"78": "Unplanned return to OR during global period.",
"79": "Unrelated procedure during postoperative period.",
"80": "Assistant surgeon documented.",
"82": "Assistant surgeon; resident not available."
}
for k, pat in mod_map.items():
if _has(t, pat): case_modifiers.append({"modifier": k, "reason": reasons[k]})
# Force -50 if 'bilateral' appears near decompression nouns and not already set
if not any(m["modifier"]=="50" for m in case_modifiers):
if _has(t, r"\bbilateral\b") and _has(t, r"(foraminotom(y|ies)|facetectom(y|ies)|laminectom(y|ies)|laminotom(y|ies))"):
case_modifiers.append({"modifier":"50","reason": reasons["50"]})
# --- Modifier sanity rules ---
if any(m["modifier"]=="82" for m in case_modifiers):
case_modifiers = [m for m in case_modifiers if m["modifier"]!="80"]
if any(m["modifier"]=="53" for m in case_modifiers):
case_modifiers = [m for m in case_modifiers if m["modifier"]!="52"]
# ---------- Default guard ----------
if not suggestions:
suggestions = [{
"cpt": "00000",
"desc": "No recognizable spine CPT pattern found",
"rationale": "No fusion/decompression pattern detected.",
"confidence": 0.0, "category": "none", "primary": True, "modifiers": [], "units": 1, "score": 0.0
}]
# ---------- De-dup ----------
merged: Dict[Tuple[str, str, str, str], Dict[str, Any]] = {}
for r in suggestions:
key = (r["cpt"], r["desc"], r["category"], "|".join(sorted(r.get("modifiers", []))))
if key not in merged:
merged[key] = r.copy()
else:
merged[key]["units"] = merged[key].get("units", 1) + r.get("units", 1)
merged[key]["confidence"] = max(merged[key]["confidence"], r.get("confidence", 0.0))
merged[key]["score"] = max(merged[key]["score"], r.get("score", 0.0))
merged[key]["primary"] = merged[key]["primary"] or r.get("primary", False)
out = list(merged.values())
# --- Propagate case-level -53 (discontinued) onto primary rows only ---
has_53 = any(m["modifier"] == "53" for m in case_modifiers)
if has_53:
for r in out:
if r.get("primary", False):
r.setdefault("modifiers", [])
if "53" not in r["modifiers"]:
r["modifiers"].append("53")
# ensure add-ons don't inherit -53
for r in out:
if not r.get("primary", False) and r.get("modifiers"):
r["modifiers"] = [m for m in r["modifiers"] if m != "53"]
# --- Apply laterality to rows; strip when case-level bilateral (-50) present ---
has_50 = any(m["modifier"] == "50" for m in case_modifiers)
lat_row_mods = [] if has_50 else lat_mods
for row in out:
if row["category"] in {"decompression","decompression add-on","TLIF/PLIF","posterior_fusion","posterior_fusion add-on"}:
if lat_row_mods and not row.get("modifiers"):
row["modifiers"] = lat_row_mods[:]
if has_50:
for row in out:
if row.get("modifiers"):
row["modifiers"] = [m for m in row["modifiers"] if m not in ("LT","RT")]
# ---------- Order ----------
def _cpt_num(x: Dict[str, Any]) -> int:
try: return int(re.sub(r"\D","", x.get("cpt","0")) or 0)
except: return 0
out.sort(key=lambda r: (not r.get("primary", False), -(r.get("score", r.get("confidence",0.0))), _cpt_num(r)))
if isinstance(top_k, int) and top_k > 0:
out = out[:top_k]
# ---------- Tech flags (microscope / navigation / neuromonitoring / fluoro) ----------
flags_map = {
"microscope": _has(t, r"\bmicroscope\b|microdissection"),
"nav": _has(t, r"\bnavigation\b|o-?arm|stealth|mazor|7d|image[- ]?guided"),
"io_monitor": _has(t, r"\bneuromonitor\w*|ssep|tcem|tcme|emg\b|\bintra[- ]?op(erative)? monitoring\b"),
"fluoro": _has(t, r"\bfluoro\w*|c[- ]?arm\b|fluoroscop\w*"),
}
flags_list = [k for k, v in flags_map.items() if v]
# ---------- Case-level laterality (meta) ----------
bilateral_procedure_pat = (
r"\bboth sides\b"
r"|"
r"\bbilateral\b(?!\s*(pedicle|screw|screws|rod|rods|instrument|instrumentation|hardware|construct))"
)
case_laterality = "na"
if _has(t, r"\bleft[- ]sided|\bleft\b"):
case_laterality = "left"
elif _has(t, r"\bright[- ]sided|\bright\b"):
case_laterality = "right"
elif _has(t, bilateral_procedure_pat):
case_laterality = "bilateral"
# ---------- OPTIONAL: surface tech flags in primary rationale ----------
if flags_list:
for r in out:
if r.get("primary", False):
r["rationale"] = (r.get("rationale","") + f" (Tech: {', '.join(flags_list)})").strip()
break
return {
"payer": payer,
"region": region,
"levels": levels,
"interspaces_est": inters,
"span_segments_est": span,
"suggestions": out,
"case_modifiers": case_modifiers,
"flags": flags_list,
"flags_map": flags_map,
"laterality": case_laterality,
"build": "FINAL-v2.1",
"mode": "standard",
}
|