feat: add plugin dependency pipeline and HTML rendering service
- 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.
This commit is contained in:
121
src/plugin_runtime/capabilities/render.py
Normal file
121
src/plugin_runtime/capabilities/render.py
Normal file
@@ -0,0 +1,121 @@
|
||||
"""插件运行时的浏览器渲染能力。"""
|
||||
|
||||
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)}
|
||||
Reference in New Issue
Block a user