File size: 12,589 Bytes
cab3343 d1245ca cab3343 f5e6f2c cab3343 d1245ca cab3343 6001123 d1245ca 6001123 d1245ca cab3343 d1245ca cab3343 d1245ca cab3343 d1245ca cab3343 6001123 cab3343 6001123 cab3343 22fd7fd 2732537 22fd7fd 2732537 57b2f21 22fd7fd 57b2f21 2732537 22fd7fd cab3343 6001123 cab3343 6001123 cab3343 8af29c0 cab3343 152a5d2 cab3343 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 | import os
from dotenv import load_dotenv
from typing import Annotated, List, Dict
from typing_extensions import TypedDict
import requests
import json
from langgraph.graph.message import add_messages
# from langchain_google_genai import ChatGoogleGenerativeAI
from huggingface_hub import InferenceClient
from langchain.prompts import ChatPromptTemplate
from langgraph.graph import StateGraph, START, END
from unity_functions import parse_extraction_result, parse_scoring_result
from scrapper import scrape_product_info
# Load environment variables from .env file
load_dotenv()
# Initialize StateGraph
class EnvironmentalAnalysisState(TypedDict):
messages: Annotated[list, add_messages]
product_description: str
extracted_data: Dict
carbon_footprint: float
environmental_score: float
recommendations: List[str]
analysis_complete: bool
route: str
# Initialize llm
llm = InferenceClient(
provider="hyperbolic",
api_key=os.getenv("HF_API_KEY"), # You'll need to add this to your .env file
)
# Product Information Extraction Tool
def extract_product_from_url(state: EnvironmentalAnalysisState):
"""
Extract product information from URL and update the product description
"""
if state.get("product_description"):
scraped_info = scrape_product_info(state["product_description"])
extraction_prompt = ChatPromptTemplate.from_template("""
Analyze the following scraped product information and extract environmental impact factors.
Product: {scraped_info}
Please provide the information in the following JSON format:
{{
"material_composition": "main materials used",
"manufacturing_location": "country or region",
"product_weight": numeric_value_in_kg,
"transport_distance": numeric_value_in_km,
"packaging_type": "packaging materials and type",
"energy_usage": numeric_value_in_kwh,
"recyclability": "recyclable/biodegradable/non-recyclable",
"durability": "estimated lifespan",
"certifications": "environmental certifications if any"
}}
If specific values are not mentioned, make reasonable estimates based on typical products of this type.
""")
rendered_prompt_content = extraction_prompt.format(scraped_info=scraped_info)
messages = [
{
"role": "user",
"content": rendered_prompt_content
}
]
result = llm.chat.completions.create(
model="deepseek-ai/DeepSeek-R1",
messages=messages,
max_tokens=1000,
temperature=0.2
)
extracted_data = parse_extraction_result(result.choices[0].message.content)
return {
"extracted_data": extracted_data,
"messages": [result.choices[0].message]
}
def extract_product_info(state: EnvironmentalAnalysisState):
extraction_prompt = ChatPromptTemplate.from_template("""
Analyze the following product description and extract environmental impact factors.
Product: {product_description}
Please provide the information in the following JSON format:
{{
"material_composition": "main materials used",
"manufacturing_location": "country or region",
"product_weight": numeric_value_in_kg,
"transport_distance": numeric_value_in_km,
"packaging_type": "packaging materials and type",
"energy_usage": numeric_value_in_kwh,
"recyclability": "recyclable/biodegradable/non-recyclable",
"durability": "estimated lifespan",
"certifications": "environmental certifications if any"
}}
If specific values are not mentioned, make reasonable estimates based on typical products of this type.
""")
rendered_prompt_content = extraction_prompt.format(product_description=state["product_description"])
messages = [
{
"role": "user",
"content": rendered_prompt_content
}
]
result = llm.chat.completions.create(
model="deepseek-ai/DeepSeek-R1",
messages=messages,
max_tokens=1000,
temperature=0.2
)
extracted_data = parse_extraction_result(result.choices[0].message.content)
return {
"extracted_data": extracted_data,
"messages": [result.choices[0].message]
}
def classify_route(state: EnvironmentalAnalysisState):
"""
Classify the Route based on the product description if it starts with a URL or text.
"""
if state["product_description"].startswith("http"):
state["route"] = "extract_info_from_url"
else:
state["route"] = "extract_info_from_description"
return state
def calculate_carbon_footprint(state: EnvironmentalAnalysisState):
climatiq_url = "https://api.climatiq.io/v1/estimate"
headers = {
"Authorization": f"Bearer {os.getenv('CLIMATIQ_API_KEY')}",
"Content-Type": "application/json"
}
extracted_data = state["extracted_data"]
total_emissions = 0
# Calculate emissions from different Activites
calculations = []
if "material_weight" in extracted_data:
manufacturing_request = {
"emission_factor": {
"activity_id": "manufacturing-metals-aluminum"
},
"parameters": {
"weight": extracted_data["material_weight"],
"weight_unit": "kg"
}
}
response = requests.post(climatiq_url, headers=headers,
json=manufacturing_request)
if response.status_code == 200:
result = response.json()
total_emissions += result["co2e"]
calculations.append(result)
# Transportation emissions calculation
if "transport_distance" in extracted_data:
transport_request = {
"emission_factor": {
"activity_id": "freight_flight-route_type_international-distance_na-weight_na"
},
"parameters": {
"distance": extracted_data["transport_distance"],
"distance_unit": "km",
"weight": extracted_data.get("product_weight", 1),
"weight_unit": "kg"
}
}
response = requests.post(climatiq_url, headers=headers,
json=transport_request)
if response.status_code == 200:
result = response.json()
total_emissions += result["co2e"]
calculations.append(result)
return {
"carbon_footprint": total_emissions,
"calculation_details": calculations
}
def generate_environmental_score(state: EnvironmentalAnalysisState):
scoring_prompt = ChatPromptTemplate.from_template("""
Based on the following environmental data, generate a comprehensive environmental score (0-100).
Aim to score products fairly, recognizing positive efforts and current industry sustainability trends. Be **generous where any notable sustainable practices are observed**, reflecting a positive outlook on efforts towards environmental responsibility. Focus on a **broader assessment rather than scrutinizing every minor detail**, aiming for an overall, balanced sustainability score.
Product Data: {extracted_data}
Carbon Footprint: {carbon_footprint} kg CO2e
Scoring Guide:
- 90-100 (Excellent/Leader): Demonstrates exceptional environmental stewardship, innovative green practices, and minimal negative impact across all factors. A true leader in sustainability.
- 70-89 (Good/Above Average): Shows strong environmental practices with notable positive attributes and efforts to reduce impact. Generally a good choice for sustainability.
- 40-69 (Average/Acceptable): Meets basic environmental considerations but has significant areas for improvement. Represents typical industry practices without outstanding sustainable features.
- 0-39 (Poor/Concerning): Significant environmental concerns, unsustainable practices, or a clear lack of attention to environmental impact.
Examples to guide scoring perspective:
- Product: "Electronic smartphone with aluminum body, manufactured in China, typical global distribution for sales in Europe."
Typical Score Range: 50-65 (Reasonable for modern electronics with some recyclable components and efforts, but significant global transport, energy consumption during use, and complex end-of-life disposal.)
- Product: "Local handmade wooden furniture using sustainably harvested, certified wood, finished with non-toxic oil, sold within 50km of production."
Typical Score Range: 85-95 (Excellent material choice, minimal transport, durable product designed for longevity, non-toxic finish, often supports local economy.)
- Product: "Single-use disposable coffee cup made from virgin paper with a plastic lining, widely distributed via international shipping."
Typical Score Range: 20-35 (Poor due to single-use nature, material composite making recycling difficult, high transport, and significant waste contribution.)
Consider these factors:
1. Carbon emissions intensity
2. Recyclability of materials
3. Manufacturing sustainability
4. Transportation impact
5. Product longevity
6. End-of-life disposal
Here are some examples of products and their potential scores to guide your assessment, aiming for higher scores where appropriate:
- "Electronic smartphone with aluminum body, manufactured in China": Likely score in the range of 50-65. While it has some recyclable materials, manufacturing and transportation in a global supply chain contribute to a moderate footprint.
- "Local handmade wooden furniture using sustainable forest wood": Likely score in the range of 85-95. This product benefits from local production, renewable resources, and often a longer lifespan.
- "Single-use plastic water bottle produced with virgin plastics": Likely score in the range of 10-25. High environmental impact due to non-renewable materials, single-use nature, and pollution potential.
Provide:
- Overall score (0-100, where 100 is most sustainable and 0 is least sustainable)
- Improvement recommendations
""")
rendered_prompt_content = scoring_prompt.format(
extracted_data=state["extracted_data"],
carbon_footprint=state["carbon_footprint"]
)
messages = [
{
"role": "user",
"content": rendered_prompt_content
}
]
result = llm.chat.completions.create(
model="meta-llama/Meta-Llama-3.1-70B-Instruct",
messages=messages,
max_tokens=1500,
temperature=0.2
)
# Parse score and recommendations
score_data = parse_scoring_result(result.choices[0].message.content)
return {
"environmental_score": score_data["score"],
"recommendations": score_data["recommendations"],
"analysis_complete": True,
"messages": [result.choices[0].message]
}
def route_selector(state: EnvironmentalAnalysisState) -> str:
return state["route"]
def create_environmental_analyzer():
#initialize the state graph
graph_builder = StateGraph(EnvironmentalAnalysisState)
#nodes
graph_builder.add_node("classify_route", classify_route)
graph_builder.add_node("extract_info_from_description", extract_product_info)
graph_builder.add_node("extract_info_from_url", extract_product_from_url)
graph_builder.add_node("calculate_carbon", calculate_carbon_footprint)
graph_builder.add_node("generate_score", generate_environmental_score)
#edges
graph_builder.add_edge(START, "classify_route")
# graph_builder.add_edge("classify_route", "extract_info_from_description")
# graph_builder.add_edge("classify_route", "extract_info_from_url")
graph_builder.add_conditional_edges(
"classify_route",
route_selector,
{
"extract_info_from_description": "extract_info_from_description",
"extract_info_from_url": "extract_info_from_url"
}
)
graph_builder.add_edge("extract_info_from_description", "calculate_carbon")
graph_builder.add_edge("extract_info_from_url", "calculate_carbon")
graph_builder.add_edge("calculate_carbon", "generate_score")
graph_builder.add_edge("generate_score", END)
return graph_builder.compile() |