mirror of
https://gitcode.com/JianFeeeee/HomeAgent.git
synced 2026-09-21 17:38:10 +00:00
- Output channels generate per-channel tools: output_send__{name} (type=output) + output_send__{name}_help
- content is JSON string transparently passed to plugin handler for routing
- EventAgentLLMChain: full LLM response forwarded after each turn for webui/logs
- sdk.New refactored to SDKConfig struct (no more 13 positional args)
- RegisterOutputChannel adds desc param for JSON format documentation
- channelDevice simplified (no Tools method), desc field added
- Child agent permission updated for output_send__ prefix
- System prompt: output gates, multi-call, long messages split
- WebUI: subscribes to EventAgentLLMChain in SSE, no output channel
- Tests updated for new naming convention
202 lines
7.1 KiB
Go
202 lines
7.1 KiB
Go
package webui
|
|
|
|
import (
|
|
"crypto/rand"
|
|
"encoding/hex"
|
|
"fmt"
|
|
"log"
|
|
"net/http"
|
|
|
|
agentAPI "gitcode.com/JianFeeeee/HomeAgent/internal/agent/api"
|
|
agentCore "gitcode.com/JianFeeeee/HomeAgent/internal/agent/core"
|
|
agentIO "gitcode.com/JianFeeeee/HomeAgent/internal/agent/io"
|
|
internalConfig "gitcode.com/JianFeeeee/HomeAgent/internal/config"
|
|
"gitcode.com/JianFeeeee/HomeAgent/internal/events"
|
|
"gitcode.com/JianFeeeee/HomeAgent/internal/knowledge"
|
|
luaVM "gitcode.com/JianFeeeee/HomeAgent/internal/lua"
|
|
"gitcode.com/JianFeeeee/HomeAgent/internal/memory"
|
|
"gitcode.com/JianFeeeee/HomeAgent/internal/memory/text"
|
|
"gitcode.com/JianFeeeee/HomeAgent/internal/plugin"
|
|
"gitcode.com/JianFeeeee/HomeAgent/internal/skill"
|
|
sdk "gitcode.com/JianFeeeee/HomeAgent/internal/sdk"
|
|
"gitcode.com/JianFeeeee/HomeAgent/internal/supervisor"
|
|
"gitcode.com/JianFeeeee/HomeAgent/internal/tracker"
|
|
"gitcode.com/JianFeeeee/HomeAgent/pkg/types"
|
|
)
|
|
|
|
// 包级依赖注入 — 由 main.go 在 Load() 前调用 Configure() 设置。
|
|
var (
|
|
webuiAddr string
|
|
webuiSup *supervisor.Daemon
|
|
webuiMem *memory.GraphDB
|
|
webuiSK *skill.Manager
|
|
webuiLua *luaVM.VM
|
|
webuiCfg *types.Config
|
|
webuiIOM *agentIO.IOManager
|
|
webuiTM *text.Memory
|
|
webuiKS *knowledge.Store
|
|
webuiTR *tracker.Tracker
|
|
webuiCR *internalConfig.ConfigRegistry
|
|
webuiPR *plugin.Registry
|
|
webuiEvBus *events.Bus
|
|
webuiStatusProvider agentCore.StatusProvider
|
|
webuiProviderMgr *agentAPI.ProviderManager
|
|
webuiBaseAPIKey string
|
|
)
|
|
|
|
// Configure 注入 WebUI 插件需要的内核依赖。必须在 Load() 之前调用。
|
|
func Configure(addr string,
|
|
sup *supervisor.Daemon, mem *memory.GraphDB, sk *skill.Manager,
|
|
lua *luaVM.VM, cfg *types.Config, iom *agentIO.IOManager,
|
|
tm *text.Memory, ks *knowledge.Store, tr *tracker.Tracker,
|
|
cr *internalConfig.ConfigRegistry, pr *plugin.Registry, evBus *events.Bus,
|
|
sp agentCore.StatusProvider, pm *agentAPI.ProviderManager, baseKey string,
|
|
) {
|
|
webuiAddr = addr
|
|
webuiSup, webuiMem, webuiSK, webuiLua = sup, mem, sk, lua
|
|
webuiCfg, webuiIOM, webuiTM, webuiKS = cfg, iom, tm, ks
|
|
webuiTR, webuiCR, webuiPR, webuiEvBus = tr, cr, pr, evBus
|
|
webuiStatusProvider = sp
|
|
webuiProviderMgr = pm
|
|
webuiBaseAPIKey = baseKey
|
|
}
|
|
|
|
func init() {
|
|
plugin.RegisterPluginMeta("webui", "Web 控制台", "WebUI")
|
|
plugin.RegisterFactory("webui", func(name string, config map[string]interface{}) (sdk.Plugin, error) {
|
|
if webuiSup == nil {
|
|
return nil, nil // 未 Configure 则跳过(不给日志警告)
|
|
}
|
|
addr := webuiAddr
|
|
if a, ok := config["addr"].(string); ok {
|
|
addr = a
|
|
}
|
|
return New(name, addr,
|
|
webuiSup, webuiMem, webuiSK, webuiLua,
|
|
webuiCfg, webuiIOM, webuiTM, webuiKS,
|
|
webuiTR, webuiCR, webuiPR, webuiEvBus, webuiStatusProvider,
|
|
webuiProviderMgr, webuiBaseAPIKey,
|
|
), nil
|
|
})
|
|
}
|
|
|
|
type Plugin struct {
|
|
name string
|
|
addr string
|
|
handler *Handler
|
|
server *http.Server
|
|
mux *http.ServeMux
|
|
|
|
sup *supervisor.Daemon
|
|
mem *memory.GraphDB
|
|
sk *skill.Manager
|
|
lua *luaVM.VM
|
|
cfg *types.Config
|
|
iom *agentIO.IOManager
|
|
tm *text.Memory
|
|
ks *knowledge.Store
|
|
tr *tracker.Tracker
|
|
cr *internalConfig.ConfigRegistry
|
|
pr *plugin.Registry
|
|
evBus *events.Bus
|
|
statusProvider agentCore.StatusProvider
|
|
providerMgr *agentAPI.ProviderManager
|
|
baseAPIKey string
|
|
}
|
|
|
|
func New(name, addr string,
|
|
sup *supervisor.Daemon, mem *memory.GraphDB, sk *skill.Manager,
|
|
lua *luaVM.VM, cfg *types.Config, iom *agentIO.IOManager,
|
|
tm *text.Memory, ks *knowledge.Store, tr *tracker.Tracker,
|
|
cr *internalConfig.ConfigRegistry, pr *plugin.Registry, evBus *events.Bus,
|
|
sp agentCore.StatusProvider, pm *agentAPI.ProviderManager, baseKey string,
|
|
) *Plugin {
|
|
return &Plugin{
|
|
name: name,
|
|
addr: addr,
|
|
mux: http.NewServeMux(),
|
|
sup: sup, mem: mem, sk: sk, lua: lua, cfg: cfg,
|
|
iom: iom, tm: tm, ks: ks, tr: tr, cr: cr, pr: pr, evBus: evBus,
|
|
statusProvider: sp, providerMgr: pm, baseAPIKey: baseKey,
|
|
}
|
|
}
|
|
|
|
func randomSecret(n int) string {
|
|
buf := make([]byte, n)
|
|
if _, err := rand.Read(buf); err != nil {
|
|
return ""
|
|
}
|
|
return hex.EncodeToString(buf)
|
|
}
|
|
|
|
func (p *Plugin) ensureAuthBootstrap(s *sdk.PluginSDK) {
|
|
sett := s.Settings()
|
|
if sett == nil {
|
|
return
|
|
}
|
|
if v, _ := sett.Get("username"); v == nil || fmt.Sprint(v) == "" {
|
|
_ = sett.Set("username", "admin")
|
|
}
|
|
if v, _ := sett.Get("password"); v == nil || fmt.Sprint(v) == "" {
|
|
pw := randomSecret(12)
|
|
_ = sett.Set("password", pw)
|
|
log.Printf("[webui] bootstrap password generated for user admin: %s", pw)
|
|
}
|
|
if v, _ := sett.Get("api_key"); v == nil || fmt.Sprint(v) == "" {
|
|
key := randomSecret(16)
|
|
_ = sett.Set("api_key", key)
|
|
log.Printf("[webui] bootstrap api_key generated: %s", key)
|
|
}
|
|
if v, _ := sett.Get("session_ttl_hours"); v == nil || fmt.Sprint(v) == "" {
|
|
_ = sett.Set("session_ttl_hours", "24")
|
|
}
|
|
}
|
|
|
|
func (p *Plugin) Name() string { return p.name }
|
|
|
|
func (p *Plugin) Start(s *sdk.PluginSDK) error {
|
|
s.SetAutoRestart(true)
|
|
s.Settings().RegisterDef(sdk.ConfigDef{Key: "api_key", Default: "", Type: "password", DisplayName: "API 密钥", Description: "访问 API 时需要的密钥", Category: "webui"})
|
|
s.Settings().RegisterDef(sdk.ConfigDef{Key: "username", Default: "admin", Type: "string", DisplayName: "登录用户名", Description: "Web 控制台登录用户名", Category: "webui"})
|
|
s.Settings().RegisterDef(sdk.ConfigDef{Key: "password", Default: "", Type: "password", DisplayName: "Web 控制台登录密码", Description: "Web 控制台登录密码", Category: "webui"})
|
|
s.Settings().RegisterDef(sdk.ConfigDef{Key: "session_ttl_hours", Default: "24", Type: "int", DisplayName: "会话时长(小时)", Description: "登录 cookie 有效时长", Category: "webui"})
|
|
p.ensureAuthBootstrap(s)
|
|
|
|
s.RegisterStage(sdk.StagePreAction, func(ctx *sdk.StageContext) error {
|
|
p.evBus.Publish(&events.Event{Type: events.EventStage, Payload: map[string]interface{}{"phase": "pre_action", "message": "thinking"}})
|
|
return nil
|
|
})
|
|
s.RegisterStage(sdk.StageBeforeToolcall, func(ctx *sdk.StageContext) error {
|
|
tool := ""
|
|
if len(ctx.ToolCalls) > 0 {
|
|
tool = ctx.ToolCalls[0].Name
|
|
}
|
|
p.evBus.Publish(&events.Event{Type: events.EventStage, Payload: map[string]interface{}{"phase": "before_toolcall", "tool": tool, "message": "tool:" + tool}})
|
|
return nil
|
|
})
|
|
s.RegisterStage(sdk.StageBeforeOutput, func(ctx *sdk.StageContext) error {
|
|
p.evBus.Publish(&events.Event{Type: events.EventStage, Payload: map[string]interface{}{"phase": "before_output", "message": "output"}})
|
|
return nil
|
|
})
|
|
|
|
h := NewHandler(p.sup, p.mem, p.sk, p.lua, p.cfg, p.iom, p.tm, p.ks, p.tr, p.cr, p.pr, p.evBus, p.statusProvider, p.providerMgr, p.baseAPIKey)
|
|
p.handler = h
|
|
h.RegisterRoutes(p.mux)
|
|
|
|
p.server = &http.Server{Addr: p.addr, Handler: p.mux}
|
|
go func() {
|
|
log.Printf("[webui] HTTP server listening on %s", p.addr)
|
|
if err := p.server.ListenAndServe(); err != nil && err != http.ErrServerClosed {
|
|
log.Printf("[webui] server error: %v", err)
|
|
}
|
|
}()
|
|
return nil
|
|
}
|
|
|
|
func (p *Plugin) Stop() error {
|
|
if p.server != nil {
|
|
return p.server.Close()
|
|
}
|
|
return nil
|
|
}
|