milwright commited on
Commit
f26741d
·
verified ·
1 Parent(s): 24026d5

Upload 3 files

Browse files
Files changed (2) hide show
  1. app.py +219 -39
  2. config.json +6 -6
app.py CHANGED
@@ -12,14 +12,34 @@ import urllib.parse
12
  # Configuration
13
  SPACE_NAME = "AI Assistant"
14
  SPACE_DESCRIPTION = "A customizable AI assistant"
15
- SYSTEM_PROMPT = """You are an AI assistant specialized in mathematics and statistics who guides users through problem-solving rather than providing direct answers. You help users discover solutions by asking strategic questions ('What do we know so far?' 'What method might apply here?' 'Can you identify a pattern?'), prompting them to explain their reasoning, and offering hints that build on their current understanding. Format all mathematical expressions in LaTeX (inline: $x^2 + y^2 = r^2$, display: $$\int_a^b f(x)dx$$). When users are stuck, provide scaffolded support: suggest examining simpler cases, identifying relevant formulas or theorems, or breaking the problem into smaller parts. Use multiple representations to illuminate different aspects of the problem, validate partial progress to build confidence, and help users recognize and correct their own errors through targeted questions rather than corrections. Your goal is to develop problem-solving skills and mathematical reasoning, not just arrive at answers."""
16
- MODEL = "anthropic/claude-3.5-sonnet"
17
- THEME = "Monochrome" # Gradio theme name
18
- GROUNDING_URLS = ["https://www.cmor-faculty.rice.edu/~heinken/latex/symbols.pdf"]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
19
  # Get access code from environment variable for security
20
  # If SPACE_ACCESS_CODE is not set, no access control is applied
21
  ACCESS_CODE = os.environ.get("SPACE_ACCESS_CODE")
22
- ENABLE_DYNAMIC_URLS = False
23
 
24
  # Get API key from environment - customizable variable name with validation
25
  API_KEY = os.environ.get("OPENROUTER_API_KEY")
@@ -463,41 +483,22 @@ def get_configuration_status():
463
  """Generate a configuration status message for display"""
464
  status_parts = []
465
 
 
466
  if API_KEY_VALID:
467
- status_parts.append("✅ **API Key:** Configured and valid")
468
  else:
469
- status_parts.append("❌ **API Key:** Not configured - Set `OPENROUTER_API_KEY` in Space secrets")
470
 
471
- status_parts.append(f"🤖 **Model:** {MODEL}")
472
- status_parts.append(f"🌡️ **Temperature:** 0.7")
473
- status_parts.append(f"📝 **Max Tokens:** 750")
474
 
475
- # URL Grounding details
476
  if GROUNDING_URLS:
477
- status_parts.append(f"🔗 **URL Grounding:** {len(GROUNDING_URLS)} URLs configured")
478
- # Show first few URLs as examples
479
- for i, url in enumerate(GROUNDING_URLS[:3], 1):
480
- priority_label = "Primary" if i <= 2 else "Secondary"
481
- status_parts.append(f" - [{priority_label}] {url}")
482
- if len(GROUNDING_URLS) > 3:
483
- status_parts.append(f" - ... and {len(GROUNDING_URLS) - 3} more URLs")
484
- else:
485
- status_parts.append("🔗 **URL Grounding:** No URLs configured")
486
-
487
- if ENABLE_DYNAMIC_URLS:
488
- status_parts.append("🔄 **Dynamic URLs:** Enabled")
489
- else:
490
- status_parts.append("🔄 **Dynamic URLs:** Disabled")
491
 
 
492
  if ACCESS_CODE is not None:
493
- status_parts.append("🔐 **Access Control:** Enabled")
494
- else:
495
- status_parts.append("🌐 **Access:** Public Chatbot")
496
-
497
- # System Prompt (add at the end)
498
- status_parts.append("") # Empty line for spacing
499
- status_parts.append("**System Prompt:**")
500
- status_parts.append(f"{SYSTEM_PROMPT}")
501
 
502
  return "\n".join(status_parts)
503
 
@@ -527,14 +528,17 @@ with gr.Blocks(title=SPACE_NAME, theme=theme_class()) as demo:
527
  fn=store_and_generate_response, # Use wrapper function to store history
528
  title="", # Title already shown above
529
  description="", # Description already shown above
530
- examples=["I'm stuck on a math problem. Can you help?", 'What is Bayesian statistics and can you provide an example?'],
531
  type="messages" # Use modern message format for better compatibility
532
  )
533
 
534
- # Export functionality
535
  with gr.Row():
536
- export_btn = gr.Button("📥 Export Conversation", variant="secondary", size="sm")
537
- export_file = gr.File(label="Download Conversation", visible=False)
 
 
 
538
 
539
  # Connect export functionality
540
  export_btn.click(
@@ -542,8 +546,8 @@ with gr.Blocks(title=SPACE_NAME, theme=theme_class()) as demo:
542
  outputs=[export_file]
543
  )
544
 
545
- # Configuration status (always visible)
546
- with gr.Accordion("📊 Configuration Status", open=not API_KEY_VALID):
547
  gr.Markdown(get_configuration_status())
548
 
549
  # Connect access verification
@@ -559,5 +563,181 @@ with gr.Blocks(title=SPACE_NAME, theme=theme_class()) as demo:
559
  outputs=[access_error, chat_section, access_granted]
560
  )
561
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
562
  if __name__ == "__main__":
563
  demo.launch()
 
12
  # Configuration
13
  SPACE_NAME = "AI Assistant"
14
  SPACE_DESCRIPTION = "A customizable AI assistant"
15
+
16
+ # Default configuration values
17
+ DEFAULT_SYSTEM_PROMPT = """You are a research aid specializing in academic literature search and analysis. Your expertise spans discovering peer-reviewed sources, assessing research methodologies, synthesizing findings across studies, and delivering properly formatted citations. When responding, anchor claims in specific sources from provided URL contexts, differentiate between direct evidence and interpretive analysis, and note any limitations or contradictory results. Employ clear, accessible language that demystifies complex research, and propose connected research directions when appropriate. Your purpose is to serve as an informed research tool supporting users through initial concept development, exploratory investigation, information collection, and source compilation."""
18
+ DEFAULT_TEMPERATURE = 0.7
19
+ DEFAULT_MAX_TOKENS = 750
20
+
21
+ # Try to load configuration from file (if modified by faculty)
22
+ try:
23
+ with open('config.json', 'r') as f:
24
+ saved_config = json.load(f)
25
+ SYSTEM_PROMPT = saved_config.get('system_prompt', DEFAULT_SYSTEM_PROMPT)
26
+ temperature = saved_config.get('temperature', DEFAULT_TEMPERATURE)
27
+ max_tokens = saved_config.get('max_tokens', DEFAULT_MAX_TOKENS)
28
+ print("✅ Loaded configuration from config.json")
29
+ except:
30
+ # Use defaults if no config file or error
31
+ SYSTEM_PROMPT = DEFAULT_SYSTEM_PROMPT
32
+ temperature = DEFAULT_TEMPERATURE
33
+ max_tokens = DEFAULT_MAX_TOKENS
34
+ print("ℹ️ Using default configuration")
35
+
36
+ MODEL = "google/gemini-2.0-flash-001"
37
+ THEME = "Default" # Gradio theme name
38
+ GROUNDING_URLS = []
39
  # Get access code from environment variable for security
40
  # If SPACE_ACCESS_CODE is not set, no access control is applied
41
  ACCESS_CODE = os.environ.get("SPACE_ACCESS_CODE")
42
+ ENABLE_DYNAMIC_URLS = True
43
 
44
  # Get API key from environment - customizable variable name with validation
45
  API_KEY = os.environ.get("OPENROUTER_API_KEY")
 
483
  """Generate a configuration status message for display"""
484
  status_parts = []
485
 
486
+ # API Key status - simplified
487
  if API_KEY_VALID:
488
+ status_parts.append("✅ **API Key:** Ready")
489
  else:
490
+ status_parts.append("❌ **API Key:** Not configured ([set OPENROUTER_API_KEY in settings](https://huggingface.co/docs/hub/spaces-overview#managing-secrets))")
491
 
492
+ # Basic info in a more compact format
493
+ status_parts.append(f"**Model:** {MODEL.split('/')[-1]}") # Show only model name, not provider
 
494
 
495
+ # Only show URLs if configured
496
  if GROUNDING_URLS:
497
+ status_parts.append(f"**Context URLs:** {len(GROUNDING_URLS)} configured")
 
 
 
 
 
 
 
 
 
 
 
 
 
498
 
499
+ # Access info only if restricted
500
  if ACCESS_CODE is not None:
501
+ status_parts.append("🔐 **Access:** Password protected")
 
 
 
 
 
 
 
502
 
503
  return "\n".join(status_parts)
504
 
 
528
  fn=store_and_generate_response, # Use wrapper function to store history
529
  title="", # Title already shown above
530
  description="", # Description already shown above
531
+ examples=['Hello! How can you help me?', 'Tell me something interesting', 'What can you do?'],
532
  type="messages" # Use modern message format for better compatibility
533
  )
534
 
535
+ # Export functionality - subtle placement
536
  with gr.Row():
537
+ with gr.Column(scale=10):
538
+ pass # Empty column for spacing
539
+ with gr.Column(scale=2):
540
+ export_btn = gr.Button("Export", variant="secondary", size="sm")
541
+ export_file = gr.File(label="Download", visible=False)
542
 
543
  # Connect export functionality
544
  export_btn.click(
 
546
  outputs=[export_file]
547
  )
548
 
549
+ # Configuration status - minimized when everything is working
550
+ with gr.Accordion("ℹ️ Status", open=not API_KEY_VALID):
551
  gr.Markdown(get_configuration_status())
552
 
553
  # Connect access verification
 
563
  outputs=[access_error, chat_section, access_granted]
564
  )
565
 
566
+ # Faculty Configuration Section - appears at the bottom with password protection
567
+ with gr.Accordion("🔧 Faculty Configuration", open=False, visible=True) as faculty_section:
568
+ gr.Markdown("**Faculty Only:** Edit assistant configuration. Requires FACULTY_CONFIG_PASSWORD secret.")
569
+
570
+ # Check if faculty password is configured
571
+ FACULTY_PASSWORD = os.environ.get("FACULTY_CONFIG_PASSWORD", "").strip()
572
+
573
+ if FACULTY_PASSWORD:
574
+ faculty_auth_state = gr.State(False)
575
+
576
+ # Authentication row
577
+ with gr.Row() as faculty_auth_row:
578
+ faculty_password_input = gr.Textbox(
579
+ label="Faculty Password",
580
+ type="password",
581
+ placeholder="Enter faculty configuration password"
582
+ )
583
+ faculty_auth_btn = gr.Button("Unlock Configuration", variant="primary")
584
+ faculty_auth_status = gr.Markdown("")
585
+
586
+ # Configuration editor (hidden until authenticated)
587
+ with gr.Column(visible=False) as faculty_config_section:
588
+ gr.Markdown("### Edit Assistant Configuration")
589
+ gr.Markdown("⚠️ **Warning:** Changes will affect all users immediately.")
590
+
591
+ # Load current configuration
592
+ try:
593
+ with open('config.json', 'r') as f:
594
+ current_config = json.load(f)
595
+ except:
596
+ current_config = {
597
+ 'system_prompt': SYSTEM_PROMPT,
598
+ 'temperature': 0.7,
599
+ 'max_tokens': 750,
600
+ 'locked': False
601
+ }
602
+
603
+ # Editable fields
604
+ edit_system_prompt = gr.Textbox(
605
+ label="System Prompt",
606
+ value=current_config.get('system_prompt', SYSTEM_PROMPT),
607
+ lines=5
608
+ )
609
+
610
+ with gr.Row():
611
+ edit_temperature = gr.Slider(
612
+ label="Temperature",
613
+ minimum=0,
614
+ maximum=2,
615
+ value=current_config.get('temperature', 0.7),
616
+ step=0.1
617
+ )
618
+ edit_max_tokens = gr.Slider(
619
+ label="Max Tokens",
620
+ minimum=50,
621
+ maximum=4096,
622
+ value=current_config.get('max_tokens', 750),
623
+ step=50
624
+ )
625
+
626
+ config_locked = gr.Checkbox(
627
+ label="Lock Configuration (Prevent further edits)",
628
+ value=current_config.get('locked', False)
629
+ )
630
+
631
+ with gr.Row():
632
+ save_config_btn = gr.Button("💾 Save Configuration", variant="primary")
633
+ reset_config_btn = gr.Button("↩️ Reset to Defaults", variant="secondary")
634
+
635
+ config_status = gr.Markdown("")
636
+
637
+ # Faculty authentication function
638
+ def verify_faculty_password(password):
639
+ if password == FACULTY_PASSWORD:
640
+ return (
641
+ gr.update(value="✅ Authentication successful!"),
642
+ gr.update(visible=False), # Hide auth row
643
+ gr.update(visible=True), # Show config section
644
+ True # Update auth state
645
+ )
646
+ else:
647
+ return (
648
+ gr.update(value="❌ Invalid password"),
649
+ gr.update(visible=True), # Keep auth row visible
650
+ gr.update(visible=False), # Keep config hidden
651
+ False # Auth failed
652
+ )
653
+
654
+ # Save configuration function
655
+ def save_configuration(new_prompt, new_temp, new_tokens, lock_config, is_authenticated):
656
+ if not is_authenticated:
657
+ return "❌ Not authenticated"
658
+
659
+ # Check if configuration is already locked
660
+ try:
661
+ with open('config.json', 'r') as f:
662
+ existing_config = json.load(f)
663
+ if existing_config.get('locked', False):
664
+ return "🔒 Configuration is locked and cannot be modified"
665
+ except:
666
+ pass
667
+
668
+ # Save new configuration
669
+ new_config = {
670
+ 'system_prompt': new_prompt,
671
+ 'temperature': new_temp,
672
+ 'max_tokens': int(new_tokens),
673
+ 'locked': lock_config,
674
+ 'last_modified': datetime.now().isoformat(),
675
+ 'modified_by': 'faculty'
676
+ }
677
+
678
+ try:
679
+ with open('config.json', 'w') as f:
680
+ json.dump(new_config, f, indent=2)
681
+
682
+ # Update global variables
683
+ global SYSTEM_PROMPT, temperature, max_tokens
684
+ SYSTEM_PROMPT = new_prompt
685
+ temperature = new_temp
686
+ max_tokens = int(new_tokens)
687
+
688
+ return f"✅ Configuration saved successfully at {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}"
689
+ except Exception as e:
690
+ return f"❌ Error saving configuration: {str(e)}"
691
+
692
+ # Reset configuration function
693
+ def reset_configuration(is_authenticated):
694
+ if not is_authenticated:
695
+ return "❌ Not authenticated", gr.update(), gr.update(), gr.update()
696
+
697
+ # Check if locked
698
+ try:
699
+ with open('config.json', 'r') as f:
700
+ existing_config = json.load(f)
701
+ if existing_config.get('locked', False):
702
+ return "🔒 Configuration is locked", gr.update(), gr.update(), gr.update()
703
+ except:
704
+ pass
705
+
706
+ # Reset to original values
707
+ return (
708
+ "↩️ Reset to default values",
709
+ gr.update(value=SYSTEM_PROMPT),
710
+ gr.update(value=0.7),
711
+ gr.update(value=750)
712
+ )
713
+
714
+ # Connect authentication
715
+ faculty_auth_btn.click(
716
+ verify_faculty_password,
717
+ inputs=[faculty_password_input],
718
+ outputs=[faculty_auth_status, faculty_auth_row, faculty_config_section, faculty_auth_state]
719
+ )
720
+
721
+ faculty_password_input.submit(
722
+ verify_faculty_password,
723
+ inputs=[faculty_password_input],
724
+ outputs=[faculty_auth_status, faculty_auth_row, faculty_config_section, faculty_auth_state]
725
+ )
726
+
727
+ # Connect configuration buttons
728
+ save_config_btn.click(
729
+ save_configuration,
730
+ inputs=[edit_system_prompt, edit_temperature, edit_max_tokens, config_locked, faculty_auth_state],
731
+ outputs=[config_status]
732
+ )
733
+
734
+ reset_config_btn.click(
735
+ reset_configuration,
736
+ inputs=[faculty_auth_state],
737
+ outputs=[config_status, edit_system_prompt, edit_temperature, edit_max_tokens]
738
+ )
739
+ else:
740
+ gr.Markdown("ℹ️ Faculty configuration is not enabled. Set FACULTY_CONFIG_PASSWORD in Space secrets to enable.")
741
+
742
  if __name__ == "__main__":
743
  demo.launch()
config.json CHANGED
@@ -1,13 +1,13 @@
1
  {
2
  "name": "AI Assistant",
3
  "description": "A customizable AI assistant",
4
- "system_prompt": "You are an AI assistant specialized in mathematics and statistics who guides users through problem-solving rather than providing direct answers. You help users discover solutions by asking strategic questions ('What do we know so far?' 'What method might apply here?' 'Can you identify a pattern?'), prompting them to explain their reasoning, and offering hints that build on their current understanding. Format all mathematical expressions in LaTeX (inline: $x^2 + y^2 = r^2$, display: $$\\int_a^b f(x)dx$$). When users are stuck, provide scaffolded support: suggest examining simpler cases, identifying relevant formulas or theorems, or breaking the problem into smaller parts. Use multiple representations to illuminate different aspects of the problem, validate partial progress to build confidence, and help users recognize and correct their own errors through targeted questions rather than corrections. Your goal is to develop problem-solving skills and mathematical reasoning, not just arrive at answers.",
5
- "model": "anthropic/claude-3.5-sonnet",
6
  "api_key_var": "OPENROUTER_API_KEY",
7
  "temperature": 0.7,
8
  "max_tokens": 750,
9
- "examples": "[\"I'm stuck on a math problem. Can you help?\", 'What is Bayesian statistics and can you provide an example?']",
10
- "grounding_urls": "[\"https://www.cmor-faculty.rice.edu/~heinken/latex/symbols.pdf\"]",
11
- "enable_dynamic_urls": false,
12
- "theme": "Monochrome"
13
  }
 
1
  {
2
  "name": "AI Assistant",
3
  "description": "A customizable AI assistant",
4
+ "system_prompt": "You are a research aid specializing in academic literature search and analysis. Your expertise spans discovering peer-reviewed sources, assessing research methodologies, synthesizing findings across studies, and delivering properly formatted citations. When responding, anchor claims in specific sources from provided URL contexts, differentiate between direct evidence and interpretive analysis, and note any limitations or contradictory results. Employ clear, accessible language that demystifies complex research, and propose connected research directions when appropriate. Your purpose is to serve as an informed research tool supporting users through initial concept development, exploratory investigation, information collection, and source compilation.",
5
+ "model": "google/gemini-2.0-flash-001",
6
  "api_key_var": "OPENROUTER_API_KEY",
7
  "temperature": 0.7,
8
  "max_tokens": 750,
9
+ "examples": "['Hello! How can you help me?', 'Tell me something interesting', 'What can you do?']",
10
+ "grounding_urls": "[]",
11
+ "enable_dynamic_urls": true,
12
+ "theme": "Default"
13
  }