plot-agent 0.1.2__py3-none-any.whl → 0.2.1__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.
@@ -7,168 +7,21 @@ from plotly.subplots import make_subplots
7
7
  from io import StringIO
8
8
  import traceback
9
9
  import sys
10
- import re
11
- from typing import Dict, List, Optional, Any
10
+ from typing import Dict, Optional, Any
12
11
 
13
12
  from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder
14
13
  from langchain_core.messages import AIMessage, HumanMessage
15
14
  from langchain_core.tools import Tool, StructuredTool
16
- from pydantic import BaseModel, Field
17
15
  from langchain.agents import AgentExecutor, create_openai_tools_agent
18
16
  from langchain_openai import ChatOpenAI
19
17
 
20
-
21
- DEFAULT_SYSTEM_PROMPT = """
22
- You are an expert data visualization assistant that helps users create Plotly visualizations in Python.
23
- Your job is to generate Python and Plotly code based on the user's request that will create the desired visualization
24
- of their pandas DataFrame (df).
25
-
26
- You have access to a pandas df with the following information:
27
-
28
- df.info():
29
- ```plaintext
30
- {df_info}
31
- ```
32
-
33
- df.head():
34
- ```plaintext
35
- {df_head}
36
- ```
37
-
38
- {sql_context}
39
-
40
- NOTES:
41
- - You must use the execute_plotly_code(generated_code) tool run your code and use the does_fig_exist() tool to check that a fig object is available for display.
42
- - You must paste the full code, not just a reference to the code.
43
- - You must not use fig.show() in your code as it will ultimately be executed elsewhere in a headless environment.
44
- - If you need to do any data cleaning or wrangling, do it in the code before generating the plotly code as preprocessing steps assume the data is in the pandas 'df' object.
45
-
46
- TOOLS:
47
- - execute_plotly_code(generated_code) to execute the generated code.
48
- - does_fig_exist() to check that a fig object is available for display. This tool takes no arguments.
49
- - view_generated_code() to view the generated code if need to fix it. This tool takes no arguments.
50
-
51
- IMPORTANT CODE FORMATTING INSTRUCTIONS:
52
- 1. Include thorough, detailed comments in your code to explain what each section does.
53
- 2. Use descriptive variable names.
54
- 3. DO NOT include fig.show() in your code - the visualization will be rendered externally.
55
- 4. Ensure your code creates a variable named 'fig' that contains the Plotly figure object.
56
-
57
- When a user asks for a visualization:
58
- 1. YOU MUST ALWAYS use the execute_plotly_code(generated_code) tool to test and run your code.
59
- 2. If there are errors, view the generated code using view_generated_code() and fix the code.
60
- 3. Check that a figure object is available using does_fig_exist(). does_fig_exist() takes no arguments.
61
- 4. If the figure object is not available, repeat the process until it is available.
62
-
63
- IMPORTANT: The code you generate MUST be executed using the execute_plotly_code tool or no figure will be created!
64
- YOU MUST CALL execute_plotly_code WITH THE FULL CODE, NOT JUST A REFERENCE TO THE CODE.
65
-
66
- YOUR WORKFLOW MUST BE:
67
- 1. execute_plotly_code(generated_code) to make sure the code is ran and a figure object is created.
68
- 2. check that a figure object is available using does_fig_exist() to make sure the figure object was created.
69
- 3. if there are errors, view the generated code using view_generated_code() to see what went wrong.
70
- 4. fix the code and execute it again with execute_plotly_code(generated_code) to make sure the figure object is created.
71
- 5. repeat until the figure object is available.
72
-
73
- Always return the final working code (with all the comments) to the user along with an explanation of what the visualization shows.
74
- Make sure to follow best practices for data visualization, such as appropriate chart types, labels, and colors.
75
-
76
- Remember that users may want to iterate on their visualizations, so be responsive to requests for changes.
77
- """
78
-
79
-
80
- # Define input schemas for the tools
81
- class PlotDescriptionInput(BaseModel):
82
- plot_description: str = Field(
83
- ..., description="Description of the plot the user wants to create"
84
- )
85
-
86
-
87
- class GeneratedCodeInput(BaseModel):
88
- generated_code: str = Field(
89
- ..., description="Python code that creates a Plotly figure"
90
- )
91
-
92
-
93
- class DoesFigExistInput(BaseModel):
94
- """Model indicating that the does_fig_exist function takes no arguments."""
95
-
96
- pass
97
-
98
-
99
- class ViewGeneratedCodeInput(BaseModel):
100
- """Model indicating that the view_generated_code function takes no arguments."""
101
-
102
- pass
103
-
104
-
105
- class PlotlyAgentExecutionEnvironment:
106
- """Environment to safely execute plotly code and capture the fig object."""
107
-
108
- def __init__(self, df: pd.DataFrame):
109
- self.df = df
110
- self.locals_dict = {
111
- "df": df,
112
- "px": px,
113
- "go": go,
114
- "pd": pd,
115
- "np": np,
116
- "plt": plt,
117
- "make_subplots": make_subplots,
118
- }
119
- self.output = None
120
- self.error = None
121
- self.fig = None
122
-
123
- def execute_code(self, generated_code: str) -> Dict[str, Any]:
124
- """
125
- Execute the provided code and capture the fig object if created.
126
-
127
- Args:
128
- generated_code (str): The code to execute.
129
-
130
- Returns:
131
- Dict[str, Any]: A dictionary containing the fig object, output, error, and success status.
132
- """
133
- self.output = None
134
- self.error = None
135
-
136
- # Capture stdout
137
- old_stdout = sys.stdout
138
- sys.stdout = mystdout = StringIO()
139
-
140
- try:
141
- # Execute the code
142
- exec(generated_code, globals(), self.locals_dict)
143
-
144
- # Check if a fig object was created
145
- if "fig" in self.locals_dict:
146
- self.fig = self.locals_dict["fig"]
147
- self.output = "Code executed successfully. 'fig' object was created."
148
- else:
149
- print(f"no fig object created: {generated_code}")
150
- self.error = "Code executed without errors, but no 'fig' object was created. Make sure your code creates a variable named 'fig'."
151
-
152
- except Exception as e:
153
- self.error = f"Error executing code: {str(e)}\n{traceback.format_exc()}"
154
-
155
- finally:
156
- # Restore stdout
157
- sys.stdout = old_stdout
158
- captured_output = mystdout.getvalue()
159
-
160
- if captured_output.strip():
161
- if self.output:
162
- self.output += f"\nOutput:\n{captured_output}"
163
- else:
164
- self.output = f"Output:\n{captured_output}"
165
-
166
- return {
167
- "fig": self.fig,
168
- "output": self.output,
169
- "error": self.error,
170
- "success": self.error is None and self.fig is not None,
171
- }
18
+ from plot_agent.prompt import DEFAULT_SYSTEM_PROMPT
19
+ from plot_agent.models import (
20
+ GeneratedCodeInput,
21
+ DoesFigExistInput,
22
+ ViewGeneratedCodeInput,
23
+ )
24
+ from plot_agent.execution import PlotlyAgentExecutionEnvironment
172
25
 
173
26
 
174
27
  class PlotlyAgent:
@@ -176,13 +29,25 @@ class PlotlyAgent:
176
29
  A class that uses an LLM to generate Plotly code based on a user's plot description.
177
30
  """
178
31
 
179
- def __init__(self, model="gpt-4o-mini", system_prompt: Optional[str] = None):
32
+ def __init__(
33
+ self,
34
+ model="gpt-4o-mini",
35
+ system_prompt: Optional[str] = None,
36
+ verbose: bool = True,
37
+ max_iterations: int = 10,
38
+ early_stopping_method: str = "force",
39
+ handle_parsing_errors: bool = True,
40
+ ):
180
41
  """
181
42
  Initialize the PlotlyAgent.
182
43
 
183
44
  Args:
184
45
  model (str): The model to use for the LLM.
185
46
  system_prompt (Optional[str]): The system prompt to use for the LLM.
47
+ verbose (bool): Whether to print verbose output from the agent.
48
+ max_iterations (int): Maximum number of iterations for the agent to take.
49
+ early_stopping_method (str): Method to use for early stopping.
50
+ handle_parsing_errors (bool): Whether to handle parsing errors gracefully.
186
51
  """
187
52
  self.llm = ChatOpenAI(model=model)
188
53
  self.df = None
@@ -194,6 +59,10 @@ class PlotlyAgent:
194
59
  self.agent_executor = None
195
60
  self.generated_code = None
196
61
  self.system_prompt = system_prompt or DEFAULT_SYSTEM_PROMPT
62
+ self.verbose = verbose
63
+ self.max_iterations = max_iterations
64
+ self.early_stopping_method = early_stopping_method
65
+ self.handle_parsing_errors = handle_parsing_errors
197
66
 
198
67
  def set_df(self, df: pd.DataFrame, sql_query: Optional[str] = None):
199
68
  """
@@ -321,10 +190,10 @@ class PlotlyAgent:
321
190
  self.agent_executor = AgentExecutor(
322
191
  agent=agent,
323
192
  tools=tools,
324
- verbose=True,
325
- max_iterations=10,
326
- early_stopping_method="force",
327
- handle_parsing_errors=True,
193
+ verbose=self.verbose,
194
+ max_iterations=self.max_iterations,
195
+ early_stopping_method=self.early_stopping_method,
196
+ handle_parsing_errors=self.handle_parsing_errors,
328
197
  )
329
198
 
330
199
  def process_message(self, user_message: str) -> str:
@@ -0,0 +1,79 @@
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
+ """Environment to safely execute plotly code and capture the fig object."""
15
+
16
+ def __init__(self, df: pd.DataFrame):
17
+ self.df = df
18
+ self.locals_dict = {
19
+ "df": df,
20
+ "px": px,
21
+ "go": go,
22
+ "pd": pd,
23
+ "np": np,
24
+ "plt": plt,
25
+ "make_subplots": make_subplots,
26
+ }
27
+ self.output = None
28
+ self.error = None
29
+ self.fig = None
30
+
31
+ def execute_code(self, generated_code: str) -> Dict[str, Any]:
32
+ """
33
+ Execute the provided code and capture the fig object if created.
34
+
35
+ Args:
36
+ generated_code (str): The code to execute.
37
+
38
+ Returns:
39
+ Dict[str, Any]: A dictionary containing the fig object, output, error, and success status.
40
+ """
41
+ self.output = None
42
+ self.error = None
43
+
44
+ # Capture stdout
45
+ old_stdout = sys.stdout
46
+ sys.stdout = mystdout = StringIO()
47
+
48
+ try:
49
+ # Execute the code
50
+ exec(generated_code, globals(), self.locals_dict)
51
+
52
+ # Check if a fig object was created
53
+ if "fig" in self.locals_dict:
54
+ self.fig = self.locals_dict["fig"]
55
+ self.output = "Code executed successfully. 'fig' object was created."
56
+ else:
57
+ print(f"no fig object created: {generated_code}")
58
+ self.error = "Code executed without errors, but no 'fig' object was created. Make sure your code creates a variable named 'fig'."
59
+
60
+ except Exception as e:
61
+ self.error = f"Error executing code: {str(e)}\n{traceback.format_exc()}"
62
+
63
+ finally:
64
+ # Restore stdout
65
+ sys.stdout = old_stdout
66
+ captured_output = mystdout.getvalue()
67
+
68
+ if captured_output.strip():
69
+ if self.output:
70
+ self.output += f"\nOutput:\n{captured_output}"
71
+ else:
72
+ self.output = f"Output:\n{captured_output}"
73
+
74
+ return {
75
+ "fig": self.fig,
76
+ "output": self.output,
77
+ "error": self.error,
78
+ "success": self.error is None and self.fig is not None,
79
+ }
plot_agent/models.py ADDED
@@ -0,0 +1,26 @@
1
+ from pydantic import BaseModel, Field
2
+
3
+
4
+ # Define input schemas for the tools
5
+ class PlotDescriptionInput(BaseModel):
6
+ plot_description: str = Field(
7
+ ..., description="Description of the plot the user wants to create"
8
+ )
9
+
10
+
11
+ class GeneratedCodeInput(BaseModel):
12
+ generated_code: str = Field(
13
+ ..., description="Python code that creates a Plotly figure"
14
+ )
15
+
16
+
17
+ class DoesFigExistInput(BaseModel):
18
+ """Model indicating that the does_fig_exist function takes no arguments."""
19
+
20
+ pass
21
+
22
+
23
+ class ViewGeneratedCodeInput(BaseModel):
24
+ """Model indicating that the view_generated_code function takes no arguments."""
25
+
26
+ pass
plot_agent/prompt.py ADDED
@@ -0,0 +1,57 @@
1
+ DEFAULT_SYSTEM_PROMPT = """
2
+ You are an expert data visualization assistant that helps users create Plotly visualizations in Python.
3
+ Your job is to generate Python and Plotly code based on the user's request that will create the desired visualization
4
+ of their pandas DataFrame (df).
5
+
6
+ You have access to a pandas df with the following information:
7
+
8
+ df.info():
9
+ ```plaintext
10
+ {df_info}
11
+ ```
12
+
13
+ df.head():
14
+ ```plaintext
15
+ {df_head}
16
+ ```
17
+
18
+ {sql_context}
19
+
20
+ NOTES:
21
+ - You must use the execute_plotly_code(generated_code) tool run your code and use the does_fig_exist() tool to check that a fig object is available for display.
22
+ - You must paste the full code, not just a reference to the code.
23
+ - You must not use fig.show() in your code as it will ultimately be executed elsewhere in a headless environment.
24
+ - If you need to do any data cleaning or wrangling, do it in the code before generating the plotly code as preprocessing steps assume the data is in the pandas 'df' object.
25
+
26
+ TOOLS:
27
+ - execute_plotly_code(generated_code) to execute the generated code.
28
+ - does_fig_exist() to check that a fig object is available for display. This tool takes no arguments.
29
+ - view_generated_code() to view the generated code if need to fix it. This tool takes no arguments.
30
+
31
+ IMPORTANT CODE FORMATTING INSTRUCTIONS:
32
+ 1. Include thorough, detailed comments in your code to explain what each section does.
33
+ 2. Use descriptive variable names.
34
+ 3. DO NOT include fig.show() in your code - the visualization will be rendered externally.
35
+ 4. Ensure your code creates a variable named 'fig' that contains the Plotly figure object.
36
+
37
+ When a user asks for a visualization:
38
+ 1. YOU MUST ALWAYS use the execute_plotly_code(generated_code) tool to test and run your code.
39
+ 2. If there are errors, view the generated code using view_generated_code() and fix the code.
40
+ 3. Check that a figure object is available using does_fig_exist(). does_fig_exist() takes no arguments.
41
+ 4. If the figure object is not available, repeat the process until it is available.
42
+
43
+ IMPORTANT: The code you generate MUST be executed using the execute_plotly_code tool or no figure will be created!
44
+ YOU MUST CALL execute_plotly_code WITH THE FULL CODE, NOT JUST A REFERENCE TO THE CODE.
45
+
46
+ YOUR WORKFLOW MUST BE:
47
+ 1. execute_plotly_code(generated_code) to make sure the code is ran and a figure object is created.
48
+ 2. check that a figure object is available using does_fig_exist() to make sure the figure object was created.
49
+ 3. if there are errors, view the generated code using view_generated_code() to see what went wrong.
50
+ 4. fix the code and execute it again with execute_plotly_code(generated_code) to make sure the figure object is created.
51
+ 5. repeat until the figure object is available.
52
+
53
+ Always return the final working code (with all the comments) to the user along with an explanation of what the visualization shows.
54
+ Make sure to follow best practices for data visualization, such as appropriate chart types, labels, and colors.
55
+
56
+ Remember that users may want to iterate on their visualizations, so be responsive to requests for changes.
57
+ """
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: plot-agent
3
- Version: 0.1.2
3
+ Version: 0.2.1
4
4
  Summary: An AI-powered data visualization assistant using Plotly
5
5
  Author-email: andrewm4894 <andrewm4894@gmail.com>
6
6
  Project-URL: Homepage, https://github.com/andrewm4894/plot-agent
@@ -14,6 +14,9 @@ Dynamic: license-file
14
14
 
15
15
  # Plot Agent
16
16
 
17
+ [![Tests](https://github.com/andrewm4894/plot-agent/actions/workflows/test.yml/badge.svg)](https://github.com/andrewm4894/plot-agent/actions/workflows/test.yml)
18
+ [![PyPI version](https://badge.fury.io/py/plot-agent.svg)](https://badge.fury.io/py/plot-agent)
19
+
17
20
  An AI-powered data visualization assistant that helps users create Plotly visualizations in Python.
18
21
 
19
22
  ## Installation
@@ -26,12 +29,15 @@ pip install plot-agent
26
29
 
27
30
  ## Usage
28
31
 
29
- Here's a simple example of how to use Plot Agent:
32
+ See more examples in [/examples/](https://nbviewer.org/github/andrewm4894/plot-agent/tree/main/examples/) (via nbviewer so that can see the charts easily).
33
+
34
+ Here's a simple minimal example of how to use Plot Agent:
30
35
 
31
36
  ```python
32
37
  import pandas as pd
33
- from plot_agent.plotly_agent import PlotlyAgent
38
+ from plot_agent.agent import PlotlyAgent
34
39
 
40
+ # ensure OPENAI_API_KEY is set and available for langchain
35
41
 
36
42
  # Create a sample dataframe
37
43
  df = pd.DataFrame({
@@ -47,10 +53,45 @@ agent.set_df(df)
47
53
 
48
54
  # Process a visualization request
49
55
  response = agent.process_message("Create a line plot of x vs y")
56
+
57
+ # Print generated code
58
+ print(agent.generated_code)
59
+
60
+ # Get fig
50
61
  fig = agent.get_figure()
51
62
  fig.show()
52
63
  ```
53
64
 
65
+ `agent.generated_code`:
66
+
67
+ ```python
68
+ import pandas as pd
69
+ import plotly.graph_objects as go
70
+
71
+ # Creating a line plot of x vs y
72
+ # Create a figure object
73
+ fig = go.Figure()
74
+
75
+ # Add a line trace to the figure
76
+ fig.add_trace(
77
+ go.Scatter(
78
+ x=df['x'], # The x values
79
+ y=df['y'], # The y values
80
+ mode='lines+markers', # Display both lines and markers
81
+ name='Line Plot', # Name of the trace
82
+ line=dict(color='blue', width=2) # Specify line color and width
83
+ )
84
+ )
85
+
86
+ # Adding titles and labels
87
+ fig.update_layout(
88
+ title='Line Plot of x vs y', # Plot title
89
+ xaxis_title='x', # x-axis label
90
+ yaxis_title='y', # y-axis label
91
+ template='plotly_white' # A clean layout
92
+ )
93
+ ```
94
+
54
95
  ## Features
55
96
 
56
97
  - AI-powered visualization generation
@@ -0,0 +1,10 @@
1
+ plot_agent/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
2
+ plot_agent/agent.py,sha256=8JhZf0r6_0HjV1dATzbiVgkf8g1fSLvOo8dLJkFzDVA,8682
3
+ plot_agent/execution.py,sha256=B7XJCJOlOd99PUG5K9K9Al3TgvQtyta0X9LAxvodARo,2479
4
+ plot_agent/models.py,sha256=ZOWWeYaqmnKJarXYyXnBQQ4nwUe71ae_gMul3bZXaWU,644
5
+ plot_agent/prompt.py,sha256=HjRgbsAe8HHs8arQogvzOGQdThEWKRqQhtQyaUplxhQ,3064
6
+ plot_agent-0.2.1.dist-info/licenses/LICENSE,sha256=A4DPih7wHrh4VMEG3p1PhorqdhjmGIo8nQdYNQL7daA,1062
7
+ plot_agent-0.2.1.dist-info/METADATA,sha256=0GZe10CtdaXJ9qySDPFKjeCwmaTACYye6e23mCayMD8,2841
8
+ plot_agent-0.2.1.dist-info/WHEEL,sha256=lTU6B6eIfYoiQJTZNc-fyaR6BpL6ehTzU3xGYxn2n8k,91
9
+ plot_agent-0.2.1.dist-info/top_level.txt,sha256=KyOjpihUssx26Ra-37vKUQ71pI2qgJsHaRwXHJUhjzQ,11
10
+ plot_agent-0.2.1.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: setuptools (78.1.0)
2
+ Generator: setuptools (78.1.1)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5
 
@@ -1,7 +0,0 @@
1
- plot_agent/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
2
- plot_agent/plotly_agent.py,sha256=93DSwnPU7_915nsXs8SIUuL1hXFTEozBaqWQxs4HM9Y,13637
3
- plot_agent-0.1.2.dist-info/licenses/LICENSE,sha256=A4DPih7wHrh4VMEG3p1PhorqdhjmGIo8nQdYNQL7daA,1062
4
- plot_agent-0.1.2.dist-info/METADATA,sha256=GddWhRuPQ3DpujQGTANyk22oMsMASmZ2bdjz9CkZfdc,1589
5
- plot_agent-0.1.2.dist-info/WHEEL,sha256=CmyFI0kx5cdEMTLiONQRbGQwjIoR1aIYB7eCAQ4KPJ0,91
6
- plot_agent-0.1.2.dist-info/top_level.txt,sha256=KyOjpihUssx26Ra-37vKUQ71pI2qgJsHaRwXHJUhjzQ,11
7
- plot_agent-0.1.2.dist-info/RECORD,,