vision-agent 0.2.118__py3-none-any.whl → 0.2.119__py3-none-any.whl

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,12 +1,17 @@
1
1
  import os
2
+ import pickle as pkl
2
3
  import subprocess
4
+ import tempfile
3
5
  from pathlib import Path
4
- from typing import Any, Dict, List, Union
6
+ from typing import Any, Dict, List, Optional, Union
7
+
8
+ from IPython.display import display
5
9
 
6
10
  import vision_agent as va
7
11
  from vision_agent.lmm.types import Message
8
12
  from vision_agent.tools.tool_utils import get_tool_documentation
9
13
  from vision_agent.tools.tools import TOOL_DESCRIPTIONS
14
+ from vision_agent.utils.execute import Execution, MimeType
10
15
 
11
16
  # These tools are adapted from SWE-Agent https://github.com/princeton-nlp/SWE-agent
12
17
 
@@ -35,97 +40,91 @@ def filter_file(file_name: Union[str, Path]) -> bool:
35
40
  )
36
41
 
37
42
 
38
- def generate_vision_code(save_file: str, chat: str, media: List[str]) -> str:
39
- """Generates python code to solve vision based tasks.
40
-
41
- Parameters:
42
- save_file (str): The file path to save the code.
43
- chat (str): The chat message from the user.
44
- media (List[str]): The media files to use.
45
-
46
- Returns:
47
- str: The generated code.
48
-
49
- Examples
50
- --------
51
- >>> generate_vision_code("code.py", "Can you detect the dogs in this image?", ["image.jpg"])
52
- from vision_agent.tools import load_image, owl_v2
53
- def detect_dogs(image_path: str):
54
- image = load_image(image_path)
55
- dogs = owl_v2("dog", image)
56
- return dogs
43
+ def redisplay_results(execution: Execution) -> None:
44
+ """This function is used to add previous execution results to the current output.
45
+ This is handy if you are inside a notebook environment, call it notebook1, and you
46
+ have a nested notebook environment, call it notebook2, and you want the execution
47
+ results from notebook2 to be included in the execution results for notebook1.
48
+ """
49
+ for result in execution.results:
50
+ if result.text is not None:
51
+ display({MimeType.TEXT_PLAIN: result.text})
52
+ if result.html is not None:
53
+ display({MimeType.TEXT_HTML: result.html})
54
+ if result.markdown is not None:
55
+ display({MimeType.TEXT_MARKDOWN: result.markdown})
56
+ if result.svg is not None:
57
+ display({MimeType.IMAGE_SVG: result.svg})
58
+ if result.png is not None:
59
+ display({MimeType.IMAGE_PNG: result.png})
60
+ if result.jpeg is not None:
61
+ display({MimeType.IMAGE_JPEG: result.jpeg})
62
+ if result.mp4 is not None:
63
+ display({MimeType.VIDEO_MP4_B64: result.mp4})
64
+ if result.latex is not None:
65
+ display({MimeType.TEXT_LATEX: result.latex})
66
+ if result.json is not None:
67
+ display({MimeType.APPLICATION_JSON: result.json})
68
+ if result.extra is not None:
69
+ display(result.extra)
70
+
71
+
72
+ class Artifacts:
73
+ """Artifacts is a class that allows you to sync files between a local and remote
74
+ environment. In our case, the remote environment could be where the VisionAgent is
75
+ executing code and as the user adds new images, files or modifies files, those
76
+ need to be in sync with the remote environment the VisionAgent is running in.
57
77
  """
58
78
 
59
- if ZMQ_PORT is not None:
60
- agent = va.agent.VisionAgentCoder(
61
- report_progress_callback=lambda inp: report_progress_callback(
62
- int(ZMQ_PORT), inp
79
+ def __init__(self, remote_save_path: Union[str, Path]) -> None:
80
+ self.remote_save_path = Path(remote_save_path)
81
+ self.artifacts: Dict[str, Any] = {}
82
+
83
+ self.code_sandbox_runtime = None
84
+
85
+ def load(self, file_path: Union[str, Path]) -> None:
86
+ """Loads are artifacts into the remote environment. If an artifact value is None
87
+ it will skip loading it.
88
+
89
+ Parameters:
90
+ file_path (Union[str, Path]): The file path to load the artifacts from
91
+ """
92
+ with open(file_path, "rb") as f:
93
+ self.artifacts = pkl.load(f)
94
+ for k, v in self.artifacts.items():
95
+ if v is not None:
96
+ mode = "w" if isinstance(v, str) else "wb"
97
+ with open(self.remote_save_path.parent / k, mode) as f:
98
+ f.write(v)
99
+
100
+ def show(self) -> str:
101
+ """Shows the artifacts that have been loaded and their remote save paths."""
102
+ out_str = "[Artifacts loaded]\n"
103
+ for k in self.artifacts.keys():
104
+ out_str += (
105
+ f"Artifact {k} loaded to {str(self.remote_save_path.parent / k)}\n"
63
106
  )
107
+ out_str += "[End of artifacts]\n"
108
+ return out_str
109
+
110
+ def save(self, local_path: Optional[Union[str, Path]] = None) -> None:
111
+ save_path = (
112
+ Path(local_path) if local_path is not None else self.remote_save_path
64
113
  )
65
- else:
66
- agent = va.agent.VisionAgentCoder()
67
- try:
68
- fixed_chat: List[Message] = [{"role": "user", "content": chat, "media": media}]
69
- response = agent.chat_with_workflow(fixed_chat)
70
- code = response["code"]
71
- with open(save_file, "w") as f:
72
- f.write(code)
73
- code_lines = code.splitlines(keepends=True)
74
- total_lines = len(code_lines)
75
- return view_lines(code_lines, 0, total_lines, save_file, total_lines)
76
- except Exception as e:
77
- return str(e)
78
-
79
-
80
- def edit_vision_code(code_file: str, chat_history: List[str], media: List[str]) -> str:
81
- """Edits python code to solve a vision based task.
114
+ with open(save_path, "wb") as f:
115
+ pkl.dump(self.artifacts, f)
82
116
 
83
- Parameters:
84
- code_file (str): The file path to the code.
85
- chat_history (List[str]): The chat history to used to generate the code.
117
+ def __iter__(self) -> Any:
118
+ return iter(self.artifacts)
86
119
 
87
- Returns:
88
- str: The edited code.
120
+ def __getitem__(self, name: str) -> Any:
121
+ return self.artifacts[name]
89
122
 
90
- Examples
91
- --------
92
- >>> edit_vision_code(
93
- >>> "code.py",
94
- >>> ["Can you detect the dogs in this image?", "Can you use a higher threshold?"],
95
- >>> ["dog.jpg"],
96
- >>> )
97
- from vision_agent.tools import load_image, owl_v2
98
- def detect_dogs(image_path: str):
99
- image = load_image(image_path)
100
- dogs = owl_v2("dog", image, threshold=0.8)
101
- return dogs
102
- """
123
+ def __setitem__(self, name: str, value: Any) -> None:
124
+ self.artifacts[name] = value
103
125
 
104
- agent = va.agent.VisionAgentCoder()
105
- with open(code_file, "r") as f:
106
- code = f.read()
107
-
108
- # Append latest code to second to last message from assistant
109
- fixed_chat_history: List[Message] = []
110
- for i, chat in enumerate(chat_history):
111
- if i == 0:
112
- fixed_chat_history.append({"role": "user", "content": chat, "media": media})
113
- elif i > 0 and i < len(chat_history) - 1:
114
- fixed_chat_history.append({"role": "user", "content": chat})
115
- elif i == len(chat_history) - 1:
116
- fixed_chat_history.append({"role": "assistant", "content": code})
117
- fixed_chat_history.append({"role": "user", "content": chat})
118
-
119
- try:
120
- response = agent.chat_with_workflow(fixed_chat_history, test_multi_plan=False)
121
- code = response["code"]
122
- with open(code_file, "w") as f:
123
- f.write(code)
124
- code_lines = code.splitlines(keepends=True)
125
- total_lines = len(code_lines)
126
- return view_lines(code_lines, 0, total_lines, code_file, total_lines)
127
- except Exception as e:
128
- return str(e)
126
+ def __contains__(self, name: str) -> bool:
127
+ return name in self.artifacts
129
128
 
130
129
 
131
130
  def format_lines(lines: List[str], start_idx: int) -> str:
@@ -136,34 +135,40 @@ def format_lines(lines: List[str], start_idx: int) -> str:
136
135
 
137
136
 
138
137
  def view_lines(
139
- lines: List[str], line_num: int, window_size: int, file_path: str, total_lines: int
138
+ lines: List[str], line_num: int, window_size: int, name: str, total_lines: int
140
139
  ) -> str:
141
140
  start = max(0, line_num - window_size)
142
141
  end = min(len(lines), line_num + window_size)
143
- return (
144
- f"[File: {file_path} ({total_lines} lines total)]\n"
142
+ return_str = (
143
+ f"[Artifact: {name} ({total_lines} lines total)]\n"
145
144
  + format_lines(lines[start:end], start)
146
- + ("[End of file]" if end == len(lines) else f"[{len(lines) - end} more lines]")
145
+ + (
146
+ "[End of artifact]"
147
+ if end == len(lines)
148
+ else f"[{len(lines) - end} more lines]"
149
+ )
147
150
  )
151
+ print(return_str)
152
+ return return_str
148
153
 
149
154
 
150
- def open_file(file_path: str, line_num: int = 0, window_size: int = 100) -> str:
151
- """Opens the file at at the given path in the editor. If `line_num` is provided,
152
- the window will be moved to include that line. It only shows the first 100 lines by
153
- default! Max `window_size` supported is 2000. use `scroll up/down` to view the file
154
- if you want to see more.
155
+ def open_code_artifact(
156
+ artifacts: Artifacts, name: str, line_num: int = 0, window_size: int = 100
157
+ ) -> str:
158
+ """Opens the provided code artifact. If `line_num` is provided, the window will be
159
+ moved to include that line. It only shows the first 100 lines by default! Max
160
+ `window_size` supported is 2000.
155
161
 
156
162
  Parameters:
157
- file_path (str): The file path to open, preferred absolute path.
163
+ artifacts (Artifacts): The artifacts object to open the artifact from.
164
+ name (str): The name of the artifact to open.
158
165
  line_num (int): The line number to move the window to.
159
166
  window_size (int): The number of lines to show above and below the line.
160
167
  """
168
+ if name not in artifacts:
169
+ return f"[Artifact {name} does not exist]"
161
170
 
162
- file_path_p = Path(file_path)
163
- if not file_path_p.exists():
164
- return f"[File {file_path} does not exist]"
165
-
166
- total_lines = sum(1 for _ in open(file_path_p))
171
+ total_lines = len(artifacts[name].splitlines())
167
172
  window_size = min(window_size, 2000)
168
173
  window_size = window_size // 2
169
174
  if line_num - window_size < 0:
@@ -171,211 +176,218 @@ def open_file(file_path: str, line_num: int = 0, window_size: int = 100) -> str:
171
176
  elif line_num >= total_lines:
172
177
  line_num = total_lines - 1 - window_size
173
178
 
174
- global CURRENT_LINE, CURRENT_FILE
175
- CURRENT_LINE = line_num
176
- CURRENT_FILE = file_path
179
+ lines = artifacts[name].splitlines(keepends=True)
177
180
 
178
- with open(file_path, "r") as f:
179
- lines = f.readlines()
181
+ return view_lines(lines, line_num, window_size, name, total_lines)
180
182
 
181
- return view_lines(lines, line_num, window_size, file_path, total_lines)
182
183
 
183
-
184
- def create_file(file_path: str) -> str:
185
- """Creates and opens a new file with the given name.
184
+ def create_code_artifact(artifacts: Artifacts, name: str) -> str:
185
+ """Creates a new code artifiact with the given name.
186
186
 
187
187
  Parameters:
188
- file_path (str): The file path to create, preferred absolute path.
188
+ artifacts (Artifacts): The artifacts object to add the new artifact to.
189
+ name (str): The name of the new artifact.
189
190
  """
191
+ if name in artifacts:
192
+ return_str = f"[Artifact {name} already exists]"
193
+ else:
194
+ artifacts[name] = ""
195
+ return_str = f"[Artifact {name} created]"
196
+ print(return_str)
190
197
 
191
- file_path_p = Path(file_path)
192
- if file_path_p.exists():
193
- return f"[File {file_path} already exists]"
194
- file_path_p.touch()
195
- global CURRENT_FILE
196
- CURRENT_FILE = file_path
197
- return f"[File created {file_path}]"
198
-
198
+ display({MimeType.APPLICATION_JSON: {"last_artifact": name}})
199
+ return return_str
199
200
 
200
- def scroll_up() -> str:
201
- """Moves the window up by 100 lines."""
202
- if CURRENT_FILE is None:
203
- return "[No file is open]"
204
201
 
205
- return open_file(CURRENT_FILE, CURRENT_LINE + DEFAULT_WINDOW_SIZE)
202
+ def edit_code_artifact(
203
+ artifacts: Artifacts, name: str, start: int, end: int, content: str
204
+ ) -> str:
205
+ """Edits the given code artifact with the provided content. The content will be
206
+ inserted between the `start` and `end` line numbers. If the `start` and `end` are
207
+ the same, the content will be inserted at the `start` line number. If the `end` is
208
+ greater than the total number of lines in the file, the content will be inserted at
209
+ the end of the file. If the `start` or `end` are negative, the function will return
210
+ an error message.
206
211
 
212
+ Parameters:
213
+ artifacts (Artifacts): The artifacts object to edit the artifact from.
214
+ name (str): The name of the artifact to edit.
215
+ start (int): The line number to start the edit.
216
+ end (int): The line number to end the edit.
217
+ content (str): The content to insert.
218
+ """
219
+ # just make the artifact if it doesn't exist instead of forcing agent to call
220
+ # create_artifact
221
+ if name not in artifacts:
222
+ artifacts[name] = ""
207
223
 
208
- def scroll_down() -> str:
209
- """Moves the window down by 100 lines."""
210
- if CURRENT_FILE is None:
211
- return "[No file is open]"
224
+ total_lines = len(artifacts[name].splitlines())
225
+ if start < 0 or end < 0 or start > end or end > total_lines:
226
+ return "[Invalid line range]"
227
+ if start == end:
228
+ end += 1
212
229
 
213
- return open_file(CURRENT_FILE, CURRENT_LINE - DEFAULT_WINDOW_SIZE)
230
+ new_content_lines = content.splitlines(keepends=True)
231
+ new_content_lines = [
232
+ line if line.endswith("\n") else line + "\n" for line in new_content_lines
233
+ ]
234
+ lines = artifacts[name].splitlines()
235
+ edited_lines = lines[:start] + new_content_lines + lines[end:]
214
236
 
237
+ cur_line = start + len(content.split("\n")) // 2
238
+ with tempfile.NamedTemporaryFile(delete=True) as f:
239
+ with open(f.name, "w") as f: # type: ignore
240
+ f.writelines(edited_lines)
241
+
242
+ process = subprocess.Popen(
243
+ [
244
+ "flake8",
245
+ "--isolated",
246
+ "--select=F821,F822,F831,E111,E112,E113,E999,E902",
247
+ f.name,
248
+ ],
249
+ stdout=subprocess.PIPE,
250
+ stderr=subprocess.PIPE,
251
+ text=True,
252
+ )
253
+ stdout, _ = process.communicate()
254
+
255
+ if stdout != "":
256
+ stdout = stdout.replace(f.name, name)
257
+ error_msg = "[Edit failed with the following status]\n" + stdout
258
+ original_view = view_lines(
259
+ lines,
260
+ start + ((end - start) // 2),
261
+ DEFAULT_WINDOW_SIZE,
262
+ name,
263
+ total_lines,
264
+ )
265
+ total_lines_edit = sum(1 for _ in edited_lines)
266
+ edited_view = view_lines(
267
+ edited_lines, cur_line, DEFAULT_WINDOW_SIZE, name, total_lines_edit
268
+ )
215
269
 
216
- def search_dir(search_term: str, dir_path: str) -> str:
217
- """Searches for search_term in all files in a directory.
270
+ error_msg += f"\n[This is how your edit would have looked like if applied]\n{edited_view}\n\n[This is the original code before your edit]\n{original_view}"
271
+ return error_msg
218
272
 
219
- Parameters:
220
- search_term (str): The search term to look for.
221
- dir_path (str): The directory path to search in, preferred absolute path.
222
- """
273
+ artifacts[name] = "".join(edited_lines)
223
274
 
224
- dir_path_p = Path(dir_path)
225
- if not dir_path_p.exists():
226
- return f"[Directory {dir_path} does not exist]"
227
-
228
- matches = []
229
- for file in dir_path_p.glob("**/*"):
230
- if filter_file(file):
231
- with open(file, "r") as f:
232
- lines = f.readlines()
233
- for i, line in enumerate(lines):
234
- if search_term in line:
235
- matches.append(f"{file}:{i}|{line.strip()}\n")
236
- if not matches:
237
- return f"[No matches found for {search_term} in {dir_path}]"
238
- if len(matches) > 100:
239
- return f"[More than {len(matches)} matches found for {search_term} in {dir_path}. Please narrow your search]"
240
-
241
- return_str = f"[Found {len(matches)} matches for {search_term} in {dir_path}]\n"
242
- for match in matches:
243
- return_str += match
244
-
245
- return_str += f"[End of matches for {search_term} in {dir_path}]"
246
- return return_str
275
+ display({MimeType.APPLICATION_JSON: {"last_artifact": name}})
276
+ return open_code_artifact(artifacts, name, cur_line)
247
277
 
248
278
 
249
- def search_file(search_term: str, file_path: str) -> str:
250
- """Searches the file for the given search term.
279
+ def generate_vision_code(
280
+ artifacts: Artifacts, name: str, chat: str, media: List[str]
281
+ ) -> str:
282
+ """Generates python code to solve vision based tasks.
251
283
 
252
284
  Parameters:
253
- search_term (str): The search term to look for.
254
- file_path (str): The file path to search in, preferred absolute path.
255
- """
256
-
257
- file_path_p = Path(file_path)
258
- if not file_path_p.exists():
259
- return f"[File {file_path} does not exist]"
285
+ artifacts (Artifacts): The artifacts object to save the code to.
286
+ name (str): The name of the artifact to save the code to.
287
+ chat (str): The chat message from the user.
288
+ media (List[str]): The media files to use.
260
289
 
261
- with open(file_path_p, "r") as f:
262
- lines = f.readlines()
290
+ Returns:
291
+ str: The generated code.
263
292
 
264
- search_results = []
265
- for i, line in enumerate(lines):
266
- if search_term in line:
267
- search_results.append(f"{i}|{line.strip()}\n")
293
+ Examples
294
+ --------
295
+ >>> generate_vision_code(artifacts, "code.py", "Can you detect the dogs in this image?", ["image.jpg"])
296
+ from vision_agent.tools import load_image, owl_v2
297
+ def detect_dogs(image_path: str):
298
+ image = load_image(image_path)
299
+ dogs = owl_v2("dog", image)
300
+ return dogs
301
+ """
268
302
 
269
- if not search_results:
270
- return f"[No matches found for {search_term} in {file_path}]"
303
+ if ZMQ_PORT is not None:
304
+ agent = va.agent.VisionAgentCoder(
305
+ report_progress_callback=lambda inp: report_progress_callback(
306
+ int(ZMQ_PORT), inp
307
+ )
308
+ )
309
+ else:
310
+ agent = va.agent.VisionAgentCoder()
271
311
 
272
- return_str = (
273
- f"[Found {len(search_results)} matches for {search_term} in {file_path}]\n"
274
- )
275
- for result in search_results:
276
- return_str += result
312
+ fixed_chat: List[Message] = [{"role": "user", "content": chat, "media": media}]
313
+ response = agent.chat_with_workflow(fixed_chat, test_multi_plan=True)
314
+ redisplay_results(response["test_result"])
315
+ code = response["code"]
316
+ artifacts[name] = code
317
+ code_lines = code.splitlines(keepends=True)
318
+ total_lines = len(code_lines)
277
319
 
278
- return_str += f"[End of matches for {search_term} in {file_path}]"
279
- return return_str
320
+ display({MimeType.APPLICATION_JSON: {"last_artifact": name}})
321
+ return view_lines(code_lines, 0, total_lines, name, total_lines)
280
322
 
281
323
 
282
- def find_file(file_name: str, dir_path: str = "./") -> str:
283
- """Finds all files with the given name in the specified directory.
324
+ def edit_vision_code(
325
+ artifacts: Artifacts, name: str, chat_history: List[str], media: List[str]
326
+ ) -> str:
327
+ """Edits python code to solve a vision based task.
284
328
 
285
329
  Parameters:
286
- file_name (str): The file name to look for.
287
- dir_path (str): The directory path to search in, preferred absolute path.
288
- """
289
-
290
- dir_path_p = Path(dir_path)
291
- if not dir_path_p.exists():
292
- return f"[Directory {dir_path} does not exist]"
293
-
294
- files = list(dir_path_p.glob(f"**/*{file_name}*"))
295
- files = [f for f in files if filter_file(f)]
296
- if not files:
297
- return f"[No files found in {dir_path} with name {file_name}]"
298
-
299
- return_str = f"[Found {len(files)} matches for {file_name} in {dir_path}]\n"
300
- for match in files:
301
- return_str += str(match) + "\n"
330
+ artifacts (Artifacts): The artifacts object to save the code to.
331
+ name (str): The file path to the code.
332
+ chat_history (List[str]): The chat history to used to generate the code.
302
333
 
303
- return_str += f"[End of matches for {file_name} in {dir_path}]"
304
- return return_str
334
+ Returns:
335
+ str: The edited code.
305
336
 
337
+ Examples
338
+ --------
339
+ >>> edit_vision_code(
340
+ >>> artifacts,
341
+ >>> "code.py",
342
+ >>> ["Can you detect the dogs in this image?", "Can you use a higher threshold?"],
343
+ >>> ["dog.jpg"],
344
+ >>> )
345
+ from vision_agent.tools import load_image, owl_v2
346
+ def detect_dogs(image_path: str):
347
+ image = load_image(image_path)
348
+ dogs = owl_v2("dog", image, threshold=0.8)
349
+ return dogs
350
+ """
306
351
 
307
- def edit_file(file_path: str, start: int, end: int, content: str) -> str:
308
- """Edits the file at the given path with the provided content. The content will be
309
- inserted between the `start` and `end` line numbers. If the `start` and `end` are
310
- the same, the content will be inserted at the `start` line number. If the `end` is
311
- greater than the total number of lines in the file, the content will be inserted at
312
- the end of the file. If the `start` or `end` are negative, the function will return
313
- an error message.
352
+ agent = va.agent.VisionAgentCoder()
353
+ if name not in artifacts:
354
+ return f"[Artifact {name} does not exist]"
314
355
 
315
- Parameters:
316
- file_path (str): The file path to edit, preferred absolute path.
317
- start (int): The line number to start the edit.
318
- end (int): The line number to end the edit.
319
- content (str): The content to insert.
320
- """
321
- file_path_p = Path(file_path)
322
- if not file_path_p.exists():
323
- return f"[File {file_path} does not exist]"
356
+ code = artifacts[name]
324
357
 
325
- total_lines = sum(1 for _ in open(file_path_p))
326
- if start < 0 or end < 0 or start > end or end > total_lines:
327
- return "[Invalid line range]"
328
- if start == end:
329
- end += 1
358
+ # Append latest code to second to last message from assistant
359
+ fixed_chat_history: List[Message] = []
360
+ for i, chat in enumerate(chat_history):
361
+ if i == 0:
362
+ fixed_chat_history.append({"role": "user", "content": chat, "media": media})
363
+ elif i > 0 and i < len(chat_history) - 1:
364
+ fixed_chat_history.append({"role": "user", "content": chat})
365
+ elif i == len(chat_history) - 1:
366
+ fixed_chat_history.append({"role": "assistant", "content": code})
367
+ fixed_chat_history.append({"role": "user", "content": chat})
330
368
 
331
- new_content_lines = content.splitlines(keepends=True)
332
- new_content_lines = [
333
- line if line.endswith("\n") else line + "\n" for line in new_content_lines
334
- ]
335
- with open(file_path_p, "r") as f:
336
- lines = f.readlines()
337
- edited_lines = lines[:start] + new_content_lines + lines[end:]
369
+ response = agent.chat_with_workflow(fixed_chat_history, test_multi_plan=False)
370
+ redisplay_results(response["test_result"])
371
+ code = response["code"]
372
+ artifacts[name] = code
373
+ code_lines = code.splitlines(keepends=True)
374
+ total_lines = len(code_lines)
338
375
 
339
- cur_line = start + len(content.split("\n")) // 2
340
- tmp_file = file_path_p.with_suffix(".tmp")
341
- with open(tmp_file, "w") as f:
342
- f.writelines(edited_lines)
343
-
344
- process = subprocess.Popen(
345
- [
346
- "flake8",
347
- "--isolated",
348
- "--select=F821,F822,F831,E111,E112,E113,E999,E902",
349
- tmp_file,
350
- ],
351
- stdout=subprocess.PIPE,
352
- stderr=subprocess.PIPE,
353
- text=True,
354
- )
355
- stdout, _ = process.communicate()
356
- tmp_file.unlink()
357
- if stdout != "":
358
- stdout = stdout.replace(tmp_file.name, file_path)
359
- error_msg = "[Edit failed with the following status]\n" + stdout
360
- original_view = view_lines(
361
- lines,
362
- start + ((end - start) // 2),
363
- DEFAULT_WINDOW_SIZE,
364
- file_path,
365
- total_lines,
366
- )
367
- total_lines_edit = sum(1 for _ in edited_lines)
368
- edited_view = view_lines(
369
- edited_lines, cur_line, DEFAULT_WINDOW_SIZE, file_path, total_lines_edit
370
- )
376
+ display({MimeType.APPLICATION_JSON: {"last_artifact": name}})
377
+ return view_lines(code_lines, 0, total_lines, name, total_lines)
371
378
 
372
- error_msg += f"\n[This is how your edit would have looked like if applied]\n{edited_view}\n\n[This is the original code before your edit]\n{original_view}"
373
- return error_msg
374
379
 
375
- with open(file_path_p, "w") as f:
376
- f.writelines(edited_lines)
380
+ def write_media_artifact(artifacts: Artifacts, local_path: str) -> str:
381
+ """Writes a media file to the artifacts object.
377
382
 
378
- return open_file(file_path, cur_line)
383
+ Parameters:
384
+ artifacts (Artifacts): The artifacts object to save the media to.
385
+ local_path (str): The local path to the media file.
386
+ """
387
+ with open(local_path, "rb") as f:
388
+ media = f.read()
389
+ artifacts[Path(local_path).name] = media
390
+ return f"[Media {Path(local_path).name} saved]"
379
391
 
380
392
 
381
393
  def get_tool_descriptions() -> str:
@@ -388,15 +400,11 @@ def get_tool_descriptions() -> str:
388
400
  META_TOOL_DOCSTRING = get_tool_documentation(
389
401
  [
390
402
  get_tool_descriptions,
403
+ open_code_artifact,
404
+ create_code_artifact,
405
+ edit_code_artifact,
391
406
  generate_vision_code,
392
407
  edit_vision_code,
393
- open_file,
394
- create_file,
395
- scroll_up,
396
- scroll_down,
397
- edit_file,
398
- search_dir,
399
- search_file,
400
- find_file,
408
+ write_media_artifact,
401
409
  ]
402
410
  )