mirror of
https://gitcode.com/JianFeeeee/homeagent-sdk.git
synced 2026-10-02 15:14:37 +00:00
docs: 修正全部文档使其与源码实现一致
- Plugin.Start(sdk *PluginSDK) 接口签名改为指针 - 方法表重写: 移除 CallLLM/QueryKnowledge/SetMemory 等不存在方法 - IOInjector 参数顺序修正为 (source, channel, text) - 删除虚构 SDKConfig, 替换为实际 New() 构造函数签名 - .hmap 内容描述一致化 (plugin.so + plugin.dll + main.lua) - 添加 meta/ 包元数据文件
This commit is contained in:
7
example/webfetch/go.mod
Normal file
7
example/webfetch/go.mod
Normal file
@ -0,0 +1,7 @@
|
||||
module webfetch
|
||||
|
||||
go 1.25.0
|
||||
|
||||
require gitcode.com/JianFeeeee/homeagent-sdk v0.0.0-20260708004841-e9bdcf9304b0
|
||||
|
||||
replace gitcode.com/JianFeeeee/homeagent-sdk => ../../.
|
||||
11
example/webfetch/plg.json
Normal file
11
example/webfetch/plg.json
Normal file
@ -0,0 +1,11 @@
|
||||
{
|
||||
"name": "webfetch",
|
||||
"name_zh": "网页抓取",
|
||||
"name_en": "webfetch",
|
||||
"version": "1.0.0",
|
||||
"description": "网页内容抓取工具,使用无头 Chromium 浏览器获取网页文字内容",
|
||||
"author": "HomeAgent",
|
||||
"entry": "plugin.so",
|
||||
"tags": ["web", "fetch"],
|
||||
"targets": "linux/amd64"
|
||||
}
|
||||
143
example/webfetch/plugin.go
Normal file
143
example/webfetch/plugin.go
Normal file
@ -0,0 +1,143 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
"net/http"
|
||||
"os"
|
||||
"os/exec"
|
||||
"regexp"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"gitcode.com/JianFeeeee/homeagent-sdk/sdk"
|
||||
)
|
||||
|
||||
type Plugin struct {
|
||||
name string
|
||||
sdk *sdk.PluginSDK
|
||||
}
|
||||
|
||||
func (p *Plugin) Name() string { return p.name }
|
||||
|
||||
func (p *Plugin) Start(s *sdk.PluginSDK) error {
|
||||
s.SetAutoRestart(true)
|
||||
p.sdk = s
|
||||
|
||||
s.RegisterTool("web_fetch", sdk.ToolDef{
|
||||
Name: "web_fetch",
|
||||
Description: "获取网页文字内容。使用无头 Chromium 浏览器渲染页面后提取正文文字,返回标题和前 5000 字符。适用于需要查看网页内容的场景。",
|
||||
Parameters: map[string]interface{}{
|
||||
"type": "object",
|
||||
"properties": map[string]interface{}{
|
||||
"url": map[string]interface{}{"type": "string", "description": "要访问的网页 URL"},
|
||||
"wait": map[string]interface{}{"type": "integer", "description": "等待秒数(用于 JS 渲染页面,默认 0)"},
|
||||
},
|
||||
"required": []string{"url"},
|
||||
},
|
||||
}, p.handleWebFetch)
|
||||
|
||||
log.Printf("[%s] plugin started", p.name)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *Plugin) Stop() error { return nil }
|
||||
|
||||
func convInt64(v interface{}) (int64, error) {
|
||||
switch x := v.(type) {
|
||||
case float64:
|
||||
return int64(x), nil
|
||||
case int64:
|
||||
return x, nil
|
||||
case json.Number:
|
||||
return x.Int64()
|
||||
case string:
|
||||
return 0, fmt.Errorf("cannot convert string to int64")
|
||||
default:
|
||||
return 0, fmt.Errorf("cannot convert %T to int64", v)
|
||||
}
|
||||
}
|
||||
|
||||
func (p *Plugin) handleWebFetch(args map[string]interface{}) (interface{}, error) {
|
||||
url, _ := args["url"].(string)
|
||||
if url == "" {
|
||||
return nil, fmt.Errorf("url is required")
|
||||
}
|
||||
waitSec, _ := convInt64(args["wait"])
|
||||
|
||||
if waitSec > 0 {
|
||||
time.Sleep(time.Duration(waitSec) * time.Second)
|
||||
}
|
||||
|
||||
var html string
|
||||
chromiumPath := "/usr/local/bin/chromium"
|
||||
if _, err := os.Stat(chromiumPath); err == nil {
|
||||
var out bytes.Buffer
|
||||
argsList := []string{"--headless", "--disable-gpu", "--no-sandbox", "--dump-dom", url}
|
||||
cmd := exec.Command(chromiumPath, argsList...)
|
||||
cmd.Stdout = &out
|
||||
cmd.Stderr = nil
|
||||
if err := cmd.Run(); err != nil {
|
||||
return nil, fmt.Errorf("chromium: %w", err)
|
||||
}
|
||||
html = out.String()
|
||||
} else {
|
||||
resp, err := http.Get(url)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("http get: %w", err)
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
body, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("read body: %w", err)
|
||||
}
|
||||
html = string(body)
|
||||
}
|
||||
|
||||
title := ""
|
||||
if m := regexp.MustCompile(`<title>([^<]+)</title>`).FindStringSubmatch(html); len(m) > 1 {
|
||||
title = m[1]
|
||||
}
|
||||
|
||||
var textOut bytes.Buffer
|
||||
pyCmd := exec.Command("python3", "-c", `
|
||||
import sys, re, html
|
||||
raw = sys.stdin.read()
|
||||
text = re.sub(r'<[^>]+>', ' ', raw)
|
||||
text = re.sub(r'\s+', ' ', text).strip()
|
||||
text = html.unescape(text)
|
||||
sys.stdout.write(text)
|
||||
`)
|
||||
pyCmd.Stdin = strings.NewReader(html)
|
||||
pyCmd.Stdout = &textOut
|
||||
pyCmd.Stderr = nil
|
||||
pyCmd.Run()
|
||||
text := strings.TrimSpace(textOut.String())
|
||||
|
||||
origLen := len(text)
|
||||
truncated := origLen > 5000
|
||||
if truncated {
|
||||
text = text[:5000]
|
||||
}
|
||||
|
||||
result := ""
|
||||
if title != "" {
|
||||
result = fmt.Sprintf("标题: %s\nURL: %s\n\n", title, url)
|
||||
}
|
||||
result += text
|
||||
if truncated {
|
||||
result += fmt.Sprintf("\n\n...(内容过长,仅显示前 5000 字符,共 %d 字符)", origLen)
|
||||
}
|
||||
|
||||
return map[string]interface{}{
|
||||
"content": result,
|
||||
"title": title,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func NewPlugin(name string, config map[string]interface{}) (sdk.Plugin, error) {
|
||||
return &Plugin{name: name}, nil
|
||||
}
|
||||
Reference in New Issue
Block a user