From f439552faca952720b6d9d6e11aedaf7798b63b7 Mon Sep 17 00:00:00 2001 From: Dita Aji Pratama Date: Fri, 24 Jul 2026 19:01:26 +0700 Subject: [PATCH] Vision tool --- tools/vision.py | 57 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 57 insertions(+) create mode 100644 tools/vision.py diff --git a/tools/vision.py b/tools/vision.py new file mode 100644 index 0000000..82abd6e --- /dev/null +++ b/tools/vision.py @@ -0,0 +1,57 @@ +import os +import base64 +from tools.coder import get_current_workspace + +MIME_MAP = { + ".jpg": "image/jpeg", + ".jpeg": "image/jpeg", + ".png": "image/png", + ".gif": "image/gif", + ".webp": "image/webp", + ".bmp": "image/bmp", +} + +MAX_SIZE = 10 * 1024 * 1024 # 10MB + +class ImagePayload: + def __init__(self, data, mime, path): + self.data = data + self.mime = mime + self.path = path + +schema_describe_image = { + "type": "function", + "function": { + "name": "describe_image", + "description": "Load an image file and send it to the model for visual analysis. Use this when the user wants to look at, describe, or analyze an image file.", + "parameters": { + "type": "object", + "properties": { + "path": {"type": "string", "description": "Path to the image file (absolute or relative to workspace)"} + }, + "required": ["path"] + } + } +} + +def describe_image(path): + full_path = os.path.join(get_current_workspace(), path) if not os.path.isabs(path) else path + + if not os.path.isfile(full_path): + return f"Error: file not found: {full_path}" + + ext = os.path.splitext(full_path)[1].lower() + if ext not in MIME_MAP: + return f"Error: unsupported image format '{ext}'. Supported: {', '.join(MIME_MAP.keys())}" + + size = os.path.getsize(full_path) + if size > MAX_SIZE: + return f"Error: image too large ({size / 1024 / 1024:.1f}MB). Max: {MAX_SIZE / 1024 / 1024:.0f}MB" + + with open(full_path, "rb") as f: + raw = f.read() + + encoded = base64.b64encode(raw).decode("ascii") + mime = MIME_MAP[ext] + + return ImagePayload(data=encoded, mime=mime, path=full_path)