Kunal commited on
Commit
cab3343
·
1 Parent(s): dfc4e40

added LangGraph Based workflow and tools for product extraction, routes classification, calculating carbon footprint and score generator

Browse files
Files changed (2) hide show
  1. Agent.py +271 -0
  2. requirements.txt +6 -0
Agent.py ADDED
@@ -0,0 +1,271 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ from dotenv import load_dotenv
3
+ from typing import Annotated, List, Dict
4
+ from typing_extensions import TypedDict
5
+ import requests
6
+ import json
7
+
8
+ from langgraph.graph.message import add_messages
9
+ # from langchain_google_genai import ChatGoogleGenerativeAI
10
+ from huggingface_hub import InferenceClient
11
+ from langchain.prompts import ChatPromptTemplate
12
+ from langgraph.graph import StateGraph, START, END
13
+
14
+ # from unity_functions import parse_extraction_result, parse_scoring_result
15
+ # from scrapper import scrape_product_info
16
+
17
+ # Load environment variables from .env file
18
+ load_dotenv()
19
+
20
+ # Initialize StateGraph
21
+ class EnvironmentalAnalysisState(TypedDict):
22
+ messages: Annotated[list, add_messages]
23
+ product_description: str
24
+ extracted_data: Dict
25
+ carbon_footprint: float
26
+ environmental_score: float
27
+ recommendations: List[str]
28
+ analysis_complete: bool
29
+ route: str
30
+
31
+ # Initialize llm
32
+ llm = InferenceClient(
33
+ provider="hyperbolic",
34
+ api_key=os.getenv("HYPERBOLIC_API_KEY"), # You'll need to add this to your .env file
35
+ )
36
+
37
+
38
+ # Product Information Extraction Tool
39
+ def extract_product_from_url(state: EnvironmentalAnalysisState):
40
+ """
41
+ Extract product information from URL and update the product description
42
+ """
43
+ extraction_prompt = ChatPromptTemplate.from_template("""
44
+ You are an expert environmental analyst. You are given a URL to a product page. Your job is to extract environmental impact data by simulating access to the product's specifications and making reasonable assumptions if exact data is missing.
45
+
46
+ Product URL: {product_description}
47
+
48
+ Carefully extract or infer the following in JSON format:
49
+ {{
50
+ "material_composition": "main materials used (e.g., plastic, metal, organic cotton)",
51
+ "manufacturing_location": "country or region of manufacturing",
52
+ "product_weight": numeric_value_in_kg,
53
+ "transport_distance": numeric_value_in_km (assumed from manufacturing to India),
54
+ "packaging_type": "type and material of packaging (e.g., cardboard box, plastic wrap)",
55
+ "energy_usage": numeric_value_in_kwh (if applicable or estimated),
56
+ "recyclability": "recyclable / biodegradable / non-recyclable",
57
+ "durability": "expected lifespan in years",
58
+ "certifications": "environmental or sustainability certifications (e.g., Energy Star, Fair Trade)"
59
+ }}
60
+
61
+ If some data is not explicitly available on the page, infer based on similar products or industry standards.
62
+ Use domain knowledge and mention estimates clearly.
63
+ """)
64
+
65
+ messages = [
66
+ {
67
+ "role": "user",
68
+ "content": extraction_prompt
69
+ }
70
+ ]
71
+
72
+ result = llm.chat.completions.create(
73
+ model="deepseek-ai/DeepSeek-R1", # or another suitable model available on Hyperbolic
74
+ messages=messages,
75
+ max_tokens=1000,
76
+ temperature=0.2
77
+ )
78
+ extracted_data = parse_extraction_result(result.choices[0].message.content)
79
+ return {
80
+ "extracted_data": extracted_data,
81
+ "messages": [result.choices[0].message]
82
+ }
83
+
84
+ def extract_product_info(state: EnvironmentalAnalysisState):
85
+ extraction_prompt = ChatPromptTemplate.from_template("""
86
+ Analyze the following product description and extract environmental impact factors.
87
+
88
+ Product: {product_description}
89
+
90
+ Please provide the information in the following JSON format:
91
+ {{
92
+ "material_composition": "main materials used",
93
+ "manufacturing_location": "country or region",
94
+ "product_weight": numeric_value_in_kg,
95
+ "transport_distance": numeric_value_in_km,
96
+ "packaging_type": "packaging materials and type",
97
+ "energy_usage": numeric_value_in_kwh,
98
+ "recyclability": "recyclable/biodegradable/non-recyclable",
99
+ "durability": "estimated lifespan",
100
+ "certifications": "environmental certifications if any"
101
+ }}
102
+
103
+ If specific values are not mentioned, make reasonable estimates based on typical products of this type.
104
+ """)
105
+
106
+ messages = [
107
+ {
108
+ "role": "user",
109
+ "content": extraction_prompt
110
+ }
111
+ ]
112
+
113
+ result = llm.chat.completions.create(
114
+ model="deepseek-ai/DeepSeek-R1",
115
+ messages=messages,
116
+ max_tokens=1000,
117
+ temperature=0.2
118
+ )
119
+
120
+ extracted_data = parse_extraction_result(result.choices[0].message.content)
121
+
122
+ return {
123
+ "extracted_data": extracted_data,
124
+ "messages": [result.choices[0].message]
125
+ }
126
+
127
+ def classify_route(state: EnvironmentalAnalysisState):
128
+ """
129
+ Classify the Route based on the product description if it starts with a URL or text.
130
+ """
131
+ if state["product_description"].startswith("http"):
132
+ state["route"] = "extract_info_from_url"
133
+ else:
134
+ state["route"] = "extract_info_from_description"
135
+
136
+ return state
137
+
138
+
139
+ def calculate_carbon_footprint(state: EnvironmentalAnalysisState):
140
+ climatiq_url = "https://api.climatiq.io/v1/estimate"
141
+ headers = {
142
+ "Authorization": f"Bearer {os.getenv('CLIMATIQ_API_KEY')}",
143
+ "Content-Type": "application/json"
144
+ }
145
+
146
+ extracted_data = state["extracted_data"]
147
+ total_emissions = 0
148
+
149
+ # Calculate emissions from different Activites
150
+ calculations = []
151
+
152
+ if "material_weight" in extracted_data:
153
+ manufacturing_request = {
154
+ "emission_factor": {
155
+ "activity_id": "manufacturing-metals-aluminum"
156
+ },
157
+ "parameters": {
158
+ "weight": extracted_data["material_weight"],
159
+ "weight_unit": "kg"
160
+ }
161
+ }
162
+
163
+ response = requests.post(climatiq_url, headers=headers,
164
+ json=manufacturing_request)
165
+ if response.status_code == 200:
166
+ result = response.json()
167
+ total_emissions += result["co2e"]
168
+ calculations.append(result)
169
+
170
+ # Transportation emissions calculation
171
+ if "transport_distance" in extracted_data:
172
+ transport_request = {
173
+ "emission_factor": {
174
+ "activity_id": "freight_flight-route_type_international-distance_na-weight_na"
175
+ },
176
+ "parameters": {
177
+ "distance": extracted_data["transport_distance"],
178
+ "distance_unit": "km",
179
+ "weight": extracted_data.get("product_weight", 1),
180
+ "weight_unit": "kg"
181
+ }
182
+ }
183
+
184
+ response = requests.post(climatiq_url, headers=headers,
185
+ json=transport_request)
186
+ if response.status_code == 200:
187
+ result = response.json()
188
+ total_emissions += result["co2e"]
189
+ calculations.append(result)
190
+
191
+ return {
192
+ "carbon_footprint": total_emissions,
193
+ "calculation_details": calculations
194
+ }
195
+
196
+ def generate_environmental_score(state: EnvironmentalAnalysisState):
197
+ scoring_prompt = ChatPromptTemplate.from_template("""
198
+ Based on the following environmental data, generate a comprehensive environmental score (0-100):
199
+
200
+ Product Data: {extracted_data}
201
+ Carbon Footprint: {carbon_footprint} kg CO2e
202
+
203
+ Consider these factors:
204
+ 1. Carbon emissions intensity
205
+ 2. Recyclability of materials
206
+ 3. Manufacturing sustainability
207
+ 4. Transportation impact
208
+ 5. Product longevity
209
+ 6. End-of-life disposal
210
+
211
+ Provide:
212
+ - Overall score (0-100, where 100 is most sustainable)
213
+ - Category breakdown
214
+ - Improvement recommendations
215
+ """)
216
+
217
+ messages = [
218
+ {
219
+ "role": "user",
220
+ "content": scoring_prompt
221
+ }
222
+ ]
223
+
224
+ result = llm.chat.completions.create(
225
+ model="deepseek-ai/DeepSeek-R1",
226
+ messages=messages,
227
+ max_tokens=1500,
228
+ temperature=0.2
229
+ )
230
+
231
+ # Parse score and recommendations
232
+ score_data = parse_scoring_result(result.choices[0].message.content)
233
+
234
+ return {
235
+ "environmental_score": score_data["score"],
236
+ "recommendations": score_data["recommendations"],
237
+ "analysis_complete": True,
238
+ "messages": [result.choices[0].message]
239
+ }
240
+
241
+ def route_selector(state: EnvironmentalAnalysisState) -> str:
242
+ return state["route"]
243
+
244
+ def create_environmental_analyzer():
245
+ #initialize the state graph
246
+ graph_builder = StateGraph(EnvironmentalAnalysisState)
247
+
248
+ #nodes
249
+ graph_builder.add_node("classify_route", classify_route)
250
+ graph_builder.add_node("extract_info_from_description", extract_product_info)
251
+ graph_builder.add_node("extract_info_from_url", extract_product_from_url)
252
+ graph_builder.add_node("calculate_carbon", calculate_carbon_footprint)
253
+ graph_builder.add_node("generate_score", generate_environmental_score)
254
+ #edges
255
+ graph_builder.add_edge(START, "classify_route")
256
+ # graph_builder.add_edge("classify_route", "extract_info_from_description")
257
+ # graph_builder.add_edge("classify_route", "extract_info_from_url")
258
+ graph_builder.add_conditional_edges(
259
+ "classify",
260
+ route_selector,
261
+ {
262
+ "extract_info_from_description": "extract_info_from_description",
263
+ "extract_info_from_url": "extract_info_from_url"
264
+ }
265
+ )
266
+ graph_builder.add_edge("extract_info_from_description", "calculate_carbon")
267
+ graph_builder.add_edge("extract_info_from_url", "calculate_carbon")
268
+ graph_builder.add_edge("calculate_carbon", "generate_score")
269
+ graph_builder.add_edge("generate_score", END)
270
+
271
+ return graph_builder.compile()
requirements.txt ADDED
@@ -0,0 +1,6 @@
 
 
 
 
 
 
 
1
+ python-dotenv
2
+ langgraph
3
+ langchain
4
+ requests
5
+ bs4
6
+ gradio