From a8e4b6213b3665b3e2967894908aeb2aa7270b51 Mon Sep 17 00:00:00 2001 From: Shankar <90070882+shankar-vision-eng@users.noreply.github.com> Date: Thu, 29 Aug 2024 00:48:47 -0700 Subject: [PATCH 1/7] Minor tool fixes for Florence OD and Overlay Seg masks for tracking (#212) * fixed florence OD as phrase grounding * added code to plot tracking labels which are dynamic * adding the multplan log only when user invokes multi plan mode * fix code complexity linting error * fix mypy issues --- tests/integ/test_tools.py | 4 +- vision_agent/agent/vision_agent_coder.py | 60 +++++++++++++++--------- vision_agent/tools/__init__.py | 2 +- vision_agent/tools/tools.py | 28 ++++++----- 4 files changed, 59 insertions(+), 35 deletions(-) diff --git a/tests/integ/test_tools.py b/tests/integ/test_tools.py index afa9dcb4..bca1f6ea 100644 --- a/tests/integ/test_tools.py +++ b/tests/integ/test_tools.py @@ -10,7 +10,7 @@ detr_segmentation, dpt_hybrid_midas, florence2_image_caption, - florence2_object_detection, + florence2_phrase_grounding, florence2_ocr, florence2_roberta_vqa, florence2_sam2_image, @@ -65,7 +65,7 @@ def test_owl(): def test_object_detection(): img = ski.data.coins() - result = florence2_object_detection( + result = florence2_phrase_grounding( image=img, prompt="coin", ) diff --git a/vision_agent/agent/vision_agent_coder.py b/vision_agent/agent/vision_agent_coder.py index b10988c6..3b3b5f68 100644 --- a/vision_agent/agent/vision_agent_coder.py +++ b/vision_agent/agent/vision_agent_coder.py @@ -744,29 +744,14 @@ def chat_with_workflow( results = {"code": "", "test": "", "plan": []} plan = [] success = False - self.log_progress( - { - "type": "log", - "log_content": "Creating plans", - "status": "started", - } - ) - plans = write_plans( - int_chat, - T.get_tool_descriptions_by_names( - customized_tool_names, T.FUNCTION_TOOLS, T.UTIL_TOOLS # type: ignore - ), - format_memory(working_memory), - self.planner, + + plans = self._create_plans( + int_chat, customized_tool_names, working_memory, self.planner ) - if self.verbosity >= 1: - for p in plans: - # tabulate will fail if the keys are not the same for all elements - p_fixed = [{"instructions": e} for e in plans[p]["instructions"]] - _LOGGER.info( - f"\n{tabulate(tabular_data=p_fixed, headers='keys', tablefmt='mixed_grid', maxcolwidths=_MAX_TABULATE_COL_WIDTH)}" - ) + if test_multi_plan: + self._log_plans(plans, self.verbosity) + tool_infos = retrieve_tools( plans, self.tool_recommender, @@ -860,6 +845,39 @@ def log_progress(self, data: Dict[str, Any]) -> None: if self.report_progress_callback is not None: self.report_progress_callback(data) + def _create_plans( + self, + int_chat: List[Message], + customized_tool_names: Optional[List[str]], + working_memory: List[Dict[str, str]], + planner: LMM, + ) -> Dict[str, Any]: + self.log_progress( + { + "type": "log", + "log_content": "Creating plans", + "status": "started", + } + ) + plans = write_plans( + int_chat, + T.get_tool_descriptions_by_names( + customized_tool_names, T.FUNCTION_TOOLS, T.UTIL_TOOLS # type: ignore + ), + format_memory(working_memory), + planner, + ) + return plans + + def _log_plans(self, plans: Dict[str, Any], verbosity: int) -> None: + if verbosity >= 1: + for p in plans: + # tabulate will fail if the keys are not the same for all elements + p_fixed = [{"instructions": e} for e in plans[p]["instructions"]] + _LOGGER.info( + f"\n{tabulate(tabular_data=p_fixed, headers='keys', tablefmt='mixed_grid', maxcolwidths=_MAX_TABULATE_COL_WIDTH)}" + ) + class OllamaVisionAgentCoder(VisionAgentCoder): """VisionAgentCoder that uses Ollama models for planning, coding, testing. diff --git a/vision_agent/tools/__init__.py b/vision_agent/tools/__init__.py index a90b7181..3372fcbb 100644 --- a/vision_agent/tools/__init__.py +++ b/vision_agent/tools/__init__.py @@ -21,7 +21,7 @@ dpt_hybrid_midas, extract_frames, florence2_image_caption, - florence2_object_detection, + florence2_phrase_grounding, florence2_ocr, florence2_roberta_vqa, florence2_sam2_image, diff --git a/vision_agent/tools/tools.py b/vision_agent/tools/tools.py index 594fcf6d..4e1a0f40 100644 --- a/vision_agent/tools/tools.py +++ b/vision_agent/tools/tools.py @@ -760,10 +760,10 @@ def florence2_image_caption(image: np.ndarray, detail_caption: bool = True) -> s return answer[task] # type: ignore -def florence2_object_detection(prompt: str, image: np.ndarray) -> List[Dict[str, Any]]: - """'florencev2_object_detection' is a tool that can detect and count multiple - objects given a text prompt such as category names or referring expressions. You - can optionally separate the categories in the text with commas. It returns a list +def florence2_phrase_grounding(prompt: str, image: np.ndarray) -> List[Dict[str, Any]]: + """'florence2_phrase_grounding' is a tool that can detect multiple + objects given a text prompt which can be object names or caption. You + can optionally separate the object names in the text with commas. It returns a list of bounding boxes with normalized coordinates, label names and associated probability scores of 1.0. @@ -780,7 +780,7 @@ def florence2_object_detection(prompt: str, image: np.ndarray) -> List[Dict[str, Example ------- - >>> florence2_object_detection('person looking at a coyote', image) + >>> florence2_phrase_grounding('person looking at a coyote', image) [ {'score': 1.0, 'label': 'person', 'bbox': [0.1, 0.11, 0.35, 0.4]}, {'score': 1.0, 'label': 'coyote', 'bbox': [0.34, 0.21, 0.85, 0.5}, @@ -792,7 +792,7 @@ def florence2_object_detection(prompt: str, image: np.ndarray) -> List[Dict[str, "image": image_b64, "task": "", "prompt": prompt, - "function_name": "florence2_object_detection", + "function_name": "florence2_phrase_grounding", } detections = send_inference_request(data, "florence2", v2=True) @@ -1418,6 +1418,7 @@ def overlay_segmentation_masks( medias: Union[np.ndarray, List[np.ndarray]], masks: Union[List[Dict[str, Any]], List[List[Dict[str, Any]]]], draw_label: bool = True, + secondary_label_key: str = "tracking_label", ) -> Union[np.ndarray, List[np.ndarray]]: """'overlay_segmentation_masks' is a utility function that displays segmentation masks. @@ -1426,7 +1427,10 @@ def overlay_segmentation_masks( medias (Union[np.ndarray, List[np.ndarray]]): The image or frames to display the masks on. masks (Union[List[Dict[str, Any]], List[List[Dict[str, Any]]]]): A list of - dictionaries containing the masks. + dictionaries containing the masks, labels and scores. + draw_label (bool, optional): If True, the labels will be displayed on the image. + secondary_label_key (str, optional): The key to use for the secondary + tracking label which is needed in videos to display tracking information. Returns: np.ndarray: The image with the masks displayed. @@ -1471,6 +1475,7 @@ def overlay_segmentation_masks( for elt in masks_int[i]: mask = elt["mask"] label = elt["label"] + tracking_lbl = elt.get(secondary_label_key, None) np_mask = np.zeros((pil_image.size[1], pil_image.size[0], 4)) np_mask[mask > 0, :] = color[label] + (255 * 0.5,) mask_img = Image.fromarray(np_mask.astype(np.uint8)) @@ -1478,16 +1483,17 @@ def overlay_segmentation_masks( if draw_label: draw = ImageDraw.Draw(pil_image) - text_box = draw.textbbox((0, 0), text=label, font=font) + text = tracking_lbl if tracking_lbl else label + text_box = draw.textbbox((0, 0), text=text, font=font) x, y = _get_text_coords_from_mask( mask, v_gap=(text_box[3] - text_box[1]) + 10, h_gap=(text_box[2] - text_box[0]) // 2, ) if x != 0 and y != 0: - text_box = draw.textbbox((x, y), text=label, font=font) + text_box = draw.textbbox((x, y), text=text, font=font) draw.rectangle((x, y, text_box[2], text_box[3]), fill=color[label]) - draw.text((x, y), label, fill="black", font=font) + draw.text((x, y), text, fill="black", font=font) frame_out.append(np.array(pil_image)) return frame_out[0] if len(frame_out) == 1 else frame_out @@ -1663,7 +1669,7 @@ def florencev2_fine_tuned_object_detection( florence2_ocr, florence2_sam2_image, florence2_sam2_video, - florence2_object_detection, + florence2_phrase_grounding, ixc25_image_vqa, ixc25_video_vqa, detr_segmentation, From 5df3ced38526efcbf4ae61568d58e07e4c3335b3 Mon Sep 17 00:00:00 2001 From: Zhichao Date: Thu, 29 Aug 2024 16:02:41 +0800 Subject: [PATCH 2/7] feat: use media url directly in both LMM and code sandbox (#215) * do not upload to code_interpreter * endcode_media support url ad mp4 * load_image * remove print * add comment for video associated png * minor revert * lint * backwards * lint * also save video * more strict check for vision-agent-ui * fix lint --- vision_agent/agent/vision_agent_coder.py | 7 ++++++- vision_agent/lmm/lmm.py | 15 +++++++++++---- vision_agent/tools/tools.py | 20 ++++++++++++++++++-- 3 files changed, 35 insertions(+), 7 deletions(-) diff --git a/vision_agent/agent/vision_agent_coder.py b/vision_agent/agent/vision_agent_coder.py index 3b3b5f68..7856bdb8 100644 --- a/vision_agent/agent/vision_agent_coder.py +++ b/vision_agent/agent/vision_agent_coder.py @@ -718,7 +718,12 @@ def chat_with_workflow( for chat_i in chat: if "media" in chat_i: for media in chat_i["media"]: - media = code_interpreter.upload_file(media) + media = ( + media + if type(media) is str + and media.startswith(("http", "https")) + else code_interpreter.upload_file(media) + ) chat_i["content"] += f" Media name {media}" # type: ignore media_list.append(media) diff --git a/vision_agent/lmm/lmm.py b/vision_agent/lmm/lmm.py index e78a0593..15df5ac9 100644 --- a/vision_agent/lmm/lmm.py +++ b/vision_agent/lmm/lmm.py @@ -30,6 +30,12 @@ def encode_image_bytes(image: bytes) -> str: def encode_media(media: Union[str, Path]) -> str: + if type(media) is str and media.startswith(("http", "https")): + # for mp4 video url, we assume there is a same url but ends with png + # vision-agent-ui will upload this png when uploading the video + if media.endswith((".mp4", "mov")) and media.find("vision-agent-dev.s3") != -1: + return media[:-4] + ".png" + return media extension = "png" extension = Path(media).suffix if extension.lower() not in { @@ -138,7 +144,11 @@ def chat( { "type": "image_url", "image_url": { - "url": f"data:image/png;base64,{encoded_media}", + "url": ( + encoded_media + if encoded_media.startswith(("http", "https")) + else f"data:image/png;base64,{encoded_media}" + ), "detail": "low", }, }, @@ -390,7 +400,6 @@ def chat( tmp_kwargs = self.kwargs | kwargs data.update(tmp_kwargs) if "stream" in tmp_kwargs and tmp_kwargs["stream"]: - json_data = json.dumps(data) def f() -> Iterator[Optional[str]]: @@ -424,7 +433,6 @@ def generate( media: Optional[List[Union[str, Path]]] = None, **kwargs: Any, ) -> Union[str, Iterator[Optional[str]]]: - url = f"{self.url}/generate" data: Dict[str, Any] = { "model": self.model_name, @@ -439,7 +447,6 @@ def generate( tmp_kwargs = self.kwargs | kwargs data.update(tmp_kwargs) if "stream" in tmp_kwargs and tmp_kwargs["stream"]: - json_data = json.dumps(data) def f() -> Iterator[Optional[str]]: diff --git a/vision_agent/tools/tools.py b/vision_agent/tools/tools.py index 4e1a0f40..62a1908a 100644 --- a/vision_agent/tools/tools.py +++ b/vision_agent/tools/tools.py @@ -1,3 +1,4 @@ +import os import io import json import logging @@ -14,6 +15,7 @@ from PIL import Image, ImageDraw, ImageFont from pillow_heif import register_heif_opener # type: ignore from pytube import YouTube # type: ignore +import urllib.request from vision_agent.clients.landing_public_api import LandingPublicAPI from vision_agent.tools.tool_utils import ( @@ -1220,6 +1222,13 @@ def extract_frames( video_file_path = video.download(output_path=temp_dir) return extract_frames_from_video(video_file_path, fps) + elif str(video_uri).startswith(("http", "https")): + _, image_suffix = os.path.splitext(video_uri) + with tempfile.NamedTemporaryFile(delete=False, suffix=image_suffix) as tmp_file: + # Download the video and save it to the temporary file + with urllib.request.urlopen(str(video_uri)) as response: + tmp_file.write(response.read()) + return extract_frames_from_video(tmp_file.name, fps) return extract_frames_from_video(str(video_uri), fps) @@ -1250,10 +1259,10 @@ def default(self, obj: Any): # type: ignore def load_image(image_path: str) -> np.ndarray: - """'load_image' is a utility function that loads an image from the given file path string. + """'load_image' is a utility function that loads an image from the given file path string or an URL. Parameters: - image_path (str): The path to the image. + image_path (str): The path or URL to the image. Returns: np.ndarray: The image as a NumPy array. @@ -1265,6 +1274,13 @@ def load_image(image_path: str) -> np.ndarray: # NOTE: sometimes the generated code pass in a NumPy array if isinstance(image_path, np.ndarray): return image_path + if image_path.startswith(("http", "https")): + _, image_suffix = os.path.splitext(image_path) + with tempfile.NamedTemporaryFile(delete=False, suffix=image_suffix) as tmp_file: + # Download the image and save it to the temporary file + with urllib.request.urlopen(image_path) as response: + tmp_file.write(response.read()) + image_path = tmp_file.name image = Image.open(image_path).convert("RGB") return np.array(image) From 2842fdcded05225228d31bf4bbf37cd3a0eb7d97 Mon Sep 17 00:00:00 2001 From: GitHub Actions Bot Date: Thu, 29 Aug 2024 08:18:09 +0000 Subject: [PATCH 3/7] [skip ci] chore(release): vision-agent 0.2.118 --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 95db7876..ebb2415d 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "poetry.core.masonry.api" [tool.poetry] name = "vision-agent" -version = "0.2.117" +version = "0.2.118" description = "Toolset for Vision Agent" authors = ["Landing AI "] readme = "README.md" From cacae4421b226b3c30a796c815fb6ae0e996c0cb Mon Sep 17 00:00:00 2001 From: Dillon Laird Date: Fri, 30 Aug 2024 08:52:59 -0700 Subject: [PATCH 4/7] Update conversation (#217) * update for new conv * add artifact tools * update local executor * fix upload/download * cleaned up code for artifacts * starting artifact prompts * app to add files to artifacts * add support for artifacts * add artifact meta tools * ran isort * prompt to work with artifacts * minor fixes for prompts * add docs, fix load and saving remote files * rename prompts * add docs for artifacts, allow None artifacts (which don't load) to be added * e2b and local uplaod/download work similarly now, can pass in target download path * add Artifacts to exports * local chat app to work with artifacts * updated docs * fix flake8 * fix mypy errors * fix format * add execution to conversation * fixed type errors * fixed bug with upload file * added ability to write media files to artifacts * return outside of context * make remote path execute variable * add codec for video encoding * fix prompts to include writing media artifacts * isort * fix typo * added redisplay for nested notebook sessions * return artifacts * add trace for last edited artifact * handle artifact return * only add text to obs, no trace --- README.md | 18 +- docs/index.md | 21 +- examples/chat/app.py | 16 +- vision_agent/agent/agent.py | 2 +- vision_agent/agent/vision_agent.py | 156 ++++-- vision_agent/agent/vision_agent_coder.py | 4 +- vision_agent/agent/vision_agent_prompts.py | 65 ++- vision_agent/clients/landing_public_api.py | 4 +- vision_agent/lmm/lmm.py | 6 +- vision_agent/lmm/types.py | 4 +- vision_agent/tools/__init__.py | 4 +- vision_agent/tools/meta_tools.py | 554 +++++++++++---------- vision_agent/tools/tools.py | 6 +- vision_agent/tools/tools_types.py | 6 +- vision_agent/utils/execute.py | 91 +++- vision_agent/utils/image_utils.py | 4 +- 16 files changed, 560 insertions(+), 401 deletions(-) diff --git a/README.md b/README.md index f41bef31..88c59973 100644 --- a/README.md +++ b/README.md @@ -41,15 +41,15 @@ export OPENAI_API_KEY="your-api-key" ``` ### Vision Agent -There are two agents that you can use. Vision Agent is a conversational agent that has +There are two agents that you can use. `VisionAgent` is a conversational agent that has access to tools that allow it to write an navigate python code and file systems. It can -converse with the user in natural language. VisionAgentCoder is an agent that can write -code for vision tasks, such as counting people in an image. However, it cannot converse -and can only respond with code. VisionAgent can call VisionAgentCoder to write vision -code. +converse with the user in natural language. `VisionAgentCoder` is an agent specifically +for writing code for vision tasks, such as counting people in an image. However, it +cannot chat with you and can only respond with code. `VisionAgent` can call +`VisionAgentCoder` to write vision code. #### Basic Usage -To run the streamlit app locally to chat with Vision Agent, you can run the following +To run the streamlit app locally to chat with `VisionAgent`, you can run the following command: ```bash @@ -146,7 +146,7 @@ the code and having it update. You just need to add the code as a response from assistant: ```python -agent = va.agent.VisionAgent(verbosity=2) +agent = va.agent.VisionAgentCoder(verbosity=2) conv = [ { "role": "user", @@ -212,6 +212,10 @@ function. Make sure the documentation is in the same format above with descripti `Parameters:`, `Returns:`, and `Example\n-------`. You can find an example use case [here](examples/custom_tools/) as this is what the agent uses to pick and use the tool. +Can't find the tool you need and want add it to `VisionAgent`? Check out our +[vision-agent-tools](https://github.com/landing-ai/vision-agent-tools) repository where +we add the source code for all the tools used in `VisionAgent`. + ## Additional Backends ### Ollama We also provide a `VisionAgentCoder` that uses Ollama. To get started you must download diff --git a/docs/index.md b/docs/index.md index 8569c5cc..0f5022f9 100644 --- a/docs/index.md +++ b/docs/index.md @@ -38,15 +38,15 @@ export OPENAI_API_KEY="your-api-key" ``` ### Vision Agent -There are two agents that you can use. Vision Agent is a conversational agent that has +There are two agents that you can use. `VisionAgent` is a conversational agent that has access to tools that allow it to write an navigate python code and file systems. It can -converse with the user in natural language. VisionAgentCoder is an agent that can write -code for vision tasks, such as counting people in an image. However, it cannot converse -and can only respond with code. VisionAgent can call VisionAgentCoder to write vision -code. +converse with the user in natural language. `VisionAgentCoder` is an agent specifically +for writing code for vision tasks, such as counting people in an image. However, it +cannot chat with you and can only respond with code. `VisionAgent` can call +`VisionAgentCoder` to write vision code. #### Basic Usage -To run the streamlit app locally to chat with Vision Agent, you can run the following +To run the streamlit app locally to chat with `VisionAgent`, you can run the following command: ```bash @@ -143,7 +143,7 @@ the code and having it update. You just need to add the code as a response from assistant: ```python -agent = va.agent.VisionAgent(verbosity=2) +agent = va.agent.VisionAgentCoder(verbosity=2) conv = [ { "role": "user", @@ -209,6 +209,10 @@ function. Make sure the documentation is in the same format above with descripti `Parameters:`, `Returns:`, and `Example\n-------`. You can find an example use case [here](examples/custom_tools/) as this is what the agent uses to pick and use the tool. +Can't find the tool you need and want add it to `VisionAgent`? Check out our +[vision-agent-tools](https://github.com/landing-ai/vision-agent-tools) repository where +we add the source code for all the tools used in `VisionAgent`. + ## Additional Backends ### Ollama We also provide a `VisionAgentCoder` that uses Ollama. To get started you must download @@ -230,6 +234,7 @@ tools. You can use it just like you would use `VisionAgentCoder`: >>> agent = va.agent.OllamaVisionAgentCoder() >>> agent("Count the apples in the image", media="apples.jpg") ``` +> WARNING: VisionAgent doesn't work well unless the underlying LMM is sufficiently powerful. Do not expect good results or even working code with smaller models like Llama 3.1 8B. ### Azure OpenAI We also provide a `AzureVisionAgentCoder` that uses Azure OpenAI models. To get started @@ -241,7 +246,7 @@ follow the Azure Setup section below. You can use it just like you would use= >>> agent = va.agent.AzureVisionAgentCoder() >>> agent("Count the apples in the image", media="apples.jpg") ``` -> WARNING: VisionAgent doesn't work well unless the underlying LMM is sufficiently powerful. Do not expect good results or even working code with smaller models like Llama 3.1 8B. + ### Azure Setup If you want to use Azure OpenAI models, you need to have two OpenAI model deployments: diff --git a/examples/chat/app.py b/examples/chat/app.py index f1cd62e7..9291f65a 100644 --- a/examples/chat/app.py +++ b/examples/chat/app.py @@ -26,7 +26,14 @@ "response": "saved", "style": {"bottom": "calc(50% - 4.25rem", "right": "0.4rem"}, } -agent = va.agent.VisionAgent(verbosity=1) +# set artifacts remote_path to WORKSPACE +artifacts = va.tools.Artifacts(WORKSPACE / "artifacts.pkl") +if Path("artifacts.pkl").exists(): + artifacts.load("artifacts.pkl") +else: + artifacts.save("artifacts.pkl") + +agent = va.agent.VisionAgent(verbosity=1, local_artifacts_path="artifacts.pkl") st.set_page_config(layout="wide") @@ -44,7 +51,9 @@ def update_messages(messages, lock): - new_chat = agent.chat_with_code(messages) + if Path("artifacts.pkl").exists(): + artifacts.load("artifacts.pkl") + new_chat, _ = agent.chat_with_code(messages, artifacts=artifacts) with lock: for new_message in new_chat: if new_message not in messages: @@ -122,6 +131,9 @@ def main(): with open(WORKSPACE / uploaded_file.name, "wb") as f: f.write(uploaded_file.getbuffer()) + # make it None so it wont load and overwrite the image + artifacts.artifacts[uploaded_file.name] = None + for file in WORKSPACE.iterdir(): if "__pycache__" not in str(file) and not str(file).startswith("."): if st.button(file.name): diff --git a/vision_agent/agent/agent.py b/vision_agent/agent/agent.py index 6b11f297..ca2cf181 100644 --- a/vision_agent/agent/agent.py +++ b/vision_agent/agent/agent.py @@ -11,7 +11,7 @@ def __call__( self, input: Union[str, List[Message]], media: Optional[Union[str, Path]] = None, - ) -> str: + ) -> Union[str, List[Message]]: pass @abstractmethod diff --git a/vision_agent/agent/vision_agent.py b/vision_agent/agent/vision_agent.py index cfb482e1..2bb04343 100644 --- a/vision_agent/agent/vision_agent.py +++ b/vision_agent/agent/vision_agent.py @@ -1,8 +1,9 @@ import copy import logging import os +import tempfile from pathlib import Path -from typing import Any, Dict, List, Optional, Union, cast +from typing import Any, Dict, List, Optional, Tuple, Union, cast from vision_agent.agent import Agent from vision_agent.agent.agent_utils import extract_json @@ -13,8 +14,9 @@ ) from vision_agent.lmm import LMM, Message, OpenAILMM from vision_agent.tools import META_TOOL_DOCSTRING +from vision_agent.tools.meta_tools import Artifacts from vision_agent.utils import CodeInterpreterFactory -from vision_agent.utils.execute import CodeInterpreter +from vision_agent.utils.execute import CodeInterpreter, Execution logging.basicConfig(level=logging.INFO) _LOGGER = logging.getLogger(__name__) @@ -24,23 +26,30 @@ os.environ["PYTHONPATH"] = f"{WORKSPACE}:{os.getenv('PYTHONPATH', '')}" -class DefaultImports: - code = [ +class BoilerplateCode: + pre_code = [ "from typing import *", "from vision_agent.utils.execute import CodeInterpreter", - "from vision_agent.tools.meta_tools import generate_vision_code, edit_vision_code, open_file, create_file, scroll_up, scroll_down, edit_file, get_tool_descriptions", + "from vision_agent.tools.meta_tools import Artifacts, open_code_artifact, create_code_artifact, edit_code_artifact, get_tool_descriptions, generate_vision_code, edit_vision_code, write_media_artifact", + "artifacts = Artifacts('{remote_path}')", + "artifacts.load('{remote_path}')", + ] + post_code = [ + "artifacts.save()", ] @staticmethod - def to_code_string() -> str: - return "\n".join(DefaultImports.code) - - @staticmethod - def prepend_imports(code: str) -> str: + def add_boilerplate(code: str, **format: Any) -> str: """Run this method to prepend the default imports to the code. NOTE: be sure to run this method after the custom tools have been registered. """ - return DefaultImports.to_code_string() + "\n\n" + code + return ( + "\n".join([s.format(**format) for s in BoilerplateCode.pre_code]) + + "\n\n" + + code + + "\n\n" + + "\n".join([s.format(**format) for s in BoilerplateCode.post_code]) + ) def run_conversation(orch: LMM, chat: List[Message]) -> Dict[str, Any]: @@ -60,35 +69,17 @@ def run_conversation(orch: LMM, chat: List[Message]) -> Dict[str, Any]: prompt = VA_CODE.format( documentation=META_TOOL_DOCSTRING, examples=f"{EXAMPLES_CODE1}\n{EXAMPLES_CODE2}", - dir=WORKSPACE, conversation=conversation, ) return extract_json(orch([{"role": "user", "content": prompt}], stream=False)) # type: ignore -def run_code_action(code: str, code_interpreter: CodeInterpreter) -> str: - # Note the code interpreter needs to keep running in the same environment because - # the SWE tools hold state like line numbers and currently open files. - result = code_interpreter.exec_cell(DefaultImports.prepend_imports(code)) - - return_str = "" - if result.success: - for res in result.results: - if res.text is not None: - return_str += res.text.replace("\\n", "\n") - if result.logs.stdout: - return_str += "----- stdout -----\n" - for log in result.logs.stdout: - return_str += log.replace("\\n", "\n") - else: - # for log in result.logs.stderr: - # return_str += log.replace("\\n", "\n") - if result.error: - return_str += ( - "\n" + result.error.value + "\n".join(result.error.traceback_raw) - ) - - return return_str +def run_code_action( + code: str, code_interpreter: CodeInterpreter, artifact_remote_path: str +) -> Execution: + return code_interpreter.exec_isolation( + BoilerplateCode.add_boilerplate(code, remote_path=artifact_remote_path) + ) def parse_execution(response: str) -> Optional[str]: @@ -101,8 +92,8 @@ def parse_execution(response: str) -> Optional[str]: class VisionAgent(Agent): """Vision Agent is an agent that can chat with the user and call tools or other - agents to generate code for it. Vision Agent uses python code to execute actions for - the user. Vision Agent is inspired by by OpenDev + agents to generate code for it. Vision Agent uses python code to execute actions + for the user. Vision Agent is inspired by by OpenDev https://github.com/OpenDevin/OpenDevin and CodeAct https://arxiv.org/abs/2402.01030 Example @@ -118,8 +109,20 @@ def __init__( self, agent: Optional[LMM] = None, verbosity: int = 0, + local_artifacts_path: Optional[Union[str, Path]] = None, code_sandbox_runtime: Optional[str] = None, ) -> None: + """Initialize the VisionAgent. + + Parameters: + agent (Optional[LMM]): The agent to use for conversation and orchestration + of other agents. + verbosity (int): The verbosity level of the agent. + local_artifacts_path (Optional[Union[str, Path]]): The path to the local + artifacts file. + code_sandbox_runtime (Optional[str]): The code sandbox runtime to use. + """ + self.agent = ( OpenAILMM(temperature=0.0, json_mode=True) if agent is None else agent ) @@ -128,12 +131,21 @@ def __init__( self.code_sandbox_runtime = code_sandbox_runtime if self.verbosity >= 1: _LOGGER.setLevel(logging.INFO) + self.local_artifacts_path = cast( + str, + ( + Path(local_artifacts_path) + if local_artifacts_path is not None + else Path(tempfile.NamedTemporaryFile(delete=False).name) + ), + ) def __call__( self, input: Union[str, List[Message]], media: Optional[Union[str, Path]] = None, - ) -> str: + artifacts: Optional[Artifacts] = None, + ) -> List[Message]: """Chat with VisionAgent and get the conversation response. Parameters: @@ -141,6 +153,7 @@ def __call__( [{"role": "user", "content": "describe your task here..."}, ...] or a string of just the contents. media (Optional[Union[str, Path]]): The media file to be used in the task. + artifacts (Optional[Artifacts]): The artifacts to use in the task. Returns: str: The conversation response. @@ -149,22 +162,23 @@ def __call__( input = [{"role": "user", "content": input}] if media is not None: input[0]["media"] = [media] - results = self.chat_with_code(input) - return results # type: ignore + results, _ = self.chat_with_code(input, artifacts) + return results def chat_with_code( self, chat: List[Message], - ) -> List[Message]: + artifacts: Optional[Artifacts] = None, + ) -> Tuple[List[Message], Artifacts]: """Chat with VisionAgent, it will use code to execute actions to accomplish its tasks. Parameters: - chat (List[Message]): A conversation - in the format of: + chat (List[Message]): A conversation in the format of: [{"role": "user", "content": "describe your task here..."}] or if it contains media files, it should be in the format of: [{"role": "user", "content": "describe your task here...", "media": ["image1.jpg", "image2.jpg"]}] + artifacts (Optional[Artifacts]): The artifacts to use in the task. Returns: List[Message]: The conversation response. @@ -173,6 +187,10 @@ def chat_with_code( if not chat: raise ValueError("chat cannot be empty") + if not artifacts: + # this is setting remote artifacts path + artifacts = Artifacts(WORKSPACE / "artifacts.pkl") + with CodeInterpreterFactory.new_instance( code_sandbox_runtime=self.code_sandbox_runtime ) as code_interpreter: @@ -182,9 +200,14 @@ def chat_with_code( for chat_i in int_chat: if "media" in chat_i: for media in chat_i["media"]: - media = code_interpreter.upload_file(media) - chat_i["content"] += f" Media name {media}" # type: ignore - media_list.append(media) + media = cast(str, media) + artifacts.artifacts[Path(media).name] = open(media, "rb").read() + + media_remote_path = ( + Path(code_interpreter.remote_path) / Path(media).name + ) + chat_i["content"] += f" Media name {media_remote_path}" # type: ignore + media_list.append(media_remote_path) int_chat = cast( List[Message], @@ -204,6 +227,22 @@ def chat_with_code( finished = False iterations = 0 + last_response = None + + # Save the current state of artifacts, will include any images the user + # passed in. + artifacts.save(self.local_artifacts_path) + + # Upload artifacts to remote location and show where they are going + # to be loaded to. The actual loading happens in BoilerplateCode as + # part of the pre_code. + remote_artifacts_path = code_interpreter.upload_file( + self.local_artifacts_path + ) + artifacts_loaded = artifacts.show() + int_chat.append({"role": "observation", "content": artifacts_loaded}) + orig_chat.append({"role": "observation", "content": artifacts_loaded}) + while not finished and iterations < self.max_iterations: response = run_conversation(self.agent, int_chat) if self.verbosity >= 1: @@ -211,20 +250,39 @@ def chat_with_code( int_chat.append({"role": "assistant", "content": str(response)}) orig_chat.append({"role": "assistant", "content": str(response)}) + # sometimes it gets stuck in a loop, so we force it to exit + if last_response == response: + response["let_user_respond"] = True + if response["let_user_respond"]: break code_action = parse_execution(response["response"]) if code_action is not None: - obs = run_code_action(code_action, code_interpreter) + result = run_code_action( + code_action, code_interpreter, str(remote_artifacts_path) + ) + obs = str(result.logs) + if self.verbosity >= 1: _LOGGER.info(obs) + # don't add execution results to internal chat int_chat.append({"role": "observation", "content": obs}) - orig_chat.append({"role": "observation", "content": obs}) + orig_chat.append( + {"role": "observation", "content": obs, "execution": result} + ) iterations += 1 - return orig_chat + last_response = response + + # after running the agent, download the artifacts locally + code_interpreter.download_file( + str(remote_artifacts_path.name), str(self.local_artifacts_path) + ) + artifacts.load(self.local_artifacts_path) + artifacts.save() + return orig_chat, artifacts def log_progress(self, data: Dict[str, Any]) -> None: pass diff --git a/vision_agent/agent/vision_agent_coder.py b/vision_agent/agent/vision_agent_coder.py index 7856bdb8..c8488902 100644 --- a/vision_agent/agent/vision_agent_coder.py +++ b/vision_agent/agent/vision_agent_coder.py @@ -722,10 +722,10 @@ def chat_with_workflow( media if type(media) is str and media.startswith(("http", "https")) - else code_interpreter.upload_file(media) + else code_interpreter.upload_file(cast(str, media)) ) chat_i["content"] += f" Media name {media}" # type: ignore - media_list.append(media) + media_list.append(str(media)) int_chat = cast( List[Message], diff --git a/vision_agent/agent/vision_agent_prompts.py b/vision_agent/agent/vision_agent_prompts.py index 4774d84d..85e34cd5 100644 --- a/vision_agent/agent/vision_agent_prompts.py +++ b/vision_agent/agent/vision_agent_prompts.py @@ -1,7 +1,7 @@ VA_CODE = """ **Role**: You are a helpful conversational agent that assists users with their requests by writing code to solve it. -**Taks**: As a conversational agent, you are required to understand the user's request and provide a helpful response. Use a Chain-of-Thought approach to break down the problem, create a plan, and then provide a response. Ensure that your response is clear, concise, and helpful. You can use an interactive Python (Jupyter Notebook) environment, executing code with . +**Taks**: As a conversational agent, you are required to understand the user's request and provide a helpful response. Use a Chain-of-Thought approach to break down the problem, create a plan, and then provide a response. Ensure that your response is clear, concise, and helpful. You can use an interactive Python (Jupyter Notebook) environment, executing code with . You are given access to an `artifacts` object which contains files shared between you and the user. `artifacts` will be automatically saved everytime you execute python code. print("Hello World!") @@ -15,7 +15,6 @@ **Examples**: Here is an example of how you can interact with a user and Actions to complete a task: --- START EXAMPLES --- -[Current directory: /example/workspace] {examples} --- END EXAMPLES --- @@ -26,24 +25,28 @@ **Conversation**: Here is the current conversation so far: --- START CONVERSATION --- -[Current directory: {dir}] - {conversation} """ + EXAMPLES_CODE1 = """ USER: Can you detect the dogs in this image? Media name dog.jpg -AGENT: {"thoughts": "I will use the generate_vision_code to detect the dogs in the image.", "response": "generate_vision_code('/example/workspace/dog_detector.py', 'Can you write code to detect dogs in this image?', media=['/example/workspace/dog.jpg'])", "let_user_respond": false} +OBSERVATION: +[Artifacts loaded] +Artifact dog.jpg loaded to /path/to/images/dog.jpg +[End of artifacts] + +AGENT: {"thoughts": "I will use the generate_vision_code to detect the dogs in the image.", "response": "generate_vision_code(artifacts, 'dog_detector.py', 'Can you write code to detect dogs in this image?', media=['/path/to/images/dog.jpg'])", "let_user_respond": false} OBSERVATION: -[File /example/workspace/dog_detector.py] +[Artifact dog_detector.py] 0|from vision_agent.tools import load_image, owl_v2 1|def detect_dogs(image_path: str): 2| image = load_image(image_path) 3| dogs = owl_v2("dog", image) 4| return dogs -[End of file] +[End of artifact] AGENT: {"thoughts": "I have generated the code to detect the dogs in the image, I must now run the code to get the output.", "response": "from dog_detector import detect_dogs\n print(detect_dogs('/example/workspace/dog.jpg'))", "let_user_respond": false} @@ -56,18 +59,23 @@ USER: The the image only has one dog, can you fix this? -AGENT: {"thoughts": "I will use edit_vision_code to fix the code to detect only one dog. The lower score is 0.23 so I will set the threshold above that.", "response": "edit_vision_code('/example/workspace/dog_detector.py', ['Can you write code to detect dogs in this image?', 'Can you increase the threshold to 0.24?'], media=['/example/workspace/dog.jpg'])", "let_user_respond": false} +[Artifacts loaded] +Artifact dog.jpg loaded to /path/to/images/dog.jpg +Artifact dog_detector.py loaded to /path/to/code/dog_detector.py +[End of artifacts] + +AGENT: {"thoughts": "I will use edit_vision_code to fix the code to detect only one dog. The lower score is 0.23 so I will set the threshold above that.", "response": "edit_vision_code(artifacts, 'dog_detector.py', ['Can you write code to detect dogs in this image?', 'Can you increase the threshold to 0.24?'], media=['/path/to/images/dog.jpg'])", "let_user_respond": false} OBSERVATION: -[File /example/workspace/dog_detector.py] +[Artifact dog_detector.py] 0|from vision_agent.tools import load_image, owl_v2 1|def detect_dogs(image_path: str): 2| image = load_image(image_path) 3| dogs = owl_v2("dog", image, threshold=0.24) 4| return dogs -[End of file] +[End of artifact] -AGENT: {"thoughts": "I have edited the code to detect only one dog, I must now run the code to get the output.", "response": "from dog_detector import detect_dogs\n print(detect_dogs('/example/workspace/dog.jpg'))", "let_user_respond": false} +AGENT: {"thoughts": "I have edited the code to detect only one dog, I must now run the code to get the output.", "response": "from dog_detector import detect_dogs\n print(detect_dogs('/path/to/images/dog.jpg'))", "let_user_respond": false} OBSERVATION: ----- stdout ----- @@ -76,23 +84,34 @@ AGENT: {"thoughts": "One dog is detected, I will show this to the user and ask them if the result looks good.", "response": "I have written the code to detect one dog and shown you the output, do the results look good to you?", "let_user_respond": true} """ + EXAMPLES_CODE2 = """ -USER: Can you create a function to count workers with helmets? +USER: Can you create a function to count workers with helmets, return the count and save a visaulization of the bounding boxes? + +OBSERVATION: +[Artifacts loaded] +[End of artifacts] AGENT: {"thoughts": "The user has asked to count workers with helmets but has not provided an image. I will ask the user for an image and then generate the code to count workers with helmets.", "response": "Can you provide an image of workers with helmets?", "let_user_respond": true} USER: Yes you can use workers.png -AGENT: {"thoughts": "I will use the generate_vision_code to count the workers with helmets in the image.", "response": "generate_vision_code('/example/workspace/code.py', 'Can you write code to count workers with helmets in this image?', media=['/example/workspace/workers.png'])", "let_user_respond": false} +OBSERVATION: +[Artifacts loaded] +Artifact workers.png loaded to /path/to/images/workers.png +[End of artifacts] + +AGENT: {"thoughts": "I will use the generate_vision_code to count the workers with helmets in the image.", "response": "generate_vision_code(artifacts, 'code.py', 'Can you create a function to count workers with helmets, return the count and save a visaulization of the bounding boxes?', media=['/paths/to/images/workers.png'])", "let_user_respond": false} OBSERVATION: -[File /example/workspace/code.py] -0|from vision_agent.tools import load_image, owl_v2, closest_box_distance -1|def count_workers_with_helmets(image_path: str): +[Artifact code.py] +0|from vision_agent.tools import load_image, owl_v2, closest_box_distance, overlay_bounding_boxes, save_image +1|def count_workers_with_helmets(image_path: str, output_path: str): 2| image = load_image(image_path) -3| workers = owl_v2("worker", image) -4| helmets = owl_v2("helmet", image) -5| count = 0 +3| detections = owl_v2("worker, helmet", image) +4| workers = [d for d in detections if d['label'] == 'worker'] +5| helmets = [d for d in detections if d['label'] == 'helmet'] +6| count = 0 6| for worker in workers: 7| person_box = worker['bbox'] 8| person_has_helmet = False @@ -102,14 +121,16 @@ 12| break 13| if person_has_helmet: 14| count += 1 +15| overlay_bounding_boxes(image, detections) +16| save_image(output_path, image) 15| return count -[End of file] +[End of artifact] -AGENT: {"thoughts": "I have generated the code to count the workers with helmets in the image, I must now run the code to get the output.", "response": "from code import count_workers_with_helmets\n print(count_workers_with_helmets('/example/workspace/workers.png'))", "let_user_respond": false} +AGENT: {"thoughts": "I have generated the code to count the workers with helmets in the image, I must now run the code to get the output and write the visualization to the artifacts so the user can see it.", "response": "from code import count_workers_with_helmets\n print(count_workers_with_helmets('/path/to/images/workers.png', 'workers_viz.png'))\n write_media_artifact(artifacts, 'workers_viz.png')", "let_user_respond": false} OBSERVATION: ----- stdout ----- 2 -AGENT: {"thoughts": "Two workers with helmets are detected, I will show this to the user and ask them if the result looks good.", "response": "I have written the code to count the workers wearing helmets in code.py", "let_user_respond": true} +AGENT: {"thoughts": "Two workers with helmets are detected, I will show this to the user and ask them if the result looks good.", "response": "I have written the code to count the workers wearing helmets in code.py and saved the visualization under 'workers_viz.png'.", "let_user_respond": true} """ diff --git a/vision_agent/clients/landing_public_api.py b/vision_agent/clients/landing_public_api.py index eec218ad..2319bf89 100644 --- a/vision_agent/clients/landing_public_api.py +++ b/vision_agent/clients/landing_public_api.py @@ -5,9 +5,9 @@ from requests.exceptions import HTTPError from vision_agent.clients.http import BaseHTTP -from vision_agent.utils.type_defs import LandingaiAPIKey +from vision_agent.tools.tools_types import BboxInputBase64, JobStatus, PromptTask from vision_agent.utils.exceptions import FineTuneModelNotFound -from vision_agent.tools.tools_types import BboxInputBase64, PromptTask, JobStatus +from vision_agent.utils.type_defs import LandingaiAPIKey class LandingPublicAPI(BaseHTTP): diff --git a/vision_agent/lmm/lmm.py b/vision_agent/lmm/lmm.py index 15df5ac9..76481f3f 100644 --- a/vision_agent/lmm/lmm.py +++ b/vision_agent/lmm/lmm.py @@ -138,7 +138,7 @@ def chat( fixed_c["content"] = [{"type": "text", "text": c["content"]}] # type: ignore if "media" in c: for media in c["media"]: - encoded_media = encode_media(media) + encoded_media = encode_media(cast(str, media)) fixed_c["content"].append( # type: ignore { @@ -389,7 +389,9 @@ def chat( fixed_chat = [] for message in chat: if "media" in message: - message["images"] = [encode_media(m) for m in message["media"]] + message["images"] = [ + encode_media(cast(str, m)) for m in message["media"] + ] del message["media"] fixed_chat.append(message) url = f"{self.url}/chat" diff --git a/vision_agent/lmm/types.py b/vision_agent/lmm/types.py index ded6a42b..b9c99fe2 100644 --- a/vision_agent/lmm/types.py +++ b/vision_agent/lmm/types.py @@ -1,5 +1,7 @@ from pathlib import Path from typing import Dict, Sequence, Union +from vision_agent.utils.execute import Execution + TextOrImage = Union[str, Sequence[Union[str, Path]]] -Message = Dict[str, TextOrImage] +Message = Dict[str, Union[TextOrImage, Execution]] diff --git a/vision_agent/tools/__init__.py b/vision_agent/tools/__init__.py index 3372fcbb..e82d7553 100644 --- a/vision_agent/tools/__init__.py +++ b/vision_agent/tools/__init__.py @@ -1,6 +1,6 @@ from typing import Callable, List, Optional -from .meta_tools import META_TOOL_DOCSTRING +from .meta_tools import META_TOOL_DOCSTRING, Artifacts from .prompts import CHOOSE_PARAMS, SYSTEM_PROMPT from .tool_utils import get_tool_descriptions_by_names from .tools import ( @@ -21,8 +21,8 @@ dpt_hybrid_midas, extract_frames, florence2_image_caption, - florence2_phrase_grounding, florence2_ocr, + florence2_phrase_grounding, florence2_roberta_vqa, florence2_sam2_image, florence2_sam2_video, diff --git a/vision_agent/tools/meta_tools.py b/vision_agent/tools/meta_tools.py index 4a82436d..833ad542 100644 --- a/vision_agent/tools/meta_tools.py +++ b/vision_agent/tools/meta_tools.py @@ -1,12 +1,17 @@ import os +import pickle as pkl import subprocess +import tempfile from pathlib import Path -from typing import Any, Dict, List, Union +from typing import Any, Dict, List, Optional, Union + +from IPython.display import display import vision_agent as va from vision_agent.lmm.types import Message from vision_agent.tools.tool_utils import get_tool_documentation from vision_agent.tools.tools import TOOL_DESCRIPTIONS +from vision_agent.utils.execute import Execution, MimeType # These tools are adapted from SWE-Agent https://github.com/princeton-nlp/SWE-agent @@ -35,97 +40,91 @@ def filter_file(file_name: Union[str, Path]) -> bool: ) -def generate_vision_code(save_file: str, chat: str, media: List[str]) -> str: - """Generates python code to solve vision based tasks. - - Parameters: - save_file (str): The file path to save the code. - chat (str): The chat message from the user. - media (List[str]): The media files to use. - - Returns: - str: The generated code. - - Examples - -------- - >>> generate_vision_code("code.py", "Can you detect the dogs in this image?", ["image.jpg"]) - from vision_agent.tools import load_image, owl_v2 - def detect_dogs(image_path: str): - image = load_image(image_path) - dogs = owl_v2("dog", image) - return dogs +def redisplay_results(execution: Execution) -> None: + """This function is used to add previous execution results to the current output. + This is handy if you are inside a notebook environment, call it notebook1, and you + have a nested notebook environment, call it notebook2, and you want the execution + results from notebook2 to be included in the execution results for notebook1. + """ + for result in execution.results: + if result.text is not None: + display({MimeType.TEXT_PLAIN: result.text}) + if result.html is not None: + display({MimeType.TEXT_HTML: result.html}) + if result.markdown is not None: + display({MimeType.TEXT_MARKDOWN: result.markdown}) + if result.svg is not None: + display({MimeType.IMAGE_SVG: result.svg}) + if result.png is not None: + display({MimeType.IMAGE_PNG: result.png}) + if result.jpeg is not None: + display({MimeType.IMAGE_JPEG: result.jpeg}) + if result.mp4 is not None: + display({MimeType.VIDEO_MP4_B64: result.mp4}) + if result.latex is not None: + display({MimeType.TEXT_LATEX: result.latex}) + if result.json is not None: + display({MimeType.APPLICATION_JSON: result.json}) + if result.extra is not None: + display(result.extra) + + +class Artifacts: + """Artifacts is a class that allows you to sync files between a local and remote + environment. In our case, the remote environment could be where the VisionAgent is + executing code and as the user adds new images, files or modifies files, those + need to be in sync with the remote environment the VisionAgent is running in. """ - if ZMQ_PORT is not None: - agent = va.agent.VisionAgentCoder( - report_progress_callback=lambda inp: report_progress_callback( - int(ZMQ_PORT), inp + def __init__(self, remote_save_path: Union[str, Path]) -> None: + self.remote_save_path = Path(remote_save_path) + self.artifacts: Dict[str, Any] = {} + + self.code_sandbox_runtime = None + + def load(self, file_path: Union[str, Path]) -> None: + """Loads are artifacts into the remote environment. If an artifact value is None + it will skip loading it. + + Parameters: + file_path (Union[str, Path]): The file path to load the artifacts from + """ + with open(file_path, "rb") as f: + self.artifacts = pkl.load(f) + for k, v in self.artifacts.items(): + if v is not None: + mode = "w" if isinstance(v, str) else "wb" + with open(self.remote_save_path.parent / k, mode) as f: + f.write(v) + + def show(self) -> str: + """Shows the artifacts that have been loaded and their remote save paths.""" + out_str = "[Artifacts loaded]\n" + for k in self.artifacts.keys(): + out_str += ( + f"Artifact {k} loaded to {str(self.remote_save_path.parent / k)}\n" ) + out_str += "[End of artifacts]\n" + return out_str + + def save(self, local_path: Optional[Union[str, Path]] = None) -> None: + save_path = ( + Path(local_path) if local_path is not None else self.remote_save_path ) - else: - agent = va.agent.VisionAgentCoder() - try: - fixed_chat: List[Message] = [{"role": "user", "content": chat, "media": media}] - response = agent.chat_with_workflow(fixed_chat) - code = response["code"] - with open(save_file, "w") as f: - f.write(code) - code_lines = code.splitlines(keepends=True) - total_lines = len(code_lines) - return view_lines(code_lines, 0, total_lines, save_file, total_lines) - except Exception as e: - return str(e) - - -def edit_vision_code(code_file: str, chat_history: List[str], media: List[str]) -> str: - """Edits python code to solve a vision based task. + with open(save_path, "wb") as f: + pkl.dump(self.artifacts, f) - Parameters: - code_file (str): The file path to the code. - chat_history (List[str]): The chat history to used to generate the code. + def __iter__(self) -> Any: + return iter(self.artifacts) - Returns: - str: The edited code. + def __getitem__(self, name: str) -> Any: + return self.artifacts[name] - Examples - -------- - >>> edit_vision_code( - >>> "code.py", - >>> ["Can you detect the dogs in this image?", "Can you use a higher threshold?"], - >>> ["dog.jpg"], - >>> ) - from vision_agent.tools import load_image, owl_v2 - def detect_dogs(image_path: str): - image = load_image(image_path) - dogs = owl_v2("dog", image, threshold=0.8) - return dogs - """ + def __setitem__(self, name: str, value: Any) -> None: + self.artifacts[name] = value - agent = va.agent.VisionAgentCoder() - with open(code_file, "r") as f: - code = f.read() - - # Append latest code to second to last message from assistant - fixed_chat_history: List[Message] = [] - for i, chat in enumerate(chat_history): - if i == 0: - fixed_chat_history.append({"role": "user", "content": chat, "media": media}) - elif i > 0 and i < len(chat_history) - 1: - fixed_chat_history.append({"role": "user", "content": chat}) - elif i == len(chat_history) - 1: - fixed_chat_history.append({"role": "assistant", "content": code}) - fixed_chat_history.append({"role": "user", "content": chat}) - - try: - response = agent.chat_with_workflow(fixed_chat_history, test_multi_plan=False) - code = response["code"] - with open(code_file, "w") as f: - f.write(code) - code_lines = code.splitlines(keepends=True) - total_lines = len(code_lines) - return view_lines(code_lines, 0, total_lines, code_file, total_lines) - except Exception as e: - return str(e) + def __contains__(self, name: str) -> bool: + return name in self.artifacts def format_lines(lines: List[str], start_idx: int) -> str: @@ -136,34 +135,40 @@ def format_lines(lines: List[str], start_idx: int) -> str: def view_lines( - lines: List[str], line_num: int, window_size: int, file_path: str, total_lines: int + lines: List[str], line_num: int, window_size: int, name: str, total_lines: int ) -> str: start = max(0, line_num - window_size) end = min(len(lines), line_num + window_size) - return ( - f"[File: {file_path} ({total_lines} lines total)]\n" + return_str = ( + f"[Artifact: {name} ({total_lines} lines total)]\n" + format_lines(lines[start:end], start) - + ("[End of file]" if end == len(lines) else f"[{len(lines) - end} more lines]") + + ( + "[End of artifact]" + if end == len(lines) + else f"[{len(lines) - end} more lines]" + ) ) + print(return_str) + return return_str -def open_file(file_path: str, line_num: int = 0, window_size: int = 100) -> str: - """Opens the file at at the given path in the editor. If `line_num` is provided, - the window will be moved to include that line. It only shows the first 100 lines by - default! Max `window_size` supported is 2000. use `scroll up/down` to view the file - if you want to see more. +def open_code_artifact( + artifacts: Artifacts, name: str, line_num: int = 0, window_size: int = 100 +) -> str: + """Opens the provided code artifact. If `line_num` is provided, the window will be + moved to include that line. It only shows the first 100 lines by default! Max + `window_size` supported is 2000. Parameters: - file_path (str): The file path to open, preferred absolute path. + artifacts (Artifacts): The artifacts object to open the artifact from. + name (str): The name of the artifact to open. line_num (int): The line number to move the window to. window_size (int): The number of lines to show above and below the line. """ + if name not in artifacts: + return f"[Artifact {name} does not exist]" - file_path_p = Path(file_path) - if not file_path_p.exists(): - return f"[File {file_path} does not exist]" - - total_lines = sum(1 for _ in open(file_path_p)) + total_lines = len(artifacts[name].splitlines()) window_size = min(window_size, 2000) window_size = window_size // 2 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: elif line_num >= total_lines: line_num = total_lines - 1 - window_size - global CURRENT_LINE, CURRENT_FILE - CURRENT_LINE = line_num - CURRENT_FILE = file_path + lines = artifacts[name].splitlines(keepends=True) - with open(file_path, "r") as f: - lines = f.readlines() + return view_lines(lines, line_num, window_size, name, total_lines) - return view_lines(lines, line_num, window_size, file_path, total_lines) - -def create_file(file_path: str) -> str: - """Creates and opens a new file with the given name. +def create_code_artifact(artifacts: Artifacts, name: str) -> str: + """Creates a new code artifiact with the given name. Parameters: - file_path (str): The file path to create, preferred absolute path. + artifacts (Artifacts): The artifacts object to add the new artifact to. + name (str): The name of the new artifact. """ + if name in artifacts: + return_str = f"[Artifact {name} already exists]" + else: + artifacts[name] = "" + return_str = f"[Artifact {name} created]" + print(return_str) - file_path_p = Path(file_path) - if file_path_p.exists(): - return f"[File {file_path} already exists]" - file_path_p.touch() - global CURRENT_FILE - CURRENT_FILE = file_path - return f"[File created {file_path}]" - + display({MimeType.APPLICATION_JSON: {"last_artifact": name}}) + return return_str -def scroll_up() -> str: - """Moves the window up by 100 lines.""" - if CURRENT_FILE is None: - return "[No file is open]" - return open_file(CURRENT_FILE, CURRENT_LINE + DEFAULT_WINDOW_SIZE) +def edit_code_artifact( + artifacts: Artifacts, name: str, start: int, end: int, content: str +) -> str: + """Edits the given code artifact with the provided content. The content will be + inserted between the `start` and `end` line numbers. If the `start` and `end` are + the same, the content will be inserted at the `start` line number. If the `end` is + greater than the total number of lines in the file, the content will be inserted at + the end of the file. If the `start` or `end` are negative, the function will return + an error message. + Parameters: + artifacts (Artifacts): The artifacts object to edit the artifact from. + name (str): The name of the artifact to edit. + start (int): The line number to start the edit. + end (int): The line number to end the edit. + content (str): The content to insert. + """ + # just make the artifact if it doesn't exist instead of forcing agent to call + # create_artifact + if name not in artifacts: + artifacts[name] = "" -def scroll_down() -> str: - """Moves the window down by 100 lines.""" - if CURRENT_FILE is None: - return "[No file is open]" + total_lines = len(artifacts[name].splitlines()) + if start < 0 or end < 0 or start > end or end > total_lines: + return "[Invalid line range]" + if start == end: + end += 1 - return open_file(CURRENT_FILE, CURRENT_LINE - DEFAULT_WINDOW_SIZE) + new_content_lines = content.splitlines(keepends=True) + new_content_lines = [ + line if line.endswith("\n") else line + "\n" for line in new_content_lines + ] + lines = artifacts[name].splitlines() + edited_lines = lines[:start] + new_content_lines + lines[end:] + cur_line = start + len(content.split("\n")) // 2 + with tempfile.NamedTemporaryFile(delete=True) as f: + with open(f.name, "w") as f: # type: ignore + f.writelines(edited_lines) + + process = subprocess.Popen( + [ + "flake8", + "--isolated", + "--select=F821,F822,F831,E111,E112,E113,E999,E902", + f.name, + ], + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + text=True, + ) + stdout, _ = process.communicate() + + if stdout != "": + stdout = stdout.replace(f.name, name) + error_msg = "[Edit failed with the following status]\n" + stdout + original_view = view_lines( + lines, + start + ((end - start) // 2), + DEFAULT_WINDOW_SIZE, + name, + total_lines, + ) + total_lines_edit = sum(1 for _ in edited_lines) + edited_view = view_lines( + edited_lines, cur_line, DEFAULT_WINDOW_SIZE, name, total_lines_edit + ) -def search_dir(search_term: str, dir_path: str) -> str: - """Searches for search_term in all files in a directory. + 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}" + return error_msg - Parameters: - search_term (str): The search term to look for. - dir_path (str): The directory path to search in, preferred absolute path. - """ + artifacts[name] = "".join(edited_lines) - dir_path_p = Path(dir_path) - if not dir_path_p.exists(): - return f"[Directory {dir_path} does not exist]" - - matches = [] - for file in dir_path_p.glob("**/*"): - if filter_file(file): - with open(file, "r") as f: - lines = f.readlines() - for i, line in enumerate(lines): - if search_term in line: - matches.append(f"{file}:{i}|{line.strip()}\n") - if not matches: - return f"[No matches found for {search_term} in {dir_path}]" - if len(matches) > 100: - return f"[More than {len(matches)} matches found for {search_term} in {dir_path}. Please narrow your search]" - - return_str = f"[Found {len(matches)} matches for {search_term} in {dir_path}]\n" - for match in matches: - return_str += match - - return_str += f"[End of matches for {search_term} in {dir_path}]" - return return_str + display({MimeType.APPLICATION_JSON: {"last_artifact": name}}) + return open_code_artifact(artifacts, name, cur_line) -def search_file(search_term: str, file_path: str) -> str: - """Searches the file for the given search term. +def generate_vision_code( + artifacts: Artifacts, name: str, chat: str, media: List[str] +) -> str: + """Generates python code to solve vision based tasks. Parameters: - search_term (str): The search term to look for. - file_path (str): The file path to search in, preferred absolute path. - """ - - file_path_p = Path(file_path) - if not file_path_p.exists(): - return f"[File {file_path} does not exist]" + artifacts (Artifacts): The artifacts object to save the code to. + name (str): The name of the artifact to save the code to. + chat (str): The chat message from the user. + media (List[str]): The media files to use. - with open(file_path_p, "r") as f: - lines = f.readlines() + Returns: + str: The generated code. - search_results = [] - for i, line in enumerate(lines): - if search_term in line: - search_results.append(f"{i}|{line.strip()}\n") + Examples + -------- + >>> generate_vision_code(artifacts, "code.py", "Can you detect the dogs in this image?", ["image.jpg"]) + from vision_agent.tools import load_image, owl_v2 + def detect_dogs(image_path: str): + image = load_image(image_path) + dogs = owl_v2("dog", image) + return dogs + """ - if not search_results: - return f"[No matches found for {search_term} in {file_path}]" + if ZMQ_PORT is not None: + agent = va.agent.VisionAgentCoder( + report_progress_callback=lambda inp: report_progress_callback( + int(ZMQ_PORT), inp + ) + ) + else: + agent = va.agent.VisionAgentCoder() - return_str = ( - f"[Found {len(search_results)} matches for {search_term} in {file_path}]\n" - ) - for result in search_results: - return_str += result + fixed_chat: List[Message] = [{"role": "user", "content": chat, "media": media}] + response = agent.chat_with_workflow(fixed_chat, test_multi_plan=True) + redisplay_results(response["test_result"]) + code = response["code"] + artifacts[name] = code + code_lines = code.splitlines(keepends=True) + total_lines = len(code_lines) - return_str += f"[End of matches for {search_term} in {file_path}]" - return return_str + display({MimeType.APPLICATION_JSON: {"last_artifact": name}}) + return view_lines(code_lines, 0, total_lines, name, total_lines) -def find_file(file_name: str, dir_path: str = "./") -> str: - """Finds all files with the given name in the specified directory. +def edit_vision_code( + artifacts: Artifacts, name: str, chat_history: List[str], media: List[str] +) -> str: + """Edits python code to solve a vision based task. Parameters: - file_name (str): The file name to look for. - dir_path (str): The directory path to search in, preferred absolute path. - """ - - dir_path_p = Path(dir_path) - if not dir_path_p.exists(): - return f"[Directory {dir_path} does not exist]" - - files = list(dir_path_p.glob(f"**/*{file_name}*")) - files = [f for f in files if filter_file(f)] - if not files: - return f"[No files found in {dir_path} with name {file_name}]" - - return_str = f"[Found {len(files)} matches for {file_name} in {dir_path}]\n" - for match in files: - return_str += str(match) + "\n" + artifacts (Artifacts): The artifacts object to save the code to. + name (str): The file path to the code. + chat_history (List[str]): The chat history to used to generate the code. - return_str += f"[End of matches for {file_name} in {dir_path}]" - return return_str + Returns: + str: The edited code. + Examples + -------- + >>> edit_vision_code( + >>> artifacts, + >>> "code.py", + >>> ["Can you detect the dogs in this image?", "Can you use a higher threshold?"], + >>> ["dog.jpg"], + >>> ) + from vision_agent.tools import load_image, owl_v2 + def detect_dogs(image_path: str): + image = load_image(image_path) + dogs = owl_v2("dog", image, threshold=0.8) + return dogs + """ -def edit_file(file_path: str, start: int, end: int, content: str) -> str: - """Edits the file at the given path with the provided content. The content will be - inserted between the `start` and `end` line numbers. If the `start` and `end` are - the same, the content will be inserted at the `start` line number. If the `end` is - greater than the total number of lines in the file, the content will be inserted at - the end of the file. If the `start` or `end` are negative, the function will return - an error message. + agent = va.agent.VisionAgentCoder() + if name not in artifacts: + return f"[Artifact {name} does not exist]" - Parameters: - file_path (str): The file path to edit, preferred absolute path. - start (int): The line number to start the edit. - end (int): The line number to end the edit. - content (str): The content to insert. - """ - file_path_p = Path(file_path) - if not file_path_p.exists(): - return f"[File {file_path} does not exist]" + code = artifacts[name] - total_lines = sum(1 for _ in open(file_path_p)) - if start < 0 or end < 0 or start > end or end > total_lines: - return "[Invalid line range]" - if start == end: - end += 1 + # Append latest code to second to last message from assistant + fixed_chat_history: List[Message] = [] + for i, chat in enumerate(chat_history): + if i == 0: + fixed_chat_history.append({"role": "user", "content": chat, "media": media}) + elif i > 0 and i < len(chat_history) - 1: + fixed_chat_history.append({"role": "user", "content": chat}) + elif i == len(chat_history) - 1: + fixed_chat_history.append({"role": "assistant", "content": code}) + fixed_chat_history.append({"role": "user", "content": chat}) - new_content_lines = content.splitlines(keepends=True) - new_content_lines = [ - line if line.endswith("\n") else line + "\n" for line in new_content_lines - ] - with open(file_path_p, "r") as f: - lines = f.readlines() - edited_lines = lines[:start] + new_content_lines + lines[end:] + response = agent.chat_with_workflow(fixed_chat_history, test_multi_plan=False) + redisplay_results(response["test_result"]) + code = response["code"] + artifacts[name] = code + code_lines = code.splitlines(keepends=True) + total_lines = len(code_lines) - cur_line = start + len(content.split("\n")) // 2 - tmp_file = file_path_p.with_suffix(".tmp") - with open(tmp_file, "w") as f: - f.writelines(edited_lines) - - process = subprocess.Popen( - [ - "flake8", - "--isolated", - "--select=F821,F822,F831,E111,E112,E113,E999,E902", - tmp_file, - ], - stdout=subprocess.PIPE, - stderr=subprocess.PIPE, - text=True, - ) - stdout, _ = process.communicate() - tmp_file.unlink() - if stdout != "": - stdout = stdout.replace(tmp_file.name, file_path) - error_msg = "[Edit failed with the following status]\n" + stdout - original_view = view_lines( - lines, - start + ((end - start) // 2), - DEFAULT_WINDOW_SIZE, - file_path, - total_lines, - ) - total_lines_edit = sum(1 for _ in edited_lines) - edited_view = view_lines( - edited_lines, cur_line, DEFAULT_WINDOW_SIZE, file_path, total_lines_edit - ) + display({MimeType.APPLICATION_JSON: {"last_artifact": name}}) + return view_lines(code_lines, 0, total_lines, name, total_lines) - 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}" - return error_msg - with open(file_path_p, "w") as f: - f.writelines(edited_lines) +def write_media_artifact(artifacts: Artifacts, local_path: str) -> str: + """Writes a media file to the artifacts object. - return open_file(file_path, cur_line) + Parameters: + artifacts (Artifacts): The artifacts object to save the media to. + local_path (str): The local path to the media file. + """ + with open(local_path, "rb") as f: + media = f.read() + artifacts[Path(local_path).name] = media + return f"[Media {Path(local_path).name} saved]" def get_tool_descriptions() -> str: @@ -388,15 +400,11 @@ def get_tool_descriptions() -> str: META_TOOL_DOCSTRING = get_tool_documentation( [ get_tool_descriptions, + open_code_artifact, + create_code_artifact, + edit_code_artifact, generate_vision_code, edit_vision_code, - open_file, - create_file, - scroll_up, - scroll_down, - edit_file, - search_dir, - search_file, - find_file, + write_media_artifact, ] ) diff --git a/vision_agent/tools/tools.py b/vision_agent/tools/tools.py index 62a1908a..0695b547 100644 --- a/vision_agent/tools/tools.py +++ b/vision_agent/tools/tools.py @@ -1,8 +1,9 @@ -import os import io import json import logging +import os import tempfile +import urllib.request from importlib import resources from pathlib import Path from typing import Any, Dict, List, Optional, Tuple, Union, cast @@ -15,7 +16,6 @@ from PIL import Image, ImageDraw, ImageFont from pillow_heif import register_heif_opener # type: ignore from pytube import YouTube # type: ignore -import urllib.request from vision_agent.clients.landing_public_api import LandingPublicAPI from vision_agent.tools.tool_utils import ( @@ -1332,7 +1332,7 @@ def save_video( video.write_videofile(f.name, codec="libx264") f.close() _save_video_to_result(f.name) - return f.name + return f.name def _save_video_to_result(video_uri: str) -> None: diff --git a/vision_agent/tools/tools_types.py b/vision_agent/tools/tools_types.py index aeb45c95..7b640adb 100644 --- a/vision_agent/tools/tools_types.py +++ b/vision_agent/tools/tools_types.py @@ -1,8 +1,8 @@ -from uuid import UUID from enum import Enum -from typing import List, Tuple, Optional +from typing import List, Optional, Tuple +from uuid import UUID -from pydantic import BaseModel, ConfigDict, Field, field_serializer, SerializationInfo +from pydantic import BaseModel, ConfigDict, Field, SerializationInfo, field_serializer class BboxInput(BaseModel): diff --git a/vision_agent/utils/execute.py b/vision_agent/utils/execute.py index 033276d3..37c8d260 100644 --- a/vision_agent/utils/execute.py +++ b/vision_agent/utils/execute.py @@ -5,7 +5,6 @@ import platform import re import sys -import tempfile import traceback import warnings from enum import Enum @@ -40,6 +39,7 @@ load_dotenv() _LOGGER = logging.getLogger(__name__) _SESSION_TIMEOUT = 600 # 10 minutes +WORKSPACE = Path(os.getenv("WORKSPACE", "")) class MimeType(str, Enum): @@ -384,8 +384,15 @@ def from_e2b_execution(exec: E2BExecution) -> "Execution": class CodeInterpreter(abc.ABC): """Code interpreter interface.""" - def __init__(self, timeout: int, *args: Any, **kwargs: Any) -> None: + def __init__( + self, + timeout: int, + remote_path: Optional[Union[str, Path]] = None, + *args: Any, + **kwargs: Any, + ) -> None: self.timeout = timeout + self.remote_path = Path(remote_path if remote_path is not None else WORKSPACE) def __enter__(self) -> Self: return self @@ -406,17 +413,21 @@ def exec_isolation(self, code: str) -> Execution: self.restart_kernel() return self.exec_cell(code) - def upload_file(self, file: Union[str, Path]) -> str: + def upload_file(self, file: Union[str, Path]) -> Path: # Default behavior is a no-op (for local code interpreter) - return str(file) + return Path(file) - def download_file(self, file_path: str) -> Path: + def download_file( + self, remote_file_path: Union[str, Path], local_file_path: Union[str, Path] + ) -> Path: # Default behavior is a no-op (for local code interpreter) - return Path(file_path) + return Path(local_file_path) class E2BCodeInterpreter(CodeInterpreter): - def __init__(self, *args: Any, **kwargs: Any) -> None: + def __init__( + self, remote_path: Optional[Union[str, Path]] = None, *args: Any, **kwargs: Any + ) -> None: super().__init__(*args, **kwargs) assert os.getenv("E2B_API_KEY"), "E2B_API_KEY environment variable must be set" try: @@ -443,6 +454,9 @@ def __init__(self, *args: Any, **kwargs: Any) -> None: _LOGGER.info( f"E2BCodeInterpreter (sandbox id: {self.interpreter.sandbox_id}) initialized:\n{sys_versions}" ) + self.remote_path = Path( + remote_path if remote_path is not None else "/home/user" + ) def close(self, *args: Any, **kwargs: Any) -> None: try: @@ -516,19 +530,22 @@ def exec_cell(self, code: str) -> Execution: before_sleep=tenacity.before_sleep_log(_LOGGER, logging.INFO), after=tenacity.after_log(_LOGGER, logging.INFO), ) - def upload_file(self, file: Union[str, Path]) -> str: + def upload_file(self, file: Union[str, Path]) -> Path: file_name = Path(file).name - remote_path = f"/home/user/{file_name}" with open(file, "rb") as f: - self.interpreter.files.write(path=remote_path, data=f) - _LOGGER.info(f"File ({file}) is uploaded to: {remote_path}") - return remote_path - - def download_file(self, file_path: str) -> Path: - with tempfile.NamedTemporaryFile(mode="w+b", delete=False) as file: - file.write(self.interpreter.files.read(path=file_path, format="bytes")) - _LOGGER.info(f"File ({file_path}) is downloaded to: {file.name}") - return Path(file.name) + self.interpreter.files.write(path=str(self.remote_path / file_name), data=f) + _LOGGER.info(f"File ({file}) is uploaded to: {str(self.remote_path)}") + return self.remote_path / file_name + + def download_file( + self, remote_file_path: Union[str, Path], local_file_path: Union[str, Path] + ) -> Path: + with open(local_file_path, "w+b") as f: + f.write( + self.interpreter.files.read(path=str(remote_file_path), format="bytes") + ) + _LOGGER.info(f"File ({remote_file_path}) is downloaded to: {local_file_path}") + return Path(local_file_path) @staticmethod def _new_e2b_interpreter_impl(*args, **kwargs) -> E2BCodeInterpreterImpl: # type: ignore @@ -540,7 +557,11 @@ def _new_e2b_interpreter_impl(*args, **kwargs) -> E2BCodeInterpreterImpl: # typ class LocalCodeInterpreter(CodeInterpreter): - def __init__(self, timeout: int = _SESSION_TIMEOUT) -> None: + def __init__( + self, + timeout: int = _SESSION_TIMEOUT, + remote_path: Optional[Union[str, Path]] = None, + ) -> None: super().__init__(timeout=timeout) self.nb = nbformat.v4.new_notebook() self.nb_client = NotebookClient(self.nb, timeout=self.timeout) @@ -554,6 +575,7 @@ def __init__(self, timeout: int = _SESSION_TIMEOUT) -> None: ) sleep(1) self._new_kernel() + self.remote_path = Path(remote_path if remote_path is not None else WORKSPACE) def _new_kernel(self) -> None: if self.nb_client.kc is None or not run_sync(self.nb_client.kc.is_alive)(): # type: ignore @@ -607,6 +629,25 @@ def exec_cell(self, code: str) -> Execution: traceback_raw = traceback.format_exc().splitlines() return Execution.from_exception(e, traceback_raw) + def upload_file(self, file_path: Union[str, Path]) -> Path: + with open(file_path, "rb") as f: + contents = f.read() + with open(self.remote_path / Path(file_path).name, "wb") as f: + f.write(contents) + _LOGGER.info(f"File ({file_path}) is uploaded to: {str(self.remote_path)}") + + return Path(self.remote_path / file_path) + + def download_file( + self, remote_file_path: Union[str, Path], local_file_path: Union[str, Path] + ) -> Path: + with open(self.remote_path / remote_file_path, "rb") as f: + contents = f.read() + with open(local_file_path, "wb") as f: + f.write(contents) + _LOGGER.info(f"File ({remote_file_path}) is downloaded to: {local_file_path}") + return Path(local_file_path) + class CodeInterpreterFactory: """Factory class for creating code interpreters. @@ -630,13 +671,19 @@ def get_default_instance() -> CodeInterpreter: return instance @staticmethod - def new_instance(code_sandbox_runtime: Optional[str] = None) -> CodeInterpreter: + def new_instance( + code_sandbox_runtime: Optional[str] = None, remote_path: Optional[str] = None + ) -> CodeInterpreter: if not code_sandbox_runtime: code_sandbox_runtime = os.getenv("CODE_SANDBOX_RUNTIME", "local") if code_sandbox_runtime == "e2b": - instance: CodeInterpreter = E2BCodeInterpreter(timeout=_SESSION_TIMEOUT) + instance: CodeInterpreter = E2BCodeInterpreter( + timeout=_SESSION_TIMEOUT, remote_path=remote_path + ) elif code_sandbox_runtime == "local": - instance = LocalCodeInterpreter(timeout=_SESSION_TIMEOUT) + instance = LocalCodeInterpreter( + timeout=_SESSION_TIMEOUT, remote_path=remote_path + ) else: raise ValueError( f"Unsupported code sandbox runtime: {code_sandbox_runtime}. Supported runtimes: e2b, local" diff --git a/vision_agent/utils/image_utils.py b/vision_agent/utils/image_utils.py index d2bc8a6d..c1cc8eb6 100644 --- a/vision_agent/utils/image_utils.py +++ b/vision_agent/utils/image_utils.py @@ -70,7 +70,7 @@ def rle_decode_array(rle: Dict[str, List[int]]) -> np.ndarray: r"""Decode a run-length encoded mask. Returns numpy array, 1 - mask, 0 - background. Parameters: - mask: The mask in run-length encoded as an array. + rle: The run-length encoded mask. """ size = rle["size"] counts = rle["counts"] @@ -100,7 +100,7 @@ def frames_to_bytes( """ with tempfile.NamedTemporaryFile(delete=True) as temp_file: clip = ImageSequenceClip(frames, fps=fps) - clip.write_videofile(temp_file.name + f".{file_ext}", fps=fps) + clip.write_videofile(temp_file.name + f".{file_ext}", fps=fps, codec="libx264") with open(temp_file.name + f".{file_ext}", "rb") as f: buffer_bytes = f.read() return buffer_bytes From 172c6a75734f45ec21b81400547b40cfed4b7cd3 Mon Sep 17 00:00:00 2001 From: GitHub Actions Bot Date: Fri, 30 Aug 2024 15:57:35 +0000 Subject: [PATCH 5/7] [skip ci] chore(release): vision-agent 0.2.119 --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index ebb2415d..4eb0cf82 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "poetry.core.masonry.api" [tool.poetry] name = "vision-agent" -version = "0.2.118" +version = "0.2.119" description = "Toolset for Vision Agent" authors = ["Landing AI "] readme = "README.md" From fcbed15cab0b5169360d3c369f391110aa6f3213 Mon Sep 17 00:00:00 2001 From: Dillon Laird Date: Fri, 30 Aug 2024 10:27:03 -0700 Subject: [PATCH 6/7] fix dependency for mkdocs --- poetry.lock | 420 +++++++++++++++++++++++++------------------------ pyproject.toml | 1 + 2 files changed, 214 insertions(+), 207 deletions(-) diff --git a/poetry.lock b/poetry.lock index dc8131ee..35c09e1b 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,4 +1,4 @@ -# This file is automatically @generated by Poetry 1.8.3 and should not be changed by hand. +# This file is automatically @generated by Poetry 1.8.2 and should not be changed by hand. [[package]] name = "annotated-types" @@ -183,13 +183,13 @@ uvloop = ["uvloop (>=0.15.2)"] [[package]] name = "certifi" -version = "2024.7.4" +version = "2024.8.30" description = "Python package for providing Mozilla's CA Bundle." optional = false python-versions = ">=3.6" files = [ - {file = "certifi-2024.7.4-py3-none-any.whl", hash = "sha256:c198e21b1289c2ab85ee4e67bb4b4ef3ead0892059901a8d5b622f24a1101e90"}, - {file = "certifi-2024.7.4.tar.gz", hash = "sha256:5a1e7645bc0ec61a09e26c36f6106dd4cf40c6db3a1fb6352b0244e7fb057c7b"}, + {file = "certifi-2024.8.30-py3-none-any.whl", hash = "sha256:922820b53db7a7257ffbda3f597266d435245903d80737e34f8a45ff3e3230d8"}, + {file = "certifi-2024.8.30.tar.gz", hash = "sha256:bec941d2aa8195e248a60b31ff9f0558284cf01a52591ceda73ea9afffd69fd9"}, ] [[package]] @@ -503,13 +503,13 @@ files = [ [[package]] name = "e2b" -version = "0.17.2a50" +version = "0.17.2a52" description = "E2B SDK that give agents cloud environments" optional = false python-versions = "<4.0,>=3.8" files = [ - {file = "e2b-0.17.2a50-py3-none-any.whl", hash = "sha256:ff60a96df5df4fc2fe3bf104218f3c36f2ab98f21db0cddb29ac74f1922fcc02"}, - {file = "e2b-0.17.2a50.tar.gz", hash = "sha256:14989853ddee5f75d4b651a275ff2445f2c3cc24f53fb061921c12823f7d81de"}, + {file = "e2b-0.17.2a52-py3-none-any.whl", hash = "sha256:e15c585b0fc266fa23acc0826bc6869d24d69effb2b59e951ea3f77bd4be9b8c"}, + {file = "e2b-0.17.2a52.tar.gz", hash = "sha256:63c8b90b2d5208e0178e0ab352d82bb19d91d0796a9a39410cc4b2f43b996343"}, ] [package.dependencies] @@ -669,13 +669,13 @@ dev = ["flake8", "markdown", "twine", "wheel"] [[package]] name = "griffe" -version = "1.1.0" +version = "0.45.3" description = "Signatures for entire Python programs. Extract the structure, the frame, the skeleton of your project, to generate API documentation or find breaking changes in your API." optional = false python-versions = ">=3.8" files = [ - {file = "griffe-1.1.0-py3-none-any.whl", hash = "sha256:38ccc5721571c95ae427123074cf0dc0d36bce7c9701ab2ada9fe0566ff50c10"}, - {file = "griffe-1.1.0.tar.gz", hash = "sha256:c6328cbdec0d449549c1cc332f59227cd5603f903479d73e4425d828b782ffc3"}, + {file = "griffe-0.45.3-py3-none-any.whl", hash = "sha256:ed1481a680ae3e28f91a06e0d8a51a5c9b97555aa2527abc2664447cc22337d6"}, + {file = "griffe-0.45.3.tar.gz", hash = "sha256:02ee71cc1a5035864b97bd0dbfff65c33f6f2c8854d3bd48a791905c2b8a44b9"}, ] [package.dependencies] @@ -715,13 +715,13 @@ trio = ["trio (>=0.22.0,<0.26.0)"] [[package]] name = "httpx" -version = "0.27.0" +version = "0.27.2" description = "The next generation HTTP client." optional = false python-versions = ">=3.8" files = [ - {file = "httpx-0.27.0-py3-none-any.whl", hash = "sha256:71d5465162c13681bff01ad59b2cc68dd838ea1f10e51574bac27103f00c91a5"}, - {file = "httpx-0.27.0.tar.gz", hash = "sha256:a0cb88a46f32dc874e04ee956e4c2764aba2aa228f650b06788ba6bda2962ab5"}, + {file = "httpx-0.27.2-py3-none-any.whl", hash = "sha256:7bb2708e112d8fdd7829cd4243970f0c223274051cb35ee80c03301ee29a3df0"}, + {file = "httpx-0.27.2.tar.gz", hash = "sha256:f7c2be1d2f3c3c3160d441802406b206c2b76f5947b11115e6df10c6c65e66c2"}, ] [package.dependencies] @@ -736,6 +736,7 @@ brotli = ["brotli", "brotlicffi"] cli = ["click (==8.*)", "pygments (==2.*)", "rich (>=10,<14)"] http2 = ["h2 (>=3,<5)"] socks = ["socksio (==1.*)"] +zstd = ["zstandard (>=0.18.0)"] [[package]] name = "huggingface-hub" @@ -787,13 +788,13 @@ license = ["ukkonen"] [[package]] name = "idna" -version = "3.7" +version = "3.8" description = "Internationalized Domain Names in Applications (IDNA)" optional = false -python-versions = ">=3.5" +python-versions = ">=3.6" files = [ - {file = "idna-3.7-py3-none-any.whl", hash = "sha256:82fee1fc78add43492d3a1898bfa6d8a904cc97d8427f683ed8e798d07761aa0"}, - {file = "idna-3.7.tar.gz", hash = "sha256:028ff3aadf0609c1fd278d8ea3089299412a7a8b9bd005dd08b9f8285bcb5cfc"}, + {file = "idna-3.8-py3-none-any.whl", hash = "sha256:050b4e5baadcd44d760cedbd2b8e639f2ff89bbc7a5730fcc662954303377aac"}, + {file = "idna-3.8.tar.gz", hash = "sha256:d838c2c0ed6fced7693d5e8ab8e734d5f8fda53a039c0164afb0b82e771e3603"}, ] [[package]] @@ -849,13 +850,13 @@ setuptools = "*" [[package]] name = "importlib-metadata" -version = "8.2.0" +version = "8.4.0" description = "Read metadata from Python packages" optional = false python-versions = ">=3.8" files = [ - {file = "importlib_metadata-8.2.0-py3-none-any.whl", hash = "sha256:11901fa0c2f97919b288679932bb64febaeacf289d18ac84dd68cb2e74213369"}, - {file = "importlib_metadata-8.2.0.tar.gz", hash = "sha256:72e8d4399996132204f9a16dcc751af254a48f8d1b20b9ff0f98d4a8f901e73d"}, + {file = "importlib_metadata-8.4.0-py3-none-any.whl", hash = "sha256:66f342cc6ac9818fc6ff340576acd24d65ba0b3efabb2b4ac08b598965a4a2f1"}, + {file = "importlib_metadata-8.4.0.tar.gz", hash = "sha256:9a547d3bc3608b025f93d403fdd1aae741c24fbb8314df4b155675742ce303c5"}, ] [package.dependencies] @@ -1147,16 +1148,17 @@ test = ["ipykernel", "pre-commit", "pytest (<8)", "pytest-cov", "pytest-timeout" [[package]] name = "langsmith" -version = "0.1.99" +version = "0.1.107" description = "Client library to connect to the LangSmith LLM Tracing and Evaluation Platform." optional = false python-versions = "<4.0,>=3.8.1" files = [ - {file = "langsmith-0.1.99-py3-none-any.whl", hash = "sha256:ef8d1d74a2674c514aa429b0171a9fbb661207dc3835142cca0e8f1bf97b26b0"}, - {file = "langsmith-0.1.99.tar.gz", hash = "sha256:b5c6a1f158abda61600a4a445081ee848b4a28b758d91f2793dc02aeffafcaf1"}, + {file = "langsmith-0.1.107-py3-none-any.whl", hash = "sha256:ddd0c846980474e271a553e9c220122e32d1f2ce877cc87d39ecd86726b9e78c"}, + {file = "langsmith-0.1.107.tar.gz", hash = "sha256:f44de0a5f199381d0b518ecbe295d541c44ff33d13f18098ecc54a4547eccb3f"}, ] [package.dependencies] +httpx = ">=0.23.0,<1" orjson = ">=3.9.14,<4.0.0" pydantic = [ {version = ">=1,<3", markers = "python_full_version < \"3.12.4\""}, @@ -1343,13 +1345,13 @@ files = [ [[package]] name = "mkdocs" -version = "1.6.0" +version = "1.6.1" description = "Project documentation with Markdown." optional = false python-versions = ">=3.8" files = [ - {file = "mkdocs-1.6.0-py3-none-any.whl", hash = "sha256:1eb5cb7676b7d89323e62b56235010216319217d4af5ddc543a91beb8d125ea7"}, - {file = "mkdocs-1.6.0.tar.gz", hash = "sha256:a73f735824ef83a4f3bcb7a231dcab23f5a838f88b7efc54a0eef5fbdbc3c512"}, + {file = "mkdocs-1.6.1-py3-none-any.whl", hash = "sha256:db91759624d1647f3f34aa0c3f327dd2601beae39a366d6e064c03468d35c20e"}, + {file = "mkdocs-1.6.1.tar.gz", hash = "sha256:7b432f01d928c084353ab39c57282f29f92136665bdd6abf7c1ec8d822ef86f2"}, ] [package.dependencies] @@ -1374,13 +1376,13 @@ min-versions = ["babel (==2.9.0)", "click (==7.0)", "colorama (==0.4)", "ghp-imp [[package]] name = "mkdocs-autorefs" -version = "1.0.1" +version = "1.1.0" description = "Automatically link across pages in MkDocs." optional = false python-versions = ">=3.8" files = [ - {file = "mkdocs_autorefs-1.0.1-py3-none-any.whl", hash = "sha256:aacdfae1ab197780fb7a2dac92ad8a3d8f7ca8049a9cbe56a4218cd52e8da570"}, - {file = "mkdocs_autorefs-1.0.1.tar.gz", hash = "sha256:f684edf847eced40b570b57846b15f0bf57fb93ac2c510450775dcf16accb971"}, + {file = "mkdocs_autorefs-1.1.0-py3-none-any.whl", hash = "sha256:492ac42f50214e81565e968f8cb0df9aba9d981542b9e7121b8f8ae9407fe6eb"}, + {file = "mkdocs_autorefs-1.1.0.tar.gz", hash = "sha256:f2fd43b11f66284bd014f9b542a05c8ecbfaad4e0d7b30b68584788217b6c656"}, ] [package.dependencies] @@ -1407,13 +1409,13 @@ pyyaml = ">=5.1" [[package]] name = "mkdocs-material" -version = "9.5.32" +version = "9.5.33" description = "Documentation that simply works" optional = false python-versions = ">=3.8" files = [ - {file = "mkdocs_material-9.5.32-py3-none-any.whl", hash = "sha256:f3704f46b63d31b3cd35c0055a72280bed825786eccaf19c655b44e0cd2c6b3f"}, - {file = "mkdocs_material-9.5.32.tar.gz", hash = "sha256:38ed66e6d6768dde4edde022554553e48b2db0d26d1320b19e2e2b9da0be1120"}, + {file = "mkdocs_material-9.5.33-py3-none-any.whl", hash = "sha256:dbc79cf0fdc6e2c366aa987de8b0c9d4e2bb9f156e7466786ba2fd0f9bf7ffca"}, + {file = "mkdocs_material-9.5.33.tar.gz", hash = "sha256:d23a8b5e3243c9b2f29cdfe83051104a8024b767312dc8fde05ebe91ad55d89d"}, ] [package.dependencies] @@ -1699,13 +1701,13 @@ files = [ [[package]] name = "openai" -version = "1.41.0" +version = "1.43.0" description = "The official Python library for the openai API" optional = false python-versions = ">=3.7.1" files = [ - {file = "openai-1.41.0-py3-none-any.whl", hash = "sha256:3b6cca4571667f3e0800442ef8f2bfa6a6f3301c51776bc7626159a4d81c242c"}, - {file = "openai-1.41.0.tar.gz", hash = "sha256:26b81f39b49dce92ff5d30c373625ddb212c2f1050e1574e456d18423730cdd0"}, + {file = "openai-1.43.0-py3-none-any.whl", hash = "sha256:1a748c2728edd3a738a72a0212ba866f4fdbe39c9ae03813508b267d45104abe"}, + {file = "openai-1.43.0.tar.gz", hash = "sha256:e607aff9fc3e28eade107e5edd8ca95a910a4b12589336d3cbb6bfe2ac306b3c"}, ] [package.dependencies] @@ -1826,14 +1828,19 @@ files = [ [[package]] name = "paginate" -version = "0.5.6" +version = "0.5.7" description = "Divides large result sets into pages for easier browsing" optional = false python-versions = "*" files = [ - {file = "paginate-0.5.6.tar.gz", hash = "sha256:5e6007b6a9398177a7e1648d04fdd9f8c9766a1a945bceac82f1929e8c78af2d"}, + {file = "paginate-0.5.7-py2.py3-none-any.whl", hash = "sha256:b885e2af73abcf01d9559fd5216b57ef722f8c42affbb63942377668e35c7591"}, + {file = "paginate-0.5.7.tar.gz", hash = "sha256:22bd083ab41e1a8b4f3690544afb2c60c25e5c9a63a30fa2f483f6c60c8e5945"}, ] +[package.extras] +dev = ["pytest", "tox"] +lint = ["black"] + [[package]] name = "pandas" version = "2.2.2" @@ -2192,22 +2199,22 @@ wcwidth = "*" [[package]] name = "protobuf" -version = "5.27.3" +version = "5.28.0" description = "" optional = false python-versions = ">=3.8" files = [ - {file = "protobuf-5.27.3-cp310-abi3-win32.whl", hash = "sha256:dcb307cd4ef8fec0cf52cb9105a03d06fbb5275ce6d84a6ae33bc6cf84e0a07b"}, - {file = "protobuf-5.27.3-cp310-abi3-win_amd64.whl", hash = "sha256:16ddf3f8c6c41e1e803da7abea17b1793a97ef079a912e42351eabb19b2cffe7"}, - {file = "protobuf-5.27.3-cp38-abi3-macosx_10_9_universal2.whl", hash = "sha256:68248c60d53f6168f565a8c76dc58ba4fa2ade31c2d1ebdae6d80f969cdc2d4f"}, - {file = "protobuf-5.27.3-cp38-abi3-manylinux2014_aarch64.whl", hash = "sha256:b8a994fb3d1c11156e7d1e427186662b64694a62b55936b2b9348f0a7c6625ce"}, - {file = "protobuf-5.27.3-cp38-abi3-manylinux2014_x86_64.whl", hash = "sha256:a55c48f2a2092d8e213bd143474df33a6ae751b781dd1d1f4d953c128a415b25"}, - {file = "protobuf-5.27.3-cp38-cp38-win32.whl", hash = "sha256:043853dcb55cc262bf2e116215ad43fa0859caab79bb0b2d31b708f128ece035"}, - {file = "protobuf-5.27.3-cp38-cp38-win_amd64.whl", hash = "sha256:c2a105c24f08b1e53d6c7ffe69cb09d0031512f0b72f812dd4005b8112dbe91e"}, - {file = "protobuf-5.27.3-cp39-cp39-win32.whl", hash = "sha256:c84eee2c71ed83704f1afbf1a85c3171eab0fd1ade3b399b3fad0884cbcca8bf"}, - {file = "protobuf-5.27.3-cp39-cp39-win_amd64.whl", hash = "sha256:af7c0b7cfbbb649ad26132e53faa348580f844d9ca46fd3ec7ca48a1ea5db8a1"}, - {file = "protobuf-5.27.3-py3-none-any.whl", hash = "sha256:8572c6533e544ebf6899c360e91d6bcbbee2549251643d32c52cf8a5de295ba5"}, - {file = "protobuf-5.27.3.tar.gz", hash = "sha256:82460903e640f2b7e34ee81a947fdaad89de796d324bcbc38ff5430bcdead82c"}, + {file = "protobuf-5.28.0-cp310-abi3-win32.whl", hash = "sha256:66c3edeedb774a3508ae70d87b3a19786445fe9a068dd3585e0cefa8a77b83d0"}, + {file = "protobuf-5.28.0-cp310-abi3-win_amd64.whl", hash = "sha256:6d7cc9e60f976cf3e873acb9a40fed04afb5d224608ed5c1a105db4a3f09c5b6"}, + {file = "protobuf-5.28.0-cp38-abi3-macosx_10_9_universal2.whl", hash = "sha256:532627e8fdd825cf8767a2d2b94d77e874d5ddb0adefb04b237f7cc296748681"}, + {file = "protobuf-5.28.0-cp38-abi3-manylinux2014_aarch64.whl", hash = "sha256:018db9056b9d75eb93d12a9d35120f97a84d9a919bcab11ed56ad2d399d6e8dd"}, + {file = "protobuf-5.28.0-cp38-abi3-manylinux2014_x86_64.whl", hash = "sha256:6206afcb2d90181ae8722798dcb56dc76675ab67458ac24c0dd7d75d632ac9bd"}, + {file = "protobuf-5.28.0-cp38-cp38-win32.whl", hash = "sha256:eef7a8a2f4318e2cb2dee8666d26e58eaf437c14788f3a2911d0c3da40405ae8"}, + {file = "protobuf-5.28.0-cp38-cp38-win_amd64.whl", hash = "sha256:d001a73c8bc2bf5b5c1360d59dd7573744e163b3607fa92788b7f3d5fefbd9a5"}, + {file = "protobuf-5.28.0-cp39-cp39-win32.whl", hash = "sha256:dde9fcaa24e7a9654f4baf2a55250b13a5ea701493d904c54069776b99a8216b"}, + {file = "protobuf-5.28.0-cp39-cp39-win_amd64.whl", hash = "sha256:853db610214e77ee817ecf0514e0d1d052dff7f63a0c157aa6eabae98db8a8de"}, + {file = "protobuf-5.28.0-py3-none-any.whl", hash = "sha256:510ed78cd0980f6d3218099e874714cdf0d8a95582e7b059b06cabad855ed0a0"}, + {file = "protobuf-5.28.0.tar.gz", hash = "sha256:dde74af0fa774fa98892209992295adbfb91da3fa98c8f67a88afe8f5a349add"}, ] [[package]] @@ -2632,120 +2639,120 @@ pyyaml = "*" [[package]] name = "pyzmq" -version = "26.1.1" +version = "26.2.0" description = "Python bindings for 0MQ" optional = false python-versions = ">=3.7" files = [ - {file = "pyzmq-26.1.1-cp310-cp310-macosx_10_15_universal2.whl", hash = "sha256:b1bb952d1e407463c9333ea7e0c0600001e54e08ce836d4f0aff1fb3f902cf63"}, - {file = "pyzmq-26.1.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:65e2a18e845c6ea7ab849c70db932eaeadee5edede9e379eb21c0a44cf523b2e"}, - {file = "pyzmq-26.1.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:def7ae3006924b8a0c146a89ab4008310913fa903beedb95e25dea749642528e"}, - {file = "pyzmq-26.1.1-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a8234571df7816f99dde89c3403cb396d70c6554120b795853a8ea56fcc26cd3"}, - {file = "pyzmq-26.1.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:18da8e84dbc30688fd2baefd41df7190607511f916be34f9a24b0e007551822e"}, - {file = "pyzmq-26.1.1-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:c70dab93d98b2bf3f0ac1265edbf6e7f83acbf71dabcc4611889bb0dea45bed7"}, - {file = "pyzmq-26.1.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:fcb90592c5d5c562e1b1a1ceccf6f00036d73c51db0271bf4d352b8d6b31d468"}, - {file = "pyzmq-26.1.1-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:cf4be7460a0c1bc71e9b0e64ecdd75a86386ca6afaa36641686f5542d0314e9d"}, - {file = "pyzmq-26.1.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:a4cbecda4ddbfc1e309c3be04d333f9be3fc6178b8b6592b309676f929767a15"}, - {file = "pyzmq-26.1.1-cp310-cp310-win32.whl", hash = "sha256:583f73b113b8165713b6ce028d221402b1b69483055b5aa3f991937e34dd1ead"}, - {file = "pyzmq-26.1.1-cp310-cp310-win_amd64.whl", hash = "sha256:5e6f39ecb8eb7bfcb976c49262e8cf83ff76e082b77ca23ba90c9b6691a345be"}, - {file = "pyzmq-26.1.1-cp310-cp310-win_arm64.whl", hash = "sha256:8d042d6446cab3a1388b38596f5acabb9926b0b95c3894c519356b577a549458"}, - {file = "pyzmq-26.1.1-cp311-cp311-macosx_10_15_universal2.whl", hash = "sha256:362cac2423e36966d336d79d3ec3eafeabc153ee3e7a5cf580d7e74a34b3d912"}, - {file = "pyzmq-26.1.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:0841633446cb1539a832a19bb24c03a20c00887d0cedd1d891b495b07e5c5cb5"}, - {file = "pyzmq-26.1.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4e1fcdc333afbf9918d0a614a6e10858aede7da49a60f6705a77e343fe86a317"}, - {file = "pyzmq-26.1.1-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cc8d655627d775475eafdcf0e49e74bcc1e5e90afd9ab813b4da98f092ed7b93"}, - {file = "pyzmq-26.1.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:32de51744820857a6f7c3077e620ab3f607d0e4388dfead885d5124ab9bcdc5e"}, - {file = "pyzmq-26.1.1-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:a880240597010914ffb1d6edd04d3deb7ce6a2abf79a0012751438d13630a671"}, - {file = "pyzmq-26.1.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:26131b1cec02f941ed2d2b4b8cc051662b1c248b044eff5069df1f500bbced56"}, - {file = "pyzmq-26.1.1-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:ce05841322b58510607f9508a573138d995a46c7928887bc433de9cb760fd2ad"}, - {file = "pyzmq-26.1.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:32123ff0a6db521aadf2b95201e967a4e0d11fb89f73663a99d2f54881c07214"}, - {file = "pyzmq-26.1.1-cp311-cp311-win32.whl", hash = "sha256:e790602d7ea1d6c7d8713d571226d67de7ffe47b1e22ae2c043ebd537de1bccb"}, - {file = "pyzmq-26.1.1-cp311-cp311-win_amd64.whl", hash = "sha256:717960855f2d6fdc2dba9df49dff31c414187bb11c76af36343a57d1f7083d9a"}, - {file = "pyzmq-26.1.1-cp311-cp311-win_arm64.whl", hash = "sha256:08956c26dbcd4fd8835cb777a16e21958ed2412317630e19f0018d49dbeeb470"}, - {file = "pyzmq-26.1.1-cp312-cp312-macosx_10_15_universal2.whl", hash = "sha256:e80345900ae241c2c51bead7c9fa247bba6d4b2a83423e9791bae8b0a7f12c52"}, - {file = "pyzmq-26.1.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:ec8fe214fcc45dfb0c32e4a7ad1db20244ba2d2fecbf0cbf9d5242d81ca0a375"}, - {file = "pyzmq-26.1.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cf4e283f97688d993cb7a8acbc22889effbbb7cbaa19ee9709751f44be928f5d"}, - {file = "pyzmq-26.1.1-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2508bdc8ab246e5ed7c92023d4352aaad63020ca3b098a4e3f1822db202f703d"}, - {file = "pyzmq-26.1.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:741bdb4d96efe8192616abdc3671931d51a8bcd38c71da2d53fb3127149265d1"}, - {file = "pyzmq-26.1.1-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:76154943e4c4054b2591792eb3484ef1dd23d59805759f9cebd2f010aa30ee8c"}, - {file = "pyzmq-26.1.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:9498ac427d20d0e0ef0e4bbd6200841e91640dfdf619f544ceec7f464cfb6070"}, - {file = "pyzmq-26.1.1-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:6f34453ef3496ca3462f30435bf85f535f9550392987341f9ccc92c102825a79"}, - {file = "pyzmq-26.1.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:50f0669324e27cc2091ef6ab76ca7112f364b6249691790b4cffce31e73fda28"}, - {file = "pyzmq-26.1.1-cp312-cp312-win32.whl", hash = "sha256:3ee5cbf2625b94de21c68d0cefd35327c8dfdbd6a98fcc41682b4e8bb00d841f"}, - {file = "pyzmq-26.1.1-cp312-cp312-win_amd64.whl", hash = "sha256:75bd448a28b1001b6928679015bc95dd5f172703ed30135bb9e34fc9cda0a3e7"}, - {file = "pyzmq-26.1.1-cp312-cp312-win_arm64.whl", hash = "sha256:4350233569b4bbef88595c5e77ee38995a6f1f1790fae148b578941bfffd1c24"}, - {file = "pyzmq-26.1.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:6c8087a3281c20b1d11042d372ed5a47734af05975d78e4d1d6e7bd1018535f3"}, - {file = "pyzmq-26.1.1-cp313-cp313-macosx_10_15_universal2.whl", hash = "sha256:ebef7d3fe11fe4c688f08bc0211a976c3318c097057f258428200737b9fff4da"}, - {file = "pyzmq-26.1.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7a5342110510045a47de1e87f5f1dcc1d9d90109522316dc9830cfc6157c800f"}, - {file = "pyzmq-26.1.1-cp313-cp313-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:af690ea4be6ca92a67c2b44a779a023bf0838e92d48497a2268175dc4a505691"}, - {file = "pyzmq-26.1.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bc994e220c1403ae087d7f0fa45129d583e46668a019e389060da811a5a9320e"}, - {file = "pyzmq-26.1.1-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:b8e153f5dffb0310af71fc6fc9cd8174f4c8ea312c415adcb815d786fee78179"}, - {file = "pyzmq-26.1.1-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:0065026e624052a51033857e5cd45a94b52946b44533f965f0bdf182460e965d"}, - {file = "pyzmq-26.1.1-cp313-cp313-musllinux_1_1_i686.whl", hash = "sha256:63351392f948b5d50b9f55161994bc4feedbfb3f3cfe393d2f503dea2c3ec445"}, - {file = "pyzmq-26.1.1-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:ffecc43b3c18e36b62fcec995761829b6ac325d8dd74a4f2c5c1653afbb4495a"}, - {file = "pyzmq-26.1.1-cp313-cp313-win32.whl", hash = "sha256:6ff14c2fae6c0c2c1c02590c5c5d75aa1db35b859971b3ca2fcd28f983d9f2b6"}, - {file = "pyzmq-26.1.1-cp313-cp313-win_amd64.whl", hash = "sha256:85f2d2ee5ea9a8f1de86a300e1062fbab044f45b5ce34d20580c0198a8196db0"}, - {file = "pyzmq-26.1.1-cp313-cp313-win_arm64.whl", hash = "sha256:cc09b1de8b985ca5a0ca343dd7fb007267c6b329347a74e200f4654268084239"}, - {file = "pyzmq-26.1.1-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:bc904e86de98f8fc5bd41597da5d61232d2d6d60c4397f26efffabb961b2b245"}, - {file = "pyzmq-26.1.1-cp313-cp313t-macosx_10_15_universal2.whl", hash = "sha256:00f39c367bbd6aa8e4bc36af6510561944c619b58eb36199fa334b594a18f615"}, - {file = "pyzmq-26.1.1-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:de6f384864a959866b782e6a3896538d1424d183f2d3c7ef079f71dcecde7284"}, - {file = "pyzmq-26.1.1-cp313-cp313t-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3abb15df0c763339edb27a644c19381b2425ddd1aea3dbd77c1601a3b31867b8"}, - {file = "pyzmq-26.1.1-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:40908ec2dd3b29bbadc0916a0d3c87f8dbeebbd8fead8e618539f09e0506dec4"}, - {file = "pyzmq-26.1.1-cp313-cp313t-manylinux_2_28_x86_64.whl", hash = "sha256:c11a95d3f6fc7e714ccd1066f68f9c1abd764a8b3596158be92f46dd49f41e03"}, - {file = "pyzmq-26.1.1-cp313-cp313t-musllinux_1_1_aarch64.whl", hash = "sha256:4437af9fee7a58302dbd511cc49f0cc2b35c112a33a1111fb123cf0be45205ca"}, - {file = "pyzmq-26.1.1-cp313-cp313t-musllinux_1_1_i686.whl", hash = "sha256:76390d3d66406cb01b9681c382874400e9dfd77f30ecdea4bd1bf5226dd4aff0"}, - {file = "pyzmq-26.1.1-cp313-cp313t-musllinux_1_1_x86_64.whl", hash = "sha256:4d4c7fe5e50e269f9c63a260638488fec194a73993008618a59b54c47ef6ae72"}, - {file = "pyzmq-26.1.1-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:25d128524207f53f7aae7c5abdc2b63f8957a060b00521af5ffcd20986b5d8f4"}, - {file = "pyzmq-26.1.1-cp37-cp37m-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:d74b925d997e4f92b042bdd7085cd0a309ee0fd7cb4dc376059bbff6b32ff34f"}, - {file = "pyzmq-26.1.1-cp37-cp37m-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:732f957441e5b1c65a7509395e6b6cafee9e12df9aa5f4bf92ed266fe0ba70ee"}, - {file = "pyzmq-26.1.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f0a45102ad7ed9f9ddf2bd699cc5df37742cf7301111cba06001b927efecb120"}, - {file = "pyzmq-26.1.1-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:9f380d5333fc7cd17423f486125dcc073918676e33db70a6a8172b19fc78d23d"}, - {file = "pyzmq-26.1.1-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:8eaffcd6bf6a9d00b66a2052a33fa7e6a6575427e9644395f13c3d070f2918dc"}, - {file = "pyzmq-26.1.1-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:f1483d4975ae1b387b39bb8e23d1ff32fe5621aa9e4ed3055d05e9c5613fea53"}, - {file = "pyzmq-26.1.1-cp37-cp37m-win32.whl", hash = "sha256:a83653c6bbe5887caea55e49fbd2909c14b73acf43bcc051eb60b2d514bbd46e"}, - {file = "pyzmq-26.1.1-cp37-cp37m-win_amd64.whl", hash = "sha256:9763a8d3f5f74ef679989b373c37cc22e8d07e56d26439205cb83edb7722357f"}, - {file = "pyzmq-26.1.1-cp38-cp38-macosx_10_15_universal2.whl", hash = "sha256:2b045647caf620ce0ed6c8fd9fb6a73116f99aceed966b152a5ba1b416d25311"}, - {file = "pyzmq-26.1.1-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:f66dcb6625c002f209cdc12cae1a1fec926493cd2262efe37dc6b25a30cea863"}, - {file = "pyzmq-26.1.1-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:0cf1d980c969fb9e538f52abd2227f09e015096bc5c3ef7aa26e0d64051c1db8"}, - {file = "pyzmq-26.1.1-cp38-cp38-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:443ebf5e261a95ee9725693f2a5a71401f89b89df0e0ea58844b074067aac2f1"}, - {file = "pyzmq-26.1.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:29de77ba1b1877fe7defc1b9140e65cbd35f72a63bc501e56c2eae55bde5fff4"}, - {file = "pyzmq-26.1.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:2f6071ec95af145d7b659dae6786871cd85f0acc599286b6f8ba0c74592d83dd"}, - {file = "pyzmq-26.1.1-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:6f0512fc87629ad968889176bf2165d721cd817401a281504329e2a2ed0ca6a3"}, - {file = "pyzmq-26.1.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:5ccfcf13e80719f6a2d9c0a021d9e47d4550907a29253554be2c09582f6d7963"}, - {file = "pyzmq-26.1.1-cp38-cp38-win32.whl", hash = "sha256:809673947e95752e407aaaaf03f205ee86ebfff9ca51db6d4003dfd87b8428d1"}, - {file = "pyzmq-26.1.1-cp38-cp38-win_amd64.whl", hash = "sha256:62b5180e23e6f581600459cd983473cd723fdc64350f606d21407c99832aaf5f"}, - {file = "pyzmq-26.1.1-cp39-cp39-macosx_10_15_universal2.whl", hash = "sha256:fe73d7c89d6f803bed122135ff5783364e8cdb479cf6fe2d764a44b6349e7e0f"}, - {file = "pyzmq-26.1.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:db1b7e2b50ef21f398036786da4c153db63203a402396d9f21e08ea61f3f8dba"}, - {file = "pyzmq-26.1.1-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:7c506a51cb01bb997a3f6440db0d121e5e7a32396e9948b1fdb6a7bfa67243f4"}, - {file = "pyzmq-26.1.1-cp39-cp39-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:92eca4f80e8a748d880e55d3cf57ef487692e439f12d5c5a2e1cce84aaa7f6cb"}, - {file = "pyzmq-26.1.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:14bdbae02f72f4716b0ffe7500e9da303d719ddde1f3dcfb4c4f6cc1cf73bb02"}, - {file = "pyzmq-26.1.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:e03be7ed17836c9434cce0668ac1e2cc9143d7169f90f46a0167f6155e176e32"}, - {file = "pyzmq-26.1.1-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:bc5df31e36e4fddd4c8b5c42daee8d54d7b529e898ac984be97bf5517de166a7"}, - {file = "pyzmq-26.1.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:f218179c90a12d660906e04b25a340dd63e9743000ba16232ddaf46888f269da"}, - {file = "pyzmq-26.1.1-cp39-cp39-win32.whl", hash = "sha256:7dfabc180a4da422a4b349c63077347392463a75fa07aa3be96712ed6d42c547"}, - {file = "pyzmq-26.1.1-cp39-cp39-win_amd64.whl", hash = "sha256:c5248e6e0fcbbbc912982e99cdd51c342601f495b0fa5bd667f3bdbdbf3e170f"}, - {file = "pyzmq-26.1.1-cp39-cp39-win_arm64.whl", hash = "sha256:2ae7aa1408778dc74582a1226052b930f9083b54b64d7e6ef6ec0466cfdcdec2"}, - {file = "pyzmq-26.1.1-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:be3fc2b11c0c384949cf1f01f9a48555039408b0f3e877863b1754225635953e"}, - {file = "pyzmq-26.1.1-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:48dee75c2a9fa4f4a583d4028d564a0453447ee1277a29b07acc3743c092e259"}, - {file = "pyzmq-26.1.1-pp310-pypy310_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:23f2fe4fb567e8098ebaa7204819658195b10ddd86958a97a6058eed2901eed3"}, - {file = "pyzmq-26.1.1-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:472cacd16f627c06d3c8b2d374345ab74446bae913584a6245e2aa935336d929"}, - {file = "pyzmq-26.1.1-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:8285b25aa20fcc46f1ca4afbc39fd3d5f2fe4c4bbf7f2c7f907a214e87a70024"}, - {file = "pyzmq-26.1.1-pp37-pypy37_pp73-macosx_10_9_x86_64.whl", hash = "sha256:2067e63fd9d5c13cfe12624dab0366053e523b37a7a01678ce4321f839398939"}, - {file = "pyzmq-26.1.1-pp37-pypy37_pp73-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:cc109be2ee3638035d276e18eaf66a1e1f44201c0c4bea4ee0c692766bbd3570"}, - {file = "pyzmq-26.1.1-pp37-pypy37_pp73-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:d0da97e65ee73261dba70469cc8f63d8da3a8a825337a2e3d246b9e95141cdd0"}, - {file = "pyzmq-26.1.1-pp37-pypy37_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:aa79c528706561306938b275f89bb2c6985ce08469c27e5de05bc680df5e826f"}, - {file = "pyzmq-26.1.1-pp37-pypy37_pp73-win_amd64.whl", hash = "sha256:3ddbd851a3a2651fdc5065a2804d50cf2f4b13b1bcd66de8e9e855d0217d4fcd"}, - {file = "pyzmq-26.1.1-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:d3df226ab7464684ae6706e20a5cbab717c3735a7e409b3fa598b754d49f1946"}, - {file = "pyzmq-26.1.1-pp38-pypy38_pp73-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:abad7b897e960d577eb4a0f3f789c1780bc3ffe2e7c27cf317e7c90ad26acf12"}, - {file = "pyzmq-26.1.1-pp38-pypy38_pp73-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:c513d829a548c2d5c88983167be2b3aa537f6d1191edcdc6fcd8999e18bdd994"}, - {file = "pyzmq-26.1.1-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:70af4c9c991714ef1c65957605a8de42ef0d0620dd5f125953c8e682281bdb80"}, - {file = "pyzmq-26.1.1-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:8d4234f335b0d0842f7d661d8cd50cbad0729be58f1c4deb85cd96b38fe95025"}, - {file = "pyzmq-26.1.1-pp39-pypy39_pp73-macosx_10_15_x86_64.whl", hash = "sha256:2c0fdb7b758e0e1605157e480b00b3a599073068a37091a1c75ec65bf7498645"}, - {file = "pyzmq-26.1.1-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:fc657577f057d60dd3642c9f95f28b432889b73143140061f7c1331d02f03df6"}, - {file = "pyzmq-26.1.1-pp39-pypy39_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3e3b66fe6131b4f33d239f7d4c3bfb2f8532d8644bae3b3da4f3987073edac55"}, - {file = "pyzmq-26.1.1-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:59b57e912feef6951aec8bb03fe0faa5ad5f36962883c72a30a9c965e6d988fd"}, - {file = "pyzmq-26.1.1-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:146956aec7d947c5afc5e7da0841423d7a53f84fd160fff25e682361dcfb32cb"}, - {file = "pyzmq-26.1.1-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:9521b874fd489495865172f344e46e0159095d1f161858e3fc6e28e43ca15160"}, - {file = "pyzmq-26.1.1.tar.gz", hash = "sha256:a7db05d8b7cd1a8c6610e9e9aa55d525baae7a44a43e18bc3260eb3f92de96c6"}, + {file = "pyzmq-26.2.0-cp310-cp310-macosx_10_15_universal2.whl", hash = "sha256:ddf33d97d2f52d89f6e6e7ae66ee35a4d9ca6f36eda89c24591b0c40205a3629"}, + {file = "pyzmq-26.2.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:dacd995031a01d16eec825bf30802fceb2c3791ef24bcce48fa98ce40918c27b"}, + {file = "pyzmq-26.2.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:89289a5ee32ef6c439086184529ae060c741334b8970a6855ec0b6ad3ff28764"}, + {file = "pyzmq-26.2.0-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5506f06d7dc6ecf1efacb4a013b1f05071bb24b76350832c96449f4a2d95091c"}, + {file = "pyzmq-26.2.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8ea039387c10202ce304af74def5021e9adc6297067f3441d348d2b633e8166a"}, + {file = "pyzmq-26.2.0-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:a2224fa4a4c2ee872886ed00a571f5e967c85e078e8e8c2530a2fb01b3309b88"}, + {file = "pyzmq-26.2.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:28ad5233e9c3b52d76196c696e362508959741e1a005fb8fa03b51aea156088f"}, + {file = "pyzmq-26.2.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:1c17211bc037c7d88e85ed8b7d8f7e52db6dc8eca5590d162717c654550f7282"}, + {file = "pyzmq-26.2.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:b8f86dd868d41bea9a5f873ee13bf5551c94cf6bc51baebc6f85075971fe6eea"}, + {file = "pyzmq-26.2.0-cp310-cp310-win32.whl", hash = "sha256:46a446c212e58456b23af260f3d9fb785054f3e3653dbf7279d8f2b5546b21c2"}, + {file = "pyzmq-26.2.0-cp310-cp310-win_amd64.whl", hash = "sha256:49d34ab71db5a9c292a7644ce74190b1dd5a3475612eefb1f8be1d6961441971"}, + {file = "pyzmq-26.2.0-cp310-cp310-win_arm64.whl", hash = "sha256:bfa832bfa540e5b5c27dcf5de5d82ebc431b82c453a43d141afb1e5d2de025fa"}, + {file = "pyzmq-26.2.0-cp311-cp311-macosx_10_15_universal2.whl", hash = "sha256:8f7e66c7113c684c2b3f1c83cdd3376103ee0ce4c49ff80a648643e57fb22218"}, + {file = "pyzmq-26.2.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:3a495b30fc91db2db25120df5847d9833af237546fd59170701acd816ccc01c4"}, + {file = "pyzmq-26.2.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:77eb0968da535cba0470a5165468b2cac7772cfb569977cff92e240f57e31bef"}, + {file = "pyzmq-26.2.0-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6ace4f71f1900a548f48407fc9be59c6ba9d9aaf658c2eea6cf2779e72f9f317"}, + {file = "pyzmq-26.2.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:92a78853d7280bffb93df0a4a6a2498cba10ee793cc8076ef797ef2f74d107cf"}, + {file = "pyzmq-26.2.0-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:689c5d781014956a4a6de61d74ba97b23547e431e9e7d64f27d4922ba96e9d6e"}, + {file = "pyzmq-26.2.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:0aca98bc423eb7d153214b2df397c6421ba6373d3397b26c057af3c904452e37"}, + {file = "pyzmq-26.2.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:1f3496d76b89d9429a656293744ceca4d2ac2a10ae59b84c1da9b5165f429ad3"}, + {file = "pyzmq-26.2.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:5c2b3bfd4b9689919db068ac6c9911f3fcb231c39f7dd30e3138be94896d18e6"}, + {file = "pyzmq-26.2.0-cp311-cp311-win32.whl", hash = "sha256:eac5174677da084abf378739dbf4ad245661635f1600edd1221f150b165343f4"}, + {file = "pyzmq-26.2.0-cp311-cp311-win_amd64.whl", hash = "sha256:5a509df7d0a83a4b178d0f937ef14286659225ef4e8812e05580776c70e155d5"}, + {file = "pyzmq-26.2.0-cp311-cp311-win_arm64.whl", hash = "sha256:c0e6091b157d48cbe37bd67233318dbb53e1e6327d6fc3bb284afd585d141003"}, + {file = "pyzmq-26.2.0-cp312-cp312-macosx_10_15_universal2.whl", hash = "sha256:ded0fc7d90fe93ae0b18059930086c51e640cdd3baebdc783a695c77f123dcd9"}, + {file = "pyzmq-26.2.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:17bf5a931c7f6618023cdacc7081f3f266aecb68ca692adac015c383a134ca52"}, + {file = "pyzmq-26.2.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:55cf66647e49d4621a7e20c8d13511ef1fe1efbbccf670811864452487007e08"}, + {file = "pyzmq-26.2.0-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4661c88db4a9e0f958c8abc2b97472e23061f0bc737f6f6179d7a27024e1faa5"}, + {file = "pyzmq-26.2.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ea7f69de383cb47522c9c208aec6dd17697db7875a4674c4af3f8cfdac0bdeae"}, + {file = "pyzmq-26.2.0-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:7f98f6dfa8b8ccaf39163ce872bddacca38f6a67289116c8937a02e30bbe9711"}, + {file = "pyzmq-26.2.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:e3e0210287329272539eea617830a6a28161fbbd8a3271bf4150ae3e58c5d0e6"}, + {file = "pyzmq-26.2.0-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:6b274e0762c33c7471f1a7471d1a2085b1a35eba5cdc48d2ae319f28b6fc4de3"}, + {file = "pyzmq-26.2.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:29c6a4635eef69d68a00321e12a7d2559fe2dfccfa8efae3ffb8e91cd0b36a8b"}, + {file = "pyzmq-26.2.0-cp312-cp312-win32.whl", hash = "sha256:989d842dc06dc59feea09e58c74ca3e1678c812a4a8a2a419046d711031f69c7"}, + {file = "pyzmq-26.2.0-cp312-cp312-win_amd64.whl", hash = "sha256:2a50625acdc7801bc6f74698c5c583a491c61d73c6b7ea4dee3901bb99adb27a"}, + {file = "pyzmq-26.2.0-cp312-cp312-win_arm64.whl", hash = "sha256:4d29ab8592b6ad12ebbf92ac2ed2bedcfd1cec192d8e559e2e099f648570e19b"}, + {file = "pyzmq-26.2.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:9dd8cd1aeb00775f527ec60022004d030ddc51d783d056e3e23e74e623e33726"}, + {file = "pyzmq-26.2.0-cp313-cp313-macosx_10_15_universal2.whl", hash = "sha256:28c812d9757fe8acecc910c9ac9dafd2ce968c00f9e619db09e9f8f54c3a68a3"}, + {file = "pyzmq-26.2.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4d80b1dd99c1942f74ed608ddb38b181b87476c6a966a88a950c7dee118fdf50"}, + {file = "pyzmq-26.2.0-cp313-cp313-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8c997098cc65e3208eca09303630e84d42718620e83b733d0fd69543a9cab9cb"}, + {file = "pyzmq-26.2.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7ad1bc8d1b7a18497dda9600b12dc193c577beb391beae5cd2349184db40f187"}, + {file = "pyzmq-26.2.0-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:bea2acdd8ea4275e1278350ced63da0b166421928276c7c8e3f9729d7402a57b"}, + {file = "pyzmq-26.2.0-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:23f4aad749d13698f3f7b64aad34f5fc02d6f20f05999eebc96b89b01262fb18"}, + {file = "pyzmq-26.2.0-cp313-cp313-musllinux_1_1_i686.whl", hash = "sha256:a4f96f0d88accc3dbe4a9025f785ba830f968e21e3e2c6321ccdfc9aef755115"}, + {file = "pyzmq-26.2.0-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:ced65e5a985398827cc9276b93ef6dfabe0273c23de8c7931339d7e141c2818e"}, + {file = "pyzmq-26.2.0-cp313-cp313-win32.whl", hash = "sha256:31507f7b47cc1ead1f6e86927f8ebb196a0bab043f6345ce070f412a59bf87b5"}, + {file = "pyzmq-26.2.0-cp313-cp313-win_amd64.whl", hash = "sha256:70fc7fcf0410d16ebdda9b26cbd8bf8d803d220a7f3522e060a69a9c87bf7bad"}, + {file = "pyzmq-26.2.0-cp313-cp313-win_arm64.whl", hash = "sha256:c3789bd5768ab5618ebf09cef6ec2b35fed88709b104351748a63045f0ff9797"}, + {file = "pyzmq-26.2.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:034da5fc55d9f8da09015d368f519478a52675e558c989bfcb5cf6d4e16a7d2a"}, + {file = "pyzmq-26.2.0-cp313-cp313t-macosx_10_15_universal2.whl", hash = "sha256:c92d73464b886931308ccc45b2744e5968cbaade0b1d6aeb40d8ab537765f5bc"}, + {file = "pyzmq-26.2.0-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:794a4562dcb374f7dbbfb3f51d28fb40123b5a2abadee7b4091f93054909add5"}, + {file = "pyzmq-26.2.0-cp313-cp313t-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:aee22939bb6075e7afededabad1a56a905da0b3c4e3e0c45e75810ebe3a52672"}, + {file = "pyzmq-26.2.0-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2ae90ff9dad33a1cfe947d2c40cb9cb5e600d759ac4f0fd22616ce6540f72797"}, + {file = "pyzmq-26.2.0-cp313-cp313t-manylinux_2_28_x86_64.whl", hash = "sha256:43a47408ac52647dfabbc66a25b05b6a61700b5165807e3fbd40063fcaf46386"}, + {file = "pyzmq-26.2.0-cp313-cp313t-musllinux_1_1_aarch64.whl", hash = "sha256:25bf2374a2a8433633c65ccb9553350d5e17e60c8eb4de4d92cc6bd60f01d306"}, + {file = "pyzmq-26.2.0-cp313-cp313t-musllinux_1_1_i686.whl", hash = "sha256:007137c9ac9ad5ea21e6ad97d3489af654381324d5d3ba614c323f60dab8fae6"}, + {file = "pyzmq-26.2.0-cp313-cp313t-musllinux_1_1_x86_64.whl", hash = "sha256:470d4a4f6d48fb34e92d768b4e8a5cc3780db0d69107abf1cd7ff734b9766eb0"}, + {file = "pyzmq-26.2.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:3b55a4229ce5da9497dd0452b914556ae58e96a4381bb6f59f1305dfd7e53fc8"}, + {file = "pyzmq-26.2.0-cp37-cp37m-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:9cb3a6460cdea8fe8194a76de8895707e61ded10ad0be97188cc8463ffa7e3a8"}, + {file = "pyzmq-26.2.0-cp37-cp37m-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:8ab5cad923cc95c87bffee098a27856c859bd5d0af31bd346035aa816b081fe1"}, + {file = "pyzmq-26.2.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9ed69074a610fad1c2fda66180e7b2edd4d31c53f2d1872bc2d1211563904cd9"}, + {file = "pyzmq-26.2.0-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:cccba051221b916a4f5e538997c45d7d136a5646442b1231b916d0164067ea27"}, + {file = "pyzmq-26.2.0-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:0eaa83fc4c1e271c24eaf8fb083cbccef8fde77ec8cd45f3c35a9a123e6da097"}, + {file = "pyzmq-26.2.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:9edda2df81daa129b25a39b86cb57dfdfe16f7ec15b42b19bfac503360d27a93"}, + {file = "pyzmq-26.2.0-cp37-cp37m-win32.whl", hash = "sha256:ea0eb6af8a17fa272f7b98d7bebfab7836a0d62738e16ba380f440fceca2d951"}, + {file = "pyzmq-26.2.0-cp37-cp37m-win_amd64.whl", hash = "sha256:4ff9dc6bc1664bb9eec25cd17506ef6672d506115095411e237d571e92a58231"}, + {file = "pyzmq-26.2.0-cp38-cp38-macosx_10_15_universal2.whl", hash = "sha256:2eb7735ee73ca1b0d71e0e67c3739c689067f055c764f73aac4cc8ecf958ee3f"}, + {file = "pyzmq-26.2.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:1a534f43bc738181aa7cbbaf48e3eca62c76453a40a746ab95d4b27b1111a7d2"}, + {file = "pyzmq-26.2.0-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:aedd5dd8692635813368e558a05266b995d3d020b23e49581ddd5bbe197a8ab6"}, + {file = "pyzmq-26.2.0-cp38-cp38-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:8be4700cd8bb02cc454f630dcdf7cfa99de96788b80c51b60fe2fe1dac480289"}, + {file = "pyzmq-26.2.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1fcc03fa4997c447dce58264e93b5aa2d57714fbe0f06c07b7785ae131512732"}, + {file = "pyzmq-26.2.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:402b190912935d3db15b03e8f7485812db350d271b284ded2b80d2e5704be780"}, + {file = "pyzmq-26.2.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:8685fa9c25ff00f550c1fec650430c4b71e4e48e8d852f7ddcf2e48308038640"}, + {file = "pyzmq-26.2.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:76589c020680778f06b7e0b193f4b6dd66d470234a16e1df90329f5e14a171cd"}, + {file = "pyzmq-26.2.0-cp38-cp38-win32.whl", hash = "sha256:8423c1877d72c041f2c263b1ec6e34360448decfb323fa8b94e85883043ef988"}, + {file = "pyzmq-26.2.0-cp38-cp38-win_amd64.whl", hash = "sha256:76589f2cd6b77b5bdea4fca5992dc1c23389d68b18ccc26a53680ba2dc80ff2f"}, + {file = "pyzmq-26.2.0-cp39-cp39-macosx_10_15_universal2.whl", hash = "sha256:b1d464cb8d72bfc1a3adc53305a63a8e0cac6bc8c5a07e8ca190ab8d3faa43c2"}, + {file = "pyzmq-26.2.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:4da04c48873a6abdd71811c5e163bd656ee1b957971db7f35140a2d573f6949c"}, + {file = "pyzmq-26.2.0-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:d049df610ac811dcffdc147153b414147428567fbbc8be43bb8885f04db39d98"}, + {file = "pyzmq-26.2.0-cp39-cp39-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:05590cdbc6b902101d0e65d6a4780af14dc22914cc6ab995d99b85af45362cc9"}, + {file = "pyzmq-26.2.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c811cfcd6a9bf680236c40c6f617187515269ab2912f3d7e8c0174898e2519db"}, + {file = "pyzmq-26.2.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:6835dd60355593de10350394242b5757fbbd88b25287314316f266e24c61d073"}, + {file = "pyzmq-26.2.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:bc6bee759a6bddea5db78d7dcd609397449cb2d2d6587f48f3ca613b19410cfc"}, + {file = "pyzmq-26.2.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:c530e1eecd036ecc83c3407f77bb86feb79916d4a33d11394b8234f3bd35b940"}, + {file = "pyzmq-26.2.0-cp39-cp39-win32.whl", hash = "sha256:367b4f689786fca726ef7a6c5ba606958b145b9340a5e4808132cc65759abd44"}, + {file = "pyzmq-26.2.0-cp39-cp39-win_amd64.whl", hash = "sha256:e6fa2e3e683f34aea77de8112f6483803c96a44fd726d7358b9888ae5bb394ec"}, + {file = "pyzmq-26.2.0-cp39-cp39-win_arm64.whl", hash = "sha256:7445be39143a8aa4faec43b076e06944b8f9d0701b669df4af200531b21e40bb"}, + {file = "pyzmq-26.2.0-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:706e794564bec25819d21a41c31d4df2d48e1cc4b061e8d345d7fb4dd3e94072"}, + {file = "pyzmq-26.2.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8b435f2753621cd36e7c1762156815e21c985c72b19135dac43a7f4f31d28dd1"}, + {file = "pyzmq-26.2.0-pp310-pypy310_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:160c7e0a5eb178011e72892f99f918c04a131f36056d10d9c1afb223fc952c2d"}, + {file = "pyzmq-26.2.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2c4a71d5d6e7b28a47a394c0471b7e77a0661e2d651e7ae91e0cab0a587859ca"}, + {file = "pyzmq-26.2.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:90412f2db8c02a3864cbfc67db0e3dcdbda336acf1c469526d3e869394fe001c"}, + {file = "pyzmq-26.2.0-pp37-pypy37_pp73-macosx_10_9_x86_64.whl", hash = "sha256:2ea4ad4e6a12e454de05f2949d4beddb52460f3de7c8b9d5c46fbb7d7222e02c"}, + {file = "pyzmq-26.2.0-pp37-pypy37_pp73-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:fc4f7a173a5609631bb0c42c23d12c49df3966f89f496a51d3eb0ec81f4519d6"}, + {file = "pyzmq-26.2.0-pp37-pypy37_pp73-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:878206a45202247781472a2d99df12a176fef806ca175799e1c6ad263510d57c"}, + {file = "pyzmq-26.2.0-pp37-pypy37_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:17c412bad2eb9468e876f556eb4ee910e62d721d2c7a53c7fa31e643d35352e6"}, + {file = "pyzmq-26.2.0-pp37-pypy37_pp73-win_amd64.whl", hash = "sha256:0d987a3ae5a71c6226b203cfd298720e0086c7fe7c74f35fa8edddfbd6597eed"}, + {file = "pyzmq-26.2.0-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:39887ac397ff35b7b775db7201095fc6310a35fdbae85bac4523f7eb3b840e20"}, + {file = "pyzmq-26.2.0-pp38-pypy38_pp73-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:fdb5b3e311d4d4b0eb8b3e8b4d1b0a512713ad7e6a68791d0923d1aec433d919"}, + {file = "pyzmq-26.2.0-pp38-pypy38_pp73-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:226af7dcb51fdb0109f0016449b357e182ea0ceb6b47dfb5999d569e5db161d5"}, + {file = "pyzmq-26.2.0-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0bed0e799e6120b9c32756203fb9dfe8ca2fb8467fed830c34c877e25638c3fc"}, + {file = "pyzmq-26.2.0-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:29c7947c594e105cb9e6c466bace8532dc1ca02d498684128b339799f5248277"}, + {file = "pyzmq-26.2.0-pp39-pypy39_pp73-macosx_10_15_x86_64.whl", hash = "sha256:cdeabcff45d1c219636ee2e54d852262e5c2e085d6cb476d938aee8d921356b3"}, + {file = "pyzmq-26.2.0-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:35cffef589bcdc587d06f9149f8d5e9e8859920a071df5a2671de2213bef592a"}, + {file = "pyzmq-26.2.0-pp39-pypy39_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:18c8dc3b7468d8b4bdf60ce9d7141897da103c7a4690157b32b60acb45e333e6"}, + {file = "pyzmq-26.2.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7133d0a1677aec369d67dd78520d3fa96dd7f3dcec99d66c1762870e5ea1a50a"}, + {file = "pyzmq-26.2.0-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:6a96179a24b14fa6428cbfc08641c779a53f8fcec43644030328f44034c7f1f4"}, + {file = "pyzmq-26.2.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:4f78c88905461a9203eac9faac157a2a0dbba84a0fd09fd29315db27be40af9f"}, + {file = "pyzmq-26.2.0.tar.gz", hash = "sha256:070672c258581c8e4f640b5159297580a9974b026043bd4ab0470be9ed324f1f"}, ] [package.dependencies] @@ -2897,13 +2904,13 @@ tests = ["coverage (>=6.0.0)", "flake8", "mypy", "pytest (>=7.0.0)", "pytest-asy [[package]] name = "rich" -version = "13.7.1" +version = "13.8.0" description = "Render rich text, tables, progress bars, syntax highlighting, markdown and more to the terminal" optional = false python-versions = ">=3.7.0" files = [ - {file = "rich-13.7.1-py3-none-any.whl", hash = "sha256:4edbae314f59eb482f54e9e30bf00d33350aaa94f4bfcd4e9e3110e64d0d7222"}, - {file = "rich-13.7.1.tar.gz", hash = "sha256:9be308cb1fe2f1f57d67ce99e95af38a1e2bc71ad9813b0e247cf7ffbcc3a432"}, + {file = "rich-13.8.0-py3-none-any.whl", hash = "sha256:2e85306a063b9492dffc86278197a60cbece75bcb766022f3436f567cae11bdc"}, + {file = "rich-13.8.0.tar.gz", hash = "sha256:a5ac1f1cd448ade0d59cc3356f7db7a7ccda2c8cbae9c7a90c28ff463d3e91f4"}, ] [package.dependencies] @@ -3203,13 +3210,13 @@ test = ["pytest", "tornado (>=4.5)", "typeguard"] [[package]] name = "tifffile" -version = "2024.8.10" +version = "2024.8.28" description = "Read and write TIFF files" optional = false python-versions = ">=3.9" files = [ - {file = "tifffile-2024.8.10-py3-none-any.whl", hash = "sha256:1c224564fa92e7e9f9a0ed65880b2ece97c3f0d10029ffbebfa5e62b3f6b343d"}, - {file = "tifffile-2024.8.10.tar.gz", hash = "sha256:fdc12124f1478a07b1524641dc6b50cf6bde0483011a63fd2a773094090c3dcf"}, + {file = "tifffile-2024.8.28-py3-none-any.whl", hash = "sha256:7b618100c9043f2b7f196287ae5e9d139a6f4a0fa603d3698fd5b8f679ca2c66"}, + {file = "tifffile-2024.8.28.tar.gz", hash = "sha256:a84056c02865d20090235816cddabc99a34493e870effcd119499548583f3204"}, ] [package.dependencies] @@ -3525,46 +3532,41 @@ test = ["covdefaults (>=2.3)", "coverage (>=7.2.7)", "coverage-enable-subprocess [[package]] name = "watchdog" -version = "4.0.2" +version = "5.0.0" description = "Filesystem events monitoring" optional = false -python-versions = ">=3.8" +python-versions = ">=3.9" files = [ - {file = "watchdog-4.0.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:ede7f010f2239b97cc79e6cb3c249e72962404ae3865860855d5cbe708b0fd22"}, - {file = "watchdog-4.0.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:a2cffa171445b0efa0726c561eca9a27d00a1f2b83846dbd5a4f639c4f8ca8e1"}, - {file = "watchdog-4.0.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c50f148b31b03fbadd6d0b5980e38b558046b127dc483e5e4505fcef250f9503"}, - {file = "watchdog-4.0.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:7c7d4bf585ad501c5f6c980e7be9c4f15604c7cc150e942d82083b31a7548930"}, - {file = "watchdog-4.0.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:914285126ad0b6eb2258bbbcb7b288d9dfd655ae88fa28945be05a7b475a800b"}, - {file = "watchdog-4.0.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:984306dc4720da5498b16fc037b36ac443816125a3705dfde4fd90652d8028ef"}, - {file = "watchdog-4.0.2-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:1cdcfd8142f604630deef34722d695fb455d04ab7cfe9963055df1fc69e6727a"}, - {file = "watchdog-4.0.2-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:d7ab624ff2f663f98cd03c8b7eedc09375a911794dfea6bf2a359fcc266bff29"}, - {file = "watchdog-4.0.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:132937547a716027bd5714383dfc40dc66c26769f1ce8a72a859d6a48f371f3a"}, - {file = "watchdog-4.0.2-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:cd67c7df93eb58f360c43802acc945fa8da70c675b6fa37a241e17ca698ca49b"}, - {file = "watchdog-4.0.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:bcfd02377be80ef3b6bc4ce481ef3959640458d6feaae0bd43dd90a43da90a7d"}, - {file = "watchdog-4.0.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:980b71510f59c884d684b3663d46e7a14b457c9611c481e5cef08f4dd022eed7"}, - {file = "watchdog-4.0.2-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:aa160781cafff2719b663c8a506156e9289d111d80f3387cf3af49cedee1f040"}, - {file = "watchdog-4.0.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:f6ee8dedd255087bc7fe82adf046f0b75479b989185fb0bdf9a98b612170eac7"}, - {file = "watchdog-4.0.2-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:0b4359067d30d5b864e09c8597b112fe0a0a59321a0f331498b013fb097406b4"}, - {file = "watchdog-4.0.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:770eef5372f146997638d737c9a3c597a3b41037cfbc5c41538fc27c09c3a3f9"}, - {file = "watchdog-4.0.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:eeea812f38536a0aa859972d50c76e37f4456474b02bd93674d1947cf1e39578"}, - {file = "watchdog-4.0.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:b2c45f6e1e57ebb4687690c05bc3a2c1fb6ab260550c4290b8abb1335e0fd08b"}, - {file = "watchdog-4.0.2-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:10b6683df70d340ac3279eff0b2766813f00f35a1d37515d2c99959ada8f05fa"}, - {file = "watchdog-4.0.2-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:f7c739888c20f99824f7aa9d31ac8a97353e22d0c0e54703a547a218f6637eb3"}, - {file = "watchdog-4.0.2-pp38-pypy38_pp73-macosx_10_9_x86_64.whl", hash = "sha256:c100d09ac72a8a08ddbf0629ddfa0b8ee41740f9051429baa8e31bb903ad7508"}, - {file = "watchdog-4.0.2-pp38-pypy38_pp73-macosx_11_0_arm64.whl", hash = "sha256:f5315a8c8dd6dd9425b974515081fc0aadca1d1d61e078d2246509fd756141ee"}, - {file = "watchdog-4.0.2-pp39-pypy39_pp73-macosx_10_15_x86_64.whl", hash = "sha256:2d468028a77b42cc685ed694a7a550a8d1771bb05193ba7b24006b8241a571a1"}, - {file = "watchdog-4.0.2-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:f15edcae3830ff20e55d1f4e743e92970c847bcddc8b7509bcd172aa04de506e"}, - {file = "watchdog-4.0.2-py3-none-manylinux2014_aarch64.whl", hash = "sha256:936acba76d636f70db8f3c66e76aa6cb5136a936fc2a5088b9ce1c7a3508fc83"}, - {file = "watchdog-4.0.2-py3-none-manylinux2014_armv7l.whl", hash = "sha256:e252f8ca942a870f38cf785aef420285431311652d871409a64e2a0a52a2174c"}, - {file = "watchdog-4.0.2-py3-none-manylinux2014_i686.whl", hash = "sha256:0e83619a2d5d436a7e58a1aea957a3c1ccbf9782c43c0b4fed80580e5e4acd1a"}, - {file = "watchdog-4.0.2-py3-none-manylinux2014_ppc64.whl", hash = "sha256:88456d65f207b39f1981bf772e473799fcdc10801062c36fd5ad9f9d1d463a73"}, - {file = "watchdog-4.0.2-py3-none-manylinux2014_ppc64le.whl", hash = "sha256:32be97f3b75693a93c683787a87a0dc8db98bb84701539954eef991fb35f5fbc"}, - {file = "watchdog-4.0.2-py3-none-manylinux2014_s390x.whl", hash = "sha256:c82253cfc9be68e3e49282831afad2c1f6593af80c0daf1287f6a92657986757"}, - {file = "watchdog-4.0.2-py3-none-manylinux2014_x86_64.whl", hash = "sha256:c0b14488bd336c5b1845cee83d3e631a1f8b4e9c5091ec539406e4a324f882d8"}, - {file = "watchdog-4.0.2-py3-none-win32.whl", hash = "sha256:0d8a7e523ef03757a5aa29f591437d64d0d894635f8a50f370fe37f913ce4e19"}, - {file = "watchdog-4.0.2-py3-none-win_amd64.whl", hash = "sha256:c344453ef3bf875a535b0488e3ad28e341adbd5a9ffb0f7d62cefacc8824ef2b"}, - {file = "watchdog-4.0.2-py3-none-win_ia64.whl", hash = "sha256:baececaa8edff42cd16558a639a9b0ddf425f93d892e8392a56bf904f5eff22c"}, - {file = "watchdog-4.0.2.tar.gz", hash = "sha256:b4dfbb6c49221be4535623ea4474a4d6ee0a9cef4a80b20c28db4d858b64e270"}, + {file = "watchdog-5.0.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:bf3216ec994eabb2212df9861f19056ca0d4cd3516d56cb95801933876519bfe"}, + {file = "watchdog-5.0.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:cb59ad83a1700304fc1ac7bc53ae9e5cbe9d60a52ed9bba8e2e2d782a201bb2b"}, + {file = "watchdog-5.0.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:1228cb097e855d1798b550be8f0e9f0cfbac4384f9a3e91f66d250d03e11294e"}, + {file = "watchdog-5.0.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:3c177085c3d210d1c73cb4569442bdaef706ebebc423bd7aed9e90fc12b2e553"}, + {file = "watchdog-5.0.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:01ab36cddc836a0f202c66267daaef92ba5c17c7d6436deff0587bb61234c5c9"}, + {file = "watchdog-5.0.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:0834c21efa3e767849b09e667274604c7cdfe30b49eb95d794565c53f4db3c1e"}, + {file = "watchdog-5.0.0-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:1e26f570dd7f5178656affb24d6f0e22ce66c8daf88d4061a27bfb9ac866b40d"}, + {file = "watchdog-5.0.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:d146331e6b206baa9f6dd40f72b5783ad2302c240df68e7fce196d30588ccf7b"}, + {file = "watchdog-5.0.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:6c96b1706430839872a3e33b9370ee3f7a0079f6b828129d88498ad1f96a0f45"}, + {file = "watchdog-5.0.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:663b096368ed7831ac42259919fdb9e0a1f0a8994d972675dfbcca0225e74de1"}, + {file = "watchdog-5.0.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:685931412978d00a91a193d9018fc9e394e565e8e7a0c275512a80e59c6e85f8"}, + {file = "watchdog-5.0.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:109daafc5b0f2a98d1fa9475ff9737eb3559d57b18129a36495e20c71de0b44f"}, + {file = "watchdog-5.0.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:c2b4d90962639ae7cee371ea3a8da506831945d4418eee090c53bc38e6648dc6"}, + {file = "watchdog-5.0.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:6e58eafe9cc5ceebe1562cdb89bacdcd0ef470896e8b0139fe677a5abec243da"}, + {file = "watchdog-5.0.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:b8d747bf6d8fe5ce89cb1a36c3724d1599bd4cde3f90fcba518e6260c7058a52"}, + {file = "watchdog-5.0.0-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:bc16d448a74a929b896ed9578c25756b2125400b19b3258be8d9a681c7ae8e71"}, + {file = "watchdog-5.0.0-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:7e6b0e9b8a9dc3865d65888b5f5222da4ba9c4e09eab13cff5e305e7b7e7248f"}, + {file = "watchdog-5.0.0-pp39-pypy39_pp73-macosx_10_15_x86_64.whl", hash = "sha256:4fe6780915000743074236b21b6c37419aea71112af62237881bc265589fe463"}, + {file = "watchdog-5.0.0-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:0710e9502727f688a7e06d48078545c54485b3d6eb53b171810879d8223c362a"}, + {file = "watchdog-5.0.0-py3-none-manylinux2014_aarch64.whl", hash = "sha256:d76efab5248aafbf8a2c2a63cd7b9545e6b346ad1397af8b862a3bb3140787d8"}, + {file = "watchdog-5.0.0-py3-none-manylinux2014_armv7l.whl", hash = "sha256:ff4e957c45c446de34c513eadce01d0b65da7eee47c01dce472dd136124552c9"}, + {file = "watchdog-5.0.0-py3-none-manylinux2014_i686.whl", hash = "sha256:16c1aa3377bb1f82c5e24277fcbf4e2cac3c4ce46aaaf7212d53caa9076eb7b7"}, + {file = "watchdog-5.0.0-py3-none-manylinux2014_ppc64.whl", hash = "sha256:22fcad6168fc43cf0e709bd854be5b8edbb0b260f0a6f28f1ea9baa53c6907f7"}, + {file = "watchdog-5.0.0-py3-none-manylinux2014_ppc64le.whl", hash = "sha256:0120b2fa65732797ffa65fa8ee5540c288aa861d91447df298626d6385a24658"}, + {file = "watchdog-5.0.0-py3-none-manylinux2014_s390x.whl", hash = "sha256:2aa59fab7ff75281778c649557275ca3085eccbdf825a0e2a5ca3810e977afe5"}, + {file = "watchdog-5.0.0-py3-none-manylinux2014_x86_64.whl", hash = "sha256:78db0fe0336958fc0e1269545c980b6f33d04d184ba191b2800a8b71d3e971a9"}, + {file = "watchdog-5.0.0-py3-none-win32.whl", hash = "sha256:d1acef802916083f2ad7988efc7decf07e46e266916c0a09d8fb9d387288ea12"}, + {file = "watchdog-5.0.0-py3-none-win_amd64.whl", hash = "sha256:3c2d50fdb86aa6df3973313272f5a17eb26eab29ff5a0bf54b6d34597b4dc4e4"}, + {file = "watchdog-5.0.0-py3-none-win_ia64.whl", hash = "sha256:1d17ec7e022c34fa7ddc72aa41bf28c9d1207ffb193df18ba4f6fde453725b3c"}, + {file = "watchdog-5.0.0.tar.gz", hash = "sha256:990aedb9e2f336b45a70aed9c014450e7c4a70fd99c5f5b1834d57e1453a177e"}, ] [package.extras] @@ -3583,20 +3585,24 @@ files = [ [[package]] name = "zipp" -version = "3.20.0" +version = "3.20.1" description = "Backport of pathlib-compatible object wrapper for zip files" optional = false python-versions = ">=3.8" files = [ - {file = "zipp-3.20.0-py3-none-any.whl", hash = "sha256:58da6168be89f0be59beb194da1250516fdaa062ccebd30127ac65d30045e10d"}, - {file = "zipp-3.20.0.tar.gz", hash = "sha256:0145e43d89664cfe1a2e533adc75adafed82fe2da404b4bbb6b026c0157bdb31"}, + {file = "zipp-3.20.1-py3-none-any.whl", hash = "sha256:9960cd8967c8f85a56f920d5d507274e74f9ff813a0ab8889a5b5be2daf44064"}, + {file = "zipp-3.20.1.tar.gz", hash = "sha256:c22b14cc4763c5a5b04134207736c107db42e9d3ef2d9779d465f5f1bcba572b"}, ] [package.extras] +check = ["pytest-checkdocs (>=2.4)", "pytest-ruff (>=0.2.1)"] +cover = ["pytest-cov"] doc = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-lint"] -test = ["big-O", "importlib-resources", "jaraco.functools", "jaraco.itertools", "jaraco.test", "more-itertools", "pytest (>=6,!=8.1.*)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-ignore-flaky", "pytest-mypy", "pytest-ruff (>=0.2.1)"] +enabler = ["pytest-enabler (>=2.2)"] +test = ["big-O", "importlib-resources", "jaraco.functools", "jaraco.itertools", "jaraco.test", "more-itertools", "pytest (>=6,!=8.1.*)", "pytest-ignore-flaky"] +type = ["pytest-mypy"] [metadata] lock-version = "2.0" python-versions = ">=3.9,<4.0" -content-hash = "6a0d9801f4f61b58809ba68589dcf916a47fef09e19688eeee4a4011467666d3" +content-hash = "c4ec995f30323a864a2acb3358161c5383c96404384562b1cff1446a54616973" diff --git a/pyproject.toml b/pyproject.toml index 4eb0cf82..474faa28 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -56,6 +56,7 @@ types-pillow = "^9.5.0.4" data-science-types = "^0.2.23" types-tqdm = "^4.65.0.1" setuptools = "^68.0.0" +griffe = "^0.45.3" mkdocs = "^1.5.3" mkdocstrings = {extras = ["python"], version = "^0.23.0"} mkdocs-material = "^9.4.2" From e6995534872d9c5b6d65db38aeeb4740c9033c19 Mon Sep 17 00:00:00 2001 From: GitHub Actions Bot Date: Fri, 30 Aug 2024 17:30:08 +0000 Subject: [PATCH 7/7] [skip ci] chore(release): vision-agent 0.2.120 --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 474faa28..9b946211 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "poetry.core.masonry.api" [tool.poetry] name = "vision-agent" -version = "0.2.119" +version = "0.2.120" description = "Toolset for Vision Agent" authors = ["Landing AI "] readme = "README.md"