Slaiwala commited on
Commit
6ffb800
Β·
verified Β·
1 Parent(s): e520e38

Update spine_coder/spine_coder_core.py

Browse files
Files changed (1) hide show
  1. spine_coder/spine_coder_core.py +313 -146
spine_coder/spine_coder_core.py CHANGED
@@ -1,72 +1,94 @@
1
- # spine_coder_core.py
2
- # Vertebro FINAL-v2.2 β€” Production Hybrid Engine
 
3
 
4
  import re, json
5
  from typing import Dict, Any, List, Tuple
6
 
7
  # ─────────────────────────────────────────────
8
- # NORMALIZATION UTILITIES
9
  # ─────────────────────────────────────────────
10
  def _norm(s: str) -> str:
 
11
  if not s:
12
  return ""
13
  s = (s.replace("\u2013", "-").replace("\u2014", "-")
14
- .replace("β€”", "-").replace("–", "-")
15
- .replace("\u00A0", " "))
16
  return re.sub(r"\s+", " ", s.strip().lower())
17
 
18
  def _has(text: str, pat: str) -> bool:
 
19
  return re.search(pat, text, flags=re.I) is not None
20
 
 
21
  # ─────────────────────────────────────────────
22
- # LEVEL / REGION LOGIC
23
  # ─────────────────────────────────────────────
24
  _ORDER = ["C", "T", "L", "S"]
25
- _MAXNUM = {"C":7, "T":12, "L":5, "S":5}
26
 
27
- def _level_sort_key(lv:str)->Tuple[int,int]:
28
- rank={"C":100,"T":200,"L":300,"S":400}
29
- return (rank.get(lv[0].upper(),999), int(re.sub(r"\D","",lv) or 0))
 
 
30
 
31
  _SPAN = re.compile(r"\b([CTLS])\s?(\d{1,2})\s*[-–]\s*([CTLS])?\s?(\d{1,2})\b", re.I)
32
  _SINGLE = re.compile(r"\b([CTLS])\s?(\d{1,2})\b", re.I)
33
 
34
- def _expand_across_regions(p1:str,n1:int,p2:str,n2:int)->List[str]:
35
- p1,p2=p1.upper(),p2.upper()
36
- out=[]; i=_ORDER.index(p1); r=i; num=n1
 
 
 
 
 
37
  while True:
38
  out.append(f"{_ORDER[r]}{num}")
39
- if _ORDER[r]==p2 and num==n2: break
40
- if num<_MAXNUM[_ORDER[r]]:
41
- num+=1
 
42
  else:
43
- r+=1
44
- if r>=len(_ORDER): break
45
- num=1
 
46
  return out
47
 
48
- def _extract_levels(t:str)->List[str]:
49
- t=_norm(t); levels=set()
 
 
50
  for m in _SPAN.finditer(t):
51
- p1,n1=m.group(1).upper(),int(m.group(2))
52
- p2=(m.group(3) or p1).upper(); n2=int(m.group(4))
53
- if p1==p2:
54
- for k in range(min(n1,n2),max(n1,n2)+1): levels.add(f"{p1}{k}")
 
 
55
  else:
56
- for lv in _expand_across_regions(p1,n1,p2,n2): levels.add(lv)
 
57
  for m in _SINGLE.finditer(t):
58
  levels.add(f"{m.group(1).upper()}{int(m.group(2))}")
59
- return sorted(levels,key=_level_sort_key)
60
 
61
- def _count_interspaces(levels:List[str])->int:
62
- if not levels: return 0
63
- return max(0,len(sorted(set(levels),key=_level_sort_key))-1)
 
 
 
64
 
65
- def _classify_region(levels:List[str])->str:
66
- b={"C":False,"T":False,"L":False,"S":False}
 
67
  for lv in levels:
68
- if lv: b[lv[0].upper()]=True
69
- c,t,l,s=b["C"],b["T"],b["L"],b["S"]
 
70
  if c and t and not (l or s): return "cervicothoracic"
71
  if t and l and not (c or s): return "thoracolumbar"
72
  if l and s and not (c or t): return "lumbosacral"
@@ -76,134 +98,279 @@ def _classify_region(levels:List[str])->str:
76
  if s and not (c or t or l): return "sacral"
77
  return "mixed"
78
 
 
79
  # ─────────────────────────────────────────────
80
- # LATERALITY / MODIFIERS
81
  # ─────────────────────────────────────────────
82
- def _laterality_and_modifiers(note:str)->Tuple[str,List[str]]:
83
- t=_norm(note)
84
- midline_kw=[
85
- r"bilateral medial facetectom",r"bilateral foraminotom",
86
- r"bilateral decompression",r"bilateral pedicle screws",
87
- r"bilateral rods?",r"left hemilaminectomy",r"right hemilaminectomy"
 
88
  ]
89
- if any(_has(t,p) for p in midline_kw):
90
- return "midline",[]
91
- return "midline",[]
 
92
 
93
  # ─────────────────────────────────────────────
94
- # FLAG PATTERNS
95
  # ─────────────────────────────────────────────
96
- FUSION_KW=r"\b(arthrodesis|fusion|t?lif|alif|plif|xlif|interbody\s+cage|peek\s+cage|structural\s+cage)\b"
97
- INSTR_KW=r"\b(pedicle\s+screws?|lateral\s+mass\s+screws?|rods?|set\s+screws?|instrument(?:ed|ation))\b"
98
- NAV_KW=r"\b(navigation|navigated|o-?arm|ziehm|3d\s+spin)\b"
99
- ALLO_KW=r"\b(allograft|dbm|demineralized\s+bone\s+matrix)\b"
100
- AUTO_LOCAL_KW=r"\b(local\s+autograft|spinous\s+process\s+bone|lamina\s+bone\s+retained|morselized\s+autograft)\b"
101
- AUTO_SEP_KW=r"\b(iliac\s+crest|separate\s+incision|rib\s+graft|harvested\s+from\s+iliac)\b"
 
 
 
 
 
 
102
 
103
  # ─────────────────────────────────────────────
104
  # INSTRUMENTATION SPAN HELPER
105
  # ─────────────────────────────────────────────
106
- def _inst_code_by_span(span:int, anterior:bool)->str:
107
- if span<=1: return ""
 
 
108
  if anterior:
109
- return "22845" if span<=3 else ("22846" if span<=7 else "22847")
110
  else:
111
- return "22842" if span<=3 else ("22843" if span<=7 else "22844")
112
-
113
- # ─────────────────────────────────────────────
114
- # PRIMARY CPT LOGIC (streamlined from v2.1)
115
- # ─────────────────────────────────────────────
116
- def _infer_cpts(note:str, region:str, levels:List[str])->List[str]:
117
- t=_norm(note); cpts=[]
118
- inters=_count_interspaces(levels)
119
-
120
- # Decompression
121
- if re.search(r"laminectomy|decompression|facetectomy|foraminotomy",t):
122
- base={"cervical":"63045","thoracic":"63046","lumbar":"63047"}.get(region,"63047")
123
- extras=max(0,inters-1)
124
- cpts.append(base)
125
- if extras>0: cpts.extend(["63048"]*extras)
126
-
127
- # Fusions (posterior/interbody)
128
- if re.search(FUSION_KW,t):
129
- if region.startswith("cervico"): base="22600"
130
- elif region.startswith("thoraco"): base="22610"
131
- elif region.startswith("lumbo"): base="22612"
132
- elif region=="lumbar": base="22630"
133
- else: base="22612"
134
- extras=max(0,inters)
135
- cpts.append(base)
136
- if extras>0: cpts.extend(["22614"]*extras)
137
-
138
- # Instrumentation
139
- if re.search(INSTR_KW,t):
140
- segs=len(levels)
141
- if segs<=2: cpts.append("22840")
142
- elif segs<=6: cpts.append("22842")
143
- elif segs<=12: cpts.append("22843")
144
- else: cpts.append("22844")
145
-
146
- # Navigation
147
- if re.search(NAV_KW,t): cpts.append("61783")
148
-
149
- # Grafts
150
- if re.search(AUTO_SEP_KW,t): cpts.append("20937")
151
- elif re.search(AUTO_LOCAL_KW,t): cpts.append("20936")
152
- if re.search(ALLO_KW,t): cpts.append("20930")
153
-
154
- # Hardware removal
155
- if re.search(r"remov(ed|al).*instrument",t): cpts.append("22852")
156
-
157
- return sorted(set(cpts))
158
-
159
- # ─────────────────────────────────────────────
160
- # MAIN WRAPPER
161
- # ─────────────────────────────────────────────
162
- def vertebro_infer(note:str, payer:str="Medicare")->Dict[str,Any]:
163
- t=_norm(note)
164
- levels=_extract_levels(t)
165
- region=_classify_region(levels)
166
- inter=_count_interspaces(levels)
167
- laterality,mods=_laterality_and_modifiers(t)
168
-
169
- # Flag summary
170
- flags=[]
171
- for pat,label in [(FUSION_KW,"arthrodesis"),(INSTR_KW,"instrumentation"),
172
- (NAV_KW,"nav"),(ALLO_KW,"allograft"),
173
- (AUTO_LOCAL_KW,"autograft"),(AUTO_SEP_KW,"autograft_sep")]:
174
- if re.search(pat,t): flags.append(label)
175
-
176
- cpts=_infer_cpts(t,region,levels)
177
-
178
- # Tech flags
179
- flags_map={
180
- "microscope":_has(t,r"\bmicroscope\b|microdissection"),
181
- "nav":_has(t,r"\bnavigation\b|o-?arm|stealth|7d|image[- ]?guided"),
182
- "io_monitor":_has(t,r"\bneuromonitor|ssep|emg|monitoring\b"),
183
- "fluoro":_has(t,r"\bfluoro|c[- ]?arm|fluoroscop"),
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
184
  }
185
- tech_flags=[k for k,v in flags_map.items() if v]
186
-
187
- return {
188
- "payer":payer,
189
- "region":region,
190
- "levels":levels,
191
- "interspaces_est":inter,
192
- "laterality":laterality,
193
- "case_modifiers":mods,
194
- "flags":list(set(flags+tech_flags)),
195
- "flags_map":flags_map,
196
- "suggested_cpts":cpts,
197
- "build":"FINAL-v2.2",
198
- "mode":"standard"
 
 
 
 
 
 
 
 
 
199
  }
 
 
200
 
201
  # ─────────────────────────────────────────────
202
- # CLI TEST
203
  # ─────────────────────────────────────────────
204
- if __name__=="__main__":
205
- sample = """Preop Dx: C3–C6 stenosis with myelopathy.
206
- Procedure: C3–C6 laminectomy and C2–T1 posterior fusion with instrumentation.
207
- Navigation used; local autograft and allograft placed."""
 
 
 
 
 
 
208
  result = vertebro_infer(sample)
209
  print(json.dumps(result, indent=2))
 
1
+ # spine_coder_core_pro.py
2
+ # Vertebro FINAL-v2.2-PRO (Block 1 / 3)
3
+ # Core utilities, text normalization, level parsing, region classification
4
 
5
  import re, json
6
  from typing import Dict, Any, List, Tuple
7
 
8
  # ─────────────────────────────────────────────
9
+ # TEXT NORMALIZATION
10
  # ─────────────────────────────────────────────
11
  def _norm(s: str) -> str:
12
+ """Clean unicode dashes, normalize spaces, lowercase text."""
13
  if not s:
14
  return ""
15
  s = (s.replace("\u2013", "-").replace("\u2014", "-")
16
+ .replace("β€”", "-").replace("–", "-").replace("\u00A0", " "))
 
17
  return re.sub(r"\s+", " ", s.strip().lower())
18
 
19
  def _has(text: str, pat: str) -> bool:
20
+ """Case-insensitive regex presence check."""
21
  return re.search(pat, text, flags=re.I) is not None
22
 
23
+
24
  # ─────────────────────────────────────────────
25
+ # LEVEL AND REGION LOGIC
26
  # ─────────────────────────────────────────────
27
  _ORDER = ["C", "T", "L", "S"]
28
+ _MAXNUM = {"C": 7, "T": 12, "L": 5, "S": 5}
29
 
30
+ def _level_sort_key(lv: str) -> Tuple[int, int]:
31
+ band_rank = {"C": 100, "T": 200, "L": 300, "S": 400}
32
+ band = band_rank.get(lv[0].upper(), 999)
33
+ num = int(re.sub(r"\D", "", lv) or 0)
34
+ return (band, num)
35
 
36
  _SPAN = re.compile(r"\b([CTLS])\s?(\d{1,2})\s*[-–]\s*([CTLS])?\s?(\d{1,2})\b", re.I)
37
  _SINGLE = re.compile(r"\b([CTLS])\s?(\d{1,2})\b", re.I)
38
 
39
+ def _expand_across_regions(p1: str, n1: int, p2: str, n2: int) -> List[str]:
40
+ """Inclusive sequence from (p1,n1) to (p2,n2), crossing C/T/L/S boundaries."""
41
+ p1, p2 = p1.upper(), p2.upper()
42
+ out = []
43
+ i = _ORDER.index(p1)
44
+ j = _ORDER.index(p2)
45
+ r = i
46
+ num = n1
47
  while True:
48
  out.append(f"{_ORDER[r]}{num}")
49
+ if _ORDER[r] == p2 and num == n2:
50
+ break
51
+ if num < _MAXNUM[_ORDER[r]]:
52
+ num += 1
53
  else:
54
+ r += 1
55
+ if r >= len(_ORDER):
56
+ break
57
+ num = 1
58
  return out
59
 
60
+ def _extract_levels(t: str) -> List[str]:
61
+ """Return all vertebral levels found in text (handles cross-region spans)."""
62
+ t = _norm(t)
63
+ levels = set()
64
  for m in _SPAN.finditer(t):
65
+ p1, n1 = m.group(1).upper(), int(m.group(2))
66
+ p2 = (m.group(3) or p1).upper()
67
+ n2 = int(m.group(4))
68
+ if p1 == p2:
69
+ for k in range(min(n1, n2), max(n1, n2) + 1):
70
+ levels.add(f"{p1}{k}")
71
  else:
72
+ for lv in _expand_across_regions(p1, n1, p2, n2):
73
+ levels.add(lv)
74
  for m in _SINGLE.finditer(t):
75
  levels.add(f"{m.group(1).upper()}{int(m.group(2))}")
76
+ return sorted(levels, key=_level_sort_key)
77
 
78
+ def _count_interspaces(levels: List[str]) -> int:
79
+ """Estimate motion segments between unique sequential levels."""
80
+ if not levels:
81
+ return 0
82
+ lv_sorted = sorted(set(levels), key=_level_sort_key)
83
+ return max(0, len(lv_sorted) - 1)
84
 
85
+ def _classify_region(levels: List[str]) -> str:
86
+ """Return spinal region classification based on detected level bands."""
87
+ b = {"C": False, "T": False, "L": False, "S": False}
88
  for lv in levels:
89
+ if lv:
90
+ b[lv[0].upper()] = True
91
+ c, t, l, s = b["C"], b["T"], b["L"], b["S"]
92
  if c and t and not (l or s): return "cervicothoracic"
93
  if t and l and not (c or s): return "thoracolumbar"
94
  if l and s and not (c or t): return "lumbosacral"
 
98
  if s and not (c or t or l): return "sacral"
99
  return "mixed"
100
 
101
+
102
  # ─────────────────────────────────────────────
103
+ # LATERALITY AND MODIFIERS
104
  # ─────────────────────────────────────────────
105
+ def _laterality_and_modifiers(note: str) -> Tuple[str, List[str]]:
106
+ """Force midline for spine work; suppress invalid -50."""
107
+ t = _norm(note)
108
+ midline_kw = [
109
+ r"bilateral medial facetectom", r"bilateral foraminotom",
110
+ r"bilateral decompression", r"bilateral pedicle screws",
111
+ r"bilateral rods?", r"left hemilaminectomy", r"right hemilaminectomy"
112
  ]
113
+ if any(_has(t, p) for p in midline_kw):
114
+ return "midline", []
115
+ return "midline", []
116
+
117
 
118
  # ─────────────────────────────────────────────
119
+ # BASE FLAG PATTERNS
120
  # ─────────────────────────────────────────────
121
+ FUSION_KW = r"\b(arthrodesis|fusion|t?lif|alif|plif|xlif|interbody\s+cage|peek\s+cage|structural\s+cage)\b"
122
+ INSTR_KW = r"\b(pedicle\s+screws?|lateral\s+mass\s+screws?|rods?|set\s+screws?|instrument(?:ed|ation))\b"
123
+ NAV_KW = r"\b(navigation|navigated|o-?arm|ziehm|3d\s+spin)\b"
124
+ ALLO_KW = r"\b(allograft|dbm|demineralized\s+bone\s+matrix)\b"
125
+ AUTO_LOCAL_KW = r"\b(local\s+autograft|spinous\s+process\s+bone|lamina\s+bone\s+retained|morselized\s+autograft)\b"
126
+ AUTO_SEP_KW = r"\b(iliac\s+crest|separate\s+incision|rib\s+graft|harvested\s+from\s+iliac)\b"
127
+
128
+ ### --- END OF BLOCK 1 --- ###
129
+
130
+ # spine_coder_core_pro.py
131
+ # Vertebro FINAL-v2.2-PRO (Block 2 / 3)
132
+ # CPT reasoning logic and inference tree
133
 
134
  # ─────────────────────────────────────────────
135
  # INSTRUMENTATION SPAN HELPER
136
  # ─────────────────────────────────────────────
137
+ def _inst_code_by_span(span: int, anterior: bool) -> str:
138
+ """Return instrumentation CPT code based on span length."""
139
+ if span <= 1:
140
+ return ""
141
  if anterior:
142
+ return "22845" if span <= 3 else ("22846" if span <= 7 else "22847")
143
  else:
144
+ return "22842" if span <= 3 else ("22843" if span <= 7 else "22844")
145
+
146
+
147
+ # ─────────────────────────────────────────────
148
+ # CORE CPT INFERENCE ENGINE
149
+ # ─────────────────────────────────────────────
150
+ def _infer_cpts(note: str, region: str, levels: List[str]) -> List[Dict[str, Any]]:
151
+ """Return CPT dictionary list with rationale and confidence."""
152
+ t = _norm(note)
153
+ out: List[Dict[str, Any]] = []
154
+ inters = _count_interspaces(levels)
155
+ span = max(inters + 1, 2)
156
+ conf = 0.85
157
+
158
+ def add(cpt: str, desc: str, rationale: str, cat: str, conf: float = 0.85, primary: bool = False):
159
+ out.append({
160
+ "cpt": cpt,
161
+ "desc": desc,
162
+ "rationale": rationale,
163
+ "category": cat,
164
+ "confidence": round(conf, 2),
165
+ "primary": primary
166
+ })
167
+
168
+ # ----- 1. Decompression -----
169
+ if _has(t, r"laminectomy|decompression|facetectomy|foraminotomy"):
170
+ base_map = {"cervical": "63045", "thoracic": "63046", "lumbar": "63047"}
171
+ base = base_map.get(region, "63047")
172
+ add(base, "Decompression, first level",
173
+ "Detected decompression terms (laminectomy/facetectomy/foraminotomy).",
174
+ "decompression", 0.86, True)
175
+ if inters > 0:
176
+ add("63048", f"Each additional level Γ—{inters}",
177
+ "Multi-level decompression inferred.", "decompression add-on", 0.82)
178
+
179
+ # ----- 2. Fusion -----
180
+ if _has(t, FUSION_KW):
181
+ base_map = {"cervicothoracic": "22600", "cervical": "22600",
182
+ "thoracic": "22610", "lumbar": "22630", "lumbosacral": "22612"}
183
+ base = base_map.get(region, "22612")
184
+ add(base, "Fusion, first level",
185
+ f"Fusion/arthrodesis detected ({region} region).", "fusion", 0.9, True)
186
+ if inters > 0:
187
+ add("22614", f"Each additional level Γ—{inters}",
188
+ "Multi-level fusion extension.", "fusion add-on", 0.84)
189
+
190
+ # ----- 3. Instrumentation -----
191
+ if _has(t, INSTR_KW):
192
+ code = _inst_code_by_span(span, anterior=False)
193
+ desc = {
194
+ "22842": "Posterior segmental instrumentation (2–3 segments)",
195
+ "22843": "Posterior segmental instrumentation (4–7 segments)",
196
+ "22844": "Posterior segmental instrumentation (8+ segments)"
197
+ }.get(code, "Posterior instrumentation")
198
+ add(code, desc, "Posterior instrumentation detected (pedicle screws/rods).",
199
+ "instrumentation", 0.88)
200
+
201
+ # ----- 4. Navigation -----
202
+ if _has(t, NAV_KW):
203
+ add("61783", "Intraoperative navigation (image-guided)",
204
+ "Navigation terms detected (Ziehm/O-arm/3D spin).", "navigation", 0.82)
205
+
206
+ # ----- 5. Bone grafts -----
207
+ if _has(t, AUTO_SEP_KW):
208
+ add("20937", "Autograft, separate incision",
209
+ "Iliac crest or separate-site autograft documented.", "graft", 0.8)
210
+ elif _has(t, AUTO_LOCAL_KW):
211
+ add("20936", "Autograft, local (same incision)",
212
+ "Local autograft material retained.", "graft", 0.8)
213
+ if _has(t, ALLO_KW):
214
+ add("20930", "Allograft, morselized / DBM",
215
+ "Allograft or DBM usage detected.", "graft", 0.8)
216
+
217
+ # ----- 6. Hardware removal -----
218
+ if _has(t, r"remov(ed|al).*instrument"):
219
+ add("22852", "Removal of posterior instrumentation",
220
+ "Hardware removal/explantation documented.", "hardware_removal", 0.84, True)
221
+
222
+ return out
223
+
224
+
225
+ # ─────────────────────────────────────────────
226
+ # SPECIALTY PACKS (Tumor / Deformity / Stimulator / Kyphoplasty / Revision)
227
+ # ─────────────────────────────────────────────
228
+ def _apply_specialty_packs(note: str, region: str, inters: int, levels: List[str]) -> List[Dict[str, Any]]:
229
+ """Return CPTs from specialty packs layered onto base logic."""
230
+ t = _norm(note)
231
+ extra: List[Dict[str, Any]] = []
232
+
233
+ def add(cpt, desc, rationale, cat, conf=0.84, primary=False):
234
+ extra.append({
235
+ "cpt": cpt,
236
+ "desc": desc,
237
+ "rationale": rationale,
238
+ "category": cat,
239
+ "confidence": round(conf, 2),
240
+ "primary": primary
241
+ })
242
+
243
+ # Tumor / corpectomy
244
+ if _has(t, r"corpectomy|tumou?r|metastatic|metastasis|en bloc"):
245
+ base = "63081" if region.startswith("cervical") else "63085"
246
+ add(base, "Vertebral corpectomy, first segment",
247
+ "Corpectomy or tumor resection detected.", "tumor/corpectomy", 0.88, True)
248
+ if inters > 0:
249
+ addon = "63082" if region.startswith("cervical") else "63086"
250
+ add(addon, f"Each additional segment Γ—{inters}",
251
+ "Multi-segment corpectomy.", "tumor/corpectomy add-on", 0.83)
252
+
253
+ # Deformity / osteotomy
254
+ if _has(t, r"spo\b|smith[- ]?petersen|posterior column osteotomy|osteotomy"):
255
+ base = {"cervical": "22210", "thoracic": "22214", "lumbar": "22206"}.get(region, "22206")
256
+ add(base, "Posterior column osteotomy, first level",
257
+ "Deformity correction (SPO/osteotomy) documented.", "deformity", 0.84, True)
258
+ if inters > 0:
259
+ add(str(int(base) + 1), f"Each additional level Γ—{inters}",
260
+ "Multi-level osteotomy.", "deformity add-on", 0.8)
261
+
262
+ # Stimulator
263
+ if _has(t, r"stimulator|paddle lead|scs"):
264
+ add("63655", "Laminectomy for implantation of neurostimulator paddle",
265
+ "Paddle lead implantation detected.", "stimulator", 0.86, True)
266
+ if _has(t, r"ipg|pulse generator|battery"):
267
+ add("63685", "Insertion of implantable pulse generator (IPG)",
268
+ "Pulse generator/battery placement documented.", "stimulator add-on", 0.82)
269
+
270
+ # Kyphoplasty / Vertebral augmentation
271
+ if _has(t, r"kyphoplasty|vertebroplasty|cement"):
272
+ base_map = {"cervical": "22510", "thoracic": "22513", "lumbar": "22514"}
273
+ base = base_map.get(region, "22514")
274
+ add(base, "Percutaneous vertebral augmentation, first level",
275
+ "Kyphoplasty/vertebroplasty terms found.", "augmentation", 0.84, True)
276
+ if inters > 0:
277
+ add("22515", f"Each additional level Γ—{inters}",
278
+ "Multi-level augmentation.", "augmentation add-on", 0.8)
279
+
280
+ # Revision instrumentation
281
+ if _has(t, r"revision of instrumentation|reinsert|remove and replace"):
282
+ add("22849", "Revision/reinsertion of instrumentation",
283
+ "Instrumentation revised/reinserted after removal.", "revision", 0.84, True)
284
+
285
+ # Pelvic fixation
286
+ if _has(t, r"pelvic fixation|iliac bolt|iliac screw|s2ai"):
287
+ add("22848", "Pelvic fixation (attach instrumentation to pelvis)",
288
+ "Iliac/S2AI fixation described.", "pelvic_fixation", 0.83)
289
+
290
+ return extra
291
+
292
+ ### --- END OF BLOCK 2 --- ###
293
+
294
+ # spine_coder_core_pro.py
295
+ # Vertebro FINAL-v2.2-PRO (Block 3 / 3)
296
+ # Output builder, rationale composer, and main entrypoint
297
+
298
+ # ─────────────────────────────────────────────
299
+ # MAIN OUTPUT ASSEMBLER
300
+ # ─────────────────────────────────────────────
301
+ def vertebro_infer(note: str, payer: str = "Medicare") -> Dict[str, Any]:
302
+ """Master inference wrapper β€” merges base logic + specialty packs."""
303
+ t = _norm(note)
304
+ levels = _extract_levels(t)
305
+ region = _classify_region(levels)
306
+ inters = _count_interspaces(levels)
307
+ laterality, mods = _laterality_and_modifiers(t)
308
+
309
+ # --- run inference ---
310
+ base_rows = _infer_cpts(t, region, levels)
311
+ extra_rows = _apply_specialty_packs(t, region, inters, levels)
312
+ rows = base_rows + extra_rows
313
+
314
+ # --- merge duplicates and compute confidence average ---
315
+ merged: Dict[str, Dict[str, Any]] = {}
316
+ for r in rows:
317
+ key = (r["cpt"], r["category"])
318
+ if key not in merged:
319
+ merged[key] = r.copy()
320
+ else:
321
+ merged[key]["confidence"] = round(
322
+ max(merged[key]["confidence"], r["confidence"]), 2)
323
+ merged[key]["rationale"] += " / " + r["rationale"]
324
+
325
+ rows = list(merged.values())
326
+ rows.sort(key=lambda r: (-r.get("confidence", 0.0), r["cpt"]))
327
+
328
+ # --- flag map (tech) ---
329
+ flags_map = {
330
+ "microscope": _has(t, r"\bmicroscope\b|microdissection"),
331
+ "nav": _has(t, r"\bnavigation\b|o-?arm|ziehm|3d\s+spin"),
332
+ "io_monitor": _has(t, r"\bneuromonitor|ssep|emg|monitoring\b"),
333
+ "fluoro": _has(t, r"\bfluoro|c[- ]?arm|fluoroscop"),
334
  }
335
+ tech_flags = [k for k, v in flags_map.items() if v]
336
+
337
+ # --- attach tech info to rationale for primaries ---
338
+ if tech_flags:
339
+ for r in rows:
340
+ if r.get("primary", False):
341
+ r["rationale"] = (
342
+ r.get("rationale", "") + f" (Tech: {', '.join(tech_flags)})"
343
+ ).strip()
344
+
345
+ # --- build final JSON output ---
346
+ result = {
347
+ "payer": payer,
348
+ "region": region,
349
+ "levels": levels,
350
+ "interspaces_est": inters,
351
+ "laterality": laterality,
352
+ "case_modifiers": mods,
353
+ "flags": tech_flags,
354
+ "flags_map": flags_map,
355
+ "suggestions": rows,
356
+ "build": "FINAL-v2.2-PRO",
357
+ "mode": "standard",
358
  }
359
+ return result
360
+
361
 
362
  # ─────────────────────────────────────────────
363
+ # CLI / HF Space TEST ENTRYPOINT
364
  # ─────────────────────────────────────────────
365
+ if __name__ == "__main__":
366
+ sample = """
367
+ Preoperative Diagnosis: C3–C6 stenosis with myelopathy.
368
+ Procedure:
369
+ 1. C3–C6 laminectomy and medial facetectomies.
370
+ 2. C2–T1 posterolateral arthrodesis.
371
+ 3. C2–T1 posterior instrumentation.
372
+ 4. Use of navigation and intra-operative fluoroscopy.
373
+ 5. Local autograft and allograft placed.
374
+ """
375
  result = vertebro_infer(sample)
376
  print(json.dumps(result, indent=2))