|
|
@@ -52,7 +52,7 @@ import aiohttp
|
|
|
import re
|
|
|
import base64
|
|
|
from urllib.parse import urlparse, parse_qs, unquote
|
|
|
-from typing import Optional, List, Dict, Any, Tuple, Union
|
|
|
+from typing import Literal, Optional, List, Dict, Any, Tuple, Union
|
|
|
from pathlib import Path
|
|
|
from langchain_core.runnables import RunnableLambda
|
|
|
from argparse import Namespace # 使用 Namespace 快速构造带属性的对象
|
|
|
@@ -548,7 +548,6 @@ def _fetch_profile_id(cookie_type: str) -> Optional[str]:
|
|
|
# 导航类工具 (Navigation Tools)
|
|
|
# ============================================================
|
|
|
|
|
|
-@tool()
|
|
|
async def browser_get_live_url() -> ToolResult:
|
|
|
"""
|
|
|
获取云浏览器的实时画面链接(Live URL),可用于在本地浏览器中查看或分享给他人操作。
|
|
|
@@ -567,7 +566,6 @@ async def browser_get_live_url() -> ToolResult:
|
|
|
)
|
|
|
|
|
|
|
|
|
-@tool()
|
|
|
async def browser_navigate_to_url(url: str, new_tab: bool = False) -> ToolResult:
|
|
|
"""
|
|
|
导航到指定的 URL
|
|
|
@@ -607,7 +605,6 @@ async def browser_navigate_to_url(url: str, new_tab: bool = False) -> ToolResult
|
|
|
)
|
|
|
|
|
|
|
|
|
-@tool()
|
|
|
async def browser_search_web(query: str, engine: str = "bing") -> ToolResult:
|
|
|
"""
|
|
|
使用搜索引擎搜索
|
|
|
@@ -644,7 +641,6 @@ async def browser_search_web(query: str, engine: str = "bing") -> ToolResult:
|
|
|
)
|
|
|
|
|
|
|
|
|
-@tool()
|
|
|
async def browser_go_back() -> ToolResult:
|
|
|
"""
|
|
|
返回到上一个页面
|
|
|
@@ -671,10 +667,9 @@ async def browser_go_back() -> ToolResult:
|
|
|
)
|
|
|
|
|
|
|
|
|
-@tool()
|
|
|
-async def browser_wait(seconds: int = 3) -> ToolResult:
|
|
|
+async def browser_wait_impl(seconds: int = 3) -> ToolResult:
|
|
|
"""
|
|
|
- 等待指定的秒数
|
|
|
+ 等待指定的秒数(内部实现)
|
|
|
Wait for a specified number of seconds
|
|
|
|
|
|
用于等待页面加载、动画完成或其他异步操作。
|
|
|
@@ -732,7 +727,6 @@ class DownloadLinkCaptureHandler(logging.Handler):
|
|
|
self.captured_url = url
|
|
|
# print(f"🎯 成功锁定完整直链: {url[:50]}...") # 调试用
|
|
|
|
|
|
-@tool()
|
|
|
async def browser_download_direct_url(url: str, save_name: str = "book.epub") -> ToolResult:
|
|
|
save_dir = Path.cwd() / ".cache/.browser_use_files"
|
|
|
save_dir.mkdir(parents=True, exist_ok=True)
|
|
|
@@ -797,7 +791,6 @@ async def browser_download_direct_url(url: str, save_name: str = "book.epub") ->
|
|
|
long_term_memory=f"下载任务由于异常中断: {str(e)}"
|
|
|
)
|
|
|
|
|
|
-@tool()
|
|
|
async def browser_click_element(index: int) -> ToolResult:
|
|
|
"""
|
|
|
点击页面元素,并自动通过拦截内部日志获取下载直链。
|
|
|
@@ -847,7 +840,6 @@ async def browser_click_element(index: int) -> ToolResult:
|
|
|
logger.removeHandler(capture_handler)
|
|
|
|
|
|
|
|
|
-@tool()
|
|
|
async def browser_input_text(index: int, text: str, clear: bool = True) -> ToolResult:
|
|
|
"""
|
|
|
在指定元素中输入文本
|
|
|
@@ -885,7 +877,6 @@ async def browser_input_text(index: int, text: str, clear: bool = True) -> ToolR
|
|
|
)
|
|
|
|
|
|
|
|
|
-@tool()
|
|
|
async def browser_send_keys(keys: str) -> ToolResult:
|
|
|
"""
|
|
|
发送键盘按键或快捷键
|
|
|
@@ -925,7 +916,6 @@ async def browser_send_keys(keys: str) -> ToolResult:
|
|
|
)
|
|
|
|
|
|
|
|
|
-@tool()
|
|
|
async def browser_upload_file(index: int, path: str) -> ToolResult:
|
|
|
"""
|
|
|
上传文件到文件输入元素
|
|
|
@@ -968,7 +958,6 @@ async def browser_upload_file(index: int, path: str) -> ToolResult:
|
|
|
# ============================================================
|
|
|
# 滚动和视图工具 (Scroll & View Tools)
|
|
|
# ============================================================
|
|
|
-@tool()
|
|
|
async def browser_scroll_page(down: bool = True, pages: float = 1.0, index: Optional[int] = None) -> ToolResult:
|
|
|
try:
|
|
|
# 限制单次滚动幅度,避免 agent 一次滚 100 页
|
|
|
@@ -1028,7 +1017,6 @@ async def browser_scroll_page(down: bool = True, pages: float = 1.0, index: Opti
|
|
|
|
|
|
|
|
|
|
|
|
-@tool()
|
|
|
async def browser_find_text(text: str) -> ToolResult:
|
|
|
"""
|
|
|
查找页面中的文本并滚动到该位置
|
|
|
@@ -1063,7 +1051,6 @@ async def browser_find_text(text: str) -> ToolResult:
|
|
|
long_term_memory=f"查找文本 '{text}' 失败"
|
|
|
)
|
|
|
|
|
|
-@tool()
|
|
|
async def browser_get_visual_selector_map() -> ToolResult:
|
|
|
"""
|
|
|
获取当前页面的视觉快照和交互元素索引映射。
|
|
|
@@ -1160,10 +1147,9 @@ async def browser_get_visual_selector_map() -> ToolResult:
|
|
|
long_term_memory="获取视觉元素映射失败"
|
|
|
)
|
|
|
|
|
|
-@tool()
|
|
|
-async def browser_screenshot() -> ToolResult:
|
|
|
+async def browser_screenshot_impl() -> ToolResult:
|
|
|
"""
|
|
|
- 请求在下次观察中包含页面截图
|
|
|
+ 请求在下次观察中包含页面截图(内部实现)
|
|
|
Request a screenshot to be included in the next observation
|
|
|
|
|
|
用于视觉检查页面状态,帮助理解页面布局和内容。
|
|
|
@@ -1197,7 +1183,6 @@ async def browser_screenshot() -> ToolResult:
|
|
|
# 标签页管理工具 (Tab Management Tools)
|
|
|
# ============================================================
|
|
|
|
|
|
-@tool()
|
|
|
async def browser_switch_tab(tab_id: str) -> ToolResult:
|
|
|
"""
|
|
|
切换到指定标签页
|
|
|
@@ -1232,7 +1217,6 @@ async def browser_switch_tab(tab_id: str) -> ToolResult:
|
|
|
)
|
|
|
|
|
|
|
|
|
-@tool()
|
|
|
async def browser_close_tab(tab_id: str) -> ToolResult:
|
|
|
"""
|
|
|
关闭指定标签页
|
|
|
@@ -1271,7 +1255,6 @@ async def browser_close_tab(tab_id: str) -> ToolResult:
|
|
|
# 下拉框工具 (Dropdown Tools)
|
|
|
# ============================================================
|
|
|
|
|
|
-@tool()
|
|
|
async def browser_get_dropdown_options(index: int) -> ToolResult:
|
|
|
"""
|
|
|
获取下拉框的所有选项
|
|
|
@@ -1305,7 +1288,6 @@ async def browser_get_dropdown_options(index: int) -> ToolResult:
|
|
|
)
|
|
|
|
|
|
|
|
|
-@tool()
|
|
|
async def browser_select_dropdown_option(index: int, text: str) -> ToolResult:
|
|
|
"""
|
|
|
选择下拉框选项
|
|
|
@@ -1408,7 +1390,6 @@ async def extraction_adapter(input_data):
|
|
|
from argparse import Namespace
|
|
|
return Namespace(completion=content)
|
|
|
|
|
|
-@tool()
|
|
|
async def browser_extract_content(query: str, extract_links: bool = False,
|
|
|
start_from_char: int = 0) -> ToolResult:
|
|
|
"""
|
|
|
@@ -1550,7 +1531,6 @@ async def _detect_and_download_pdf_via_cdp(browser) -> Optional[str]:
|
|
|
return None
|
|
|
|
|
|
|
|
|
-@tool()
|
|
|
async def browser_read_long_content(
|
|
|
goal: Union[str, dict],
|
|
|
source: str = "page",
|
|
|
@@ -1609,7 +1589,6 @@ async def browser_read_long_content(
|
|
|
error=f"Read long content failed: {str(e)}",
|
|
|
long_term_memory="参数解析或校验失败,请检查输入"
|
|
|
)
|
|
|
-@tool()
|
|
|
async def browser_get_page_html() -> ToolResult:
|
|
|
"""
|
|
|
获取当前页面的完整 HTML
|
|
|
@@ -1671,7 +1650,6 @@ async def browser_get_page_html() -> ToolResult:
|
|
|
)
|
|
|
|
|
|
|
|
|
-@tool()
|
|
|
async def browser_get_selector_map() -> ToolResult:
|
|
|
"""
|
|
|
获取当前页面的元素索引映射
|
|
|
@@ -1743,7 +1721,6 @@ async def browser_get_selector_map() -> ToolResult:
|
|
|
# JavaScript 执行工具 (JavaScript Tools)
|
|
|
# ============================================================
|
|
|
|
|
|
-@tool()
|
|
|
async def browser_evaluate(code: str) -> ToolResult:
|
|
|
"""
|
|
|
在页面中执行 JavaScript 代码
|
|
|
@@ -1785,7 +1762,6 @@ async def browser_evaluate(code: str) -> ToolResult:
|
|
|
)
|
|
|
|
|
|
|
|
|
-@tool()
|
|
|
async def browser_ensure_login_with_cookies(cookie_type: str, url: str = "https://www.xiaohongshu.com") -> ToolResult:
|
|
|
"""
|
|
|
检查登录状态并在需要时注入 cookies
|
|
|
@@ -1881,7 +1857,6 @@ async def browser_ensure_login_with_cookies(cookie_type: str, url: str = "https:
|
|
|
# 等待用户操作工具 (Wait for User Action)
|
|
|
# ============================================================
|
|
|
|
|
|
-@tool()
|
|
|
async def browser_wait_for_user_action(message: str = "Please complete the action in browser",
|
|
|
timeout: int = 300) -> ToolResult:
|
|
|
"""
|
|
|
@@ -1953,7 +1928,6 @@ async def browser_wait_for_user_action(message: str = "Please complete the actio
|
|
|
# 任务完成工具 (Task Completion)
|
|
|
# ============================================================
|
|
|
|
|
|
-@tool()
|
|
|
async def browser_done(text: str, success: bool = True,
|
|
|
files_to_display: Optional[List[str]] = None) -> ToolResult:
|
|
|
"""
|
|
|
@@ -1998,7 +1972,6 @@ async def browser_done(text: str, success: bool = True,
|
|
|
|
|
|
_COOKIES_DIR = Path(__file__).parent.parent.parent.parent.parent / ".cache/.cookies"
|
|
|
|
|
|
-@tool()
|
|
|
async def browser_export_cookies(name: str = "", account: str = "") -> ToolResult:
|
|
|
"""
|
|
|
导出当前浏览器的所有 Cookie 到本地 .cookies/ 目录。
|
|
|
@@ -2042,7 +2015,6 @@ async def browser_export_cookies(name: str = "", account: str = "") -> ToolResul
|
|
|
return ToolResult(title="Cookie 导出失败", output="", error=str(e), long_term_memory="导出 Cookie 失败")
|
|
|
|
|
|
|
|
|
-@tool()
|
|
|
async def browser_load_cookies(url: str, name: str = "", auto_navigate: bool = True) -> ToolResult:
|
|
|
"""
|
|
|
根据目标 URL 自动查找本地 Cookie 文件,注入浏览器并导航到目标页面恢复登录态。
|
|
|
@@ -2178,60 +2150,345 @@ async def browser_load_cookies(url: str, name: str = "", auto_navigate: bool = T
|
|
|
|
|
|
|
|
|
# ============================================================
|
|
|
-# 导出所有工具函数(供外部使用)
|
|
|
+# 新版统一入口(13 个 @tool,替代原来 28 个)
|
|
|
# ============================================================
|
|
|
|
|
|
-__all__ = [
|
|
|
- # 会话管理
|
|
|
- 'init_browser_session',
|
|
|
- 'get_browser_session',
|
|
|
- 'cleanup_browser_session',
|
|
|
- 'kill_browser_session',
|
|
|
|
|
|
- # 导航类工具
|
|
|
- 'browser_navigate_to_url',
|
|
|
- 'browser_search_web',
|
|
|
- 'browser_go_back',
|
|
|
- 'browser_wait',
|
|
|
+@tool()
|
|
|
+async def browser_navigate(url: str, new_tab: bool = False) -> ToolResult:
|
|
|
+ """
|
|
|
+ 导航到指定 URL。
|
|
|
|
|
|
- # 元素交互工具
|
|
|
- 'browser_click_element',
|
|
|
- 'browser_input_text',
|
|
|
- 'browser_send_keys',
|
|
|
- 'browser_upload_file',
|
|
|
+ Args:
|
|
|
+ url: 目标 URL
|
|
|
+ new_tab: 是否在新标签页打开(默认 False)
|
|
|
+ """
|
|
|
+ return await browser_navigate_to_url(url=url, new_tab=new_tab)
|
|
|
|
|
|
- # 滚动和视图工具
|
|
|
- 'browser_scroll_page',
|
|
|
- 'browser_find_text',
|
|
|
- 'browser_screenshot',
|
|
|
|
|
|
- # 标签页管理工具
|
|
|
- 'browser_switch_tab',
|
|
|
- 'browser_close_tab',
|
|
|
+@tool()
|
|
|
+async def browser_search(query: str, engine: str = "bing") -> ToolResult:
|
|
|
+ """
|
|
|
+ 使用搜索引擎搜索。
|
|
|
+
|
|
|
+ Args:
|
|
|
+ query: 搜索关键词
|
|
|
+ engine: 搜索引擎,可选 google / bing / duckduckgo,默认 bing
|
|
|
+ """
|
|
|
+ return await browser_search_web(query=query, engine=engine)
|
|
|
+
|
|
|
+
|
|
|
+@tool()
|
|
|
+async def browser_back() -> ToolResult:
|
|
|
+ """返回上一页。"""
|
|
|
+ return await browser_go_back()
|
|
|
+
|
|
|
+
|
|
|
+@tool()
|
|
|
+async def browser_interact(
|
|
|
+ action: Literal["click", "type", "send_keys", "upload", "dropdown_list", "dropdown_select"],
|
|
|
+ index: Optional[int] = None,
|
|
|
+ text: Optional[str] = None,
|
|
|
+ path: Optional[str] = None,
|
|
|
+ keys: Optional[str] = None,
|
|
|
+ clear: bool = True,
|
|
|
+) -> ToolResult:
|
|
|
+ """
|
|
|
+ 与页面元素交互。根据 action 选择具体操作:
|
|
|
+
|
|
|
+ - click: 点击元素。需要 index。
|
|
|
+ - type: 在输入框输入文本。需要 index + text。clear 控制是否先清空。
|
|
|
+ - send_keys: 发送键盘按键(如 Enter、Control+A)。需要 keys,不需要 index。
|
|
|
+ - upload: 上传文件到文件输入框。需要 index + path(绝对路径)。
|
|
|
+ - dropdown_list: 列出下拉框选项。需要 index。
|
|
|
+ - dropdown_select: 选择下拉框选项。需要 index + text(选项文本)。
|
|
|
+
|
|
|
+ Args:
|
|
|
+ action: 交互类型
|
|
|
+ index: 元素索引(从 browser_elements 或 browser_screenshot(highlight=True) 获取)
|
|
|
+ text: 输入文本 / 下拉框选项文本
|
|
|
+ path: 上传文件的绝对路径
|
|
|
+ keys: 键盘按键字符串(如 "Enter"、"Control+A")
|
|
|
+ clear: type 时是否先清空(默认 True)
|
|
|
+ """
|
|
|
+ if action == "click":
|
|
|
+ if index is None:
|
|
|
+ return ToolResult(title="参数错误", output="", error="click 需要 index 参数")
|
|
|
+ return await browser_click_element(index=index)
|
|
|
+
|
|
|
+ elif action == "type":
|
|
|
+ if index is None or text is None:
|
|
|
+ return ToolResult(title="参数错误", output="", error="type 需要 index 和 text 参数")
|
|
|
+ return await browser_input_text(index=index, text=text, clear=clear)
|
|
|
+
|
|
|
+ elif action == "send_keys":
|
|
|
+ if keys is None:
|
|
|
+ return ToolResult(title="参数错误", output="", error="send_keys 需要 keys 参数")
|
|
|
+ return await browser_send_keys(keys=keys)
|
|
|
+
|
|
|
+ elif action == "upload":
|
|
|
+ if index is None or path is None:
|
|
|
+ return ToolResult(title="参数错误", output="", error="upload 需要 index 和 path 参数")
|
|
|
+ return await browser_upload_file(index=index, path=path)
|
|
|
+
|
|
|
+ elif action == "dropdown_list":
|
|
|
+ if index is None:
|
|
|
+ return ToolResult(title="参数错误", output="", error="dropdown_list 需要 index 参数")
|
|
|
+ return await browser_get_dropdown_options(index=index)
|
|
|
+
|
|
|
+ elif action == "dropdown_select":
|
|
|
+ if index is None or text is None:
|
|
|
+ return ToolResult(title="参数错误", output="", error="dropdown_select 需要 index 和 text 参数")
|
|
|
+ return await browser_select_dropdown_option(index=index, text=text)
|
|
|
+
|
|
|
+ else:
|
|
|
+ return ToolResult(title="未知 action", output="", error=f"不支持的 action: {action}")
|
|
|
+
|
|
|
+
|
|
|
+@tool()
|
|
|
+async def browser_scroll(
|
|
|
+ down: bool = True,
|
|
|
+ pages: float = 1.0,
|
|
|
+ into_view_index: Optional[int] = None,
|
|
|
+) -> ToolResult:
|
|
|
+ """
|
|
|
+ 滚动页面。
|
|
|
+
|
|
|
+ Args:
|
|
|
+ down: True 向下滚动,False 向上(默认 True)
|
|
|
+ pages: 滚动的页面数(默认 1.0)
|
|
|
+ into_view_index: 传入元素索引则滚动到该元素可见(忽略 down 和 pages)
|
|
|
+ """
|
|
|
+ return await browser_scroll_page(down=down, pages=pages, index=into_view_index)
|
|
|
+
|
|
|
+
|
|
|
+@tool()
|
|
|
+async def browser_screenshot(highlight_elements: bool = False) -> ToolResult:
|
|
|
+ """
|
|
|
+ 截取当前页面。
|
|
|
+
|
|
|
+ Args:
|
|
|
+ highlight_elements: False 返回纯截图;True 返回带交互元素编号标注的截图
|
|
|
+ + 元素列表(原 visual_selector_map 功能)
|
|
|
+ """
|
|
|
+ if highlight_elements:
|
|
|
+ return await browser_get_visual_selector_map()
|
|
|
+ else:
|
|
|
+ return await browser_screenshot_impl()
|
|
|
+
|
|
|
+
|
|
|
+@tool()
|
|
|
+async def browser_elements() -> ToolResult:
|
|
|
+ """
|
|
|
+ 获取当前页面的可交互元素列表(纯文本,不截图)。
|
|
|
+ 返回的 index 用于 browser_interact / browser_scroll 等操作。
|
|
|
+ """
|
|
|
+ return await browser_get_selector_map()
|
|
|
+
|
|
|
+
|
|
|
+@tool()
|
|
|
+async def browser_read(
|
|
|
+ mode: Literal["html", "find", "long"],
|
|
|
+ query: Optional[str] = None,
|
|
|
+ source: str = "page",
|
|
|
+ context: str = "",
|
|
|
+) -> ToolResult:
|
|
|
+ """
|
|
|
+ 读取页面内容,三种模式:
|
|
|
+
|
|
|
+ - html: 获取当前页面的 HTML 源码(大页面会截断到 10000 字符)
|
|
|
+ - find: 在页面中查找文本。需要 query。
|
|
|
+ - long: 智能分页读取长内容(支持自动检测 PDF)。query 描述阅读目标。
|
|
|
|
|
|
- # 下拉框工具
|
|
|
- 'browser_get_dropdown_options',
|
|
|
- 'browser_select_dropdown_option',
|
|
|
+ Args:
|
|
|
+ mode: 读取模式
|
|
|
+ query: find 模式下的查找文本;long 模式下的阅读目标描述
|
|
|
+ source: long 模式的内容来源("page" 或文件路径),默认 "page"
|
|
|
+ context: long 模式的业务背景(可选)
|
|
|
+ """
|
|
|
+ if mode == "html":
|
|
|
+ return await browser_get_page_html()
|
|
|
+
|
|
|
+ elif mode == "find":
|
|
|
+ if not query:
|
|
|
+ return ToolResult(title="参数错误", output="", error="find 模式需要 query 参数")
|
|
|
+ return await browser_find_text(text=query)
|
|
|
+
|
|
|
+ elif mode == "long":
|
|
|
+ return await browser_read_long_content(
|
|
|
+ goal=query or "阅读页面内容",
|
|
|
+ source=source,
|
|
|
+ context=context,
|
|
|
+ )
|
|
|
+
|
|
|
+ else:
|
|
|
+ return ToolResult(title="未知 mode", output="", error=f"不支持的 mode: {mode}")
|
|
|
|
|
|
- # 内容提取工具
|
|
|
- 'browser_extract_content',
|
|
|
- 'browser_get_page_html',
|
|
|
- 'browser_read_long_content',
|
|
|
- 'browser_download_direct_url',
|
|
|
- 'browser_get_selector_map',
|
|
|
- 'browser_get_visual_selector_map',
|
|
|
|
|
|
- # JavaScript 执行工具
|
|
|
- 'browser_evaluate',
|
|
|
- 'browser_ensure_login_with_cookies',
|
|
|
+@tool()
|
|
|
+async def browser_extract(
|
|
|
+ query: str,
|
|
|
+ extract_links: bool = False,
|
|
|
+ start_from_char: int = 0,
|
|
|
+) -> ToolResult:
|
|
|
+ """
|
|
|
+ 使用 LLM 从当前页面提取结构化数据。
|
|
|
|
|
|
- # 等待用户操作
|
|
|
- 'browser_wait_for_user_action',
|
|
|
+ 与 browser_read 不同,此工具会调用 LLM 分析页面内容并返回结构化结果。
|
|
|
+ 适合"提取所有产品价格"、"总结文章要点"等需要理解语义的场景。
|
|
|
|
|
|
- # 任务完成
|
|
|
- 'browser_done',
|
|
|
+ Args:
|
|
|
+ query: 提取指令(如"提取页面上所有产品名称和价格")
|
|
|
+ extract_links: 是否同时提取链接(默认 False)
|
|
|
+ start_from_char: 从第几个字符开始提取(用于分页处理大内容)
|
|
|
+ """
|
|
|
+ return await browser_extract_content(
|
|
|
+ query=query,
|
|
|
+ extract_links=extract_links,
|
|
|
+ start_from_char=start_from_char,
|
|
|
+ )
|
|
|
+
|
|
|
+
|
|
|
+@tool()
|
|
|
+async def browser_tabs(
|
|
|
+ action: Literal["switch", "close"],
|
|
|
+ tab_id: str = "",
|
|
|
+) -> ToolResult:
|
|
|
+ """
|
|
|
+ 管理浏览器标签页。
|
|
|
+
|
|
|
+ Args:
|
|
|
+ action: "switch" 切换到指定标签页;"close" 关闭指定标签页
|
|
|
+ tab_id: 标签页 ID(4 字符)
|
|
|
+ """
|
|
|
+ if not tab_id:
|
|
|
+ return ToolResult(title="参数错误", output="", error="需要 tab_id 参数")
|
|
|
+
|
|
|
+ if action == "switch":
|
|
|
+ return await browser_switch_tab(tab_id=tab_id)
|
|
|
+ elif action == "close":
|
|
|
+ return await browser_close_tab(tab_id=tab_id)
|
|
|
+ else:
|
|
|
+ return ToolResult(title="未知 action", output="", error=f"不支持的 action: {action}")
|
|
|
+
|
|
|
+
|
|
|
+@tool()
|
|
|
+async def browser_cookies(
|
|
|
+ action: Literal["load", "export", "ensure_login"],
|
|
|
+ url: str = "",
|
|
|
+ name: str = "",
|
|
|
+ account: str = "",
|
|
|
+ cookie_type: str = "",
|
|
|
+ auto_navigate: bool = True,
|
|
|
+) -> ToolResult:
|
|
|
+ """
|
|
|
+ Cookie / 登录态管理:
|
|
|
+
|
|
|
+ - load: 从本地加载已保存的 cookie 并注入浏览器。需要 url(自动匹配 cookie 文件)。
|
|
|
+ - export: 导出当前浏览器 cookie 到本地。可选 name 和 account 标识。
|
|
|
+ - ensure_login: 检查登录状态,未登录时自动注入 cookie。需要 cookie_type 和 url。
|
|
|
+
|
|
|
+ Args:
|
|
|
+ action: 操作类型
|
|
|
+ url: 目标 URL(load / ensure_login 必填)
|
|
|
+ name: cookie 文件名(可选)
|
|
|
+ account: 账号名(export 时可选)
|
|
|
+ cookie_type: cookie 类型标识(ensure_login 必填)
|
|
|
+ auto_navigate: load 时找不到 cookie 是否自动导航到目标页面(默认 True)
|
|
|
+ """
|
|
|
+ if action == "load":
|
|
|
+ if not url:
|
|
|
+ return ToolResult(title="参数错误", output="", error="load 需要 url 参数")
|
|
|
+ return await browser_load_cookies(url=url, name=name, auto_navigate=auto_navigate)
|
|
|
+
|
|
|
+ elif action == "export":
|
|
|
+ return await browser_export_cookies(name=name, account=account)
|
|
|
+
|
|
|
+ elif action == "ensure_login":
|
|
|
+ if not cookie_type:
|
|
|
+ return ToolResult(title="参数错误", output="", error="ensure_login 需要 cookie_type 参数")
|
|
|
+ return await browser_ensure_login_with_cookies(
|
|
|
+ cookie_type=cookie_type,
|
|
|
+ url=url or "https://www.xiaohongshu.com",
|
|
|
+ )
|
|
|
|
|
|
- # Cookie 持久化
|
|
|
- 'browser_export_cookies',
|
|
|
- 'browser_load_cookies',
|
|
|
+ else:
|
|
|
+ return ToolResult(title="未知 action", output="", error=f"不支持的 action: {action}")
|
|
|
+
|
|
|
+
|
|
|
+@tool()
|
|
|
+async def browser_wait(
|
|
|
+ seconds: Optional[int] = None,
|
|
|
+ user_message: Optional[str] = None,
|
|
|
+ timeout: int = 300,
|
|
|
+) -> ToolResult:
|
|
|
+ """
|
|
|
+ 等待。两种模式:
|
|
|
+
|
|
|
+ - 传 seconds: 纯等待指定秒数(默认 3 秒)
|
|
|
+ - 传 user_message: 暂停并提示用户在浏览器中完成操作(如登录、验证码),
|
|
|
+ 用户完成后按回车继续。timeout 控制最长等待时间。
|
|
|
+ - 两者都不传: 默认等待 3 秒
|
|
|
+
|
|
|
+ Args:
|
|
|
+ seconds: 等待秒数
|
|
|
+ user_message: 用户操作提示消息
|
|
|
+ timeout: user_message 模式的最长等待(秒),默认 300
|
|
|
+ """
|
|
|
+ if user_message:
|
|
|
+ return await browser_wait_for_user_action(message=user_message, timeout=timeout)
|
|
|
+ else:
|
|
|
+ return await browser_wait_impl(seconds=seconds or 3)
|
|
|
+
|
|
|
+
|
|
|
+@tool()
|
|
|
+async def browser_js(code: str) -> ToolResult:
|
|
|
+ """
|
|
|
+ 在当前页面执行 JavaScript 代码。
|
|
|
+
|
|
|
+ Args:
|
|
|
+ code: JavaScript 代码字符串。返回值会被自动序列化。
|
|
|
+ """
|
|
|
+ return await browser_evaluate(code=code)
|
|
|
+
|
|
|
+
|
|
|
+@tool()
|
|
|
+async def browser_download(url: str, save_name: str = "") -> ToolResult:
|
|
|
+ """
|
|
|
+ 下载指定 URL 的文件到本地。
|
|
|
+
|
|
|
+ Args:
|
|
|
+ url: 文件 URL
|
|
|
+ save_name: 保存文件名(可选,默认自动推断)
|
|
|
+ """
|
|
|
+ return await browser_download_direct_url(url=url, save_name=save_name or "download")
|
|
|
+
|
|
|
+
|
|
|
+# ============================================================
|
|
|
+# 导出(供外部使用)
|
|
|
+# ============================================================
|
|
|
+
|
|
|
+__all__ = [
|
|
|
+ # 会话管理(非 @tool)
|
|
|
+ 'init_browser_session',
|
|
|
+ 'get_browser_session',
|
|
|
+ 'get_browser_live_url',
|
|
|
+ 'cleanup_browser_session',
|
|
|
+ 'kill_browser_session',
|
|
|
+
|
|
|
+ # 13 个 @tool 入口
|
|
|
+ 'browser_navigate',
|
|
|
+ 'browser_search',
|
|
|
+ 'browser_back',
|
|
|
+ 'browser_interact',
|
|
|
+ 'browser_scroll',
|
|
|
+ 'browser_screenshot',
|
|
|
+ 'browser_elements',
|
|
|
+ 'browser_read',
|
|
|
+ 'browser_extract',
|
|
|
+ 'browser_tabs',
|
|
|
+ 'browser_cookies',
|
|
|
+ 'browser_wait',
|
|
|
+ 'browser_js',
|
|
|
+ 'browser_download',
|
|
|
]
|