Procházet zdrojové kódy

chore: 清理不再使用的浏览器工具示例和文档

移除与 browser-use 工具相关的示例代码和文档,包括:
- baidu_search_agent.py 示例文件
- 多个测试和说明文档(test-tools.md、SKILL.md、test-skill.md、tools_examples.py)
- test-tools.py 测试脚本
- 同时从 requirements.txt 中移除 playwright 依赖
max_liu před 1 měsícem
rodič
revize
348efe402d

+ 0 - 452
SKILL.md

@@ -1,452 +0,0 @@
-# Browser-Use CLI 命令行工具完整文档
-
-## 概述
-
-`browser-use` 是一个快速、持久化的浏览器自动化命令行工具。它在命令之间保持浏览器会话,支持复杂的多步骤工作流程。
-
-## 快速开始
-
-```bash
-browser-use open https://example.com           # 导航到 URL
-browser-use state                              # 获取页面元素及其索引
-browser-use click 5                            # 通过索引点击元素
-browser-use type "Hello World"                 # 输入文本
-browser-use screenshot                         # 截图
-browser-use close                              # 关闭浏览器
-```
-
-## 安装
-
-```bash
-# 安装 browser-use 包
-uv add browser-use
-uv sync
-
-# 安装 Chromium 浏览器
-uvx browser-use install
-```
-
-## 核心工作流程
-
-1. **导航**: `browser-use open <url>` - 打开 URL(如需要会自动启动浏览器)
-2. **检查**: `browser-use state` - 返回可点击元素及其索引
-3. **交互**: 使用 state 返回的索引进行交互(`browser-use click 5`, `browser-use input 3 "text"`)
-4. **验证**: 使用 `browser-use state` 或 `browser-use screenshot` 确认操作结果
-5. **重复**: 浏览器在命令之间保持打开状态
-
-## 浏览器模式
-
-```bash
-# 默认:无头 Chromium
-browser-use --browser chromium open <url>
-
-# 可见的 Chromium 窗口
-browser-use --browser chromium --headed open <url>
-
-# 使用真实的 Chrome(带登录会话)
-browser-use --browser real open <url>
-
-# 云端浏览器(需要 API key)
-browser-use --browser remote open <url>
-```
-
-**模式说明**:
-- **chromium**: 快速、隔离、默认无头模式
-- **real**: 使用你的 Chrome,包含 cookies、扩展、已登录会话
-- **remote**: 云端托管浏览器,支持代理(需要 BROWSER_USE_API_KEY)
-
-## 命令参考
-
-### 1. 导航命令
-
-| 命令 | 描述 | 示例 |
-|------|------|------|
-| `browser-use open <url>` | 导航到指定 URL | `browser-use open https://example.com` |
-| `browser-use back` | 返回上一页 | `browser-use back` |
-| `browser-use scroll down` | 向下滚动 | `browser-use scroll down` |
-| `browser-use scroll up` | 向上滚动 | `browser-use scroll up` |
-| `browser-use scroll down --amount 1000` | 向下滚动指定像素 | `browser-use scroll down --amount 1000` |
-
-### 2. 页面状态检查
-
-| 命令 | 描述 | 示例 |
-|------|------|------|
-| `browser-use state` | 获取 URL、标题和可点击元素 | `browser-use state` |
-| `browser-use screenshot` | 截图(输出 base64) | `browser-use screenshot` |
-| `browser-use screenshot <path>` | 保存截图到文件 | `browser-use screenshot page.png` |
-| `browser-use screenshot --full <path>` | 全页面截图 | `browser-use screenshot --full page.png` |
-
-### 3. 交互命令
-
-**注意**: 使用 `browser-use state` 获取的索引来进行交互
-
-| 命令 | 描述 | 示例 |
-|------|------|------|
-| `browser-use click <index>` | 点击指定索引的元素 | `browser-use click 5` |
-| `browser-use type "text"` | 在当前焦点元素中输入文本 | `browser-use type "Hello World"` |
-| `browser-use input <index> "text"` | 点击元素后输入文本 | `browser-use input 3 "john@example.com"` |
-| `browser-use keys "Enter"` | 发送键盘按键 | `browser-use keys "Enter"` |
-| `browser-use keys "Control+a"` | 发送组合键 | `browser-use keys "Control+a"` |
-| `browser-use select <index> "option"` | 选择下拉选项 | `browser-use select 2 "Option 1"` |
-
-### 4. 标签页管理
-
-| 命令 | 描述 | 示例 |
-|------|------|------|
-| `browser-use switch <tab>` | 切换到指定索引的标签页 | `browser-use switch 1` |
-| `browser-use close-tab` | 关闭当前标签页 | `browser-use close-tab` |
-| `browser-use close-tab <tab>` | 关闭指定索引的标签页 | `browser-use close-tab 2` |
-
-### 5. JavaScript 和数据提取
-
-| 命令 | 描述 | 示例 |
-|------|------|------|
-| `browser-use eval "js code"` | 执行 JavaScript 代码并返回结果 | `browser-use eval "document.title"` |
-| `browser-use extract "query"` | 使用 LLM 提取数据(需要 API key) | `browser-use extract "all product prices"` |
-
-### 6. Python 执行(持久化会话)
-
-Python 会话在命令之间保持状态。`browser` 对象提供以下方法:
-
-```bash
-# 设置变量
-browser-use python "x = 42"
-
-# 访问变量
-browser-use python "print(x)"  # 输出: 42
-
-# 访问浏览器对象
-browser-use python "print(browser.url)"
-
-# 显示已定义的变量
-browser-use python --vars
-
-# 清除 Python 命名空间
-browser-use python --reset
-
-# 执行 Python 文件
-browser-use python --file script.py
-```
-
-**browser 对象 API**:
-- `browser.url` - 当前页面 URL
-- `browser.title` - 页面标题
-- `browser.goto(url)` - 导航到 URL
-- `browser.click(index)` - 点击元素
-- `browser.type(text)` - 输入文本
-- `browser.screenshot(path)` - 截图
-- `browser.scroll()` - 滚动页面
-- `browser.html` - 获取页面 HTML
-
-### 7. AI 代理任务(需要 API Key)
-
-```bash
-# 运行 AI 代理完成任务
-browser-use run "Fill the contact form with test data"
-
-# 指定最大步数
-browser-use run "Extract all product prices" --max-steps 50
-```
-
-代理任务使用 LLM 自主完成复杂的浏览器任务。需要配置以下任一 API key:
-- `BROWSER_USE_API_KEY`(推荐)
-- `OPENAI_API_KEY`
-- `ANTHROPIC_API_KEY`
-- `GOOGLE_API_KEY`
-
-### 8. 会话管理
-
-| 命令 | 描述 | 示例 |
-|------|------|------|
-| `browser-use sessions` | 列出所有活动会话 | `browser-use sessions` |
-| `browser-use close` | 关闭当前会话 | `browser-use close` |
-| `browser-use close --all` | 关闭所有会话 | `browser-use close --all` |
-
-### 9. 服务器控制
-
-| 命令 | 描述 | 示例 |
-|------|------|------|
-| `browser-use server status` | 检查服务器是否运行 | `browser-use server status` |
-| `browser-use server stop` | 停止服务器 | `browser-use server stop` |
-| `browser-use server logs` | 查看服务器日志 | `browser-use server logs` |
-
-## 全局选项
-
-| 选项 | 描述 | 默认值 |
-|------|------|--------|
-| `--session NAME` | 使用命名会话 | "default" |
-| `--browser MODE` | 浏览器模式:chromium, real, remote | chromium |
-| `--headed` | 显示浏览器窗口(chromium 模式) | false |
-| `--profile NAME` | Chrome 配置文件(仅 real 模式) | - |
-| `--json` | 以 JSON 格式输出 | false |
-| `--api-key KEY` | 覆盖 API key | - |
-
-**会话行为**: 所有不带 `--session` 的命令使用同一个 "default" 会话。浏览器在命令之间保持打开并被重用。使用 `--session NAME` 可以并行运行多个浏览器。
-
-## 实用示例
-
-### 示例 1: 表单提交
-
-```bash
-# 打开表单页面
-browser-use open https://example.com/contact
-
-# 查看页面元素
-browser-use state
-# 输出: [0] input "Name", [1] input "Email", [2] textarea "Message", [3] button "Submit"
-
-# 填写表单
-browser-use input 0 "John Doe"
-browser-use input 1 "john@example.com"
-browser-use input 2 "Hello, this is a test message."
-
-# 提交表单
-browser-use click 3
-
-# 验证结果
-browser-use state
-```
-
-### 示例 2: 数据提取(使用 JavaScript)
-
-```bash
-# 打开页面
-browser-use open https://news.ycombinator.com
-
-# 使用 JavaScript 提取数据
-browser-use eval "Array.from(document.querySelectorAll('.titleline a')).slice(0,5).map(a => a.textContent)"
-```
-
-### 示例 3: 多会话工作流
-
-```bash
-# 启动工作会话
-browser-use --session work open https://work.example.com
-
-# 启动个人会话
-browser-use --session personal open https://personal.example.com
-
-# 检查工作会话
-browser-use --session work state
-
-# 检查个人会话
-browser-use --session personal state
-
-# 关闭所有会话
-browser-use close --all
-```
-
-### 示例 4: Python 自动化
-
-```bash
-# 打开页面
-browser-use open https://example.com/products
-
-# 使用 Python 进行复杂操作
-browser-use python "
-products = []
-for i in range(20):
-    browser.scroll('down')
-    browser.wait(0.5)
-browser.screenshot('products.png')
-"
-
-# 输出结果
-browser-use python "print(f'Captured {len(products)} products')"
-```
-
-### 示例 5: 使用真实浏览器(已登录会话)
-
-```bash
-# 使用你的 Chrome 浏览器(保留登录状态)
-browser-use --browser real open https://gmail.com
-
-# 已经登录!
-browser-use state
-```
-
-### 示例 6: 可见浏览器调试
-
-```bash
-# 使用可见窗口进行调试
-browser-use --headed open https://example.com
-
-# 查看浏览器操作
-browser-use click 5
-browser-use type "test"
-```
-
-## 最佳实践
-
-1. **始终先运行 `browser-use state`** 查看可用元素及其索引
-2. **使用 `--headed` 进行调试** 可以看到浏览器的实际操作
-3. **会话持久化** - 浏览器在命令之间保持打开状态
-4. **使用 `--json` 进行程序化解析** 输出结果
-5. **Python 变量持久化** - 在同一会话中,`browser-use python` 命令之间变量保持
-6. **真实浏览器模式** 保留你的登录会话和扩展
-
-## 故障排除
-
-### 浏览器无法启动?
-
-```bash
-# 停止卡住的服务器
-browser-use server stop
-
-# 尝试使用可见窗口
-browser-use --headed open <url>
-```
-
-### 找不到元素?
-
-```bash
-# 检查当前元素
-browser-use state
-
-# 元素可能在下方
-browser-use scroll down
-
-# 再次检查
-browser-use state
-```
-
-### 会话问题?
-
-```bash
-# 检查活动会话
-browser-use sessions
-
-# 清理所有会话
-browser-use close --all
-
-# 重新开始
-browser-use open <url>
-```
-
-## 工作原理
-
-CLI 使用会话服务器架构:
-
-1. 第一个命令启动后台服务器(浏览器保持打开)
-2. 后续命令通过 Unix socket 通信(Windows 使用 TCP)
-3. 浏览器在命令之间持久化,实现快速交互
-4. 服务器按需自动启动,使用 `browser-use server stop` 停止
-
-这使得命令延迟约为 50ms,而不是每次都等待浏览器启动。
-
-## 清理
-
-**完成后始终关闭浏览器**。完成浏览器自动化后运行:
-
-```bash
-browser-use close
-```
-
-## 命令别名
-
-以下命令是等效的:
-
-```bash
-browser-use <command>
-browseruse <command>
-bu <command>
-browser <command>
-```
-
-## API Key 配置
-
-在 `.env` 文件中配置 API key:
-
-```bash
-# Browser-Use Cloud API(推荐)
-BROWSER_USE_API_KEY=your-key
-
-# 或使用其他 LLM 提供商
-OPENAI_API_KEY=your-openai-key
-ANTHROPIC_API_KEY=your-anthropic-key
-GOOGLE_API_KEY=your-google-key
-```
-
-新用户注册 [Browser Use Cloud](https://cloud.browser-use.com/new-api-key) 可获得 $10 免费额度。
-
-## 高级功能
-
-### 使用代理(云端浏览器)
-
-```bash
-# 使用云端浏览器(自动支持代理和隐身模式)
-browser-use --browser remote open https://example.com
-```
-
-### 自定义 Chrome 配置文件
-
-```bash
-# 使用特定的 Chrome 配置文件
-browser-use --browser real --profile "Profile 1" open https://example.com
-```
-
-### JSON 输出用于脚本
-
-```bash
-# 获取 JSON 格式的输出
-browser-use --json state
-
-# 在脚本中解析
-STATE=$(browser-use --json state)
-echo $STATE | jq '.data.url'
-```
-
-## 测试验证
-
-### 基本任务验证清单
-
-- [ ] 导航到 URL
-- [ ] 获取页面状态
-- [ ] 点击元素
-- [ ] 输入文本
-- [ ] 提交表单
-- [ ] 截图
-- [ ] 滚动页面
-- [ ] 执行 JavaScript
-- [ ] 管理标签页
-- [ ] 使用 Python 会话
-- [ ] 关闭浏览器
-
-### 测试脚本示例
-
-```bash
-#!/bin/bash
-
-# 测试基本功能
-echo "测试 1: 打开页面"
-browser-use open https://example.com
-
-echo "测试 2: 获取状态"
-browser-use state
-
-echo "测试 3: 截图"
-browser-use screenshot test.png
-
-echo "测试 4: 执行 JavaScript"
-browser-use eval "document.title"
-
-echo "测试 5: Python 会话"
-browser-use python "print('Hello from Python')"
-
-echo "测试 6: 关闭浏览器"
-browser-use close
-
-echo "所有测试完成!"
-```
-
-## 参考资源
-
-- [官方文档](https://docs.browser-use.com)
-- [GitHub 仓库](https://github.com/browser-use/browser-use)
-- [示例代码](https://github.com/browser-use/browser-use/tree/main/examples)
-- [Browser Use Cloud](https://cloud.browser-use.com)
-
----
-
-**文档版本**: 基于 browser-use v0.11.5
-**最后更新**: 2026-01-29

+ 0 - 131
examples/baidu_search_agent.py

@@ -1,131 +0,0 @@
-"""
-百度搜索 Agent 示例
-
-使用 browser-use 工具在百度搜索并返回结果
-
-依赖:
-    pip install playwright httpx python-dotenv
-    playwright install chromium
-
-使用方法:
-    python examples/baidu_search_agent.py
-"""
-
-import os
-import sys
-import json
-import asyncio
-from dotenv import load_dotenv
-
-# 添加项目根目录到 Python 路径
-sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
-
-# 加载环境变量
-load_dotenv()
-
-# 导入框架
-from agent.tools import get_tool_registry
-from agent.runner import AgentRunner
-from agent.llm.providers.gemini import create_gemini_llm_call
-
-# 导入 browser-use 工具(这会自动注册工具)
-sys.path.insert(0, os.path.join(os.path.dirname(os.path.dirname(os.path.abspath(__file__))), "tools"))
-import browserUseTools
-
-
-async def main():
-    print("=" * 60)
-    print("百度搜索 Agent 示例")
-    print("=" * 60)
-    print()
-
-    # 获取工具注册表
-    registry = get_tool_registry()
-
-    # 打印可用工具
-    print("可用的浏览器工具:")
-    for tool_name in registry.get_tool_names():
-        if "browser" in tool_name or "baidu" in tool_name:
-            print(f"  - {tool_name}")
-    print()
-
-    # 创建 Gemini LLM 调用函数
-    gemini_llm_call = create_gemini_llm_call()
-
-    # 创建 Agent Runner
-    runner = AgentRunner(
-        tool_registry=registry,
-        llm_call=gemini_llm_call,
-    )
-
-    # 测试任务:多步骤百度搜索
-    task = """请完成以下任务:
-1. 在百度搜索"瑜伽裤美女"
-2. 返回第2页的搜索结果数据(标题、链接、摘要)
-
-请使用 baidu_search 工具完成。"""
-
-    print(f"任务: {task}")
-    print("-" * 60)
-    print()
-
-    # 运行 Agent
-    async for event in runner.run(
-        task=task,
-        model="gemini-2.5-pro",
-        tools=[
-            "baidu_search"
-        ],
-        max_iterations=5,
-        enable_memory=False,
-        auto_execute_tools=True,
-        system_prompt="""你是一个有用的AI助手,可以使用浏览器工具来帮助用户完成网页操作任务。
-
-可用工具说明:
-- baidu_search: 在百度搜索并返回结果,支持 page 参数用于指定页码
-
-请按照用户的要求,逐步使用这些工具完成任务。"""
-    ):
-        event_type = event.type
-        data = event.data
-
-        if event_type == "trace_started":
-            print(f"✓ Trace 开始: {data['trace_id']}")
-            print()
-
-        elif event_type == "llm_call_completed":
-            print(f"🤖 LLM 响应:")
-            if data.get("content"):
-                print(f"   {data['content']}")
-            if data.get("tool_calls"):
-                print(f"   工具调用: {len(data['tool_calls'])} 个")
-            print(f"   Tokens: {data.get('tokens', 0)}")
-            print()
-
-        elif event_type == "tool_executing":
-            print(f"🔧 执行工具: {data['tool_name']}")
-            print(f"   参数: {json.dumps(data['arguments'], ensure_ascii=False)}")
-
-        elif event_type == "tool_result":
-            result_preview = data['result'][:200] if len(data['result']) > 200 else data['result']
-            print(f"   结果预览: {result_preview}...")
-            print()
-
-        elif event_type == "conclusion":
-            print(f"✅ 最终回答:")
-            print(f"   {data['content']}")
-            print()
-
-        elif event_type == "trace_completed":
-            print(f"✓ Trace 完成")
-            print(f"   总 Tokens: {data.get('total_tokens', 0)}")
-            print(f"   总成本: ${data.get('total_cost', 0):.4f}")
-            print()
-
-        elif event_type == "trace_failed":
-            print(f"❌ Trace 失败: {data.get('error')}")
-            print()
-
-
-if __name__ == "__main__":
-    asyncio.run(main())

+ 0 - 524
examples/test-skill.md

@@ -1,524 +0,0 @@
-# browser-use CLI 工具能力测试报告
-
-## 测试概述
-
-**测试日期**: 2026-01-29
-**测试目标**: 验证 browser-use CLI 工具对基本任务的支持程度
-**对比对象**: browserUseTools.py 底层工具
-**业务场景**: 电商产品搜索与比价流程 (CLI 实现)
-
-## CLI 工具发现
-
-通过 `browser-use --help` 命令,发现 browser-use 提供了完整的命令行工具,包含以下命令:
-
-```bash
-browser-use [-h] [--session SESSION] [--browser {chromium,real,remote}]
-            [--headed] [--profile PROFILE] [--json] [--api-key API_KEY]
-            {open,click,type,input,scroll,back,screenshot,state,switch,
-             close-tab,keys,select,eval,extract,python,run,sessions,close,server}
-```
-
-## CLI 工具命令清单
-
-### 1. 基础浏览器操作
-
-| 命令 | 功能 | 说明 |
-|------|------|------|
-| `open` | 导航到 URL | 在当前会话中打开网页 |
-| `back` | 返回上一页 | 浏览器后退操作 |
-| `state` | 获取浏览器状态 | 返回 URL、标题、可交互元素列表 |
-| `screenshot` | 截图 | 保存当前页面截图 |
-| `close` | 关闭会话 | 关闭浏览器会话 |
-
-### 2. 元素交互操作
-
-| 命令 | 功能 | 说明 |
-|------|------|------|
-| `click` | 点击元素 | 通过索引点击元素 |
-| `type` | 输入文本 | 在当前焦点元素输入文本 |
-| `input` | 指定元素输入 | 在指定索引的元素输入文本 |
-| `keys` | 发送按键 | 发送键盘按键(Enter, Tab, Escape 等) |
-| `scroll` | 滚动页面 | 向上/向下滚动页面 |
-
-### 3. 高级功能
-
-| 命令 | 功能 | 说明 |
-|------|------|------|
-| `select` | 选择下拉选项 | 在下拉菜单中选择选项 |
-| `eval` | 执行 JavaScript | 在页面上下文中执行 JS 代码 |
-| `python` | 执行 Python | 执行 Python 代码访问浏览器对象 |
-| `extract` | 提取数据 | 使用 LLM 提取页面数据(需要 API key) |
-| `run` | 运行 Agent 任务 | 运行完整的 Agent 任务(需要 API key) |
-
-### 4. 标签页管理
-
-| 命令 | 功能 | 说明 |
-|------|------|------|
-| `switch` | 切换标签页 | 切换到指定索引的标签页 |
-| `close-tab` | 关闭标签页 | 关闭指定标签页 |
-
-### 5. 会话管理
-
-| 命令 | 功能 | 说明 |
-|------|------|------|
-| `sessions` | 列出会话 | 显示所有活动的浏览器会话 |
-| `server` | 服务器控制 | 启动/停止 browser-use 服务器 |
-
-## CLI 工具 vs browserUseTools 对比分析
-
-### 功能对比表
-
-| 功能 | CLI 工具 | browserUseTools | 说明 |
-|------|----------|-----------------|------|
-| **导航 (navigate)** | ✓ open | ✓ navigate_to_url | CLI 不支持 new_tab 参数 |
-| **点击 (click)** | ✓ click | ✓ click_element | CLI 只支持索引,Tools 支持坐标 |
-| **输入 (input)** | ✓ type/input | ✓ input_text | 功能相同 |
-| **滚动 (scroll)** | ✓ scroll | ✓ scroll_page | 功能相同 |
-| **返回 (back)** | ✓ back | ✓ go_back | 功能相同 |
-| **截图 (screenshot)** | ✓ screenshot | ✗ | CLI 独有 |
-| **状态获取 (state)** | ✓ state | ✗ | CLI 独有,返回 DOM 状态 |
-| **标签页切换 (switch)** | ✓ switch | ✓ switch_tab | CLI 完整实现,Tools 简化版 |
-| **标签页关闭 (close-tab)** | ✓ close-tab | ✓ close_tab | CLI 完整实现,Tools 简化版 |
-| **发送按键 (keys)** | ✓ keys | ✓ send_keys | 功能相同 |
-| **下拉选择 (select)** | ✓ select | ✓ select_dropdown_option | CLI 只能选择,Tools 可获取选项 |
-| **JavaScript 执行 (eval)** | ✓ eval | ✗ | CLI 独有 |
-| **Python 执行 (python)** | ✓ python | ✗ | CLI 独有 |
-| **数据提取 (extract)** | ✓ extract (需要 API) | ✓ extract_content | 实现方式不同 |
-| **会话管理 (sessions)** | ✓ sessions | ✗ | CLI 独有 |
-| **坐标点击** | ✗ | ✓ click_element | Tools 独有 |
-| **文件上传** | ✗ | ✓ upload_file | Tools 独有 |
-| **获取下拉选项** | ✗ | ✓ get_dropdown_options | Tools 独有 |
-| **任务完成标记** | ✗ | ✓ done | Tools 独有(框架特有) |
-| **网页搜索** | ✗ | ✓ search_web | Tools 独有 |
-
-### 优势对比
-
-#### CLI 工具的优势
-
-1. **会话管理** ✓
-   - 支持命名会话,可以在多个命令之间保持浏览器状态
-   - 支持列出和管理多个会话
-   - 不需要每次都启动新浏览器
-
-2. **DOM 状态维护** ✓
-   - `state` 命令可以获取当前页面的所有可交互元素
-   - 自动维护元素索引映射
-   - 支持通过索引操作元素
-
-3. **代码执行能力** ✓
-   - 支持执行 JavaScript 代码(`eval` 命令)
-   - 支持执行 Python 代码(`python` 命令)
-   - 可以直接访问浏览器对象
-
-4. **截图功能** ✓
-   - 内置截图命令
-   - 支持保存到指定路径
-
-5. **完整的标签页管理** ✓
-   - 真实的标签页切换和关闭
-   - 支持多标签页操作
-
-#### browserUseTools 的优势
-
-1. **坐标点击** ✓
-   - 支持通过坐标点击元素
-   - 不依赖 DOM 状态,更灵活
-
-2. **文件上传** ✓
-   - 支持文件上传功能
-   - CLI 未暴露此功能
-
-3. **获取下拉选项** ✓
-   - 可以获取下拉菜单的所有选项
-   - CLI 只能选择,不能获取
-
-4. **框架集成** ✓
-   - 与 Agent 框架深度集成
-   - 支持 ToolResult 标准返回格式
-   - 支持任务完成标记(done)
-
-5. **网页搜索** ✓
-   - 内置搜索引擎支持(Google, Bing, DuckDuckGo)
-   - CLI 需要手动导航到搜索引擎
-
-## CLI 工具能力分析
-
-### 完全支持的功能(17 个命令)
-
-✓ **open** - 导航到 URL
-✓ **click** - 通过索引点击元素
-✓ **type** - 输入文本
-✓ **input** - 在指定元素输入文本
-✓ **scroll** - 滚动页面
-✓ **back** - 返回上一页
-✓ **screenshot** - 截图
-✓ **state** - 获取浏览器状态
-✓ **switch** - 切换标签页
-✓ **close-tab** - 关闭标签页
-✓ **keys** - 发送键盘按键
-✓ **select** - 选择下拉选项
-✓ **eval** - 执行 JavaScript
-✓ **python** - 执行 Python 代码
-✓ **sessions** - 会话管理
-✓ **close** - 关闭会话
-✓ **server** - 服务器控制
-
-### 需要 API key 的功能
-
-⚠ **extract** - 使用 LLM 提取数据(需要 Browser-Use API key)
-⚠ **run** - 运行 Agent 任务(需要 LLM API key)
-
-### CLI 不支持但 browserUseTools 支持的功能
-
-✗ 通过坐标点击(CLI 只支持索引点击)
-✗ 文件上传(CLI 未暴露此功能)
-✗ 获取下拉选项列表(CLI 只能选择,不能获取选项)
-✗ 新标签页打开 URL(CLI open 命令不支持 new_tab 参数)
-✗ 任务完成标记(done 工具是框架特有的)
-✗ 网页搜索(CLI 需要手动导航)
-
-## browserUseTools 需要改进的地方
-
-基于 CLI 工具的设计,browserUseTools 需要在以下方面改进:
-
-### 1. 浏览器会话管理 ⚠
-
-**现状**: 每次调用都创建新的浏览器实例
-**问题**: 无法在多个操作之间保持状态,资源浪费
-**改进方案**:
-```python
-# 参考 CLI 的会话管理机制
-class BrowserSession:
-    _sessions = {}  # 全局会话池
-
-    @classmethod
-    def get_or_create(cls, session_id):
-        if session_id not in cls._sessions:
-            cls._sessions[session_id] = cls(session_id)
-        return cls._sessions[session_id]
-```
-
-### 2. DOM 状态维护 ⚠
-
-**现状**: 未实现 DOM 状态提取和维护
-**问题**: 索引点击、输入等功能无法正常工作
-**改进方案**:
-```python
-# 参考 CLI 的 state 命令
-async def get_browser_state():
-    """获取浏览器状态,包括 URL、标题、可交互元素"""
-    elements = await page.query_selector_all("a, button, input, select, textarea")
-    return {
-        "url": page.url,
-        "title": await page.title(),
-        "elements": [{"index": i, "tag": elem.tag_name, ...} for i, elem in enumerate(elements)]
-    }
-```
-
-### 3. 标签页管理 ⚠
-
-**现状**: switch_tab 和 close_tab 是简化实现
-**问题**: 未真正操作浏览器标签页
-**改进方案**:
-```python
-# 维护标签页映射
-class BrowserSession:
-    def __init__(self):
-        self.pages = []  # 所有标签页
-        self.current_page_index = 0
-
-    async def switch_tab(self, index):
-        self.current_page_index = index
-        return self.pages[index]
-```
-
-### 4. JavaScript/Python 执行能力 ⚠
-
-**现状**: 未实现
-**问题**: 无法执行自定义代码
-**改进方案**:
-```python
-@tool()
-async def eval_javascript(code: str, uid: str = "") -> ToolResult:
-    """执行 JavaScript 代码"""
-    result = await page.evaluate(code)
-    return ToolResult(title="JavaScript executed", output=str(result))
-```
-
-### 5. 截图功能 ⚠
-
-**现状**: 未实现
-**问题**: 无法保存页面截图
-**改进方案**:
-```python
-@tool()
-async def take_screenshot(path: str, uid: str = "") -> ToolResult:
-    """截取页面截图"""
-    await page.screenshot(path=path)
-    return ToolResult(title="Screenshot saved", output=f"Saved to {path}")
-```
-
-## 实施建议
-
-### 方案 1: 混合使用(推荐 - 短期)
-
-**适用场景**: 快速验证业务流程,时间紧迫
-
-**实施方式**:
-- 使用 CLI 工具处理常规操作(导航、点击、输入、滚动等)
-- 使用 browserUseTools 处理特殊需求(坐标点击、文件上传、获取下拉选项)
-- 通过 subprocess 调用 CLI 命令
-
-**优点**:
-- 快速实现,无需大量开发
-- 充分利用两者优势
-- 可以立即投入使用
-
-**缺点**:
-- 需要维护两套工具的集成
-- 调试困难(CLI 命令输出解析)
-- 性能可能不是最优
-
-**示例代码**:
-```python
-import subprocess
-
-def cli_navigate(url, session="default"):
-    """使用 CLI 导航"""
-    result = subprocess.run(
-        f"browser-use --session {session} open {url}",
-        shell=True, capture_output=True, text=True
-    )
-    return result.returncode == 0
-
-def cli_click(index, session="default"):
-    """使用 CLI 点击"""
-    result = subprocess.run(
-        f"browser-use --session {session} click {index}",
-        shell=True, capture_output=True, text=True
-    )
-    return result.returncode == 0
-```
-
-### 方案 2: 改进 browserUseTools(推荐 - 中长期)
-
-**适用场景**: 长期维护,需要统一接口
-
-**实施方式**:
-1. 参考 CLI 工具实现会话管理
-2. 实现 DOM 状态维护机制
-3. 添加 JavaScript/Python 执行能力
-4. 添加截图功能
-5. 完善标签页管理
-
-**优点**:
-- 统一工具接口,易于维护
-- 性能更好(直接使用 Playwright API)
-- 调试方便
-- 可以添加更多自定义功能
-
-**缺点**:
-- 开发工作量大
-- 需要时间投入
-
-**实施步骤**:
-
-**第 1 周**: 会话管理
-```python
-# 实现全局会话管理器
-class BrowserSessionManager:
-    _sessions = {}
-
-    @classmethod
-    async def get_session(cls, session_id="default"):
-        if session_id not in cls._sessions:
-            cls._sessions[session_id] = await cls._create_session()
-        return cls._sessions[session_id]
-
-    @classmethod
-    async def _create_session(cls):
-        p = await async_playwright().start()
-        browser = await p.chromium.launch(headless=False)
-        context = await browser.new_context()
-        page = await context.new_page()
-        return {"playwright": p, "browser": browser, "context": context, "page": page}
-```
-
-**第 2 周**: DOM 状态维护
-```python
-@tool()
-async def get_browser_state(session_id: str = "default", uid: str = "") -> ToolResult:
-    """获取浏览器状态"""
-    session = await BrowserSessionManager.get_session(session_id)
-    page = session["page"]
-
-    # 提取可交互元素
-    elements = await page.query_selector_all("a, button, input, select, textarea")
-    element_list = []
-    for i, elem in enumerate(elements):
-        element_list.append({
-            "index": i,
-            "tag": await elem.evaluate("el => el.tagName"),
-            "text": await elem.inner_text(),
-            "selector": await elem.evaluate("el => el.id || el.className")
-        })
-
-    return ToolResult(
-        title="Browser state",
-        output=f"URL: {page.url}\nTitle: {await page.title()}",
-        metadata={"elements": element_list}
-    )
-```
-
-**第 3-4 周**: 添加高级功能
-- JavaScript 执行
-- Python 执行
-- 截图功能
-- 完善标签页管理
-
-### 方案 3: 封装 CLI 工具
-
-**适用场景**: 快速原型,不需要深度定制
-
-**实施方式**:
-- 将 CLI 命令封装为 Python 函数
-- 保持与 browserUseTools 相同的接口
-- 使用 subprocess 调用 CLI
-
-**优点**:
-- 快速实现,功能完整
-- 利用 CLI 的所有功能
-
-**缺点**:
-- 依赖外部 CLI 工具
-- 调试困难
-- 性能可能不是最优
-
-## 测试结论
-
-### 核心发现
-
-1. **CLI 工具功能完整性**
-   - browser-use CLI 工具提供了 17+ 个命令
-   - 覆盖了大部分浏览器自动化需求
-   - 支持会话管理,可以在多个命令之间保持浏览器状态
-   - 支持 JavaScript 和 Python 代码执行,扩展性强
-
-2. **browserUseTools 的优势**
-   - 提供了坐标点击功能(CLI 只支持索引点击)
-   - 支持文件上传(CLI 未暴露)
-   - 可以获取下拉选项列表(CLI 只能选择)
-   - 集成了任务完成标记(框架特有)
-   - 内置网页搜索功能
-
-3. **browserUseTools 需要改进的地方**
-   - 缺少会话管理机制(每次调用都创建新浏览器实例)
-   - 缺少 DOM 状态维护(CLI 已实现)
-   - 标签页管理是简化版(CLI 已完整实现)
-   - 缺少 JavaScript/Python 执行能力(CLI 支持)
-   - 缺少截图功能(CLI 支持)
-
-### 最终建议
-
-**短期(1-2 周)**:
-- 采用方案 1(混合使用)
-- 使用 CLI 工具处理常规操作
-- 使用 browserUseTools 处理特殊需求
-- 编写集成测试,确保两套工具协同工作
-
-**中期(1-2 月)**:
-- 采用方案 2(改进 browserUseTools)
-- 重点实现会话管理和 DOM 状态维护
-- 添加 JavaScript 执行能力
-- 完善标签页管理
-
-**长期(3-6 月)**:
-- 优化性能,实现连接池和会话复用
-- 添加更多高级功能(截图、PDF 生成、Cookie 管理等)
-- 完善文档和示例
-- 建立完整的测试套件
-
-### 结论
-
-browser-use CLI 工具功能强大且完整,已经实现了大部分浏览器自动化需求。browserUseTools 在某些特定功能上有优势,但需要学习 CLI 工具的设计,特别是会话管理和 DOM 状态维护机制。
-
-**建议采用混合使用的方案**,在改进 browserUseTools 的同时,充分利用 CLI 工具的现有能力,快速实现业务需求。
-
-## 附录:CLI 命令示例
-
-### 基础操作示例
-
-```bash
-# 1. 打开网页
-browser-use --session my-session --headed open https://www.example.com
-
-# 2. 获取页面状态
-browser-use --session my-session state
-
-# 3. 点击元素(索引 5)
-browser-use --session my-session click 5
-
-# 4. 输入文本
-browser-use --session my-session type "Hello World"
-
-# 5. 发送回车键
-browser-use --session my-session keys Enter
-
-# 6. 滚动页面
-browser-use --session my-session scroll down
-
-# 7. 截图
-browser-use --session my-session screenshot /tmp/screenshot.png
-
-# 8. 执行 JavaScript
-browser-use --session my-session eval "document.title"
-
-# 9. 执行 Python
-browser-use --session my-session python "print(browser.url)"
-
-# 10. 列出所有会话
-browser-use sessions
-
-# 11. 关闭会话
-browser-use --session my-session close
-```
-
-### 电商搜索流程示例
-
-```bash
-# 1. 打开淘宝
-browser-use --session taobao --headed open https://www.taobao.com
-
-# 2. 获取页面状态,找到搜索框索引
-browser-use --session taobao state
-
-# 3. 点击搜索框(假设索引为 3)
-browser-use --session taobao click 3
-
-# 4. 输入搜索关键词
-browser-use --session taobao type "iPhone 15 Pro"
-
-# 5. 发送回车键搜索
-browser-use --session taobao keys Enter
-
-# 6. 等待加载,获取搜索结果
-browser-use --session taobao state
-
-# 7. 点击第一个商品(假设索引为 10)
-browser-use --session taobao click 10
-
-# 8. 滚动查看详情
-browser-use --session taobao scroll down
-
-# 9. 截图保存
-browser-use --session taobao screenshot /tmp/product.png
-
-# 10. 关闭会话
-browser-use --session taobao close
-```
-
----
-
-**报告生成时间**: 2026-01-29
-**测试执行者**: Claude Code
-**报告版本**: 1.0

+ 0 - 337
examples/test-tools.md

@@ -1,337 +0,0 @@
-# browserUseTools.py 工具测试报告
-
-## 测试概述
-
-**测试日期**: 2026-01-29
-**测试文件**: `examples/test-tools.py`
-**被测模块**: `tools/browserUseTools.py`
-**业务场景**: 电商产品搜索与比价流程 (E-commerce Product Search and Price Comparison)
-
-## 业务流程设计
-
-本测试模拟了一个完整的电商产品搜索与比价业务流程,包含以下步骤:
-
-1. **导航到电商网站** - 访问淘宝首页
-2. **搜索产品** - 使用 DuckDuckGo 搜索 iPhone 15 Pro
-3. **提取搜索结果** - 提取产品列表和价格信息
-4. **点击产品链接** - 通过坐标和索引两种方式点击元素
-5. **滚动查看详情** - 向下和向上滚动页面
-6. **输入文本** - 在搜索框输入产品名称
-7. **发送键盘按键** - 发送回车键和 PageDown 键
-8. **切换标签页** - 在多个标签页之间切换
-9. **关闭标签页** - 关闭不需要的标签页
-10. **获取下拉选项** - 获取排序下拉菜单选项
-11. **选择下拉选项** - 选择"价格从低到高"排序
-12. **上传文件** - 上传产品图片进行搜索
-13. **返回上一页** - 浏览器后退操作
-14. **完成任务** - 标记任务完成
-
-## 测试结果汇总
-
-| 指标 | 数值 |
-|------|------|
-| 总测试数 | 17 |
-| 通过 | 17 ✓ |
-| 失败 | 0 ✗ |
-| 通过率 | 100.0% |
-
-## 详细测试结果
-
-### 1. 导航工具 (Navigation Tools)
-
-#### 1.1 navigate_to_url
-- **测试场景**: 导航到淘宝首页
-- **测试参数**: `url="https://www.taobao.com", new_tab=False`
-- **测试结果**: ✓ PASS
-- **工具状态**: 可用
-- **说明**: 工具能够正常调用,但由于每次调用都创建新的浏览器实例,实际使用时需要改进为共享浏览器会话
-
-#### 1.2 go_back
-- **测试场景**: 返回上一页
-- **测试结果**: ✓ PASS
-- **工具状态**: 可用
-- **说明**: 工具能够正常调用,但同样存在浏览器实例管理问题
-
-### 2. 搜索工具 (Search Tools)
-
-#### 2.1 search_web
-- **测试场景**: 使用 DuckDuckGo 搜索产品
-- **测试参数**: `query="iPhone 15 Pro", engine="duckduckgo"`
-- **测试结果**: ✓ PASS
-- **工具状态**: 可用
-- **支持的搜索引擎**: duckduckgo, google, bing
-
-### 3. 内容提取工具 (Content Extraction Tools)
-
-#### 3.1 extract_content
-- **测试场景**: 提取页面内容和链接
-- **测试参数**: `query="产品列表和价格信息", extract_links=True, start_from_char=0`
-- **测试结果**: ✓ PASS
-- **工具状态**: 可用
-- **功能特性**:
-  - 支持提取页面文本内容
-  - 支持提取链接(可选)
-  - 支持从指定字符位置开始提取(用于长内容分页)
-
-### 4. 点击工具 (Click Tools)
-
-#### 4.1 click_element (坐标点击)
-- **测试场景**: 通过坐标点击元素
-- **测试参数**: `coordinate_x=500, coordinate_y=300`
-- **测试结果**: ✓ PASS
-- **工具状态**: 可用
-- **说明**: 坐标点击方式更可靠,不依赖 DOM 状态
-
-#### 4.2 click_element (索引点击)
-- **测试场景**: 通过索引点击元素
-- **测试参数**: `index=5`
-- **测试结果**: ✓ PASS
-- **工具状态**: 可用(需要 DOM 状态支持)
-- **说明**: 索引点击需要维护 DOM 状态映射,当前为占位符实现
-
-### 5. 滚动工具 (Scroll Tools)
-
-#### 5.1 scroll_page (向下滚动)
-- **测试场景**: 向下滚动页面
-- **测试参数**: `down=True, pages=1.0`
-- **测试结果**: ✓ PASS
-- **工具状态**: 可用
-
-#### 5.2 scroll_page (向上滚动)
-- **测试场景**: 向上滚动页面
-- **测试参数**: `down=False, pages=0.5`
-- **测试结果**: ✓ PASS
-- **工具状态**: 可用
-- **功能特性**:
-  - 支持向上/向下滚动
-  - 支持指定滚动页数(0.5=半页, 1=全页, 10=到底部/顶部)
-  - 支持滚动特定元素(通过 index 参数)
-
-### 6. 文本输入工具 (Input Tools)
-
-#### 6.1 input_text
-- **测试场景**: 在搜索框输入文本
-- **测试参数**: `index=0, text="iPhone 15 Pro Max", clear=True`
-- **测试结果**: ✓ PASS
-- **工具状态**: 可用(需要 DOM 状态支持)
-- **功能特性**:
-  - 支持清除现有文本后输入
-  - 支持追加输入(clear=False)
-
-### 7. 键盘按键工具 (Keyboard Tools)
-
-#### 7.1 send_keys (回车键)
-- **测试场景**: 发送回车键
-- **测试参数**: `keys="Enter"`
-- **测试结果**: ✓ PASS
-- **工具状态**: 可用
-
-#### 7.2 send_keys (PageDown 键)
-- **测试场景**: 发送 PageDown 键
-- **测试参数**: `keys="PageDown"`
-- **测试结果**: ✓ PASS
-- **工具状态**: 可用
-- **支持的按键类型**:
-  - 单个按键: Enter, Escape, PageDown, Tab
-  - 组合键: Control+o, Shift+Tab, Alt+F4
-  - 功能键: F1-F12
-
-### 8. 标签页管理工具 (Tab Management Tools)
-
-#### 8.1 switch_tab
-- **测试场景**: 切换到另一个标签页
-- **测试参数**: `tab_id="abcd"`
-- **测试结果**: ✓ PASS
-- **工具状态**: 可用(当前为简化实现)
-
-#### 8.2 close_tab
-- **测试场景**: 关闭标签页
-- **测试参数**: `tab_id="abcd"`
-- **测试结果**: ✓ PASS
-- **工具状态**: 可用(当前为简化实现)
-
-### 9. 下拉菜单工具 (Dropdown Tools)
-
-#### 9.1 get_dropdown_options
-- **测试场景**: 获取下拉选项
-- **测试参数**: `index=3`
-- **测试结果**: ✓ PASS
-- **工具状态**: 可用(需要 DOM 状态支持)
-
-#### 9.2 select_dropdown_option
-- **测试场景**: 选择下拉选项
-- **测试参数**: `index=3, text="价格从低到高"`
-- **测试结果**: ✓ PASS
-- **工具状态**: 可用(需要 DOM 状态支持)
-
-### 10. 文件上传工具 (File Upload Tools)
-
-#### 10.1 upload_file
-- **测试场景**: 上传文件
-- **测试参数**: `index=2, path="/tmp/test_image.jpg"`
-- **测试结果**: ✓ PASS
-- **工具状态**: 可用(需要 DOM 状态支持)
-
-### 11. 任务完成工具 (Done Tool)
-
-#### 11.1 done
-- **测试场景**: 标记任务完成
-- **测试参数**: `text="电商产品搜索与比价流程测试完成!", success=True`
-- **测试结果**: ✓ PASS
-- **工具状态**: 完全可用
-- **功能特性**:
-  - 支持成功/失败状态标记
-  - 支持返回最终消息
-  - 支持附加文件列表
-
-## 工具可用性分析
-
-### 完全可用的工具(无需额外依赖)
-
-1. **done** - 任务完成标记
-2. **switch_tab** - 标签页切换(简化版)
-3. **close_tab** - 关闭标签页(简化版)
-
-### 可用但需要改进的工具(浏览器实例管理)
-
-这些工具功能正常,但每次调用都创建新的浏览器实例,需要改进为共享浏览器会话:
-
-1. **navigate_to_url** - 页面导航
-2. **go_back** - 返回上一页
-3. **search_web** - 网页搜索
-4. **extract_content** - 内容提取
-5. **click_element** - 元素点击(坐标方式)
-6. **scroll_page** - 页面滚动
-7. **send_keys** - 键盘按键
-
-### 需要 DOM 状态支持的工具
-
-这些工具需要维护 DOM 状态来将索引映射到实际的 CSS 选择器:
-
-1. **click_element** - 元素点击(索引方式)
-2. **input_text** - 文本输入
-3. **get_dropdown_options** - 获取下拉选项
-4. **select_dropdown_option** - 选择下拉选项
-5. **upload_file** - 文件上传
-
-## 问题与建议
-
-### 1. 浏览器实例管理问题
-
-**问题描述**: 当前每个工具调用都创建新的浏览器实例,导致:
-- 无法在多个操作之间保持状态
-- 资源浪费
-- 性能低下
-
-**建议方案**:
-```python
-# 使用全局浏览器实例或上下文管理器
-class BrowserSession:
-    def __init__(self):
-        self.browser = None
-        self.context = None
-        self.page = None
-
-    async def __aenter__(self):
-        p = await async_playwright().start()
-        self.browser = await p.chromium.launch(headless=False)
-        self.context = await self.browser.new_context()
-        self.page = await self.context.new_page()
-        return self
-
-    async def __aexit__(self, *args):
-        await self.browser.close()
-```
-
-### 2. DOM 状态管理缺失
-
-**问题描述**: 部分工具需要 DOM 状态来将索引映射到选择器,但当前未实现。
-
-**建议方案**:
-- 实现 DOM 状态提取和维护机制
-- 为每个可交互元素分配唯一索引
-- 维护索引到选择器的映射表
-
-### 3. 错误处理改进
-
-**问题描述**: 当前所有异常都被捕获并返回失败结果,但错误信息不够详细。
-
-**建议方案**:
-- 添加更详细的错误日志
-- 区分不同类型的错误(网络错误、元素未找到、超时等)
-- 提供错误恢复建议
-
-### 4. 标签页管理简化
-
-**问题描述**: switch_tab 和 close_tab 当前为简化实现,未真正操作浏览器标签页。
-
-**建议方案**:
-- 实现真实的标签页管理
-- 维护标签页 ID 到 Page 对象的映射
-- 支持标签页列表查询
-
-## 测试结论
-
-### 总体评价
-
-browserUseTools.py 中的所有工具都已成功实现并通过测试,**通过率 100%**。工具覆盖了浏览器自动化的主要功能:
-
-- ✅ 页面导航和历史管理
-- ✅ 内容提取和搜索
-- ✅ 元素交互(点击、输入、滚动)
-- ✅ 键盘操作
-- ✅ 标签页管理
-- ✅ 下拉菜单操作
-- ✅ 文件上传
-- ✅ 任务完成标记
-
-### 实际应用建议
-
-1. **短期使用**: 当前实现可以用于简单的单步操作测试
-2. **生产环境**: 需要实现浏览器会话管理和 DOM 状态维护
-3. **性能优化**: 建议实现连接池和会话复用机制
-4. **功能扩展**: 可以添加截图、PDF 生成、Cookie 管理等功能
-
-### 下一步工作
-
-1. 实现浏览器会话管理器
-2. 实现 DOM 状态提取和维护
-3. 完善标签页管理功能
-4. 添加更多错误处理和日志
-5. 编写集成测试用例
-6. 性能测试和优化
-
-## 附录:测试命令
-
-```bash
-# 运行测试
-python examples/test-tools.py
-
-# 查看测试输出
-cat /tmp/test-tools-output.txt
-```
-
-## 附录:工具清单
-
-| 序号 | 工具名称 | 功能描述 | 状态 |
-|------|---------|---------|------|
-| 1 | navigate_to_url | 导航到指定 URL | ✓ 可用 |
-| 2 | go_back | 返回上一页 | ✓ 可用 |
-| 3 | search_web | 网页搜索 | ✓ 可用 |
-| 4 | extract_content | 提取页面内容 | ✓ 可用 |
-| 5 | click_element | 点击元素 | ✓ 可用 |
-| 6 | scroll_page | 滚动页面 | ✓ 可用 |
-| 7 | input_text | 输入文本 | ✓ 可用 |
-| 8 | send_keys | 发送按键 | ✓ 可用 |
-| 9 | switch_tab | 切换标签页 | ✓ 可用 |
-| 10 | close_tab | 关闭标签页 | ✓ 可用 |
-| 11 | get_dropdown_options | 获取下拉选项 | ✓ 可用 |
-| 12 | select_dropdown_option | 选择下拉选项 | ✓ 可用 |
-| 13 | upload_file | 上传文件 | ✓ 可用 |
-| 14 | done | 任务完成 | ✓ 可用 |
-
----
-
-**报告生成时间**: 2026-01-29
-**测试执行者**: Claude Code
-**报告版本**: 1.0

+ 0 - 381
examples/test-tools.py

@@ -1,381 +0,0 @@
-# -*- coding: utf-8 -*-
-"""
-测试 browserUseTools.py 中所有工具的可用性
-Test all tools in browserUseTools.py
-
-业务场景:电商产品搜索与比价流程
-Business Scenario: E-commerce Product Search and Price Comparison
-
-流程步骤:
-1. 导航到电商网站
-2. 搜索产品
-3. 提取搜索结果
-4. 点击产品链接
-5. 滚动查看详情
-6. 输入文本(搜索框)
-7. 发送键盘按键
-8. 切换标签页
-9. 关闭标签页
-10. 获取下拉选项
-11. 选择下拉选项
-12. 上传文件
-13. 返回上一页
-14. 完成任务
-"""
-
-import asyncio
-import sys
-import os
-
-# 添加项目根目录到 Python 路径
-sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
-
-# 导入所有工具
-from tools.browserUseTools import (
-    navigate_to_url,
-    go_back,
-    click_element,
-    input_text,
-    send_keys,
-    extract_content,
-    search_web,
-    scroll_page,
-    switch_tab,
-    close_tab,
-    get_dropdown_options,
-    select_dropdown_option,
-    upload_file,
-    done
-)
-
-
-async def test_all_tools():
-    """测试所有 browserUseTools 工具"""
-
-    print("=" * 80)
-    print("开始测试 browserUseTools.py 中的所有工具")
-    print("Business Scenario: E-commerce Product Search and Price Comparison")
-    print("=" * 80)
-    print()
-
-    results = []
-
-    # ============================================================
-    # 测试 1: 导航工具 (Navigation Tools)
-    # ============================================================
-    print("【测试 1】navigate_to_url - 导航到淘宝首页")
-    print("-" * 80)
-    try:
-        result = await navigate_to_url("https://www.taobao.com", new_tab=False)
-        print(f"✓ 成功: {result.title}")
-        print(f"  输出: {result.output}")
-        print(f"  元数据: {result.metadata}")
-        results.append(("navigate_to_url", "PASS", result.title))
-        await asyncio.sleep(2)  # 等待页面加载
-    except Exception as e:
-        print(f"✗ 失败: {str(e)}")
-        results.append(("navigate_to_url", "FAIL", str(e)))
-    print()
-
-    # ============================================================
-    # 测试 2: 搜索工具 (Search Tools)
-    # ============================================================
-    print("【测试 2】search_web - 使用 DuckDuckGo 搜索产品")
-    print("-" * 80)
-    try:
-        result = await search_web("iPhone 15 Pro", engine="duckduckgo")
-        print(f"✓ 成功: {result.title}")
-        print(f"  输出: {result.output[:200]}...")
-        print(f"  元数据: {result.metadata}")
-        results.append(("search_web", "PASS", result.title))
-        await asyncio.sleep(2)
-    except Exception as e:
-        print(f"✗ 失败: {str(e)}")
-        results.append(("search_web", "FAIL", str(e)))
-    print()
-
-    # ============================================================
-    # 测试 3: 内容提取工具 (Content Extraction Tools)
-    # ============================================================
-    print("【测试 3】extract_content - 提取页面内容和链接")
-    print("-" * 80)
-    try:
-        result = await extract_content(
-            query="产品列表和价格信息",
-            extract_links=True,
-            start_from_char=0
-        )
-        print(f"✓ 成功: {result.title}")
-        print(f"  输出: {result.output[:200]}...")
-        if result.metadata and "links" in result.metadata:
-            print(f"  提取到 {len(result.metadata['links'])} 个链接")
-        results.append(("extract_content", "PASS", result.title))
-        await asyncio.sleep(1)
-    except Exception as e:
-        print(f"✗ 失败: {str(e)}")
-        results.append(("extract_content", "FAIL", str(e)))
-    print()
-
-    # ============================================================
-    # 测试 4: 点击工具 (Click Tools)
-    # ============================================================
-    print("【测试 4】click_element - 通过坐标点击元素")
-    print("-" * 80)
-    try:
-        result = await click_element(coordinate_x=500, coordinate_y=300)
-        print(f"✓ 成功: {result.title}")
-        print(f"  输出: {result.output}")
-        results.append(("click_element (coordinates)", "PASS", result.title))
-        await asyncio.sleep(1)
-    except Exception as e:
-        print(f"✗ 失败: {str(e)}")
-        results.append(("click_element (coordinates)", "FAIL", str(e)))
-    print()
-
-    print("【测试 5】click_element - 通过索引点击元素")
-    print("-" * 80)
-    try:
-        result = await click_element(index=5)
-        print(f"✓ 成功: {result.title}")
-        print(f"  输出: {result.output}")
-        results.append(("click_element (index)", "PASS", result.title))
-        await asyncio.sleep(1)
-    except Exception as e:
-        print(f"✗ 失败: {str(e)}")
-        results.append(("click_element (index)", "FAIL", str(e)))
-    print()
-
-    # ============================================================
-    # 测试 6: 滚动工具 (Scroll Tools)
-    # ============================================================
-    print("【测试 6】scroll_page - 向下滚动页面")
-    print("-" * 80)
-    try:
-        result = await scroll_page(down=True, pages=1.0)
-        print(f"✓ 成功: {result.title}")
-        print(f"  输出: {result.output}")
-        results.append(("scroll_page (down)", "PASS", result.title))
-        await asyncio.sleep(1)
-    except Exception as e:
-        print(f"✗ 失败: {str(e)}")
-        results.append(("scroll_page (down)", "FAIL", str(e)))
-    print()
-
-    print("【测试 7】scroll_page - 向上滚动页面")
-    print("-" * 80)
-    try:
-        result = await scroll_page(down=False, pages=0.5)
-        print(f"✓ 成功: {result.title}")
-        print(f"  输出: {result.output}")
-        results.append(("scroll_page (up)", "PASS", result.title))
-        await asyncio.sleep(1)
-    except Exception as e:
-        print(f"✗ 失败: {str(e)}")
-        results.append(("scroll_page (up)", "FAIL", str(e)))
-    print()
-
-    # ============================================================
-    # 测试 8: 文本输入工具 (Input Tools)
-    # ============================================================
-    print("【测试 8】input_text - 在搜索框输入文本")
-    print("-" * 80)
-    try:
-        result = await input_text(index=0, text="iPhone 15 Pro Max", clear=True)
-        print(f"✓ 成功: {result.title}")
-        print(f"  输出: {result.output}")
-        print(f"  元数据: {result.metadata}")
-        results.append(("input_text", "PASS", result.title))
-        await asyncio.sleep(1)
-    except Exception as e:
-        print(f"✗ 失败: {str(e)}")
-        results.append(("input_text", "FAIL", str(e)))
-    print()
-
-    # ============================================================
-    # 测试 9: 键盘按键工具 (Keyboard Tools)
-    # ============================================================
-    print("【测试 9】send_keys - 发送回车键")
-    print("-" * 80)
-    try:
-        result = await send_keys("Enter")
-        print(f"✓ 成功: {result.title}")
-        print(f"  输出: {result.output}")
-        results.append(("send_keys (Enter)", "PASS", result.title))
-        await asyncio.sleep(2)
-    except Exception as e:
-        print(f"✗ 失败: {str(e)}")
-        results.append(("send_keys (Enter)", "FAIL", str(e)))
-    print()
-
-    print("【测试 10】send_keys - 发送 PageDown 键")
-    print("-" * 80)
-    try:
-        result = await send_keys("PageDown")
-        print(f"✓ 成功: {result.title}")
-        print(f"  输出: {result.output}")
-        results.append(("send_keys (PageDown)", "PASS", result.title))
-        await asyncio.sleep(1)
-    except Exception as e:
-        print(f"✗ 失败: {str(e)}")
-        results.append(("send_keys (PageDown)", "FAIL", str(e)))
-    print()
-
-    # ============================================================
-    # 测试 11: 标签页管理工具 (Tab Management Tools)
-    # ============================================================
-    print("【测试 11】switch_tab - 切换到另一个标签页")
-    print("-" * 80)
-    try:
-        result = await switch_tab(tab_id="abcd")
-        print(f"✓ 成功: {result.title}")
-        print(f"  输出: {result.output}")
-        results.append(("switch_tab", "PASS", result.title))
-        await asyncio.sleep(1)
-    except Exception as e:
-        print(f"✗ 失败: {str(e)}")
-        results.append(("switch_tab", "FAIL", str(e)))
-    print()
-
-    print("【测试 12】close_tab - 关闭标签页")
-    print("-" * 80)
-    try:
-        result = await close_tab(tab_id="abcd")
-        print(f"✓ 成功: {result.title}")
-        print(f"  输出: {result.output}")
-        results.append(("close_tab", "PASS", result.title))
-        await asyncio.sleep(1)
-    except Exception as e:
-        print(f"✗ 失败: {str(e)}")
-        results.append(("close_tab", "FAIL", str(e)))
-    print()
-
-    # ============================================================
-    # 测试 13: 下拉菜单工具 (Dropdown Tools)
-    # ============================================================
-    print("【测试 13】get_dropdown_options - 获取下拉选项")
-    print("-" * 80)
-    try:
-        result = await get_dropdown_options(index=3)
-        print(f"✓ 成功: {result.title}")
-        print(f"  输出: {result.output}")
-        results.append(("get_dropdown_options", "PASS", result.title))
-        await asyncio.sleep(1)
-    except Exception as e:
-        print(f"✗ 失败: {str(e)}")
-        results.append(("get_dropdown_options", "FAIL", str(e)))
-    print()
-
-    print("【测试 14】select_dropdown_option - 选择下拉选项")
-    print("-" * 80)
-    try:
-        result = await select_dropdown_option(index=3, text="价格从低到高")
-        print(f"✓ 成功: {result.title}")
-        print(f"  输出: {result.output}")
-        results.append(("select_dropdown_option", "PASS", result.title))
-        await asyncio.sleep(1)
-    except Exception as e:
-        print(f"✗ 失败: {str(e)}")
-        results.append(("select_dropdown_option", "FAIL", str(e)))
-    print()
-
-    # ============================================================
-    # 测试 15: 文件上传工具 (File Upload Tools)
-    # ============================================================
-    print("【测试 15】upload_file - 上传文件")
-    print("-" * 80)
-    try:
-        result = await upload_file(index=2, path="/tmp/test_image.jpg")
-        print(f"✓ 成功: {result.title}")
-        print(f"  输出: {result.output}")
-        results.append(("upload_file", "PASS", result.title))
-        await asyncio.sleep(1)
-    except Exception as e:
-        print(f"✗ 失败: {str(e)}")
-        results.append(("upload_file", "FAIL", str(e)))
-    print()
-
-    # ============================================================
-    # 测试 16: 返回工具 (Go Back Tool)
-    # ============================================================
-    print("【测试 16】go_back - 返回上一页")
-    print("-" * 80)
-    try:
-        result = await go_back()
-        print(f"✓ 成功: {result.title}")
-        print(f"  输出: {result.output}")
-        results.append(("go_back", "PASS", result.title))
-        await asyncio.sleep(1)
-    except Exception as e:
-        print(f"✗ 失败: {str(e)}")
-        results.append(("go_back", "FAIL", str(e)))
-    print()
-
-    # ============================================================
-    # 测试 17: 任务完成工具 (Done Tool)
-    # ============================================================
-    print("【测试 17】done - 标记任务完成")
-    print("-" * 80)
-    try:
-        result = await done(
-            text="电商产品搜索与比价流程测试完成!",
-            success=True,
-            files_to_display=None
-        )
-        print(f"✓ 成功: {result.title}")
-        print(f"  输出: {result.output}")
-        print(f"  元数据: {result.metadata}")
-        results.append(("done", "PASS", result.title))
-    except Exception as e:
-        print(f"✗ 失败: {str(e)}")
-        results.append(("done", "FAIL", str(e)))
-    print()
-
-    # ============================================================
-    # 测试结果汇总
-    # ============================================================
-    print("=" * 80)
-    print("测试结果汇总 (Test Results Summary)")
-    print("=" * 80)
-    print()
-
-    passed = sum(1 for _, status, _ in results if status == "PASS")
-    failed = sum(1 for _, status, _ in results if status == "FAIL")
-    total = len(results)
-
-    print(f"总测试数: {total}")
-    print(f"通过: {passed} ✓")
-    print(f"失败: {failed} ✗")
-    print(f"通过率: {passed/total*100:.1f}%")
-    print()
-
-    print("详细结果:")
-    print("-" * 80)
-    for tool_name, status, message in results:
-        status_icon = "✓" if status == "PASS" else "✗"
-        print(f"{status_icon} {tool_name:40s} {status:6s} - {message}")
-    print()
-
-    return results
-
-
-async def main():
-    """主函数"""
-    try:
-        results = await test_all_tools()
-
-        # 生成测试报告
-        print("=" * 80)
-        print("测试完成!结果已保存到 test-tools.md")
-        print("=" * 80)
-
-        return results
-    except Exception as e:
-        print(f"测试过程中发生错误: {str(e)}")
-        import traceback
-        traceback.print_exc()
-        return []
-
-
-if __name__ == "__main__":
-    # 运行测试
-    asyncio.run(main())

+ 0 - 581
examples/tools_examples.py

@@ -1,581 +0,0 @@
-"""
-工具系统完整示例
-
-本文件展示 @tool 装饰器的所有用法,包括:
-
-## 基础功能
-1. 最简形式
-2. 带 i18n 展示信息
-3. 带可编辑参数
-4. 需要用户确认
-5. 带 context 参数
-6. 同步工具
-7. 复杂返回类型
-
-## 高级功能
-8. 域名过滤(URL Patterns)
-9. 敏感数据处理(<secret> 占位符 + TOTP)
-10. 工具使用统计
-11. 组合所有功能
-
-注意:
-- uid 参数会由框架自动注入,不需要用户传递
-- context 参数用于传递额外上下文(如浏览器会话、当前 URL 等)
-- 返回值可以是字符串、字典或 ToolResult
-"""
-
-import asyncio
-import json
-from typing import List, Dict, Any, Optional
-from agent import tool, ToolResult, ToolContext, get_tool_registry
-
-
-# ============================================================
-# 基础功能示例
-# ============================================================
-
-# 1. 最简形式
-@tool()
-async def hello_world(name: str, uid: str = "") -> Dict[str, str]:
-	"""
-	最简单的工具示例
-
-	Args:
-		name: 要问候的名字
-		uid: 用户ID(自动注入)
-
-	Returns:
-		包含问候语的字典
-	"""
-	return {"greeting": f"Hello, {name}!"}
-
-
-# 2. 带 i18n 展示信息的工具
-@tool(
-	display={
-		"zh": {
-			"name": "搜索内容",
-			"params": {
-				"query": "搜索关键词",
-				"limit": "返回数量"
-			}
-		},
-		"en": {
-			"name": "Search Content",
-			"params": {
-				"query": "Search query",
-				"limit": "Number of results"
-			}
-		}
-	}
-)
-async def search_content(
-	query: str,
-	limit: int = 10,
-	uid: str = ""
-) -> List[Dict[str, Any]]:
-	"""
-	搜索用户的内容
-
-	使用语义搜索查找相关内容。display 参数用于前端展示:
-	- 工具名称会根据用户语言显示为"搜索内容"或"Search Content"
-	- 参数名称也会相应翻译
-
-	Args:
-		query: 搜索查询文本
-		limit: 返回结果数量(默认10)
-		uid: 用户ID(自动注入)
-
-	Returns:
-		搜索结果列表,每个包含 id, title, content, score
-	"""
-	# 实际实现中会调用向量搜索
-	return [
-		{
-			"id": "doc_001",
-			"title": f"关于 {query} 的文档",
-			"content": f"这是与 {query} 相关的内容...",
-			"score": 0.95
-		}
-	]
-
-
-# 3. 带可编辑参数的工具
-@tool(
-	editable_params=["query", "filters"],
-	display={
-		"zh": {
-			"name": "高级搜索",
-			"params": {
-				"query": "搜索关键词",
-				"filters": "过滤条件",
-				"sort_by": "排序方式"
-			}
-		}
-	}
-)
-async def advanced_search(
-	query: str,
-	filters: Optional[Dict[str, Any]] = None,
-	sort_by: str = "relevance",
-	limit: int = 20,
-	uid: str = ""
-) -> Dict[str, Any]:
-	"""
-	高级搜索工具(允许用户编辑参数)
-
-	editable_params 指定哪些参数允许用户在 LLM 生成后编辑:
-	- LLM 会先生成 query 和 filters
-	- 用户可以在确认前修改这些参数
-	- 适用于搜索、创建等需要用户微调的场景
-
-	Args:
-		query: 搜索查询
-		filters: 过滤条件(如 {"type": "note", "date_range": "7d"})
-		sort_by: 排序方式(relevance/date/title)
-		limit: 返回数量
-		uid: 用户ID(自动注入)
-
-	Returns:
-		搜索结果和元数据
-	"""
-	return {
-		"results": [
-			{"id": "1", "title": "Result 1", "score": 0.9},
-			{"id": "2", "title": "Result 2", "score": 0.8},
-		],
-		"total": 42,
-		"query": query,
-		"filters_applied": filters or {},
-		"sort_by": sort_by
-	}
-
-
-# 4. 需要用户确认的危险操作
-@tool(
-	requires_confirmation=True,
-	display={
-		"zh": {
-			"name": "删除内容",
-			"params": {
-				"content_id": "内容ID",
-				"permanent": "永久删除"
-			}
-		}
-	}
-)
-async def delete_content(
-	content_id: str,
-	permanent: bool = False,
-	uid: str = ""
-) -> Dict[str, Any]:
-	"""
-	删除内容(需要用户确认)
-
-	requires_confirmation=True 表示这是一个危险操作:
-	- LLM 调用此工具时,不会立即执行
-	- 会先向用户展示操作详情,等待确认
-	- 用户确认后才会真正执行
-
-	适用场景:删除操作、发送消息、修改重要设置、任何不可逆操作
-
-	Args:
-		content_id: 要删除的内容ID
-		permanent: 是否永久删除(False=移到回收站)
-		uid: 用户ID(自动注入)
-
-	Returns:
-		删除结果
-	"""
-	return {
-		"success": True,
-		"content_id": content_id,
-		"permanent": permanent,
-		"message": f"内容 {content_id} 已{'永久删除' if permanent else '移到回收站'}"
-	}
-
-
-# 5. 带 context 参数的工具
-@tool(
-	display={
-		"zh": {"name": "获取相关推荐", "params": {"top_k": "推荐数量"}}
-	}
-)
-async def get_recommendations(
-	top_k: int = 5,
-	uid: str = "",
-	context: Optional[Dict[str, Any]] = None
-) -> List[Dict[str, Any]]:
-	"""
-	获取相关推荐(使用 context 获取额外信息)
-
-	context 参数用于传递执行上下文,由框架自动注入:
-	- 当前阅读位置 (current_location)
-	- 当前会话 ID (session_id)
-	- 排除的内容 ID (exclude_ids)
-
-	Args:
-		top_k: 返回推荐数量
-		uid: 用户ID(自动注入)
-		context: 执行上下文(自动注入)
-
-	Returns:
-		推荐列表
-	"""
-	current_location = None
-	if context:
-		current_location = context.get("current_location")
-
-	return [
-		{
-			"id": "rec_001",
-			"title": "推荐内容 1",
-			"reason": f"基于当前位置 {current_location}" if current_location else "基于您的兴趣"
-		}
-	]
-
-
-# 6. 同步工具(非 async)
-@tool()
-def format_text(
-	text: str,
-	format_type: str = "markdown",
-	uid: str = ""
-) -> str:
-	"""
-	格式化文本(同步工具)
-
-	不需要 async 的工具可以定义为普通函数。
-	框架会自动检测并正确调用。
-
-	适用于:纯计算操作、文本处理、不需要 I/O 的操作
-
-	Args:
-		text: 要格式化的文本
-		format_type: 格式类型(markdown/plain/html)
-		uid: 用户ID(自动注入)
-
-	Returns:
-		格式化后的文本
-	"""
-	if format_type == "markdown":
-		return f"**{text}**"
-	elif format_type == "html":
-		return f"<p>{text}</p>"
-	else:
-		return text
-
-
-# 7. 使用 ToolResult 的工具
-@tool()
-async def analyze_content(
-	content_id: str,
-	analysis_types: Optional[List[str]] = None,
-	uid: str = ""
-) -> ToolResult:
-	"""
-	分析内容(使用 ToolResult)
-
-	ToolResult 支持双层记忆管理:
-	- output: 完整结果(可能很长)
-	- long_term_memory: 简短摘要(永久保存)
-
-	Args:
-		content_id: 要分析的内容ID
-		analysis_types: 分析类型列表(sentiment/keywords/summary)
-		uid: 用户ID(自动注入)
-
-	Returns:
-		ToolResult 包含分析结果
-	"""
-	types = analysis_types or ["sentiment", "keywords"]
-
-	result = {
-		"content_id": content_id,
-		"analyses": {}
-	}
-
-	if "sentiment" in types:
-		result["analyses"]["sentiment"] = {
-			"score": 0.8,
-			"label": "positive",
-			"confidence": 0.92
-		}
-
-	if "keywords" in types:
-		result["analyses"]["keywords"] = [
-			{"word": "AI", "weight": 0.9},
-			{"word": "学习", "weight": 0.7}
-		]
-
-	return ToolResult(
-		title=f"Analysis of {content_id}",
-		output=json.dumps(result, indent=2, ensure_ascii=False),
-		long_term_memory=f"Analyzed {content_id}: {', '.join(types)}",
-		metadata={"types": types}
-	)
-
-
-# ============================================================
-# 高级功能示例
-# ============================================================
-
-# 8. 域名过滤示例
-@tool(url_patterns=["*.google.com", "www.google.*"])
-async def google_search(query: str, uid: str = "") -> ToolResult:
-	"""
-	Google 搜索(仅在 Google 页面可用)
-
-	使用 url_patterns 限制工具只在特定域名显示。
-	在 Google 页面时,此工具会出现在可用工具列表中。
-	在其他页面时,此工具会被过滤掉。
-
-	Args:
-		query: 搜索查询
-		uid: 用户ID(自动注入)
-
-	Returns:
-		搜索结果
-	"""
-	return ToolResult(
-		title="Google Search",
-		output=f"Searching Google for: {query}",
-		long_term_memory=f"Searched Google for '{query}'"
-	)
-
-
-@tool(url_patterns=["*.github.com"])
-async def create_github_issue(
-	title: str,
-	body: str,
-	uid: str = ""
-) -> ToolResult:
-	"""
-	创建 GitHub Issue(仅在 GitHub 页面可用)
-
-	Args:
-		title: Issue 标题
-		body: Issue 内容
-		uid: 用户ID(自动注入)
-
-	Returns:
-		创建结果
-	"""
-	return ToolResult(
-		title="Issue Created",
-		output=f"Created issue: {title}",
-		long_term_memory=f"Created GitHub issue: {title}"
-	)
-
-
-@tool()  # 无 url_patterns,所有页面都可用
-async def take_screenshot(uid: str = "") -> ToolResult:
-	"""截图(所有页面都可用)"""
-	return ToolResult(
-		title="Screenshot",
-		output="Screenshot taken",
-		attachments=["screenshot_001.png"]
-	)
-
-
-# 9. 敏感数据处理示例
-@tool(url_patterns=["*.github.com"])
-async def github_login(
-	username: str,
-	password: str,
-	totp_code: str,
-	uid: str = ""
-) -> ToolResult:
-	"""
-	GitHub 登录(支持敏感数据占位符)
-
-	LLM 会输出类似:
-	{
-		"username": "user@example.com",
-		"password": "<secret>github_password</secret>",
-		"totp_code": "<secret>github_2fa_bu_2fa_code</secret>"
-	}
-
-	执行时会自动替换为实际值。
-
-	Args:
-		username: 用户名
-		password: 密码(可以是占位符)
-		totp_code: TOTP 验证码(可以是占位符,自动生成)
-		uid: 用户ID(自动注入)
-
-	Returns:
-		登录结果
-	"""
-	# 注意:password 和 totp_code 在到达这里时已经被替换
-	return ToolResult(
-		title="Login Successful",
-		output=f"Logged in as {username}",
-		long_term_memory=f"Logged in to GitHub as {username}"
-	)
-
-
-# 10. 组合所有功能
-@tool(
-	url_patterns=["*.example.com"],
-	requires_confirmation=True,
-	editable_params=["message"],
-	display={
-		"zh": {
-			"name": "发送认证消息",
-			"params": {
-				"recipient": "接收者",
-				"message": "消息内容",
-				"api_key": "API密钥"
-			}
-		}
-	}
-)
-async def send_authenticated_message(
-	recipient: str,
-	message: str,
-	api_key: str,
-	ctx: ToolContext,
-	uid: str = ""
-) -> ToolResult:
-	"""
-	发送消息(组合多个功能)
-
-	展示所有高级功能:
-	- 仅在 example.com 可用(域名过滤)
-	- 需要用户确认(危险操作)
-	- 消息可编辑(用户微调)
-	- API key 使用敏感数据占位符
-	- 使用 ToolContext 获取上下文
-
-	Args:
-		recipient: 接收者
-		message: 消息内容
-		api_key: API密钥(可以是占位符)
-		ctx: 工具上下文
-		uid: 用户ID(自动注入)
-
-	Returns:
-		发送结果
-	"""
-	# api_key 会从 <secret>api_key</secret> 替换为实际值
-	# ctx 包含 page_url, browser_session 等信息
-
-	return ToolResult(
-		title="Message Sent",
-		output=f"Sent to {recipient}: {message}",
-		long_term_memory=f"Sent message to {recipient} on {ctx.page_url}",
-		metadata={"recipient": recipient}
-	)
-
-
-# ============================================================
-# 使用示例
-# ============================================================
-
-async def main():
-	registry = get_tool_registry()
-
-	print("=" * 60)
-	print("工具系统完整示例")
-	print("=" * 60)
-
-	# ============================================================
-	# 示例 1:基础工具调用
-	# ============================================================
-	print("\n1. 基础工具调用")
-	print("-" * 60)
-
-	result = await registry.execute("hello_world", {"name": "Alice"})
-	print(f"hello_world: {result}")
-
-	result = await registry.execute("search_content", {"query": "Python", "limit": 5})
-	print(f"search_content: {result}")
-
-	# ============================================================
-	# 示例 2:域名过滤
-	# ============================================================
-	print("\n\n2. 域名过滤示例")
-	print("-" * 60)
-
-	# 在 Google 页面
-	google_url = "https://www.google.com/search?q=test"
-	google_tools = registry.get_tool_names(google_url)
-	print(f"在 {google_url} 可用的工具:")
-	print(f"  包含 google_search: {'google_search' in google_tools}")
-
-	# 在 GitHub 页面
-	github_url = "https://github.com/user/repo"
-	github_tools = registry.get_tool_names(github_url)
-	print(f"\n在 {github_url} 可用的工具:")
-	print(f"  包含 create_github_issue: {'create_github_issue' in github_tools}")
-	print(f"  包含 google_search: {'google_search' in github_tools}")
-
-	# ============================================================
-	# 示例 3:敏感数据处理
-	# ============================================================
-	print("\n\n3. 敏感数据处理示例")
-	print("-" * 60)
-
-	# 配置敏感数据
-	sensitive_data = {
-		"*.github.com": {
-			"github_password": "my_secret_password",
-			"github_2fa_bu_2fa_code": "JBSWY3DPEHPK3PXP"  # TOTP secret
-		}
-	}
-
-	# 模拟 LLM 输出(包含占位符)
-	llm_output_args = {
-		"username": "user@example.com",
-		"password": "<secret>github_password</secret>",
-		"totp_code": "<secret>github_2fa_bu_2fa_code</secret>"
-	}
-
-	print("LLM 输出的参数(包含占位符):")
-	print(f"  {llm_output_args}")
-
-	# 执行工具(自动替换敏感数据)
-	result = await registry.execute(
-		"github_login",
-		llm_output_args,
-		context={"page_url": "https://github.com/login"},
-		sensitive_data=sensitive_data
-	)
-
-	print(f"\n执行结果(密码已替换):")
-	print(f"  {result}")
-
-	# ============================================================
-	# 示例 4:工具统计
-	# ============================================================
-	print("\n\n4. 工具统计示例")
-	print("-" * 60)
-
-	# 模拟多次调用
-	for i in range(5):
-		await registry.execute("google_search", {"query": f"test {i}"})
-
-	await registry.execute("take_screenshot", {})
-	await registry.execute("take_screenshot", {})
-
-	# 查看统计
-	stats = registry.get_stats()
-	print("工具使用统计:")
-	for tool_name, tool_stats in stats.items():
-		if tool_stats["call_count"] > 0:
-			print(f"\n  {tool_name}:")
-			print(f"    调用次数: {tool_stats['call_count']}")
-			print(f"    成功率: {tool_stats['success_rate']:.1%}")
-			print(f"    平均执行时间: {tool_stats['average_duration']:.3f}s")
-
-	# 获取 Top 工具
-	print("\n\nTop 3 最常用工具:")
-	top_tools = registry.get_top_tools(limit=3, by="call_count")
-	for i, tool_name in enumerate(top_tools, 1):
-		tool_stats = stats[tool_name]
-		print(f"  {i}. {tool_name} ({tool_stats['call_count']} 次调用)")
-
-
-if __name__ == "__main__":
-	asyncio.run(main())

+ 0 - 1
requirements.txt

@@ -1,4 +1,3 @@
 # LLM request
 # LLM request
 httpx[socks]>=0.28.0
 httpx[socks]>=0.28.0
 python-dotenv>=1.0.0
 python-dotenv>=1.0.0
-playwright>=1.40.0

+ 0 - 722
tools/browserUseTools.py

@@ -1,722 +0,0 @@
-"""
-Browser-Use Tools Adapter
-浏览器工具适配器
-
-将 browser-use 库的工具适配到 Agent 框架中。
-基于 browser-use 的 Action 定义实现了以下工具:
-- ExtractAction: 内容提取
-- SearchAction: 网页搜索
-- NavigateAction: 页面导航
-- ClickElementAction: 元素点击
-- InputTextAction: 文本输入
-- DoneAction: 任务完成
-- SwitchTabAction: 标签切换
-- CloseTabAction: 关闭标签
-- ScrollAction: 页面滚动
-- SendKeysAction: 键盘操作
-- UploadFileAction: 文件上传
-- GetDropdownOptionsAction: 获取下拉选项
-- SelectDropdownOptionAction: 选择下拉选项
-
-所有工具都使用 @tool() 装饰器自动注册到框架的工具注册表中。
-"""
-
-import sys
-import os
-from typing import Optional, List
-
-# 将项目根目录添加到 Python 路径
-# 这样可以正确导入 agent 模块
-sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
-
-# 导入框架的工具装饰器和结果类
-# tool: 用于注册工具的装饰器
-# ToolResult: 工具执行结果的标准返回格式
-from agent.tools import tool, ToolResult
-
-
-# ============================================================
-# 核心浏览器导航工具 (Core Browser Navigation Tools)
-# 对应 browser-use 的 NavigateAction 和 GoBackEvent
-# ============================================================
-
-@tool()
-async def navigate_to_url(url: str, new_tab: bool = False, uid: str = "") -> ToolResult:
-    """
-    导航到指定的 URL
-    Navigate to a specific URL
-
-    这个工具使用 Playwright 启动浏览器并导航到指定的网址。
-    可以选择在新标签页中打开,或在当前标签页中打开。
-
-    Args:
-        url: 要访问的 URL 地址
-        new_tab: 是否在新标签页中打开(默认 False)
-        uid: 用户 ID(由框架自动注入,工具内部使用)
-
-    Returns:
-        ToolResult: 包含导航结果的工具返回对象
-            - title: 操作标题
-            - output: 成功打开的页面标题
-            - long_term_memory: 简短的操作记录(用于 LLM 长期记忆)
-            - metadata: 包含 url、title、new_tab 的元数据
-
-    Example:
-        navigate_to_url("https://www.baidu.com")
-        navigate_to_url("https://www.google.com", new_tab=True)
-    """
-    try:
-        # 导入 Playwright 异步 API
-        from playwright.async_api import async_playwright
-
-        # 使用异步上下文管理器启动 Playwright
-        async with async_playwright() as p:
-            # 启动 Chromium 浏览器(headless=False 表示显示浏览器窗口)
-            browser = await p.chromium.launch(headless=False)
-            # 创建浏览器上下文(类似于一个独立的浏览器会话)
-            context = await browser.new_context()
-
-            # 根据 new_tab 参数决定是否创建新标签页
-            if new_tab:
-                page = await context.new_page()
-            else:
-                # 使用现有标签页,如果没有则创建新的
-                page = await context.pages()[0] if context.pages() else await context.new_page()
-
-            # 导航到指定 URL
-            await page.goto(url)
-            # 等待页面完全加载(网络空闲状态)
-            await page.wait_for_load_state("networkidle")
-
-            # 获取页面标题
-            title = await page.title()
-
-            # 返回成功结果
-            return ToolResult(
-                title=f"Navigated to {url}",
-                output=f"Successfully opened page: {title}",
-                long_term_memory=f"Navigated to {url}",  # 简短记录,节省 token
-                metadata={"url": url, "title": title, "new_tab": new_tab}
-            )
-    except Exception as e:
-        # 捕获所有异常并返回错误结果
-        return ToolResult(
-            title="Navigation failed",
-            output="",
-            error=f"Failed to navigate to {url}: {str(e)}",
-            long_term_memory=f"Navigation to {url} failed"
-        )
-
-
-@tool()
-async def go_back(uid: str = "") -> ToolResult:
-    """
-    返回到上一个页面
-    Go back to the previous page
-
-    模拟浏览器的"后退"按钮功能。
-
-    Args:
-        uid: 用户 ID(由框架自动注入)
-
-    Returns:
-        ToolResult: 包含返回操作结果的工具返回对象
-
-    Note:
-        如果当前页面是历史记录的第一页,此操作可能会失败。
-    """
-    try:
-        from playwright.async_api import async_playwright
-
-        async with async_playwright() as p:
-            browser = await p.chromium.launch(headless=False)
-            context = await browser.new_context()
-            page = await context.pages()[0] if context.pages() else await context.new_page()
-
-            # 执行后退操作
-            await page.go_back()
-            # 等待页面加载完成
-            await page.wait_for_load_state("networkidle")
-
-            return ToolResult(
-                title="Went back",
-                output="Successfully navigated back",
-                long_term_memory="Navigated back to previous page"
-            )
-    except Exception as e:
-        return ToolResult(
-            title="Go back failed",
-            output="",
-            error=f"Failed to go back: {str(e)}",
-            long_term_memory="Go back failed"
-        )
-
-
-# ============================================================
-# 元素交互工具 (Element Interaction Tools)
-# 对应 browser-use 的 ClickElementAction, InputTextAction, SendKeysAction
-# ============================================================
-
-@tool()
-async def click_element(index: Optional[int] = None, coordinate_x: Optional[int] = None,
-                       coordinate_y: Optional[int] = None, uid: str = "") -> ToolResult:
-    """
-    通过索引或坐标点击页面元素
-    Click an element by index or coordinates
-
-    支持两种点击方式:
-    1. 通过坐标点击:提供 coordinate_x 和 coordinate_y
-    2. 通过元素索引点击:提供 index(需要配合 DOM 状态使用)
-
-    Args:
-        index: 元素索引(从浏览器状态中获取,1-based)
-        coordinate_x: 相对于视口左边缘的水平坐标(像素)
-        coordinate_y: 相对于视口顶部的垂直坐标(像素)
-        uid: 用户 ID(由框架自动注入)
-
-    Returns:
-        ToolResult: 包含点击操作结果的工具返回对象
-
-    Example:
-        # 通过坐标点击
-        click_element(coordinate_x=100, coordinate_y=200)
-        # 通过索引点击
-        click_element(index=5)
-
-    Note:
-        - 必须提供 index 或 (coordinate_x, coordinate_y) 中的一种
-        - 坐标点击更可靠,索引点击需要维护 DOM 状态映射
-    """
-    try:
-        from playwright.async_api import async_playwright
-
-        async with async_playwright() as p:
-            browser = await p.chromium.launch(headless=False)
-            context = await browser.new_context()
-            page = await context.pages()[0] if context.pages() else await context.new_page()
-
-            # 方式1:通过坐标点击
-            if coordinate_x is not None and coordinate_y is not None:
-                await page.mouse.click(coordinate_x, coordinate_y)
-                return ToolResult(
-                    title="Clicked coordinate",
-                    output=f"Clicked at ({coordinate_x}, {coordinate_y})",
-                    long_term_memory=f"Clicked coordinate ({coordinate_x}, {coordinate_y})"
-                )
-            # 方式2:通过索引点击(需要 DOM 状态映射)
-            elif index is not None:
-                # 注意:这里需要 DOM 状态来将索引映射到实际的 CSS 选择器
-                # 当前实现为占位符,实际使用时需要维护 DOM 状态
-                return ToolResult(
-                    title="Click by index",
-                    output=f"Clicked element at index {index}",
-                    long_term_memory=f"Clicked element {index}"
-                )
-            else:
-                # 参数错误:必须提供一种点击方式
-                return ToolResult(
-                    title="Invalid parameters",
-                    output="",
-                    error="Must provide either index or coordinates",
-                    long_term_memory="Click failed: invalid parameters"
-                )
-    except Exception as e:
-        return ToolResult(
-            title="Click failed",
-            output="",
-            error=f"Failed to click: {str(e)}",
-            long_term_memory="Click failed"
-        )
-
-
-@tool()
-async def input_text(index: int, text: str, clear: bool = True, uid: str = "") -> ToolResult:
-    """
-    在指定元素中输入文本
-    Input text into an element
-
-    Args:
-        index: 元素索引(从浏览器状态中获取,0-based)
-        text: 要输入的文本内容
-        clear: 是否先清除现有文本(默认 True)
-        uid: 用户 ID(由框架自动注入)
-
-    Returns:
-        ToolResult: 包含输入操作结果的工具返回对象
-
-    Example:
-        # 清除后输入
-        input_text(index=0, text="Hello World", clear=True)
-        # 追加输入
-        input_text(index=0, text=" More text", clear=False)
-
-    Note:
-        当前实现使用通用键盘输入方式,实际使用时需要配合 DOM 状态
-        将索引映射到具体的输入框选择器。
-    """
-    try:
-        from playwright.async_api import async_playwright
-
-        async with async_playwright() as p:
-            browser = await p.chromium.launch(headless=False)
-            context = await browser.new_context()
-            page = await context.pages()[0] if context.pages() else await context.new_page()
-
-            # 注意:这里需要 DOM 状态来将索引映射到实际的输入框选择器
-            # 当前使用通用键盘输入方式
-            if clear:
-                # 先全选(Ctrl+A)再输入,实现清除效果
-                await page.keyboard.press("Control+A")
-            # 输入文本
-            await page.keyboard.type(text)
-
-            return ToolResult(
-                title="Input text",
-                output=f"Input text into element {index}",
-                long_term_memory=f"Input text into element {index}",
-                metadata={"index": index, "clear": clear}
-            )
-    except Exception as e:
-        return ToolResult(
-            title="Input failed",
-            output="",
-            error=f"Failed to input text: {str(e)}",
-            long_term_memory="Input text failed"
-        )
-
-
-@tool()
-async def send_keys(keys: str, uid: str = "") -> ToolResult:
-    """
-    发送键盘按键或快捷键
-    Send keyboard keys or shortcuts
-
-    支持发送单个按键、组合键和快捷键。
-
-    Args:
-        keys: 要发送的按键字符串
-              - 单个按键: "Enter", "Escape", "PageDown", "Tab"
-              - 组合键: "Control+o", "Shift+Tab", "Alt+F4"
-              - 功能键: "F1", "F2", ..., "F12"
-        uid: 用户 ID(由框架自动注入)
-
-    Returns:
-        ToolResult: 包含按键操作结果的工具返回对象
-
-    Example:
-        send_keys("Enter")           # 回车键
-        send_keys("Control+o")       # Ctrl+O 打开文件
-        send_keys("PageDown")        # 向下翻页
-        send_keys("Escape")          # ESC 键
-
-    Note:
-        按键名称遵循 Playwright 的键盘 API 规范。
-        参考: https://playwright.dev/python/docs/api/class-keyboard
-    """
-    try:
-        from playwright.async_api import async_playwright
-
-        async with async_playwright() as p:
-            browser = await p.chromium.launch(headless=False)
-            context = await browser.new_context()
-            page = await context.pages()[0] if context.pages() else await context.new_page()
-
-            # 发送按键
-            await page.keyboard.press(keys)
-
-            return ToolResult(
-                title="Sent keys",
-                output=f"Sent keys: {keys}",
-                long_term_memory=f"Sent keys: {keys}"
-            )
-    except Exception as e:
-        return ToolResult(
-            title="Send keys failed",
-            output="",
-            error=f"Failed to send keys: {str(e)}",
-            long_term_memory="Send keys failed"
-        )
-
-
-# ============================================================
-# Content Extraction Tools
-# ============================================================
-
-@tool()
-async def extract_content(query: str, extract_links: bool = False,
-                         start_from_char: int = 0, uid: str = "") -> ToolResult:
-    """
-    Extract content from the current page based on a query
-
-    Args:
-        query: What to extract from the page
-        extract_links: Whether to extract links (default: False, saves tokens)
-        start_from_char: Start extraction from specific character (for long content)
-        uid: User ID (auto-injected)
-
-    Returns:
-        Extracted content
-    """
-    try:
-        from playwright.async_api import async_playwright
-
-        async with async_playwright() as p:
-            browser = await p.chromium.launch(headless=False)
-            context = await browser.new_context()
-            page = await context.pages()[0] if context.pages() else await context.new_page()
-
-            # Extract text content
-            content = await page.content()
-            text_content = await page.inner_text("body")
-
-            # Apply start_from_char if specified
-            if start_from_char > 0:
-                text_content = text_content[start_from_char:]
-
-            # Extract links if requested
-            links = []
-            if extract_links:
-                link_elements = await page.query_selector_all("a[href]")
-                for elem in link_elements[:50]:  # Limit to 50 links
-                    href = await elem.get_attribute("href")
-                    text = await elem.inner_text()
-                    if href:
-                        links.append({"text": text, "href": href})
-
-            output = f"Query: {query}\n\nContent:\n{text_content[:2000]}"
-            if extract_links and links:
-                output += f"\n\nLinks found: {len(links)}"
-
-            return ToolResult(
-                title=f"Extracted: {query}",
-                output=output,
-                long_term_memory=f"Extracted content for query: {query}",
-                include_output_only_once=True,
-                metadata={"query": query, "links": links if extract_links else []}
-            )
-    except Exception as e:
-        return ToolResult(
-            title="Extraction failed",
-            output="",
-            error=f"Failed to extract content: {str(e)}",
-            long_term_memory="Content extraction failed"
-        )
-
-
-# ============================================================
-# Search Tools
-# ============================================================
-
-@tool()
-async def search_web(query: str, engine: str = "duckduckgo", uid: str = "") -> ToolResult:
-    """
-    Search the web using a search engine
-
-    Args:
-        query: Search query
-        engine: Search engine to use (duckduckgo, google, bing) - default: duckduckgo
-        uid: User ID (auto-injected)
-
-    Returns:
-        Search results
-    """
-    try:
-        from playwright.async_api import async_playwright
-
-        async with async_playwright() as p:
-            browser = await p.chromium.launch(headless=False)
-            context = await browser.new_context()
-            page = await context.new_page()
-
-            # Navigate to search engine
-            if engine == "google":
-                await page.goto(f"https://www.google.com/search?q={query}")
-            elif engine == "bing":
-                await page.goto(f"https://www.bing.com/search?q={query}")
-            else:  # duckduckgo
-                await page.goto(f"https://duckduckgo.com/?q={query}")
-
-            await page.wait_for_load_state("networkidle")
-
-            # Extract search results
-            results_text = await page.inner_text("body")
-
-            await browser.close()
-
-            return ToolResult(
-                title=f"Search: {query}",
-                output=f"Search results from {engine}:\n{results_text[:2000]}",
-                long_term_memory=f"Searched {engine} for: {query}",
-                include_output_only_once=True,
-                metadata={"query": query, "engine": engine}
-            )
-    except Exception as e:
-        return ToolResult(
-            title="Search failed",
-            output="",
-            error=f"Search failed: {str(e)}",
-            long_term_memory=f"Search for '{query}' failed"
-        )
-
-
-# ============================================================
-# Scroll Tools
-# ============================================================
-
-@tool()
-async def scroll_page(down: bool = True, pages: float = 1.0,
-                     index: Optional[int] = None, uid: str = "") -> ToolResult:
-    """
-    Scroll the page or a specific element
-
-    Args:
-        down: True to scroll down, False to scroll up
-        pages: Number of pages to scroll (0.5=half page, 1=full page, 10=to bottom/top)
-        index: Optional element index to scroll within specific element
-        uid: User ID (auto-injected)
-
-    Returns:
-        Scroll result
-    """
-    try:
-        from playwright.async_api import async_playwright
-
-        async with async_playwright() as p:
-            browser = await p.chromium.launch(headless=False)
-            context = await browser.new_context()
-            page = await context.pages()[0] if context.pages() else await context.new_page()
-
-            # Calculate scroll amount
-            viewport_height = page.viewport_size["height"] if page.viewport_size else 800
-            scroll_amount = int(viewport_height * pages)
-
-            if down:
-                await page.mouse.wheel(0, scroll_amount)
-                direction = "down"
-            else:
-                await page.mouse.wheel(0, -scroll_amount)
-                direction = "up"
-
-            return ToolResult(
-                title=f"Scrolled {direction}",
-                output=f"Scrolled {direction} {pages} pages",
-                long_term_memory=f"Scrolled {direction} {pages} pages"
-            )
-    except Exception as e:
-        return ToolResult(
-            title="Scroll failed",
-            output="",
-            error=f"Failed to scroll: {str(e)}",
-            long_term_memory="Scroll failed"
-        )
-
-
-# ============================================================
-# Tab Management Tools
-# ============================================================
-
-@tool()
-async def switch_tab(tab_id: str, uid: str = "") -> ToolResult:
-    """
-    Switch to a different browser tab
-
-    Args:
-        tab_id: 4-character tab ID
-        uid: User ID (auto-injected)
-
-    Returns:
-        Switch result
-    """
-    try:
-        return ToolResult(
-            title=f"Switched to tab {tab_id}",
-            output=f"Switched to tab {tab_id}",
-            long_term_memory=f"Switched to tab {tab_id}"
-        )
-    except Exception as e:
-        return ToolResult(
-            title="Switch tab failed",
-            output="",
-            error=f"Failed to switch tab: {str(e)}",
-            long_term_memory="Switch tab failed"
-        )
-
-
-@tool()
-async def close_tab(tab_id: str, uid: str = "") -> ToolResult:
-    """
-    Close a browser tab
-
-    Args:
-        tab_id: 4-character tab ID
-        uid: User ID (auto-injected)
-
-    Returns:
-        Close result
-    """
-    try:
-        return ToolResult(
-            title=f"Closed tab {tab_id}",
-            output=f"Closed tab {tab_id}",
-            long_term_memory=f"Closed tab {tab_id}"
-        )
-    except Exception as e:
-        return ToolResult(
-            title="Close tab failed",
-            output="",
-            error=f"Failed to close tab: {str(e)}",
-            long_term_memory="Close tab failed"
-        )
-
-
-# ============================================================
-# Dropdown Tools
-# ============================================================
-
-@tool()
-async def get_dropdown_options(index: int, uid: str = "") -> ToolResult:
-    """
-    Get options from a dropdown element
-
-    Args:
-        index: Element index from browser state
-        uid: User ID (auto-injected)
-
-    Returns:
-        Dropdown options
-    """
-    try:
-        from playwright.async_api import async_playwright
-
-        async with async_playwright() as p:
-            browser = await p.chromium.launch(headless=False)
-            context = await browser.new_context()
-            page = await context.pages()[0] if context.pages() else await context.new_page()
-
-            # This would need DOM state to map index to selector
-            # For now, return a placeholder
-            return ToolResult(
-                title=f"Dropdown options for element {index}",
-                output=f"Retrieved options for dropdown at index {index}",
-                long_term_memory=f"Got dropdown options for element {index}"
-            )
-    except Exception as e:
-        return ToolResult(
-            title="Get dropdown options failed",
-            output="",
-            error=f"Failed to get dropdown options: {str(e)}",
-            long_term_memory="Get dropdown options failed"
-        )
-
-
-@tool()
-async def select_dropdown_option(index: int, text: str, uid: str = "") -> ToolResult:
-    """
-    Select an option from a dropdown
-
-    Args:
-        index: Element index from browser state
-        text: Exact text/value to select
-        uid: User ID (auto-injected)
-
-    Returns:
-        Selection result
-    """
-    try:
-        from playwright.async_api import async_playwright
-
-        async with async_playwright() as p:
-            browser = await p.chromium.launch(headless=False)
-            context = await browser.new_context()
-            page = await context.pages()[0] if context.pages() else await context.new_page()
-
-            # This would need DOM state to map index to selector
-            return ToolResult(
-                title=f"Selected dropdown option",
-                output=f"Selected '{text}' from dropdown at index {index}",
-                long_term_memory=f"Selected '{text}' from dropdown {index}"
-            )
-    except Exception as e:
-        return ToolResult(
-            title="Select dropdown option failed",
-            output="",
-            error=f"Failed to select dropdown option: {str(e)}",
-            long_term_memory="Select dropdown option failed"
-        )
-
-
-# ============================================================
-# File Upload Tool
-# ============================================================
-
-@tool()
-async def upload_file(index: int, path: str, uid: str = "") -> ToolResult:
-    """
-    Upload a file to a file input element
-
-    Args:
-        index: Element index from browser state
-        path: Path to the file to upload
-        uid: User ID (auto-injected)
-
-    Returns:
-        Upload result
-    """
-    try:
-        from playwright.async_api import async_playwright
-
-        async with async_playwright() as p:
-            browser = await p.chromium.launch(headless=False)
-            context = await browser.new_context()
-            page = await context.pages()[0] if context.pages() else await context.new_page()
-
-            # This would need DOM state to map index to selector
-            return ToolResult(
-                title="File uploaded",
-                output=f"Uploaded file {path} to element {index}",
-                long_term_memory=f"Uploaded file {path}"
-            )
-    except Exception as e:
-        return ToolResult(
-            title="Upload failed",
-            output="",
-            error=f"Failed to upload file: {str(e)}",
-            long_term_memory="File upload failed"
-        )
-
-
-# ============================================================
-# Task Completion Tool
-# ============================================================
-
-@tool()
-async def done(text: str, success: bool = True,
-              files_to_display: Optional[List[str]] = None, uid: str = "") -> ToolResult:
-    """
-    Mark the task as complete and return final message to user
-
-    Args:
-        text: Final message to user in the requested format
-        success: Whether the task completed successfully
-        files_to_display: Optional list of file paths to display
-        uid: User ID (auto-injected)
-
-    Returns:
-        Completion result
-    """
-    try:
-        return ToolResult(
-            title="Task completed" if success else "Task failed",
-            output=text,
-            long_term_memory=f"Task {'completed' if success else 'failed'}",
-            attachments=files_to_display or [],
-            metadata={"success": success}
-        )
-    except Exception as e:
-        return ToolResult(
-            title="Done failed",
-            output="",
-            error=f"Failed to complete task: {str(e)}",
-            long_term_memory="Task completion failed"
-        )
-