Upload 2 files
Browse files- app.py +234 -206
- config.json +5 -3
app.py
CHANGED
@@ -10,39 +10,68 @@ import urllib.parse
|
|
10 |
|
11 |
|
12 |
# Configuration
|
13 |
-
SPACE_NAME =
|
14 |
-
SPACE_DESCRIPTION =
|
15 |
|
16 |
-
# Default configuration values
|
17 |
-
|
18 |
-
|
19 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
20 |
|
21 |
-
#
|
22 |
-
|
23 |
-
|
24 |
-
|
25 |
-
|
26 |
-
|
27 |
-
|
28 |
-
|
29 |
-
except:
|
30 |
-
|
31 |
-
|
32 |
-
|
33 |
-
|
34 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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 ACCESS_CODE is not set, no access control is applied
|
41 |
ACCESS_CODE = os.environ.get("ACCESS_CODE")
|
42 |
-
ENABLE_DYNAMIC_URLS = True
|
43 |
|
44 |
# Get API key from environment - customizable variable name with validation
|
45 |
-
|
|
|
46 |
if API_KEY:
|
47 |
API_KEY = API_KEY.strip() # Remove any whitespace
|
48 |
if not API_KEY: # Check if empty after stripping
|
@@ -53,21 +82,21 @@ def validate_api_key():
|
|
53 |
"""Validate API key configuration with detailed logging"""
|
54 |
if not API_KEY:
|
55 |
print(f"β οΈ API KEY CONFIGURATION ERROR:")
|
56 |
-
print(f" Variable name:
|
57 |
print(f" Status: Not set or empty")
|
58 |
-
print(f" Action needed: Set '
|
59 |
print(f" Expected format: sk-or-xxxxxxxxxx")
|
60 |
return False
|
61 |
elif not API_KEY.startswith('sk-or-'):
|
62 |
print(f"β οΈ API KEY FORMAT WARNING:")
|
63 |
-
print(f" Variable name:
|
64 |
-
print(f" Current value: {API_KEY[:10]}..." if len(API_KEY) > 10 else API_KEY)
|
65 |
print(f" Expected format: sk-or-xxxxxxxxxx")
|
66 |
print(f" Note: OpenRouter keys should start with 'sk-or-'")
|
67 |
return True # Still try to use it
|
68 |
else:
|
69 |
print(f"β
API Key configured successfully")
|
70 |
-
print(f" Variable:
|
71 |
print(f" Format: Valid OpenRouter key")
|
72 |
return True
|
73 |
|
@@ -181,7 +210,10 @@ def get_grounding_context():
|
|
181 |
context_parts.append(f"[{priority_label}] Context from URL {i} ({url}):\n{content}")
|
182 |
|
183 |
if context_parts:
|
184 |
-
result = "\n\
|
|
|
|
|
|
|
185 |
else:
|
186 |
result = ""
|
187 |
|
@@ -195,7 +227,7 @@ def export_conversation_to_markdown(conversation_history):
|
|
195 |
return "No conversation to export."
|
196 |
|
197 |
markdown_content = f"""# Conversation Export
|
198 |
-
Generated on: {datetime.now().strftime('
|
199 |
|
200 |
---
|
201 |
|
@@ -209,17 +241,21 @@ Generated on: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}
|
|
209 |
|
210 |
if role == 'user':
|
211 |
message_pair_count += 1
|
212 |
-
markdown_content += f"## User Message {message_pair_count}\n\n{content}\n\
|
|
|
213 |
elif role == 'assistant':
|
214 |
-
markdown_content += f"## Assistant Response {message_pair_count}\n\n{content}\n\n---\n\
|
|
|
215 |
elif isinstance(message, (list, tuple)) and len(message) >= 2:
|
216 |
# Handle legacy tuple format: ["user msg", "assistant msg"]
|
217 |
message_pair_count += 1
|
218 |
user_msg, assistant_msg = message[0], message[1]
|
219 |
if user_msg:
|
220 |
-
markdown_content += f"## User Message {message_pair_count}\n\n{user_msg}\n\
|
|
|
221 |
if assistant_msg:
|
222 |
-
markdown_content += f"## Assistant Response {message_pair_count}\n\n{assistant_msg}\n\n---\n\
|
|
|
223 |
|
224 |
return markdown_content
|
225 |
|
@@ -229,14 +265,20 @@ def generate_response(message, history):
|
|
229 |
|
230 |
# Enhanced API key validation with helpful messages
|
231 |
if not API_KEY:
|
232 |
-
error_msg = f"π **API Key Required**\n\
|
233 |
-
|
234 |
-
error_msg += f"
|
235 |
-
|
236 |
-
error_msg += f"
|
237 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
238 |
error_msg += f"Get your API key at: https://openrouter.ai/keys"
|
239 |
-
print(f"β API request failed: No API key configured for
|
240 |
return error_msg
|
241 |
|
242 |
# Get grounding context
|
@@ -253,7 +295,8 @@ def generate_response(message, history):
|
|
253 |
content = fetch_url_content(url)
|
254 |
dynamic_context_parts.append(f"\n\nDynamic context from {url}:\n{content}")
|
255 |
if dynamic_context_parts:
|
256 |
-
grounding_context += "\
|
|
|
257 |
|
258 |
# Build enhanced system prompt with grounding context
|
259 |
enhanced_system_prompt = SYSTEM_PROMPT + grounding_context
|
@@ -331,19 +374,28 @@ def generate_response(message, history):
|
|
331 |
print(f"β Failed to parse API response: {str(e)}")
|
332 |
return f"API Error: Failed to parse response - {str(e)}"
|
333 |
elif response.status_code == 401:
|
334 |
-
error_msg = f"π **Authentication Error**\n\
|
335 |
-
|
336 |
-
error_msg += f"
|
337 |
-
|
338 |
-
error_msg += f"
|
339 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
340 |
error_msg += f"4. Check that you have credits on your OpenRouter account"
|
341 |
print(f"β API authentication failed: {response.status_code} - {response.text[:200]}")
|
342 |
return error_msg
|
343 |
elif response.status_code == 429:
|
344 |
-
error_msg = f"β±οΈ **Rate Limit Exceeded**\n\
|
345 |
-
|
346 |
-
error_msg += f"
|
|
|
|
|
|
|
347 |
error_msg += f"1. Wait 30-60 seconds before trying again\n"
|
348 |
error_msg += f"2. Check your OpenRouter usage limits\n"
|
349 |
error_msg += f"3. Consider upgrading your OpenRouter plan"
|
@@ -356,17 +408,21 @@ def generate_response(message, history):
|
|
356 |
except:
|
357 |
error_message = response.text
|
358 |
|
359 |
-
error_msg = f"β οΈ **Request Error**\n\
|
|
|
360 |
error_msg += f"The API request was invalid:\n"
|
361 |
-
error_msg += f"`{error_message}`\n\
|
|
|
362 |
if "model" in error_message.lower():
|
363 |
error_msg += f"**Model Issue:** The model `{MODEL}` may not be available.\n"
|
364 |
error_msg += f"Try switching to a different model in your Space configuration."
|
365 |
print(f"β Bad request: {response.status_code} - {error_message}")
|
366 |
return error_msg
|
367 |
else:
|
368 |
-
error_msg = f"π« **API Error {response.status_code}**\n\
|
369 |
-
|
|
|
|
|
370 |
error_msg += f"If this persists, check:\n"
|
371 |
error_msg += f"1. OpenRouter service status\n"
|
372 |
error_msg += f"2. Your API key and credits\n"
|
@@ -375,8 +431,10 @@ def generate_response(message, history):
|
|
375 |
return error_msg
|
376 |
|
377 |
except requests.exceptions.Timeout:
|
378 |
-
error_msg = f"β° **Request Timeout**\n\
|
379 |
-
|
|
|
|
|
380 |
error_msg += f"**Troubleshooting:**\n"
|
381 |
error_msg += f"1. Try again with a shorter message\n"
|
382 |
error_msg += f"2. Check your internet connection\n"
|
@@ -384,8 +442,10 @@ def generate_response(message, history):
|
|
384 |
print(f"β Request timeout after 30 seconds")
|
385 |
return error_msg
|
386 |
except requests.exceptions.ConnectionError:
|
387 |
-
error_msg = f"π **Connection Error**\n\
|
388 |
-
|
|
|
|
|
389 |
error_msg += f"**Troubleshooting:**\n"
|
390 |
error_msg += f"1. Check your internet connection\n"
|
391 |
error_msg += f"2. Check OpenRouter service status\n"
|
@@ -393,10 +453,12 @@ def generate_response(message, history):
|
|
393 |
print(f"β Connection error to OpenRouter API")
|
394 |
return error_msg
|
395 |
except Exception as e:
|
396 |
-
error_msg =
|
397 |
-
|
398 |
-
error_msg +=
|
399 |
-
error_msg += f"
|
|
|
|
|
400 |
print(f"β Unexpected error: {str(e)}")
|
401 |
return error_msg
|
402 |
|
@@ -480,46 +542,53 @@ def export_conversation(history):
|
|
480 |
|
481 |
# Configuration status display
|
482 |
def get_configuration_status():
|
483 |
-
"""Generate a configuration status message for display"""
|
484 |
status_parts = []
|
485 |
|
486 |
-
#
|
487 |
-
status_parts.append("
|
488 |
-
|
489 |
-
|
490 |
-
else:
|
491 |
-
status_parts.append("β **API Key:** Not configured")
|
492 |
-
status_parts.append(" Set `API_KEY` in Space secrets")
|
493 |
-
|
494 |
-
# Model and parameters
|
495 |
-
status_parts.append("") # Blank line
|
496 |
-
status_parts.append("### π€ Model Settings")
|
497 |
-
status_parts.append(f"**Model:** {MODEL.split('/')[-1]}")
|
498 |
status_parts.append(f"**Temperature:** 0.7")
|
499 |
-
status_parts.append(f"**Max Tokens:** 750")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
500 |
|
501 |
# URL Context if configured
|
502 |
-
if GROUNDING_URLS:
|
503 |
-
status_parts.append("")
|
504 |
-
status_parts.append("
|
505 |
-
|
506 |
-
|
507 |
-
|
508 |
-
|
509 |
-
|
510 |
-
|
511 |
-
|
512 |
-
|
513 |
-
|
514 |
-
|
515 |
-
|
516 |
-
|
517 |
-
|
518 |
-
status_parts.append("") # Blank line
|
519 |
-
status_parts.append("### π System Prompt")
|
520 |
-
# Show first 200 chars of system prompt
|
521 |
-
prompt_preview = SYSTEM_PROMPT[:200] + "..." if len(SYSTEM_PROMPT) > 200 else SYSTEM_PROMPT
|
522 |
-
status_parts.append(f"```\n{prompt_preview}\n```")
|
523 |
|
524 |
return "\n".join(status_parts)
|
525 |
|
@@ -545,11 +614,20 @@ with gr.Blocks(title=SPACE_NAME, theme=theme_class()) as demo:
|
|
545 |
|
546 |
# Main chat interface (hidden until access granted)
|
547 |
with gr.Column(visible=(ACCESS_CODE is None)) as chat_section:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
548 |
chat_interface = gr.ChatInterface(
|
549 |
fn=store_and_generate_response, # Use wrapper function to store history
|
550 |
title="", # Title already shown above
|
551 |
description="", # Description already shown above
|
552 |
-
examples=
|
553 |
type="messages" # Use modern message format for better compatibility
|
554 |
)
|
555 |
|
@@ -564,6 +642,9 @@ with gr.Blocks(title=SPACE_NAME, theme=theme_class()) as demo:
|
|
564 |
outputs=[export_file]
|
565 |
)
|
566 |
|
|
|
|
|
|
|
567 |
|
568 |
# Connect access verification
|
569 |
if ACCESS_CODE is not None:
|
@@ -610,29 +691,11 @@ with gr.Blocks(title=SPACE_NAME, theme=theme_class()) as demo:
|
|
610 |
with open('config.json', 'r') as f:
|
611 |
current_config = json.load(f)
|
612 |
except:
|
613 |
-
|
614 |
-
|
615 |
-
'temperature': 0.7,
|
616 |
-
'max_tokens': 750,
|
617 |
-
'locked': False
|
618 |
-
}
|
619 |
-
|
620 |
-
# Editable fields - Order matches the Configuration tab
|
621 |
-
# 1. Assistant Identity
|
622 |
-
edit_name = gr.Textbox(
|
623 |
-
label="Assistant Name",
|
624 |
-
value=current_config.get('name', SPACE_NAME),
|
625 |
-
placeholder="My AI Assistant"
|
626 |
-
)
|
627 |
-
|
628 |
-
edit_description = gr.Textbox(
|
629 |
-
label="Assistant Description",
|
630 |
-
value=current_config.get('description', SPACE_DESCRIPTION),
|
631 |
-
lines=2,
|
632 |
-
placeholder="A helpful AI assistant for..."
|
633 |
-
)
|
634 |
|
635 |
-
#
|
|
|
636 |
edit_system_prompt = gr.Textbox(
|
637 |
label="System Prompt",
|
638 |
value=current_config.get('system_prompt', SYSTEM_PROMPT),
|
@@ -654,8 +717,8 @@ with gr.Blocks(title=SPACE_NAME, theme=theme_class()) as demo:
|
|
654 |
],
|
655 |
value=current_config.get('model', MODEL)
|
656 |
)
|
657 |
-
|
658 |
-
# 4. Example
|
659 |
examples_value = current_config.get('examples', [])
|
660 |
if isinstance(examples_value, list):
|
661 |
examples_text_value = "\n".join(examples_value)
|
@@ -686,7 +749,7 @@ with gr.Blocks(title=SPACE_NAME, theme=theme_class()) as demo:
|
|
686 |
step=50
|
687 |
)
|
688 |
|
689 |
-
#
|
690 |
gr.Markdown("### URL Grounding")
|
691 |
grounding_urls_value = current_config.get('grounding_urls', [])
|
692 |
if isinstance(grounding_urls_value, str):
|
@@ -713,8 +776,8 @@ with gr.Blocks(title=SPACE_NAME, theme=theme_class()) as demo:
|
|
713 |
)
|
714 |
|
715 |
with gr.Row():
|
716 |
-
save_config_btn = gr.Button("
|
717 |
-
reset_config_btn = gr.Button("
|
718 |
|
719 |
config_status = gr.Markdown("")
|
720 |
|
@@ -722,39 +785,30 @@ with gr.Blocks(title=SPACE_NAME, theme=theme_class()) as demo:
|
|
722 |
def verify_faculty_password(password):
|
723 |
if password == FACULTY_PASSWORD:
|
724 |
return (
|
725 |
-
gr.update(value="
|
726 |
gr.update(visible=False), # Hide auth row
|
727 |
gr.update(visible=True), # Show config section
|
728 |
True # Update auth state
|
729 |
)
|
730 |
else:
|
731 |
return (
|
732 |
-
gr.update(value="
|
733 |
gr.update(visible=True), # Keep auth row visible
|
734 |
gr.update(visible=False), # Keep config hidden
|
735 |
False # Auth failed
|
736 |
)
|
737 |
|
738 |
# Save configuration function
|
739 |
-
def save_configuration(
|
740 |
-
# Extract URL values, lock_config, and is_authenticated from args
|
741 |
-
# args should contain: url1, url2, ..., url10, lock_config, is_authenticated
|
742 |
-
if len(args) < 12: # Need at least 10 URLs + lock_config + is_authenticated
|
743 |
-
return "β Invalid number of parameters"
|
744 |
-
|
745 |
-
url_values = args[:10] # First 10 are URLs
|
746 |
-
lock_config = args[10] # 11th is lock_config
|
747 |
-
is_authenticated = args[11] # 12th is is_authenticated
|
748 |
-
|
749 |
if not is_authenticated:
|
750 |
-
return "
|
751 |
|
752 |
# Check if configuration is already locked
|
753 |
try:
|
754 |
with open('config.json', 'r') as f:
|
755 |
existing_config = json.load(f)
|
756 |
if existing_config.get('locked', False):
|
757 |
-
return "
|
758 |
except:
|
759 |
pass
|
760 |
|
@@ -763,26 +817,27 @@ with gr.Blocks(title=SPACE_NAME, theme=theme_class()) as demo:
|
|
763 |
with open('config.json', 'r') as f:
|
764 |
current_full_config = json.load(f)
|
765 |
except:
|
766 |
-
# If config.json doesn't exist, use
|
767 |
-
current_full_config =
|
768 |
|
769 |
# Process example prompts
|
770 |
examples_list = [ex.strip() for ex in new_examples.split('\n') if ex.strip()]
|
771 |
|
772 |
-
# Process URL values
|
773 |
-
|
|
|
|
|
|
|
774 |
|
775 |
# Update all editable fields while preserving everything else
|
776 |
current_full_config.update({
|
777 |
-
'name': new_name,
|
778 |
-
'description': new_description,
|
779 |
'system_prompt': new_prompt,
|
780 |
'model': new_model,
|
781 |
'examples': examples_list,
|
782 |
'temperature': new_temp,
|
783 |
'max_tokens': int(new_tokens),
|
784 |
'grounding_urls': grounding_urls,
|
785 |
-
'locked':
|
786 |
'last_modified': datetime.now().isoformat(),
|
787 |
'last_modified_by': 'faculty'
|
788 |
})
|
@@ -791,61 +846,34 @@ with gr.Blocks(title=SPACE_NAME, theme=theme_class()) as demo:
|
|
791 |
with open('config.json', 'w') as f:
|
792 |
json.dump(current_full_config, f, indent=2)
|
793 |
|
794 |
-
#
|
795 |
-
|
796 |
-
|
797 |
-
SPACE_DESCRIPTION = current_full_config.get('description', SPACE_DESCRIPTION)
|
798 |
-
SYSTEM_PROMPT = current_full_config.get('system_prompt', SYSTEM_PROMPT)
|
799 |
-
MODEL = current_full_config.get('model', MODEL)
|
800 |
-
temperature = current_full_config.get('temperature', temperature)
|
801 |
-
max_tokens = current_full_config.get('max_tokens', max_tokens)
|
802 |
-
GROUNDING_URLS = current_full_config.get('grounding_urls', GROUNDING_URLS)
|
803 |
|
804 |
-
|
805 |
-
|
806 |
-
|
807 |
-
|
808 |
-
|
809 |
-
|
810 |
-
|
811 |
-
|
812 |
-
|
813 |
-
|
814 |
-
|
815 |
-
|
816 |
-
|
817 |
-
|
818 |
-
|
819 |
-
old_examples = config.get('examples', [])
|
820 |
-
if examples_list != old_examples:
|
821 |
-
restart_required = True
|
822 |
-
restart_reasons.append("Example prompts")
|
823 |
-
|
824 |
-
# Build response message
|
825 |
-
response_msg = f"β
Configuration saved successfully at {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}"
|
826 |
-
|
827 |
-
# Add restart instructions if needed
|
828 |
-
if restart_required:
|
829 |
-
response_msg += f"\n\nπ **Restart Required**\n"
|
830 |
-
response_msg += f"The following changes require a Space restart to take effect:\n"
|
831 |
-
for reason in restart_reasons:
|
832 |
-
response_msg += f"β’ {reason}\n"
|
833 |
-
response_msg += "\n**To restart your Space:**\n"
|
834 |
-
response_msg += "1. Go to your Space settings (βοΈ icon)\n"
|
835 |
-
response_msg += "2. Click 'Factory reboot' for a complete restart\n"
|
836 |
-
response_msg += "3. Wait ~30 seconds for the Space to reload\n"
|
837 |
-
response_msg += "\n*Note: System prompt, model, temperature, and URL changes take effect immediately for new conversations.*"
|
838 |
else:
|
839 |
-
|
840 |
-
|
841 |
-
return response_msg
|
842 |
except Exception as e:
|
843 |
return f"β Error saving configuration: {str(e)}"
|
844 |
|
845 |
# Reset configuration function
|
846 |
def reset_configuration(is_authenticated):
|
847 |
if not is_authenticated:
|
848 |
-
updates = ["
|
849 |
return tuple(updates)
|
850 |
|
851 |
# Check if locked
|
@@ -853,7 +881,7 @@ with gr.Blocks(title=SPACE_NAME, theme=theme_class()) as demo:
|
|
853 |
with open('config.json', 'r') as f:
|
854 |
existing_config = json.load(f)
|
855 |
if existing_config.get('locked', False):
|
856 |
-
updates = ["
|
857 |
return tuple(updates)
|
858 |
except:
|
859 |
pass
|
@@ -865,20 +893,20 @@ with gr.Blocks(title=SPACE_NAME, theme=theme_class()) as demo:
|
|
865 |
else:
|
866 |
examples_text = ""
|
867 |
|
868 |
-
# Get default URLs
|
869 |
default_urls = DEFAULT_CONFIG.get('grounding_urls', [])
|
870 |
if isinstance(default_urls, str):
|
871 |
try:
|
872 |
-
import
|
873 |
-
default_urls =
|
874 |
except:
|
875 |
default_urls = []
|
|
|
|
|
876 |
|
877 |
# Reset to original default values
|
878 |
updates = [
|
879 |
-
"
|
880 |
-
gr.update(value=DEFAULT_CONFIG.get('name', SPACE_NAME)),
|
881 |
-
gr.update(value=DEFAULT_CONFIG.get('description', SPACE_DESCRIPTION)),
|
882 |
gr.update(value=DEFAULT_CONFIG.get('system_prompt', SYSTEM_PROMPT)),
|
883 |
gr.update(value=DEFAULT_CONFIG.get('model', MODEL)),
|
884 |
gr.update(value=examples_text),
|
@@ -909,17 +937,17 @@ with gr.Blocks(title=SPACE_NAME, theme=theme_class()) as demo:
|
|
909 |
# Connect configuration buttons
|
910 |
save_config_btn.click(
|
911 |
save_configuration,
|
912 |
-
inputs=[
|
913 |
outputs=[config_status]
|
914 |
)
|
915 |
|
916 |
reset_config_btn.click(
|
917 |
reset_configuration,
|
918 |
inputs=[faculty_auth_state],
|
919 |
-
outputs=[config_status,
|
920 |
)
|
921 |
else:
|
922 |
-
gr.Markdown("
|
923 |
|
924 |
if __name__ == "__main__":
|
925 |
demo.launch()
|
|
|
10 |
|
11 |
|
12 |
# Configuration
|
13 |
+
SPACE_NAME = 'AI Assistant'
|
14 |
+
SPACE_DESCRIPTION = 'A customizable AI assistant'
|
15 |
|
16 |
+
# Default configuration values (used only if config.json is missing)
|
17 |
+
DEFAULT_CONFIG = {{
|
18 |
+
'name': SPACE_NAME,
|
19 |
+
'description': SPACE_DESCRIPTION,
|
20 |
+
'system_prompt': "You are a pedagogically-minded academic assistant designed for introductory courses. Your approach follows constructivist learning principles: build on students' prior knowledge, scaffold complex concepts through graduated questioning, and use Socratic dialogue to guide discovery. Provide concise, evidence-based explanations that connect theory to lived experiences. Each response should model critical thinking by acknowledging multiple perspectives, identifying assumptions, and revealing conceptual relationships. Conclude with open-ended questions that promote higher-order thinkingβanalysis, synthesis, or evaluationβrather than recall.",
|
21 |
+
'temperature': 0.7,
|
22 |
+
'max_tokens': 750,
|
23 |
+
'model': 'google/gemini-2.0-flash-001',
|
24 |
+
'api_key_var': 'API_KEY',
|
25 |
+
'theme': 'Default',
|
26 |
+
'grounding_urls': '[]',
|
27 |
+
'enable_dynamic_urls': True,
|
28 |
+
'examples': ['Can you help me understand why the sky is blue?'],
|
29 |
+
'locked': False
|
30 |
+
}}
|
31 |
|
32 |
+
# Load configuration from file - this is the single source of truth
|
33 |
+
def load_config():
|
34 |
+
"""Load configuration from config.json with fallback to defaults"""
|
35 |
+
try:
|
36 |
+
with open('config.json', 'r') as f:
|
37 |
+
config = json.load(f)
|
38 |
+
print("β
Loaded configuration from config.json")
|
39 |
+
return config
|
40 |
+
except FileNotFoundError:
|
41 |
+
print("βΉοΈ No config.json found, using default configuration")
|
42 |
+
# Save default config for future use
|
43 |
+
try:
|
44 |
+
with open('config.json', 'w') as f:
|
45 |
+
json.dump(DEFAULT_CONFIG, f, indent=2)
|
46 |
+
print("β
Created config.json with default values")
|
47 |
+
except:
|
48 |
+
pass
|
49 |
+
return DEFAULT_CONFIG
|
50 |
+
except Exception as e:
|
51 |
+
print(f"β οΈ Error loading config.json: {{e}}, using defaults")
|
52 |
+
return DEFAULT_CONFIG
|
53 |
+
|
54 |
+
# Load configuration
|
55 |
+
config = load_config()
|
56 |
+
|
57 |
+
# Initial load of configuration values
|
58 |
+
SPACE_NAME = config.get('name', DEFAULT_CONFIG['name'])
|
59 |
+
SPACE_DESCRIPTION = config.get('description', DEFAULT_CONFIG['description'])
|
60 |
+
SYSTEM_PROMPT = config.get('system_prompt', DEFAULT_CONFIG['system_prompt'])
|
61 |
+
temperature = config.get('temperature', DEFAULT_CONFIG['temperature'])
|
62 |
+
max_tokens = config.get('max_tokens', DEFAULT_CONFIG['max_tokens'])
|
63 |
+
MODEL = config.get('model', DEFAULT_CONFIG['model'])
|
64 |
+
THEME = config.get('theme', DEFAULT_CONFIG['theme'])
|
65 |
+
GROUNDING_URLS = config.get('grounding_urls', DEFAULT_CONFIG['grounding_urls'])
|
66 |
+
ENABLE_DYNAMIC_URLS = config.get('enable_dynamic_urls', DEFAULT_CONFIG['enable_dynamic_urls'])
|
67 |
|
|
|
|
|
|
|
68 |
# Get access code from environment variable for security
|
69 |
# If ACCESS_CODE is not set, no access control is applied
|
70 |
ACCESS_CODE = os.environ.get("ACCESS_CODE")
|
|
|
71 |
|
72 |
# Get API key from environment - customizable variable name with validation
|
73 |
+
API_KEY_VAR = config.get('api_key_var', DEFAULT_CONFIG['api_key_var'])
|
74 |
+
API_KEY = os.environ.get(API_KEY_VAR)
|
75 |
if API_KEY:
|
76 |
API_KEY = API_KEY.strip() # Remove any whitespace
|
77 |
if not API_KEY: # Check if empty after stripping
|
|
|
82 |
"""Validate API key configuration with detailed logging"""
|
83 |
if not API_KEY:
|
84 |
print(f"β οΈ API KEY CONFIGURATION ERROR:")
|
85 |
+
print(f" Variable name: {API_KEY_VAR}")
|
86 |
print(f" Status: Not set or empty")
|
87 |
+
print(f" Action needed: Set '{API_KEY_VAR}' in HuggingFace Space secrets")
|
88 |
print(f" Expected format: sk-or-xxxxxxxxxx")
|
89 |
return False
|
90 |
elif not API_KEY.startswith('sk-or-'):
|
91 |
print(f"β οΈ API KEY FORMAT WARNING:")
|
92 |
+
print(f" Variable name: {API_KEY_VAR}")
|
93 |
+
print(f" Current value: {API_KEY[:10]}..." if len(API_KEY) > 10 else "{API_KEY}")
|
94 |
print(f" Expected format: sk-or-xxxxxxxxxx")
|
95 |
print(f" Note: OpenRouter keys should start with 'sk-or-'")
|
96 |
return True # Still try to use it
|
97 |
else:
|
98 |
print(f"β
API Key configured successfully")
|
99 |
+
print(f" Variable: {API_KEY_VAR}")
|
100 |
print(f" Format: Valid OpenRouter key")
|
101 |
return True
|
102 |
|
|
|
210 |
context_parts.append(f"[{priority_label}] Context from URL {i} ({url}):\n{content}")
|
211 |
|
212 |
if context_parts:
|
213 |
+
result = "\n\
|
214 |
+
" + "\n\
|
215 |
+
".join(context_parts) + "\n\
|
216 |
+
"
|
217 |
else:
|
218 |
result = ""
|
219 |
|
|
|
227 |
return "No conversation to export."
|
228 |
|
229 |
markdown_content = f"""# Conversation Export
|
230 |
+
Generated on: {datetime.now().strftime('%%Y-%%m-%%d %%H:%%M:%%S')}
|
231 |
|
232 |
---
|
233 |
|
|
|
241 |
|
242 |
if role == 'user':
|
243 |
message_pair_count += 1
|
244 |
+
markdown_content += f"## User Message {{message_pair_count}}\n\n{{content}}\n\
|
245 |
+
"
|
246 |
elif role == 'assistant':
|
247 |
+
markdown_content += f"## Assistant Response {{message_pair_count}}\n\n{{content}}\n\n---\n\
|
248 |
+
"
|
249 |
elif isinstance(message, (list, tuple)) and len(message) >= 2:
|
250 |
# Handle legacy tuple format: ["user msg", "assistant msg"]
|
251 |
message_pair_count += 1
|
252 |
user_msg, assistant_msg = message[0], message[1]
|
253 |
if user_msg:
|
254 |
+
markdown_content += f"## User Message {{message_pair_count}}\n\n{{user_msg}}\n\
|
255 |
+
"
|
256 |
if assistant_msg:
|
257 |
+
markdown_content += f"## Assistant Response {{message_pair_count}}\n\n{{assistant_msg}}\n\n---\n\
|
258 |
+
"
|
259 |
|
260 |
return markdown_content
|
261 |
|
|
|
265 |
|
266 |
# Enhanced API key validation with helpful messages
|
267 |
if not API_KEY:
|
268 |
+
error_msg = f"π **API Key Required**\n\
|
269 |
+
"
|
270 |
+
error_msg += f"Please configure your OpenRouter API key:\
|
271 |
+
"
|
272 |
+
error_msg += f"1. Go to Settings (βοΈ) in your HuggingFace Space\
|
273 |
+
"
|
274 |
+
error_msg += f"2. Click 'Variables and secrets'\
|
275 |
+
"
|
276 |
+
error_msg += f"3. Add secret: **{API_KEY_VAR}**\
|
277 |
+
"
|
278 |
+
error_msg += f"4. Value: Your OpenRouter API key (starts with `sk-or-`)\n\
|
279 |
+
"
|
280 |
error_msg += f"Get your API key at: https://openrouter.ai/keys"
|
281 |
+
print(f"β API request failed: No API key configured for {API_KEY_VAR}")
|
282 |
return error_msg
|
283 |
|
284 |
# Get grounding context
|
|
|
295 |
content = fetch_url_content(url)
|
296 |
dynamic_context_parts.append(f"\n\nDynamic context from {url}:\n{content}")
|
297 |
if dynamic_context_parts:
|
298 |
+
grounding_context += "\
|
299 |
+
".join(dynamic_context_parts)
|
300 |
|
301 |
# Build enhanced system prompt with grounding context
|
302 |
enhanced_system_prompt = SYSTEM_PROMPT + grounding_context
|
|
|
374 |
print(f"β Failed to parse API response: {str(e)}")
|
375 |
return f"API Error: Failed to parse response - {str(e)}"
|
376 |
elif response.status_code == 401:
|
377 |
+
error_msg = f"π **Authentication Error**\n\
|
378 |
+
"
|
379 |
+
error_msg += f"Your API key appears to be invalid or expired.\n\
|
380 |
+
"
|
381 |
+
error_msg += f"**Troubleshooting:**\
|
382 |
+
"
|
383 |
+
error_msg += f"1. Check that your **{API_KEY_VAR}** secret is set correctly\
|
384 |
+
"
|
385 |
+
error_msg += f"2. Verify your API key at: https://openrouter.ai/keys\
|
386 |
+
"
|
387 |
+
error_msg += f"3. Ensure your key starts with `sk-or-`\
|
388 |
+
"
|
389 |
error_msg += f"4. Check that you have credits on your OpenRouter account"
|
390 |
print(f"β API authentication failed: {response.status_code} - {response.text[:200]}")
|
391 |
return error_msg
|
392 |
elif response.status_code == 429:
|
393 |
+
error_msg = f"β±οΈ **Rate Limit Exceeded**\n\
|
394 |
+
"
|
395 |
+
error_msg += f"Too many requests. Please wait a moment and try again.\n\
|
396 |
+
"
|
397 |
+
error_msg += f"**Troubleshooting:**\
|
398 |
+
"
|
399 |
error_msg += f"1. Wait 30-60 seconds before trying again\n"
|
400 |
error_msg += f"2. Check your OpenRouter usage limits\n"
|
401 |
error_msg += f"3. Consider upgrading your OpenRouter plan"
|
|
|
408 |
except:
|
409 |
error_message = response.text
|
410 |
|
411 |
+
error_msg = f"β οΈ **Request Error**\n\
|
412 |
+
"
|
413 |
error_msg += f"The API request was invalid:\n"
|
414 |
+
error_msg += f"`{error_message}`\n\
|
415 |
+
"
|
416 |
if "model" in error_message.lower():
|
417 |
error_msg += f"**Model Issue:** The model `{MODEL}` may not be available.\n"
|
418 |
error_msg += f"Try switching to a different model in your Space configuration."
|
419 |
print(f"β Bad request: {response.status_code} - {error_message}")
|
420 |
return error_msg
|
421 |
else:
|
422 |
+
error_msg = f"π« **API Error {response.status_code}**\n\
|
423 |
+
"
|
424 |
+
error_msg += f"An unexpected error occurred. Please try again.\n\
|
425 |
+
"
|
426 |
error_msg += f"If this persists, check:\n"
|
427 |
error_msg += f"1. OpenRouter service status\n"
|
428 |
error_msg += f"2. Your API key and credits\n"
|
|
|
431 |
return error_msg
|
432 |
|
433 |
except requests.exceptions.Timeout:
|
434 |
+
error_msg = f"β° **Request Timeout**\n\
|
435 |
+
"
|
436 |
+
error_msg += f"The API request took too long (30s limit).\n\
|
437 |
+
"
|
438 |
error_msg += f"**Troubleshooting:**\n"
|
439 |
error_msg += f"1. Try again with a shorter message\n"
|
440 |
error_msg += f"2. Check your internet connection\n"
|
|
|
442 |
print(f"β Request timeout after 30 seconds")
|
443 |
return error_msg
|
444 |
except requests.exceptions.ConnectionError:
|
445 |
+
error_msg = f"π **Connection Error**\n\
|
446 |
+
"
|
447 |
+
error_msg += f"Could not connect to OpenRouter API.\n\
|
448 |
+
"
|
449 |
error_msg += f"**Troubleshooting:**\n"
|
450 |
error_msg += f"1. Check your internet connection\n"
|
451 |
error_msg += f"2. Check OpenRouter service status\n"
|
|
|
453 |
print(f"β Connection error to OpenRouter API")
|
454 |
return error_msg
|
455 |
except Exception as e:
|
456 |
+
error_msg = "β **Unexpected Error**\n\
|
457 |
+
"
|
458 |
+
error_msg += "An unexpected error occurred:\n"
|
459 |
+
error_msg += f"`{str(e)}`\n\
|
460 |
+
"
|
461 |
+
error_msg += "Please try again or contact support if this persists."
|
462 |
print(f"β Unexpected error: {str(e)}")
|
463 |
return error_msg
|
464 |
|
|
|
542 |
|
543 |
# Configuration status display
|
544 |
def get_configuration_status():
|
545 |
+
"""Generate a clean configuration status message for display"""
|
546 |
status_parts = []
|
547 |
|
548 |
+
# Basic configuration info (without redundant "Configuration:" header)
|
549 |
+
status_parts.append(f"**Name:** {SPACE_NAME}")
|
550 |
+
status_parts.append(f"**Model:** {MODEL}")
|
551 |
+
status_parts.append(f"**Theme:** {THEME}")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
552 |
status_parts.append(f"**Temperature:** 0.7")
|
553 |
+
status_parts.append(f"**Max Response Tokens:** 750")
|
554 |
+
status_parts.append("")
|
555 |
+
|
556 |
+
# Example prompts
|
557 |
+
status_parts.append("")
|
558 |
+
examples_list = config.get('examples', [])
|
559 |
+
if isinstance(examples_list, str):
|
560 |
+
try:
|
561 |
+
import ast
|
562 |
+
examples_list = ast.literal_eval(examples_list)
|
563 |
+
except:
|
564 |
+
examples_list = []
|
565 |
+
|
566 |
+
if examples_list and len(examples_list) > 0:
|
567 |
+
status_parts.append("**Example Prompts:**")
|
568 |
+
for example in examples_list[:5]: # Show up to 5 examples
|
569 |
+
status_parts.append(f"β’ {example}")
|
570 |
+
if len(examples_list) > 5:
|
571 |
+
status_parts.append(f"β’ ... and {len(examples_list) - 5} more")
|
572 |
+
else:
|
573 |
+
status_parts.append("**Example Prompts:** No example prompts configured")
|
574 |
|
575 |
# URL Context if configured
|
576 |
+
if GROUNDING_URLS and len(GROUNDING_URLS) > 0:
|
577 |
+
status_parts.append("")
|
578 |
+
status_parts.append("**Grounding URLs:**")
|
579 |
+
for i, url in enumerate(GROUNDING_URLS[:5], 1): # Show first 5 URLs
|
580 |
+
status_parts.append(f"{i}. {url}")
|
581 |
+
if len(GROUNDING_URLS) > 5:
|
582 |
+
status_parts.append(f"... and {len(GROUNDING_URLS) - 5} more URLs")
|
583 |
+
|
584 |
+
# System prompt at the end
|
585 |
+
status_parts.append("")
|
586 |
+
status_parts.append(f"**System Prompt:** {SYSTEM_PROMPT}")
|
587 |
+
|
588 |
+
# API Key status (minimal, at the end)
|
589 |
+
status_parts.append("")
|
590 |
+
if not API_KEY_VALID:
|
591 |
+
status_parts.append(f"**Note:** API key ({API_KEY_VAR}) not configured in Space secrets")
|
|
|
|
|
|
|
|
|
|
|
592 |
|
593 |
return "\n".join(status_parts)
|
594 |
|
|
|
614 |
|
615 |
# Main chat interface (hidden until access granted)
|
616 |
with gr.Column(visible=(ACCESS_CODE is None)) as chat_section:
|
617 |
+
# Get examples from config
|
618 |
+
examples = config.get('examples', [])
|
619 |
+
if isinstance(examples, str):
|
620 |
+
try:
|
621 |
+
import ast
|
622 |
+
examples = ast.literal_eval(examples)
|
623 |
+
except:
|
624 |
+
examples = []
|
625 |
+
|
626 |
chat_interface = gr.ChatInterface(
|
627 |
fn=store_and_generate_response, # Use wrapper function to store history
|
628 |
title="", # Title already shown above
|
629 |
description="", # Description already shown above
|
630 |
+
examples=examples if examples else None,
|
631 |
type="messages" # Use modern message format for better compatibility
|
632 |
)
|
633 |
|
|
|
642 |
outputs=[export_file]
|
643 |
)
|
644 |
|
645 |
+
# Configuration status
|
646 |
+
with gr.Accordion("Configuration", open=False):
|
647 |
+
gr.Markdown(get_configuration_status())
|
648 |
|
649 |
# Connect access verification
|
650 |
if ACCESS_CODE is not None:
|
|
|
691 |
with open('config.json', 'r') as f:
|
692 |
current_config = json.load(f)
|
693 |
except:
|
694 |
+
# Use DEFAULT_CONFIG as fallback
|
695 |
+
current_config = DEFAULT_CONFIG.copy()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
696 |
|
697 |
+
# Editable fields
|
698 |
+
# System Prompt
|
699 |
edit_system_prompt = gr.Textbox(
|
700 |
label="System Prompt",
|
701 |
value=current_config.get('system_prompt', SYSTEM_PROMPT),
|
|
|
717 |
],
|
718 |
value=current_config.get('model', MODEL)
|
719 |
)
|
720 |
+
|
721 |
+
# 4. Example prompts field
|
722 |
examples_value = current_config.get('examples', [])
|
723 |
if isinstance(examples_value, list):
|
724 |
examples_text_value = "\n".join(examples_value)
|
|
|
749 |
step=50
|
750 |
)
|
751 |
|
752 |
+
# URL Grounding fields
|
753 |
gr.Markdown("### URL Grounding")
|
754 |
grounding_urls_value = current_config.get('grounding_urls', [])
|
755 |
if isinstance(grounding_urls_value, str):
|
|
|
776 |
)
|
777 |
|
778 |
with gr.Row():
|
779 |
+
save_config_btn = gr.Button("Save Configuration", variant="primary")
|
780 |
+
reset_config_btn = gr.Button("Reset to Defaults", variant="secondary")
|
781 |
|
782 |
config_status = gr.Markdown("")
|
783 |
|
|
|
785 |
def verify_faculty_password(password):
|
786 |
if password == FACULTY_PASSWORD:
|
787 |
return (
|
788 |
+
gr.update(value="Authentication successful!"),
|
789 |
gr.update(visible=False), # Hide auth row
|
790 |
gr.update(visible=True), # Show config section
|
791 |
True # Update auth state
|
792 |
)
|
793 |
else:
|
794 |
return (
|
795 |
+
gr.update(value="Invalid password"),
|
796 |
gr.update(visible=True), # Keep auth row visible
|
797 |
gr.update(visible=False), # Keep config hidden
|
798 |
False # Auth failed
|
799 |
)
|
800 |
|
801 |
# Save configuration function
|
802 |
+
def save_configuration(new_prompt, new_model, new_examples, new_temp, new_tokens, *url_values, lock_config, is_authenticated):
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
803 |
if not is_authenticated:
|
804 |
+
return "Not authenticated"
|
805 |
|
806 |
# Check if configuration is already locked
|
807 |
try:
|
808 |
with open('config.json', 'r') as f:
|
809 |
existing_config = json.load(f)
|
810 |
if existing_config.get('locked', False):
|
811 |
+
return "Configuration is locked and cannot be modified"
|
812 |
except:
|
813 |
pass
|
814 |
|
|
|
817 |
with open('config.json', 'r') as f:
|
818 |
current_full_config = json.load(f)
|
819 |
except:
|
820 |
+
# If config.json doesn't exist, use default configuration
|
821 |
+
current_full_config = DEFAULT_CONFIG.copy()
|
822 |
|
823 |
# Process example prompts
|
824 |
examples_list = [ex.strip() for ex in new_examples.split('\n') if ex.strip()]
|
825 |
|
826 |
+
# Process URL values - lock_config is the last parameter
|
827 |
+
urls = list(url_values[:-1]) # All but last are URLs
|
828 |
+
lock_config_from_args = url_values[-1] # Last is lock_config
|
829 |
+
# Filter out empty URLs
|
830 |
+
grounding_urls = [url.strip() for url in urls if url.strip()]
|
831 |
|
832 |
# Update all editable fields while preserving everything else
|
833 |
current_full_config.update({
|
|
|
|
|
834 |
'system_prompt': new_prompt,
|
835 |
'model': new_model,
|
836 |
'examples': examples_list,
|
837 |
'temperature': new_temp,
|
838 |
'max_tokens': int(new_tokens),
|
839 |
'grounding_urls': grounding_urls,
|
840 |
+
'locked': lock_config_from_args,
|
841 |
'last_modified': datetime.now().isoformat(),
|
842 |
'last_modified_by': 'faculty'
|
843 |
})
|
|
|
846 |
with open('config.json', 'w') as f:
|
847 |
json.dump(current_full_config, f, indent=2)
|
848 |
|
849 |
+
# Optional: Auto-commit to HuggingFace if token is available
|
850 |
+
hf_token = os.environ.get("HF_TOKEN")
|
851 |
+
space_id = os.environ.get("SPACE_ID")
|
|
|
|
|
|
|
|
|
|
|
|
|
852 |
|
853 |
+
if hf_token and space_id:
|
854 |
+
try:
|
855 |
+
from huggingface_hub import HfApi
|
856 |
+
api = HfApi()
|
857 |
+
api.upload_file(
|
858 |
+
path_or_fileobj="config.json",
|
859 |
+
path_in_repo="config.json",
|
860 |
+
repo_id=space_id,
|
861 |
+
repo_type="space",
|
862 |
+
commit_message=f"Update configuration by faculty at {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}"
|
863 |
+
)
|
864 |
+
return f"β
Configuration saved and committed to repository at {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}\n\nπ **Space will restart automatically** to apply changes."
|
865 |
+
except Exception as commit_error:
|
866 |
+
print(f"Note: Could not auto-commit to repository: {commit_error}")
|
867 |
+
return f"β
Configuration saved locally at {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}\n\nπ **Manual Restart Required**\nFor changes to take effect:\n1. Go to Settings (βοΈ)\n2. Click 'Factory reboot'\n3. Wait ~30 seconds for restart"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
868 |
else:
|
869 |
+
return f"β
Configuration saved at {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}\n\nπ **Manual Restart Required**\nFor changes to take effect:\n1. Go to Settings (βοΈ)\n2. Click 'Factory reboot'\n3. Wait ~30 seconds for restart"
|
|
|
|
|
870 |
except Exception as e:
|
871 |
return f"β Error saving configuration: {str(e)}"
|
872 |
|
873 |
# Reset configuration function
|
874 |
def reset_configuration(is_authenticated):
|
875 |
if not is_authenticated:
|
876 |
+
updates = ["Not authenticated"] + [gr.update() for _ in range(14)] # 1 status + 14 fields (prompt, model, examples, temp, tokens + 10 urls)
|
877 |
return tuple(updates)
|
878 |
|
879 |
# Check if locked
|
|
|
881 |
with open('config.json', 'r') as f:
|
882 |
existing_config = json.load(f)
|
883 |
if existing_config.get('locked', False):
|
884 |
+
updates = ["Configuration is locked"] + [gr.update() for _ in range(14)]
|
885 |
return tuple(updates)
|
886 |
except:
|
887 |
pass
|
|
|
893 |
else:
|
894 |
examples_text = ""
|
895 |
|
896 |
+
# Get default URLs - parse from JSON string if needed
|
897 |
default_urls = DEFAULT_CONFIG.get('grounding_urls', [])
|
898 |
if isinstance(default_urls, str):
|
899 |
try:
|
900 |
+
import json
|
901 |
+
default_urls = json.loads(default_urls)
|
902 |
except:
|
903 |
default_urls = []
|
904 |
+
elif not isinstance(default_urls, list):
|
905 |
+
default_urls = []
|
906 |
|
907 |
# Reset to original default values
|
908 |
updates = [
|
909 |
+
"Reset to default values",
|
|
|
|
|
910 |
gr.update(value=DEFAULT_CONFIG.get('system_prompt', SYSTEM_PROMPT)),
|
911 |
gr.update(value=DEFAULT_CONFIG.get('model', MODEL)),
|
912 |
gr.update(value=examples_text),
|
|
|
937 |
# Connect configuration buttons
|
938 |
save_config_btn.click(
|
939 |
save_configuration,
|
940 |
+
inputs=[edit_system_prompt, edit_model, edit_examples, edit_temperature, edit_max_tokens] + url_fields + [config_locked, faculty_auth_state],
|
941 |
outputs=[config_status]
|
942 |
)
|
943 |
|
944 |
reset_config_btn.click(
|
945 |
reset_configuration,
|
946 |
inputs=[faculty_auth_state],
|
947 |
+
outputs=[config_status, edit_system_prompt, edit_model, edit_examples, edit_temperature, edit_max_tokens] + url_fields
|
948 |
)
|
949 |
else:
|
950 |
+
gr.Markdown("Faculty configuration is not enabled. Set CONFIG_CODE in Space secrets to enable.")
|
951 |
|
952 |
if __name__ == "__main__":
|
953 |
demo.launch()
|
config.json
CHANGED
@@ -1,13 +1,15 @@
|
|
1 |
{
|
2 |
"name": "AI Assistant",
|
3 |
"description": "A customizable AI assistant",
|
4 |
-
"system_prompt": "You are a
|
5 |
"model": "google/gemini-2.0-flash-001",
|
6 |
"api_key_var": "API_KEY",
|
7 |
"temperature": 0.7,
|
8 |
"max_tokens": 750,
|
9 |
-
"examples":
|
10 |
-
|
|
|
|
|
11 |
"enable_dynamic_urls": true,
|
12 |
"theme": "Default"
|
13 |
}
|
|
|
1 |
{
|
2 |
"name": "AI Assistant",
|
3 |
"description": "A customizable AI assistant",
|
4 |
+
"system_prompt": "You are a pedagogically-minded academic assistant designed for introductory courses. Your approach follows constructivist learning principles: build on students' prior knowledge, scaffold complex concepts through graduated questioning, and use Socratic dialogue to guide discovery. Provide concise, evidence-based explanations that connect theory to lived experiences. Each response should model critical thinking by acknowledging multiple perspectives, identifying assumptions, and revealing conceptual relationships. Conclude with open-ended questions that promote higher-order thinking\u2014analysis, synthesis, or evaluation\u2014rather than recall.",
|
5 |
"model": "google/gemini-2.0-flash-001",
|
6 |
"api_key_var": "API_KEY",
|
7 |
"temperature": 0.7,
|
8 |
"max_tokens": 750,
|
9 |
+
"examples": [
|
10 |
+
"Can you help me understand why the sky is blue?"
|
11 |
+
],
|
12 |
+
"grounding_urls": [],
|
13 |
"enable_dynamic_urls": true,
|
14 |
"theme": "Default"
|
15 |
}
|