plot-agent 0.2.0__py3-none-any.whl → 0.2.2__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- plot_agent/agent.py +32 -84
- plot_agent/execution.py +90 -0
- {plot_agent-0.2.0.dist-info → plot_agent-0.2.2.dist-info}/METADATA +1 -1
- plot_agent-0.2.2.dist-info/RECORD +10 -0
- plot_agent-0.2.0.dist-info/RECORD +0 -9
- {plot_agent-0.2.0.dist-info → plot_agent-0.2.2.dist-info}/WHEEL +0 -0
- {plot_agent-0.2.0.dist-info → plot_agent-0.2.2.dist-info}/licenses/LICENSE +0 -0
- {plot_agent-0.2.0.dist-info → plot_agent-0.2.2.dist-info}/top_level.txt +0 -0
plot_agent/agent.py
CHANGED
|
@@ -1,94 +1,20 @@
|
|
|
1
1
|
import pandas as pd
|
|
2
|
-
import numpy as np
|
|
3
|
-
import matplotlib.pyplot as plt
|
|
4
|
-
import plotly.express as px
|
|
5
|
-
import plotly.graph_objects as go
|
|
6
|
-
from plotly.subplots import make_subplots
|
|
7
2
|
from io import StringIO
|
|
8
|
-
import
|
|
9
|
-
import sys
|
|
10
|
-
from typing import Dict, List, Optional, Any
|
|
3
|
+
from typing import Optional
|
|
11
4
|
|
|
12
5
|
from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder
|
|
13
6
|
from langchain_core.messages import AIMessage, HumanMessage
|
|
14
7
|
from langchain_core.tools import Tool, StructuredTool
|
|
15
8
|
from langchain.agents import AgentExecutor, create_openai_tools_agent
|
|
16
9
|
from langchain_openai import ChatOpenAI
|
|
10
|
+
|
|
17
11
|
from plot_agent.prompt import DEFAULT_SYSTEM_PROMPT
|
|
18
12
|
from plot_agent.models import (
|
|
19
13
|
GeneratedCodeInput,
|
|
20
14
|
DoesFigExistInput,
|
|
21
15
|
ViewGeneratedCodeInput,
|
|
22
16
|
)
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
class PlotlyAgentExecutionEnvironment:
|
|
26
|
-
"""Environment to safely execute plotly code and capture the fig object."""
|
|
27
|
-
|
|
28
|
-
def __init__(self, df: pd.DataFrame):
|
|
29
|
-
self.df = df
|
|
30
|
-
self.locals_dict = {
|
|
31
|
-
"df": df,
|
|
32
|
-
"px": px,
|
|
33
|
-
"go": go,
|
|
34
|
-
"pd": pd,
|
|
35
|
-
"np": np,
|
|
36
|
-
"plt": plt,
|
|
37
|
-
"make_subplots": make_subplots,
|
|
38
|
-
}
|
|
39
|
-
self.output = None
|
|
40
|
-
self.error = None
|
|
41
|
-
self.fig = None
|
|
42
|
-
|
|
43
|
-
def execute_code(self, generated_code: str) -> Dict[str, Any]:
|
|
44
|
-
"""
|
|
45
|
-
Execute the provided code and capture the fig object if created.
|
|
46
|
-
|
|
47
|
-
Args:
|
|
48
|
-
generated_code (str): The code to execute.
|
|
49
|
-
|
|
50
|
-
Returns:
|
|
51
|
-
Dict[str, Any]: A dictionary containing the fig object, output, error, and success status.
|
|
52
|
-
"""
|
|
53
|
-
self.output = None
|
|
54
|
-
self.error = None
|
|
55
|
-
|
|
56
|
-
# Capture stdout
|
|
57
|
-
old_stdout = sys.stdout
|
|
58
|
-
sys.stdout = mystdout = StringIO()
|
|
59
|
-
|
|
60
|
-
try:
|
|
61
|
-
# Execute the code
|
|
62
|
-
exec(generated_code, globals(), self.locals_dict)
|
|
63
|
-
|
|
64
|
-
# Check if a fig object was created
|
|
65
|
-
if "fig" in self.locals_dict:
|
|
66
|
-
self.fig = self.locals_dict["fig"]
|
|
67
|
-
self.output = "Code executed successfully. 'fig' object was created."
|
|
68
|
-
else:
|
|
69
|
-
print(f"no fig object created: {generated_code}")
|
|
70
|
-
self.error = "Code executed without errors, but no 'fig' object was created. Make sure your code creates a variable named 'fig'."
|
|
71
|
-
|
|
72
|
-
except Exception as e:
|
|
73
|
-
self.error = f"Error executing code: {str(e)}\n{traceback.format_exc()}"
|
|
74
|
-
|
|
75
|
-
finally:
|
|
76
|
-
# Restore stdout
|
|
77
|
-
sys.stdout = old_stdout
|
|
78
|
-
captured_output = mystdout.getvalue()
|
|
79
|
-
|
|
80
|
-
if captured_output.strip():
|
|
81
|
-
if self.output:
|
|
82
|
-
self.output += f"\nOutput:\n{captured_output}"
|
|
83
|
-
else:
|
|
84
|
-
self.output = f"Output:\n{captured_output}"
|
|
85
|
-
|
|
86
|
-
return {
|
|
87
|
-
"fig": self.fig,
|
|
88
|
-
"output": self.output,
|
|
89
|
-
"error": self.error,
|
|
90
|
-
"success": self.error is None and self.fig is not None,
|
|
91
|
-
}
|
|
17
|
+
from plot_agent.execution import PlotlyAgentExecutionEnvironment
|
|
92
18
|
|
|
93
19
|
|
|
94
20
|
class PlotlyAgent:
|
|
@@ -142,6 +68,14 @@ class PlotlyAgent:
|
|
|
142
68
|
Returns:
|
|
143
69
|
None
|
|
144
70
|
"""
|
|
71
|
+
|
|
72
|
+
# Check df
|
|
73
|
+
assert isinstance(df, pd.DataFrame), "The dataframe must be a pandas dataframe."
|
|
74
|
+
assert not df.empty, "The dataframe must not be empty."
|
|
75
|
+
|
|
76
|
+
if sql_query:
|
|
77
|
+
assert isinstance(sql_query, str), "The SQL query must be a string."
|
|
78
|
+
|
|
145
79
|
self.df = df
|
|
146
80
|
|
|
147
81
|
# Capture df.info() output
|
|
@@ -171,18 +105,27 @@ class PlotlyAgent:
|
|
|
171
105
|
Returns:
|
|
172
106
|
str: The result of the execution.
|
|
173
107
|
"""
|
|
108
|
+
assert isinstance(generated_code, str), "The generated code must be a string."
|
|
109
|
+
|
|
174
110
|
if not self.execution_env:
|
|
175
111
|
return "Error: No dataframe has been set. Please set a dataframe first."
|
|
176
112
|
|
|
177
113
|
# Store this as the last generated code
|
|
178
114
|
self.generated_code = generated_code
|
|
179
115
|
|
|
180
|
-
|
|
116
|
+
# Execute the generated code
|
|
117
|
+
code_execution_result = self.execution_env.execute_code(generated_code)
|
|
181
118
|
|
|
182
|
-
|
|
183
|
-
|
|
119
|
+
# Extract the results from the code execution
|
|
120
|
+
code_execution_success = code_execution_result.get("success", False)
|
|
121
|
+
code_execution_output = code_execution_result.get("output", "")
|
|
122
|
+
code_execution_error = code_execution_result.get("error", "")
|
|
123
|
+
|
|
124
|
+
# Check if the code executed successfully
|
|
125
|
+
if code_execution_success:
|
|
126
|
+
return f"Code executed successfully! A figure object was created.\n{code_execution_output}"
|
|
184
127
|
else:
|
|
185
|
-
return f"Error: {
|
|
128
|
+
return f"Error: {code_execution_error}\n{code_execution_output}"
|
|
186
129
|
|
|
187
130
|
def does_fig_exist(self, *args, **kwargs) -> str:
|
|
188
131
|
"""
|
|
@@ -211,23 +154,25 @@ class PlotlyAgent:
|
|
|
211
154
|
|
|
212
155
|
def _initialize_agent(self):
|
|
213
156
|
"""Initialize the LangChain agent with the necessary tools and prompt."""
|
|
157
|
+
|
|
158
|
+
# Initialize the tools
|
|
214
159
|
tools = [
|
|
215
160
|
Tool.from_function(
|
|
216
161
|
func=self.execute_plotly_code,
|
|
217
162
|
name="execute_plotly_code",
|
|
218
|
-
description="Execute the provided Plotly code and return the
|
|
163
|
+
description="Execute the provided Plotly code and return a result indicating if the code executed successfully and if a figure object was created.",
|
|
219
164
|
args_schema=GeneratedCodeInput,
|
|
220
165
|
),
|
|
221
166
|
StructuredTool.from_function(
|
|
222
167
|
func=self.does_fig_exist,
|
|
223
168
|
name="does_fig_exist",
|
|
224
|
-
description="Check if a figure exists and is available for display. This tool takes no arguments.",
|
|
169
|
+
description="Check if a figure exists and is available for display. This tool takes no arguments and returns a string indicating if a figure is available for display or not.",
|
|
225
170
|
args_schema=DoesFigExistInput,
|
|
226
171
|
),
|
|
227
172
|
StructuredTool.from_function(
|
|
228
173
|
func=self.view_generated_code,
|
|
229
174
|
name="view_generated_code",
|
|
230
|
-
description="View the generated code.",
|
|
175
|
+
description="View the generated code. This tool takes no arguments and returns the generated code as a string.",
|
|
231
176
|
args_schema=ViewGeneratedCodeInput,
|
|
232
177
|
),
|
|
233
178
|
]
|
|
@@ -265,6 +210,8 @@ class PlotlyAgent:
|
|
|
265
210
|
|
|
266
211
|
def process_message(self, user_message: str) -> str:
|
|
267
212
|
"""Process a user message and return the agent's response."""
|
|
213
|
+
assert isinstance(user_message, str), "The user message must be a string."
|
|
214
|
+
|
|
268
215
|
if not self.agent_executor:
|
|
269
216
|
return "Please set a dataframe first using set_df() method."
|
|
270
217
|
|
|
@@ -305,3 +252,4 @@ class PlotlyAgent:
|
|
|
305
252
|
def reset_conversation(self):
|
|
306
253
|
"""Reset the conversation history."""
|
|
307
254
|
self.chat_history = []
|
|
255
|
+
self.generated_code = None
|
plot_agent/execution.py
ADDED
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import sys
|
|
2
|
+
from io import StringIO
|
|
3
|
+
import traceback
|
|
4
|
+
import pandas as pd
|
|
5
|
+
import plotly.express as px
|
|
6
|
+
import plotly.graph_objects as go
|
|
7
|
+
import numpy as np
|
|
8
|
+
import matplotlib.pyplot as plt
|
|
9
|
+
from plotly.subplots import make_subplots
|
|
10
|
+
from typing import Dict, Any
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class PlotlyAgentExecutionEnvironment:
|
|
14
|
+
"""
|
|
15
|
+
Environment to safely execute plotly code and capture the fig object.
|
|
16
|
+
|
|
17
|
+
Args:
|
|
18
|
+
df (pd.DataFrame): The dataframe to use for the execution environment.
|
|
19
|
+
"""
|
|
20
|
+
|
|
21
|
+
def __init__(self, df: pd.DataFrame):
|
|
22
|
+
"""
|
|
23
|
+
Initialize the execution environment with the given dataframe.
|
|
24
|
+
|
|
25
|
+
Args:
|
|
26
|
+
df (pd.DataFrame): The dataframe to use for the execution environment.
|
|
27
|
+
"""
|
|
28
|
+
self.df = df
|
|
29
|
+
self.locals_dict = {
|
|
30
|
+
"df": df,
|
|
31
|
+
"px": px,
|
|
32
|
+
"go": go,
|
|
33
|
+
"pd": pd,
|
|
34
|
+
"np": np,
|
|
35
|
+
"plt": plt,
|
|
36
|
+
"make_subplots": make_subplots,
|
|
37
|
+
}
|
|
38
|
+
self.output = None
|
|
39
|
+
self.error = None
|
|
40
|
+
self.fig = None
|
|
41
|
+
|
|
42
|
+
def execute_code(self, generated_code: str) -> Dict[str, Any]:
|
|
43
|
+
"""
|
|
44
|
+
Execute the provided code and capture the fig object if created.
|
|
45
|
+
|
|
46
|
+
Args:
|
|
47
|
+
generated_code (str): The code to execute.
|
|
48
|
+
|
|
49
|
+
Returns:
|
|
50
|
+
Dict[str, Any]: A dictionary containing the fig object, output, error, and success status.
|
|
51
|
+
"""
|
|
52
|
+
self.output = None
|
|
53
|
+
self.error = None
|
|
54
|
+
|
|
55
|
+
# Capture stdout
|
|
56
|
+
old_stdout = sys.stdout
|
|
57
|
+
sys.stdout = mystdout = StringIO()
|
|
58
|
+
|
|
59
|
+
try:
|
|
60
|
+
# Execute the code
|
|
61
|
+
exec(generated_code, globals(), self.locals_dict)
|
|
62
|
+
|
|
63
|
+
# Check if a fig object was created
|
|
64
|
+
if "fig" in self.locals_dict:
|
|
65
|
+
self.fig = self.locals_dict["fig"]
|
|
66
|
+
self.output = "Code executed successfully. 'fig' object was created."
|
|
67
|
+
else:
|
|
68
|
+
print(f"no fig object created: {generated_code}")
|
|
69
|
+
self.error = "Code executed without errors, but no 'fig' object was created. Make sure your code creates a variable named 'fig'."
|
|
70
|
+
|
|
71
|
+
except Exception as e:
|
|
72
|
+
self.error = f"Error executing code: {str(e)}\n{traceback.format_exc()}"
|
|
73
|
+
|
|
74
|
+
finally:
|
|
75
|
+
# Restore stdout
|
|
76
|
+
sys.stdout = old_stdout
|
|
77
|
+
captured_output = mystdout.getvalue()
|
|
78
|
+
|
|
79
|
+
if captured_output.strip():
|
|
80
|
+
if self.output:
|
|
81
|
+
self.output += f"\nOutput:\n{captured_output}"
|
|
82
|
+
else:
|
|
83
|
+
self.output = f"Output:\n{captured_output}"
|
|
84
|
+
|
|
85
|
+
return {
|
|
86
|
+
"fig": self.fig,
|
|
87
|
+
"output": self.output,
|
|
88
|
+
"error": self.error,
|
|
89
|
+
"success": self.error is None and self.fig is not None,
|
|
90
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
plot_agent/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
2
|
+
plot_agent/agent.py,sha256=mflUj-vE_x9_W7XTJ3-GgYFfvg3uXV3wkrUw7wRPlVM,9592
|
|
3
|
+
plot_agent/execution.py,sha256=BBKBVGQDrg7BPWvPbLWOjkAAFRlfyu28QxClXuspd8o,2772
|
|
4
|
+
plot_agent/models.py,sha256=ZOWWeYaqmnKJarXYyXnBQQ4nwUe71ae_gMul3bZXaWU,644
|
|
5
|
+
plot_agent/prompt.py,sha256=HjRgbsAe8HHs8arQogvzOGQdThEWKRqQhtQyaUplxhQ,3064
|
|
6
|
+
plot_agent-0.2.2.dist-info/licenses/LICENSE,sha256=A4DPih7wHrh4VMEG3p1PhorqdhjmGIo8nQdYNQL7daA,1062
|
|
7
|
+
plot_agent-0.2.2.dist-info/METADATA,sha256=dLTSBMjvxg0U63r-EbN2tfI7-eZGYexq854L0kN8M6M,2841
|
|
8
|
+
plot_agent-0.2.2.dist-info/WHEEL,sha256=lTU6B6eIfYoiQJTZNc-fyaR6BpL6ehTzU3xGYxn2n8k,91
|
|
9
|
+
plot_agent-0.2.2.dist-info/top_level.txt,sha256=KyOjpihUssx26Ra-37vKUQ71pI2qgJsHaRwXHJUhjzQ,11
|
|
10
|
+
plot_agent-0.2.2.dist-info/RECORD,,
|
|
@@ -1,9 +0,0 @@
|
|
|
1
|
-
plot_agent/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
2
|
-
plot_agent/agent.py,sha256=zukHGVexMXOHHjtmb7dNB3l2iEpC0rq0a6UUpVPLe14,10845
|
|
3
|
-
plot_agent/models.py,sha256=ZOWWeYaqmnKJarXYyXnBQQ4nwUe71ae_gMul3bZXaWU,644
|
|
4
|
-
plot_agent/prompt.py,sha256=HjRgbsAe8HHs8arQogvzOGQdThEWKRqQhtQyaUplxhQ,3064
|
|
5
|
-
plot_agent-0.2.0.dist-info/licenses/LICENSE,sha256=A4DPih7wHrh4VMEG3p1PhorqdhjmGIo8nQdYNQL7daA,1062
|
|
6
|
-
plot_agent-0.2.0.dist-info/METADATA,sha256=1_3wlHu5E1_gONv4uckGTc4X9F7hHzXuQRv9EjAHIVI,2841
|
|
7
|
-
plot_agent-0.2.0.dist-info/WHEEL,sha256=lTU6B6eIfYoiQJTZNc-fyaR6BpL6ehTzU3xGYxn2n8k,91
|
|
8
|
-
plot_agent-0.2.0.dist-info/top_level.txt,sha256=KyOjpihUssx26Ra-37vKUQ71pI2qgJsHaRwXHJUhjzQ,11
|
|
9
|
-
plot_agent-0.2.0.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|