yuvrajyadav commited on
Commit
7e5062a
·
verified ·
1 Parent(s): 36fdd5a

Delete app.py

Browse files
Files changed (1) hide show
  1. app.py +0 -643
app.py DELETED
@@ -1,643 +0,0 @@
1
- import gradio as gr
2
- import pandas as pd
3
- import requests
4
- import openai
5
- import json
6
- import time
7
- from typing import List, Dict, Tuple
8
- import re
9
- from urllib.parse import quote_plus
10
- from datetime import datetime, timedelta
11
- import os
12
-
13
- # Load environment variables for Hugging Face Spaces
14
- OPENAI_API_KEY = os.getenv("OAPI1", "")
15
- DEEPSEEK_API_KEY = os.getenv("DAPI", "")
16
- GOOGLE_CSE_KEYS = os.getenv("API1", "") # comma-separated
17
- GOOGLE_CSE_IDS = os.getenv("CX1", "") # comma-separated
18
-
19
- class CitationChecker:
20
- def __init__(self):
21
- self.openai_api_key = None
22
- self.deepseek_api_key = None
23
- self.google_cse_keys = []
24
- self.google_cse_ids = []
25
-
26
- def initialize_apis(self, openai_key, deepseek_key, google_keys_str, google_ids_str):
27
- """Initialize API keys - use provided keys or fall back to environment variables"""
28
-
29
- # Use provided keys if available, otherwise use environment variables
30
- self.openai_api_key = openai_key if openai_key.strip() else OPENAI_API_KEY
31
- self.deepseek_api_key = deepseek_key if deepseek_key.strip() else DEEPSEEK_API_KEY
32
-
33
- # Handle Google keys
34
- if google_keys_str.strip():
35
- self.google_cse_keys = [k.strip() for k in google_keys_str.split(',') if k.strip()]
36
- elif GOOGLE_CSE_KEYS:
37
- self.google_cse_keys = [k.strip() for k in GOOGLE_CSE_KEYS.split(',') if k.strip()]
38
- else:
39
- self.google_cse_keys = []
40
-
41
- # Handle Google CSE IDs
42
- if google_ids_str.strip():
43
- self.google_cse_ids = [i.strip() for i in google_ids_str.split(',') if i.strip()]
44
- elif GOOGLE_CSE_IDS:
45
- self.google_cse_ids = [i.strip() for i in GOOGLE_CSE_IDS.split(',') if i.strip()]
46
- else:
47
- self.google_cse_ids = []
48
-
49
- # Set OpenAI API key
50
- if self.openai_api_key:
51
- openai.api_key = self.openai_api_key
52
-
53
- # Validate that we have the required keys
54
- if not self.openai_api_key:
55
- raise ValueError("OpenAI API key is required")
56
- if not self.google_cse_keys or not self.google_cse_ids:
57
- raise ValueError("Google CSE keys and IDs are required")
58
- if len(self.google_cse_keys) != len(self.google_cse_ids):
59
- raise ValueError("Number of Google CSE keys must match number of CSE IDs")
60
-
61
- def parse_csv_data(self, df):
62
- """Parse CSV data and extract requirements"""
63
- requirements = []
64
-
65
- for idx, row in df.iterrows():
66
- if pd.notna(row.get('Requirement', '')) and str(row['Requirement']).strip():
67
- requirement_data = {
68
- 'id': row.get('ID', f'req_{idx}'),
69
- 'rubric': row.get('Rubric', 'Unknown'),
70
- 'weight': row.get('Weight', 1),
71
- 'requirement': str(row['Requirement']).strip(),
72
- 'evaluation_rules': str(row.get('Important Evaluation Rules', '')) if pd.notna(row.get('Important Evaluation Rules', '')) else '',
73
- 'original_prompt': row.get('Prompt', '')
74
- }
75
- requirements.append(requirement_data)
76
-
77
- return requirements
78
-
79
- def extract_key_claims(self, requirement_text, evaluation_rules):
80
- """Use OpenAI to extract key factual claims that need verification"""
81
-
82
- prompt = f"""
83
- Analyze the following requirement text and evaluation rules to extract specific factual claims that need verification:
84
-
85
- Requirement: {requirement_text}
86
-
87
- Evaluation Rules: {evaluation_rules}
88
-
89
- Extract specific, verifiable claims such as:
90
- - Market share percentages
91
- - Revenue figures
92
- - Subscriber counts
93
- - Growth rates
94
- - Company statistics
95
- - Technology capabilities
96
- - Market data
97
-
98
- Return a JSON list of claims, each with:
99
- - "claim": the specific factual statement
100
- - "type": category (market_share, revenue, subscribers, growth, technology, etc.)
101
- - "search_terms": suggested search terms for verification
102
- - "priority": high/medium/low based on importance
103
-
104
- Focus only on concrete, verifiable facts, not opinions or projections.
105
- """
106
-
107
- try:
108
- response = openai.ChatCompletion.create(
109
- model="gpt-4",
110
- messages=[
111
- {"role": "system", "content": "You are a fact-checking analyst specializing in business and technology claims. Extract only verifiable factual statements."},
112
- {"role": "user", "content": prompt}
113
- ],
114
- max_tokens=1000,
115
- temperature=0.1
116
- )
117
-
118
- content = response.choices[0].message.content
119
-
120
- # Try to parse JSON from the response
121
- try:
122
- # Look for JSON in the response
123
- json_match = re.search(r'\[.*\]', content, re.DOTALL)
124
- if json_match:
125
- claims = json.loads(json_match.group())
126
- return claims
127
- else:
128
- # Fallback parsing
129
- return []
130
- except json.JSONDecodeError:
131
- return []
132
-
133
- except Exception as e:
134
- print(f"Error extracting claims: {str(e)}")
135
- return []
136
-
137
- def search_google_cse(self, query, cse_key, cse_id, num_results=10):
138
- """Search using Google Custom Search Engine"""
139
-
140
- try:
141
- url = "https://www.googleapis.com/customsearch/v1"
142
- params = {
143
- 'key': cse_key,
144
- 'cx': cse_id,
145
- 'q': query,
146
- 'num': num_results,
147
- 'dateRestrict': 'y2', # Last 2 years
148
- 'sort': 'date'
149
- }
150
-
151
- response = requests.get(url, params=params, timeout=10)
152
-
153
- if response.status_code == 200:
154
- data = response.json()
155
- results = []
156
-
157
- if 'items' in data:
158
- for item in data['items']:
159
- results.append({
160
- 'title': item.get('title', ''),
161
- 'link': item.get('link', ''),
162
- 'snippet': item.get('snippet', ''),
163
- 'displayLink': item.get('displayLink', ''),
164
- 'formattedUrl': item.get('formattedUrl', ''),
165
- 'publishDate': item.get('pagemap', {}).get('metatags', [{}])[0].get('article:published_time', 'Unknown')
166
- })
167
-
168
- return results
169
- else:
170
- print(f"Google CSE API error: {response.status_code}")
171
- return []
172
-
173
- except Exception as e:
174
- print(f"Search error: {str(e)}")
175
- return []
176
-
177
- def verify_claim_with_sources(self, claim, search_results):
178
- """Use OpenAI to verify claim against search results"""
179
-
180
- # Prepare search results text
181
- results_text = ""
182
- for i, result in enumerate(search_results[:5], 1):
183
- results_text += f"\nSource {i}:\nTitle: {result['title']}\nURL: {result['link']}\nContent: {result['snippet']}\nPublisher: {result['displayLink']}\nDate: {result['publishDate']}\n"
184
-
185
- prompt = f"""
186
- Verify the following claim against the provided search results:
187
-
188
- CLAIM TO VERIFY: {claim['claim']}
189
-
190
- SEARCH RESULTS:
191
- {results_text}
192
-
193
- Analyze whether the claim is:
194
- 1. VERIFIED - supported by the sources
195
- 2. CONTRADICTED - contradicted by the sources
196
- 3. PARTIALLY_VERIFIED - partially supported
197
- 4. INSUFFICIENT_DATA - not enough information
198
-
199
- Provide your analysis in JSON format:
200
- {{
201
- "verification_status": "VERIFIED|CONTRADICTED|PARTIALLY_VERIFIED|INSUFFICIENT_DATA",
202
- "confidence_score": 0.0-1.0,
203
- "supporting_sources": [list of source numbers that support the claim],
204
- "contradicting_sources": [list of source numbers that contradict],
205
- "summary": "brief explanation of findings",
206
- "specific_data_found": "any specific numbers or facts found",
207
- "source_reliability": "assessment of source quality",
208
- "recommendation": "what action to take"
209
- }}
210
- """
211
-
212
- try:
213
- response = openai.ChatCompletion.create(
214
- model="gpt-4",
215
- messages=[
216
- {"role": "system", "content": "You are a fact-checking expert. Analyze claims against sources objectively and provide detailed verification analysis."},
217
- {"role": "user", "content": prompt}
218
- ],
219
- max_tokens=800,
220
- temperature=0.1
221
- )
222
-
223
- content = response.choices[0].message.content
224
-
225
- # Parse JSON response
226
- try:
227
- json_match = re.search(r'\{.*\}', content, re.DOTALL)
228
- if json_match:
229
- verification = json.loads(json_match.group())
230
- return verification
231
- else:
232
- return {
233
- "verification_status": "INSUFFICIENT_DATA",
234
- "confidence_score": 0.0,
235
- "supporting_sources": [],
236
- "contradicting_sources": [],
237
- "summary": "Could not parse verification results",
238
- "specific_data_found": "",
239
- "source_reliability": "Unknown",
240
- "recommendation": "Manual review required"
241
- }
242
- except json.JSONDecodeError:
243
- return {
244
- "verification_status": "INSUFFICIENT_DATA",
245
- "confidence_score": 0.0,
246
- "supporting_sources": [],
247
- "contradicting_sources": [],
248
- "summary": content[:200],
249
- "specific_data_found": "",
250
- "source_reliability": "Unknown",
251
- "recommendation": "Manual review required"
252
- }
253
-
254
- except Exception as e:
255
- print(f"Verification error: {str(e)}")
256
- return {
257
- "verification_status": "ERROR",
258
- "confidence_score": 0.0,
259
- "supporting_sources": [],
260
- "contradicting_sources": [],
261
- "summary": f"Error during verification: {str(e)}",
262
- "specific_data_found": "",
263
- "source_reliability": "Unknown",
264
- "recommendation": "Manual review required"
265
- }
266
-
267
- def process_requirement(self, requirement_data, progress=gr.Progress()):
268
- """Process a single requirement - extract claims, search, and verify"""
269
-
270
- results = {
271
- 'requirement_id': requirement_data['id'],
272
- 'rubric': requirement_data['rubric'],
273
- 'weight': requirement_data['weight'],
274
- 'requirement_text': requirement_data['requirement'],
275
- 'evaluation_rules': requirement_data['evaluation_rules'],
276
- 'claims': [],
277
- 'overall_assessment': '',
278
- 'sources_found': [],
279
- 'timestamp': datetime.now().isoformat()
280
- }
281
-
282
- progress(0.1, desc=f"Extracting claims for {requirement_data['id']}...")
283
-
284
- # Extract key claims
285
- claims = self.extract_key_claims(requirement_data['requirement'], requirement_data['evaluation_rules'])
286
-
287
- for i, claim in enumerate(claims):
288
- progress(0.1 + (0.8 * i / len(claims)), desc=f"Verifying claim {i+1}/{len(claims)} for {requirement_data['id']}...")
289
-
290
- # Search for each claim using multiple CSE instances
291
- all_search_results = []
292
-
293
- for j, (cse_key, cse_id) in enumerate(zip(self.google_cse_keys, self.google_cse_ids)):
294
- search_query = " ".join(claim.get('search_terms', [claim['claim']]))
295
- search_results = self.search_google_cse(search_query, cse_key, cse_id)
296
- all_search_results.extend(search_results)
297
-
298
- # Add delay between API calls
299
- time.sleep(0.5)
300
-
301
- # Remove duplicates
302
- seen_urls = set()
303
- unique_results = []
304
- for result in all_search_results:
305
- if result['link'] not in seen_urls:
306
- seen_urls.add(result['link'])
307
- unique_results.append(result)
308
-
309
- # Verify claim against search results
310
- verification = self.verify_claim_with_sources(claim, unique_results)
311
-
312
- claim_result = {
313
- 'claim': claim,
314
- 'search_results': unique_results[:10], # Keep top 10 results
315
- 'verification': verification
316
- }
317
-
318
- results['claims'].append(claim_result)
319
- results['sources_found'].extend(unique_results[:5]) # Add top sources to overall list
320
-
321
- progress(0.9, desc="Generating assessment...")
322
-
323
- # Generate overall assessment
324
- results['overall_assessment'] = self.generate_overall_assessment(results)
325
-
326
- return results
327
-
328
- def generate_overall_assessment(self, results):
329
- """Generate overall assessment for a requirement"""
330
-
331
- verified_claims = [c for c in results['claims'] if c['verification']['verification_status'] == 'VERIFIED']
332
- contradicted_claims = [c for c in results['claims'] if c['verification']['verification_status'] == 'CONTRADICTED']
333
-
334
- total_claims = len(results['claims'])
335
- verified_count = len(verified_claims)
336
- contradicted_count = len(contradicted_claims)
337
-
338
- if total_claims == 0:
339
- return "No specific claims found for verification"
340
-
341
- verification_rate = verified_count / total_claims
342
-
343
- if verification_rate >= 0.8:
344
- status = "WELL_SUPPORTED"
345
- elif verification_rate >= 0.6:
346
- status = "MODERATELY_SUPPORTED"
347
- elif contradicted_count > verified_count:
348
- status = "CONTRADICTED"
349
- else:
350
- status = "INSUFFICIENT_EVIDENCE"
351
-
352
- return f"{status}: {verified_count}/{total_claims} claims verified, {contradicted_count} contradicted"
353
-
354
- def generate_final_report(self, all_results):
355
- """Generate comprehensive final report with conclusions"""
356
-
357
- prompt = f"""
358
- Generate a comprehensive citation and verification report based on the following analysis results:
359
-
360
- {json.dumps(all_results, indent=2, default=str)}
361
-
362
- Create a structured report with:
363
-
364
- 1. EXECUTIVE SUMMARY
365
- - Overall verification status
366
- - Key findings
367
- - Confidence level in the analysis
368
-
369
- 2. DETAILED FINDINGS BY CATEGORY
370
- - Domain Knowledge and Factual Accuracy
371
- - Analysis Quality
372
- - Evidence and Sources
373
- - Writing Quality
374
- - Instruction Following
375
- - Comprehensiveness
376
-
377
- 3. SPECIFIC CITATION ISSUES
378
- - Verified claims with strong sources
379
- - Contradicted claims requiring correction
380
- - Claims needing additional verification
381
-
382
- 4. SOURCE QUALITY ASSESSMENT
383
- - Credible sources identified
384
- - Source reliability ratings
385
- - Publication dates and currency
386
-
387
- 5. RECOMMENDATIONS
388
- - Priority corrections needed
389
- - Additional research required
390
- - Overall reliability assessment
391
-
392
- 6. CONCLUSIONS
393
- - Final assessment of citation quality
394
- - Confidence in the overall analysis
395
- - Next steps recommended
396
-
397
- Format as a professional report with clear sections and actionable insights.
398
- """
399
-
400
- try:
401
- response = openai.ChatCompletion.create(
402
- model="gpt-4",
403
- messages=[
404
- {"role": "system", "content": "You are a research analyst generating professional verification reports. Be thorough, objective, and actionable."},
405
- {"role": "user", "content": prompt}
406
- ],
407
- max_tokens=2000,
408
- temperature=0.2
409
- )
410
-
411
- return response.choices[0].message.content
412
-
413
- except Exception as e:
414
- return f"Error generating final report: {str(e)}"
415
-
416
- # Initialize the citation checker
417
- checker = CitationChecker()
418
-
419
- def process_csv_and_verify(csv_file, openai_key, deepseek_key, google_keys, google_ids, progress=gr.Progress()):
420
- """Main function to process CSV and run verification"""
421
-
422
- if not csv_file:
423
- return "❌ Please upload a CSV file", "", ""
424
-
425
- try:
426
- # Initialize APIs (will use environment variables if inputs are empty)
427
- checker.initialize_apis(openai_key, deepseek_key, google_keys, google_ids)
428
-
429
- except ValueError as e:
430
- return f"❌ API Configuration Error: {str(e)}", "", ""
431
- except Exception as e:
432
- return f"❌ Error initializing APIs: {str(e)}", "", ""
433
-
434
- # Read CSV file
435
- df = pd.read_csv(csv_file.name)
436
- progress(0.05, desc="Parsing CSV data...")
437
-
438
- # Parse requirements
439
- requirements = checker.parse_csv_data(df)
440
-
441
- if not requirements:
442
- return "❌ No valid requirements found in CSV", "", ""
443
-
444
- progress(0.1, desc=f"Found {len(requirements)} requirements to verify...")
445
-
446
- all_results = []
447
- detailed_output = f"# 📊 Verification Results\n\n**Total Requirements:** {len(requirements)}\n\n"
448
-
449
- # Process each requirement
450
- for i, requirement in enumerate(requirements):
451
- progress(0.1 + (0.7 * i / len(requirements)), desc=f"Processing requirement {i+1}/{len(requirements)}: {requirement['id']}")
452
-
453
- result = checker.process_requirement(requirement, progress)
454
- all_results.append(result)
455
-
456
- # Add to detailed output
457
- detailed_output += f"## 📋 {result['requirement_id']} - {result['rubric']}\n\n"
458
- detailed_output += f"**Weight:** {result['weight']}\n\n"
459
- detailed_output += f"**Requirement:** {result['requirement_text']}\n\n"
460
- detailed_output += f"**Overall Assessment:** {result['overall_assessment']}\n\n"
461
-
462
- if result['evaluation_rules']:
463
- detailed_output += f"**Evaluation Rules:** {result['evaluation_rules']}\n\n"
464
-
465
- detailed_output += "### 🔍 Claims Analysis\n\n"
466
-
467
- for j, claim_result in enumerate(result['claims'], 1):
468
- claim = claim_result['claim']
469
- verification = claim_result['verification']
470
-
471
- status_icons = {
472
- 'VERIFIED': '✅',
473
- 'CONTRADICTED': '❌',
474
- 'PARTIALLY_VERIFIED': '⚠️',
475
- 'INSUFFICIENT_DATA': '❓',
476
- 'ERROR': '❌'
477
- }
478
-
479
- status_icon = status_icons.get(verification['verification_status'], '❓')
480
-
481
- detailed_output += f"**{status_icon} Claim {j}:** {claim['claim']}\n\n"
482
- detailed_output += f"**Status:** {verification['verification_status']} (Confidence: {verification['confidence_score']:.2f})\n\n"
483
- detailed_output += f"**Summary:** {verification['summary']}\n\n"
484
-
485
- if verification['specific_data_found']:
486
- detailed_output += f"**Data Found:** {verification['specific_data_found']}\n\n"
487
-
488
- if verification['recommendation']:
489
- detailed_output += f"**Recommendation:** {verification['recommendation']}\n\n"
490
-
491
- # Show top sources
492
- if claim_result['search_results'][:3]:
493
- detailed_output += "**📚 Top Sources:**\n\n"
494
- for k, source in enumerate(claim_result['search_results'][:3], 1):
495
- detailed_output += f"{k}. [{source['title']}]({source['link']}) - {source['displayLink']}\n\n"
496
-
497
- detailed_output += "---\n\n"
498
-
499
- progress(0.9, desc="Generating final report...")
500
-
501
- # Generate final report
502
- final_report = checker.generate_final_report(all_results)
503
-
504
- # Generate summary
505
- verified_requirements = len([r for r in all_results if 'WELL_SUPPORTED' in r['overall_assessment']])
506
- contradicted_requirements = len([r for r in all_results if 'CONTRADICTED' in r['overall_assessment']])
507
- total_claims = sum(len(r['claims']) for r in all_results)
508
-
509
- summary = f"""# ✅ Processing Complete!
510
-
511
- **📊 Summary Metrics:**
512
- - Total Requirements: {len(requirements)}
513
- - Well Supported: {verified_requirements}
514
- - Contradicted: {contradicted_requirements}
515
- - Total Claims Checked: {total_claims}
516
-
517
- **⏰ Processed at:** {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}
518
-
519
- Use the tabs below to view detailed results and the comprehensive final report.
520
- """
521
-
522
- progress(1.0, desc="Complete!")
523
-
524
- return summary, detailed_output, final_report
525
-
526
- except Exception as e:
527
- return f"❌ Error processing file: {str(e)}", "", ""
528
-
529
- def create_interface():
530
- """Create the Gradio interface"""
531
-
532
- with gr.Blocks(title="🔍 Citation Checker & Verification System", theme=gr.themes.Soft()) as demo:
533
-
534
- gr.Markdown("""
535
- # 🔍 Citation Checker & Verification System
536
- ### Verify requirements against internet sources with comprehensive citation analysis
537
-
538
- Upload your CSV file with requirements and provide API credentials to start the verification process.
539
- """)
540
-
541
- with gr.Row():
542
- with gr.Column(scale=1):
543
- gr.Markdown("## 📁 File Upload")
544
- csv_file = gr.File(
545
- label="Upload CSV File",
546
- file_types=[".csv"],
547
- type="filepath"
548
- )
549
-
550
- gr.Markdown("""
551
- **CSV Requirements:**
552
- - Must contain columns: `Requirement`, `ID`, `Rubric`, `Weight`
553
- - Optional: `Important Evaluation Rules`, `Prompt`
554
- """)
555
-
556
- with gr.Column(scale=1):
557
- gr.Markdown("## 🔑 API Configuration")
558
-
559
- # Show status of environment variables without exposing them
560
- env_status = []
561
- if OPENAI_API_KEY:
562
- env_status.append("✅ OpenAI API Key (from environment)")
563
- if DEEPSEEK_API_KEY:
564
- env_status.append("✅ DeepSeek API Key (from environment)")
565
- if GOOGLE_CSE_KEYS:
566
- env_status.append("✅ Google CSE Keys (from environment)")
567
- if GOOGLE_CSE_IDS:
568
- env_status.append("✅ Google CSE IDs (from environment)")
569
-
570
- if env_status:
571
- gr.Markdown("**🔐 Environment Variables Detected:**\n" + "\n".join(env_status) + "\n\n*Leave fields below empty to use environment variables*")
572
- else:
573
- gr.Markdown("**⚠️ No environment variables detected - please enter API keys manually**")
574
-
575
- openai_key = gr.Textbox(
576
- label="OpenAI API Key",
577
- type="password",
578
- placeholder="sk-... (leave empty to use environment variable)",
579
- value="" # NEVER pre-fill with actual keys!
580
- )
581
-
582
- deepseek_key = gr.Textbox(
583
- label="DeepSeek API Key (Optional)",
584
- type="password",
585
- placeholder="sk-... (leave empty to use environment variable)",
586
- value="" # NEVER pre-fill with actual keys!
587
- )
588
-
589
- google_keys = gr.Textbox(
590
- label="Google CSE API Keys",
591
- placeholder="key1,key2,key3 (comma-separated, leave empty to use env var)",
592
- info="Multiple keys for better rate limiting",
593
- value="" # NEVER pre-fill with actual keys!
594
- )
595
-
596
- google_ids = gr.Textbox(
597
- label="Google CSE IDs",
598
- placeholder="cx1,cx2,cx3 (comma-separated, leave empty to use env var)",
599
- info="Corresponding CSE IDs for each API key",
600
- value="" # NEVER pre-fill with actual keys!
601
- )
602
-
603
- with gr.Row():
604
- process_btn = gr.Button(
605
- "🚀 Start Verification Process",
606
- variant="primary",
607
- size="lg"
608
- )
609
-
610
- with gr.Row():
611
- summary_output = gr.Markdown(label="Summary")
612
-
613
- with gr.Tabs():
614
- with gr.TabItem("📊 Detailed Results"):
615
- detailed_output = gr.Markdown()
616
-
617
- with gr.TabItem("📄 Final Report"):
618
- final_report = gr.Markdown()
619
-
620
- # Set up the processing function
621
- process_btn.click(
622
- fn=process_csv_and_verify,
623
- inputs=[csv_file, openai_key, deepseek_key, google_keys, google_ids],
624
- outputs=[summary_output, detailed_output, final_report],
625
- show_progress=True
626
- )
627
-
628
- gr.Markdown("""
629
- ---
630
- **Citation Checker & Verification System** - Powered by OpenAI GPT-4, DeepSeek, and Google Custom Search
631
-
632
- **🔐 For Hugging Face Deployment:**
633
- - Set environment variables: `OPENAI_API_KEY`, `GOOGLE_CSE_KEYS`, `GOOGLE_CSE_IDS`
634
- - Or enter API keys manually in the form above
635
- - Environment variables will be used automatically if form fields are left empty
636
- """)
637
-
638
- return demo
639
-
640
- # Create and launch the interface
641
- if __name__ == "__main__":
642
- demo = create_interface()
643
- demo.launch()