rairo commited on
Commit
14ce297
Β·
verified Β·
1 Parent(s): 275e65d

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +65 -58
app.py CHANGED
@@ -50,7 +50,6 @@ def parse_numbered_steps(text):
50
  # 2. SESSION STATE SETUP
51
  # ─────────────────────────────────────────────────────────────────────────────
52
 
53
- # Central dictionary for session state management
54
  if "app_state" not in st.session_state:
55
  st.session_state.app_state = {
56
  "steps": [], "images": {}, "tools_list": [], "current_step": 1,
@@ -76,30 +75,23 @@ def reset_state():
76
  st.success("βœ… Reset complete!")
77
  st.rerun()
78
 
79
- def send_gemini_request(model_name, prompt, image):
80
- """Helper to send requests using the client.chats interface."""
81
  try:
82
- chat = client.chats.create(
83
- model=model_name,
84
- config=types.GenerateContentConfig(response_modalities=["Text"]) # Assuming text response for these tasks
85
- )
86
  response = chat.send_message([prompt, image])
87
- # Combine all text parts from the response
88
  response_text = "".join(part.text for part in response.candidates[0].content.parts if part.text)
89
  return response_text.strip()
90
  except Exception as e:
91
- st.error(f"Error communicating with model {model_name}: {str(e)}")
92
  return None
93
 
94
  def initial_analysis(uploaded_file, context_text):
95
- """
96
- First pass with AI: get category, then get title, description, and initial plan.
97
- """
98
  image = Image.open(uploaded_file)
99
  st.session_state.app_state['user_image'] = image
100
 
101
  with st.spinner("πŸ€– Analyzing your project and preparing a plan..."):
102
- # Step 1: Detect Category using CATEGORY_MODEL
103
  category_prompt = (
104
  "You are an expert DIY assistant. Analyze the user's image and context. "
105
  f"Context: '{context_text}'. "
@@ -108,32 +100,28 @@ def initial_analysis(uploaded_file, context_text):
108
  "Upcycling & Sustainable Crafts, or DIY Project Creation. "
109
  "Reply with ONLY the category name."
110
  )
111
- category = send_gemini_request(CATEGORY_MODEL, category_prompt, image)
112
  if not category: return
113
  st.session_state.app_state['category'] = category
114
 
115
- # Step 2: Generate Title, Description, and Plan using GENERATION_MODEL
116
  plan_prompt = f"""
117
- You are an expert DIY assistant. The user's project is in the category: {category}.
118
  User Context: "{context_text if context_text else 'No context provided.'}"
119
-
120
  Based on the image and context, perform the following:
121
  1. **Title:** Create a short, clear title for this project.
122
  2. **Description:** Write a brief, one-paragraph description of the goal.
123
  3. **Initial Plan:**
124
- - If the category is 'Upcycling & Sustainable Crafts' AND the user has NOT specified a project, propose three distinct project options as a numbered list under the heading "UPCYCLING OPTIONS:".
125
  - For all other cases, briefly outline the main stages of the proposed solution.
126
-
127
  Structure your response EXACTLY like this:
128
  TITLE: [Your title]
129
  DESCRIPTION: [Your description]
130
  INITIAL PLAN:
131
  [Your plan or 3 options]
132
  """
133
- plan_response = send_gemini_request(GENERATION_MODEL, plan_prompt, image)
134
  if not plan_response: return
135
 
136
- # Parse the second response
137
  try:
138
  st.session_state.app_state['project_title'] = re.search(r"TITLE:\s*(.*)", plan_response).group(1).strip()
139
  st.session_state.app_state['project_description'] = re.search(r"DESCRIPTION:\s*(.*)", plan_response, re.DOTALL).group(1).strip()
@@ -148,16 +136,15 @@ def initial_analysis(uploaded_file, context_text):
148
  st.session_state.app_state['prompt_sent'] = True
149
  if context_text:
150
  st.session_state.app_state['plan_approved'] = True
151
- generate_detailed_steps()
152
  else:
153
  st.session_state.app_state['plan_approved'] = False
154
  except AttributeError:
155
  st.error("The AI response was not in the expected format. Please try again.")
156
  st.session_state.app_state['prompt_sent'] = False
157
 
158
-
159
- def generate_detailed_steps(selected_option=None):
160
- """Generates the detailed, step-by-step guide using GENERATION_MODEL."""
161
  image = st.session_state.app_state.get('user_image')
162
  if not image:
163
  st.error("Image not found. Please start over."); return
@@ -169,39 +156,55 @@ def generate_detailed_steps(selected_option=None):
169
  detailed_prompt = f"""
170
  You are a DIY expert. The user wants to proceed with the project titled "{st.session_state.app_state['project_title']}".
171
  {context}
172
- Provide a detailed guide in this EXACT format:
 
173
 
174
  TOOLS AND MATERIALS:
175
  - Tool A
176
  - Material B
177
 
178
  STEPS:
179
- 1. First step instructions. Be specific and clear.
180
  2. Second step instructions...
181
  """
182
- with st.spinner("πŸ› οΈ Generating your detailed step-by-step guide..."):
183
- full_resp = send_gemini_request(GENERATION_MODEL, detailed_prompt, image)
184
- if not full_resp: return
185
-
186
- try:
187
- tools_section = re.search(r"TOOLS AND MATERIALS:\s*(.*?)\s*STEPS:", full_resp, re.DOTALL).group(1).strip()
188
- steps_section = re.search(r"STEPS:\s*(.*)", full_resp, re.DOTALL).group(1).strip()
189
-
190
- st.session_state.app_state['tools_list'] = [line.strip("- ").strip() for line in tools_section.split('\n') if line.strip()]
191
- st.session_state.app_state['steps'] = parse_numbered_steps(steps_section)
192
-
193
- for idx, step_text in st.session_state.app_state['steps']:
194
- st.session_state.app_state['done_flags'][idx] = False
195
- st.session_state.app_state['notes'][idx] = ""
196
- timer_match = re.search(r"wait\s+for\s+(\d+)\s+(seconds?|minutes?)", step_text.lower())
197
- if timer_match:
198
- val, unit = int(timer_match.group(1)), timer_match.group(2)
199
- st.session_state.app_state['timers'][idx] = val * (60 if "minute" in unit else 1)
200
- else:
201
- st.session_state.app_state['timers'][idx] = 0
202
- except AttributeError:
203
- st.error("Failed to parse the detailed guide from the AI. The format was incorrect.")
204
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
205
 
206
  def render_sidebar_navigation():
207
  st.sidebar.markdown("## Steps Navigation")
@@ -228,7 +231,14 @@ def render_step(idx, text):
228
  total = len(st.session_state.app_state['steps'])
229
  st.markdown(f"### Step {idx} of {total}")
230
  st.write(text)
231
- # Timer, checkbox, notes, and navigation logic remains the same...
 
 
 
 
 
 
 
232
  done = st.checkbox("βœ… Mark this step as completed", value=st.session_state.app_state['done_flags'].get(idx, False), key=f"done_{idx}")
233
  st.session_state.app_state['done_flags'][idx] = done
234
  notes = st.text_area("πŸ“ Your notes for this step:", value=st.session_state.app_state['notes'].get(idx, ""), height=100, key=f"notes_{idx}")
@@ -253,12 +263,11 @@ with st.expander("ℹ️ How it works", expanded=False):
253
  st.write("""
254
  1. **Upload a photo** of your project.
255
  2. **(Optional) Describe your goal** for more accurate results.
256
- 3. **Review the Plan.** The AI will propose a plan. If you didn't provide a description, you'll be asked to approve it. For crafts, you may get options to choose from.
257
- 4. **Get Your Guide** with tools and step-by-step instructions.
258
  5. **Follow the Steps** using the interactive checklist.
259
  """)
260
 
261
- # --- Main UI ---
262
  if not st.session_state.app_state['prompt_sent']:
263
  st.markdown("---")
264
  col1, col2 = st.columns([3, 1])
@@ -275,8 +284,6 @@ if not st.session_state.app_state['prompt_sent']:
275
  st.warning("⚠️ Please upload an image first!")
276
  if st.button("πŸ”„ Start Over", use_container_width=True):
277
  reset_state()
278
-
279
- # --- Results and Steps UI ---
280
  else:
281
  render_sidebar_navigation()
282
  st.markdown("---")
@@ -290,14 +297,14 @@ else:
290
  st.markdown("#### The AI has suggested a few projects. Please choose one:")
291
  for i, option in enumerate(st.session_state.app_state['upcycling_options']):
292
  if st.button(option, key=f"option_{i}"):
293
- generate_detailed_steps(selected_option=option)
294
  st.rerun()
295
  elif not st.session_state.app_state['plan_approved']:
296
  st.markdown("#### The AI has proposed the following plan:")
297
  st.success(st.session_state.app_state['initial_plan'])
298
  if st.button("βœ… Looks good, proceed with this plan", type="primary"):
299
  st.session_state.app_state['plan_approved'] = True
300
- generate_detailed_steps()
301
  st.rerun()
302
  else:
303
  render_tools_list()
 
50
  # 2. SESSION STATE SETUP
51
  # ─────────────────────────────────────────────────────────────────────────────
52
 
 
53
  if "app_state" not in st.session_state:
54
  st.session_state.app_state = {
55
  "steps": [], "images": {}, "tools_list": [], "current_step": 1,
 
75
  st.success("βœ… Reset complete!")
76
  st.rerun()
77
 
78
+ def send_text_request(model_name, prompt, image):
79
+ """Helper to send requests that expect only a text response."""
80
  try:
81
+ chat = client.chats.create(model=model_name)
 
 
 
82
  response = chat.send_message([prompt, image])
 
83
  response_text = "".join(part.text for part in response.candidates[0].content.parts if part.text)
84
  return response_text.strip()
85
  except Exception as e:
86
+ st.error(f"Error with model {model_name}: {str(e)}")
87
  return None
88
 
89
  def initial_analysis(uploaded_file, context_text):
90
+ """First pass with AI: get category, then title, description, and initial plan."""
 
 
91
  image = Image.open(uploaded_file)
92
  st.session_state.app_state['user_image'] = image
93
 
94
  with st.spinner("πŸ€– Analyzing your project and preparing a plan..."):
 
95
  category_prompt = (
96
  "You are an expert DIY assistant. Analyze the user's image and context. "
97
  f"Context: '{context_text}'. "
 
100
  "Upcycling & Sustainable Crafts, or DIY Project Creation. "
101
  "Reply with ONLY the category name."
102
  )
103
+ category = send_text_request(CATEGORY_MODEL, category_prompt, image)
104
  if not category: return
105
  st.session_state.app_state['category'] = category
106
 
 
107
  plan_prompt = f"""
108
+ You are an expert DIY assistant in the category: {category}.
109
  User Context: "{context_text if context_text else 'No context provided.'}"
 
110
  Based on the image and context, perform the following:
111
  1. **Title:** Create a short, clear title for this project.
112
  2. **Description:** Write a brief, one-paragraph description of the goal.
113
  3. **Initial Plan:**
114
+ - If 'Upcycling & Sustainable Crafts' AND no specific project is mentioned, propose three distinct project options as a numbered list under "UPCYCLING OPTIONS:".
115
  - For all other cases, briefly outline the main stages of the proposed solution.
 
116
  Structure your response EXACTLY like this:
117
  TITLE: [Your title]
118
  DESCRIPTION: [Your description]
119
  INITIAL PLAN:
120
  [Your plan or 3 options]
121
  """
122
+ plan_response = send_text_request(GENERATION_MODEL, plan_prompt, image)
123
  if not plan_response: return
124
 
 
125
  try:
126
  st.session_state.app_state['project_title'] = re.search(r"TITLE:\s*(.*)", plan_response).group(1).strip()
127
  st.session_state.app_state['project_description'] = re.search(r"DESCRIPTION:\s*(.*)", plan_response, re.DOTALL).group(1).strip()
 
136
  st.session_state.app_state['prompt_sent'] = True
137
  if context_text:
138
  st.session_state.app_state['plan_approved'] = True
139
+ generate_detailed_guide_with_images()
140
  else:
141
  st.session_state.app_state['plan_approved'] = False
142
  except AttributeError:
143
  st.error("The AI response was not in the expected format. Please try again.")
144
  st.session_state.app_state['prompt_sent'] = False
145
 
146
+ def generate_detailed_guide_with_images(selected_option=None):
147
+ """Generates the detailed guide with steps and illustrations."""
 
148
  image = st.session_state.app_state.get('user_image')
149
  if not image:
150
  st.error("Image not found. Please start over."); return
 
156
  detailed_prompt = f"""
157
  You are a DIY expert. The user wants to proceed with the project titled "{st.session_state.app_state['project_title']}".
158
  {context}
159
+ Provide a detailed guide. For each step, you MUST provide a simple, clear illustrative image.
160
+ Format your response EXACTLY like this:
161
 
162
  TOOLS AND MATERIALS:
163
  - Tool A
164
  - Material B
165
 
166
  STEPS:
167
+ 1. First step instructions.
168
  2. Second step instructions...
169
  """
170
+ with st.spinner("πŸ› οΈ Generating your detailed guide with illustrations..."):
171
+ try:
172
+ chat = client.chats.create(
173
+ model=GENERATION_MODEL,
174
+ config=types.GenerateContentConfig(response_modalities=["Text", "Image"])
175
+ )
176
+ full_resp = chat.send_message([detailed_prompt, image])
177
+ gen_parts = full_resp.candidates[0].content.parts
178
+
179
+ combined_text = ""
180
+ inline_images = []
181
+ for part in gen_parts:
182
+ if part.text is not None:
183
+ combined_text += part.text + "\n"
184
+ if part.inline_data is not None:
185
+ img = Image.open(BytesIO(part.inline_data.data))
186
+ inline_images.append(img)
187
+ combined_text = combined_text.strip()
188
+
189
+ tools_section = re.search(r"TOOLS AND MATERIALS:\s*(.*?)\s*STEPS:", combined_text, re.DOTALL).group(1).strip()
190
+ steps_section = re.search(r"STEPS:\s*(.*)", combined_text, re.DOTALL).group(1).strip()
191
+ parsed_steps = parse_numbered_steps(steps_section)
192
+
193
+ st.session_state.app_state['tools_list'] = [line.strip("- ").strip() for line in tools_section.split('\n') if line.strip()]
194
+ st.session_state.app_state['steps'] = parsed_steps
195
+ st.session_state.app_state['images'] = {idx: inline_images[idx - 1] for idx, _ in parsed_steps if idx - 1 < len(inline_images)}
196
+
197
+ for idx, step_text in parsed_steps:
198
+ st.session_state.app_state['done_flags'][idx] = False
199
+ st.session_state.app_state['notes'][idx] = ""
200
+ timer_match = re.search(r"wait\s+for\s+(\d+)\s+(seconds?|minutes?)", step_text.lower())
201
+ if timer_match:
202
+ val, unit = int(timer_match.group(1)), timer_match.group(2)
203
+ st.session_state.app_state['timers'][idx] = val * (60 if "minute" in unit else 1)
204
+ else:
205
+ st.session_state.app_state['timers'][idx] = 0
206
+ except Exception as e:
207
+ st.error(f"Failed to generate or parse the illustrated guide: {str(e)}")
208
 
209
  def render_sidebar_navigation():
210
  st.sidebar.markdown("## Steps Navigation")
 
231
  total = len(st.session_state.app_state['steps'])
232
  st.markdown(f"### Step {idx} of {total}")
233
  st.write(text)
234
+
235
+ if idx in st.session_state.app_state['images']:
236
+ st.image(
237
+ st.session_state.app_state['images'][idx],
238
+ caption=f"Illustration for step {idx}",
239
+ use_container_width=True
240
+ )
241
+
242
  done = st.checkbox("βœ… Mark this step as completed", value=st.session_state.app_state['done_flags'].get(idx, False), key=f"done_{idx}")
243
  st.session_state.app_state['done_flags'][idx] = done
244
  notes = st.text_area("πŸ“ Your notes for this step:", value=st.session_state.app_state['notes'].get(idx, ""), height=100, key=f"notes_{idx}")
 
263
  st.write("""
264
  1. **Upload a photo** of your project.
265
  2. **(Optional) Describe your goal** for more accurate results.
266
+ 3. **Review the Plan.** The AI will propose a plan. If you didn't provide a description, you'll be asked to approve it.
267
+ 4. **Get Your Guide** with tools and illustrated step-by-step instructions.
268
  5. **Follow the Steps** using the interactive checklist.
269
  """)
270
 
 
271
  if not st.session_state.app_state['prompt_sent']:
272
  st.markdown("---")
273
  col1, col2 = st.columns([3, 1])
 
284
  st.warning("⚠️ Please upload an image first!")
285
  if st.button("πŸ”„ Start Over", use_container_width=True):
286
  reset_state()
 
 
287
  else:
288
  render_sidebar_navigation()
289
  st.markdown("---")
 
297
  st.markdown("#### The AI has suggested a few projects. Please choose one:")
298
  for i, option in enumerate(st.session_state.app_state['upcycling_options']):
299
  if st.button(option, key=f"option_{i}"):
300
+ generate_detailed_guide_with_images(selected_option=option)
301
  st.rerun()
302
  elif not st.session_state.app_state['plan_approved']:
303
  st.markdown("#### The AI has proposed the following plan:")
304
  st.success(st.session_state.app_state['initial_plan'])
305
  if st.button("βœ… Looks good, proceed with this plan", type="primary"):
306
  st.session_state.app_state['plan_approved'] = True
307
+ generate_detailed_guide_with_images()
308
  st.rerun()
309
  else:
310
  render_tools_list()