louiecerv commited on
Commit
4a228f8
·
2 Parent(s): 8a05d0a 20a94fa

Merge branch 'main' of https://huggingface.co/spaces/louiecerv/problem_solving_ai_tutor

Browse files
app.py CHANGED
@@ -3,7 +3,23 @@ import os
3
  import google.generativeai as genai
4
  import json
5
  from PIL import Image
 
 
 
6
 
 
 
 
 
 
 
 
 
 
 
 
 
 
7
 
8
  MODEL_ID = "gemini-2.0-flash-exp"
9
  try:
@@ -17,17 +33,17 @@ except Exception as e:
17
  model = genai.GenerativeModel(MODEL_ID)
18
  chat = model.start_chat()
19
 
20
- def get_local_pdf_path():
21
  """
22
  Returns the path to the local PDF file.
23
  """
24
  try:
25
- pdf_path = os.path.join("problems", "problems.pdf")
26
- if not os.path.exists(pdf_path):
27
- raise FileNotFoundError(f"{pdf_path} does not exist.")
28
- return pdf_path
29
  except Exception as e:
30
- st.error(f"Failed to find the local PDF: {e}")
31
  st.stop() # Stop if the file is not found
32
 
33
  # Initialize conversation history in Streamlit session state
@@ -36,35 +52,62 @@ if "conversation_history" not in st.session_state:
36
  if "uploaded_file_part" not in st.session_state: # Store the file *part*
37
  st.session_state.uploaded_file_part = None
38
  if "uploaded_pdf_path" not in st.session_state:
39
- st.session_state.uploaded_pdf_path = get_local_pdf_path()
40
 
41
- def multimodal_prompt(pdf_path, text_prompt):
42
  """
43
  Sends a multimodal prompt to Gemini, handling file uploads efficiently.
44
  Args:
45
- pdf_path: The path to the PDF file.
46
  text_prompt: The text prompt for the model.
 
47
  Returns:
48
  The model's response as a string, or an error message.
49
  """
50
  try:
51
- if st.session_state.uploaded_file_part is None: # First time, upload
52
- pdf_part = genai.upload_file(pdf_path, mime_type="application/pdf")
53
- st.session_state.uploaded_file_part = pdf_part
54
- prompt = [text_prompt, pdf_part] # First turn includes the actual file
55
- else: # Subsequent turns, reference the file
56
- prompt = [text_prompt, st.session_state.uploaded_file_part] # Subsequent turns include the file reference
 
 
 
 
 
 
57
 
58
  response = chat.send_message(prompt)
59
 
60
  # Update conversation history
61
- st.session_state.conversation_history.append({"role": "user", "content": text_prompt, "has_pdf": True})
62
  st.session_state.conversation_history.append({"role": "assistant", "content": response.text})
63
  return response.text
64
 
65
  except Exception as e:
66
  return f"An error occurred: {e}"
67
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
68
 
69
  # --- Main Page ---
70
  st.title("📚❓Problem Solving Tutor")
@@ -73,79 +116,66 @@ about = """
73
  Replace this placeholder with the actual text.
74
  """
75
 
76
-
77
- import re
78
- import json
79
-
80
- # Define constants
81
- TEXT_PROMPT = """Use the provided document. Read the list of 5 quadratic equations.
82
- Return your response as a JSON list. Do not include any extra text, explanations, or backslashes.
83
-
84
- Example JSON output:
85
- [
86
- "x^2 - 5x + 6 = 0",
87
- "2x^2 + 3x - 1 = 0",
88
- "x^2 - 9 = 0",
89
- "3x^2 - 2x + 4 = 0",
90
- "x^2 + 8x + 15 = 0"
91
- ]
92
- """
93
-
94
- # Define a function to extract equations from the AI response
95
- def extract_equations(response):
96
- try:
97
- if isinstance(response, str):
98
- response = response.strip().replace("\n", "").replace("\r", "")
99
- if response.lower().startswith("json"):
100
- response = response[4:].strip()
101
- if response.startswith("[") and response.endswith("]"):
102
- return json.loads(response)
103
- else:
104
- st.error("Error: AI response is not in expected JSON list format.")
105
- return []
106
- elif isinstance(response, list):
107
- return response
108
- else:
109
- st.error("Error: Unexpected response format from AI.")
110
- return []
111
- except json.JSONDecodeError:
112
- st.error("Error: Failed to parse AI response as a list.")
113
- return []
114
-
115
- # Define a function to extract quadratic equations from the problems
116
- def extract_quadratic_equations(problems):
117
- equations = []
118
- for problem in problems:
119
- match = re.search(r'([0-9x\^\+\-\=\s]+)', problem)
120
- if match:
121
- equations.append(match.group(1).strip())
122
- else:
123
- st.warning(f"Could not extract equation from: '{problem}'")
124
- return equations
125
-
126
- # Main code
127
- with st.spinner("AI is thinking..."):
128
  if st.session_state.get("uploaded_pdf_path") is None:
129
- st.session_state.uploaded_pdf_path = get_local_pdf_path()
130
 
131
  filepath = st.session_state.uploaded_pdf_path
132
- response = multimodal_prompt(filepath, TEXT_PROMPT)
133
-
134
- # Debugging: Print response
135
- st.write("Raw AI Response:", response)
136
-
137
- # Extract equations
138
- problems = extract_equations(response)
139
- if problems:
140
- equations = extract_quadratic_equations(problems)
141
- st.write("Extracted Equations:")
142
- for equation in equations:
143
- st.write(equation)
144
- else:
145
- st.error("Error: No valid equations extracted.")
146
-
147
- except Exception as e:
148
- st.error(f"An unexpected error occurred: {e}")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
149
 
150
 
151
  st.markdown("Visit our Hugging Face Space!")
 
3
  import google.generativeai as genai
4
  import json
5
  from PIL import Image
6
+ import re
7
+ import json
8
+ import tempfile
9
 
10
+ # Define constants
11
+ TEXT_PROMPT = """Use the provided document. Read the list of quadratic equations.
12
+ Return your response as a JSON list. Do not include any extra text, explanations, or backslashes.
13
+
14
+ Example JSON output:
15
+ [
16
+ "x^2 - 5x + 6 = 0",
17
+ "2x^2 + 3x - 1 = 0",
18
+ "x^2 - 9 = 0",
19
+ "3x^2 - 2x + 4 = 0",
20
+ "x^2 + 8x + 15 = 0"
21
+ ]
22
+ """
23
 
24
  MODEL_ID = "gemini-2.0-flash-exp"
25
  try:
 
33
  model = genai.GenerativeModel(MODEL_ID)
34
  chat = model.start_chat()
35
 
36
+ def get_local_file_path(img_file="problem1.png"):
37
  """
38
  Returns the path to the local PDF file.
39
  """
40
  try:
41
+ file_path = os.path.join("problems", img_file)
42
+ if not os.path.exists(file_path):
43
+ raise FileNotFoundError(f"{file_path} does not exist.")
44
+ return file_path
45
  except Exception as e:
46
+ st.error(f"Failed to find the local file: {e}")
47
  st.stop() # Stop if the file is not found
48
 
49
  # Initialize conversation history in Streamlit session state
 
52
  if "uploaded_file_part" not in st.session_state: # Store the file *part*
53
  st.session_state.uploaded_file_part = None
54
  if "uploaded_pdf_path" not in st.session_state:
55
+ st.session_state.uploaded_pdf_path = get_local_file_path()
56
 
57
+ def multimodal_prompt(pdf_path, text_prompt, file_type="PDF"):
58
  """
59
  Sends a multimodal prompt to Gemini, handling file uploads efficiently.
60
  Args:
61
+ pdf_path: The path to the file (PDF or image).
62
  text_prompt: The text prompt for the model.
63
+ file_type: "PDF" or "image" to specify the file type.
64
  Returns:
65
  The model's response as a string, or an error message.
66
  """
67
  try:
68
+ if file_type == "PDF":
69
+ mime_type = "application/pdf"
70
+ elif file_type == "image":
71
+ import mimetypes
72
+ mime_type, _ = mimetypes.guess_type(pdf_path)
73
+ if mime_type is None:
74
+ return "Could not determine MIME type for image. Please check the file path or type."
75
+ else:
76
+ return "Invalid file_type. Must be 'PDF' or 'image'."
77
+
78
+ pdf_part = genai.upload_file(pdf_path, mime_type=mime_type)
79
+ prompt = [text_prompt, pdf_part] # First turn includes the actual file
80
 
81
  response = chat.send_message(prompt)
82
 
83
  # Update conversation history
84
+ st.session_state.conversation_history.append({"role": "user", "content": text_prompt, "has_file": True})
85
  st.session_state.conversation_history.append({"role": "assistant", "content": response.text})
86
  return response.text
87
 
88
  except Exception as e:
89
  return f"An error occurred: {e}"
90
 
91
+ def get_equation(response):
92
+ # Remove the ```json and ``` and extra spaces.
93
+ try:
94
+ json_string = response.replace('```json', '').replace('```', '').strip()
95
+
96
+ # Parse the JSON string into a Python list.
97
+ problems_list = json.loads(json_string)
98
+
99
+ # return the first item found
100
+ return problems_list[0]
101
+
102
+ except json.JSONDecodeError:
103
+ st.error("Invalid JSON format in the response.")
104
+ return None
105
+ except Exception as e:
106
+ st.error(f"An unexpected error occurred: {e}")
107
+ return None
108
+
109
+ if "problem_step" not in st.session_state:
110
+ st.session_state.problem_step = 0
111
 
112
  # --- Main Page ---
113
  st.title("📚❓Problem Solving Tutor")
 
116
  Replace this placeholder with the actual text.
117
  """
118
 
119
+ with st.spinner("Loading the problem..."):
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
120
  if st.session_state.get("uploaded_pdf_path") is None:
121
+ st.session_state.uploaded_pdf_path = get_local_file_path("problem1.png")
122
 
123
  filepath = st.session_state.uploaded_pdf_path
124
+ response = multimodal_prompt(filepath, TEXT_PROMPT, file_type="image")
125
+
126
+ # --- Display the image ---
127
+ st.image(filepath, caption="Problem Image", use_container_width=True)
128
+
129
+ equation = get_equation(response)
130
+ st.write(f"**Equation:** {equation}")
131
+
132
+ problem_step = st.session_state.problem_step
133
+ if problem_step == 0:
134
+ #Show instructions to submit the answer
135
+ st.write("Please write down your answer in a piece of paper. Take a picture of the paper and submit it in the next step.")
136
+ st.write("Click the button below to proceed.")
137
+ img_file_buffer = st.camera_input("Take a picture of your answer.")
138
+
139
+ if img_file_buffer is not None:
140
+ # process the answer
141
+ st.write("Processing your answer...")
142
+
143
+ # Save the image to a temporary file
144
+ with tempfile.NamedTemporaryFile(delete=False, suffix=".jpg") as temp_file:
145
+ temp_file.write(img_file_buffer.read())
146
+ image_path = temp_file.name
147
+ st.write("Image saved to:", image_path)
148
+
149
+ # create the text prompt
150
+ text_prompt = """Use the provided image. The image shows,
151
+ my answer to the problem. If the image is not clear ask for another image and do not perform
152
+ any of the following instructions. If the image is clear, evaluate the answer as either
153
+ correct or incorrect. If the answer is incorrect, do not provide the correct answer.
154
+ Provide feedback how can the student improve their answer.
155
+ If the answer is correct, provide feedback that the answer is correct and discuss the solution.
156
+ If the student used the factoring method, ask the student to try again and next time use the
157
+ quadratic formula method. The goal is to solve the problem using the quadratic formula method do not
158
+ ask the student to use the factoring method.
159
+ """
160
+
161
+ if st.button("Ask Tutor for Feedback"):
162
+ if text_prompt:
163
+ with st.spinner("AI is thinking..."):
164
+ response = multimodal_prompt(image_path, text_prompt, file_type="image")
165
+ st.markdown(response)
166
+
167
+ if st.button("Next"):
168
+ # Evaluate the response
169
+ if "Correct" in response:
170
+ st.write("Correct! 🎉")
171
+ st.session_state.problem_step = 1
172
+ else:
173
+ st.write("Incorrect. 😞")
174
+ st.session_state.problem_step = 0
175
+
176
+
177
+ else:
178
+ st.write("Please take a picture of your answer.")
179
 
180
 
181
  st.markdown("Visit our Hugging Face Space!")
problems/problem1.png ADDED
problems/problem2.png ADDED
problems/problem3.png ADDED
problems/problem4.png ADDED
problems/problem5.png ADDED