AbhayVG commited on
Commit
0a856ce
Β·
verified Β·
1 Parent(s): 983aeb4

Update src.py

Browse files
Files changed (1) hide show
  1. src.py +548 -379
src.py CHANGED
@@ -1,379 +1,548 @@
1
- import os
2
- import pandas as pd
3
- from pandasai import Agent, SmartDataframe
4
- from typing import Tuple
5
- from PIL import Image
6
- from pandasai.llm import HuggingFaceTextGen
7
- from dotenv import load_dotenv
8
- from langchain_groq import ChatGroq
9
- from langchain_google_genai import ChatGoogleGenerativeAI
10
- import matplotlib.pyplot as plt
11
-
12
- # FORCE reload environment variables
13
- load_dotenv(override=True)
14
-
15
- # Get API keys with explicit None handling and debugging
16
- Groq_Token = os.getenv("GROQ_API_KEY")
17
- hf_token = os.getenv("HF_TOKEN")
18
- gemini_token = os.getenv("GEMINI_TOKEN")
19
-
20
- # Debug print (remove in production)
21
- print(f"Debug - Groq Token: {'Present' if Groq_Token else 'Missing'}")
22
- print(f"Debug - Groq Token Value: {Groq_Token[:10] + '...' if Groq_Token else 'None'}")
23
- print(f"Debug - Gemini Token: {'Present' if gemini_token else 'Missing'}")
24
-
25
- models = {
26
- "mistral": "mistral-saba-24b",
27
- "llama3.3": "llama-3.3-70b-versatile",
28
- "llama3.1": "llama-3.1-8b-instant",
29
- "gemma2": "gemma2-9b-it",
30
- "gemini-pro": "gemini-1.5-pro"
31
- }
32
-
33
- def preprocess_and_load_df(path: str) -> pd.DataFrame:
34
- """Load and preprocess the dataframe"""
35
- try:
36
- df = pd.read_csv(path)
37
- df["Timestamp"] = pd.to_datetime(df["Timestamp"])
38
- return df
39
- except Exception as e:
40
- raise Exception(f"Error loading dataframe: {e}")
41
-
42
- def load_agent(df: pd.DataFrame, context: str, inference_server: str, name="mistral") -> Agent:
43
- """Load pandas AI agent with error handling"""
44
- try:
45
- if name == "gemini-pro":
46
- if not gemini_token or gemini_token.strip() == "":
47
- raise ValueError("Gemini API token not available or empty")
48
- llm = ChatGoogleGenerativeAI(
49
- model=models[name],
50
- google_api_key=gemini_token,
51
- temperature=0.1
52
- )
53
- else:
54
- if not Groq_Token or Groq_Token.strip() == "":
55
- raise ValueError("Groq API token not available or empty")
56
- llm = ChatGroq(
57
- model=models[name],
58
- api_key=Groq_Token,
59
- temperature=0.1
60
- )
61
-
62
- agent = Agent(df, config={"llm": llm, "enable_cache": False, "options": {"wait_for_model": True}})
63
- if context:
64
- agent.add_message(context)
65
- return agent
66
- except Exception as e:
67
- raise Exception(f"Error loading agent: {e}")
68
-
69
- def load_smart_df(df: pd.DataFrame, inference_server: str, name="mistral") -> SmartDataframe:
70
- """Load smart dataframe with error handling"""
71
- try:
72
- if name == "gemini-pro":
73
- if not gemini_token or gemini_token.strip() == "":
74
- raise ValueError("Gemini API token not available or empty")
75
- llm = ChatGoogleGenerativeAI(
76
- model=models[name],
77
- google_api_key=gemini_token,
78
- temperature=0.1
79
- )
80
- else:
81
- if not Groq_Token or Groq_Token.strip() == "":
82
- raise ValueError("Groq API token not available or empty")
83
- llm = ChatGroq(
84
- model=models[name],
85
- api_key=Groq_Token,
86
- temperature=0.1
87
- )
88
-
89
- df = SmartDataframe(df, config={"llm": llm, "max_retries": 5, "enable_cache": False})
90
- return df
91
- except Exception as e:
92
- raise Exception(f"Error loading smart dataframe: {e}")
93
-
94
- def get_from_user(prompt):
95
- """Format user prompt"""
96
- return {"role": "user", "content": prompt}
97
-
98
- def ask_agent(agent: Agent, prompt: str) -> dict:
99
- """Ask agent with comprehensive error handling"""
100
- try:
101
- response = agent.chat(prompt)
102
- gen_code = getattr(agent, 'last_code_generated', '')
103
- ex_code = getattr(agent, 'last_code_executed', '')
104
- last_prompt = getattr(agent, 'last_prompt', prompt)
105
-
106
- return {
107
- "role": "assistant",
108
- "content": response,
109
- "gen_code": gen_code,
110
- "ex_code": ex_code,
111
- "last_prompt": last_prompt,
112
- "error": None
113
- }
114
- except Exception as e:
115
- return {
116
- "role": "assistant",
117
- "content": f"Error: {str(e)}",
118
- "gen_code": "",
119
- "ex_code": "",
120
- "last_prompt": prompt,
121
- "error": str(e)
122
- }
123
-
124
- def decorate_with_code(response: dict) -> str:
125
- """Decorate response with code details"""
126
- gen_code = response.get("gen_code", "No code generated")
127
- last_prompt = response.get("last_prompt", "No prompt")
128
-
129
- return f"""<details>
130
- <summary>Generated Code</summary>
131
-
132
- ```python
133
- {gen_code}
134
- ```
135
- </details>
136
-
137
- <details>
138
- <summary>Prompt</summary>
139
-
140
- {last_prompt}
141
- """
142
-
143
- def show_response(st, response):
144
- """Display response with error handling"""
145
- try:
146
- with st.chat_message(response["role"]):
147
- content = response.get("content", "No content")
148
-
149
- try:
150
- # Try to open as image
151
- image = Image.open(content)
152
- if response.get("gen_code"):
153
- st.markdown(decorate_with_code(response), unsafe_allow_html=True)
154
- st.image(image)
155
- return {"is_image": True}
156
- except:
157
- # Not an image, display as text
158
- if response.get("gen_code"):
159
- display_content = decorate_with_code(response) + f"""</details>
160
-
161
- {content}"""
162
- else:
163
- display_content = content
164
- st.markdown(display_content, unsafe_allow_html=True)
165
- return {"is_image": False}
166
- except Exception as e:
167
- st.error(f"Error displaying response: {e}")
168
- return {"is_image": False}
169
-
170
- def ask_question(model_name, question):
171
- """Ask question with comprehensive error handling"""
172
- try:
173
- # Reload environment variables to get fresh values
174
- load_dotenv(override=True)
175
- fresh_groq_token = os.getenv("GROQ_API_KEY")
176
- fresh_gemini_token = os.getenv("GEMINI_TOKEN")
177
-
178
- print(f"ask_question - Fresh Groq Token: {'Present' if fresh_groq_token else 'Missing'}")
179
-
180
- # Check API availability with fresh tokens
181
- if model_name == "gemini-pro":
182
- if not fresh_gemini_token or fresh_gemini_token.strip() == "":
183
- return {
184
- "role": "assistant",
185
- "content": "❌ Gemini API token not available or empty. Please set GEMINI_TOKEN in your environment variables.",
186
- "gen_code": "",
187
- "ex_code": "",
188
- "last_prompt": question,
189
- "error": "Missing or empty API token"
190
- }
191
- llm = ChatGoogleGenerativeAI(
192
- model=models[model_name],
193
- google_api_key=fresh_gemini_token,
194
- temperature=0
195
- )
196
- else:
197
- if not fresh_groq_token or fresh_groq_token.strip() == "":
198
- return {
199
- "role": "assistant",
200
- "content": "❌ Groq API token not available or empty. Please set GROQ_API_KEY in your environment variables and restart the application.",
201
- "gen_code": "",
202
- "ex_code": "",
203
- "last_prompt": question,
204
- "error": "Missing or empty API token"
205
- }
206
-
207
- # Test the API key by trying to create the client
208
- try:
209
- llm = ChatGroq(
210
- model=models[model_name],
211
- api_key=fresh_groq_token,
212
- temperature=0.1
213
- )
214
- # Test with a simple call to verify the API key works
215
- test_response = llm.invoke("Test")
216
- print("API key test successful")
217
- except Exception as api_error:
218
- error_msg = str(api_error).lower()
219
- if "organization_restricted" in error_msg or "unauthorized" in error_msg:
220
- return {
221
- "role": "assistant",
222
- "content": "❌ API Key Error: Your Groq API key appears to be invalid, expired, or restricted. Please check your API key in the .env file.",
223
- "gen_code": "",
224
- "ex_code": "",
225
- "last_prompt": question,
226
- "error": f"API key validation failed: {str(api_error)}"
227
- }
228
- else:
229
- return {
230
- "role": "assistant",
231
- "content": f"❌ API Connection Error: {str(api_error)}",
232
- "gen_code": "",
233
- "ex_code": "",
234
- "last_prompt": question,
235
- "error": str(api_error)
236
- }
237
-
238
- # Check if data file exists
239
- if not os.path.exists("Data.csv"):
240
- return {
241
- "role": "assistant",
242
- "content": "❌ Data.csv file not found. Please ensure the data file is in the correct location.",
243
- "gen_code": "",
244
- "ex_code": "",
245
- "last_prompt": question,
246
- "error": "Data file not found"
247
- }
248
-
249
- df_check = pd.read_csv("Data.csv")
250
- df_check["Timestamp"] = pd.to_datetime(df_check["Timestamp"])
251
- df_check = df_check.head(5)
252
-
253
- new_line = "\n"
254
- parameters = {"font.size": 12, "figure.dpi": 600}
255
-
256
- template = f"""```python
257
- import pandas as pd
258
- import matplotlib.pyplot as plt
259
- import uuid
260
-
261
- plt.rcParams.update({parameters})
262
-
263
- df = pd.read_csv("Data.csv")
264
- df["Timestamp"] = pd.to_datetime(df["Timestamp"])
265
-
266
- # Available columns and data types:
267
- {new_line.join(map(lambda x: '# '+x, str(df_check.dtypes).split(new_line)))}
268
-
269
- # Question: {question.strip()}
270
- # Generate code to answer the question and save result in 'answer' variable
271
- # If creating a plot, save it with a unique filename and store the filename in 'answer'
272
- # If returning text/numbers, store the result directly in 'answer'
273
- ```"""
274
-
275
- system_prompt = """You are a helpful assistant that generates Python code for data analysis.
276
-
277
- Rules:
278
- 1. Always save your final result in a variable called 'answer'
279
- 2. If creating a plot, save it with plt.savefig() and store the filename in 'answer'
280
- 3. If returning text/numbers, store the result directly in 'answer'
281
- 4. Use descriptive variable names and add comments
282
- 5. Handle potential errors gracefully
283
- 6. For plots, use unique filenames to avoid conflicts
284
- """
285
-
286
- query = f"""{system_prompt}
287
-
288
- Complete the following code to answer the user's question:
289
-
290
- {template}
291
- """
292
-
293
- # Make API call
294
- if model_name == "gemini-pro":
295
- response = llm.invoke(query)
296
- answer = response.content
297
- else:
298
- response = llm.invoke(query)
299
- answer = response.content
300
-
301
- # Extract and execute code
302
- try:
303
- if "```python" in answer:
304
- code_part = answer.split("```python")[1].split("```")[0]
305
- else:
306
- code_part = answer
307
-
308
- full_code = f"""
309
- {template.split("```python")[1].split("```")[0]}
310
- {code_part}
311
- """
312
-
313
- # Execute code in a controlled environment
314
- local_vars = {}
315
- global_vars = {
316
- 'pd': pd,
317
- 'plt': plt,
318
- 'os': os,
319
- 'uuid': __import__('uuid')
320
- }
321
-
322
- exec(full_code, global_vars, local_vars)
323
-
324
- # Get the answer
325
- if 'answer' in local_vars:
326
- answer_result = local_vars['answer']
327
- else:
328
- answer_result = "No answer variable found in generated code"
329
-
330
- return {
331
- "role": "assistant",
332
- "content": answer_result,
333
- "gen_code": full_code,
334
- "ex_code": full_code,
335
- "last_prompt": question,
336
- "error": None
337
- }
338
-
339
- except Exception as code_error:
340
- return {
341
- "role": "assistant",
342
- "content": f"❌ Error executing generated code: {str(code_error)}",
343
- "gen_code": full_code if 'full_code' in locals() else "",
344
- "ex_code": full_code if 'full_code' in locals() else "",
345
- "last_prompt": question,
346
- "error": str(code_error)
347
- }
348
-
349
- except Exception as e:
350
- error_msg = str(e)
351
-
352
- # Handle specific API errors
353
- if "organization_restricted" in error_msg:
354
- return {
355
- "role": "assistant",
356
- "content": "❌ API Organization Restricted: Your API key access has been restricted. Please check your Groq API key or try generating a new one.",
357
- "gen_code": "",
358
- "ex_code": "",
359
- "last_prompt": question,
360
- "error": "API access restricted"
361
- }
362
- elif "rate_limit" in error_msg.lower():
363
- return {
364
- "role": "assistant",
365
- "content": "❌ Rate limit exceeded. Please wait a moment and try again.",
366
- "gen_code": "",
367
- "ex_code": "",
368
- "last_prompt": question,
369
- "error": "Rate limit exceeded"
370
- }
371
- else:
372
- return {
373
- "role": "assistant",
374
- "content": f"❌ Error: {error_msg}",
375
- "gen_code": "",
376
- "ex_code": "",
377
- "last_prompt": question,
378
- "error": error_msg
379
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import pandas as pd
3
+ from pandasai import Agent, SmartDataframe
4
+ from typing import Tuple
5
+ from PIL import Image
6
+ from pandasai.llm import HuggingFaceTextGen
7
+ from dotenv import load_dotenv
8
+ from langchain_groq import ChatGroq
9
+ from langchain_google_genai import ChatGoogleGenerativeAI
10
+ import matplotlib.pyplot as plt
11
+ import json
12
+ from datetime import datetime
13
+ from huggingface_hub import HfApi
14
+ import uuid
15
+
16
+ # FORCE reload environment variables
17
+ load_dotenv(override=True)
18
+
19
+ # Get API keys with explicit None handling and debugging
20
+ Groq_Token = os.getenv("GROQ_API_KEY")
21
+ hf_token = os.getenv("HF_TOKEN")
22
+ gemini_token = os.getenv("GEMINI_TOKEN")
23
+
24
+ # Debug print (remove in production)
25
+ print(f"Debug - Groq Token: {'Present' if Groq_Token else 'Missing'}")
26
+ print(f"Debug - Groq Token Value: {Groq_Token[:10] + '...' if Groq_Token else 'None'}")
27
+ print(f"Debug - Gemini Token: {'Present' if gemini_token else 'Missing'}")
28
+
29
+ models = {
30
+ "mistral": "mistral-saba-24b",
31
+ "llama3.3": "llama-3.3-70b-versatile",
32
+ "llama3.1": "llama-3.1-8b-instant",
33
+ "gemma2": "gemma2-9b-it",
34
+ "gemini-pro": "gemini-1.5-pro"
35
+ }
36
+
37
+ def log_interaction(user_query, model_name, response_content, generated_code, execution_time, error_message=None, is_image=False):
38
+ """Log user interactions to Hugging Face dataset"""
39
+ try:
40
+ if not hf_token or hf_token.strip() == "":
41
+ print("Warning: HF_TOKEN not available, skipping logging")
42
+ return
43
+
44
+ # Create log entry
45
+ log_entry = {
46
+ "timestamp": datetime.now().isoformat(),
47
+ "session_id": str(uuid.uuid4()),
48
+ "user_query": user_query,
49
+ "model_name": model_name,
50
+ "response_content": str(response_content),
51
+ "generated_code": generated_code or "",
52
+ "execution_time_seconds": execution_time,
53
+ "error_message": error_message or "",
54
+ "is_image_output": is_image,
55
+ "success": error_message is None
56
+ }
57
+
58
+ # Create DataFrame
59
+ df = pd.DataFrame([log_entry])
60
+
61
+ # Create unique filename with timestamp
62
+ timestamp_str = datetime.now().strftime("%Y%m%d_%H%M%S")
63
+ random_id = str(uuid.uuid4())[:8]
64
+ filename = f"interaction_log_{timestamp_str}_{random_id}.parquet"
65
+
66
+ # Save locally first
67
+ local_path = f"/tmp/{filename}"
68
+ df.to_parquet(local_path, index=False)
69
+
70
+ # Upload to Hugging Face
71
+ api = HfApi(token=hf_token)
72
+ api.upload_file(
73
+ path_or_fileobj=local_path,
74
+ path_in_repo=f"data/{filename}",
75
+ repo_id="SustainabilityLabIITGN/VayuBuddy_logs",
76
+ repo_type="dataset",
77
+ )
78
+
79
+ # Clean up local file
80
+ if os.path.exists(local_path):
81
+ os.remove(local_path)
82
+
83
+ print(f"Successfully logged interaction to HuggingFace: {filename}")
84
+
85
+ except Exception as e:
86
+ print(f"Error logging interaction: {e}")
87
+
88
+ def preprocess_and_load_df(path: str) -> pd.DataFrame:
89
+ """Load and preprocess the dataframe"""
90
+ try:
91
+ df = pd.read_csv(path)
92
+ df["Timestamp"] = pd.to_datetime(df["Timestamp"])
93
+ return df
94
+ except Exception as e:
95
+ raise Exception(f"Error loading dataframe: {e}")
96
+
97
+ def load_agent(df: pd.DataFrame, context: str, inference_server: str, name="mistral") -> Agent:
98
+ """Load pandas AI agent with error handling"""
99
+ try:
100
+ if name == "gemini-pro":
101
+ if not gemini_token or gemini_token.strip() == "":
102
+ raise ValueError("Gemini API token not available or empty")
103
+ llm = ChatGoogleGenerativeAI(
104
+ model=models[name],
105
+ google_api_key=gemini_token,
106
+ temperature=0.1
107
+ )
108
+ else:
109
+ if not Groq_Token or Groq_Token.strip() == "":
110
+ raise ValueError("Groq API token not available or empty")
111
+ llm = ChatGroq(
112
+ model=models[name],
113
+ api_key=Groq_Token,
114
+ temperature=0.1
115
+ )
116
+
117
+ agent = Agent(df, config={"llm": llm, "enable_cache": False, "options": {"wait_for_model": True}})
118
+ if context:
119
+ agent.add_message(context)
120
+ return agent
121
+ except Exception as e:
122
+ raise Exception(f"Error loading agent: {e}")
123
+
124
+ def load_smart_df(df: pd.DataFrame, inference_server: str, name="mistral") -> SmartDataframe:
125
+ """Load smart dataframe with error handling"""
126
+ try:
127
+ if name == "gemini-pro":
128
+ if not gemini_token or gemini_token.strip() == "":
129
+ raise ValueError("Gemini API token not available or empty")
130
+ llm = ChatGoogleGenerativeAI(
131
+ model=models[name],
132
+ google_api_key=gemini_token,
133
+ temperature=0.1
134
+ )
135
+ else:
136
+ if not Groq_Token or Groq_Token.strip() == "":
137
+ raise ValueError("Groq API token not available or empty")
138
+ llm = ChatGroq(
139
+ model=models[name],
140
+ api_key=Groq_Token,
141
+ temperature=0.1
142
+ )
143
+
144
+ df = SmartDataframe(df, config={"llm": llm, "max_retries": 5, "enable_cache": False})
145
+ return df
146
+ except Exception as e:
147
+ raise Exception(f"Error loading smart dataframe: {e}")
148
+
149
+ def get_from_user(prompt):
150
+ """Format user prompt"""
151
+ return {"role": "user", "content": prompt}
152
+
153
+ def ask_agent(agent: Agent, prompt: str) -> dict:
154
+ """Ask agent with comprehensive error handling"""
155
+ start_time = datetime.now()
156
+ try:
157
+ response = agent.chat(prompt)
158
+ execution_time = (datetime.now() - start_time).total_seconds()
159
+
160
+ gen_code = getattr(agent, 'last_code_generated', '')
161
+ ex_code = getattr(agent, 'last_code_executed', '')
162
+ last_prompt = getattr(agent, 'last_prompt', prompt)
163
+
164
+ # Log the interaction
165
+ log_interaction(
166
+ user_query=prompt,
167
+ model_name="pandas_ai_agent",
168
+ response_content=response,
169
+ generated_code=gen_code,
170
+ execution_time=execution_time,
171
+ error_message=None,
172
+ is_image=isinstance(response, str) and any(response.endswith(ext) for ext in ['.png', '.jpg', '.jpeg'])
173
+ )
174
+
175
+ return {
176
+ "role": "assistant",
177
+ "content": response,
178
+ "gen_code": gen_code,
179
+ "ex_code": ex_code,
180
+ "last_prompt": last_prompt,
181
+ "error": None
182
+ }
183
+ except Exception as e:
184
+ execution_time = (datetime.now() - start_time).total_seconds()
185
+ error_msg = str(e)
186
+
187
+ # Log the failed interaction
188
+ log_interaction(
189
+ user_query=prompt,
190
+ model_name="pandas_ai_agent",
191
+ response_content=f"Error: {error_msg}",
192
+ generated_code="",
193
+ execution_time=execution_time,
194
+ error_message=error_msg,
195
+ is_image=False
196
+ )
197
+
198
+ return {
199
+ "role": "assistant",
200
+ "content": f"Error: {error_msg}",
201
+ "gen_code": "",
202
+ "ex_code": "",
203
+ "last_prompt": prompt,
204
+ "error": error_msg
205
+ }
206
+
207
+ def decorate_with_code(response: dict) -> str:
208
+ """Decorate response with code details"""
209
+ gen_code = response.get("gen_code", "No code generated")
210
+ last_prompt = response.get("last_prompt", "No prompt")
211
+
212
+ return f"""<details>
213
+ <summary>Generated Code</summary>
214
+
215
+ ```python
216
+ {gen_code}
217
+ ```
218
+ </details>
219
+
220
+ <details>
221
+ <summary>Prompt</summary>
222
+
223
+ {last_prompt}
224
+ """
225
+
226
+ def show_response(st, response):
227
+ """Display response with error handling"""
228
+ try:
229
+ with st.chat_message(response["role"]):
230
+ content = response.get("content", "No content")
231
+
232
+ try:
233
+ # Try to open as image
234
+ image = Image.open(content)
235
+ if response.get("gen_code"):
236
+ st.markdown(decorate_with_code(response), unsafe_allow_html=True)
237
+ st.image(image)
238
+ return {"is_image": True}
239
+ except:
240
+ # Not an image, display as text
241
+ if response.get("gen_code"):
242
+ display_content = decorate_with_code(response) + f"""</details>
243
+
244
+ {content}"""
245
+ else:
246
+ display_content = content
247
+ st.markdown(display_content, unsafe_allow_html=True)
248
+ return {"is_image": False}
249
+ except Exception as e:
250
+ st.error(f"Error displaying response: {e}")
251
+ return {"is_image": False}
252
+
253
+ def ask_question(model_name, question):
254
+ """Ask question with comprehensive error handling and logging"""
255
+ start_time = datetime.now()
256
+ try:
257
+ # Reload environment variables to get fresh values
258
+ load_dotenv(override=True)
259
+ fresh_groq_token = os.getenv("GROQ_API_KEY")
260
+ fresh_gemini_token = os.getenv("GEMINI_TOKEN")
261
+
262
+ print(f"ask_question - Fresh Groq Token: {'Present' if fresh_groq_token else 'Missing'}")
263
+
264
+ # Check API availability with fresh tokens
265
+ if model_name == "gemini-pro":
266
+ if not fresh_gemini_token or fresh_gemini_token.strip() == "":
267
+ execution_time = (datetime.now() - start_time).total_seconds()
268
+ error_msg = "Missing or empty API token"
269
+
270
+ # Log the failed interaction
271
+ log_interaction(
272
+ user_query=question,
273
+ model_name=model_name,
274
+ response_content="❌ Gemini API token not available or empty",
275
+ generated_code="",
276
+ execution_time=execution_time,
277
+ error_message=error_msg,
278
+ is_image=False
279
+ )
280
+
281
+ return {
282
+ "role": "assistant",
283
+ "content": "❌ Gemini API token not available or empty. Please set GEMINI_TOKEN in your environment variables.",
284
+ "gen_code": "",
285
+ "ex_code": "",
286
+ "last_prompt": question,
287
+ "error": error_msg
288
+ }
289
+ llm = ChatGoogleGenerativeAI(
290
+ model=models[model_name],
291
+ google_api_key=fresh_gemini_token,
292
+ temperature=0
293
+ )
294
+ else:
295
+ if not fresh_groq_token or fresh_groq_token.strip() == "":
296
+ execution_time = (datetime.now() - start_time).total_seconds()
297
+ error_msg = "Missing or empty API token"
298
+
299
+ # Log the failed interaction
300
+ log_interaction(
301
+ user_query=question,
302
+ model_name=model_name,
303
+ response_content="❌ Groq API token not available or empty",
304
+ generated_code="",
305
+ execution_time=execution_time,
306
+ error_message=error_msg,
307
+ is_image=False
308
+ )
309
+
310
+ return {
311
+ "role": "assistant",
312
+ "content": "❌ Groq API token not available or empty. Please set GROQ_API_KEY in your environment variables and restart the application.",
313
+ "gen_code": "",
314
+ "ex_code": "",
315
+ "last_prompt": question,
316
+ "error": error_msg
317
+ }
318
+
319
+ # Test the API key by trying to create the client
320
+ try:
321
+ llm = ChatGroq(
322
+ model=models[model_name],
323
+ api_key=fresh_groq_token,
324
+ temperature=0.1
325
+ )
326
+ # Test with a simple call to verify the API key works
327
+ test_response = llm.invoke("Test")
328
+ print("API key test successful")
329
+ except Exception as api_error:
330
+ execution_time = (datetime.now() - start_time).total_seconds()
331
+ error_msg = str(api_error)
332
+
333
+ if "organization_restricted" in error_msg.lower() or "unauthorized" in error_msg.lower():
334
+ response_content = "❌ API Key Error: Your Groq API key appears to be invalid, expired, or restricted. Please check your API key in the .env file."
335
+ log_error_msg = f"API key validation failed: {error_msg}"
336
+ else:
337
+ response_content = f"❌ API Connection Error: {error_msg}"
338
+ log_error_msg = error_msg
339
+
340
+ # Log the failed interaction
341
+ log_interaction(
342
+ user_query=question,
343
+ model_name=model_name,
344
+ response_content=response_content,
345
+ generated_code="",
346
+ execution_time=execution_time,
347
+ error_message=log_error_msg,
348
+ is_image=False
349
+ )
350
+
351
+ return {
352
+ "role": "assistant",
353
+ "content": response_content,
354
+ "gen_code": "",
355
+ "ex_code": "",
356
+ "last_prompt": question,
357
+ "error": log_error_msg
358
+ }
359
+
360
+ # Check if data file exists
361
+ if not os.path.exists("Data.csv"):
362
+ execution_time = (datetime.now() - start_time).total_seconds()
363
+ error_msg = "Data file not found"
364
+
365
+ # Log the failed interaction
366
+ log_interaction(
367
+ user_query=question,
368
+ model_name=model_name,
369
+ response_content="❌ Data.csv file not found",
370
+ generated_code="",
371
+ execution_time=execution_time,
372
+ error_message=error_msg,
373
+ is_image=False
374
+ )
375
+
376
+ return {
377
+ "role": "assistant",
378
+ "content": "❌ Data.csv file not found. Please ensure the data file is in the correct location.",
379
+ "gen_code": "",
380
+ "ex_code": "",
381
+ "last_prompt": question,
382
+ "error": error_msg
383
+ }
384
+
385
+ df_check = pd.read_csv("Data.csv")
386
+ df_check["Timestamp"] = pd.to_datetime(df_check["Timestamp"])
387
+ df_check = df_check.head(5)
388
+
389
+ new_line = "\n"
390
+ parameters = {"font.size": 12, "figure.dpi": 600}
391
+
392
+ template = f"""```python
393
+ import pandas as pd
394
+ import matplotlib.pyplot as plt
395
+ import uuid
396
+
397
+ plt.rcParams.update({parameters})
398
+
399
+ df = pd.read_csv("Data.csv")
400
+ df["Timestamp"] = pd.to_datetime(df["Timestamp"])
401
+
402
+ # Available columns and data types:
403
+ {new_line.join(map(lambda x: '# '+x, str(df_check.dtypes).split(new_line)))}
404
+
405
+ # Question: {question.strip()}
406
+ # Generate code to answer the question and save result in 'answer' variable
407
+ # If creating a plot, save it with a unique filename and store the filename in 'answer'
408
+ # If returning text/numbers, store the result directly in 'answer'
409
+ ```"""
410
+
411
+ system_prompt = """You are a helpful assistant that generates Python code for data analysis.
412
+
413
+ Rules:
414
+ 1. Always save your final result in a variable called 'answer'
415
+ 2. If creating a plot, save it with plt.savefig() and store the filename in 'answer'
416
+ 3. If returning text/numbers, store the result directly in 'answer'
417
+ 4. Use descriptive variable names and add comments
418
+ 5. Handle potential errors gracefully
419
+ 6. For plots, use unique filenames to avoid conflicts
420
+ """
421
+
422
+ query = f"""{system_prompt}
423
+
424
+ Complete the following code to answer the user's question:
425
+
426
+ {template}
427
+ """
428
+
429
+ # Make API call
430
+ if model_name == "gemini-pro":
431
+ response = llm.invoke(query)
432
+ answer = response.content
433
+ else:
434
+ response = llm.invoke(query)
435
+ answer = response.content
436
+
437
+ # Extract and execute code
438
+ try:
439
+ if "```python" in answer:
440
+ code_part = answer.split("```python")[1].split("```")[0]
441
+ else:
442
+ code_part = answer
443
+
444
+ full_code = f"""
445
+ {template.split("```python")[1].split("```")[0]}
446
+ {code_part}
447
+ """
448
+
449
+ # Execute code in a controlled environment
450
+ local_vars = {}
451
+ global_vars = {
452
+ 'pd': pd,
453
+ 'plt': plt,
454
+ 'os': os,
455
+ 'uuid': __import__('uuid')
456
+ }
457
+
458
+ exec(full_code, global_vars, local_vars)
459
+
460
+ # Get the answer
461
+ if 'answer' in local_vars:
462
+ answer_result = local_vars['answer']
463
+ else:
464
+ answer_result = "No answer variable found in generated code"
465
+
466
+ execution_time = (datetime.now() - start_time).total_seconds()
467
+
468
+ # Determine if output is an image
469
+ is_image = isinstance(answer_result, str) and any(answer_result.endswith(ext) for ext in ['.png', '.jpg', '.jpeg'])
470
+
471
+ # Log successful interaction
472
+ log_interaction(
473
+ user_query=question,
474
+ model_name=model_name,
475
+ response_content=str(answer_result),
476
+ generated_code=full_code,
477
+ execution_time=execution_time,
478
+ error_message=None,
479
+ is_image=is_image
480
+ )
481
+
482
+ return {
483
+ "role": "assistant",
484
+ "content": answer_result,
485
+ "gen_code": full_code,
486
+ "ex_code": full_code,
487
+ "last_prompt": question,
488
+ "error": None
489
+ }
490
+
491
+ except Exception as code_error:
492
+ execution_time = (datetime.now() - start_time).total_seconds()
493
+ error_msg = str(code_error)
494
+
495
+ # Log the failed code execution
496
+ log_interaction(
497
+ user_query=question,
498
+ model_name=model_name,
499
+ response_content=f"❌ Error executing generated code: {error_msg}",
500
+ generated_code=full_code if 'full_code' in locals() else "",
501
+ execution_time=execution_time,
502
+ error_message=error_msg,
503
+ is_image=False
504
+ )
505
+
506
+ return {
507
+ "role": "assistant",
508
+ "content": f"❌ Error executing generated code: {error_msg}",
509
+ "gen_code": full_code if 'full_code' in locals() else "",
510
+ "ex_code": full_code if 'full_code' in locals() else "",
511
+ "last_prompt": question,
512
+ "error": error_msg
513
+ }
514
+
515
+ except Exception as e:
516
+ execution_time = (datetime.now() - start_time).total_seconds()
517
+ error_msg = str(e)
518
+
519
+ # Handle specific API errors
520
+ if "organization_restricted" in error_msg:
521
+ response_content = "❌ API Organization Restricted: Your API key access has been restricted. Please check your Groq API key or try generating a new one."
522
+ log_error_msg = "API access restricted"
523
+ elif "rate_limit" in error_msg.lower():
524
+ response_content = "❌ Rate limit exceeded. Please wait a moment and try again."
525
+ log_error_msg = "Rate limit exceeded"
526
+ else:
527
+ response_content = f"❌ Error: {error_msg}"
528
+ log_error_msg = error_msg
529
+
530
+ # Log the failed interaction
531
+ log_interaction(
532
+ user_query=question,
533
+ model_name=model_name,
534
+ response_content=response_content,
535
+ generated_code="",
536
+ execution_time=execution_time,
537
+ error_message=log_error_msg,
538
+ is_image=False
539
+ )
540
+
541
+ return {
542
+ "role": "assistant",
543
+ "content": response_content,
544
+ "gen_code": "",
545
+ "ex_code": "",
546
+ "last_prompt": question,
547
+ "error": log_error_msg
548
+ }