""" 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)}