daa2efde14
* fix: 修复5081号PR在子代理执行后台任务时,未正确使用系统配置的流式/非流请求的问题(#5081) * feat:为子代理增加远程图片URL参数支持 * fix: update description for image_urls parameter in HandoffTool to clarify usage in multimodal tasks * ruff format * fix:修正子agent无法正确接收本地图片(参考图)路径的问题 * fix:增强image_urls接收的鲁棒性 * fix:ruff检查 * fix: harden handoff image_urls preprocessing * fix: refactor handoff image_urls preprocessing flow * refactor: simplify handoff image_urls data flow * fix: filter non-string handoff image_urls entries * refactor: streamline handoff image url collection * refactor: share handoff image ref validation utilities * refactor: simplify handoff image url processing * refactor: honor prepared handoff image urls contract --------- Co-authored-by: Soulter <905617992@qq.com> Co-authored-by: 邹永赫 <1259085392@qq.com>
87 lines
2.2 KiB
Python
87 lines
2.2 KiB
Python
from __future__ import annotations
|
|
|
|
import os
|
|
from collections.abc import Sequence
|
|
from pathlib import Path
|
|
from urllib.parse import unquote, urlparse
|
|
|
|
ALLOWED_IMAGE_EXTENSIONS = {
|
|
".png",
|
|
".jpg",
|
|
".jpeg",
|
|
".gif",
|
|
".webp",
|
|
".bmp",
|
|
".tif",
|
|
".tiff",
|
|
".svg",
|
|
".heic",
|
|
}
|
|
|
|
|
|
def resolve_file_url_path(image_ref: str) -> str:
|
|
parsed = urlparse(image_ref)
|
|
if parsed.scheme != "file":
|
|
return image_ref
|
|
|
|
path = unquote(parsed.path or "")
|
|
netloc = unquote(parsed.netloc or "")
|
|
|
|
# Keep support for file://<host>/path and file://<path> forms.
|
|
if netloc and netloc.lower() != "localhost":
|
|
path = f"//{netloc}{path}" if path else netloc
|
|
elif not path and netloc:
|
|
path = netloc
|
|
|
|
if os.name == "nt" and len(path) > 2 and path[0] == "/" and path[2] == ":":
|
|
path = path[1:]
|
|
|
|
return path or image_ref
|
|
|
|
|
|
def _is_path_within_roots(path: str, roots: Sequence[str]) -> bool:
|
|
try:
|
|
candidate = Path(path).resolve(strict=False)
|
|
except Exception:
|
|
return False
|
|
|
|
for root in roots:
|
|
try:
|
|
root_path = Path(root).resolve(strict=False)
|
|
candidate.relative_to(root_path)
|
|
return True
|
|
except Exception:
|
|
continue
|
|
return False
|
|
|
|
|
|
def is_supported_image_ref(
|
|
image_ref: str,
|
|
*,
|
|
allow_extensionless_existing_local_file: bool = False,
|
|
extensionless_local_roots: Sequence[str] | None = None,
|
|
) -> bool:
|
|
if not image_ref:
|
|
return False
|
|
|
|
lowered = image_ref.lower()
|
|
if lowered.startswith(("http://", "https://", "base64://")):
|
|
return True
|
|
|
|
file_path = (
|
|
resolve_file_url_path(image_ref) if lowered.startswith("file://") else image_ref
|
|
)
|
|
ext = os.path.splitext(file_path)[1].lower()
|
|
if ext in ALLOWED_IMAGE_EXTENSIONS:
|
|
return True
|
|
if not allow_extensionless_existing_local_file:
|
|
return False
|
|
if not extensionless_local_roots:
|
|
return False
|
|
# Keep support for extension-less temp files returned by image converters.
|
|
return (
|
|
ext == ""
|
|
and os.path.exists(file_path)
|
|
and _is_path_within_roots(file_path, extensionless_local_roots)
|
|
)
|