Upload 3 files
Browse files- app.py +73 -53
- config.json +10 -10
app.py
CHANGED
@@ -10,13 +10,13 @@ import urllib.parse
|
|
10 |
|
11 |
|
12 |
# Configuration
|
13 |
-
SPACE_NAME = "
|
14 |
-
SPACE_DESCRIPTION = "A customizable AI assistant"
|
15 |
|
16 |
# Default configuration values
|
17 |
-
DEFAULT_SYSTEM_PROMPT = """You are
|
18 |
-
DEFAULT_TEMPERATURE = 0.
|
19 |
-
DEFAULT_MAX_TOKENS =
|
20 |
|
21 |
# Try to load configuration from file (if modified by faculty)
|
22 |
try:
|
@@ -33,16 +33,16 @@ except:
|
|
33 |
max_tokens = DEFAULT_MAX_TOKENS
|
34 |
print("βΉοΈ Using default configuration")
|
35 |
|
36 |
-
MODEL = "
|
37 |
-
THEME = "
|
38 |
-
GROUNDING_URLS = []
|
39 |
# Get access code from environment variable for security
|
40 |
-
# If
|
41 |
-
ACCESS_CODE = os.environ.get("
|
42 |
ENABLE_DYNAMIC_URLS = True
|
43 |
|
44 |
# Get API key from environment - customizable variable name with validation
|
45 |
-
API_KEY = os.environ.get("
|
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 +53,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 |
|
@@ -233,10 +233,10 @@ def generate_response(message, history):
|
|
233 |
error_msg += f"Please configure your OpenRouter API key:\n"
|
234 |
error_msg += f"1. Go to Settings (βοΈ) in your HuggingFace Space\n"
|
235 |
error_msg += f"2. Click 'Variables and secrets'\n"
|
236 |
-
error_msg += f"3. Add secret: **
|
237 |
error_msg += f"4. Value: Your OpenRouter API key (starts with `sk-or-`)\n\n"
|
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
|
@@ -294,8 +294,8 @@ def generate_response(message, history):
|
|
294 |
json={
|
295 |
"model": MODEL,
|
296 |
"messages": messages,
|
297 |
-
"temperature": 0.
|
298 |
-
"max_tokens":
|
299 |
},
|
300 |
timeout=30
|
301 |
)
|
@@ -334,7 +334,7 @@ def generate_response(message, history):
|
|
334 |
error_msg = f"π **Authentication Error**\n\n"
|
335 |
error_msg += f"Your API key appears to be invalid or expired.\n\n"
|
336 |
error_msg += f"**Troubleshooting:**\n"
|
337 |
-
error_msg += f"1. Check that your **
|
338 |
error_msg += f"2. Verify your API key at: https://openrouter.ai/keys\n"
|
339 |
error_msg += f"3. Ensure your key starts with `sk-or-`\n"
|
340 |
error_msg += f"4. Check that you have credits on your OpenRouter account"
|
@@ -483,22 +483,43 @@ def get_configuration_status():
|
|
483 |
"""Generate a configuration status message for display"""
|
484 |
status_parts = []
|
485 |
|
486 |
-
# API Key status
|
|
|
487 |
if API_KEY_VALID:
|
488 |
status_parts.append("β
**API Key:** Ready")
|
489 |
else:
|
490 |
-
status_parts.append("β **API Key:** Not configured
|
|
|
491 |
|
492 |
-
#
|
493 |
-
status_parts.append(
|
|
|
|
|
|
|
|
|
494 |
|
495 |
-
#
|
496 |
if GROUNDING_URLS:
|
497 |
-
status_parts.append(
|
498 |
-
|
499 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
500 |
if ACCESS_CODE is not None:
|
501 |
-
status_parts.append("
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
502 |
|
503 |
return "\n".join(status_parts)
|
504 |
|
@@ -528,16 +549,13 @@ with gr.Blocks(title=SPACE_NAME, theme=theme_class()) as demo:
|
|
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=['
|
532 |
type="messages" # Use modern message format for better compatibility
|
533 |
)
|
534 |
|
535 |
-
# Export functionality
|
536 |
with gr.Row():
|
537 |
-
|
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
|
@@ -546,8 +564,8 @@ with gr.Blocks(title=SPACE_NAME, theme=theme_class()) as demo:
|
|
546 |
outputs=[export_file]
|
547 |
)
|
548 |
|
549 |
-
# Configuration status
|
550 |
-
with gr.Accordion("
|
551 |
gr.Markdown(get_configuration_status())
|
552 |
|
553 |
# Connect access verification
|
@@ -565,22 +583,24 @@ with gr.Blocks(title=SPACE_NAME, theme=theme_class()) as demo:
|
|
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
|
569 |
|
570 |
# Check if faculty password is configured
|
571 |
-
FACULTY_PASSWORD = os.environ.get("
|
572 |
|
573 |
if FACULTY_PASSWORD:
|
574 |
faculty_auth_state = gr.State(False)
|
575 |
|
576 |
# Authentication row
|
577 |
-
with gr.
|
578 |
-
|
579 |
-
|
580 |
-
|
581 |
-
|
582 |
-
|
583 |
-
|
|
|
|
|
584 |
faculty_auth_status = gr.Markdown("")
|
585 |
|
586 |
# Configuration editor (hidden until authenticated)
|
@@ -595,8 +615,8 @@ with gr.Blocks(title=SPACE_NAME, theme=theme_class()) as demo:
|
|
595 |
except:
|
596 |
current_config = {
|
597 |
'system_prompt': SYSTEM_PROMPT,
|
598 |
-
'temperature': 0.
|
599 |
-
'max_tokens':
|
600 |
'locked': False
|
601 |
}
|
602 |
|
@@ -612,14 +632,14 @@ with gr.Blocks(title=SPACE_NAME, theme=theme_class()) as demo:
|
|
612 |
label="Temperature",
|
613 |
minimum=0,
|
614 |
maximum=2,
|
615 |
-
value=current_config.get('temperature', 0.
|
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',
|
623 |
step=50
|
624 |
)
|
625 |
|
@@ -707,8 +727,8 @@ with gr.Blocks(title=SPACE_NAME, theme=theme_class()) as demo:
|
|
707 |
return (
|
708 |
"β©οΈ Reset to default values",
|
709 |
gr.update(value=SYSTEM_PROMPT),
|
710 |
-
gr.update(value=0.
|
711 |
-
gr.update(value=
|
712 |
)
|
713 |
|
714 |
# Connect authentication
|
@@ -737,7 +757,7 @@ with gr.Blocks(title=SPACE_NAME, theme=theme_class()) as demo:
|
|
737 |
outputs=[config_status, edit_system_prompt, edit_temperature, edit_max_tokens]
|
738 |
)
|
739 |
else:
|
740 |
-
gr.Markdown("βΉοΈ Faculty configuration is not enabled. Set
|
741 |
|
742 |
if __name__ == "__main__":
|
743 |
demo.launch()
|
|
|
10 |
|
11 |
|
12 |
# Configuration
|
13 |
+
SPACE_NAME = "Math-o-Matic"
|
14 |
+
SPACE_DESCRIPTION = "A customizable AI assistant for advanced mathematics help and numeracy guidance"
|
15 |
|
16 |
# Default configuration values
|
17 |
+
DEFAULT_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."""
|
18 |
+
DEFAULT_TEMPERATURE = 0.6
|
19 |
+
DEFAULT_MAX_TOKENS = 1000
|
20 |
|
21 |
# Try to load configuration from file (if modified by faculty)
|
22 |
try:
|
|
|
33 |
max_tokens = DEFAULT_MAX_TOKENS
|
34 |
print("βΉοΈ Using default configuration")
|
35 |
|
36 |
+
MODEL = "anthropic/claude-3.5-sonnet"
|
37 |
+
THEME = "Glass" # Gradio theme name
|
38 |
+
GROUNDING_URLS = ["https://www.maths.tcd.ie/~dwilkins/LaTeXPrimer/MathMode.html", "https://www.maths.tcd.ie/~dwilkins/LaTeXPrimer/TeXEntities.html", "https://www.maths.tcd.ie/~dwilkins/LaTeXPrimer/Matrices.html", "https://www.maths.tcd.ie/~dwilkins/LaTeXPrimer/StdFuncts.html"]
|
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 |
+
API_KEY = os.environ.get("API_KEY")
|
46 |
if API_KEY:
|
47 |
API_KEY = API_KEY.strip() # Remove any whitespace
|
48 |
if not API_KEY: # Check if empty after stripping
|
|
|
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: API_KEY")
|
57 |
print(f" Status: Not set or empty")
|
58 |
+
print(f" Action needed: Set 'API_KEY' in HuggingFace Space secrets")
|
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: API_KEY")
|
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: API_KEY")
|
71 |
print(f" Format: Valid OpenRouter key")
|
72 |
return True
|
73 |
|
|
|
233 |
error_msg += f"Please configure your OpenRouter API key:\n"
|
234 |
error_msg += f"1. Go to Settings (βοΈ) in your HuggingFace Space\n"
|
235 |
error_msg += f"2. Click 'Variables and secrets'\n"
|
236 |
+
error_msg += f"3. Add secret: **API_KEY**\n"
|
237 |
error_msg += f"4. Value: Your OpenRouter API key (starts with `sk-or-`)\n\n"
|
238 |
error_msg += f"Get your API key at: https://openrouter.ai/keys"
|
239 |
+
print(f"β API request failed: No API key configured for API_KEY")
|
240 |
return error_msg
|
241 |
|
242 |
# Get grounding context
|
|
|
294 |
json={
|
295 |
"model": MODEL,
|
296 |
"messages": messages,
|
297 |
+
"temperature": 0.6,
|
298 |
+
"max_tokens": 1000
|
299 |
},
|
300 |
timeout=30
|
301 |
)
|
|
|
334 |
error_msg = f"π **Authentication Error**\n\n"
|
335 |
error_msg += f"Your API key appears to be invalid or expired.\n\n"
|
336 |
error_msg += f"**Troubleshooting:**\n"
|
337 |
+
error_msg += f"1. Check that your **API_KEY** secret is set correctly\n"
|
338 |
error_msg += f"2. Verify your API key at: https://openrouter.ai/keys\n"
|
339 |
error_msg += f"3. Ensure your key starts with `sk-or-`\n"
|
340 |
error_msg += f"4. Check that you have credits on your OpenRouter account"
|
|
|
483 |
"""Generate a configuration status message for display"""
|
484 |
status_parts = []
|
485 |
|
486 |
+
# API Key status
|
487 |
+
status_parts.append("### π API Configuration")
|
488 |
if API_KEY_VALID:
|
489 |
status_parts.append("β
**API Key:** Ready")
|
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.6")
|
499 |
+
status_parts.append(f"**Max Tokens:** 1000")
|
500 |
|
501 |
+
# URL Context if configured
|
502 |
if GROUNDING_URLS:
|
503 |
+
status_parts.append("") # Blank line
|
504 |
+
status_parts.append("### π Context Sources")
|
505 |
+
status_parts.append(f"**URLs Configured:** {len(GROUNDING_URLS)}")
|
506 |
+
for i, url in enumerate(GROUNDING_URLS[:2], 1):
|
507 |
+
status_parts.append(f" {i}. {url[:50]}{'...' if len(url) > 50 else ''}")
|
508 |
+
if len(GROUNDING_URLS) > 2:
|
509 |
+
status_parts.append(f" ... and {len(GROUNDING_URLS) - 2} more")
|
510 |
+
|
511 |
+
# Access control
|
512 |
if ACCESS_CODE is not None:
|
513 |
+
status_parts.append("") # Blank line
|
514 |
+
status_parts.append("### π Access Control")
|
515 |
+
status_parts.append("**Status:** Password protected")
|
516 |
+
|
517 |
+
# System prompt
|
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 |
|
|
|
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=['Can you help me with the quadratic formula?', 'What is Bayesian statistics?', 'Help me work with a math problem'],
|
553 |
type="messages" # Use modern message format for better compatibility
|
554 |
)
|
555 |
|
556 |
+
# Export functionality
|
557 |
with gr.Row():
|
558 |
+
export_btn = gr.Button("π₯ Export Conversation", variant="secondary", size="sm")
|
|
|
|
|
|
|
559 |
export_file = gr.File(label="Download", visible=False)
|
560 |
|
561 |
# Connect export functionality
|
|
|
564 |
outputs=[export_file]
|
565 |
)
|
566 |
|
567 |
+
# Configuration status
|
568 |
+
with gr.Accordion("π Configuration Status", open=True):
|
569 |
gr.Markdown(get_configuration_status())
|
570 |
|
571 |
# Connect access verification
|
|
|
583 |
|
584 |
# Faculty Configuration Section - appears at the bottom with password protection
|
585 |
with gr.Accordion("π§ Faculty Configuration", open=False, visible=True) as faculty_section:
|
586 |
+
gr.Markdown("**Faculty Only:** Edit assistant configuration. Requires CONFIG_CODE secret.")
|
587 |
|
588 |
# Check if faculty password is configured
|
589 |
+
FACULTY_PASSWORD = os.environ.get("CONFIG_CODE", "").strip()
|
590 |
|
591 |
if FACULTY_PASSWORD:
|
592 |
faculty_auth_state = gr.State(False)
|
593 |
|
594 |
# Authentication row
|
595 |
+
with gr.Column() as faculty_auth_row:
|
596 |
+
with gr.Row():
|
597 |
+
faculty_password_input = gr.Textbox(
|
598 |
+
label="Faculty Password",
|
599 |
+
type="password",
|
600 |
+
placeholder="Enter faculty configuration password",
|
601 |
+
scale=3
|
602 |
+
)
|
603 |
+
faculty_auth_btn = gr.Button("Unlock Configuration", variant="primary", scale=1)
|
604 |
faculty_auth_status = gr.Markdown("")
|
605 |
|
606 |
# Configuration editor (hidden until authenticated)
|
|
|
615 |
except:
|
616 |
current_config = {
|
617 |
'system_prompt': SYSTEM_PROMPT,
|
618 |
+
'temperature': 0.6,
|
619 |
+
'max_tokens': 1000,
|
620 |
'locked': False
|
621 |
}
|
622 |
|
|
|
632 |
label="Temperature",
|
633 |
minimum=0,
|
634 |
maximum=2,
|
635 |
+
value=current_config.get('temperature', 0.6),
|
636 |
step=0.1
|
637 |
)
|
638 |
edit_max_tokens = gr.Slider(
|
639 |
label="Max Tokens",
|
640 |
minimum=50,
|
641 |
maximum=4096,
|
642 |
+
value=current_config.get('max_tokens', 1000),
|
643 |
step=50
|
644 |
)
|
645 |
|
|
|
727 |
return (
|
728 |
"β©οΈ Reset to default values",
|
729 |
gr.update(value=SYSTEM_PROMPT),
|
730 |
+
gr.update(value=0.6),
|
731 |
+
gr.update(value=1000)
|
732 |
)
|
733 |
|
734 |
# Connect authentication
|
|
|
757 |
outputs=[config_status, edit_system_prompt, edit_temperature, edit_max_tokens]
|
758 |
)
|
759 |
else:
|
760 |
+
gr.Markdown("βΉοΈ Faculty configuration is not enabled. Set CONFIG_CODE in Space secrets to enable.")
|
761 |
|
762 |
if __name__ == "__main__":
|
763 |
demo.launch()
|
config.json
CHANGED
@@ -1,13 +1,13 @@
|
|
1 |
{
|
2 |
-
"name": "
|
3 |
-
"description": "A customizable AI assistant",
|
4 |
-
"system_prompt": "You are
|
5 |
-
"model": "
|
6 |
-
"api_key_var": "
|
7 |
-
"temperature": 0.
|
8 |
-
"max_tokens":
|
9 |
-
"examples": "['
|
10 |
-
"grounding_urls": "[]",
|
11 |
"enable_dynamic_urls": true,
|
12 |
-
"theme": "
|
13 |
}
|
|
|
1 |
{
|
2 |
+
"name": "Math-o-Matic",
|
3 |
+
"description": "A customizable AI assistant for advanced mathematics help and numeracy guidance",
|
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": "API_KEY",
|
7 |
+
"temperature": 0.6,
|
8 |
+
"max_tokens": 1000,
|
9 |
+
"examples": "['Can you help me with the quadratic formula?', 'What is Bayesian statistics?', 'Help me work with a math problem']",
|
10 |
+
"grounding_urls": "[\"https://www.maths.tcd.ie/~dwilkins/LaTeXPrimer/MathMode.html\", \"https://www.maths.tcd.ie/~dwilkins/LaTeXPrimer/TeXEntities.html\", \"https://www.maths.tcd.ie/~dwilkins/LaTeXPrimer/Matrices.html\", \"https://www.maths.tcd.ie/~dwilkins/LaTeXPrimer/StdFuncts.html\"]",
|
11 |
"enable_dynamic_urls": true,
|
12 |
+
"theme": "Glass"
|
13 |
}
|