forked from smol-ai/developer
-
Notifications
You must be signed in to change notification settings - Fork 0
/
main_no_modal.py
240 lines (195 loc) · 8.24 KB
/
main_no_modal.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
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
import sys
import os
import ast
from time import sleep
generatedDir = "generated"
openai_model = "gpt-4" # or 'gpt-3.5-turbo',
openai_model_max_tokens = 2000 # i wonder how to tweak this properly
def generate_response(system_prompt, user_prompt, *args):
import openai
import tiktoken
def reportTokens(prompt):
encoding = tiktoken.encoding_for_model(openai_model)
# print number of tokens in light gray, with first 10 characters of prompt in green
print(
"\033[37m"
+ str(len(encoding.encode(prompt)))
+ " tokens\033[0m"
+ " in prompt: "
+ "\033[92m"
+ prompt[:50]
+ "\033[0m"
)
# Set up your OpenAI API credentials
openai.api_key = os.environ["OPENAI_API_KEY"]
messages = []
messages.append({"role": "system", "content": system_prompt})
reportTokens(system_prompt)
messages.append({"role": "user", "content": user_prompt})
reportTokens(user_prompt)
# loop thru each arg and add it to messages alternating role between "assistant" and "user"
role = "assistant"
for value in args:
messages.append({"role": role, "content": value})
reportTokens(value)
role = "user" if role == "assistant" else "assistant"
params = {
"model": openai_model,
"messages": messages,
"max_tokens": openai_model_max_tokens,
"temperature": 0,
}
# Send the API request
keep_trying = True
while keep_trying:
try:
response = openai.ChatCompletion.create(**params)
keep_trying = False
except Exception as e:
# e.g. when the API is too busy, we don't want to fail everything
print("Failed to generate response. Error: ", e)
sleep(30)
print("Retrying...")
# Get the reply from the API response
reply = response.choices[0]["message"]["content"]
return reply
def generate_file(
filename, filepaths_string=None, shared_dependencies=None, prompt=None
):
# call openai api with this prompt
filecode = generate_response(
f"""You are an AI developer who is trying to write a program that will generate code for the user based on their intent.
the app is: {prompt}
the files we have decided to generate are: {filepaths_string}
the shared dependencies (like filenames and variable names) we have decided on are: {shared_dependencies}
only write valid code for the given filepath and file type, and return only the code.
do not add any other explanation, only return valid code for that file type.
""",
f"""
We have broken up the program into per-file generation.
Now your job is to generate only the code for the file {filename}.
Make sure to have consistent filenames if you reference other files we are also generating.
Remember that you must obey 3 things:
- you are generating code for the file {filename}
- do not stray from the names of the files and the shared dependencies we have decided on
- MOST IMPORTANT OF ALL - the purpose of our app is {prompt} - every line of code you generate must be valid code. Do not include code fences in your response, for example
Bad response:
```javascript
console.log("hello world")
```
Good response:
console.log("hello world")
Begin generating the code now.
""",
)
return filename, filecode
def main(prompt, directory=generatedDir, file=None):
# read file from prompt if it ends in a .md filetype
if prompt.endswith(".md"):
with open(prompt, "r") as promptfile:
prompt = promptfile.read()
print("hi its me, 🐣the smol developer🐣! you said you wanted:")
# print the prompt in green color
print("\033[92m" + prompt + "\033[0m")
# example prompt:
# a Chrome extension that, when clicked, opens a small window with a page where you can enter
# a prompt for reading the currently open page and generating some response from openai
# call openai api with this prompt
filepaths_string = generate_response(
"""You are an AI developer who is trying to write a program that will generate code for the user based on their intent.
When given their intent, create a complete, exhaustive list of filepaths that the user would write to make the program.
only list the filepaths you would write, and return them as a python list of strings.
do not add any other explanation, only return a python list of strings.
""",
prompt,
)
print(filepaths_string)
# parse the result into a python list
list_actual = []
try:
list_actual = ast.literal_eval(filepaths_string)
# if shared_dependencies.md is there, read it in, else set it to None
shared_dependencies = None
if os.path.exists("shared_dependencies.md"):
with open("shared_dependencies.md", "r") as shared_dependencies_file:
shared_dependencies = shared_dependencies_file.read()
if file is not None:
# check file
print("file", file)
filename, filecode = generate_file(
file,
filepaths_string=filepaths_string,
shared_dependencies=shared_dependencies,
prompt=prompt,
)
write_file(filename, filecode, directory)
else:
clean_dir(directory)
# understand shared dependencies
shared_dependencies = generate_response(
"""You are an AI developer who is trying to write a program that will generate code for the user based on their intent.
In response to the user's prompt:
---
the app is: {prompt}
---
the files we have decided to generate are: {filepaths_string}
Now that we have a list of files, we need to understand what dependencies they share.
Please name and briefly describe what is shared between the files we are generating, including exported variables, data schemas, id names of every DOM elements that javascript functions will use, message names, and function names.
Exclusively focus on the names of the shared dependencies, and do not add any other explanation.
""",
prompt,
)
print(shared_dependencies)
# write shared dependencies as a md file inside the generated directory
write_file("shared_dependencies.md", shared_dependencies, directory)
for name in list_actual:
filename, filecode = generate_file(
name,
filepaths_string=filepaths_string,
shared_dependencies=shared_dependencies,
prompt=prompt,
)
write_file(filename, filecode, directory)
except ValueError:
print("Failed to parse result: " + result)
def write_file(filename, filecode, directory):
# Output the filename in blue color
print("\033[94m" + filename + "\033[0m")
print(filecode)
file_path = directory + "/" + filename
dir = os.path.dirname(file_path)
os.makedirs(dir, exist_ok=True)
# Open the file in write mode
with open(file_path, "w") as file:
# Write content to the file
file.write(filecode)
def clean_dir(directory):
extensions_to_skip = [
".png",
".jpg",
".jpeg",
".gif",
".bmp",
".svg",
".ico",
".tif",
".tiff",
] # Add more extensions if needed
# Check if the directory exists
if os.path.exists(directory):
# If it does, iterate over all files and directories
for root, dirs, files in os.walk(directory):
for file in files:
_, extension = os.path.splitext(file)
if extension not in extensions_to_skip:
os.remove(os.path.join(root, file))
else:
os.makedirs(directory, exist_ok=True)
if __name__ == "__main__":
if len(sys.argv) < 2:
print("Please provide a prompt")
sys.exit(1)
prompt = sys.argv[1]
directory = sys.argv[2] if len(sys.argv) > 2 else generatedDir
file = sys.argv[3] if len(sys.argv) > 3 else None
main(prompt, directory, file)