Spaces:
Sleeping
Sleeping
Update app.py
Browse files
app.py
CHANGED
|
@@ -382,7 +382,256 @@ class GradioInterface:
|
|
| 382 |
print("🌐 Initializing Enhanced Gradio interface...")
|
| 383 |
# Initialize AI config and pipeline properly
|
| 384 |
self.ai_config = AIConfig(safe_mode=True)
|
| 385 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 386 |
|
| 387 |
def create_interface(self):
|
| 388 |
"""Create the main Gradio interface with API integration"""
|
|
@@ -392,7 +641,7 @@ class GradioInterface:
|
|
| 392 |
theme=gr.themes.Soft()
|
| 393 |
) as interface:
|
| 394 |
|
| 395 |
-
# Main Header with API info
|
| 396 |
header_html = """
|
| 397 |
<div style="text-align: center; background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); color: white; padding: 2.5rem; border-radius: 20px; margin-bottom: 2rem;">
|
| 398 |
<h1 style="margin: 0; font-size: 2.5em;">🧠 InclusiveEdu</h1>
|
|
@@ -408,82 +657,6 @@ class GradioInterface:
|
|
| 408 |
|
| 409 |
gr.HTML(header_html)
|
| 410 |
|
| 411 |
-
# Main content sections
|
| 412 |
-
with gr.Row():
|
| 413 |
-
with gr.Column(scale=1):
|
| 414 |
-
gr.Markdown("## 📝 Content Input & Settings")
|
| 415 |
-
|
| 416 |
-
content_input = gr.Textbox(
|
| 417 |
-
label="Educational Content",
|
| 418 |
-
placeholder="Enter the educational content you want to adapt...",
|
| 419 |
-
lines=8,
|
| 420 |
-
max_lines=12
|
| 421 |
-
)
|
| 422 |
-
|
| 423 |
-
with gr.Row():
|
| 424 |
-
profile_dropdown = gr.Dropdown(
|
| 425 |
-
label="🎯 Neurodiverse Learning Profile",
|
| 426 |
-
choices=[
|
| 427 |
-
"🎯 Visual Structure",
|
| 428 |
-
"🔬 Directed Hyperfocus",
|
| 429 |
-
"🌸 Sensory Adaptation",
|
| 430 |
-
"🎮 Special Interests"
|
| 431 |
-
],
|
| 432 |
-
value="🎯 Visual Structure"
|
| 433 |
-
)
|
| 434 |
-
|
| 435 |
-
complexity_dropdown = gr.Dropdown(
|
| 436 |
-
label="📊 Content Complexity",
|
| 437 |
-
choices=[
|
| 438 |
-
"beginner",
|
| 439 |
-
"intermediate",
|
| 440 |
-
"advanced"
|
| 441 |
-
],
|
| 442 |
-
value="intermediate"
|
| 443 |
-
)
|
| 444 |
-
|
| 445 |
-
interests_input = gr.Textbox(
|
| 446 |
-
label="🎯 Personal Interests (comma-separated)",
|
| 447 |
-
placeholder="technology, science, programming, art...",
|
| 448 |
-
value="technology, learning, science"
|
| 449 |
-
)
|
| 450 |
-
|
| 451 |
-
adapt_button = gr.Button(
|
| 452 |
-
"🚀 Adapt Content",
|
| 453 |
-
variant="primary"
|
| 454 |
-
)
|
| 455 |
-
|
| 456 |
-
with gr.Column(scale=1):
|
| 457 |
-
gr.Markdown("## ✨ Adapted Learning Content")
|
| 458 |
-
|
| 459 |
-
adapted_output = gr.HTML(
|
| 460 |
-
label="Neurodiverse Adapted Content"
|
| 461 |
-
)
|
| 462 |
-
|
| 463 |
-
# Status outputs
|
| 464 |
-
with gr.Row():
|
| 465 |
-
gamification_output = gr.Textbox(
|
| 466 |
-
label="🎮 Gamification & Progress",
|
| 467 |
-
lines=2,
|
| 468 |
-
interactive=False
|
| 469 |
-
)
|
| 470 |
-
|
| 471 |
-
processing_output = gr.Textbox(
|
| 472 |
-
label="⚡ Processing Information",
|
| 473 |
-
lines=2,
|
| 474 |
-
interactive=False
|
| 475 |
-
)
|
| 476 |
-
|
| 477 |
-
stats_output = gr.Textbox(
|
| 478 |
-
label="📊 Session Statistics",
|
| 479 |
-
lines=2,
|
| 480 |
-
interactive=False
|
| 481 |
-
)
|
| 482 |
-
|
| 483 |
-
# Additional sections would go here...
|
| 484 |
-
|
| 485 |
-
return interface
|
| 486 |
-
|
| 487 |
# Main Interface Layout
|
| 488 |
with gr.Row():
|
| 489 |
# Input Section
|
|
@@ -662,7 +835,7 @@ class GradioInterface:
|
|
| 662 |
)
|
| 663 |
|
| 664 |
# Enhanced Footer with API Information
|
| 665 |
-
|
| 666 |
<div style="margin-top: 50px; padding: 30px; background: #f8f9fa; border-radius: 20px; text-align: center;">
|
| 667 |
|
| 668 |
<h3 style="color: #495057; margin-top: 0;">🧠 InclusiveEdu - Empowering Every Learner</h3>
|
|
@@ -721,7 +894,9 @@ class GradioInterface:
|
|
| 721 |
Optimized for cloud deployment
|
| 722 |
</small>
|
| 723 |
</div>
|
| 724 |
-
"""
|
|
|
|
|
|
|
| 725 |
|
| 726 |
return interface
|
| 727 |
|
|
|
|
| 382 |
print("🌐 Initializing Enhanced Gradio interface...")
|
| 383 |
# Initialize AI config and pipeline properly
|
| 384 |
self.ai_config = AIConfig(safe_mode=True)
|
| 385 |
+
self.pipeline = ContentAdaptationPipeline(self.ai_config)
|
| 386 |
+
self.session_stats = {
|
| 387 |
+
"adaptations": 0,
|
| 388 |
+
"start_time": datetime.now(),
|
| 389 |
+
"profiles_used": {},
|
| 390 |
+
"total_processing_time": 0.0
|
| 391 |
+
}
|
| 392 |
+
|
| 393 |
+
def adapt_content_interface(self, content, profile_name, interests_text, complexity):
|
| 394 |
+
"""Main interface function for content adaptation"""
|
| 395 |
+
|
| 396 |
+
if not content or not content.strip():
|
| 397 |
+
return (
|
| 398 |
+
"<div style='padding: 20px; background: #fff3cd; border-radius: 10px;'>"
|
| 399 |
+
"<h4>⚠️ Input Required</h4>"
|
| 400 |
+
"<p>Please enter some educational content to adapt for different learning styles.</p>"
|
| 401 |
+
"</div>",
|
| 402 |
+
"", "", ""
|
| 403 |
+
)
|
| 404 |
+
|
| 405 |
+
try:
|
| 406 |
+
# Convert profile name to key
|
| 407 |
+
profile_map = {
|
| 408 |
+
"🎯 Visual Structure": "visual_structure",
|
| 409 |
+
"🔬 Directed Hyperfocus": "hyperfocus_directed",
|
| 410 |
+
"🌸 Sensory Adaptation": "sensory_adaptation",
|
| 411 |
+
"🎮 Special Interests": "special_interests"
|
| 412 |
+
}
|
| 413 |
+
|
| 414 |
+
profile_key = profile_map.get(profile_name, "visual_structure")
|
| 415 |
+
interests = [i.strip() for i in interests_text.split(',') if i.strip()]
|
| 416 |
+
|
| 417 |
+
# Perform adaptation
|
| 418 |
+
result = self.pipeline.adapt_content(
|
| 419 |
+
content=content.strip(),
|
| 420 |
+
profile_key=profile_key,
|
| 421 |
+
interests=interests,
|
| 422 |
+
complexity=complexity
|
| 423 |
+
)
|
| 424 |
+
|
| 425 |
+
# Update session stats
|
| 426 |
+
self._update_session_stats(result)
|
| 427 |
+
|
| 428 |
+
# Format outputs for interface
|
| 429 |
+
adapted_html = result['adapted_content']
|
| 430 |
+
|
| 431 |
+
gamification_info = self._format_gamification_output(result['gamification'])
|
| 432 |
+
processing_info = self._format_processing_output(result)
|
| 433 |
+
stats_info = self._format_stats_output()
|
| 434 |
+
|
| 435 |
+
return adapted_html, gamification_info, processing_info, stats_info
|
| 436 |
+
|
| 437 |
+
except Exception as e:
|
| 438 |
+
error_msg = f"❌ Adaptation error: {str(e)}"
|
| 439 |
+
print(f"Interface error: {e}")
|
| 440 |
+
|
| 441 |
+
return (
|
| 442 |
+
f"<div style='padding: 20px; background: #f8d7da; border-radius: 10px;'>"
|
| 443 |
+
f"<h4>❌ Processing Error</h4>"
|
| 444 |
+
f"<p>An error occurred during content adaptation: {str(e)}</p>"
|
| 445 |
+
f"<p><em>Please try again with different content or settings.</em></p>"
|
| 446 |
+
f"</div>",
|
| 447 |
+
"", "", ""
|
| 448 |
+
)
|
| 449 |
+
|
| 450 |
+
def _update_session_stats(self, result):
|
| 451 |
+
"""Update session statistics"""
|
| 452 |
+
self.session_stats["adaptations"] += 1
|
| 453 |
+
|
| 454 |
+
profile_used = result.get("profile_used", "unknown")
|
| 455 |
+
if profile_used in self.session_stats["profiles_used"]:
|
| 456 |
+
self.session_stats["profiles_used"][profile_used] += 1
|
| 457 |
+
else:
|
| 458 |
+
self.session_stats["profiles_used"][profile_used] = 1
|
| 459 |
+
|
| 460 |
+
self.session_stats["total_processing_time"] += result.get("processing_time", 0)
|
| 461 |
+
|
| 462 |
+
def _format_gamification_output(self, gamification):
|
| 463 |
+
"""Format gamification information for display"""
|
| 464 |
+
|
| 465 |
+
level = gamification.get("current_level", 1)
|
| 466 |
+
xp = gamification.get("xp_points", 0)
|
| 467 |
+
progress = gamification.get("progress_percentage", 0)
|
| 468 |
+
achievements = len(gamification.get("achievements", []))
|
| 469 |
+
|
| 470 |
+
return (
|
| 471 |
+
f"🎮 Level {level} | ⭐ {xp:,} XP | 📈 {progress}% to next level | "
|
| 472 |
+
f"🏆 {achievements} achievements unlocked"
|
| 473 |
+
)
|
| 474 |
+
|
| 475 |
+
def _format_processing_output(self, result):
|
| 476 |
+
"""Format processing information for display"""
|
| 477 |
+
|
| 478 |
+
processing_time = result.get("processing_time", 0)
|
| 479 |
+
ai_status = "🧠 AI Model" if result.get("gemma3_used", False) else "🎭 Enhanced Simulation"
|
| 480 |
+
profile = result.get("profile_used", "unknown").replace("_", " ").title()
|
| 481 |
+
success = "✅ Success" if result.get("success", False) else "⚠️ Fallback"
|
| 482 |
+
|
| 483 |
+
return (
|
| 484 |
+
f"⚡ {processing_time:.2f}s processing | {ai_status} | "
|
| 485 |
+
f"🎯 {profile} profile | {success}"
|
| 486 |
+
)
|
| 487 |
+
|
| 488 |
+
def _format_stats_output(self):
|
| 489 |
+
"""Format session statistics for display"""
|
| 490 |
+
|
| 491 |
+
total_adaptations = self.session_stats["adaptations"]
|
| 492 |
+
session_time = (datetime.now() - self.session_stats["start_time"]).total_seconds() / 60
|
| 493 |
+
avg_processing = (
|
| 494 |
+
self.session_stats["total_processing_time"] / total_adaptations
|
| 495 |
+
if total_adaptations > 0 else 0
|
| 496 |
+
)
|
| 497 |
+
|
| 498 |
+
most_used_profile = "None"
|
| 499 |
+
if self.session_stats["profiles_used"]:
|
| 500 |
+
most_used_profile = max(
|
| 501 |
+
self.session_stats["profiles_used"].items(),
|
| 502 |
+
key=lambda x: x[1]
|
| 503 |
+
)[0].replace("_", " ").title()
|
| 504 |
+
|
| 505 |
+
return (
|
| 506 |
+
f"📊 Session: {total_adaptations} adaptations | "
|
| 507 |
+
f"⏱️ {session_time:.1f}min active | "
|
| 508 |
+
f"⚡ {avg_processing:.2f}s avg | "
|
| 509 |
+
f"🎯 Most used: {most_used_profile}"
|
| 510 |
+
)
|
| 511 |
+
|
| 512 |
+
def get_system_status(self):
|
| 513 |
+
"""Get current system status with API info"""
|
| 514 |
+
|
| 515 |
+
model_status = "🧠 AI Model Active" if not self.ai_config.simulation_mode else "🎭 Simulation Mode"
|
| 516 |
+
device_info = "🚀 GPU" if torch.cuda.is_available() else "💻 CPU"
|
| 517 |
+
|
| 518 |
+
# Determine base URL for API
|
| 519 |
+
base_url = "http://localhost:8000" if "SPACE_ID" not in os.environ else "https://your-space.hf.space"
|
| 520 |
+
|
| 521 |
+
return f"""
|
| 522 |
+
## 🔧 System Status
|
| 523 |
+
**AI Engine:** {model_status}
|
| 524 |
+
**Device:** {device_info}
|
| 525 |
+
**Profiles:** 4 neurodiverse learning profiles available
|
| 526 |
+
**Features:** Content adaptation, gamification, analytics
|
| 527 |
+
**Session:** {self.session_stats['adaptations']} adaptations completed
|
| 528 |
+
|
| 529 |
+
## 🔌 API Integration
|
| 530 |
+
**API Status:** ✅ Active and ready
|
| 531 |
+
**Base URL:** `{base_url}`
|
| 532 |
+
**Documentation:** `{base_url}/docs`
|
| 533 |
+
**Health Check:** `{base_url}/health`
|
| 534 |
+
|
| 535 |
+
**Available Endpoints:**
|
| 536 |
+
- `POST /adapt` - Content adaptation
|
| 537 |
+
- `GET /profiles` - List profiles
|
| 538 |
+
- `GET /examples` - Usage examples
|
| 539 |
+
- `GET /health` - Health check
|
| 540 |
+
|
| 541 |
+
## 🎯 Profile System
|
| 542 |
+
- 🎯 **Visual Structure:** Clear organization and hierarchy
|
| 543 |
+
- 🔬 **Directed Hyperfocus:** Technical depth and detail
|
| 544 |
+
- 🌸 **Sensory Adaptation:** Calm and accessible design
|
| 545 |
+
- 🎮 **Special Interests:** Gamification and motivation
|
| 546 |
+
|
| 547 |
+
## ✨ Capabilities
|
| 548 |
+
✅ Real-time content adaptation
|
| 549 |
+
✅ Multiple complexity levels
|
| 550 |
+
✅ Interest-based personalization
|
| 551 |
+
✅ Accessibility features
|
| 552 |
+
✅ Progress tracking and gamification
|
| 553 |
+
✅ **REST API for external access**
|
| 554 |
+
✅ **Gradio web interface**
|
| 555 |
+
"""
|
| 556 |
+
|
| 557 |
+
def get_api_examples_display(self):
|
| 558 |
+
"""Get API examples for display in Gradio"""
|
| 559 |
+
|
| 560 |
+
base_url = "http://localhost:8000" if "SPACE_ID" not in os.environ else "https://your-space.hf.space"
|
| 561 |
+
|
| 562 |
+
return f"""
|
| 563 |
+
## 🔌 API Usage Examples
|
| 564 |
+
|
| 565 |
+
### Python Example
|
| 566 |
+
```python
|
| 567 |
+
import requests
|
| 568 |
+
|
| 569 |
+
url = "{base_url}/adapt"
|
| 570 |
+
data = {{
|
| 571 |
+
"content": "Your educational content here...",
|
| 572 |
+
"profile": "visual_structure",
|
| 573 |
+
"interests": ["technology", "programming"],
|
| 574 |
+
"complexity": "intermediate",
|
| 575 |
+
"format": "html"
|
| 576 |
+
}}
|
| 577 |
+
|
| 578 |
+
response = requests.post(url, json=data)
|
| 579 |
+
result = response.json()
|
| 580 |
+
|
| 581 |
+
print("Adapted:", result["adapted_content"])
|
| 582 |
+
print("Gamification:", result["gamification"])
|
| 583 |
+
```
|
| 584 |
+
|
| 585 |
+
### JavaScript Example
|
| 586 |
+
```javascript
|
| 587 |
+
const response = await fetch('{base_url}/adapt', {{
|
| 588 |
+
method: 'POST',
|
| 589 |
+
headers: {{ 'Content-Type': 'application/json' }},
|
| 590 |
+
body: JSON.stringify({{
|
| 591 |
+
content: 'Your educational content...',
|
| 592 |
+
profile: 'visual_structure',
|
| 593 |
+
interests: ['technology'],
|
| 594 |
+
complexity: 'intermediate'
|
| 595 |
+
}})
|
| 596 |
+
}});
|
| 597 |
+
|
| 598 |
+
const result = await response.json();
|
| 599 |
+
console.log('Adapted:', result.adapted_content);
|
| 600 |
+
```
|
| 601 |
+
|
| 602 |
+
### cURL Example
|
| 603 |
+
```bash
|
| 604 |
+
curl -X POST "{base_url}/adapt" \\
|
| 605 |
+
-H "Content-Type: application/json" \\
|
| 606 |
+
-d '{{
|
| 607 |
+
"content": "AI is transforming education...",
|
| 608 |
+
"profile": "visual_structure",
|
| 609 |
+
"interests": ["technology", "AI"],
|
| 610 |
+
"complexity": "intermediate"
|
| 611 |
+
}}'
|
| 612 |
+
```
|
| 613 |
+
|
| 614 |
+
### Available Profiles
|
| 615 |
+
- `visual_structure` - Clear organization and hierarchy
|
| 616 |
+
- `hyperfocus_directed` - Technical depth and detail
|
| 617 |
+
- `sensory_adaptation` - Calm and accessible design
|
| 618 |
+
- `special_interests` - Gamification and motivation
|
| 619 |
+
|
| 620 |
+
### Response Format
|
| 621 |
+
```json
|
| 622 |
+
{{
|
| 623 |
+
"adapted_content": "Enhanced HTML content...",
|
| 624 |
+
"gamification": {{
|
| 625 |
+
"current_level": 15,
|
| 626 |
+
"xp_points": 1250,
|
| 627 |
+
"achievements": ["Explorer", "Scholar"]
|
| 628 |
+
}},
|
| 629 |
+
"processing_time": 0.45,
|
| 630 |
+
"profile_used": "visual_structure",
|
| 631 |
+
"success": true
|
| 632 |
+
}}
|
| 633 |
+
```
|
| 634 |
+
"""
|
| 635 |
|
| 636 |
def create_interface(self):
|
| 637 |
"""Create the main Gradio interface with API integration"""
|
|
|
|
| 641 |
theme=gr.themes.Soft()
|
| 642 |
) as interface:
|
| 643 |
|
| 644 |
+
# Main Header with API info
|
| 645 |
header_html = """
|
| 646 |
<div style="text-align: center; background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); color: white; padding: 2.5rem; border-radius: 20px; margin-bottom: 2rem;">
|
| 647 |
<h1 style="margin: 0; font-size: 2.5em;">🧠 InclusiveEdu</h1>
|
|
|
|
| 657 |
|
| 658 |
gr.HTML(header_html)
|
| 659 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 660 |
# Main Interface Layout
|
| 661 |
with gr.Row():
|
| 662 |
# Input Section
|
|
|
|
| 835 |
)
|
| 836 |
|
| 837 |
# Enhanced Footer with API Information
|
| 838 |
+
footer_html = f"""
|
| 839 |
<div style="margin-top: 50px; padding: 30px; background: #f8f9fa; border-radius: 20px; text-align: center;">
|
| 840 |
|
| 841 |
<h3 style="color: #495057; margin-top: 0;">🧠 InclusiveEdu - Empowering Every Learner</h3>
|
|
|
|
| 894 |
Optimized for cloud deployment
|
| 895 |
</small>
|
| 896 |
</div>
|
| 897 |
+
"""
|
| 898 |
+
|
| 899 |
+
gr.HTML(footer_html)
|
| 900 |
|
| 901 |
return interface
|
| 902 |
|