Spaces:
Sleeping
Sleeping
| """ | |
| PHASE 6: LLM Analysis via Groq API | |
| Model: llama3-70b-8192 | |
| Rules: | |
| - LLM ONLY explains and recommends | |
| - LLM does NOT calculate eco score | |
| - LLM does NOT predict impact category | |
| - API key read from environment (HF Spaces Secret: GROQ_API_KEY) | |
| """ | |
| import os | |
| def get_groq_client(): | |
| try: | |
| from groq import Groq | |
| api_key = os.getenv("GROQ_API_KEY", "").strip() | |
| if not api_key: | |
| return None, "GROQ_API_KEY not set." | |
| return Groq(api_key=api_key), None | |
| except ImportError: | |
| return None, "groq package not installed." | |
| def generate_eco_explanation( | |
| product_name: str, | |
| category: str, | |
| deforestation_risk: float, | |
| pollution_level: float, | |
| biodiversity_impact: float, | |
| eco_score: float, | |
| predicted_impact: str, | |
| packaging_type: str = "Unknown", | |
| ingredients: str = "Not specified", | |
| ) -> dict: | |
| """ | |
| Generate LLM environmental explanation. | |
| LLM receives pre-computed score and label β it never recalculates them. | |
| """ | |
| client, error = get_groq_client() | |
| if client is None: | |
| return _fallback( | |
| product_name, category, deforestation_risk, | |
| pollution_level, biodiversity_impact, | |
| eco_score, predicted_impact, error, | |
| ) | |
| prompt = f"""You are an environmental scientist providing educational insights. | |
| Product Details: | |
| - Name: {product_name} | |
| - Category: {category} | |
| - Packaging: {packaging_type} | |
| - Ingredients/Materials: {ingredients} | |
| - Eco Score: {eco_score}/10 β already calculated, do NOT recalculate | |
| - Impact Category: {predicted_impact} β already predicted by ML, do NOT re-predict | |
| Environmental Risk Metrics (0β1 scale): | |
| - Deforestation Risk: {deforestation_risk} | |
| - Pollution Level: {pollution_level} | |
| - Biodiversity Impact: {biodiversity_impact} | |
| Respond with EXACTLY these four sections: | |
| ## Environmental Impact Summary | |
| [2β3 sentences explaining the product's overall environmental footprint in plain language] | |
| ## Deforestation Risk Analysis | |
| [Explain what drives this product's deforestation risk, which regions/forests are affected, and why] | |
| ## Biodiversity & Species Impact | |
| [Explain which wildlife and ecosystems are at risk; estimate how many species could be affected and why] | |
| ## 3 Eco-Friendly Alternatives | |
| 1. **[Name]**: [Why it's better and where to find it] | |
| 2. **[Name]**: [Why it's better and where to find it] | |
| 3. **[Name]**: [Why it's better and where to find it] | |
| ## Quick Sustainability Tips | |
| [2β3 actionable tips the consumer can apply immediately] | |
| Keep language accessible. Be factual and educational, not alarmist. | |
| Do NOT produce any numerical eco score or impact category prediction.""" | |
| try: | |
| response = client.chat.completions.create( | |
| model="llama-3.3-70b-versatile", | |
| messages=[ | |
| { | |
| "role": "system", | |
| "content": ( | |
| "You are an expert environmental scientist. Provide factual, " | |
| "accessible sustainability insights. Never recalculate scores " | |
| "or re-predict categories β those come from separate systems." | |
| ), | |
| }, | |
| {"role": "user", "content": prompt}, | |
| ], | |
| max_tokens=1200, | |
| temperature=0.7, | |
| ) | |
| return { | |
| "success": True, | |
| "explanation": response.choices[0].message.content, | |
| "model": "llama-3.3-70b-versatile (Groq)", | |
| "tokens_used": response.usage.total_tokens, | |
| } | |
| except Exception as e: | |
| return _fallback( | |
| product_name, category, deforestation_risk, | |
| pollution_level, biodiversity_impact, | |
| eco_score, predicted_impact, str(e), | |
| ) | |
| def _fallback( | |
| product_name, category, deforestation_risk, | |
| pollution_level, biodiversity_impact, | |
| eco_score, predicted_impact, error_msg="", | |
| ) -> dict: | |
| """Template explanation used when Groq API is unavailable.""" | |
| if deforestation_risk > 0.5: | |
| defo = ( | |
| f"{product_name} carries a high deforestation risk ({deforestation_risk:.0%}). " | |
| "Supply chains likely involve palm oil, wood pulp, or agricultural land clearing " | |
| "in tropical regions such as the Amazon, Southeast Asian rainforests, or the Congo Basin." | |
| ) | |
| elif deforestation_risk > 0.2: | |
| defo = ( | |
| f"{product_name} has a moderate deforestation risk ({deforestation_risk:.0%}). " | |
| "Some raw materials may be linked to land-use change, though responsible sourcing " | |
| "certifications (FSC, RSPO) can mitigate this." | |
| ) | |
| else: | |
| defo = ( | |
| f"{product_name} has a low deforestation risk ({deforestation_risk:.0%}). " | |
| "Materials show minimal connection to forest loss, especially if recycled or certified organic." | |
| ) | |
| if biodiversity_impact > 0.5: | |
| bio = ( | |
| "High biodiversity impact. Estimated 50β200+ species in affected ecosystems " | |
| "face habitat disruption, including pollinators, soil microbiota, and apex predators. " | |
| "Pollution runoff may further threaten aquatic biodiversity." | |
| ) | |
| elif biodiversity_impact > 0.2: | |
| bio = ( | |
| "Moderate biodiversity impact. Localised ecosystems may experience disruption; " | |
| "approximately 10β50 species could be indirectly affected through habitat " | |
| "fragmentation and chemical pollution." | |
| ) | |
| else: | |
| bio = ( | |
| "Low biodiversity impact. Minimal disruption to local ecosystems. " | |
| "The product lifecycle has limited spillover effects on wildlife habitats." | |
| ) | |
| alts_map = { | |
| "Shampoo": [ | |
| "**Ethique Shampoo Bar** β Zero-plastic, concentrated, biodegradable", | |
| "**Briogeo Be Gentle** β Sulfate-free, plant-based ingredients", | |
| "**Baking Soda & Apple Cider Vinegar** β DIY ultra-low-impact alternative", | |
| ], | |
| "Snacks": [ | |
| "**Local Organic Produce** β Minimal packaging, zero transport emissions", | |
| "**Bulk-store Nuts & Seeds** β Bring your own container, zero waste", | |
| "**Homemade Granola** β Full control over ingredients and packaging", | |
| ], | |
| "Plastic": [ | |
| "**Stainless Steel Alternatives** β Reusable, durable, infinitely recyclable", | |
| "**Compostable Bioplastics (PLA)** β Breaks down in industrial composting", | |
| "**Glass Containers** β Inert, infinitely recyclable, no chemical leaching", | |
| ], | |
| "Cosmetics": [ | |
| "**ILIA Beauty** β Certified organic, recycled packaging", | |
| "**RMS Beauty** β Raw food-grade ingredients, glass packaging", | |
| "**DIY Natural Cosmetics** β Beeswax, coconut oil, natural pigments", | |
| ], | |
| "Clothing": [ | |
| "**Patagonia** β Recycled materials, repair programme, lifetime guarantee", | |
| "**Secondhand / Thrift** β Zero new production, circular economy", | |
| "**Tentree or Eileen Fisher** β Certified organic fibres, ethical supply chains", | |
| ], | |
| } | |
| alts = alts_map.get(category, [ | |
| "**Local / Organic alternatives** β Reduced transport and chemical footprint", | |
| "**Secondhand or refurbished** β Circular economy approach", | |
| "**DIY or zero-waste options** β Maximum control over environmental impact", | |
| ]) | |
| explanation = f"""## Environmental Impact Summary | |
| {product_name} is a {category} product with an eco score of {eco_score}/10 ({predicted_impact}). \ | |
| {"This product raises significant environmental concerns β consider switching to one of the alternatives below." if eco_score < 5 else "This product has moderate to good sustainability credentials, though improvement is always possible."} | |
| ## Deforestation Risk Analysis | |
| {defo} | |
| ## Biodiversity & Species Impact | |
| {bio} | |
| ## 3 Eco-Friendly Alternatives | |
| 1. {alts[0]} | |
| 2. {alts[1]} | |
| 3. {alts[2]} | |
| ## Quick Sustainability Tips | |
| - Look for certified eco-labels: FSC, Fair Trade, USDA Organic, or B Corp. | |
| - Choose concentrated formulas and refillable containers to reduce packaging waste. | |
| - Research brand sustainability reports and third-party certifications before purchasing.""" | |
| note = f"Groq API unavailable: {error_msg}" if error_msg else "Using template explanation." | |
| return { | |
| "success": True, | |
| "explanation": explanation, | |
| "model": "Template Fallback", | |
| "note": note, | |
| } | |
| def compare_products_llm(product1: dict, product2: dict) -> dict: | |
| """LLM comparison explanation for two products (scores pre-calculated).""" | |
| client, error = get_groq_client() | |
| if client is None: | |
| winner = product1["name"] if product1.get("eco_score", 5) >= product2.get("eco_score", 5) else product2["name"] | |
| return { | |
| "success": True, | |
| "comparison": f"**{winner}** is the more environmentally friendly choice based on its lower impact scores.", | |
| "model": "Fallback", | |
| } | |
| prompt = f"""Compare these two products' environmental impact in 3β4 sentences. | |
| Product A: {product1['name']} ({product1.get('category','')}) | |
| - Eco Score: {product1.get('eco_score','N/A')}/10 | |
| - Deforestation: {product1.get('deforestation_risk',0):.2f} | Pollution: {product1.get('pollution_level',0):.2f} | Biodiversity: {product1.get('biodiversity_impact',0):.2f} | |
| Product B: {product2['name']} ({product2.get('category','')}) | |
| - Eco Score: {product2.get('eco_score','N/A')}/10 | |
| - Deforestation: {product2.get('deforestation_risk',0):.2f} | Pollution: {product2.get('pollution_level',0):.2f} | Biodiversity: {product2.get('biodiversity_impact',0):.2f} | |
| State which is more sustainable, explain the key environmental trade-offs, and give a consumer recommendation. | |
| Do NOT recalculate scores.""" | |
| try: | |
| resp = client.chat.completions.create( | |
| model="llama-3.3-70b-versatile", | |
| messages=[{"role": "user", "content": prompt}], | |
| max_tokens=400, | |
| temperature=0.6, | |
| ) | |
| return { | |
| "success": True, | |
| "comparison": resp.choices[0].message.content, | |
| "model": "llama-3.3-70b-versatile (Groq)", | |
| } | |
| except Exception as e: | |
| return {"success": False, "comparison": "Comparison unavailable.", "error": str(e)} | |