-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathmain.py
333 lines (265 loc) · 11.8 KB
/
main.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
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
import importlib
import os
import asyncio
import csv
import yaml
from openai import OpenAI
from datetime import datetime
from schedule_task import schedule_task, check_scheduled_task
from update_lexicon import update_lexicon
from generate_task import generate_task
from models.oai import gpt
COMMANDS = """
- l: list all tasks
- t <task_name>: run test suite for an existing task
- a: see all scheduled tasks
- s <task_name> <"daily" || number of seconds>: schedule a task (human-edited tasks only)
- g: generate a coding task and test set (input and output are numbers only)
- r <task_name>: run an existing task once (human-edited tasks only)
- q: quit
- type something else to just chat with me ☕️"""
# --- Model Instance class ---
class ModelInstance:
def __init__(self, folder_path):
self.name = folder_path
with open(f"{folder_path}/index.yaml", 'r') as file:
data = yaml.safe_load(file)
self.description = data['description']
self.model = data['model']
self.actions = data['actions']
self.values = data['values']
self.references = data['references'] if 'references' in data else None
self.additional_api_data = data['additional_api_data'] if 'additional_api_data' in data else None
self.messages = [{'role': 'system', 'content': self._system_prompt()}]
self.current_session = []
def _system_prompt(self):
if not hasattr(self, 'description') or not hasattr(self, 'actions') or not hasattr(self, 'values'):
raise AttributeError("Required attributes 'description', 'actions', or 'values' not defined for ModelInstance.")
return f"""
{self.description}
You may encourage the the following actions:
{COMMANDS}
You ascribe to the following values:
{self.values}
"""
def get_response(self, user_input):
self.messages.append({'role': 'user', 'content': user_input})
text = gpt(self.model, messages=self.messages)
self.messages.append({'role': 'assistant', 'content': text})
return text
def print_debug_info(self):
print("Model Debug Information:")
print("Description:")
print(f" {self.description}")
print("Actions:")
for action in self.actions:
print(f" - {action}")
print("Values:")
for value in self.values:
print(f" - {value}")
print("References:")
print(f" {self.references}")
print("Additional API Data:")
print(f" {self.additional_api_data}")
# --- Commands ---
def c(self):
print('Describe your task')
task_desc = input("> ")
text = self.get_response(f'Break down this into a series of subtasks {task_desc}')
while True:
print(text)
self.messages.append({'role': 'assistant', 'content': 'Does this look good? yes / no + explain why'})
print(f'\nDoes this look good? yes / no + explain why')
i = input("> ")
if i == 'y' or i =='yes':
break
else:
if len(i) > 2:
feedback_prompt = f"No. Please improve tasks based on feedback: {i}"
else:
feedback_prompt = "No, please make these tasks more clear"
text = self.get_response(feedback_prompt)
while True:
break
# --- Execute ---
def initialize():
global master
master = ModelInstance('master')
# Uncomment this to print debug information
# master.print_debug_info()
# Updates all files at the end of session.
def end_session():
end_message = "\nThank you for coming to bmo.cafe. Goodbye!"
print(end_message)
def get_lexicon_tasks():
lexicon_path = 'lexicon.yaml'
# Read the current contents of the lexicon file
with open(lexicon_path, 'r') as file:
lexicon_data = yaml.safe_load(file)
# Return the tasks if they exist and are not empty
if 'tasks' in lexicon_data and lexicon_data['tasks']:
return lexicon_data['tasks']
else:
return None
def l():
print("Existing tasks:")
tasks = get_lexicon_tasks()
if tasks is not None:
for task in tasks:
for task_name, task_description in task.items():
print(f"- {task_name}: {task_description}")
else:
print("No tasks found in the lexicon.")
def g():
print("Let's generate a new task! Please describe it:")
task_desc = input("< ")
# Generate name of task
task_name = master.get_response(f"""
Help to generate a new task for a program.
The user provided this description of the task: {task_desc}
Please generate a short name for this task, using only lowercase letters, numbers and underscores. For example, "hello_world". Reply ONLY with the short name.
""")
# Make directory for task
os.makedirs(f"tasks/{task_name}")
# Create config.yaml
task_desc = master.get_response(f'Clean up this task description so it is grammatically correct and in complete sentences: {task_desc}')
with open(f"tasks/{task_name}/config.yaml", "w") as f:
f.write(f"""description: {task_desc}
generated_with:
type: ai
model: {master.model}
""")
# Create program
code, test_set = generate_task(task_name, task_desc)
with open(f"tasks/{task_name}/index.py", "w") as f:
f.write(code)
# Write tests to tests.csv
with open(f"tasks/{task_name}/tests.csv", "w", newline='') as f:
writer = csv.writer(f)
writer.writerow(['input', 'ideal_output']) # Write header
for test in test_set:
writer.writerow([test['input'], test['ideal_output']])
print(f'Task {task_name} and its tests is successfully created!')
update_lexicon()
def r(task_name):
tasks = get_lexicon_tasks()
if tasks and any(task_name in task for task in tasks):
print(f"Running task: {task_name}")
task_folder = f"tasks/{task_name}"
if os.path.exists(task_folder):
task_config_path = os.path.join(task_folder, "config.yaml")
with open(task_config_path, 'r') as file:
task_config = yaml.safe_load(file)
print(f"Task description: {task_config['description']}")
task_script_path = os.path.join(task_folder, "index.py")
if os.path.exists(task_script_path):
print(f"Executing script at {task_script_path}\n")
spec = importlib.util.spec_from_file_location(task_name, task_script_path)
task_module = importlib.util.module_from_spec(spec)
spec.loader.exec_module(task_module)
task_function = getattr(task_module, task_name)
if 'runtime' in task_config and task_config['runtime'].get('asyncio') is True:
asyncio.run(task_function())
else:
task_function()
else:
print(f"Task script not found at: {task_script_path}")
else:
print(f"Task '{task_name}' not found in the lexicon. Please provide a valid task_name.")
def t(task_name):
tasks = get_lexicon_tasks()
if tasks and any(task_name in task for task in tasks):
task_folder = f"tasks/{task_name}"
if os.path.exists(task_folder):
task_script_path = os.path.join(task_folder, "index.py")
task_tests_path = os.path.join(task_folder, "tests.csv")
if os.path.exists(task_script_path) and os.path.exists(task_tests_path):
print(f"Running tests for task: {task_name}")
with open(task_tests_path, 'r') as file:
csv_reader = csv.reader(file)
next(csv_reader) # Skip the header row
result_file = os.path.join(task_folder, "results.csv")
with open(result_file, 'w') as file:
writer = csv.writer(file)
writer.writerow(['input', 'ideal_output', 'actual_output'])
for row in csv_reader:
print(f"\nRunning test case: {row}")
input_value = row[0]
ideal_output = row[1]
spec = importlib.util.spec_from_file_location(task_name, task_script_path)
task_module = importlib.util.module_from_spec(spec)
spec.loader.exec_module(task_module)
task_function = getattr(task_module, task_name)
actual_output = task_function(input_value)
with open(result_file, 'a') as file:
writer = csv.writer(file)
writer.writerow([input_value, ideal_output, actual_output])
else:
if not os.path.exists(task_script_path):
print(f"Task script not found at: {task_script_path}")
if not os.path.exists(task_tests_path):
print(f"Test cases not found at: {task_tests_path}")
else:
print(f"Task folder not found at: {task_folder}")
else:
print(f"Task '{task_name}' not found in the lexicon. Please provide a valid task_name.")
def s(task_name, schedule):
if task_name is None:
print("Please provide a task name to schedule.")
return
if schedule is None:
print("Please provide a schedule (either 'daily' or a number of seconds) for the task.")
return
tasks = get_lexicon_tasks()
if tasks and any(task_name in task for task in tasks):
path = None
if schedule.isdigit():
seconds = int(schedule)
print(f"Scheduling task '{task_name}' to run every {seconds} seconds.")
path = schedule_task(task_name, None, seconds)
else:
print(f"Scheduling task '{task_name}' to run daily at 8pm PT.")
path = schedule_task(task_name, 'daily')
check_scheduled_task(path)
else:
print(f"Task '{task_name}' not found in the lexicon. Please provide a valid task name.")
def a():
print("Scheduled tasks:")
os.system("launchctl list | grep com.bmocafe")
def main():
initialize()
print("""Welcome to bmo.cafe ☕️ How can I help you today?""")
print("Actions:")
print(COMMANDS)
while True:
try:
user_input = input("> ")
user_input = user_input.lower()
if user_input == 'l':
l()
elif user_input == 'g':
g()
elif user_input.startswith('r ') or user_input == 'r':
task_name = user_input[2:]
r(task_name)
elif user_input.startswith('t ') or user_input == 't':
task_name = user_input[2:]
t(task_name)
elif user_input.startswith('s ') or user_input == 's':
task_name = user_input.split(' ')[1] if len(user_input.split(' ')) > 1 else None
schedule = user_input.split(' ')[2] if len(user_input.split(' ')) > 2 else None
s(task_name, schedule)
elif user_input == 'a':
a()
elif user_input == 'q':
end_session()
break
else:
text_response = master.get_response(user_input)
print(text_response)
print(COMMANDS)
except KeyboardInterrupt:
end_session()
break
if __name__ == "__main__":
main()