-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathagent.py
224 lines (177 loc) · 7.3 KB
/
agent.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
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
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
import os
import subprocess
from dotenv import load_dotenv
from langchain_openai import ChatOpenAI
from langchain.agents import AgentExecutor
from langchain.prompts import ChatPromptTemplate, MessagesPlaceholder
from langchain.tools import tool
from langsmith import traceable
from langchain.agents.format_scratchpad.openai_tools import (
format_to_openai_tool_messages,
)
from langchain.agents.output_parsers.openai_tools import OpenAIToolsAgentOutputParser
load_dotenv()
# Define the updated create_react_component tool
@tool
def create_react_component(details: str):
"""
Creates a JSX representation for a new React component based on the specified details.
Parameters:
- details (str): A comprehensive description of the React component, encompassing the component name,
type, properties, state management, styling, dependencies, default content, and user interactions.
Returns:
str: The JSX code for the React component without additional comments or data,
intended to be saved with a .md file extension.
"""
prompt = f"Create a React functional component with the following details:\n{details}\n"
llm_response = llm.invoke(prompt)
return llm_response
# Define the create_component_documentation tool
@tool
def create_component_documentation(details: str):
"""
Generates markdown documentation for a new React component based on provided details.
This function formats the documentation as a markdown file (.md), which includes descriptions of the component's
purpose, properties, usage examples, and any other relevant information.
Parameters: - details (str): A detailed description of the React component, including its purpose, properties,
expected behavior, and any special notes.
Returns:
str: The markdown-formatted documentation for the React component, intended to be saved with a .md file extension.
"""
prompt = f"Create documentation for a React component with the following details:\n{details}\n"
llm_response = llm.invoke(prompt)
return llm_response
# Define the create_unit_test tool
@tool
def create_test(details: str):
"""
Generates JavaScript unit tests for a new React component based on provided details.
This function creates a set of unit tests in plain JavaScript, without reliance on any specific
testing frameworks. These tests are designed to validate the functionality
and behavior of the specified React component. The tests check that the component performs
as expected under various conditions by directly manipulating the component's properties and
state and observing the outcomes.
Parameters:
- details (str): A detailed description of the React component, including its functionalities,
properties, state management, and interaction behaviors that need to be tested.
Returns:
str: The plain JavaScript code for the unit tests of the React component, intended to be saved with
a .js file extension.
"""
prompt = f"Create unit tests for a React component with the following details:\n{details}\n"
llm_response = llm.invoke(prompt)
return llm_response
# Define the save_to_directory tool
@tool
def save_to_directory(directory: str, filename: str, content: str, file_type: str) -> str:
"""
Creates and saves a file with the given content based on the specified file type in the specified directory.
The file extension is determined by the type of content:
- 'jsx' for React components, saved with a .jsx extension.
- 'documentation' for markdown documents, saved with a .md extension.
- 'unit_test' for JavaScript unit tests, saved with a .js extension.
Parameters:
- directory (str): The directory where the file will be saved.
- filename (str): The name of the file to be saved, without extension.
- content (str): The content to be saved in the file.
- file_type (str): The type of the file, which dictates the file extension and can be 'jsx', 'documentation', or 'unit_test'.
Returns:
str: A message indicating the success or failure of the file saving process.
"""
# Define the file extension based on the file_type
extensions = {
'react': '.jsx',
'documentation': '.md',
'test': '.js'
}
file_extension = extensions.get(file_type, '.jsx')
# Ensure directory exists
os.makedirs(directory, exist_ok=True)
# Complete file path with the appropriate extension
file_path = os.path.join(directory, f"{filename}{file_extension}")
try:
# Save the content to the file
with open(file_path, 'w') as file:
file.write(content)
return f"File '{filename}{file_extension}' successfully saved in '{directory}'."
except Exception as e:
return f"An error occurred while saving the file: {e}"
@tool
def create_component_directory(directory: str) -> str:
"""
Create a new writable directory with the given directory name if it does not exist.
If the directory exists, it ensures the directory is writable.
Parameters:
directory (str): The name of the directory to create.
Returns:
str: Success or error message.
"""
if ".." in directory:
return f"Cannot make a directory with '..' in path"
try:
os.makedirs(directory, exist_ok=True)
subprocess.run(["chmod", "u+w", directory], check=True)
return f"Directory successfully '{directory}' created and set as writeable."
except subprocess.CalledProcessError as e:
return f"Failed to create or set writable directory '{directory}': {e}"
except Exception as e:
return f"An unexpected error occurred: {e}"
# List of tools
tools = [
create_react_component,
create_component_directory,
save_to_directory,
create_component_documentation,
create_test
]
# Configure the llm
llm = ChatOpenAI(model='gpt-4o', temperature=0)
# Set up prompt template
prompt = ChatPromptTemplate.from_messages(
[
("system", "You are an expert developer in React 18 and JavaScript."),
("user", "{input}"),
MessagesPlaceholder(variable_name='agent_scratchpad'),
]
)
# Bind tools to llm
llm_with_tools = llm.bind_tools(tools)
# Define the agent
agent = (
{
"input": lambda x: x["input"],
"agent_scratchpad": lambda x: format_to_openai_tool_messages(
x["intermediate_steps"]
),
}
| prompt
| llm_with_tools
| OpenAIToolsAgentOutputParser()
)
# Define the executor
agent_executor = AgentExecutor(agent=agent, tools=tools, verbose=True)
# User prompt loop
def print_help():
help_text = """
To create a React component, please provide the following details:
1. Component Name
2. Props (list the props)
3. State Management (yes/no)
4. Styling (CSS modules/inline styles/other)
5. Dependencies (list any external libraries)
6. Default Content (structure of the component)
7. Interactions (events the component should handle)
Type 'h' to show this help message again.
Type 'q' to quit.
"""
print(help_text)
print_help()
while True:
user_prompt = input('Component Creator: (q to quit, h for help)\n')
if user_prompt.lower() == 'q':
print('Goodbye!')
break
elif user_prompt.lower() == 'h':
print_help()
else:
list(agent_executor.stream({'input': user_prompt}))