Biswarup1 commited on
Commit
3d71910
·
verified ·
1 Parent(s): d550b9a

Create app_legacy.py

Browse files
Files changed (1) hide show
  1. app_legacy.py +452 -0
app_legacy.py ADDED
@@ -0,0 +1,452 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import streamlit as st
2
+ from PIL import Image
3
+ import requests
4
+ import io
5
+ import os
6
+ import random
7
+ import time
8
+
9
+ st.set_page_config(page_title="RecycleRight Budapest", page_icon="♻️", layout="wide")
10
+
11
+
12
+
13
+ # ============== VISION AGENT ==============
14
+ class VisionAgent:
15
+ def __init__(self):
16
+ self.token = os.getenv("HF_TOKEN")
17
+ if not self.token and "HF_TOKEN" in st.secrets:
18
+ self.token = st.secrets["HF_TOKEN"]
19
+
20
+ # Use the standard API URL (Router URL can be finicky with these models)
21
+ self.base_url = "https://api-inference.huggingface.co/models"
22
+
23
+ # 🔄 CHANGED MODELS to ones that are still active on Free Tier
24
+ self.classifier_model = "microsoft/resnet-50"
25
+ self.captioner_model = "nlpconnect/vit-gpt2-image-captioning"
26
+
27
+ def query_api(self, model_id, image_bytes):
28
+ headers = {"Authorization": f"Bearer {self.token}"}
29
+
30
+ # Retry logic
31
+ max_retries = 3
32
+ for attempt in range(max_retries):
33
+ try:
34
+ response = requests.post(
35
+ f"{self.base_url}/{model_id}",
36
+ headers=headers,
37
+ data=image_bytes,
38
+ timeout=20 # 20s is enough
39
+ )
40
+
41
+ # SUCCESS
42
+ if response.status_code == 200:
43
+ return response
44
+
45
+ # LOADING (Cold Start)
46
+ elif response.status_code == 503:
47
+ error_data = response.json()
48
+ wait_time = error_data.get("estimated_time", 15)
49
+ st.toast(f"⏳ Waking up {model_id}... {wait_time:.0f}s", icon="💤")
50
+ time.sleep(wait_time)
51
+ continue
52
+
53
+ # If 410/404/500 -> Fail immediately, don't retry
54
+ else:
55
+ print(f"Error {response.status_code}: {response.text}")
56
+ return None
57
+
58
+ except Exception as e:
59
+ print(f"Connection Error: {e}")
60
+ return None
61
+
62
+ return None
63
+
64
+ def process(self, image, filename=""):
65
+ # Image Prep
66
+ if image.mode != 'RGB': image = image.convert('RGB')
67
+ buf = io.BytesIO()
68
+ image.save(buf, format='JPEG')
69
+ image_bytes = buf.getvalue()
70
+
71
+ if not self.token:
72
+ st.error("❌ HF_TOKEN is missing.")
73
+ return self.fallback_detection(filename)
74
+
75
+ detected_keywords = []
76
+ display_description = ""
77
+
78
+ # 1. Classifier (ResNet)
79
+ st.toast(f"🤖 Identifying object...", icon="🔍")
80
+ resp_class = self.query_api(self.classifier_model, image_bytes)
81
+ if resp_class and resp_class.status_code == 200:
82
+ results = resp_class.json()
83
+ if isinstance(results, list):
84
+ # ResNet returns [{'label': 'water bottle', 'score': 0.9}]
85
+ top_labels = [item.get('label', '') for item in results[:3]]
86
+ detected_keywords.extend(top_labels)
87
+ st.success(f"🏷️ Detected: {', '.join(top_labels)}")
88
+
89
+ # 2. Captioner (ViT-GPT2)
90
+ st.toast(f"👁️ Analyzing context...", icon="📝")
91
+ resp_cap = self.query_api(self.captioner_model, image_bytes)
92
+ if resp_cap and resp_cap.status_code == 200:
93
+ results = resp_cap.json()
94
+ # Format: [{'generated_text': 'a woman holding a bottle'}]
95
+ if isinstance(results, list) and len(results) > 0 and 'generated_text' in results[0]:
96
+ caption = results[0]['generated_text']
97
+ display_description = caption
98
+ detected_keywords.append(caption)
99
+
100
+ # Check if we got ANYTHING
101
+ combined_text = " ".join(detected_keywords).lower()
102
+
103
+ if not combined_text.strip():
104
+ st.warning("⚠️ AI models unavailable. Switching to filename detection.")
105
+ return self.fallback_detection(filename)
106
+
107
+ return self.analyze(combined_text, display_description)
108
+
109
+ def analyze(self, text, display_desc=""):
110
+ # ... (Keep your existing analyze logic exactly as it is) ...
111
+ # COPY PASTE YOUR EXISTING ANALYZE FUNCTION HERE
112
+ result = {
113
+ "description": display_desc if display_desc else text,
114
+ "items": [],
115
+ "materials": [],
116
+ "condition": "clean",
117
+ "needs_separation": False
118
+ }
119
+
120
+ # 1. Plastic
121
+ if any(w in text for w in ["plastic", "bottle", "poly", "water bottle"]):
122
+ if "glass" not in text and "wine" not in text:
123
+ result["materials"].append("plastic")
124
+
125
+ # 2. Glass
126
+ if any(w in text for w in ["glass", "wine", "beer bottle", "jar"]):
127
+ result["materials"].append("glass")
128
+
129
+ # 3. Metal / Aluminum
130
+ if any(w in text for w in ["can", "aluminum", "tin", "soda", "beer", "coke"]):
131
+ if "trash can" not in text:
132
+ result["materials"].append("metal")
133
+ result["items"].append("can")
134
+
135
+ # 4. Paper / Cardboard
136
+ if any(w in text for w in ["cardboard", "box", "carton", "pizza"]):
137
+ result["materials"].append("cardboard")
138
+ if any(w in text for w in ["paper", "newspaper", "magazine"]):
139
+ result["materials"].append("paper")
140
+
141
+ # 5. Special Items
142
+ if "pizza" in text:
143
+ result["items"].append("pizza")
144
+ result["condition"] = "dirty"
145
+
146
+ if any(w in text for w in ["bottle", "can", "soda", "beer", "coke"]):
147
+ result["items"].append("deposit")
148
+ result["items"].append("bottle")
149
+
150
+ if "coffee" in text or "cup" in text:
151
+ result["items"].append("cup")
152
+ result["items"].append("takeaway")
153
+
154
+ if any(w in text for w in ["dirty", "trash", "garbage", "waste", "rotten", "greasy"]):
155
+ result["condition"] = "dirty"
156
+
157
+ result["items"] = list(set(result["items"]))
158
+ result["materials"] = list(set(result["materials"]))
159
+
160
+ if not result["materials"] and not result["items"]:
161
+ result["materials"].append("unknown")
162
+
163
+ return result
164
+
165
+ def fallback_detection(self, filename):
166
+ st.warning("using filename detection")
167
+ return self.analyze(filename.lower() if filename else "")
168
+
169
+
170
+ #def fallback_detection(self, filename):
171
+ # st.warning(f"⚠️ Using filename detection for: {filename}")
172
+ # # Simple logic for fallback
173
+ # fname = filename.lower() if filename else ""
174
+ # result = {"description": fname, "items": [], "materials": [], "condition": "clean", "needs_separation": False}
175
+ #
176
+ # if "bottle" in fname: result["items"].append("bottle"); result["materials"].append("plastic")
177
+ # elif "can" in fname: result["items"].append("can"); result["materials"].append("metal")
178
+ # elif "pizza" in fname: result["items"].append("pizza"); result["materials"].append("cardboard"); result["condition"]="dirty"
179
+ # else: result["materials"].append("unknown")
180
+ # return result
181
+
182
+
183
+ # ============== RULES AGENT ==============
184
+ class RulesAgent:
185
+ def __init__(self):
186
+ self.rules = {
187
+ "plastic": {"bin": "SÁRGA", "emoji": "🟡", "label": "Műanyag és Fém"},
188
+ "metal": {"bin": "SÁRGA", "emoji": "🟡", "label": "Műanyag és Fém"},
189
+ "paper": {"bin": "KÉK", "emoji": "🔵", "label": "Papír"},
190
+ "cardboard": {"bin": "KÉK", "emoji": "🔵", "label": "Papír"},
191
+ "glass": {"bin": "GYŰJTŐPONT", "emoji": "🟢", "label": "Üveg"},
192
+ "hazardous": {"bin": "KÜLÖNLEGES", "emoji": "⚠️", "label": "Veszélyes hulladék"}
193
+ }
194
+
195
+ self.special_items = {
196
+ "deposit": {"bin": "REpont", "emoji": "♻️", "reason": "Return for 50 Ft refund!"},
197
+ "takeaway": {"bin": "FEKETE", "emoji": "⚫", "reason": "Plastic lining prevents recycling"},
198
+ "pizza": {"bin": "FEKETE", "emoji": "⚫", "reason": "Grease contamination"},
199
+ "batteries": {"bin": "ELEMGYŰJTŐ", "emoji": "🔋", "reason": "Toxic materials"}
200
+ }
201
+
202
+ def process(self, vision_result):
203
+ items = vision_result.get("items", [])
204
+ materials = vision_result.get("materials", [])
205
+ condition = vision_result.get("condition", "clean")
206
+
207
+ result = {"bins_needed": [], "steps": [], "warnings": []}
208
+ step = 1
209
+ handled = set()
210
+
211
+ # Separation warning
212
+ if vision_result.get("needs_separation"):
213
+ result["steps"].append(f"{step}. 🔄 **Separate different materials first**")
214
+ step += 1
215
+
216
+ # Special items
217
+ for item in items:
218
+ if item in self.special_items:
219
+ special = self.special_items[item]
220
+ result["steps"].append(f"{step}. {special['emoji']} **{item.title()} → {special['bin']}**")
221
+ result["bins_needed"].append({"bin": special["bin"], "emoji": special["emoji"], "label": special["bin"]})
222
+ result["warnings"].append(f"💡 {special['reason']}")
223
+ step += 1
224
+
225
+ # Mark materials as handled
226
+ if item == "deposit":
227
+ handled.update(["plastic", "metal", "glass"])
228
+ elif item == "takeaway":
229
+ handled.add("paper")
230
+
231
+ # Regular materials
232
+ for mat in materials:
233
+ if mat in handled:
234
+ continue
235
+
236
+ if mat in self.rules:
237
+ rule = self.rules[mat]
238
+
239
+ # Dirty paper/cardboard
240
+ if condition == "dirty" and mat in ["paper", "cardboard"]:
241
+ result["steps"].append(f"{step}. ⚫ **Dirty {mat} → FEKETE**")
242
+ result["warnings"].append(f"⚠️ Contaminated {mat} cannot be recycled!")
243
+ else:
244
+ result["steps"].append(f"{step}. {rule['emoji']} **{mat.title()} → {rule['bin']}**")
245
+ result["bins_needed"].append(rule)
246
+
247
+ step += 1
248
+
249
+ # Remove duplicate bins
250
+ seen = set()
251
+ unique_bins = []
252
+ for bin_info in result["bins_needed"]:
253
+ if bin_info["bin"] not in seen:
254
+ unique_bins.append(bin_info)
255
+ seen.add(bin_info["bin"])
256
+ result["bins_needed"] = unique_bins
257
+
258
+ return result
259
+
260
+ # ============== RECOMMENDATION AGENT ==============
261
+ class RecommendationAgent:
262
+ def __init__(self):
263
+ self.co2_map = {
264
+ "plastic": 2.5,
265
+ "metal": 8.1,
266
+ "paper": 3.9,
267
+ "cardboard": 3.9,
268
+ "glass": 0.5
269
+ }
270
+
271
+ self.tips = {
272
+ "plastic": "💡 Crush bottles to save 70% truck space!",
273
+ "metal": "💡 Aluminum recycles infinitely without quality loss!",
274
+ "paper": "💡 Recycling 1 ton saves 17 trees!",
275
+ "glass": "💡 Glass recycles endlessly - no quality degradation!"
276
+ }
277
+
278
+ self.locations = {
279
+ "REpont": ["📍 Tesco Árkád Shopping Center", "📍 Spar Westend", "📍 Auchan Budaörs"],
280
+ "GYŰJTŐPONT": ["📍 Glass: Major intersections", "📍 Map: fkf.hu/hulladekgyujto-szigetek"],
281
+ "ELEMGYŰJTŐ": ["📍 Media Markt", "📍 DM drugstores", "📍 Tesco Customer Service"]
282
+ }
283
+
284
+ def process(self, vision_result, rules_result):
285
+ materials = vision_result.get("materials", [])
286
+ items = vision_result.get("items", [])
287
+
288
+ # Calculate CO2 impact
289
+ co2 = sum(self.co2_map.get(m, 0) * 0.2 for m in materials)
290
+
291
+ impact_msg = f"♻️ Saves ~**{co2:.1f} kg CO₂**"
292
+ if co2 > 1:
293
+ car_km = co2 * 4.6
294
+ impact_msg += f"\n🚗 Equivalent to {car_km:.1f} km car travel!"
295
+
296
+ # Generate tips
297
+ tips = [self.tips[m] for m in materials if m in self.tips]
298
+ if "deposit" in items:
299
+ tips.append("💰 Each bottle/can = 50 Ft refund!")
300
+
301
+ # Find locations
302
+ locations = []
303
+ if "deposit" in items:
304
+ locations = self.locations["REpont"]
305
+ elif "batteries" in items:
306
+ locations = self.locations["ELEMGYŰJTŐ"]
307
+ elif "glass" in materials:
308
+ locations = self.locations["GYŰJTŐPONT"]
309
+
310
+ # Fun facts
311
+ facts = [
312
+ "🎯 Budapest aims for 65% recycling by 2035!",
313
+ "📊 Average Hungarian produces 385 kg waste/year",
314
+ "🏆 Proper sorting reduces costs by 40%!",
315
+ "🌱 Recycling 1 ton plastic saves 5,774 kWh energy"
316
+ ]
317
+
318
+ return {
319
+ "impact": impact_msg,
320
+ "co2_kg": co2,
321
+ "tips": tips,
322
+ "locations": locations,
323
+ "fun_fact": random.choice(facts)
324
+ }
325
+
326
+ # ============== MAIN APP ==============
327
+ @st.cache_resource
328
+ def load_agents():
329
+ return VisionAgent(), RulesAgent(), RecommendationAgent()
330
+
331
+ def main():
332
+ st.title("♻️ RecycleRight Budapest")
333
+ st.markdown("**Multi-Agent AI Recycling Assistant**")
334
+
335
+ vision, rules, recommender = load_agents()
336
+
337
+ with st.sidebar:
338
+ st.markdown("## 🗑️ Budapest Bins")
339
+ bins = [
340
+ ("🟡 SÁRGA", "Plastic & Metal"),
341
+ ("🔵 KÉK", "Paper & Cardboard"),
342
+ ("⚫ FEKETE", "General Waste"),
343
+ ("🟢 GYŰJTŐPONT", "Glass (Collection Points)"),
344
+ ("♻️ REpont", "Deposit Returns (50 Ft)")
345
+ ]
346
+ for emoji_bin, desc in bins:
347
+ st.markdown(f"**{emoji_bin}** - {desc}")
348
+
349
+ st.markdown("---")
350
+ st.markdown("### 🤖 Agent Status")
351
+ st.success("✅ Vision Agent ")
352
+ st.success("✅ Rules Agent (Budapest FKF)")
353
+ st.success("✅ Recommendation Agent")
354
+
355
+ tab1, tab2 = st.tabs(["📸 Analyze", "🎮 Examples"])
356
+
357
+ with tab1:
358
+ st.info("💡 **Multi-Model AI:** Tries different vision models for reliability!")
359
+
360
+ uploaded = st.file_uploader("📁 Upload image", type=['jpg','png','jpeg'])
361
+
362
+ if uploaded:
363
+ img = Image.open(uploaded)
364
+ st.image(img, caption=f"Uploaded: {uploaded.name}", use_column_width=True)
365
+
366
+ if st.button("🔍 **ANALYZE**", type="primary", use_container_width=True):
367
+ with st.spinner("🤖 Multi-agent processing..."):
368
+ # Vision analysis
369
+ v_result = vision.process(img, filename=uploaded.name)
370
+
371
+ # Rules application
372
+ r_result = rules.process(v_result)
373
+
374
+ # Recommendations
375
+ rec = recommender.process(v_result, r_result)
376
+
377
+ st.balloons()
378
+ st.success("✅ **Analysis Complete!**")
379
+
380
+ # Display results
381
+ col1, col2 = st.columns([2, 1])
382
+
383
+ with col1:
384
+ st.markdown("### 👁️ Detection")
385
+ st.write(f"**Description:** {v_result['description']}")
386
+ st.write(f"**Items:** {', '.join(v_result['items']) if v_result['items'] else 'None detected'}")
387
+ st.write(f"**Materials:** {', '.join(v_result['materials']) if v_result['materials'] else 'None detected'}")
388
+ st.write(f"**Condition:** {v_result['condition']}")
389
+
390
+ st.markdown("### 📋 Disposal Instructions")
391
+ if r_result['steps']:
392
+ for step in r_result['steps']:
393
+ st.markdown(step)
394
+ else:
395
+ st.info("No specific instructions - general waste disposal")
396
+
397
+ for warning in r_result['warnings']:
398
+ st.warning(warning)
399
+
400
+ with col2:
401
+ st.markdown("### 🗑️ Bins Needed")
402
+ if r_result['bins_needed']:
403
+ for bin_info in r_result['bins_needed']:
404
+ st.metric(bin_info.get('label', bin_info['bin']), f"{bin_info['emoji']} {bin_info['bin']}")
405
+ else:
406
+ st.info("No specific bin required")
407
+
408
+ st.markdown("### 🌍 Environmental Impact")
409
+ st.info(rec['impact'])
410
+
411
+ if rec['locations']:
412
+ st.markdown("### 📍 Where to Go")
413
+ for loc in rec['locations']:
414
+ st.markdown(loc)
415
+
416
+ # Tips
417
+ if rec['tips']:
418
+ st.markdown("### 💡 Pro Tips")
419
+ cols = st.columns(len(rec['tips']))
420
+ for i, tip in enumerate(rec['tips']):
421
+ with cols[i]:
422
+ st.info(tip)
423
+
424
+ st.markdown(f"**Did you know?** {rec['fun_fact']}")
425
+
426
+ with tab2:
427
+ st.markdown("### 🎮 Quick Test Examples")
428
+
429
+ examples = [
430
+ ("🥤 Plastic Bottle", {"items": ["bottle", "deposit"], "materials": ["plastic"], "condition": "clean", "needs_separation": False, "description": "plastic water bottle"}),
431
+ ("☕ Coffee Cup", {"items": ["cup", "takeaway"], "materials": ["paper"], "condition": "clean", "needs_separation": True, "description": "disposable coffee cup"}),
432
+ ("🍕 Pizza Box", {"items": ["pizza", "box"], "materials": ["cardboard"], "condition": "dirty", "needs_separation": False, "description": "greasy pizza box"}),
433
+ ("🍷 Wine Bottle", {"items": ["bottle"], "materials": ["glass"], "condition": "clean", "needs_separation": False, "description": "glass wine bottle"}),
434
+ ("🥫 Metal Can", {"items": ["can", "deposit"], "materials": ["metal"], "condition": "clean", "needs_separation": False, "description": "aluminum soda can"}),
435
+ ("🔋 Batteries", {"items": ["batteries"], "materials": ["hazardous"], "condition": "used", "needs_separation": False, "description": "AA batteries"})
436
+ ]
437
+
438
+ cols = st.columns(3)
439
+ for i, (name, data) in enumerate(examples):
440
+ with cols[i % 3]:
441
+ if st.button(name, use_container_width=True):
442
+ r = rules.process(data)
443
+ rec = recommender.process(data, r)
444
+
445
+ st.markdown(f"#### {name}")
446
+ st.write(f"**Items:** {', '.join(data['items'])}")
447
+ for step in r['steps']:
448
+ st.markdown(step)
449
+ st.info(rec['impact'])
450
+
451
+ if __name__ == "__main__":
452
+ main()