uumerrr684 commited on
Commit
e6541df
·
verified ·
1 Parent(s): 2f88dae

Upload 2 files

Browse files
Files changed (2) hide show
  1. app.py +586 -0
  2. requirements.txt. +19 -0
app.py ADDED
@@ -0,0 +1,586 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python3
2
+ """
3
+ Healthcare Billing Chatbot - Hugging Face Spaces App
4
+ Main application file for Hugging Face Spaces deployment
5
+ """
6
+
7
+ import os
8
+ import sys
9
+ import json
10
+ import logging
11
+ import re
12
+ from typing import Dict, Optional, Tuple, List, Any
13
+ from dataclasses import dataclass, field
14
+ from enum import Enum
15
+ import requests
16
+ import gradio as gr
17
+ from datetime import datetime, timedelta
18
+ from pathlib import Path
19
+
20
+ # Set up environment variables (for Hugging Face Spaces)
21
+ os.environ['OPENROUTER_API_KEY'] = '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'
22
+ os.environ['DATA_SOURCE'] = 'local_csv'
23
+ os.environ['DATA_PATH'] = 'data/sample_codes.csv'
24
+
25
+ # Configure logging
26
+ logging.basicConfig(
27
+ level=logging.INFO,
28
+ format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
29
+ )
30
+ logger = logging.getLogger(__name__)
31
+
32
+ # ============= Data Classes and Enums =============
33
+
34
+ class ExplanationLength(Enum):
35
+ SHORT = "short"
36
+ DETAILED = "detailed"
37
+ EXTRA = "extra"
38
+
39
+ class CodeType(Enum):
40
+ CPT = "CPT"
41
+ HCPCS = "HCPCS"
42
+ ICD10 = "ICD-10"
43
+ DRG = "DRG"
44
+ UNKNOWN = "UNKNOWN"
45
+
46
+ class DataSource(Enum):
47
+ LOCAL_CSV = "local_csv"
48
+ LOCAL_JSON = "local_json"
49
+ API = "api"
50
+
51
+ @dataclass
52
+ class ConversationContext:
53
+ current_code: Optional[str] = None
54
+ current_code_type: Optional[CodeType] = None
55
+ current_code_info: Optional['CodeInfo'] = None
56
+ conversation_history: List[Dict[str, str]] = field(default_factory=list)
57
+ last_explanation_length: Optional[ExplanationLength] = None
58
+ turn_count: int = 0
59
+
60
+ @dataclass
61
+ class CodeInfo:
62
+ code: str
63
+ description: str
64
+ code_type: str
65
+ additional_info: Optional[str] = None
66
+ effective_date: Optional[str] = None
67
+ category: Optional[str] = None
68
+
69
+ def to_dict(self) -> Dict[str, Any]:
70
+ return {
71
+ 'code': self.code,
72
+ 'description': self.description,
73
+ 'code_type': self.code_type,
74
+ 'additional_info': self.additional_info,
75
+ 'effective_date': self.effective_date,
76
+ 'category': self.category
77
+ }
78
+
79
+ # ============= HCPCS Loader Module =============
80
+
81
+ class HCPCSLoader:
82
+ """Loader for HCPCS/CPT/ICD-10/DRG codes"""
83
+
84
+ def __init__(self):
85
+ self.data_source = DataSource(os.getenv('DATA_SOURCE', 'local_csv'))
86
+ self.data_path = os.getenv('DATA_PATH', 'data/sample_codes.csv')
87
+ self.codes_cache: Dict[str, CodeInfo] = {}
88
+ self.cache_timestamp: Optional[datetime] = None
89
+
90
+ def load_data(self) -> bool:
91
+ """Load data from configured source"""
92
+ try:
93
+ return self._load_sample_data()
94
+ except Exception as e:
95
+ logger.error(f"Failed to load data: {e}")
96
+ return self._load_sample_data()
97
+
98
+ def _load_sample_data(self) -> bool:
99
+ """Load sample data as fallback"""
100
+ logger.info("Loading sample data")
101
+
102
+ sample_codes = {
103
+ 'A0429': CodeInfo(
104
+ code='A0429',
105
+ description='Ambulance service, basic life support, emergency transport (BLS-emergency)',
106
+ code_type='HCPCS',
107
+ additional_info='Ground ambulance emergency transport with BLS level care',
108
+ category='Ambulance Services'
109
+ ),
110
+ 'A0428': CodeInfo(
111
+ code='A0428',
112
+ description='Ambulance service, basic life support, non-emergency transport (BLS)',
113
+ code_type='HCPCS',
114
+ additional_info='Ground ambulance non-emergency transport with BLS level care',
115
+ category='Ambulance Services'
116
+ ),
117
+ '99213': CodeInfo(
118
+ code='99213',
119
+ description='Office or other outpatient visit for evaluation and management, established patient, low complexity',
120
+ code_type='CPT',
121
+ additional_info='Typically 20-29 minutes with patient',
122
+ category='E&M Services'
123
+ ),
124
+ '99214': CodeInfo(
125
+ code='99214',
126
+ description='Office or other outpatient visit for evaluation and management, established patient, moderate complexity',
127
+ code_type='CPT',
128
+ additional_info='Typically 30-39 minutes with patient',
129
+ category='E&M Services'
130
+ ),
131
+ '93000': CodeInfo(
132
+ code='93000',
133
+ description='Electrocardiogram, routine ECG with at least 12 leads; with interpretation and report',
134
+ code_type='CPT',
135
+ additional_info='Complete ECG service including tracing, interpretation, and report',
136
+ category='Cardiovascular'
137
+ ),
138
+ 'DRG470': CodeInfo(
139
+ code='DRG470',
140
+ description='Major hip and knee joint replacement or reattachment of lower extremity without MCC',
141
+ code_type='DRG',
142
+ additional_info='Medicare Severity-Diagnosis Related Group for joint replacement procedures',
143
+ category='Orthopedic'
144
+ ),
145
+ 'Z79.899': CodeInfo(
146
+ code='Z79.899',
147
+ description='Other long term (current) drug therapy',
148
+ code_type='ICD-10',
149
+ additional_info='Used to indicate patient is on long-term medication therapy',
150
+ category='Factors influencing health status'
151
+ ),
152
+ 'E1399': CodeInfo(
153
+ code='E1399',
154
+ description='Durable medical equipment, miscellaneous',
155
+ code_type='HCPCS',
156
+ additional_info='DME not otherwise classified, requires detailed description',
157
+ category='DME'
158
+ ),
159
+ 'G0442': CodeInfo(
160
+ code='G0442',
161
+ description='Annual alcohol screening, 5 to 10 minutes',
162
+ code_type='HCPCS',
163
+ additional_info='Medicare-covered annual alcohol misuse screening',
164
+ category='Preventive Services'
165
+ ),
166
+ '90837': CodeInfo(
167
+ code='90837',
168
+ description='Psychotherapy, 60 minutes with patient',
169
+ code_type='CPT',
170
+ additional_info='Individual psychotherapy session, 53-60 minutes',
171
+ category='Psychiatric Services'
172
+ ),
173
+ 'J3420': CodeInfo(
174
+ code='J3420',
175
+ description='Injection, vitamin B-12 cyanocobalamin, up to 1000 mcg',
176
+ code_type='HCPCS',
177
+ additional_info='Vitamin B12 injection for treatment of deficiency',
178
+ category='Drug Administration'
179
+ ),
180
+ '80053': CodeInfo(
181
+ code='80053',
182
+ description='Comprehensive metabolic panel',
183
+ code_type='CPT',
184
+ additional_info='14 tests including glucose, kidney function, liver function, and electrolytes',
185
+ category='Laboratory'
186
+ ),
187
+ 'A0425': CodeInfo(
188
+ code='A0425',
189
+ description='Ground mileage, per statute mile',
190
+ code_type='HCPCS',
191
+ additional_info='Ambulance mileage for ground transport',
192
+ category='Ambulance Services'
193
+ ),
194
+ '99215': CodeInfo(
195
+ code='99215',
196
+ description='Office visit, established patient, high complexity',
197
+ code_type='CPT',
198
+ additional_info='Typically 40-54 minutes with patient',
199
+ category='E&M Services'
200
+ ),
201
+ '70450': CodeInfo(
202
+ code='70450',
203
+ description='CT scan head or brain without contrast',
204
+ code_type='CPT',
205
+ additional_info='Computed tomography of head/brain without contrast material',
206
+ category='Radiology'
207
+ )
208
+ }
209
+
210
+ self.codes_cache = sample_codes
211
+ self.cache_timestamp = datetime.now()
212
+ return True
213
+
214
+ def lookup_code(self, code: str) -> Optional[CodeInfo]:
215
+ """Look up a single code"""
216
+ code = code.strip().upper()
217
+
218
+ if code in self.codes_cache:
219
+ return self.codes_cache[code]
220
+
221
+ # Try with DRG prefix
222
+ if code.startswith('DRG') and len(code) == 6:
223
+ drg_code = code[3:]
224
+ if drg_code in self.codes_cache:
225
+ return self.codes_cache[drg_code]
226
+
227
+ # Try adding DRG prefix
228
+ if len(code) == 3 and code.isdigit():
229
+ drg_code = f"DRG{code}"
230
+ if drg_code in self.codes_cache:
231
+ return self.codes_cache[drg_code]
232
+
233
+ return None
234
+
235
+ def get_statistics(self) -> Dict[str, Any]:
236
+ """Get statistics about loaded codes"""
237
+ stats = {
238
+ 'total_codes': len(self.codes_cache),
239
+ 'source': self.data_source.value,
240
+ 'codes_by_type': {}
241
+ }
242
+
243
+ for code_info in self.codes_cache.values():
244
+ code_type = code_info.code_type
245
+ stats['codes_by_type'][code_type] = stats['codes_by_type'].get(code_type, 0) + 1
246
+
247
+ return stats
248
+
249
+ # ============= Code Classifier =============
250
+
251
+ class CodeClassifier:
252
+ """Classifies and validates healthcare billing codes"""
253
+
254
+ PATTERNS = {
255
+ CodeType.CPT: re.compile(r'^[0-9]{5}$'),
256
+ CodeType.HCPCS: re.compile(r'^[A-V][0-9]{4}$', re.IGNORECASE),
257
+ CodeType.ICD10: re.compile(r'^[A-Z][0-9]{2}\.?[0-9]{0,3}$', re.IGNORECASE),
258
+ CodeType.DRG: re.compile(r'^(?:DRG)?[0-9]{3}$', re.IGNORECASE),
259
+ }
260
+
261
+ @classmethod
262
+ def classify_code(cls, code: str) -> CodeType:
263
+ """Classify a code based on its format"""
264
+ code = code.strip().upper()
265
+
266
+ if code.startswith('DRG'):
267
+ code = code[3:]
268
+ if cls.PATTERNS[CodeType.DRG].match(f"DRG{code}"):
269
+ return CodeType.DRG
270
+
271
+ for code_type, pattern in cls.PATTERNS.items():
272
+ if pattern.match(code):
273
+ return code_type
274
+
275
+ return CodeType.UNKNOWN
276
+
277
+ @classmethod
278
+ def extract_codes_from_text(cls, text: str) -> List[Tuple[str, CodeType]]:
279
+ """Extract potential codes from free-form text"""
280
+ codes = []
281
+
282
+ patterns = [
283
+ r'\b([A-V][0-9]{4})\b', # HCPCS
284
+ r'\b([0-9]{5})\b', # CPT
285
+ r'\bDRG\s*([0-9]{3})\b', # DRG
286
+ r'\b([A-Z][0-9]{2}\.?[0-9]{0,3})\b', # ICD-10
287
+ ]
288
+
289
+ for pattern in patterns:
290
+ matches = re.findall(pattern, text, re.IGNORECASE)
291
+ for match in matches:
292
+ code_type = cls.classify_code(match)
293
+ if code_type != CodeType.UNKNOWN:
294
+ codes.append((match.upper(), code_type))
295
+
296
+ return codes
297
+
298
+ # ============= OpenRouter Client =============
299
+
300
+ class OpenRouterClient:
301
+ """Client for OpenRouter LLM API"""
302
+
303
+ def __init__(self):
304
+ self.api_key = os.getenv('OPENROUTER_API_KEY', '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')
305
+ self.base_url = 'https://openrouter.ai/api/v1'
306
+ self.model = 'openai/gpt-3.5-turbo'
307
+ self.temperature = 0.3
308
+
309
+ self.headers = {
310
+ 'Authorization': f'Bearer {self.api_key}',
311
+ 'Content-Type': 'application/json',
312
+ 'HTTP-Referer': 'https://huggingface.co',
313
+ 'X-Title': 'Healthcare Billing Chatbot'
314
+ }
315
+
316
+ def generate_explanation(self,
317
+ code: str,
318
+ code_type: CodeType,
319
+ code_info: Optional[CodeInfo],
320
+ length: ExplanationLength,
321
+ context: Optional[ConversationContext] = None) -> str:
322
+ """Generate explanation using LLM"""
323
+
324
+ if not code_info:
325
+ return f"I couldn't find code {code} in our database. This might be an invalid code or one that's not in our current dataset."
326
+
327
+ prompt = self._build_prompt(code, code_type, code_info, length, context)
328
+
329
+ max_tokens = {
330
+ ExplanationLength.SHORT: 150,
331
+ ExplanationLength.DETAILED: 300,
332
+ ExplanationLength.EXTRA: 500
333
+ }.get(length, 200)
334
+
335
+ payload = {
336
+ 'model': self.model,
337
+ 'messages': [
338
+ {'role': 'system', 'content': self._get_system_prompt()},
339
+ {'role': 'user', 'content': prompt}
340
+ ],
341
+ 'temperature': self.temperature,
342
+ 'max_tokens': max_tokens
343
+ }
344
+
345
+ try:
346
+ response = requests.post(
347
+ f'{self.base_url}/chat/completions',
348
+ headers=self.headers,
349
+ json=payload,
350
+ timeout=30
351
+ )
352
+ response.raise_for_status()
353
+
354
+ result = response.json()
355
+ explanation = result['choices'][0]['message']['content']
356
+ return explanation
357
+
358
+ except Exception as e:
359
+ logger.error(f"OpenRouter API error: {e}")
360
+ return self._get_fallback_explanation(code, code_type, code_info, length)
361
+
362
+ def _get_system_prompt(self) -> str:
363
+ return """You are a healthcare billing expert assistant. Provide accurate,
364
+ fact-based explanations of healthcare billing codes. Only provide information
365
+ you are certain about. Be concise but informative. Use plain language."""
366
+
367
+ def _build_prompt(self, code: str, code_type: CodeType, code_info: CodeInfo,
368
+ length: ExplanationLength, context: Optional[ConversationContext]) -> str:
369
+
370
+ base_prompt = f"""Healthcare Billing Code: {code}
371
+ Type: {code_type.value}
372
+ Official Description: {code_info.description}
373
+ Additional Information: {code_info.additional_info or 'N/A'}
374
+ """
375
+
376
+ if length == ExplanationLength.SHORT:
377
+ base_prompt += "\nProvide a SHORT explanation (3-4 lines) in simple terms."
378
+ elif length == ExplanationLength.DETAILED:
379
+ base_prompt += "\nProvide a DETAILED explanation including what it covers and when it's typically used."
380
+ elif length == ExplanationLength.EXTRA:
381
+ base_prompt += "\nProvide EXTENDED details with sections for: typical use cases, coverage warnings, and important notes."
382
+
383
+ return base_prompt
384
+
385
+ def _get_fallback_explanation(self, code: str, code_type: CodeType,
386
+ code_info: CodeInfo, length: ExplanationLength) -> str:
387
+ if length == ExplanationLength.SHORT:
388
+ return f"{code} ({code_type.value}): {code_info.description[:150]}"
389
+ else:
390
+ explanation = f"{code} ({code_type.value}): {code_info.description}"
391
+ if code_info.additional_info:
392
+ explanation += f"\n\nAdditional Details: {code_info.additional_info}"
393
+ return explanation
394
+
395
+ # ============= Main Chatbot Class =============
396
+
397
+ class HealthcareBillingChatbot:
398
+ """Main chatbot class"""
399
+
400
+ def __init__(self):
401
+ self.loader = HCPCSLoader()
402
+ self.classifier = CodeClassifier()
403
+ self.llm_client = OpenRouterClient()
404
+ self.context = ConversationContext()
405
+ self.loader.load_data()
406
+
407
+ def process_input(self, user_input: str) -> str:
408
+ """Process user input and generate response"""
409
+ self.context.turn_count += 1
410
+
411
+ codes = self.classifier.extract_codes_from_text(user_input)
412
+ intent = self._determine_intent(user_input, codes)
413
+ response = self._generate_response(user_input, codes, intent)
414
+
415
+ self.context.conversation_history.append({
416
+ 'user': user_input,
417
+ 'assistant': response
418
+ })
419
+
420
+ if len(self.context.conversation_history) > 10:
421
+ self.context.conversation_history = self.context.conversation_history[-10:]
422
+
423
+ return response
424
+
425
+ def _determine_intent(self, user_input: str, codes: List[Tuple[str, CodeType]]) -> str:
426
+ """Determine user intent"""
427
+ lower_input = user_input.lower()
428
+
429
+ if self.context.current_code:
430
+ if any(keyword in lower_input for keyword in ['more', 'detail', 'explain']):
431
+ return 'more_details'
432
+
433
+ if any(keyword in lower_input for keyword in ['short', 'brief', 'quick']):
434
+ return 'explain_short'
435
+ if any(keyword in lower_input for keyword in ['detail', 'comprehensive']):
436
+ return 'explain_detailed'
437
+
438
+ if codes:
439
+ return 'explain_code'
440
+
441
+ return 'general_query'
442
+
443
+ def _generate_response(self, user_input: str, codes: List[Tuple[str, CodeType]], intent: str) -> str:
444
+ """Generate response based on intent"""
445
+
446
+ if codes and intent in ['explain_code', 'explain_short', 'explain_detailed']:
447
+ code, code_type = codes[0]
448
+ code_info = self.loader.lookup_code(code)
449
+
450
+ if not code_info:
451
+ return f"I couldn't find code {code} in our database. Please check the code and try again."
452
+
453
+ self.context.current_code = code
454
+ self.context.current_code_type = code_type
455
+ self.context.current_code_info = code_info
456
+
457
+ length = ExplanationLength.SHORT
458
+ if intent == 'explain_detailed':
459
+ length = ExplanationLength.DETAILED
460
+
461
+ explanation = self.llm_client.generate_explanation(
462
+ code, code_type, code_info, length, self.context
463
+ )
464
+
465
+ if length != ExplanationLength.EXTRA:
466
+ explanation += "\n\nWould you like more details about this code?"
467
+
468
+ return explanation
469
+
470
+ if self.context.current_code and intent == 'more_details':
471
+ if not self.context.current_code_info:
472
+ return "Please specify a code first."
473
+
474
+ return self.llm_client.generate_explanation(
475
+ self.context.current_code,
476
+ self.context.current_code_type,
477
+ self.context.current_code_info,
478
+ ExplanationLength.EXTRA,
479
+ self.context
480
+ )
481
+
482
+ return self._handle_general_query(user_input)
483
+
484
+ def _handle_general_query(self, user_input: str) -> str:
485
+ """Handle general queries"""
486
+ lower_input = user_input.lower()
487
+
488
+ if any(keyword in lower_input for keyword in ['help', 'how']):
489
+ return """I can help you understand healthcare billing codes! You can:
490
+ • Ask about specific codes (e.g., "What is HCPCS A0429?")
491
+ • Request short or detailed explanations
492
+ • Ask follow-up questions about codes we've discussed
493
+
494
+ Just type a code or ask a question to get started!"""
495
+
496
+ if any(keyword in lower_input for keyword in ['hello', 'hi', 'hey']):
497
+ return "Hello! I'm here to help you understand healthcare billing codes. Feel free to ask about any CPT, HCPCS, ICD-10, or DRG code."
498
+
499
+ return "I'm designed to help explain healthcare billing codes. Please provide a specific code (like A0429, 99213, or DRG470) and I'll give you a clear explanation."
500
+
501
+ # ============= Gradio Interface =============
502
+
503
+ def create_gradio_interface():
504
+ """Create and return the Gradio interface"""
505
+ chatbot = HealthcareBillingChatbot()
506
+
507
+ def process_message(message: str, history: List[Tuple[str, str]]) -> Tuple[str, List[Tuple[str, str]]]:
508
+ """Process a message and update chat history"""
509
+ if not message:
510
+ return "", history
511
+
512
+ try:
513
+ response = chatbot.process_input(message)
514
+ history.append((message, response))
515
+ return "", history
516
+ except Exception as e:
517
+ logger.error(f"Error: {e}")
518
+ error_msg = "I apologize, but I encountered an error. Please try again."
519
+ history.append((message, error_msg))
520
+ return "", history
521
+
522
+ def clear_conversation():
523
+ """Clear the conversation"""
524
+ chatbot.context = ConversationContext()
525
+ return None
526
+
527
+ # Create the interface
528
+ with gr.Blocks(title="Healthcare Billing Chatbot", theme=gr.themes.Soft()) as interface:
529
+ gr.Markdown(
530
+ """
531
+ # 🏥 Healthcare Billing Chatbot
532
+
533
+ Ask me about healthcare billing codes (CPT, HCPCS, ICD-10, DRG) and I'll provide clear explanations!
534
+
535
+ ### Example Queries:
536
+ - "What is HCPCS code A0429?"
537
+ - "Explain CPT 99213 in detail"
538
+ - "Tell me about DRG 470"
539
+ """
540
+ )
541
+
542
+ chatbot_ui = gr.Chatbot(label="Conversation", height=400)
543
+
544
+ with gr.Row():
545
+ msg = gr.Textbox(
546
+ label="Your Message",
547
+ placeholder="Type a billing code or ask a question...",
548
+ lines=2,
549
+ scale=4
550
+ )
551
+ submit_btn = gr.Button("Send", variant="primary", scale=1)
552
+
553
+ with gr.Row():
554
+ clear_btn = gr.Button("🔄 Clear Conversation")
555
+
556
+ with gr.Column():
557
+ gr.Examples(
558
+ examples=[
559
+ "What is HCPCS A0429?",
560
+ "Explain CPT 99213",
561
+ "Tell me about DRG 470",
562
+ "What does code 93000 mean?",
563
+ "Give me more details"
564
+ ],
565
+ inputs=msg
566
+ )
567
+
568
+ # Event handlers
569
+ msg.submit(process_message, [msg, chatbot_ui], [msg, chatbot_ui])
570
+ submit_btn.click(process_message, [msg, chatbot_ui], [msg, chatbot_ui])
571
+ clear_btn.click(clear_conversation, outputs=[chatbot_ui])
572
+
573
+ stats = chatbot.loader.get_statistics()
574
+ gr.Markdown(
575
+ f"""
576
+ ---
577
+ **Database Info:** {stats['total_codes']} codes loaded |
578
+ Types: {', '.join(stats['codes_by_type'].keys())}
579
+ """
580
+ )
581
+
582
+ return interface
583
+
584
+ # Launch the app
585
+ interface = create_gradio_interface()
586
+ interface.launch()
requirements.txt. ADDED
@@ -0,0 +1,19 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Healthcare Billing Chatbot - Python Dependencies
2
+
3
+ # Core dependencies
4
+ requests==2.31.0
5
+ python-dotenv==1.0.0
6
+
7
+ # Optional: Gradio UI
8
+ gradio==4.19.2
9
+
10
+ # Development and testing
11
+ pytest==8.0.0
12
+ pytest-cov==4.1.0
13
+ pytest-mock==3.12.0
14
+
15
+ # Optional: Enhanced logging
16
+ colorlog==6.8.0
17
+
18
+ # Optional: Data processing
19
+ pandas==2.2.0