added together ai agent
Browse files- controller.py +2 -2
- python_code_executor_service.py +88 -100
- together_ai_llama_agent.py +2 -2
controller.py
CHANGED
@@ -367,7 +367,7 @@ async def csv_chat(request: Dict, authorization: str = Header(None)):
|
|
367 |
# groq_answer = await asyncio.to_thread(groq_chat, decoded_url, query)
|
368 |
# logger.info("groq_answer:", groq_answer)
|
369 |
|
370 |
-
result = await query_csv_agent(decoded_url, query)
|
371 |
logger.info("together ai csv answer == >", result)
|
372 |
return {"answer": result}
|
373 |
|
@@ -633,7 +633,7 @@ async def csv_chart(request: dict, authorization: str = Header(None)):
|
|
633 |
# return {"image_url": image_public_url}
|
634 |
# return FileResponse(groq_result, media_type="image/png")
|
635 |
|
636 |
-
result = await query_csv_agent(csv_url,query)
|
637 |
logger.info("together ai result ==>", result)
|
638 |
return {"orchestrator_response": jsonable_encoder(result)}
|
639 |
|
|
|
367 |
# groq_answer = await asyncio.to_thread(groq_chat, decoded_url, query)
|
368 |
# logger.info("groq_answer:", groq_answer)
|
369 |
|
370 |
+
result = await query_csv_agent(decoded_url, query, chat_id)
|
371 |
logger.info("together ai csv answer == >", result)
|
372 |
return {"answer": result}
|
373 |
|
|
|
633 |
# return {"image_url": image_public_url}
|
634 |
# return FileResponse(groq_result, media_type="image/png")
|
635 |
|
636 |
+
result = await query_csv_agent(csv_url, query, chat_id)
|
637 |
logger.info("together ai result ==>", result)
|
638 |
return {"orchestrator_response": jsonable_encoder(result)}
|
639 |
|
python_code_executor_service.py
CHANGED
@@ -1,3 +1,6 @@
|
|
|
|
|
|
|
|
1 |
import uuid
|
2 |
import matplotlib.pyplot as plt
|
3 |
from pathlib import Path
|
@@ -14,6 +17,10 @@ import seaborn as sns
|
|
14 |
import scipy.stats as stats
|
15 |
from pydantic import BaseModel
|
16 |
|
|
|
|
|
|
|
|
|
17 |
|
18 |
class CodeResponse(BaseModel):
|
19 |
"""Container for code-related responses"""
|
@@ -136,126 +143,107 @@ class PythonExecutor:
|
|
136 |
'plots': plots
|
137 |
}
|
138 |
|
139 |
-
def
|
140 |
"""
|
141 |
-
Save plot to
|
142 |
|
143 |
Args:
|
144 |
plot_data (bytes): Image data in bytes
|
145 |
description (str): Description of the plot
|
|
|
146 |
|
147 |
Returns:
|
148 |
-
str:
|
149 |
"""
|
150 |
# Generate unique filename
|
151 |
filename = f"chart_{uuid.uuid4().hex}.png"
|
152 |
filepath = self.charts_folder / filename
|
153 |
|
154 |
-
# Save the plot
|
155 |
with open(filepath, 'wb') as f:
|
156 |
f.write(plot_data)
|
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 |
-
# else:
|
188 |
-
# output_parts.append(result['output'].strip())
|
189 |
-
|
190 |
-
# # Process charts if they exist
|
191 |
-
# if response.charts:
|
192 |
-
# output_parts.append("\nVisualizations:")
|
193 |
-
|
194 |
-
# for chart in response.charts:
|
195 |
-
# if chart.code:
|
196 |
-
# # Execute the chart code
|
197 |
-
# result = self.execute_code(chart.code)
|
198 |
-
|
199 |
-
# if result['plots']:
|
200 |
-
# # Save each generated plot and get dummy URL
|
201 |
-
# for plot_data in result['plots']:
|
202 |
-
# dummy_url = self.save_plot_dummy(plot_data, chart.image_description)
|
203 |
-
# output_parts.append(f"\n{chart.image_description}")
|
204 |
-
# output_parts.append(f"")
|
205 |
-
# elif result['error']:
|
206 |
-
# output_parts.append(f"\nError generating {chart.image_description}: {result['error']['message']}")
|
207 |
-
|
208 |
-
# return "\n".join(output_parts)
|
209 |
-
|
210 |
-
def process_response(self, response: CsvChatResult) -> str:
|
211 |
-
"""
|
212 |
-
Process the CsvChatResult response and generate formatted output
|
213 |
-
with markdown code blocks for structured data.
|
214 |
-
"""
|
215 |
-
output_parts = []
|
216 |
-
|
217 |
-
# Add casual response
|
218 |
-
output_parts.append(response.casual_response)
|
219 |
-
|
220 |
-
# Process analysis operations
|
221 |
-
for operation in response.analysis_operations:
|
222 |
-
# Execute the code
|
223 |
-
result = self.execute_code(operation.code.code)
|
224 |
-
|
225 |
-
# Add operation description
|
226 |
-
output_parts.append(f"\n{operation.description}:")
|
227 |
-
|
228 |
-
# Add output or error with markdown wrapping
|
229 |
-
if result['error']:
|
230 |
-
output_parts.append("```python\n" + f"Error: {result['error']['message']}" + "\n```")
|
231 |
-
else:
|
232 |
-
output = result['output'].strip()
|
233 |
-
if self._looks_like_structured_data(output): # New helper method
|
234 |
-
output_parts.append("```python\n" + output + "\n```")
|
235 |
else:
|
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 |
-
|
|
|
|
|
|
1 |
+
import os
|
2 |
+
from supabase import create_client, Client
|
3 |
+
from dotenv import load_dotenv
|
4 |
import uuid
|
5 |
import matplotlib.pyplot as plt
|
6 |
from pathlib import Path
|
|
|
17 |
import scipy.stats as stats
|
18 |
from pydantic import BaseModel
|
19 |
|
20 |
+
from supabase_service import upload_file_to_supabase
|
21 |
+
|
22 |
+
# Load environment variables from .env file
|
23 |
+
load_dotenv()
|
24 |
|
25 |
class CodeResponse(BaseModel):
|
26 |
"""Container for code-related responses"""
|
|
|
143 |
'plots': plots
|
144 |
}
|
145 |
|
146 |
+
async def save_plot_to_supabase(self, plot_data: bytes, description: str, chat_id: str) -> str:
|
147 |
"""
|
148 |
+
Save plot to Supabase storage and return the public URL
|
149 |
|
150 |
Args:
|
151 |
plot_data (bytes): Image data in bytes
|
152 |
description (str): Description of the plot
|
153 |
+
chat_id (str): ID of the chat session
|
154 |
|
155 |
Returns:
|
156 |
+
str: Public URL of the uploaded chart
|
157 |
"""
|
158 |
# Generate unique filename
|
159 |
filename = f"chart_{uuid.uuid4().hex}.png"
|
160 |
filepath = self.charts_folder / filename
|
161 |
|
162 |
+
# Save the plot locally first
|
163 |
with open(filepath, 'wb') as f:
|
164 |
f.write(plot_data)
|
165 |
|
166 |
+
try:
|
167 |
+
# Upload to Supabase
|
168 |
+
public_url = await upload_file_to_supabase(
|
169 |
+
file_path=str(filepath),
|
170 |
+
file_name=filename,
|
171 |
+
chat_id=chat_id
|
172 |
+
)
|
173 |
+
|
174 |
+
# Remove the local file after upload
|
175 |
+
os.remove(filepath)
|
176 |
+
|
177 |
+
return public_url
|
178 |
+
except Exception as e:
|
179 |
+
# Clean up local file if upload fails
|
180 |
+
if os.path.exists(filepath):
|
181 |
+
os.remove(filepath)
|
182 |
+
raise Exception(f"Failed to upload plot to Supabase: {e}")
|
183 |
+
|
184 |
+
def _looks_like_structured_data(self, output: str) -> bool:
|
185 |
+
"""Helper to detect JSON-like or array-like output"""
|
186 |
+
output = output.strip()
|
187 |
+
return (
|
188 |
+
output.startswith('{') and output.endswith('}') or # JSON object
|
189 |
+
output.startswith('[') and output.endswith(']') or # Array
|
190 |
+
'\n' in output and '=' in output # Python console output
|
191 |
+
)
|
192 |
|
193 |
+
async def process_response(self, response: CsvChatResult, chat_id: str) -> str:
|
194 |
+
"""
|
195 |
+
Process the CsvChatResult response and generate formatted output
|
196 |
+
with markdown code blocks for structured data.
|
197 |
|
198 |
+
Args:
|
199 |
+
response (CsvChatResult): Response from CSV analysis
|
200 |
+
chat_id (str): ID of the chat session
|
201 |
|
202 |
+
Returns:
|
203 |
+
str: Formatted output with results and image URLs
|
204 |
+
"""
|
205 |
+
output_parts = []
|
206 |
|
207 |
+
# Add casual response
|
208 |
+
output_parts.append(response.casual_response)
|
209 |
|
210 |
+
# Process analysis operations
|
211 |
+
for operation in response.analysis_operations:
|
212 |
+
# Execute the code
|
213 |
+
result = self.execute_code(operation.code.code)
|
214 |
|
215 |
+
# Add operation description
|
216 |
+
output_parts.append(f"\n{operation.description}:")
|
217 |
|
218 |
+
# Add output or error with markdown wrapping
|
219 |
+
if result['error']:
|
220 |
+
output_parts.append("```python\n" + f"Error: {result['error']['message']}" + "\n```")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
221 |
else:
|
222 |
+
output = result['output'].strip()
|
223 |
+
if self._looks_like_structured_data(output):
|
224 |
+
output_parts.append("```python\n" + output + "\n```")
|
225 |
+
else:
|
226 |
+
output_parts.append(output)
|
227 |
+
|
228 |
+
# Process charts
|
229 |
+
if response.charts:
|
230 |
+
output_parts.append("\nVisualizations:")
|
231 |
+
for chart in response.charts:
|
232 |
+
if chart.code:
|
233 |
+
result = self.execute_code(chart.code)
|
234 |
+
if result['plots']:
|
235 |
+
for plot_data in result['plots']:
|
236 |
+
try:
|
237 |
+
public_url = await self.save_plot_to_supabase(
|
238 |
+
plot_data=plot_data,
|
239 |
+
description=chart.image_description,
|
240 |
+
chat_id=chat_id
|
241 |
+
)
|
242 |
+
output_parts.append(f"\n{chart.image_description}")
|
243 |
+
output_parts.append(f"")
|
244 |
+
except Exception as e:
|
245 |
+
output_parts.append(f"\nError uploading chart: {str(e)}")
|
246 |
+
elif result['error']:
|
247 |
+
output_parts.append("```python\n" + f"Error generating {chart.image_description}: {result['error']['message']}" + "\n```")
|
248 |
+
|
249 |
+
return "\n".join(output_parts)
|
together_ai_llama_agent.py
CHANGED
@@ -113,7 +113,7 @@ def create_csv_agent(df: pd.DataFrame, max_retries: int = 1) -> Agent:
|
|
113 |
raise RuntimeError(f"Failed to create agent after {max_retries} attempts")
|
114 |
|
115 |
|
116 |
-
async def query_csv_agent(csv_url: str, question: str) -> str:
|
117 |
"""Query the CSV agent with a DataFrame and question and return formatted output"""
|
118 |
|
119 |
# Get the DataFrame from the CSV URL
|
@@ -140,6 +140,6 @@ async def query_csv_agent(csv_url: str, question: str) -> str:
|
|
140 |
print("Chat Result Original Object:", chat_result)
|
141 |
|
142 |
# Process and format the response
|
143 |
-
formatted_output = executor.process_response(chat_result)
|
144 |
|
145 |
return formatted_output
|
|
|
113 |
raise RuntimeError(f"Failed to create agent after {max_retries} attempts")
|
114 |
|
115 |
|
116 |
+
async def query_csv_agent(csv_url: str, question: str, chat_id: str) -> str:
|
117 |
"""Query the CSV agent with a DataFrame and question and return formatted output"""
|
118 |
|
119 |
# Get the DataFrame from the CSV URL
|
|
|
140 |
print("Chat Result Original Object:", chat_result)
|
141 |
|
142 |
# Process and format the response
|
143 |
+
formatted_output = executor.process_response(chat_result, chat_id)
|
144 |
|
145 |
return formatted_output
|