import gradio as gr import random from huggingface_hub import InferenceClient from sentence_transformers import SentenceTransformer import torch # This is the same pattern from the Generative AI lesson! It uses the # Inference Provider API to send your messages to an AI model and get # a response back. Swap out the model below for a different one if # you want to experiment! # # Note: if this Space doesn't already have one, you'll need to add an # HF_TOKEN secret in the Space's Settings tab for this to work # (Settings -> Variables and secrets -> New secret). client = InferenceClient("Qwen/Qwen2.5-7B-Instruct", bill_to="kode-with-klossy") # Open the water_cycle.txt file in read mode with UTF-8 encoding with open("knowledge.txt", "r", encoding="utf-8") as file: # Read the entire contents of the file and store it in a variable knowledge_text = file.read() def preprocess_text(text): # Strip extra whitespace from the beginning and the end of the text cleaned_text = text.strip() # Split the cleaned_text by every newline character (\n) chunks = cleaned_text.split("\n") # Create an empty list to store cleaned chunks cleaned_chunks = [] # Write your for-in loop below to clean each chunk and add it to the cleaned_chunks list # This is only one way scholars may write this, but there are other ways! for chunk in chunks: stripped_chunk = chunk.strip() if len(stripped_chunk) > 0: cleaned_chunks.append(stripped_chunk) # Return the cleaned_chunks return cleaned_chunks # Call the preprocess_text function and store the result in a cleaned_chunks variable cleaned_chunks = preprocess_text(knowledge_text) # Load the pre-trained embedding model that converts text to vectors model = SentenceTransformer('all-MiniLM-L6-v2') def create_embeddings(text_chunks): # Convert each text chunk into a vector embedding and store as a tensor chunk_embeddings = model.encode(text_chunks, convert_to_tensor=True) # Replace ... with the cleaned_chunks list # Return the chunk_embeddings return chunk_embeddings # Call the create_embeddings function and store the result in a new chunk_embeddings variable chunk_embeddings = create_embeddings(cleaned_chunks) # Complete this line # Define a function to find the most relevant text chunks for a given query, chunk_embeddings, and text_chunks def get_top_chunks(query, chunk_embeddings, text_chunks): # Convert the query text into a vector embedding query_embedding = model.encode(query, convert_to_tensor=True) # Complete this line # Normalize the query embedding to unit length for accurate similarity comparison query_embedding_normalized = query_embedding / query_embedding.norm() # Normalize all chunk embeddings to unit length for consistent comparison chunk_embeddings_normalized = chunk_embeddings / chunk_embeddings.norm(dim=1, keepdim=True) # Calculate cosine similarity between all chunks and the query using matrix multiplication similarities = torch.matmul(chunk_embeddings_normalized, query_embedding_normalized) # Complete this line # Find the indices of the 3 chunks with highest similarity scores top_indices = torch.topk(similarities, k=3).indices # Create an empty list to store the most relevant chunks top_chunks = [] # Loop through the top indices and retrieve the corresponding text chunks # This is only one way scholars may write this, but there are other ways! for i in top_indices: chunk = text_chunks[i] top_chunks.append(chunk) # Return the list of most relevant chunks return top_chunks def respond(message, history): top_results = get_top_chunks(message,chunk_embeddings, cleaned_chunks) context = "\n".join(top_results) systemMessage =( "You are a STEMspark chatbot ✨, and u support high school students interested in STEM." "You are AI STEM Mentor, a friendly AI tutor, study coach, and STEM mentor " "for high school students. Explain STEM concepts clearly, answer questions, " "help with homework through hints and step-by-step guidance, generate quizzes " "or flashcards when requested, and suggest free courses, scholarships, and " "STEM competitions when relevant. Encourage students to stay motivated and" "build confidence in learning. Keep responses clear, supportive, and under" "150 words unless the user asks for more detail." "Use the following context from our knowledge base to help answer the user's question:\n" f"--- CONTEXT ---\n{context}\n---------------\n\n" "Your goals are to:\n" ) messages = [{"role": "system", "content": systemMessage}] if history: messages.extend(history) messages.append({"role": "user", "content": message}) response = client.chat_completion( messages, max_tokens=200, temperature = 0.8 ) return response.choices[0].message.content.strip() # Your team's customized ChatInterface chatbot = gr.ChatInterface( fn=respond, title="Stem Spark", description=( "STEMSpark is an AI STEM mentor that helps high school students learn more effectively. " "It answers STEM questions, explains difficult concepts in a simple way, generates quizzes " "and flashcards, summarizes chapters, recommends free courses, competitions, and scholarships, " "creates personalized learning roadmaps, and helps students stay organized with study reminders, " "a Pomodoro timer, and a to-do list." ), examples=[ "Do you need help learning effectively?", "Do you want to do well in your studies?", "Do you want to excel in your STEM subjects?", ], ) # Add background custom_css = """ body{ background-image: url("https://huggingface.co/spaces/kode-with-klossy/3.4-groupA2-capstone/raw/main/background.jpg") background-size: cover; background-position: center; } /* Background */ .gradio-container { background: #D9F5F0; background-image: url("https://huggingface.co/spaces/kode-with-klossy/3.4-groupA2-capstone/raw/main/background.jpg") background-size: cover; background-position: center; } /* Title */ #chat-title { color: #1C4EA7; text-align: center; } /* Description */ #chat-desc { color: #024D60; text-align: center; margin-bottom: 20px; } /* Chat window */ .gradio-chatbot { background: white; border-radius: 15px; border: 2px solid #75E2E0; } /* Buttons */ button { background-color: #1C4EA7 !important; color: white !important; border-radius: 10px !important; } """ # ========================================================= # BEGIN Your Added To-Do List Feature (No Emojis) # ========================================================= MOTIVATIONAL_QUOTES = [ "'Nothing in life is to be feared, it is only to be understood.' - Marie Curie", "'The most difficult thing is the decision to act, the rest is merely tenacity.' - Amelia Earhart", "'You are capable of achieving anything you set your mind to in STEM!'", "'Great job! Every task finished is a step closer to changing the world with science!'", "'Don't let anyone rob you of your imagination, your creativity, or your curiosity.' - Dr. Mae Jemison", "'You crushed that task! Keep shining bright in your study journey!'" ] tasks = [] completed_tasks = [] def update_todo_ui(): all_choices = [f"[Completed] {t}" for t in completed_tasks] + tasks total = len(completed_tasks) + len(tasks) progress = int((len(completed_tasks) / total) * 100) if total > 0 else 0 status_text = f"### Progress: {len(completed_tasks)} / {total} tasks completed ({progress}%)" return gr.update(choices=all_choices, value=[f"[Completed] {t}" for t in completed_tasks]), status_text def add_task(new_task): if new_task.strip(): tasks.append(new_task.strip()) updated_choices, progress = update_todo_ui() return "", updated_choices, progress, "" def handle_task_completion(selected_items): quote_message = "" for item in selected_items: clean_item = item.replace("[Completed] ", "") if clean_item in tasks: tasks.remove(clean_item) completed_tasks.append(clean_item) # Pick a random aesthetic quote upon task completion quote_message = f"
{random.choice(MOTIVATIONAL_QUOTES)}" updated_choices, progress = update_todo_ui() return updated_choices, progress, quote_message def delete_completed_tasks(): completed_tasks.clear() updated_choices, progress = update_todo_ui() return updated_choices, progress, "Completed tasks cleared." # ========================================================= # POMODORO TIMER # ========================================================= # Standard wrapper block to present your feature alongside the team's ChatInterface with gr.Blocks(title="STEMSpark Mentor", css = custom_css) as demo: # 1. Render your team's customized chatbot interface cleanly chatbot.render() # 2. Render your To-Do feature right below it gr.Markdown("---") gr.Markdown("## STEM Study Task Tracker") gr.Markdown("Organize your daily study goals and track your progress.") progress_display = gr.Markdown("### Progress: 0 / 0 tasks completed (0%)") quote_display = gr.HTML("") with gr.Row(): task_input = gr.Textbox(placeholder="Enter a new study task...", show_label=False, scale=4) add_btn = gr.Button("Add Task", variant="primary", scale=1) task_checkboxes = gr.CheckboxGroup(choices=[], label="Tasks", interactive=True) clear_btn = gr.Button("Clear Completed Tasks", variant="stop") # Interactions add_btn.click(add_task, inputs=[task_input], outputs=[task_input, task_checkboxes, progress_display, quote_display]) task_input.submit(add_task, inputs=[task_input], outputs=[task_input, task_checkboxes, progress_display, quote_display]) task_checkboxes.change(handle_task_completion, inputs=[task_checkboxes], outputs=[task_checkboxes, progress_display, quote_display]) clear_btn.click(delete_completed_tasks, outputs=[task_checkboxes, progress_display, quote_display]) # ========================================================= # Pomodoro below # ========================================================= gr.Markdown("---") gr.Markdown("## Pomodoro Study Timer") gr.Markdown("Choose a study duration, then use the timer while studying.") study_time = gr.Dropdown( choices=[2, 5, 10, 15, 25, 30, 45, 60], value=25, label="Study Time (minutes)" ) timer_display = gr.Textbox( value="25:00", label="Timer", interactive=False ) seconds_state = gr.State(25 * 60) pomo_timer = gr.Timer(value=1, active=False) def format_time(remaining_seconds): mins, secs = divmod(max(remaining_seconds, 0), 60) return f"{mins:02}:{secs:02}" def reset_timer(minutes): remaining_seconds = int(minutes) * 60 return remaining_seconds, format_time(remaining_seconds), gr.update(active=False) def start_timer_fn(minutes, remaining_seconds): # Resume if paused mid-countdown, otherwise start fresh if not remaining_seconds: remaining_seconds = int(minutes) * 60 return remaining_seconds, format_time(remaining_seconds), gr.update(active=True) def pause_timer_fn(): return gr.update(active=False) def tick(remaining_seconds): remaining_seconds -= 1 if remaining_seconds <= 0: gr.Info("Study session complete! Time for a break!") return 0, "00:00", gr.update(active=False) return remaining_seconds, format_time(remaining_seconds), gr.update() study_time.change( fn=reset_timer, inputs=[study_time], outputs=[seconds_state, timer_display, pomo_timer] ) start_btn = gr.Button("▶ Start") pause_btn = gr.Button("Pause") stop_btn = gr.Button("Stop") start_btn.click( fn=start_timer_fn, inputs=[study_time, seconds_state], outputs=[seconds_state, timer_display, pomo_timer] ) pause_btn.click(fn=pause_timer_fn, outputs=[pomo_timer]) stop_btn.click( fn=reset_timer, inputs=[study_time], outputs=[seconds_state, timer_display, pomo_timer] ) pomo_timer.tick( fn=tick, inputs=[seconds_state], outputs=[seconds_state, timer_display, pomo_timer] ) demo.launch() # TODO: This is just a starting point! Customize the system prompt, # the model, and the interface to make this project your own! # Customize colors, sizing and fonts