- Implemented a new dependency pipeline for plugins to manage Python package dependencies, including conflict detection and automatic installation of missing dependencies. - Introduced an HTML rendering service that utilizes existing browsers to render HTML content as PNG images, with support for various configurations and error handling.
122 lines
4.1 KiB
Python
122 lines
4.1 KiB
Python
"""插件运行时的浏览器渲染能力。"""
|
|
|
|
from typing import Any, Dict
|
|
|
|
from src.common.logger import get_logger
|
|
from src.services.html_render_service import HtmlRenderRequest, get_html_render_service
|
|
|
|
logger = get_logger("plugin_runtime.integration")
|
|
|
|
|
|
class RuntimeRenderCapabilityMixin:
|
|
"""插件运行时的浏览器渲染能力混入。"""
|
|
|
|
@staticmethod
|
|
def _coerce_int(value: Any, default: int) -> int:
|
|
"""将任意值尽量转换为整数。
|
|
|
|
Args:
|
|
value: 原始输入值。
|
|
default: 转换失败时返回的默认值。
|
|
|
|
Returns:
|
|
int: 规范化后的整数结果。
|
|
"""
|
|
|
|
try:
|
|
return int(value)
|
|
except (TypeError, ValueError):
|
|
return default
|
|
|
|
@staticmethod
|
|
def _coerce_float(value: Any, default: float) -> float:
|
|
"""将任意值尽量转换为浮点数。
|
|
|
|
Args:
|
|
value: 原始输入值。
|
|
default: 转换失败时返回的默认值。
|
|
|
|
Returns:
|
|
float: 规范化后的浮点结果。
|
|
"""
|
|
|
|
try:
|
|
return float(value)
|
|
except (TypeError, ValueError):
|
|
return default
|
|
|
|
@staticmethod
|
|
def _coerce_bool(value: Any, default: bool = False) -> bool:
|
|
"""将任意值转换为布尔值。
|
|
|
|
Args:
|
|
value: 原始输入值。
|
|
default: 输入为空时返回的默认值。
|
|
|
|
Returns:
|
|
bool: 规范化后的布尔结果。
|
|
"""
|
|
|
|
if value is None:
|
|
return default
|
|
if isinstance(value, str):
|
|
normalized_value = value.strip().lower()
|
|
if normalized_value in {"0", "false", "no", "off"}:
|
|
return False
|
|
if normalized_value in {"1", "true", "yes", "on"}:
|
|
return True
|
|
return bool(value)
|
|
|
|
def _build_html_render_request(self, args: Dict[str, Any]) -> HtmlRenderRequest:
|
|
"""根据 capability 调用参数构造渲染请求。
|
|
|
|
Args:
|
|
args: capability 调用参数。
|
|
|
|
Returns:
|
|
HtmlRenderRequest: 结构化后的渲染请求。
|
|
"""
|
|
|
|
viewport = args.get("viewport", {})
|
|
viewport_width = 900
|
|
viewport_height = 500
|
|
if isinstance(viewport, dict):
|
|
viewport_width = self._coerce_int(viewport.get("width"), viewport_width)
|
|
viewport_height = self._coerce_int(viewport.get("height"), viewport_height)
|
|
|
|
return HtmlRenderRequest(
|
|
html=str(args.get("html", "") or ""),
|
|
selector=str(args.get("selector", "body") or "body"),
|
|
viewport_width=viewport_width,
|
|
viewport_height=viewport_height,
|
|
device_scale_factor=self._coerce_float(args.get("device_scale_factor"), 2.0),
|
|
full_page=self._coerce_bool(args.get("full_page"), False),
|
|
omit_background=self._coerce_bool(args.get("omit_background"), False),
|
|
wait_until=str(args.get("wait_until", "load") or "load"),
|
|
wait_for_selector=str(args.get("wait_for_selector", "") or ""),
|
|
wait_for_timeout_ms=self._coerce_int(args.get("wait_for_timeout_ms"), 0),
|
|
timeout_ms=self._coerce_int(args.get("timeout_ms"), 0),
|
|
allow_network=self._coerce_bool(args.get("allow_network"), False),
|
|
)
|
|
|
|
async def _cap_render_html2png(self, plugin_id: str, capability: str, args: Dict[str, Any]) -> Any:
|
|
"""将 HTML 内容渲染为 PNG 图片。
|
|
|
|
Args:
|
|
plugin_id: 调用该能力的插件 ID。
|
|
capability: 当前能力名称。
|
|
args: 能力调用参数。
|
|
|
|
Returns:
|
|
Any: 标准化后的能力返回结构。
|
|
"""
|
|
|
|
del plugin_id, capability
|
|
try:
|
|
request = self._build_html_render_request(args)
|
|
result = await get_html_render_service().render_html_to_png(request)
|
|
return {"success": True, "result": result.to_payload()}
|
|
except Exception as exc:
|
|
logger.error(f"[cap.render.html2png] 执行失败: {exc}", exc_info=True)
|
|
return {"success": False, "error": str(exc)}
|