milwright commited on
Commit
f448173
·
verified ·
1 Parent(s): 710e98b

Upload 3 files

Browse files
Files changed (3) hide show
  1. README.md +6 -6
  2. app.py +40 -118
  3. config.json +13 -15
README.md CHANGED
@@ -1,19 +1,19 @@
1
  ---
2
- title: Course Assistant Example
3
  emoji: 💬
4
  colorFrom: blue
5
  colorTo: green
6
  sdk: gradio
7
- sdk_version: 5.42.0
8
  app_file: app.py
9
  pinned: false
10
  license: mit
11
- short_description: Python support for cultural analytics students
12
  ---
13
 
14
- # Course Assistant Example
15
 
16
- Python support for cultural analytics students
17
 
18
  ## Quick Setup
19
 
@@ -42,7 +42,7 @@ Python support for cultural analytics students
42
  Your Space should now be running! Try the example prompts or ask your own questions.
43
 
44
  ## Configuration
45
- - **Model**: openai/gpt-oss-120b
46
  - **API Key Variable**: API_KEY
47
  - **HF Token Variable**: HF_TOKEN (for auto-updates)
48
  - **Access Control**: Enabled (ACCESS_CODE)
 
1
  ---
2
+ title: STEM Adventure Games
3
  emoji: 💬
4
  colorFrom: blue
5
  colorTo: green
6
  sdk: gradio
7
+ sdk_version: 5.39.0
8
  app_file: app.py
9
  pinned: false
10
  license: mit
11
+ short_description: Interactive STEM adventure game guide
12
  ---
13
 
14
+ # STEM Adventure Games
15
 
16
+ Interactive STEM adventure game guide
17
 
18
  ## Quick Setup
19
 
 
42
  Your Space should now be running! Try the example prompts or ask your own questions.
43
 
44
  ## Configuration
45
+ - **Model**: qwen/qwen3-30b-a3b-instruct-2507
46
  - **API Key Variable**: API_KEY
47
  - **HF Token Variable**: HF_TOKEN (for auto-updates)
48
  - **Access Control**: Enabled (ACCESS_CODE)
app.py CHANGED
@@ -12,23 +12,23 @@ from typing import List, Dict, Optional, Any, Tuple
12
 
13
 
14
  # Configuration
15
- SPACE_NAME = 'Course Assistant Example'
16
- SPACE_DESCRIPTION = 'Python support for cultural analytics students'
17
 
18
  # Default configuration values
19
  DEFAULT_CONFIG = {
20
  'name': SPACE_NAME,
21
  'description': SPACE_DESCRIPTION,
22
- 'system_prompt': "You're a Python guide for CCNY's CSC 10800 where September covers foundations (command line, Jupyter, script anatomy), October builds programming basics (data types through functions) with Activities 1-2, and November-December advances to pandas, network analysis, and data collection with Activities 3-5, culminating in a Social Coding Portfolio. Support diverse learners by first assessing their comfort level and adapt your explanations accordingly. Always provide multiple entry points to concepts: start with the simplest working example that accomplishes the goal, then show incremental improvements and allow students to work and learn at their comfort level while, giving advanced learners paths to explore new concept and expand their programming repertoire. Expect to complete all responses in under 1000 tokens.",
23
- 'temperature': 0.5,
24
- 'max_tokens': 1000,
25
- 'model': 'openai/gpt-oss-120b',
26
  'api_key_var': 'API_KEY',
27
- 'theme': 'Default',
28
- 'grounding_urls': ["https://zmuhls.github.io/ccny-data-science/syllabus/", "https://zmuhls.github.io/ccny-data-science/schedule/"],
29
  'enable_dynamic_urls': True,
30
  'enable_file_upload': True,
31
- 'examples': ['How do I set up a interactive development environment?', 'Where can I find the course schedule?', 'When is the social coding portfolio due?', 'How do I push a commit to GitHub?', "I'm confused on how to use Jupyter notebooks"],
32
  'language': 'English',
33
  'locked': False
34
  }
@@ -329,18 +329,17 @@ def get_grounding_context() -> str:
329
  return ""
330
 
331
 
332
- def export_conversation_to_text(history: List[Dict[str, str]]) -> str:
333
- """Export conversation history to text with timestamps"""
334
  if not history:
335
  return "No conversation to export."
336
 
337
- text_content = f"""Conversation Export
338
- ==================
339
  Generated on: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}
340
  Space: {SPACE_NAME}
341
  Model: {MODEL}
342
 
343
- ==================
344
 
345
  """
346
 
@@ -350,28 +349,13 @@ Model: {MODEL}
350
  role = message.get('role', 'unknown')
351
  content = message.get('content', '')
352
 
353
- # Get timestamp from message or use current time as fallback
354
- timestamp_str = message.get('timestamp', '')
355
- if timestamp_str:
356
- try:
357
- # Parse ISO format timestamp and format it nicely
358
- timestamp = datetime.fromisoformat(timestamp_str)
359
- formatted_timestamp = timestamp.strftime('%Y-%m-%d %H:%M:%S')
360
- except:
361
- formatted_timestamp = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
362
- else:
363
- formatted_timestamp = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
364
-
365
- # Get message length
366
- msg_length = message.get('length', len(content))
367
-
368
  if role == 'user':
369
  message_count += 1
370
- text_content += f"[{formatted_timestamp}] User Message {message_count} ({msg_length} chars):\n{content}\n\n"
371
  elif role == 'assistant':
372
- text_content += f"[{formatted_timestamp}] Assistant Response {message_count} ({msg_length} chars):\n{content}\n\n------------------\n\n"
373
 
374
- return text_content
375
 
376
 
377
  def generate_response(message: str, history: List[Dict[str, str]], files: Optional[List] = None) -> str:
@@ -439,7 +423,7 @@ Get your API key at: https://openrouter.ai/keys"""
439
  system_content += f"\n\nIMPORTANT: You must respond EXCLUSIVELY in {LANGUAGE}. All your responses should be written entirely in {LANGUAGE}, even when user input is in a different language, particularly English."
440
 
441
  if grounding_context:
442
- system_content += "\n\nIMPORTANT: When providing information from the reference sources below, please cite the specific URL(s) where the information can be found. Format citations as plain URLs without brackets, or use markdown link format like [text](url). Never use hard brackets like 【url】 as they break the links."
443
  system_content = f"{system_content}\n\n{grounding_context}"
444
  if file_context:
445
  system_content = f"{system_content}\n\n{file_context}"
@@ -592,11 +576,7 @@ def create_interface():
592
 
593
  # Create chat interface
594
  chatbot = gr.Chatbot(type="messages", height=400)
595
- msg = gr.Textbox(
596
- label="Message (Shift+Enter to send)",
597
- placeholder="Type your message here...",
598
- lines=2
599
- )
600
 
601
  with gr.Row():
602
  submit_btn = gr.Button("Send", variant="primary")
@@ -604,46 +584,33 @@ def create_interface():
604
 
605
  # Export functionality
606
  with gr.Row():
607
- # Use a regular Button for triggering export
608
- export_trigger_btn = gr.Button(
609
  "📥 Export Conversation",
610
  variant="secondary",
611
  size="sm"
612
  )
613
- # Hidden file component for actual download
614
- export_file = gr.File(
615
- visible=False,
616
- label="Download Export"
617
- )
618
 
619
  # Export handler
620
- def prepare_export(chat_history):
621
- if not chat_history:
622
- gr.Warning("No conversation history to export.")
623
  return None
624
 
625
- try:
626
- content = export_conversation_to_text(chat_history)
627
-
628
- # Create filename
629
- space_name_safe = re.sub(r'[^a-zA-Z0-9]+', '_', SPACE_NAME).lower()
630
- timestamp = datetime.now().strftime('%Y%m%d_%H%M%S')
631
- filename = f"{space_name_safe}_conversation_{timestamp}.txt"
632
-
633
- # Save to temp file
634
- temp_path = Path(tempfile.gettempdir()) / filename
635
- temp_path.write_text(content, encoding='utf-8')
636
-
637
- # Return the file component with visibility and value
638
- return gr.File(visible=True, value=str(temp_path))
639
- except Exception as e:
640
- gr.Error(f"Failed to export conversation: {str(e)}")
641
- return None
642
 
643
- export_trigger_btn.click(
644
  prepare_export,
645
- inputs=[chatbot],
646
- outputs=[export_file]
647
  )
648
 
649
  # Examples section
@@ -667,13 +634,10 @@ def create_interface():
667
  # Get response
668
  response = generate_response(message, formatted_history, files_state)
669
 
670
- # Get current timestamp
671
- current_time = datetime.now()
672
-
673
- # Update chat history with timestamps and message lengths
674
  chat_history = chat_history + [
675
- {"role": "user", "content": message, "timestamp": current_time.isoformat(), "length": len(message)},
676
- {"role": "assistant", "content": response, "timestamp": current_time.isoformat(), "length": len(response)}
677
  ]
678
 
679
  # Update stored history for export
@@ -685,13 +649,7 @@ def create_interface():
685
  # Wire up the interface
686
  msg.submit(respond, [msg, chatbot, uploaded_files, access_granted], [chatbot, msg, access_granted])
687
  submit_btn.click(respond, [msg, chatbot, uploaded_files, access_granted], [chatbot, msg, access_granted])
688
-
689
- def clear_chat():
690
- global chat_history_store
691
- chat_history_store = []
692
- return [], ""
693
-
694
- clear_btn.click(clear_chat, outputs=[chatbot, msg])
695
 
696
  # File upload accordion
697
  if ENABLE_FILE_UPLOAD:
@@ -804,9 +762,8 @@ def create_interface():
804
  "anthropic/claude-3.5-sonnet",
805
  "anthropic/claude-3.5-haiku",
806
  # OpenAI models
807
- "openai/gpt-4o-mini",
808
  "openai/gpt-4o-mini-search-preview",
809
- "openai/gpt-oss-120b",
810
  # MistralAI models
811
  "mistralai/mistral-medium-3",
812
  # DeepSeek models
@@ -1061,41 +1018,6 @@ def create_interface():
1061
  inputs=[access_input, access_granted],
1062
  outputs=[access_panel, main_panel, access_status, access_granted]
1063
  )
1064
-
1065
-
1066
- # Add keyboard shortcuts
1067
- demo.load(
1068
- None,
1069
- None,
1070
- None,
1071
- js="""
1072
- () => {
1073
- // Focus on message input when page loads
1074
- setTimeout(() => {
1075
- const msgInput = document.querySelector('textarea');
1076
- if (msgInput) msgInput.focus();
1077
- }, 100);
1078
-
1079
- // Keyboard shortcuts
1080
- document.addEventListener('keydown', function(e) {
1081
- // Ctrl+L to clear chat
1082
- if (e.ctrlKey && e.key === 'l') {
1083
- e.preventDefault();
1084
- const buttons = Array.from(document.querySelectorAll('button'));
1085
- const clearBtn = buttons.find(btn => btn.textContent.includes('Clear'));
1086
- if (clearBtn) clearBtn.click();
1087
- }
1088
- // Ctrl+E to export
1089
- else if (e.ctrlKey && e.key === 'e') {
1090
- e.preventDefault();
1091
- const buttons = Array.from(document.querySelectorAll('button'));
1092
- const exportBtn = buttons.find(btn => btn.textContent.includes('Export'));
1093
- if (exportBtn) exportBtn.click();
1094
- }
1095
- });
1096
- }
1097
- """
1098
- )
1099
 
1100
  return demo
1101
 
 
12
 
13
 
14
  # Configuration
15
+ SPACE_NAME = 'STEM Adventure Games'
16
+ SPACE_DESCRIPTION = 'Interactive STEM adventure game guide'
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 a unicode arcade menu that welcomes users and frames the game in 2-3 sentences, then presents 3-4 adventures to choose from before proceeding based on user input. Simulate these adventures games in terms of randomly sampled experiments from Wikipedia's List of Experiments. Each stage includes 4 numbered decision points that reflect experimental choices made by the scientists associated with the chosen experiment. Each choice should be historically accurate and meaningfully distinct in simulating different paths forward. Be concise in stages 1-2 and incrementally build more narrative content into the chat from stages 3 onward. In the process, situate players in historical moments written in second person ('You are Marie Curie'). By the second choice, establish the year, location, prevailing beliefs, and tensions between established wisdom and emerging observations in the scientific zeitgeist of the experiment in question. Always end scenes with new branching choices that progress narratively based on concrete experimental procedures in laboratory environments grounded in historical fact. Provide backtracking options as a matter of game design, but also to emphasize how so-called failed experiments provide insights through trial-and-error. Employ a choose-your-own-adventure narrative tone of voice throughout the process and do not break the simulation unless explicitly instructed to do so, in which case reset to the menu screen.",
23
+ 'temperature': 0.9,
24
+ 'max_tokens': 750,
25
+ 'model': 'qwen/qwen3-30b-a3b-instruct-2507',
26
  'api_key_var': 'API_KEY',
27
+ 'theme': 'Base',
28
+ 'grounding_urls': ["https://en.wikipedia.org/wiki/List_of_experiments", "https://en.wikipedia.org/wiki/Scientific_method"],
29
  'enable_dynamic_urls': True,
30
  'enable_file_upload': True,
31
+ 'examples': ['Initiate adventure!', 'How do I play?', "What's the meaning of this?"],
32
  'language': 'English',
33
  'locked': False
34
  }
 
329
  return ""
330
 
331
 
332
+ def export_conversation_to_markdown(history: List[Dict[str, str]]) -> str:
333
+ """Export conversation history to markdown"""
334
  if not history:
335
  return "No conversation to export."
336
 
337
+ markdown_content = f"""# Conversation Export
 
338
  Generated on: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}
339
  Space: {SPACE_NAME}
340
  Model: {MODEL}
341
 
342
+ ---
343
 
344
  """
345
 
 
349
  role = message.get('role', 'unknown')
350
  content = message.get('content', '')
351
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
352
  if role == 'user':
353
  message_count += 1
354
+ markdown_content += f"## User Message {message_count}\n\n{content}\n\n"
355
  elif role == 'assistant':
356
+ markdown_content += f"## Assistant Response {message_count}\n\n{content}\n\n---\n\n"
357
 
358
+ return markdown_content
359
 
360
 
361
  def generate_response(message: str, history: List[Dict[str, str]], files: Optional[List] = None) -> str:
 
423
  system_content += f"\n\nIMPORTANT: You must respond EXCLUSIVELY in {LANGUAGE}. All your responses should be written entirely in {LANGUAGE}, even when user input is in a different language, particularly English."
424
 
425
  if grounding_context:
426
+ system_content += "\n\nIMPORTANT: When providing information from the reference sources below, please cite the specific URL(s) where the information can be found."
427
  system_content = f"{system_content}\n\n{grounding_context}"
428
  if file_context:
429
  system_content = f"{system_content}\n\n{file_context}"
 
576
 
577
  # Create chat interface
578
  chatbot = gr.Chatbot(type="messages", height=400)
579
+ msg = gr.Textbox(label="Message", placeholder="Type your message here...", lines=2)
 
 
 
 
580
 
581
  with gr.Row():
582
  submit_btn = gr.Button("Send", variant="primary")
 
584
 
585
  # Export functionality
586
  with gr.Row():
587
+ export_btn = gr.DownloadButton(
 
588
  "📥 Export Conversation",
589
  variant="secondary",
590
  size="sm"
591
  )
 
 
 
 
 
592
 
593
  # Export handler
594
+ def prepare_export():
595
+ if not chat_history_store:
 
596
  return None
597
 
598
+ content = export_conversation_to_markdown(chat_history_store)
599
+
600
+ # Create filename
601
+ space_name_safe = re.sub(r'[^a-zA-Z0-9]+', '_', SPACE_NAME).lower()
602
+ timestamp = datetime.now().strftime('%Y%m%d_%H%M%S')
603
+ filename = f"{space_name_safe}_conversation_{timestamp}.md"
604
+
605
+ # Save to temp file
606
+ temp_path = Path(tempfile.gettempdir()) / filename
607
+ temp_path.write_text(content, encoding='utf-8')
608
+
609
+ return str(temp_path)
 
 
 
 
 
610
 
611
+ export_btn.click(
612
  prepare_export,
613
+ outputs=[export_btn]
 
614
  )
615
 
616
  # Examples section
 
634
  # Get response
635
  response = generate_response(message, formatted_history, files_state)
636
 
637
+ # Update chat history
 
 
 
638
  chat_history = chat_history + [
639
+ {"role": "user", "content": message},
640
+ {"role": "assistant", "content": response}
641
  ]
642
 
643
  # Update stored history for export
 
649
  # Wire up the interface
650
  msg.submit(respond, [msg, chatbot, uploaded_files, access_granted], [chatbot, msg, access_granted])
651
  submit_btn.click(respond, [msg, chatbot, uploaded_files, access_granted], [chatbot, msg, access_granted])
652
+ clear_btn.click(lambda: ([], ""), outputs=[chatbot, msg])
 
 
 
 
 
 
653
 
654
  # File upload accordion
655
  if ENABLE_FILE_UPLOAD:
 
762
  "anthropic/claude-3.5-sonnet",
763
  "anthropic/claude-3.5-haiku",
764
  # OpenAI models
 
765
  "openai/gpt-4o-mini-search-preview",
766
+ "openai/gpt-4.1-nano",
767
  # MistralAI models
768
  "mistralai/mistral-medium-3",
769
  # DeepSeek models
 
1018
  inputs=[access_input, access_granted],
1019
  outputs=[access_panel, main_panel, access_status, access_granted]
1020
  )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1021
 
1022
  return demo
1023
 
config.json CHANGED
@@ -1,25 +1,23 @@
1
  {
2
- "name": "Course Assistant Example",
3
- "tagline": "Python support for cultural analytics students",
4
- "description": "Python support for cultural analytics students",
5
- "system_prompt": "You're a Python guide for CCNY's CSC 10800 where September covers foundations (command line, Jupyter, script anatomy), October builds programming basics (data types through functions) with Activities 1-2, and November-December advances to pandas, network analysis, and data collection with Activities 3-5, culminating in a Social Coding Portfolio. Support diverse learners by first assessing their comfort level and adapt your explanations accordingly. Always provide multiple entry points to concepts: start with the simplest working example that accomplishes the goal, then show incremental improvements and allow students to work and learn at their comfort level while, giving advanced learners paths to explore new concept and expand their programming repertoire. Expect to complete all responses in under 1000 tokens.",
6
- "model": "openai/gpt-oss-120b",
7
  "language": "English",
8
  "api_key_var": "API_KEY",
9
- "temperature": 0.5,
10
- "max_tokens": 1000,
11
  "examples": [
12
- "How do I set up a interactive development environment?",
13
- "Where can I find the course schedule?",
14
- "When is the social coding portfolio due?",
15
- "How do I push a commit to GitHub?",
16
- "I'm confused on how to use Jupyter notebooks"
17
  ],
18
  "grounding_urls": [
19
- "https://zmuhls.github.io/ccny-data-science/syllabus/",
20
- "https://zmuhls.github.io/ccny-data-science/schedule/"
21
  ],
22
  "enable_dynamic_urls": true,
23
  "enable_file_upload": true,
24
- "theme": "Default"
25
  }
 
1
  {
2
+ "name": "STEM Adventure Games",
3
+ "tagline": "Interactive STEM adventure game guide",
4
+ "description": "Interactive STEM adventure game guide",
5
+ "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 a unicode arcade menu that welcomes users and frames the game in 2-3 sentences, then presents 3-4 adventures to choose from before proceeding based on user input. Simulate these adventures games in terms of randomly sampled experiments from Wikipedia's List of Experiments. Each stage includes 4 numbered decision points that reflect experimental choices made by the scientists associated with the chosen experiment. Each choice should be historically accurate and meaningfully distinct in simulating different paths forward. Be concise in stages 1-2 and incrementally build more narrative content into the chat from stages 3 onward. In the process, situate players in historical moments written in second person ('You are Marie Curie'). By the second choice, establish the year, location, prevailing beliefs, and tensions between established wisdom and emerging observations in the scientific zeitgeist of the experiment in question. Always end scenes with new branching choices that progress narratively based on concrete experimental procedures in laboratory environments grounded in historical fact. Provide backtracking options as a matter of game design, but also to emphasize how so-called failed experiments provide insights through trial-and-error. Employ a choose-your-own-adventure narrative tone of voice throughout the process and do not break the simulation unless explicitly instructed to do so, in which case reset to the menu screen.",
6
+ "model": "qwen/qwen3-30b-a3b-instruct-2507",
7
  "language": "English",
8
  "api_key_var": "API_KEY",
9
+ "temperature": 0.9,
10
+ "max_tokens": 750,
11
  "examples": [
12
+ "Initiate adventure!",
13
+ "How do I play?",
14
+ "What's the meaning of this?"
 
 
15
  ],
16
  "grounding_urls": [
17
+ "https://en.wikipedia.org/wiki/List_of_experiments",
18
+ "https://en.wikipedia.org/wiki/Scientific_method"
19
  ],
20
  "enable_dynamic_urls": true,
21
  "enable_file_upload": true,
22
+ "theme": "Base"
23
  }