mirror of
https://gitcode.com/JianFeeeee/HomeAgent.git
synced 2026-09-21 17:38:10 +00:00
- agent: 工具轮请求尾部补 user 占位(zen 网关强制),tool 消息正确配对 - agent: 工具提醒/中断以 system 角色注入并带 [中断消息] 前缀,不进用户履历;系统提示词说明中断消息格式 - agentcli: 基于 ConPTY 的交互式终端(ptywin fork),terminal_create/read/write/resize/close/watch - webui: server 输出通道适配器(保留 reasoning_content/disable_thinking) - GUI: 沉浸式标题栏、icon 圆角重制、mascot 等打磨
136 lines
4.2 KiB
Go
136 lines
4.2 KiB
Go
package webui
|
|
|
|
import (
|
|
"crypto/rand"
|
|
"encoding/hex"
|
|
"fmt"
|
|
"log"
|
|
"net/http"
|
|
|
|
"gitcode.com/JianFeeeee/HomeAgent/internal/plugin"
|
|
sdk "gitcode.com/JianFeeeee/HomeAgent/internal/sdk"
|
|
)
|
|
|
|
func init() {
|
|
plugin.RegisterPluginMeta("webui", "Web 控制台", "WebUI")
|
|
plugin.RegisterFactory("webui", func(name string, config map[string]interface{}) (sdk.Plugin, error) {
|
|
return New(name), nil
|
|
})
|
|
}
|
|
|
|
type Plugin struct {
|
|
name string
|
|
handler *Handler
|
|
server *http.Server
|
|
mux *http.ServeMux
|
|
}
|
|
|
|
func New(name string) *Plugin {
|
|
return &Plugin{
|
|
name: name,
|
|
mux: http.NewServeMux(),
|
|
}
|
|
}
|
|
|
|
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)
|
|
|
|
addr := ":8080"
|
|
if v, _ := s.Settings().Get("addr"); v != nil {
|
|
if s2, ok := v.(string); ok && s2 != "" {
|
|
addr = s2
|
|
}
|
|
}
|
|
|
|
s.RegisterOutputChannel("webui", 1, "Web 控制台", sdk.ChannelDef{}, func(args map[string]interface{}) (interface{}, error) {
|
|
payload, _ := args["payload"].(string)
|
|
if payload != "" {
|
|
s.Publish(&sdk.Event{
|
|
Type: sdk.EventAgentOutput,
|
|
Payload: map[string]interface{}{
|
|
"content": payload,
|
|
"channel": "webui",
|
|
"kind": "channel_output",
|
|
},
|
|
})
|
|
}
|
|
return map[string]interface{}{"status": "ok"}, nil
|
|
})
|
|
|
|
s.Settings().RegisterDef(sdk.ConfigDef{Key: "addr", Default: ":8080", Type: "string", DisplayName: "监听地址", Description: "Web 控制台监听地址", Category: "webui"})
|
|
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 {
|
|
s.Publish(&sdk.Event{Type: sdk.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
|
|
}
|
|
s.Publish(&sdk.Event{Type: sdk.EventStage, Payload: map[string]interface{}{"phase": "before_toolcall", "tool": tool, "message": "tool:" + tool}})
|
|
return nil
|
|
})
|
|
s.RegisterStage(sdk.StageBeforeOutput, func(ctx *sdk.StageContext) error {
|
|
s.Publish(&sdk.Event{Type: sdk.EventStage, Payload: map[string]interface{}{"phase": "before_output", "message": "output"}})
|
|
return nil
|
|
})
|
|
|
|
p.handler = NewHandler(s)
|
|
p.handler.RegisterRoutes(p.mux)
|
|
|
|
p.server = &http.Server{Addr: addr, Handler: p.mux}
|
|
go func() {
|
|
log.Printf("[webui] HTTP server listening on %s", 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
|
|
}
|