uumerrr684 commited on
Commit
ca5dc36
Β·
verified Β·
1 Parent(s): e6541df

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +491 -439
app.py CHANGED
@@ -1,7 +1,7 @@
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
@@ -14,48 +14,17 @@ 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:
@@ -63,222 +32,139 @@ class CodeInfo:
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
@@ -287,300 +173,466 @@ class CodeClassifier:
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()
 
 
 
 
 
 
1
  #!/usr/bin/env python3
2
  """
3
+ Hybrid AI Assistant - General Purpose + Healthcare Billing Expert
4
+ A ChatGPT-style assistant that can handle any conversation while specializing in healthcare billing codes
5
  """
6
 
7
  import os
 
14
  from enum import Enum
15
  import requests
16
  import gradio as gr
17
+ from datetime import datetime
18
+ import random
19
 
20
+ # Set up environment
21
  os.environ['OPENROUTER_API_KEY'] = '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'
 
 
22
 
23
  # Configure logging
24
+ logging.basicConfig(level=logging.INFO)
 
 
 
25
  logger = logging.getLogger(__name__)
26
 
27
+ # ============= Data Classes =============
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
28
 
29
  @dataclass
30
  class CodeInfo:
 
32
  description: str
33
  code_type: str
34
  additional_info: Optional[str] = None
 
35
  category: Optional[str] = None
 
 
 
 
 
 
 
 
 
 
36
 
37
+ @dataclass
38
+ class ConversationContext:
39
+ messages: List[Dict[str, str]] = field(default_factory=list)
40
+ detected_codes: List[str] = field(default_factory=list)
41
+ last_topic: Optional[str] = None
42
+
43
+ # ============= Healthcare Billing Database =============
44
 
45
+ class BillingCodesDB:
 
 
46
  def __init__(self):
47
+ self.codes = {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
48
  'A0429': CodeInfo(
49
  code='A0429',
50
  description='Ambulance service, basic life support, emergency transport (BLS-emergency)',
51
  code_type='HCPCS',
52
+ additional_info='Ground ambulance emergency transport with BLS level care. Used for emergency situations requiring immediate medical transport.',
53
  category='Ambulance Services'
54
  ),
55
  'A0428': CodeInfo(
56
  code='A0428',
57
+ description='Ambulance service, basic life support, non-emergency transport',
58
  code_type='HCPCS',
59
+ additional_info='Scheduled or non-urgent medical transport with basic life support.',
60
  category='Ambulance Services'
61
  ),
62
  '99213': CodeInfo(
63
  code='99213',
64
+ description='Office visit for established patient, low complexity',
65
  code_type='CPT',
66
+ additional_info='Typically 20-29 minutes. For straightforward medical issues.',
67
  category='E&M Services'
68
  ),
69
  '99214': CodeInfo(
70
  code='99214',
71
+ description='Office visit for established patient, moderate complexity',
72
  code_type='CPT',
73
+ additional_info='Typically 30-39 minutes. For moderately complex medical issues.',
74
+ category='E&M Services'
75
+ ),
76
+ '99215': CodeInfo(
77
+ code='99215',
78
+ description='Office visit for established patient, high complexity',
79
+ code_type='CPT',
80
+ additional_info='Typically 40-54 minutes. For complex medical decision making.',
81
  category='E&M Services'
82
  ),
83
  '93000': CodeInfo(
84
  code='93000',
85
+ description='Electrocardiogram (ECG/EKG) with interpretation',
86
  code_type='CPT',
87
+ additional_info='Complete 12-lead ECG including test, interpretation, and report.',
88
  category='Cardiovascular'
89
  ),
90
  'DRG470': CodeInfo(
91
  code='DRG470',
92
+ description='Major hip and knee joint replacement without complications',
93
  code_type='DRG',
94
+ additional_info='Medicare payment group for joint replacement surgeries.',
95
  category='Orthopedic'
96
  ),
97
  'Z79.899': CodeInfo(
98
  code='Z79.899',
99
+ description='Other long term drug therapy',
100
  code_type='ICD-10',
101
+ additional_info='Indicates patient is on long-term medication.',
102
+ category='Diagnosis'
103
  ),
104
  'E1399': CodeInfo(
105
  code='E1399',
106
  description='Durable medical equipment, miscellaneous',
107
  code_type='HCPCS',
108
+ additional_info='For DME not elsewhere classified.',
109
+ category='Equipment'
 
 
 
 
 
 
 
 
 
 
 
 
 
 
110
  ),
111
  'J3420': CodeInfo(
112
  code='J3420',
113
+ description='Vitamin B-12 injection',
114
  code_type='HCPCS',
115
+ additional_info='Cyanocobalamin up to 1000 mcg.',
116
+ category='Injections'
117
  ),
118
  '80053': CodeInfo(
119
  code='80053',
120
  description='Comprehensive metabolic panel',
121
  code_type='CPT',
122
+ additional_info='14 blood tests including glucose, kidney, and liver function.',
123
  category='Laboratory'
124
  ),
 
 
 
 
 
 
 
 
 
 
 
 
 
 
125
  '70450': CodeInfo(
126
  code='70450',
127
+ description='CT head/brain without contrast',
128
  code_type='CPT',
129
+ additional_info='Computed tomography of head without contrast material.',
130
  category='Radiology'
131
+ ),
132
+ '90837': CodeInfo(
133
+ code='90837',
134
+ description='Psychotherapy, 60 minutes',
135
+ code_type='CPT',
136
+ additional_info='Individual psychotherapy session.',
137
+ category='Mental Health'
138
+ ),
139
+ '36415': CodeInfo(
140
+ code='36415',
141
+ description='Venipuncture (blood draw)',
142
+ code_type='CPT',
143
+ additional_info='Collection of blood by needle.',
144
+ category='Laboratory'
145
+ ),
146
+ '99282': CodeInfo(
147
+ code='99282',
148
+ description='Emergency department visit, low-moderate severity',
149
+ code_type='CPT',
150
+ additional_info='ED visit for problems of low to moderate severity.',
151
+ category='Emergency'
152
  )
153
  }
 
 
 
 
154
 
155
+ def lookup(self, code: str) -> Optional[CodeInfo]:
 
156
  code = code.strip().upper()
157
+ if code in self.codes:
158
+ return self.codes[code]
159
+ if code.isdigit() and len(code) == 3:
 
 
 
 
 
 
 
 
 
160
  drg_code = f"DRG{code}"
161
+ if drg_code in self.codes:
162
+ return self.codes[drg_code]
 
163
  return None
164
 
165
+ def search_codes(self, text: str) -> List[str]:
166
+ """Extract potential billing codes from text"""
167
+ found_codes = []
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
168
  patterns = [
169
  r'\b([A-V][0-9]{4})\b', # HCPCS
170
  r'\b([0-9]{5})\b', # CPT
 
173
  ]
174
 
175
  for pattern in patterns:
176
+ matches = re.findall(pattern, text.upper())
177
  for match in matches:
178
+ if self.lookup(match):
179
+ found_codes.append(match)
 
180
 
181
+ return found_codes
182
 
183
+ # ============= AI Assistant Class =============
184
 
185
+ class HybridAIAssistant:
 
 
186
  def __init__(self):
187
+ self.api_key = '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'
188
+ self.billing_db = BillingCodesDB()
189
+ self.context = ConversationContext()
 
190
 
191
  self.headers = {
192
  'Authorization': f'Bearer {self.api_key}',
193
  'Content-Type': 'application/json',
194
  'HTTP-Referer': 'https://huggingface.co',
195
+ 'X-Title': 'Hybrid AI Assistant'
196
  }
197
 
198
+ def detect_intent(self, message: str) -> Dict[str, Any]:
199
+ """Detect if the message is about billing codes or general conversation"""
200
+ lower_msg = message.lower()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
201
 
202
+ # Check for billing codes in the message
203
+ codes = self.billing_db.search_codes(message)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
204
 
205
+ # Keywords that suggest billing/medical coding questions
206
+ billing_keywords = ['code', 'cpt', 'hcpcs', 'icd', 'drg', 'billing', 'medical code',
207
+ 'healthcare code', 'diagnosis code', 'procedure code']
 
 
208
 
209
+ is_billing = any(keyword in lower_msg for keyword in billing_keywords) or len(codes) > 0
 
 
 
 
 
210
 
211
+ return {
212
+ 'is_billing': is_billing,
213
+ 'codes_found': codes,
214
+ 'message': message
215
+ }
216
 
217
+ def handle_billing_query(self, message: str, codes: List[str]) -> str:
218
+ """Handle healthcare billing specific queries"""
219
+ responses = []
220
+
221
+ if codes:
222
+ for code in codes[:3]: # Limit to first 3 codes
223
+ info = self.billing_db.lookup(code)
224
+ if info:
225
+ response = f"**{info.code} ({info.code_type})**\n"
226
+ response += f"πŸ“‹ **Description:** {info.description}\n"
227
+ if info.additional_info:
228
+ response += f"ℹ️ **Details:** {info.additional_info}\n"
229
+ if info.category:
230
+ response += f"🏷️ **Category:** {info.category}\n"
231
+ responses.append(response)
232
+
233
+ if responses:
234
+ final_response = "I found information about the billing code(s) you mentioned:\n\n"
235
+ final_response += "\n---\n".join(responses)
236
+ final_response += "\n\nπŸ’‘ **Need more details?** Feel free to ask specific questions about these codes!"
237
+ return final_response
238
  else:
239
+ return self.get_general_response(message, billing_context=True)
 
 
 
 
 
 
 
 
240
 
241
+ def get_general_response(self, message: str, billing_context: bool = False) -> str:
242
+ """Get response from OpenRouter API for general queries"""
 
 
 
 
 
 
 
 
243
 
244
+ # Prepare system prompt
245
+ system_prompt = """You are a helpful, friendly AI assistant with expertise in healthcare billing codes.
246
+ You can assist with any topic - from casual conversation to complex questions.
247
+ When discussing medical billing codes, you provide accurate, detailed information.
248
+ Be conversational, helpful, and engaging. Use emojis occasionally to be friendly.
249
+ Keep responses concise but informative."""
250
 
251
+ if billing_context:
252
+ system_prompt += "\nThe user is asking about medical billing. Provide helpful information even if you don't have specific code details."
 
 
253
 
254
+ # Build conversation history for context
255
+ messages = [{'role': 'system', 'content': system_prompt}]
256
 
257
+ # Add recent conversation history (last 5 exchanges)
258
+ for msg in self.context.messages[-10:]:
259
+ messages.append(msg)
 
 
 
 
 
 
260
 
261
+ # Add current message
262
+ messages.append({'role': 'user', 'content': message})
 
 
263
 
264
+ try:
265
+ response = requests.post(
266
+ 'https://openrouter.ai/api/v1/chat/completions',
267
+ headers=self.headers,
268
+ json={
269
+ 'model': 'openai/gpt-3.5-turbo',
270
+ 'messages': messages,
271
+ 'temperature': 0.7,
272
+ 'max_tokens': 500
273
+ },
274
+ timeout=30
 
 
 
 
 
 
 
 
 
 
 
 
 
 
275
  )
276
 
277
+ if response.status_code == 200:
278
+ result = response.json()
279
+ ai_response = result['choices'][0]['message']['content']
280
+
281
+ # Update context
282
+ self.context.messages.append({'role': 'user', 'content': message})
283
+ self.context.messages.append({'role': 'assistant', 'content': ai_response})
284
+
285
+ # Keep only last 20 messages in context
286
+ if len(self.context.messages) > 20:
287
+ self.context.messages = self.context.messages[-20:]
288
+
289
+ return ai_response
290
+ else:
291
+ logger.error(f"API error: {response.status_code}")
292
+ return self.get_fallback_response(message)
293
+
294
+ except Exception as e:
295
+ logger.error(f"Request failed: {e}")
296
+ return self.get_fallback_response(message)
297
 
298
+ def get_fallback_response(self, message: str) -> str:
299
+ """Fallback responses when API fails"""
300
+ fallbacks = [
301
+ "I'm having trouble connecting right now, but I'm still here to help! Could you rephrase your question?",
302
+ "Let me think about that differently. What specific aspect would you like to know more about?",
303
+ "That's an interesting question! While I process that, is there anything specific you'd like to explore?",
304
+ "I'm here to help! Could you provide a bit more detail about what you're looking for?"
305
+ ]
306
+ return random.choice(fallbacks)
307
+
308
+ def process_message(self, message: str) -> str:
309
+ """Main method to process any message"""
310
+ if not message.strip():
311
+ return "Feel free to ask me anything! I can help with general questions or healthcare billing codes. 😊"
312
 
313
+ # Detect intent
314
+ intent = self.detect_intent(message)
315
 
316
+ # Route to appropriate handler
317
+ if intent['is_billing'] and intent['codes_found']:
318
+ return self.handle_billing_query(message, intent['codes_found'])
319
+ else:
320
+ return self.get_general_response(message, billing_context=intent['is_billing'])
321
+
322
+ def reset_context(self):
323
+ """Reset conversation context"""
324
+ self.context = ConversationContext()
325
 
326
  # ============= Gradio Interface =============
327
 
328
+ def create_interface():
329
+ assistant = HybridAIAssistant()
 
330
 
331
+ # ChatGPT-style CSS
332
+ custom_css = """
333
+ /* Main container */
334
+ .gradio-container {
335
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Helvetica Neue', Arial, sans-serif !important;
336
+ max-width: 900px !important;
337
+ margin: auto !important;
338
+ background: #ffffff !important;
339
+ }
 
 
 
 
 
340
 
341
+ /* Header styling */
342
+ .header-container {
343
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
344
+ padding: 2rem;
345
+ border-radius: 15px 15px 0 0;
346
+ margin-bottom: 0;
347
+ box-shadow: 0 4px 6px rgba(0,0,0,0.1);
348
+ }
349
 
350
+ .header-title {
351
+ color: white;
352
+ font-size: 2rem;
353
+ font-weight: 700;
354
+ margin: 0;
355
+ display: flex;
356
+ align-items: center;
357
+ justify-content: center;
358
+ gap: 0.5rem;
359
+ }
360
+
361
+ .header-subtitle {
362
+ color: rgba(255,255,255,0.9);
363
+ font-size: 1rem;
364
+ margin-top: 0.5rem;
365
+ text-align: center;
366
+ }
367
+
368
+ /* Chat container */
369
+ #chatbot {
370
+ height: 500px !important;
371
+ border: 1px solid #e5e7eb !important;
372
+ border-radius: 12px !important;
373
+ box-shadow: 0 2px 6px rgba(0,0,0,0.05) !important;
374
+ background: #ffffff !important;
375
+ }
376
+
377
+ /* Message styling */
378
+ .message {
379
+ padding: 1rem !important;
380
+ margin: 0.5rem !important;
381
+ border-radius: 12px !important;
382
+ font-size: 15px !important;
383
+ line-height: 1.6 !important;
384
+ }
385
+
386
+ .user-message {
387
+ background: #f3f4f6 !important;
388
+ border: 1px solid #e5e7eb !important;
389
+ margin-left: 20% !important;
390
+ }
391
+
392
+ .bot-message {
393
+ background: #ffffff !important;
394
+ border: 1px solid #e5e7eb !important;
395
+ margin-right: 20% !important;
396
+ }
397
+
398
+ /* Input area */
399
+ #input-box {
400
+ border: 2px solid #e5e7eb !important;
401
+ border-radius: 12px !important;
402
+ padding: 14px 16px !important;
403
+ font-size: 15px !important;
404
+ transition: all 0.3s ease !important;
405
+ background: #ffffff !important;
406
+ }
407
+
408
+ #input-box:focus {
409
+ border-color: #667eea !important;
410
+ box-shadow: 0 0 0 3px rgba(102, 126, 234, 0.1) !important;
411
+ outline: none !important;
412
+ }
413
+
414
+ /* Buttons */
415
+ .primary-btn {
416
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%) !important;
417
+ color: white !important;
418
+ border: none !important;
419
+ border-radius: 10px !important;
420
+ padding: 12px 24px !important;
421
+ font-weight: 600 !important;
422
+ font-size: 15px !important;
423
+ cursor: pointer !important;
424
+ transition: transform 0.2s ease !important;
425
+ }
426
+
427
+ .primary-btn:hover {
428
+ transform: translateY(-1px) !important;
429
+ box-shadow: 0 4px 12px rgba(102, 126, 234, 0.3) !important;
430
+ }
431
+
432
+ .secondary-btn {
433
+ background: #f3f4f6 !important;
434
+ color: #374151 !important;
435
+ border: 1px solid #e5e7eb !important;
436
+ border-radius: 10px !important;
437
+ padding: 10px 20px !important;
438
+ font-weight: 500 !important;
439
+ cursor: pointer !important;
440
+ transition: all 0.2s ease !important;
441
+ }
442
+
443
+ .secondary-btn:hover {
444
+ background: #e5e7eb !important;
445
+ border-color: #d1d5db !important;
446
+ }
447
+
448
+ /* Example chips */
449
+ .example-chip {
450
+ display: inline-block !important;
451
+ background: #ffffff !important;
452
+ border: 1px solid #e5e7eb !important;
453
+ border-radius: 20px !important;
454
+ padding: 8px 16px !important;
455
+ margin: 4px !important;
456
+ font-size: 14px !important;
457
+ color: #4b5563 !important;
458
+ cursor: pointer !important;
459
+ transition: all 0.2s ease !important;
460
+ }
461
+
462
+ .example-chip:hover {
463
+ background: #f9fafb !important;
464
+ border-color: #667eea !important;
465
+ color: #667eea !important;
466
+ transform: translateY(-1px) !important;
467
+ }
468
+
469
+ /* Info cards */
470
+ .info-card {
471
+ background: linear-gradient(135deg, #f6f8fb 0%, #f1f5f9 100%);
472
+ border: 1px solid #e5e7eb;
473
+ border-radius: 12px;
474
+ padding: 1rem;
475
+ margin: 1rem 0;
476
+ }
477
+
478
+ /* Responsive design */
479
+ @media (max-width: 768px) {
480
+ .gradio-container {
481
+ padding: 0 !important;
482
+ }
483
 
484
+ .header-title {
485
+ font-size: 1.5rem;
486
+ }
487
+
488
+ .user-message, .bot-message {
489
+ margin-left: 5% !important;
490
+ margin-right: 5% !important;
491
+ }
492
+ }
493
+ """
494
+
495
+ with gr.Blocks(css=custom_css, theme=gr.themes.Base()) as app:
496
+ # Header
497
+ gr.HTML("""
498
+ <div class="header-container">
499
+ <h1 class="header-title">
500
+ <span>πŸ€–</span>
501
+ <span>AI Assistant</span>
502
+ <span style="font-size: 0.8em; background: rgba(255,255,255,0.2); padding: 2px 8px; border-radius: 12px;">PLUS</span>
503
+ </h1>
504
+ <p class="header-subtitle">Your intelligent companion for any question + Healthcare Billing Expert</p>
505
+ </div>
506
+ """)
507
+
508
+ # Main chat interface
509
+ chatbot_ui = gr.Chatbot(
510
+ value=[[None, "πŸ‘‹ **Hello! I'm your AI Assistant!**\n\nI can help you with:\n\nπŸ₯ **Healthcare Billing Codes** - I'm an expert in CPT, HCPCS, ICD-10, and DRG codes\nπŸ’¬ **General Conversation** - Ask me anything!\nπŸ“š **Learning & Education** - Help with various topics\n✍️ **Writing & Creation** - Stories, emails, ideas\nπŸ”§ **Problem Solving** - Let's work through challenges together\n\n**Try asking:**\nβ€’ 'What is billing code A0429?'\nβ€’ 'Help me write an email'\nβ€’ 'Explain quantum physics simply'\nβ€’ 'What's the weather like?'\n\nHow can I assist you today? 😊"]],
511
+ elem_id="chatbot",
512
+ show_label=False,
513
+ type="messages",
514
+ bubble_full_width=False,
515
+ height=500
516
+ )
517
 
518
+ # Input section
519
  with gr.Row():
520
  msg = gr.Textbox(
521
+ placeholder="Ask me anything... (e.g., 'Explain code 99213' or 'Help me write a story')",
522
+ show_label=False,
523
+ elem_id="input-box",
524
+ scale=5,
525
+ lines=1,
526
+ max_lines=5
527
  )
528
+ send_btn = gr.Button("Send", elem_classes="primary-btn", scale=1)
529
+
530
+ # Quick examples
531
+ gr.HTML("<div style='text-align: center; margin: 1rem 0; color: #6b7280; font-size: 14px;'>Quick Examples</div>")
532
 
533
  with gr.Row():
534
+ ex_col1 = gr.Column(scale=1)
535
+ ex_col2 = gr.Column(scale=1)
536
+ ex_col3 = gr.Column(scale=1)
537
+
538
+ with ex_col1:
539
+ gr.HTML("<div style='color: #667eea; font-weight: 600; font-size: 13px; margin-bottom: 8px;'>πŸ₯ Medical Billing</div>")
540
+ ex1 = gr.Button("What is code A0429?", elem_classes="example-chip", size="sm")
541
+ ex2 = gr.Button("Explain CPT 99213", elem_classes="example-chip", size="sm")
542
+ ex3 = gr.Button("DRG 470 details", elem_classes="example-chip", size="sm")
543
+
544
+ with ex_col2:
545
+ gr.HTML("<div style='color: #667eea; font-weight: 600; font-size: 13px; margin-bottom: 8px;'>πŸ’­ General Questions</div>")
546
+ ex4 = gr.Button("How does AI work?", elem_classes="example-chip", size="sm")
547
+ ex5 = gr.Button("Recipe for pasta", elem_classes="example-chip", size="sm")
548
+ ex6 = gr.Button("Python tutorial", elem_classes="example-chip", size="sm")
549
+
550
+ with ex_col3:
551
+ gr.HTML("<div style='color: #667eea; font-weight: 600; font-size: 13px; margin-bottom: 8px;'>✍️ Creative Help</div>")
552
+ ex7 = gr.Button("Write a poem", elem_classes="example-chip", size="sm")
553
+ ex8 = gr.Button("Email template", elem_classes="example-chip", size="sm")
554
+ ex9 = gr.Button("Story ideas", elem_classes="example-chip", size="sm")
555
+
556
+ # Control buttons
557
+ with gr.Row():
558
+ clear_btn = gr.Button("πŸ”„ New Chat", elem_classes="secondary-btn", size="sm")
559
+ gr.HTML("<div style='flex-grow: 1;'></div>")
560
+ gr.HTML("""
561
+ <div style='text-align: right; color: #6b7280; font-size: 12px;'>
562
+ Powered by GPT-3.5 β€’ Healthcare Billing Database
563
+ </div>
564
+ """)
565
+
566
+ # Footer info
567
+ gr.HTML("""
568
+ <div class="info-card" style="margin-top: 2rem;">
569
+ <div style="display: flex; justify-content: space-around; text-align: center;">
570
+ <div>
571
+ <div style="color: #667eea; font-size: 24px; font-weight: bold;">15+</div>
572
+ <div style="color: #6b7280; font-size: 12px;">Medical Codes</div>
573
+ </div>
574
+ <div>
575
+ <div style="color: #667eea; font-size: 24px; font-weight: bold;">∞</div>
576
+ <div style="color: #6b7280; font-size: 12px;">Topics</div>
577
+ </div>
578
+ <div>
579
+ <div style="color: #667eea; font-size: 24px; font-weight: bold;">24/7</div>
580
+ <div style="color: #6b7280; font-size: 12px;">Available</div>
581
+ </div>
582
+ <div>
583
+ <div style="color: #667eea; font-size: 24px; font-weight: bold;">Fast</div>
584
+ <div style="color: #6b7280; font-size: 12px;">Responses</div>
585
+ </div>
586
+ </div>
587
+ </div>
588
+ """)
589
 
590
  # Event handlers
591
+ def respond(message, chat_history):
592
+ if not message.strip():
593
+ return "", chat_history
594
+
595
+ # Process message
596
+ response = assistant.process_message(message)
597
+
598
+ # Update chat history
599
+ chat_history.append({"role": "user", "content": message})
600
+ chat_history.append({"role": "assistant", "content": response})
601
+
602
+ return "", chat_history
603
+
604
+ def clear_chat():
605
+ assistant.reset_context()
606
+ welcome = """πŸ‘‹ **Chat cleared! Ready for a new conversation.**
607
+
608
+ I'm here to help with anything you need - from healthcare billing codes to general questions!
609
+
610
+ What would you like to know? 😊"""
611
+ return [[None, welcome]]
612
+
613
+ # Connect events
614
+ msg.submit(respond, [msg, chatbot_ui], [msg, chatbot_ui])
615
+ send_btn.click(respond, [msg, chatbot_ui], [msg, chatbot_ui])
616
+ clear_btn.click(clear_chat, outputs=[chatbot_ui])
617
+
618
+ # Example button handlers
619
+ ex1.click(lambda: "What is healthcare billing code A0429?", outputs=msg)
620
+ ex2.click(lambda: "Can you explain CPT code 99213 in detail?", outputs=msg)
621
+ ex3.click(lambda: "Tell me about DRG 470", outputs=msg)
622
+ ex4.click(lambda: "How does artificial intelligence work?", outputs=msg)
623
+ ex5.click(lambda: "Give me a simple pasta recipe", outputs=msg)
624
+ ex6.click(lambda: "Teach me Python basics", outputs=msg)
625
+ ex7.click(lambda: "Write a short poem about nature", outputs=msg)
626
+ ex8.click(lambda: "Help me write a professional email template", outputs=msg)
627
+ ex9.click(lambda: "Give me creative story ideas", outputs=msg)
628
 
629
+ return app
630
 
631
+ # Launch
632
+ if __name__ == "__main__":
633
+ app = create_interface()
634
+ app.launch(
635
+ server_name="0.0.0.0",
636
+ server_port=7860,
637
+ share=False
638
+ )