Spaces:
Running
Running
Upload 4 files
Browse files- README.md +9 -3
- app.py +379 -332
- config.json +3 -3
README.md
CHANGED
|
@@ -8,12 +8,12 @@ sdk_version: 5.39.0
|
|
| 8 |
app_file: app.py
|
| 9 |
pinned: false
|
| 10 |
license: mit
|
| 11 |
-
short_description: Interactive STEM adventure game
|
| 12 |
---
|
| 13 |
|
| 14 |
# STEM Adventure Games
|
| 15 |
|
| 16 |
-
Interactive STEM adventure game
|
| 17 |
|
| 18 |
## Quick Setup
|
| 19 |
|
|
@@ -31,6 +31,12 @@ Interactive STEM adventure game guide
|
|
| 31 |
5. This enables automatic configuration updates
|
| 32 |
|
| 33 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 34 |
|
| 35 |
### Step 3: Test Your Space
|
| 36 |
Your Space should now be running! Try the example prompts or ask your own questions.
|
|
@@ -39,7 +45,7 @@ Your Space should now be running! Try the example prompts or ask your own questi
|
|
| 39 |
- **Model**: google/gemini-2.0-flash-001
|
| 40 |
- **API Key Variable**: API_KEY
|
| 41 |
- **HF Token Variable**: HF_TOKEN (for auto-updates)
|
| 42 |
-
- **Access**:
|
| 43 |
|
| 44 |
## Support
|
| 45 |
For help, visit the HuggingFace documentation or community forums.
|
|
|
|
| 8 |
app_file: app.py
|
| 9 |
pinned: false
|
| 10 |
license: mit
|
| 11 |
+
short_description: Interactive STEM adventure game guidehhhhhhhhhhhhhhh
|
| 12 |
---
|
| 13 |
|
| 14 |
# STEM Adventure Games
|
| 15 |
|
| 16 |
+
Interactive STEM adventure game guidehhhhhhhhhhhhhhh
|
| 17 |
|
| 18 |
## Quick Setup
|
| 19 |
|
|
|
|
| 31 |
5. This enables automatic configuration updates
|
| 32 |
|
| 33 |
|
| 34 |
+
### Step 3: Set Access Code
|
| 35 |
+
1. In Settings β Variables and secrets
|
| 36 |
+
2. Add secret: `ACCESS_CODE`
|
| 37 |
+
3. Set your chosen password
|
| 38 |
+
4. Share with authorized users
|
| 39 |
+
|
| 40 |
|
| 41 |
### Step 3: Test Your Space
|
| 42 |
Your Space should now be running! Try the example prompts or ask your own questions.
|
|
|
|
| 45 |
- **Model**: google/gemini-2.0-flash-001
|
| 46 |
- **API Key Variable**: API_KEY
|
| 47 |
- **HF Token Variable**: HF_TOKEN (for auto-updates)
|
| 48 |
+
- **Access Control**: Enabled (ACCESS_CODE)
|
| 49 |
|
| 50 |
## Support
|
| 51 |
For help, visit the HuggingFace documentation or community forums.
|
app.py
CHANGED
|
@@ -13,18 +13,18 @@ from typing import List, Dict, Optional, Any, Tuple
|
|
| 13 |
|
| 14 |
# Configuration
|
| 15 |
SPACE_NAME = 'STEM Adventure Games'
|
| 16 |
-
SPACE_DESCRIPTION = 'Interactive STEM adventure game
|
| 17 |
|
| 18 |
# Default configuration values
|
| 19 |
DEFAULT_CONFIG = {
|
| 20 |
'name': SPACE_NAME,
|
| 21 |
'description': SPACE_DESCRIPTION,
|
| 22 |
-
'system_prompt':
|
| 23 |
'temperature': 0.6,
|
| 24 |
'max_tokens': 1000,
|
| 25 |
'model': 'google/gemini-2.0-flash-001',
|
| 26 |
'api_key_var': 'API_KEY',
|
| 27 |
-
'theme': '
|
| 28 |
'grounding_urls': ["https://en.wikipedia.org/wiki/List_of_experiments", "https://en.wikipedia.org/wiki/Scientific_method", "https://en.wikipedia.org/wiki/List_of_experiments#Biology", "https://en.wikipedia.org/wiki/List_of_experiments#Astronomy", "https://en.wikipedia.org/wiki/List_of_experiments#Chemistry", "https://en.wikipedia.org/wiki/List_of_experiments#Physics", "https://en.wikipedia.org/wiki/List_of_experiments#Geology"],
|
| 29 |
'enable_dynamic_urls': True,
|
| 30 |
'enable_file_upload': True,
|
|
@@ -113,35 +113,39 @@ class ConfigurationManager:
|
|
| 113 |
os.remove(os.path.join(self.backup_dir, old_backup))
|
| 114 |
except Exception as e:
|
| 115 |
print(f"β οΈ Error cleaning up backups: {e}")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 116 |
|
| 117 |
|
| 118 |
-
#
|
| 119 |
config_manager = ConfigurationManager()
|
| 120 |
config = config_manager.load()
|
| 121 |
|
| 122 |
-
#
|
| 123 |
SPACE_NAME = config.get('name', DEFAULT_CONFIG['name'])
|
| 124 |
SPACE_DESCRIPTION = config.get('description', DEFAULT_CONFIG['description'])
|
| 125 |
SYSTEM_PROMPT = config.get('system_prompt', DEFAULT_CONFIG['system_prompt'])
|
|
|
|
|
|
|
| 126 |
MODEL = config.get('model', DEFAULT_CONFIG['model'])
|
| 127 |
-
API_KEY_VAR = config.get('api_key_var', DEFAULT_CONFIG['api_key_var'])
|
| 128 |
THEME = config.get('theme', DEFAULT_CONFIG['theme'])
|
| 129 |
GROUNDING_URLS = config.get('grounding_urls', DEFAULT_CONFIG['grounding_urls'])
|
| 130 |
ENABLE_DYNAMIC_URLS = config.get('enable_dynamic_urls', DEFAULT_CONFIG['enable_dynamic_urls'])
|
| 131 |
-
ENABLE_FILE_UPLOAD = config.get('enable_file_upload', DEFAULT_CONFIG
|
| 132 |
-
|
| 133 |
-
|
|
|
|
|
|
|
|
|
|
| 134 |
HF_TOKEN = os.environ.get('HF_TOKEN', '')
|
| 135 |
SPACE_ID = os.environ.get('SPACE_ID', '')
|
| 136 |
|
| 137 |
-
# Get theme instance
|
| 138 |
-
theme = AVAILABLE_THEMES.get(THEME, gr.themes.Default())
|
| 139 |
-
|
| 140 |
-
# Configuration defaults from loaded config
|
| 141 |
-
temperature = config.get('temperature', DEFAULT_CONFIG['temperature'])
|
| 142 |
-
max_tokens = config.get('max_tokens', DEFAULT_CONFIG['max_tokens'])
|
| 143 |
-
|
| 144 |
|
|
|
|
| 145 |
def validate_api_key() -> bool:
|
| 146 |
"""Validate API key configuration"""
|
| 147 |
if not API_KEY:
|
|
@@ -229,36 +233,28 @@ def extract_urls_from_text(text: str) -> List[str]:
|
|
| 229 |
|
| 230 |
|
| 231 |
def process_file_upload(file_path: str) -> str:
|
| 232 |
-
"""Process uploaded file
|
|
|
|
|
|
|
|
|
|
| 233 |
try:
|
| 234 |
-
if not os.path.exists(file_path):
|
| 235 |
-
return "β File not found"
|
| 236 |
-
|
| 237 |
file_size = os.path.getsize(file_path)
|
| 238 |
file_name = os.path.basename(file_path)
|
| 239 |
-
_, ext = os.path.splitext(
|
| 240 |
-
ext = ext.lower()
|
| 241 |
|
| 242 |
-
# Text
|
| 243 |
-
|
| 244 |
-
|
| 245 |
-
|
| 246 |
-
|
| 247 |
-
|
| 248 |
-
|
|
|
|
| 249 |
|
| 250 |
-
#
|
| 251 |
-
elif ext in {'.java', '.cpp', '.c', '.h', '.hpp', '.cs', '.rb', '.go', '.rs', '.swift', '.kt'}:
|
| 252 |
-
with open(file_path, 'r', encoding='utf-8', errors='replace') as f:
|
| 253 |
-
content = f.read(8000)
|
| 254 |
-
if len(content) == 8000:
|
| 255 |
-
content += "\n... [truncated]"
|
| 256 |
-
return f"π **{file_name}** ({file_size:,} bytes)\n```{ext[1:]}\n{content}\n```"
|
| 257 |
|
| 258 |
-
|
| 259 |
-
|
| 260 |
-
max_chars = 5000
|
| 261 |
-
with open(file_path, 'r', encoding='utf-8', errors='replace') as f:
|
| 262 |
content = f.read(max_chars)
|
| 263 |
if len(content) == max_chars:
|
| 264 |
content += "\n... [truncated]"
|
|
@@ -328,21 +324,23 @@ Model: {MODEL}
|
|
| 328 |
"""
|
| 329 |
|
| 330 |
message_count = 0
|
| 331 |
-
for
|
| 332 |
-
|
| 333 |
-
|
| 334 |
-
|
| 335 |
-
|
| 336 |
-
|
| 337 |
-
|
| 338 |
-
|
| 339 |
-
|
|
|
|
| 340 |
|
| 341 |
return markdown_content
|
| 342 |
|
| 343 |
|
| 344 |
-
def generate_response(message: str, history: List[Dict[str, str]], files: Optional[List
|
| 345 |
-
"""Generate
|
|
|
|
| 346 |
# API key validation
|
| 347 |
if not API_KEY:
|
| 348 |
return f"""π **API Key Required**
|
|
@@ -402,10 +400,11 @@ Get your API key at: https://openrouter.ai/keys"""
|
|
| 402 |
|
| 403 |
# Add conversation history
|
| 404 |
for msg in history:
|
| 405 |
-
|
| 406 |
-
|
| 407 |
-
|
| 408 |
-
|
|
|
|
| 409 |
|
| 410 |
# Add current message with context
|
| 411 |
full_message = message
|
|
@@ -419,6 +418,7 @@ Get your API key at: https://openrouter.ai/keys"""
|
|
| 419 |
"content": full_message
|
| 420 |
})
|
| 421 |
|
|
|
|
| 422 |
try:
|
| 423 |
# Make API request
|
| 424 |
headers = {
|
|
@@ -458,11 +458,17 @@ Get your API key at: https://openrouter.ai/keys"""
|
|
| 458 |
return f"β API Error ({response.status_code}): {error_message}"
|
| 459 |
|
| 460 |
except requests.exceptions.Timeout:
|
| 461 |
-
return "
|
|
|
|
|
|
|
| 462 |
except Exception as e:
|
| 463 |
return f"β Error: {str(e)}"
|
| 464 |
|
| 465 |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 466 |
def verify_hf_token_access() -> Tuple[bool, str]:
|
| 467 |
"""Verify HuggingFace token and access"""
|
| 468 |
if not HF_TOKEN:
|
|
@@ -486,199 +492,204 @@ def verify_hf_token_access() -> Tuple[bool, str]:
|
|
| 486 |
return False, f"Error verifying HF token: {str(e)}"
|
| 487 |
|
| 488 |
|
| 489 |
-
#
|
| 490 |
-
|
| 491 |
-
"""
|
| 492 |
-
def __init__(self):
|
| 493 |
-
self._granted = not bool(ACCESS_CODE)
|
| 494 |
-
|
| 495 |
-
def verify(self, code: str) -> bool:
|
| 496 |
-
"""Verify access code"""
|
| 497 |
-
if not ACCESS_CODE:
|
| 498 |
-
return True
|
| 499 |
-
self._granted = (code == ACCESS_CODE)
|
| 500 |
-
return self._granted
|
| 501 |
|
| 502 |
-
|
| 503 |
-
|
| 504 |
-
return self._granted
|
| 505 |
-
|
| 506 |
-
|
| 507 |
-
access_control = AccessControl()
|
| 508 |
-
|
| 509 |
-
|
| 510 |
-
def store_and_generate_response(message, history, *args):
|
| 511 |
-
"""Store history and generate response"""
|
| 512 |
-
# Convert history to expected format
|
| 513 |
-
formatted_history = []
|
| 514 |
-
for h in history:
|
| 515 |
-
if isinstance(h, dict):
|
| 516 |
-
formatted_history.append(h)
|
| 517 |
-
elif isinstance(h, (list, tuple)) and len(h) == 2:
|
| 518 |
-
formatted_history.append({"role": "user", "content": h[0]})
|
| 519 |
-
if h[1]:
|
| 520 |
-
formatted_history.append({"role": "assistant", "content": h[1]})
|
| 521 |
|
| 522 |
-
#
|
| 523 |
-
files = None
|
| 524 |
-
if ENABLE_FILE_UPLOAD and args:
|
| 525 |
-
files = args[0]
|
| 526 |
-
|
| 527 |
-
response = generate_response(message, formatted_history, files)
|
| 528 |
-
|
| 529 |
-
# Update stored history
|
| 530 |
-
new_history = formatted_history + [
|
| 531 |
-
{"role": "user", "content": message},
|
| 532 |
-
{"role": "assistant", "content": response}
|
| 533 |
-
]
|
| 534 |
-
|
| 535 |
-
return response
|
| 536 |
-
|
| 537 |
-
|
| 538 |
-
def create_interface() -> gr.Blocks:
|
| 539 |
-
"""Create the main Gradio interface"""
|
| 540 |
-
# API key validation
|
| 541 |
API_KEY_VALID = validate_api_key()
|
| 542 |
|
| 543 |
# Check HuggingFace access
|
| 544 |
HF_ACCESS_VALID, HF_ACCESS_MESSAGE = verify_hf_token_access()
|
| 545 |
|
|
|
|
|
|
|
|
|
|
| 546 |
with gr.Blocks(title=SPACE_NAME, theme=theme) as demo:
|
| 547 |
-
#
|
| 548 |
-
|
| 549 |
-
|
| 550 |
-
|
| 551 |
-
|
| 552 |
-
|
| 553 |
-
|
| 554 |
-
|
| 555 |
-
|
| 556 |
-
|
| 557 |
-
|
| 558 |
-
|
| 559 |
-
|
| 560 |
-
|
| 561 |
-
|
| 562 |
-
|
| 563 |
-
|
| 564 |
-
|
| 565 |
-
|
| 566 |
-
|
| 567 |
-
|
| 568 |
-
|
| 569 |
-
|
| 570 |
-
|
| 571 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 572 |
|
| 573 |
-
|
| 574 |
-
|
| 575 |
-
return "β
Access granted! Please refresh the page."
|
| 576 |
-
else:
|
| 577 |
-
return "β Invalid access code."
|
| 578 |
|
| 579 |
-
|
| 580 |
-
|
| 581 |
-
|
| 582 |
-
|
| 583 |
-
|
| 584 |
|
| 585 |
-
|
| 586 |
-
|
| 587 |
-
inputs=[access_input],
|
| 588 |
-
outputs=[access_status]
|
| 589 |
-
)
|
| 590 |
-
else:
|
| 591 |
-
# Show main chat interface
|
| 592 |
-
# Process examples
|
| 593 |
-
examples = config.get('examples', [])
|
| 594 |
-
if isinstance(examples, str):
|
| 595 |
-
try:
|
| 596 |
-
examples = json.loads(examples)
|
| 597 |
-
except:
|
| 598 |
-
examples = []
|
| 599 |
|
| 600 |
-
#
|
| 601 |
-
|
| 602 |
-
|
| 603 |
-
|
| 604 |
-
|
| 605 |
-
formatted_examples = examples
|
| 606 |
|
| 607 |
-
#
|
| 608 |
-
|
| 609 |
-
|
| 610 |
-
additional_inputs = [
|
| 611 |
-
gr.File(
|
| 612 |
-
label="π Upload Files",
|
| 613 |
-
file_count="multiple",
|
| 614 |
-
file_types=["text", "image", "pdf"],
|
| 615 |
-
visible=True
|
| 616 |
-
)
|
| 617 |
-
]
|
| 618 |
|
| 619 |
-
|
| 620 |
-
|
| 621 |
-
|
| 622 |
-
|
| 623 |
-
|
| 624 |
-
|
| 625 |
-
|
| 626 |
-
|
| 627 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 628 |
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 629 |
|
| 630 |
-
|
| 631 |
-
with gr.Row():
|
| 632 |
-
export_btn = gr.DownloadButton(
|
| 633 |
-
"π₯ Export Conversation",
|
| 634 |
-
variant="secondary",
|
| 635 |
-
size="sm"
|
| 636 |
-
)
|
| 637 |
|
| 638 |
-
#
|
| 639 |
-
|
|
|
|
|
|
|
| 640 |
|
| 641 |
-
#
|
| 642 |
-
|
| 643 |
-
|
| 644 |
-
if not history:
|
| 645 |
-
return None
|
| 646 |
-
|
| 647 |
-
content = export_conversation_to_markdown(current_history.value or history)
|
| 648 |
-
timestamp = datetime.now().strftime('%Y%m%d_%H%M%S')
|
| 649 |
-
filename = f"conversation_{timestamp}.md"
|
| 650 |
-
|
| 651 |
-
with tempfile.NamedTemporaryFile(mode='w', delete=False, suffix='.md') as f:
|
| 652 |
-
f.write(content)
|
| 653 |
-
return f.name
|
| 654 |
|
| 655 |
-
|
| 656 |
-
|
| 657 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 658 |
)
|
| 659 |
-
|
| 660 |
-
|
| 661 |
-
with gr.Accordion("βΉοΈ Configuration", open=False):
|
| 662 |
-
config_info = f"""**Model:** {MODEL}
|
| 663 |
-
**Temperature:** {temperature}
|
| 664 |
-
**Max Tokens:** {max_tokens}
|
| 665 |
-
**Theme:** {THEME}
|
| 666 |
-
**File Upload:** {'Enabled' if ENABLE_FILE_UPLOAD else 'Disabled'}
|
| 667 |
-
**Dynamic URLs:** {'Enabled' if ENABLE_DYNAMIC_URLS else 'Disabled'}"""
|
| 668 |
-
|
| 669 |
-
if GROUNDING_URLS:
|
| 670 |
-
config_info += f"\n**Grounding URLs:** {len(GROUNDING_URLS)} configured"
|
| 671 |
-
|
| 672 |
-
if not API_KEY_VALID:
|
| 673 |
-
config_info += f"\n\nβ οΈ **Note:** API key ({API_KEY_VAR}) not configured"
|
| 674 |
-
|
| 675 |
-
gr.Markdown(config_info)
|
| 676 |
-
|
| 677 |
-
# Configuration Tab (if HF access is valid)
|
| 678 |
-
if HF_ACCESS_VALID:
|
| 679 |
with gr.Tab("βοΈ Configuration"):
|
| 680 |
gr.Markdown("## Configuration Management")
|
| 681 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 682 |
|
| 683 |
# Current configuration display
|
| 684 |
with gr.Accordion("Current Configuration", open=False):
|
|
@@ -689,28 +700,34 @@ def create_interface() -> gr.Blocks:
|
|
| 689 |
)
|
| 690 |
|
| 691 |
# Configuration editor
|
| 692 |
-
|
| 693 |
-
|
| 694 |
-
|
| 695 |
-
|
| 696 |
-
|
| 697 |
-
|
| 698 |
-
|
| 699 |
-
|
| 700 |
-
|
| 701 |
-
|
| 702 |
-
|
| 703 |
-
|
| 704 |
-
|
| 705 |
-
|
| 706 |
-
|
| 707 |
-
|
| 708 |
-
|
| 709 |
-
|
| 710 |
-
|
| 711 |
-
|
| 712 |
-
|
| 713 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 714 |
|
| 715 |
edit_description = gr.Textbox(
|
| 716 |
label="Description",
|
|
@@ -724,7 +741,6 @@ def create_interface() -> gr.Blocks:
|
|
| 724 |
lines=5
|
| 725 |
)
|
| 726 |
|
| 727 |
-
# Advanced settings
|
| 728 |
with gr.Row():
|
| 729 |
edit_temperature = gr.Slider(
|
| 730 |
label="Temperature",
|
|
@@ -747,96 +763,127 @@ def create_interface() -> gr.Blocks:
|
|
| 747 |
lines=3
|
| 748 |
)
|
| 749 |
|
| 750 |
-
#
|
| 751 |
with gr.Row():
|
| 752 |
save_btn = gr.Button("πΎ Save Configuration", variant="primary")
|
| 753 |
reset_btn = gr.Button("β©οΈ Reset to Defaults", variant="secondary")
|
| 754 |
|
| 755 |
-
config_status = gr.Markdown(
|
| 756 |
-
|
| 757 |
-
|
| 758 |
-
|
| 759 |
-
|
| 760 |
-
|
| 761 |
-
|
| 762 |
-
|
| 763 |
-
|
| 764 |
-
|
| 765 |
-
|
| 766 |
-
|
| 767 |
-
|
| 768 |
-
|
| 769 |
-
|
| 770 |
-
|
| 771 |
-
|
| 772 |
-
|
| 773 |
-
|
| 774 |
-
|
| 775 |
-
|
| 776 |
-
|
| 777 |
-
|
| 778 |
-
|
| 779 |
-
|
| 780 |
-
|
| 781 |
-
|
| 782 |
-
|
| 783 |
-
|
| 784 |
-
|
| 785 |
-
|
| 786 |
-
|
| 787 |
-
|
| 788 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 789 |
else:
|
| 790 |
-
return "
|
| 791 |
-
|
| 792 |
-
|
| 793 |
-
|
| 794 |
-
|
| 795 |
-
|
| 796 |
-
|
| 797 |
-
|
| 798 |
-
|
| 799 |
-
|
| 800 |
-
|
| 801 |
-
|
| 802 |
-
|
| 803 |
-
|
| 804 |
-
|
| 805 |
-
|
| 806 |
-
|
| 807 |
-
|
| 808 |
-
|
| 809 |
-
|
| 810 |
-
|
| 811 |
-
|
| 812 |
-
|
| 813 |
-
|
| 814 |
-
|
| 815 |
-
|
| 816 |
-
|
| 817 |
-
|
| 818 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 819 |
)
|
| 820 |
-
|
| 821 |
-
|
| 822 |
-
|
| 823 |
-
|
|
|
|
|
|
|
| 824 |
)
|
| 825 |
-
|
| 826 |
-
|
| 827 |
-
|
| 828 |
-
|
| 829 |
-
|
| 830 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 831 |
|
| 832 |
|
| 833 |
-
#
|
| 834 |
if __name__ == "__main__":
|
| 835 |
-
print("===== Application Startup at", datetime.now().strftime('%Y-%m-%d %H:%M:%S'), "=====")
|
| 836 |
demo = create_interface()
|
| 837 |
-
demo.launch(
|
| 838 |
-
share=False,
|
| 839 |
-
server_name="0.0.0.0",
|
| 840 |
-
server_port=7860,
|
| 841 |
-
show_error=True
|
| 842 |
-
)
|
|
|
|
| 13 |
|
| 14 |
# Configuration
|
| 15 |
SPACE_NAME = 'STEM Adventure Games'
|
| 16 |
+
SPACE_DESCRIPTION = 'Interactive STEM adventure game guidehhhhhhhhhhhhhhh'
|
| 17 |
|
| 18 |
# Default configuration values
|
| 19 |
DEFAULT_CONFIG = {
|
| 20 |
'name': SPACE_NAME,
|
| 21 |
'description': SPACE_DESCRIPTION,
|
| 22 |
+
'system_prompt': "Simulate an interactive game-based learning experience through Choose Your Own STEM Adventure games featuring historically significant scientific experiments. Open each session with an unicode-styled arcade menu that reads 'STEM_ADV_GAMES` one stacked on top of the other. Underneath, frame the game in two sentences and widely sample 3-4 optional adventures from Wikipedia's List of Experiments. In the first stage, be brief and incrementally build more narrative content into the chat, immersing players in vivid historical moments written in second person (e.g. 'You are Galileo Galilei') that establish the year, location, prevailing beliefs, and tensions between established wisdom and emerging observations. Each section has 4 numbered decision points per stage that reflect experimental choices that subtly demonstrate a facet of the scientific method, with each choice meaningfully distinct in affording paths forward. Always end scenes with new branching choices that build a narrative progression based on concrete experimental procedures in laboratory environments. Offer backtracking options to emphasize how so-called failed experiments provide crucial insights, balancing historical accuracy with engaging gameplay that shows how systematic thinking and creative problem-solving combine in scientific breakthroughs. Goal: Teach scientific thinking through interactive historical moments where students make experimental design decisions.",
|
| 23 |
'temperature': 0.6,
|
| 24 |
'max_tokens': 1000,
|
| 25 |
'model': 'google/gemini-2.0-flash-001',
|
| 26 |
'api_key_var': 'API_KEY',
|
| 27 |
+
'theme': 'Glass',
|
| 28 |
'grounding_urls': ["https://en.wikipedia.org/wiki/List_of_experiments", "https://en.wikipedia.org/wiki/Scientific_method", "https://en.wikipedia.org/wiki/List_of_experiments#Biology", "https://en.wikipedia.org/wiki/List_of_experiments#Astronomy", "https://en.wikipedia.org/wiki/List_of_experiments#Chemistry", "https://en.wikipedia.org/wiki/List_of_experiments#Physics", "https://en.wikipedia.org/wiki/List_of_experiments#Geology"],
|
| 29 |
'enable_dynamic_urls': True,
|
| 30 |
'enable_file_upload': True,
|
|
|
|
| 113 |
os.remove(os.path.join(self.backup_dir, old_backup))
|
| 114 |
except Exception as e:
|
| 115 |
print(f"β οΈ Error cleaning up backups: {e}")
|
| 116 |
+
|
| 117 |
+
def get(self, key: str, default: Any = None) -> Any:
|
| 118 |
+
"""Get configuration value"""
|
| 119 |
+
if self._config is None:
|
| 120 |
+
self.load()
|
| 121 |
+
return self._config.get(key, default)
|
| 122 |
|
| 123 |
|
| 124 |
+
# Initialize configuration manager
|
| 125 |
config_manager = ConfigurationManager()
|
| 126 |
config = config_manager.load()
|
| 127 |
|
| 128 |
+
# Load configuration values
|
| 129 |
SPACE_NAME = config.get('name', DEFAULT_CONFIG['name'])
|
| 130 |
SPACE_DESCRIPTION = config.get('description', DEFAULT_CONFIG['description'])
|
| 131 |
SYSTEM_PROMPT = config.get('system_prompt', DEFAULT_CONFIG['system_prompt'])
|
| 132 |
+
temperature = config.get('temperature', DEFAULT_CONFIG['temperature'])
|
| 133 |
+
max_tokens = config.get('max_tokens', DEFAULT_CONFIG['max_tokens'])
|
| 134 |
MODEL = config.get('model', DEFAULT_CONFIG['model'])
|
|
|
|
| 135 |
THEME = config.get('theme', DEFAULT_CONFIG['theme'])
|
| 136 |
GROUNDING_URLS = config.get('grounding_urls', DEFAULT_CONFIG['grounding_urls'])
|
| 137 |
ENABLE_DYNAMIC_URLS = config.get('enable_dynamic_urls', DEFAULT_CONFIG['enable_dynamic_urls'])
|
| 138 |
+
ENABLE_FILE_UPLOAD = config.get('enable_file_upload', DEFAULT_CONFIG.get('enable_file_upload', True))
|
| 139 |
+
|
| 140 |
+
# Environment variables
|
| 141 |
+
ACCESS_CODE = os.environ.get("ACCESS_CODE")
|
| 142 |
+
API_KEY_VAR = config.get('api_key_var', DEFAULT_CONFIG['api_key_var'])
|
| 143 |
+
API_KEY = os.environ.get(API_KEY_VAR, "").strip() or None
|
| 144 |
HF_TOKEN = os.environ.get('HF_TOKEN', '')
|
| 145 |
SPACE_ID = os.environ.get('SPACE_ID', '')
|
| 146 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 147 |
|
| 148 |
+
# Utility functions
|
| 149 |
def validate_api_key() -> bool:
|
| 150 |
"""Validate API key configuration"""
|
| 151 |
if not API_KEY:
|
|
|
|
| 233 |
|
| 234 |
|
| 235 |
def process_file_upload(file_path: str) -> str:
|
| 236 |
+
"""Process uploaded file with Gradio best practices"""
|
| 237 |
+
if not file_path or not os.path.exists(file_path):
|
| 238 |
+
return "β File not found"
|
| 239 |
+
|
| 240 |
try:
|
|
|
|
|
|
|
|
|
|
| 241 |
file_size = os.path.getsize(file_path)
|
| 242 |
file_name = os.path.basename(file_path)
|
| 243 |
+
_, ext = os.path.splitext(file_path.lower())
|
|
|
|
| 244 |
|
| 245 |
+
# Text file extensions
|
| 246 |
+
text_extensions = {
|
| 247 |
+
'.txt', '.md', '.markdown', '.rst',
|
| 248 |
+
'.py', '.js', '.jsx', '.ts', '.tsx', '.json', '.yaml', '.yml',
|
| 249 |
+
'.html', '.htm', '.xml', '.css', '.scss',
|
| 250 |
+
'.java', '.c', '.cpp', '.h', '.cs', '.go', '.rs',
|
| 251 |
+
'.sh', '.bash', '.log', '.csv', '.sql'
|
| 252 |
+
}
|
| 253 |
|
| 254 |
+
max_chars = 5000 # Define max_chars limit for file reading
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 255 |
|
| 256 |
+
if ext in text_extensions:
|
| 257 |
+
with open(file_path, 'r', encoding='utf-8', errors='ignore') as f:
|
|
|
|
|
|
|
| 258 |
content = f.read(max_chars)
|
| 259 |
if len(content) == max_chars:
|
| 260 |
content += "\n... [truncated]"
|
|
|
|
| 324 |
"""
|
| 325 |
|
| 326 |
message_count = 0
|
| 327 |
+
for message in history:
|
| 328 |
+
if isinstance(message, dict):
|
| 329 |
+
role = message.get('role', 'unknown')
|
| 330 |
+
content = message.get('content', '')
|
| 331 |
+
|
| 332 |
+
if role == 'user':
|
| 333 |
+
message_count += 1
|
| 334 |
+
markdown_content += f"## User Message {message_count}\n\n{content}\n\n"
|
| 335 |
+
elif role == 'assistant':
|
| 336 |
+
markdown_content += f"## Assistant Response {message_count}\n\n{content}\n\n---\n\n"
|
| 337 |
|
| 338 |
return markdown_content
|
| 339 |
|
| 340 |
|
| 341 |
+
def generate_response(message: str, history: List[Dict[str, str]], files: Optional[List] = None) -> str:
|
| 342 |
+
"""Generate response using OpenRouter API with file support"""
|
| 343 |
+
|
| 344 |
# API key validation
|
| 345 |
if not API_KEY:
|
| 346 |
return f"""π **API Key Required**
|
|
|
|
| 400 |
|
| 401 |
# Add conversation history
|
| 402 |
for msg in history:
|
| 403 |
+
if isinstance(msg, dict) and 'role' in msg and 'content' in msg:
|
| 404 |
+
messages.append({
|
| 405 |
+
"role": msg['role'],
|
| 406 |
+
"content": msg['content']
|
| 407 |
+
})
|
| 408 |
|
| 409 |
# Add current message with context
|
| 410 |
full_message = message
|
|
|
|
| 418 |
"content": full_message
|
| 419 |
})
|
| 420 |
|
| 421 |
+
# Make API request
|
| 422 |
try:
|
| 423 |
# Make API request
|
| 424 |
headers = {
|
|
|
|
| 458 |
return f"β API Error ({response.status_code}): {error_message}"
|
| 459 |
|
| 460 |
except requests.exceptions.Timeout:
|
| 461 |
+
return "β° Request timeout (30s limit). Try a shorter message or different model."
|
| 462 |
+
except requests.exceptions.ConnectionError:
|
| 463 |
+
return "π Connection error. Check your internet connection and try again."
|
| 464 |
except Exception as e:
|
| 465 |
return f"β Error: {str(e)}"
|
| 466 |
|
| 467 |
|
| 468 |
+
# Chat history for export
|
| 469 |
+
chat_history_store = []
|
| 470 |
+
|
| 471 |
+
|
| 472 |
def verify_hf_token_access() -> Tuple[bool, str]:
|
| 473 |
"""Verify HuggingFace token and access"""
|
| 474 |
if not HF_TOKEN:
|
|
|
|
| 492 |
return False, f"Error verifying HF token: {str(e)}"
|
| 493 |
|
| 494 |
|
| 495 |
+
# Create main interface with clean tab structure
|
| 496 |
+
def create_interface():
|
| 497 |
+
"""Create the Gradio interface with clean tab structure"""
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 498 |
|
| 499 |
+
# Get theme
|
| 500 |
+
theme = AVAILABLE_THEMES.get(THEME, gr.themes.Default())
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 501 |
|
| 502 |
+
# Validate API key on startup
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 503 |
API_KEY_VALID = validate_api_key()
|
| 504 |
|
| 505 |
# Check HuggingFace access
|
| 506 |
HF_ACCESS_VALID, HF_ACCESS_MESSAGE = verify_hf_token_access()
|
| 507 |
|
| 508 |
+
# Access control check
|
| 509 |
+
has_access = ACCESS_CODE is None # No access code required
|
| 510 |
+
|
| 511 |
with gr.Blocks(title=SPACE_NAME, theme=theme) as demo:
|
| 512 |
+
# State for access control
|
| 513 |
+
access_granted = gr.State(has_access)
|
| 514 |
+
|
| 515 |
+
# Header - always visible
|
| 516 |
+
gr.Markdown(f"# {SPACE_NAME}")
|
| 517 |
+
gr.Markdown(SPACE_DESCRIPTION)
|
| 518 |
+
|
| 519 |
+
# Access control panel (visible when access not granted)
|
| 520 |
+
with gr.Column(visible=(not has_access)) as access_panel:
|
| 521 |
+
gr.Markdown("### π Access Required")
|
| 522 |
+
gr.Markdown("Please enter the access code:")
|
| 523 |
+
|
| 524 |
+
with gr.Row():
|
| 525 |
+
access_input = gr.Textbox(
|
| 526 |
+
label="Access Code",
|
| 527 |
+
placeholder="Enter access code...",
|
| 528 |
+
type="password",
|
| 529 |
+
scale=3
|
| 530 |
+
)
|
| 531 |
+
access_btn = gr.Button("Submit", variant="primary", scale=1)
|
| 532 |
+
|
| 533 |
+
access_status = gr.Markdown()
|
| 534 |
+
|
| 535 |
+
# Main interface (visible when access granted)
|
| 536 |
+
with gr.Column(visible=has_access) as main_panel:
|
| 537 |
+
with gr.Tabs() as tabs:
|
| 538 |
+
# Chat Tab
|
| 539 |
+
with gr.Tab("π¬ Chat"):
|
| 540 |
+
# Get examples
|
| 541 |
+
examples = config.get('examples', [])
|
| 542 |
+
if isinstance(examples, str):
|
| 543 |
+
try:
|
| 544 |
+
examples = json.loads(examples)
|
| 545 |
+
except:
|
| 546 |
+
examples = []
|
| 547 |
+
|
| 548 |
+
# State to hold uploaded files
|
| 549 |
+
uploaded_files = gr.State([])
|
| 550 |
+
|
| 551 |
+
# Create chat interface
|
| 552 |
+
chatbot = gr.Chatbot(type="messages", height=400)
|
| 553 |
+
msg = gr.Textbox(label="Message", placeholder="Type your message here...", lines=2)
|
| 554 |
+
|
| 555 |
+
# Examples section
|
| 556 |
+
if examples:
|
| 557 |
+
gr.Examples(examples=examples, inputs=msg)
|
| 558 |
+
|
| 559 |
+
with gr.Row():
|
| 560 |
+
submit_btn = gr.Button("Send", variant="primary")
|
| 561 |
+
clear_btn = gr.Button("Clear")
|
| 562 |
+
|
| 563 |
+
# Chat functionality
|
| 564 |
+
def respond(message, chat_history, files_state, is_granted):
|
| 565 |
+
if not is_granted:
|
| 566 |
+
return chat_history, "", is_granted
|
| 567 |
|
| 568 |
+
if not message:
|
| 569 |
+
return chat_history, "", is_granted
|
|
|
|
|
|
|
|
|
|
| 570 |
|
| 571 |
+
# Format history for the generate_response function
|
| 572 |
+
formatted_history = []
|
| 573 |
+
for h in chat_history:
|
| 574 |
+
if isinstance(h, dict):
|
| 575 |
+
formatted_history.append(h)
|
| 576 |
|
| 577 |
+
# Get response
|
| 578 |
+
response = generate_response(message, formatted_history, files_state)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 579 |
|
| 580 |
+
# Update chat history
|
| 581 |
+
chat_history = chat_history + [
|
| 582 |
+
{"role": "user", "content": message},
|
| 583 |
+
{"role": "assistant", "content": response}
|
| 584 |
+
]
|
|
|
|
| 585 |
|
| 586 |
+
# Update stored history for export
|
| 587 |
+
global chat_history_store
|
| 588 |
+
chat_history_store = chat_history
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 589 |
|
| 590 |
+
return chat_history, "", is_granted
|
| 591 |
+
|
| 592 |
+
# Wire up the interface
|
| 593 |
+
msg.submit(respond, [msg, chatbot, uploaded_files, access_granted], [chatbot, msg, access_granted])
|
| 594 |
+
submit_btn.click(respond, [msg, chatbot, uploaded_files, access_granted], [chatbot, msg, access_granted])
|
| 595 |
+
clear_btn.click(lambda: ([], ""), outputs=[chatbot, msg])
|
| 596 |
+
|
| 597 |
+
# File upload accordion
|
| 598 |
+
if ENABLE_FILE_UPLOAD:
|
| 599 |
+
with gr.Accordion("π Upload Files", open=False):
|
| 600 |
+
file_upload = gr.File(
|
| 601 |
+
label="Upload Files",
|
| 602 |
+
file_types=None,
|
| 603 |
+
file_count="multiple",
|
| 604 |
+
visible=True,
|
| 605 |
+
interactive=True
|
| 606 |
+
)
|
| 607 |
+
clear_files_btn = gr.Button("Clear Files", size="sm", variant="secondary")
|
| 608 |
+
uploaded_files_display = gr.Markdown("", visible=False)
|
| 609 |
+
|
| 610 |
+
def handle_file_upload(files):
|
| 611 |
+
if not files:
|
| 612 |
+
return [], "", gr.update(visible=False)
|
| 613 |
+
|
| 614 |
+
file_names = []
|
| 615 |
+
for file_info in files:
|
| 616 |
+
if isinstance(file_info, dict):
|
| 617 |
+
file_path = file_info.get('path', file_info.get('name', ''))
|
| 618 |
+
else:
|
| 619 |
+
file_path = str(file_info)
|
| 620 |
+
|
| 621 |
+
if file_path and os.path.exists(file_path):
|
| 622 |
+
file_names.append(os.path.basename(file_path))
|
| 623 |
+
|
| 624 |
+
if file_names:
|
| 625 |
+
display_text = f"π **Uploaded files:** {', '.join(file_names)}"
|
| 626 |
+
return files, display_text, gr.update(visible=True)
|
| 627 |
+
return [], "", gr.update(visible=False)
|
| 628 |
+
|
| 629 |
+
def clear_files():
|
| 630 |
+
return None, [], "", gr.update(visible=False)
|
| 631 |
+
|
| 632 |
+
file_upload.change(
|
| 633 |
+
handle_file_upload,
|
| 634 |
+
inputs=[file_upload],
|
| 635 |
+
outputs=[uploaded_files, uploaded_files_display, uploaded_files_display]
|
| 636 |
+
)
|
| 637 |
+
|
| 638 |
+
clear_files_btn.click(
|
| 639 |
+
clear_files,
|
| 640 |
+
outputs=[file_upload, uploaded_files, uploaded_files_display, uploaded_files_display]
|
| 641 |
+
)
|
| 642 |
+
|
| 643 |
+
# Export functionality
|
| 644 |
+
with gr.Row():
|
| 645 |
+
export_btn = gr.DownloadButton(
|
| 646 |
+
"π₯ Export Conversation",
|
| 647 |
+
variant="secondary",
|
| 648 |
+
size="sm"
|
| 649 |
)
|
| 650 |
+
|
| 651 |
+
# Export handler
|
| 652 |
+
def prepare_export():
|
| 653 |
+
if not chat_history_store:
|
| 654 |
+
return None
|
| 655 |
|
| 656 |
+
content = export_conversation_to_markdown(chat_history_store)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 657 |
|
| 658 |
+
# Create filename
|
| 659 |
+
space_name_safe = re.sub(r'[^a-zA-Z0-9]+', '_', SPACE_NAME).lower()
|
| 660 |
+
timestamp = datetime.now().strftime('%Y%m%d_%H%M%S')
|
| 661 |
+
filename = f"{space_name_safe}_conversation_{timestamp}.md"
|
| 662 |
|
| 663 |
+
# Save to temp file
|
| 664 |
+
temp_path = Path(tempfile.gettempdir()) / filename
|
| 665 |
+
temp_path.write_text(content, encoding='utf-8')
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 666 |
|
| 667 |
+
return str(temp_path)
|
| 668 |
+
|
| 669 |
+
export_btn.click(
|
| 670 |
+
prepare_export,
|
| 671 |
+
outputs=[export_btn]
|
| 672 |
+
)
|
| 673 |
+
|
| 674 |
+
# Configuration accordion
|
| 675 |
+
with gr.Accordion("βΉοΈ Configuration", open=False):
|
| 676 |
+
gr.JSON(
|
| 677 |
+
value=config,
|
| 678 |
+
label="config.json",
|
| 679 |
+
show_label=True
|
| 680 |
)
|
| 681 |
+
|
| 682 |
+
# Configuration Tab
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 683 |
with gr.Tab("βοΈ Configuration"):
|
| 684 |
gr.Markdown("## Configuration Management")
|
| 685 |
+
|
| 686 |
+
# Show authentication status
|
| 687 |
+
if HF_ACCESS_VALID:
|
| 688 |
+
gr.Markdown(f"β
{HF_ACCESS_MESSAGE}")
|
| 689 |
+
gr.Markdown("Configuration changes will be saved to the HuggingFace repository.")
|
| 690 |
+
else:
|
| 691 |
+
gr.Markdown(f"βΉοΈ {HF_ACCESS_MESSAGE}")
|
| 692 |
+
gr.Markdown("Set HF_TOKEN in Space secrets to enable auto-save.")
|
| 693 |
|
| 694 |
# Current configuration display
|
| 695 |
with gr.Accordion("Current Configuration", open=False):
|
|
|
|
| 700 |
)
|
| 701 |
|
| 702 |
# Configuration editor
|
| 703 |
+
gr.Markdown("### βοΈ Configuration Editor")
|
| 704 |
+
|
| 705 |
+
# Show lock status if locked
|
| 706 |
+
if config.get('locked', False):
|
| 707 |
+
gr.Markdown("β οΈ **Note:** Configuration is locked. Contact an administrator to unlock.")
|
| 708 |
+
|
| 709 |
+
# Basic settings
|
| 710 |
+
with gr.Column():
|
| 711 |
+
edit_name = gr.Textbox(
|
| 712 |
+
label="Space Name",
|
| 713 |
+
value=config.get('name', ''),
|
| 714 |
+
max_lines=1
|
| 715 |
+
)
|
| 716 |
+
edit_model = gr.Dropdown(
|
| 717 |
+
label="Model",
|
| 718 |
+
choices=[
|
| 719 |
+
"google/gemini-2.0-flash-001",
|
| 720 |
+
"anthropic/claude-3.5-haiku",
|
| 721 |
+
"openai/gpt-4o-mini",
|
| 722 |
+
"openai/gpt-4o-mini-search-preview",
|
| 723 |
+
"meta-llama/llama-3.1-70b-instruct",
|
| 724 |
+
"qwen/qwen-2.5-72b-instruct",
|
| 725 |
+
"google/gemma-3-27b-it",
|
| 726 |
+
"custom"
|
| 727 |
+
],
|
| 728 |
+
value=config.get('model', ''),
|
| 729 |
+
allow_custom_value=True
|
| 730 |
+
)
|
| 731 |
|
| 732 |
edit_description = gr.Textbox(
|
| 733 |
label="Description",
|
|
|
|
| 741 |
lines=5
|
| 742 |
)
|
| 743 |
|
|
|
|
| 744 |
with gr.Row():
|
| 745 |
edit_temperature = gr.Slider(
|
| 746 |
label="Temperature",
|
|
|
|
| 763 |
lines=3
|
| 764 |
)
|
| 765 |
|
| 766 |
+
# Configuration actions
|
| 767 |
with gr.Row():
|
| 768 |
save_btn = gr.Button("πΎ Save Configuration", variant="primary")
|
| 769 |
reset_btn = gr.Button("β©οΈ Reset to Defaults", variant="secondary")
|
| 770 |
|
| 771 |
+
config_status = gr.Markdown()
|
| 772 |
+
|
| 773 |
+
def save_configuration(name, description, system_prompt, model, temp, tokens, examples):
|
| 774 |
+
"""Save updated configuration"""
|
| 775 |
+
try:
|
| 776 |
+
updated_config = config.copy()
|
| 777 |
+
updated_config.update({
|
| 778 |
+
'name': name,
|
| 779 |
+
'description': description,
|
| 780 |
+
'system_prompt': system_prompt,
|
| 781 |
+
'model': model,
|
| 782 |
+
'temperature': temp,
|
| 783 |
+
'max_tokens': int(tokens),
|
| 784 |
+
'examples': [ex.strip() for ex in examples.split('\n') if ex.strip()],
|
| 785 |
+
'locked': config.get('locked', False)
|
| 786 |
+
})
|
| 787 |
+
|
| 788 |
+
if config_manager.save(updated_config):
|
| 789 |
+
# Auto-commit if HF token is available
|
| 790 |
+
if HF_TOKEN and SPACE_ID:
|
| 791 |
+
try:
|
| 792 |
+
from huggingface_hub import HfApi, CommitOperationAdd
|
| 793 |
+
api = HfApi(token=HF_TOKEN)
|
| 794 |
+
|
| 795 |
+
operations = [
|
| 796 |
+
CommitOperationAdd(
|
| 797 |
+
path_or_fileobj=config_manager.config_path,
|
| 798 |
+
path_in_repo="config.json"
|
| 799 |
+
)
|
| 800 |
+
]
|
| 801 |
+
|
| 802 |
+
api.create_commit(
|
| 803 |
+
repo_id=SPACE_ID,
|
| 804 |
+
operations=operations,
|
| 805 |
+
commit_message="Update configuration via web UI",
|
| 806 |
+
commit_description=f"Configuration update at {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}",
|
| 807 |
+
repo_type="space",
|
| 808 |
+
token=HF_TOKEN
|
| 809 |
+
)
|
| 810 |
+
return "β
Configuration saved and committed to repository!"
|
| 811 |
+
except Exception as e:
|
| 812 |
+
return f"β
Configuration saved locally. β οΈ Auto-commit failed: {str(e)}"
|
| 813 |
+
else:
|
| 814 |
+
return "β
Configuration saved locally (no HF token for auto-commit)"
|
| 815 |
else:
|
| 816 |
+
return "β Failed to save configuration"
|
| 817 |
+
|
| 818 |
+
except Exception as e:
|
| 819 |
+
return f"β Error: {str(e)}"
|
| 820 |
+
|
| 821 |
+
save_btn.click(
|
| 822 |
+
save_configuration,
|
| 823 |
+
inputs=[edit_name, edit_description, edit_system_prompt, edit_model,
|
| 824 |
+
edit_temperature, edit_max_tokens, edit_examples],
|
| 825 |
+
outputs=[config_status]
|
| 826 |
+
)
|
| 827 |
+
|
| 828 |
+
def reset_configuration():
|
| 829 |
+
"""Reset to default configuration"""
|
| 830 |
+
try:
|
| 831 |
+
if config_manager.save(DEFAULT_CONFIG):
|
| 832 |
+
return (
|
| 833 |
+
DEFAULT_CONFIG['name'],
|
| 834 |
+
DEFAULT_CONFIG['description'],
|
| 835 |
+
DEFAULT_CONFIG['system_prompt'],
|
| 836 |
+
DEFAULT_CONFIG['model'],
|
| 837 |
+
DEFAULT_CONFIG['temperature'],
|
| 838 |
+
DEFAULT_CONFIG['max_tokens'],
|
| 839 |
+
'\n'.join(DEFAULT_CONFIG['examples']),
|
| 840 |
+
"β
Reset to default configuration"
|
| 841 |
+
)
|
| 842 |
+
else:
|
| 843 |
+
return (*[gr.update() for _ in range(7)], "β Failed to reset")
|
| 844 |
+
except Exception as e:
|
| 845 |
+
return (*[gr.update() for _ in range(7)], f"β Error: {str(e)}")
|
| 846 |
+
|
| 847 |
+
reset_btn.click(
|
| 848 |
+
reset_configuration,
|
| 849 |
+
outputs=[edit_name, edit_description, edit_system_prompt, edit_model,
|
| 850 |
+
edit_temperature, edit_max_tokens, edit_examples, config_status]
|
| 851 |
+
)
|
| 852 |
+
|
| 853 |
+
# Access control handler
|
| 854 |
+
if ACCESS_CODE:
|
| 855 |
+
def handle_access(code, current_state):
|
| 856 |
+
if code == ACCESS_CODE:
|
| 857 |
+
return (
|
| 858 |
+
gr.update(visible=False), # Hide access panel
|
| 859 |
+
gr.update(visible=True), # Show main panel
|
| 860 |
+
gr.update(value="β
Access granted!"), # Status message
|
| 861 |
+
True # Update state
|
| 862 |
)
|
| 863 |
+
else:
|
| 864 |
+
return (
|
| 865 |
+
gr.update(visible=True), # Keep access panel visible
|
| 866 |
+
gr.update(visible=False), # Keep main panel hidden
|
| 867 |
+
gr.update(value="β Invalid access code. Please try again."), # Status message
|
| 868 |
+
False # State remains false
|
| 869 |
)
|
| 870 |
+
|
| 871 |
+
access_btn.click(
|
| 872 |
+
handle_access,
|
| 873 |
+
inputs=[access_input, access_granted],
|
| 874 |
+
outputs=[access_panel, main_panel, access_status, access_granted]
|
| 875 |
+
)
|
| 876 |
+
|
| 877 |
+
access_input.submit(
|
| 878 |
+
handle_access,
|
| 879 |
+
inputs=[access_input, access_granted],
|
| 880 |
+
outputs=[access_panel, main_panel, access_status, access_granted]
|
| 881 |
+
)
|
| 882 |
+
|
| 883 |
+
return demo
|
| 884 |
|
| 885 |
|
| 886 |
+
# Create and launch the interface
|
| 887 |
if __name__ == "__main__":
|
|
|
|
| 888 |
demo = create_interface()
|
| 889 |
+
demo.launch()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
config.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
| 1 |
{
|
| 2 |
"name": "STEM Adventure Games",
|
| 3 |
-
"description": "Interactive STEM adventure game
|
| 4 |
-
"system_prompt": "
|
| 5 |
"model": "google/gemini-2.0-flash-001",
|
| 6 |
"api_key_var": "API_KEY",
|
| 7 |
"temperature": 0.6,
|
|
@@ -22,5 +22,5 @@
|
|
| 22 |
],
|
| 23 |
"enable_dynamic_urls": true,
|
| 24 |
"enable_file_upload": true,
|
| 25 |
-
"theme": "
|
| 26 |
}
|
|
|
|
| 1 |
{
|
| 2 |
"name": "STEM Adventure Games",
|
| 3 |
+
"description": "Interactive STEM adventure game guidehhhhhhhhhhhhhhh",
|
| 4 |
+
"system_prompt": "Simulate an interactive game-based learning experience through Choose Your Own STEM Adventure games featuring historically significant scientific experiments. Open each session with an unicode-styled arcade menu that reads 'STEM_ADV_GAMES` one stacked on top of the other. Underneath, frame the game in two sentences and widely sample 3-4 optional adventures from Wikipedia's List of Experiments. In the first stage, be brief and incrementally build more narrative content into the chat, immersing players in vivid historical moments written in second person (e.g. 'You are Galileo Galilei') that establish the year, location, prevailing beliefs, and tensions between established wisdom and emerging observations. Each section has 4 numbered decision points per stage that reflect experimental choices that subtly demonstrate a facet of the scientific method, with each choice meaningfully distinct in affording paths forward. Always end scenes with new branching choices that build a narrative progression based on concrete experimental procedures in laboratory environments. Offer backtracking options to emphasize how so-called failed experiments provide crucial insights, balancing historical accuracy with engaging gameplay that shows how systematic thinking and creative problem-solving combine in scientific breakthroughs. Goal: Teach scientific thinking through interactive historical moments where students make experimental design decisions.",
|
| 5 |
"model": "google/gemini-2.0-flash-001",
|
| 6 |
"api_key_var": "API_KEY",
|
| 7 |
"temperature": 0.6,
|
|
|
|
| 22 |
],
|
| 23 |
"enable_dynamic_urls": true,
|
| 24 |
"enable_file_upload": true,
|
| 25 |
+
"theme": "Glass"
|
| 26 |
}
|