vision-agent 0.2.93__py3-none-any.whl → 0.2.95__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.
- vision_agent/agent/vision_agent_coder.py +15 -19
- vision_agent/tools/tools.py +15 -10
- vision_agent/utils/execute.py +71 -36
- vision_agent/utils/sim.py +1 -0
- {vision_agent-0.2.93.dist-info → vision_agent-0.2.95.dist-info}/METADATA +3 -2
- {vision_agent-0.2.93.dist-info → vision_agent-0.2.95.dist-info}/RECORD +8 -8
- {vision_agent-0.2.93.dist-info → vision_agent-0.2.95.dist-info}/LICENSE +0 -0
- {vision_agent-0.2.93.dist-info → vision_agent-0.2.95.dist-info}/WHEEL +0 -0
@@ -373,7 +373,7 @@ def write_and_test_code(
|
|
373
373
|
"code": DefaultImports.prepend_imports(code),
|
374
374
|
"payload": {
|
375
375
|
"test": test,
|
376
|
-
"result": result.to_json(),
|
376
|
+
# "result": result.to_json(),
|
377
377
|
},
|
378
378
|
}
|
379
379
|
)
|
@@ -426,7 +426,8 @@ def debug_code(
|
|
426
426
|
) -> tuple[str, str, Execution]:
|
427
427
|
log_progress(
|
428
428
|
{
|
429
|
-
"type": "
|
429
|
+
"type": "log",
|
430
|
+
"log_content": ("Debugging code"),
|
430
431
|
"status": "started",
|
431
432
|
}
|
432
433
|
)
|
@@ -469,10 +470,11 @@ def debug_code(
|
|
469
470
|
)
|
470
471
|
log_progress(
|
471
472
|
{
|
472
|
-
"type": "
|
473
|
+
"type": "log",
|
474
|
+
"log_content": ("Running code"),
|
473
475
|
"status": "running",
|
476
|
+
"code": DefaultImports.prepend_imports(code),
|
474
477
|
"payload": {
|
475
|
-
"code": DefaultImports.prepend_imports(code),
|
476
478
|
"test": test,
|
477
479
|
},
|
478
480
|
}
|
@@ -483,12 +485,15 @@ def debug_code(
|
|
483
485
|
)
|
484
486
|
log_progress(
|
485
487
|
{
|
486
|
-
"type": "
|
488
|
+
"type": "log",
|
489
|
+
"log_content": (
|
490
|
+
"Code execution succeed" if result.success else "Code execution failed"
|
491
|
+
),
|
487
492
|
"status": "completed" if result.success else "failed",
|
493
|
+
"code": DefaultImports.prepend_imports(code),
|
488
494
|
"payload": {
|
489
|
-
"code": DefaultImports.prepend_imports(code),
|
490
495
|
"test": test,
|
491
|
-
"result": result.to_json(),
|
496
|
+
# "result": result.to_json(),
|
492
497
|
},
|
493
498
|
}
|
494
499
|
)
|
@@ -525,7 +530,8 @@ def retrieve_tools(
|
|
525
530
|
) -> Dict[str, str]:
|
526
531
|
log_progress(
|
527
532
|
{
|
528
|
-
"type": "
|
533
|
+
"type": "log",
|
534
|
+
"log_content": ("Retrieving tools for each plan"),
|
529
535
|
"status": "started",
|
530
536
|
}
|
531
537
|
)
|
@@ -802,17 +808,6 @@ class VisionAgentCoder(Agent):
|
|
802
808
|
plan.append({"code": code, "test": test, "plan": plan_i})
|
803
809
|
|
804
810
|
execution_result = cast(Execution, results["test_result"])
|
805
|
-
self.log_progress(
|
806
|
-
{
|
807
|
-
"type": "final_code",
|
808
|
-
"status": "completed" if success else "failed",
|
809
|
-
"payload": {
|
810
|
-
"code": DefaultImports.prepend_imports(code),
|
811
|
-
"test": test,
|
812
|
-
"result": execution_result.to_json(),
|
813
|
-
},
|
814
|
-
}
|
815
|
-
)
|
816
811
|
|
817
812
|
if display_visualization:
|
818
813
|
for res in execution_result.results:
|
@@ -822,6 +817,7 @@ class VisionAgentCoder(Agent):
|
|
822
817
|
play_video(res.mp4)
|
823
818
|
|
824
819
|
return {
|
820
|
+
"status": "completed" if success else "failed",
|
825
821
|
"code": DefaultImports.prepend_imports(code),
|
826
822
|
"test": test,
|
827
823
|
"test_result": execution_result,
|
vision_agent/tools/tools.py
CHANGED
@@ -9,7 +9,6 @@ from typing import Any, Dict, List, Optional, Tuple, Union, cast
|
|
9
9
|
import cv2
|
10
10
|
import numpy as np
|
11
11
|
import requests
|
12
|
-
from moviepy.editor import ImageSequenceClip
|
13
12
|
from PIL import Image, ImageDraw, ImageFont
|
14
13
|
from pillow_heif import register_heif_opener # type: ignore
|
15
14
|
from pytube import YouTube # type: ignore
|
@@ -1044,15 +1043,21 @@ def save_video(
|
|
1044
1043
|
if fps <= 0:
|
1045
1044
|
_LOGGER.warning(f"Invalid fps value: {fps}. Setting fps to 4 (default value).")
|
1046
1045
|
fps = 4
|
1047
|
-
|
1048
|
-
|
1049
|
-
|
1050
|
-
|
1051
|
-
|
1052
|
-
|
1053
|
-
|
1054
|
-
|
1055
|
-
|
1046
|
+
|
1047
|
+
if not output_video_path:
|
1048
|
+
output_video_path = tempfile.NamedTemporaryFile(
|
1049
|
+
suffix=".mp4", delete=False
|
1050
|
+
).name
|
1051
|
+
|
1052
|
+
height, width, layers = frames[0].shape if frames else (0, 0, 0)
|
1053
|
+
fourcc = cv2.VideoWriter_fourcc(*"mp4v") # type: ignore
|
1054
|
+
video = cv2.VideoWriter(output_video_path, fourcc, fps, (width, height))
|
1055
|
+
for frame in frames:
|
1056
|
+
video.write(cv2.cvtColor(frame, cv2.COLOR_RGB2BGR))
|
1057
|
+
video.release()
|
1058
|
+
|
1059
|
+
_save_video_to_result(output_video_path)
|
1060
|
+
return output_video_path
|
1056
1061
|
|
1057
1062
|
|
1058
1063
|
def _save_video_to_result(video_uri: str) -> None:
|
vision_agent/utils/execute.py
CHANGED
@@ -1,6 +1,5 @@
|
|
1
1
|
import abc
|
2
2
|
import base64
|
3
|
-
import copy
|
4
3
|
import logging
|
5
4
|
import os
|
6
5
|
import platform
|
@@ -17,9 +16,14 @@ from typing import Any, Dict, Iterable, List, Optional, Union
|
|
17
16
|
import nbformat
|
18
17
|
import tenacity
|
19
18
|
from dotenv import load_dotenv
|
19
|
+
from e2b.exceptions import SandboxException
|
20
20
|
from e2b_code_interpreter import CodeInterpreter as E2BCodeInterpreterImpl
|
21
21
|
from e2b_code_interpreter import Execution as E2BExecution
|
22
22
|
from e2b_code_interpreter import Result as E2BResult
|
23
|
+
from h11._util import LocalProtocolError
|
24
|
+
from httpx import ConnectError
|
25
|
+
from httpx import RemoteProtocolError as HttpcoreRemoteProtocolError
|
26
|
+
from httpx import RemoteProtocolError as HttpxRemoteProtocolError
|
23
27
|
from nbclient import NotebookClient
|
24
28
|
from nbclient import __version__ as nbclient_version
|
25
29
|
from nbclient.exceptions import CellTimeoutError, DeadKernelError
|
@@ -29,7 +33,6 @@ from pydantic import BaseModel, field_serializer
|
|
29
33
|
from typing_extensions import Self
|
30
34
|
|
31
35
|
from vision_agent.utils.exceptions import (
|
32
|
-
RemoteSandboxClosedError,
|
33
36
|
RemoteSandboxCreationError,
|
34
37
|
RemoteSandboxExecutionError,
|
35
38
|
)
|
@@ -106,13 +109,8 @@ class Result:
|
|
106
109
|
is_main_result: bool
|
107
110
|
"Whether this data is the result of the cell. Data can be produced by display calls of which can be multiple in a cell."
|
108
111
|
|
109
|
-
raw: Dict[str, str]
|
110
|
-
"Dictionary that maps MIME types to their corresponding string representations of the data."
|
111
|
-
|
112
112
|
def __init__(self, is_main_result: bool, data: Dict[str, Any]):
|
113
113
|
self.is_main_result = is_main_result
|
114
|
-
self.raw = copy.deepcopy(data)
|
115
|
-
|
116
114
|
self.text = data.pop(MimeType.TEXT_PLAIN, None)
|
117
115
|
if self.text and (self.text.startswith("'") and self.text.endswith("'")):
|
118
116
|
# This is a workaround for the issue that str result is wrapped with single quotes by notebook.
|
@@ -136,13 +134,13 @@ class Result:
|
|
136
134
|
|
137
135
|
# Allows to iterate over formats()
|
138
136
|
def __getitem__(self, key: Any) -> Any:
|
139
|
-
return
|
137
|
+
return getattr(self, key)
|
140
138
|
|
141
139
|
def __str__(self) -> str:
|
142
140
|
return repr(self)
|
143
141
|
|
144
142
|
def __repr__(self) -> str:
|
145
|
-
return str(self.
|
143
|
+
return str(self.text)
|
146
144
|
|
147
145
|
def _repr_html_(self) -> Optional[str]:
|
148
146
|
"""Returns the HTML representation of the data."""
|
@@ -215,9 +213,16 @@ class Result:
|
|
215
213
|
"""
|
216
214
|
Creates a Result object from an E2BResult object.
|
217
215
|
"""
|
216
|
+
data = {
|
217
|
+
MimeType.TEXT_PLAIN.value: result.text,
|
218
|
+
MimeType.IMAGE_PNG.value: result.png,
|
219
|
+
MimeType.APPLICATION_JSON.value: result.json,
|
220
|
+
}
|
221
|
+
for k, v in result.extra.items():
|
222
|
+
data[k] = v
|
218
223
|
return Result(
|
219
224
|
is_main_result=result.is_main_result,
|
220
|
-
data=
|
225
|
+
data=data,
|
221
226
|
)
|
222
227
|
|
223
228
|
|
@@ -367,7 +372,7 @@ class Execution(BaseModel):
|
|
367
372
|
value=_remove_escape_and_color_codes(exec.error.value),
|
368
373
|
traceback_raw=[
|
369
374
|
_remove_escape_and_color_codes(line)
|
370
|
-
for line in exec.error.
|
375
|
+
for line in exec.error.traceback.split("\n")
|
371
376
|
],
|
372
377
|
)
|
373
378
|
if exec.error
|
@@ -436,11 +441,12 @@ va_version = importlib.metadata.version("vision-agent")
|
|
436
441
|
print(f"Vision Agent version: {va_version}")"""
|
437
442
|
)
|
438
443
|
sys_versions = "\n".join(result.logs.stdout)
|
439
|
-
_LOGGER.info(
|
444
|
+
_LOGGER.info(
|
445
|
+
f"E2BCodeInterpreter (sandbox id: {self.interpreter.sandbox_id}) initialized:\n{sys_versions}"
|
446
|
+
)
|
440
447
|
|
441
448
|
def close(self, *args: Any, **kwargs: Any) -> None:
|
442
449
|
try:
|
443
|
-
self.interpreter.notebook.close()
|
444
450
|
self.interpreter.kill(request_timeout=2)
|
445
451
|
_LOGGER.info(
|
446
452
|
f"The sandbox {self.interpreter.sandbox_id} is closed successfully."
|
@@ -451,28 +457,67 @@ print(f"Vision Agent version: {va_version}")"""
|
|
451
457
|
)
|
452
458
|
|
453
459
|
def restart_kernel(self) -> None:
|
454
|
-
self._check_sandbox_liveness()
|
455
460
|
self.interpreter.notebook.restart_kernel()
|
456
461
|
|
457
462
|
@tenacity.retry(
|
458
463
|
wait=tenacity.wait_exponential_jitter(),
|
459
|
-
stop=tenacity.stop_after_attempt(
|
460
|
-
|
461
|
-
|
464
|
+
stop=tenacity.stop_after_attempt(3),
|
465
|
+
retry=tenacity.retry_if_exception_type(
|
466
|
+
(
|
467
|
+
LocalProtocolError,
|
468
|
+
HttpxRemoteProtocolError,
|
469
|
+
HttpcoreRemoteProtocolError,
|
470
|
+
ConnectError,
|
471
|
+
SandboxException,
|
472
|
+
)
|
473
|
+
),
|
474
|
+
before_sleep=tenacity.before_sleep_log(_LOGGER, logging.INFO),
|
475
|
+
after=tenacity.after_log(_LOGGER, logging.INFO),
|
462
476
|
)
|
463
477
|
def exec_cell(self, code: str) -> Execution:
|
464
|
-
self._check_sandbox_liveness()
|
465
478
|
self.interpreter.set_timeout(_SESSION_TIMEOUT) # Extend the life of the sandbox
|
466
479
|
try:
|
467
|
-
|
480
|
+
_LOGGER.info(
|
481
|
+
f"Start code execution in remote sandbox {self.interpreter.sandbox_id}. Timeout: {_SESSION_TIMEOUT}. Code hash: {hash(code)}"
|
482
|
+
)
|
483
|
+
execution = self.interpreter.notebook.exec_cell(
|
484
|
+
code=code,
|
485
|
+
on_stdout=lambda msg: _LOGGER.info(msg),
|
486
|
+
on_stderr=lambda msg: _LOGGER.info(msg),
|
487
|
+
)
|
488
|
+
_LOGGER.info(
|
489
|
+
f"Finished code execution in remote sandbox {self.interpreter.sandbox_id}. Code hash: {hash(code)}"
|
490
|
+
)
|
468
491
|
return Execution.from_e2b_execution(execution)
|
492
|
+
except (
|
493
|
+
LocalProtocolError,
|
494
|
+
HttpxRemoteProtocolError,
|
495
|
+
HttpcoreRemoteProtocolError,
|
496
|
+
ConnectError,
|
497
|
+
SandboxException,
|
498
|
+
) as e:
|
499
|
+
raise e
|
469
500
|
except Exception as e:
|
470
501
|
raise RemoteSandboxExecutionError(
|
471
|
-
f"Failed executing code in remote sandbox due to {e}: {code}"
|
502
|
+
f"Failed executing code in remote sandbox ({self.interpreter.sandbox_id}) due to error '{type(e).__name__} {str(e)}', code: {code}"
|
472
503
|
) from e
|
473
504
|
|
505
|
+
@tenacity.retry(
|
506
|
+
wait=tenacity.wait_exponential_jitter(),
|
507
|
+
stop=tenacity.stop_after_attempt(3),
|
508
|
+
retry=tenacity.retry_if_exception_type(
|
509
|
+
(
|
510
|
+
LocalProtocolError,
|
511
|
+
HttpxRemoteProtocolError,
|
512
|
+
HttpcoreRemoteProtocolError,
|
513
|
+
ConnectError,
|
514
|
+
SandboxException,
|
515
|
+
)
|
516
|
+
),
|
517
|
+
before_sleep=tenacity.before_sleep_log(_LOGGER, logging.INFO),
|
518
|
+
after=tenacity.after_log(_LOGGER, logging.INFO),
|
519
|
+
)
|
474
520
|
def upload_file(self, file: Union[str, Path]) -> str:
|
475
|
-
self._check_sandbox_liveness()
|
476
521
|
file_name = Path(file).name
|
477
522
|
remote_path = f"/home/user/{file_name}"
|
478
523
|
with open(file, "rb") as f:
|
@@ -481,28 +526,18 @@ print(f"Vision Agent version: {va_version}")"""
|
|
481
526
|
return remote_path
|
482
527
|
|
483
528
|
def download_file(self, file_path: str) -> Path:
|
484
|
-
self._check_sandbox_liveness()
|
485
529
|
with tempfile.NamedTemporaryFile(mode="w+b", delete=False) as file:
|
486
530
|
file.write(self.interpreter.files.read(path=file_path, format="bytes"))
|
487
531
|
_LOGGER.info(f"File ({file_path}) is downloaded to: {file.name}")
|
488
532
|
return Path(file.name)
|
489
533
|
|
490
|
-
def _check_sandbox_liveness(self) -> None:
|
491
|
-
try:
|
492
|
-
alive = self.interpreter.is_running(request_timeout=2)
|
493
|
-
except Exception as e:
|
494
|
-
_LOGGER.error(
|
495
|
-
f"Failed to check the health of the remote sandbox ({self.interpreter.sandbox_id}) due to {e}. Consider the sandbox as dead."
|
496
|
-
)
|
497
|
-
alive = False
|
498
|
-
if not alive:
|
499
|
-
raise RemoteSandboxClosedError(
|
500
|
-
"Remote sandbox is closed unexpectedly. Please start a new VisionAgent instance."
|
501
|
-
)
|
502
|
-
|
503
534
|
@staticmethod
|
504
535
|
def _new_e2b_interpreter_impl(*args, **kwargs) -> E2BCodeInterpreterImpl: # type: ignore
|
505
|
-
|
536
|
+
template_name = os.environ.get("E2B_TEMPLATE_NAME", "nx3fagq7sgdliww9cvm3")
|
537
|
+
_LOGGER.info(
|
538
|
+
f"Creating a new E2BCodeInterpreter using template: {template_name}"
|
539
|
+
)
|
540
|
+
return E2BCodeInterpreterImpl(template=template_name, *args, **kwargs)
|
506
541
|
|
507
542
|
|
508
543
|
class LocalCodeInterpreter(CodeInterpreter):
|
vision_agent/utils/sim.py
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: vision-agent
|
3
|
-
Version: 0.2.
|
3
|
+
Version: 0.2.95
|
4
4
|
Summary: Toolset for Vision Agent
|
5
5
|
Author: Landing AI
|
6
6
|
Author-email: dev@landing.ai
|
@@ -11,7 +11,7 @@ Classifier: Programming Language :: Python :: 3.10
|
|
11
11
|
Classifier: Programming Language :: Python :: 3.11
|
12
12
|
Requires-Dist: anthropic (>=0.31.0,<0.32.0)
|
13
13
|
Requires-Dist: e2b (>=0.17.1,<0.18.0)
|
14
|
-
Requires-Dist: e2b-code-interpreter (==0.0.
|
14
|
+
Requires-Dist: e2b-code-interpreter (==0.0.11a17)
|
15
15
|
Requires-Dist: ipykernel (>=6.29.4,<7.0.0)
|
16
16
|
Requires-Dist: langsmith (>=0.1.58,<0.2.0)
|
17
17
|
Requires-Dist: moviepy (>=1.0.0,<2.0.0)
|
@@ -23,6 +23,7 @@ Requires-Dist: opencv-python (>=4.0.0,<5.0.0)
|
|
23
23
|
Requires-Dist: pandas (>=2.0.0,<3.0.0)
|
24
24
|
Requires-Dist: pillow (>=10.0.0,<11.0.0)
|
25
25
|
Requires-Dist: pillow-heif (>=0.16.0,<0.17.0)
|
26
|
+
Requires-Dist: pydantic (==2.7.4)
|
26
27
|
Requires-Dist: pydantic-settings (>=2.2.1,<3.0.0)
|
27
28
|
Requires-Dist: pytube (==15.0.0)
|
28
29
|
Requires-Dist: requests (>=2.0.0,<3.0.0)
|
@@ -3,7 +3,7 @@ vision_agent/agent/__init__.py,sha256=qpduQ9YufJQfMmG6jwKC2xmlbtR2qK8_1eQC1sGA9K
|
|
3
3
|
vision_agent/agent/agent.py,sha256=Bt8yhjCFXuRdZaHxKEesG40V09nWRt45sZluri1R3AA,575
|
4
4
|
vision_agent/agent/agent_utils.py,sha256=JXdl2xz14LKQAmScY-MIW23AD2WBFCsnI0JS6dAyj3Q,1412
|
5
5
|
vision_agent/agent/vision_agent.py,sha256=i_rNpc7faqHTifp2c9sQE4Js3qYUKuJeiqauTp90OlE,8417
|
6
|
-
vision_agent/agent/vision_agent_coder.py,sha256=
|
6
|
+
vision_agent/agent/vision_agent_coder.py,sha256=M8J5xE9uX8Nig1WmVmwLMeCSe0E6Bg3Mo5fPPcW_a-c,30246
|
7
7
|
vision_agent/agent/vision_agent_coder_prompts.py,sha256=a3R_vHlT2FW3-DSn4OWgzF9zEAx-uKM4ZaTi9Kn-K54,11116
|
8
8
|
vision_agent/agent/vision_agent_prompts.py,sha256=hjs-m4ZHR7HE1HtOeX_1rOvTQA2FMEAqEkaBbGPBYDo,6072
|
9
9
|
vision_agent/fonts/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
@@ -15,15 +15,15 @@ vision_agent/tools/__init__.py,sha256=UNiaJAOt1C709gaJ-a9h9BzKnY5JmoEUpgKftsOnyP
|
|
15
15
|
vision_agent/tools/meta_tools.py,sha256=rmxgVzj-vJKeewHbue3qHru4sYsFLxlSZV-YH-eyH5w,13366
|
16
16
|
vision_agent/tools/prompts.py,sha256=V1z4YJLXZuUl_iZ5rY0M5hHc_2tmMEUKr0WocXKGt4E,1430
|
17
17
|
vision_agent/tools/tool_utils.py,sha256=XoB-iae8hHrBQgJd3fV6-UjZAkClysobUaOM17IcHuE,4597
|
18
|
-
vision_agent/tools/tools.py,sha256=
|
18
|
+
vision_agent/tools/tools.py,sha256=CWQY1sD-xtWchPrg_AJNAGH-k7UxrKIkiog8r0sx1Do,42446
|
19
19
|
vision_agent/utils/__init__.py,sha256=CW84HnhqI6XQVuxf2KifkLnSuO7EOhmuL09-gAymAak,219
|
20
20
|
vision_agent/utils/exceptions.py,sha256=isVH-SVL4vHj3q5kK4z7cy5_aOapAqHXWkpibfSNbUs,1659
|
21
|
-
vision_agent/utils/execute.py,sha256=
|
21
|
+
vision_agent/utils/execute.py,sha256=s43aUtuq7ZNjil2mxrddiz8EvvqlJwttkYlIiZouXqM,25125
|
22
22
|
vision_agent/utils/image_utils.py,sha256=y69wtNla0xHZ1h1x0-vv7nOyKUq69jtjSJBiDCn6EM0,7703
|
23
|
-
vision_agent/utils/sim.py,sha256=
|
23
|
+
vision_agent/utils/sim.py,sha256=7JvtWGN0Ik5ife3qQYWs7Fm3T8AnAXGFd5HnvDC15mQ,4433
|
24
24
|
vision_agent/utils/type_defs.py,sha256=oVFJcicB-s_09lqvn61u0A5ncZsTqZArZledXWbrrg0,1384
|
25
25
|
vision_agent/utils/video.py,sha256=rNmU9KEIkZB5-EztZNlUiKYN0mm_55A_2VGUM0QpqLA,8779
|
26
|
-
vision_agent-0.2.
|
27
|
-
vision_agent-0.2.
|
28
|
-
vision_agent-0.2.
|
29
|
-
vision_agent-0.2.
|
26
|
+
vision_agent-0.2.95.dist-info/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
|
27
|
+
vision_agent-0.2.95.dist-info/METADATA,sha256=-OCOFe_UAKyI5sjDr6nYklJq5jwKZbLjwFkFMO-wrV8,10728
|
28
|
+
vision_agent-0.2.95.dist-info/WHEEL,sha256=7Z8_27uaHI_UZAc4Uox4PpBhQ9Y5_modZXWMxtUi4NU,88
|
29
|
+
vision_agent-0.2.95.dist-info/RECORD,,
|
File without changes
|
File without changes
|