mirror of
https://gitcode.com/JianFeeeee/HomeAgent.git
synced 2026-10-05 15:08:35 +00:00
refactor: remove core skill direct loading, skills owned by clawhubadapter only
- Drop skill.NewManager from homed bootstrap; skills dir no longer core-managed - Remove GetInjectedPrompt system-prompt injection (skills are not first-class) - Delete internal/skill package, SkillAPI, webui /api/v1/skills, status skills block - ConfigRegistry: plugin config tables now created only via RegisterDef; arbitrary scope Set/Get no longer implicitly creates config_<name> tables (fixes stray config_today_task table from SKILL directory name being used as a scope)
This commit is contained in:
@ -36,7 +36,6 @@ import (
|
|||||||
_ "gitcode.com/JianFeeeee/HomeAgent/internal/plugins/pluginmgr"
|
_ "gitcode.com/JianFeeeee/HomeAgent/internal/plugins/pluginmgr"
|
||||||
_ "gitcode.com/JianFeeeee/HomeAgent/internal/plugins/webui"
|
_ "gitcode.com/JianFeeeee/HomeAgent/internal/plugins/webui"
|
||||||
sdk "gitcode.com/JianFeeeee/HomeAgent/internal/sdk"
|
sdk "gitcode.com/JianFeeeee/HomeAgent/internal/sdk"
|
||||||
"gitcode.com/JianFeeeee/HomeAgent/internal/skill"
|
|
||||||
"gitcode.com/JianFeeeee/HomeAgent/internal/supervisor"
|
"gitcode.com/JianFeeeee/HomeAgent/internal/supervisor"
|
||||||
"gitcode.com/JianFeeeee/HomeAgent/internal/tracker"
|
"gitcode.com/JianFeeeee/HomeAgent/internal/tracker"
|
||||||
_ "gitcode.com/JianFeeeee/HomeAgent/internal/plugins"
|
_ "gitcode.com/JianFeeeee/HomeAgent/internal/plugins"
|
||||||
@ -88,7 +87,6 @@ func main() {
|
|||||||
dirs := []string{
|
dirs := []string{
|
||||||
*dataDir,
|
*dataDir,
|
||||||
filepath.Join(*dataDir, "snapshots"),
|
filepath.Join(*dataDir, "snapshots"),
|
||||||
filepath.Join(*dataDir, "skills"),
|
|
||||||
filepath.Join(*dataDir, "plugins"),
|
filepath.Join(*dataDir, "plugins"),
|
||||||
filepath.Join(*dataDir, "changesets"),
|
filepath.Join(*dataDir, "changesets"),
|
||||||
filepath.Join(*dataDir, "memory"),
|
filepath.Join(*dataDir, "memory"),
|
||||||
@ -131,11 +129,6 @@ func main() {
|
|||||||
defer distiller.Stop()
|
defer distiller.Stop()
|
||||||
}
|
}
|
||||||
|
|
||||||
skMgr := skill.NewManager(filepath.Join(*dataDir, "skills"))
|
|
||||||
if err := skMgr.Init(); err != nil {
|
|
||||||
log.Printf("[homed] warning: skill init failed: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ========================================================================
|
// ========================================================================
|
||||||
// 配置中心(SQLite 持久化,唯一配置源)
|
// 配置中心(SQLite 持久化,唯一配置源)
|
||||||
// ========================================================================
|
// ========================================================================
|
||||||
@ -391,7 +384,6 @@ func main() {
|
|||||||
IO: iom,
|
IO: iom,
|
||||||
Memory: memDB,
|
Memory: memDB,
|
||||||
Indexer: memIdx,
|
Indexer: memIdx,
|
||||||
Skills: skMgr,
|
|
||||||
Tracker: trk,
|
Tracker: trk,
|
||||||
DocStore: docStore,
|
DocStore: docStore,
|
||||||
Knowledge: ks,
|
Knowledge: ks,
|
||||||
@ -416,7 +408,6 @@ func main() {
|
|||||||
pluginReg.SetLuaVM(luaVM)
|
pluginReg.SetLuaVM(luaVM)
|
||||||
pluginReg.SetBaseAPIKey(baseAPIKey)
|
pluginReg.SetBaseAPIKey(baseAPIKey)
|
||||||
pluginReg.SetSupervisor(supervisor.NewSDKAdapter(sup))
|
pluginReg.SetSupervisor(supervisor.NewSDKAdapter(sup))
|
||||||
pluginReg.SetSkillManager(skMgr)
|
|
||||||
pluginReg.SetTracker(trk)
|
pluginReg.SetTracker(trk)
|
||||||
pluginReg.SetConfig(cfg)
|
pluginReg.SetConfig(cfg)
|
||||||
pluginReg.SetStageHost(stageHost)
|
pluginReg.SetStageHost(stageHost)
|
||||||
|
|||||||
@ -17,7 +17,6 @@ import (
|
|||||||
"gitcode.com/JianFeeeee/HomeAgent/internal/memory/social"
|
"gitcode.com/JianFeeeee/HomeAgent/internal/memory/social"
|
||||||
"gitcode.com/JianFeeeee/HomeAgent/internal/memory/text"
|
"gitcode.com/JianFeeeee/HomeAgent/internal/memory/text"
|
||||||
"gitcode.com/JianFeeeee/HomeAgent/internal/plugin"
|
"gitcode.com/JianFeeeee/HomeAgent/internal/plugin"
|
||||||
"gitcode.com/JianFeeeee/HomeAgent/internal/skill"
|
|
||||||
"gitcode.com/JianFeeeee/HomeAgent/internal/tracker"
|
"gitcode.com/JianFeeeee/HomeAgent/internal/tracker"
|
||||||
"gitcode.com/JianFeeeee/HomeAgent/pkg/types"
|
"gitcode.com/JianFeeeee/HomeAgent/pkg/types"
|
||||||
)
|
)
|
||||||
@ -33,7 +32,6 @@ type Agent struct {
|
|||||||
io *agentIO.IOManager
|
io *agentIO.IOManager
|
||||||
memory *memory.GraphDB
|
memory *memory.GraphDB
|
||||||
indexer *memory.Indexer
|
indexer *memory.Indexer
|
||||||
skills *skill.Manager
|
|
||||||
tracker *tracker.Tracker
|
tracker *tracker.Tracker
|
||||||
context *RelevanceContext
|
context *RelevanceContext
|
||||||
systemPrompt string
|
systemPrompt string
|
||||||
@ -122,7 +120,6 @@ type AgentConfig struct {
|
|||||||
IO *agentIO.IOManager
|
IO *agentIO.IOManager
|
||||||
Memory *memory.GraphDB
|
Memory *memory.GraphDB
|
||||||
Indexer *memory.Indexer
|
Indexer *memory.Indexer
|
||||||
Skills *skill.Manager
|
|
||||||
Tracker *tracker.Tracker
|
Tracker *tracker.Tracker
|
||||||
|
|
||||||
DocStore *document.Store
|
DocStore *document.Store
|
||||||
@ -190,7 +187,6 @@ func New(cfg AgentConfig) *Agent {
|
|||||||
io: cfg.IO,
|
io: cfg.IO,
|
||||||
memory: cfg.Memory,
|
memory: cfg.Memory,
|
||||||
indexer: cfg.Indexer,
|
indexer: cfg.Indexer,
|
||||||
skills: cfg.Skills,
|
|
||||||
tracker: cfg.Tracker,
|
tracker: cfg.Tracker,
|
||||||
context: rc,
|
context: rc,
|
||||||
systemPrompt: cfg.SystemPrompt,
|
systemPrompt: cfg.SystemPrompt,
|
||||||
|
|||||||
@ -11,7 +11,6 @@ import (
|
|||||||
"gitcode.com/JianFeeeee/HomeAgent/internal/memory/social"
|
"gitcode.com/JianFeeeee/HomeAgent/internal/memory/social"
|
||||||
"gitcode.com/JianFeeeee/HomeAgent/internal/memory/text"
|
"gitcode.com/JianFeeeee/HomeAgent/internal/memory/text"
|
||||||
"gitcode.com/JianFeeeee/HomeAgent/internal/plugin"
|
"gitcode.com/JianFeeeee/HomeAgent/internal/plugin"
|
||||||
"gitcode.com/JianFeeeee/HomeAgent/internal/skill"
|
|
||||||
sdk "gitcode.com/JianFeeeee/HomeAgent/internal/sdk"
|
sdk "gitcode.com/JianFeeeee/HomeAgent/internal/sdk"
|
||||||
"gitcode.com/JianFeeeee/HomeAgent/internal/tracker"
|
"gitcode.com/JianFeeeee/HomeAgent/internal/tracker"
|
||||||
)
|
)
|
||||||
@ -30,7 +29,6 @@ type KnowledgeStatus = sdk.KnowledgeStatus
|
|||||||
type DocumentStatus = sdk.DocumentStatus
|
type DocumentStatus = sdk.DocumentStatus
|
||||||
type TextMemoryStatus = sdk.TextMemoryStatus
|
type TextMemoryStatus = sdk.TextMemoryStatus
|
||||||
type SocialStatus = sdk.SocialStatus
|
type SocialStatus = sdk.SocialStatus
|
||||||
type SkillsStatus = sdk.SkillsStatus
|
|
||||||
type LLMStatus = sdk.LLMStatus
|
type LLMStatus = sdk.LLMStatus
|
||||||
type ContextStatus = sdk.ContextStatus
|
type ContextStatus = sdk.ContextStatus
|
||||||
type RuntimeStatus = sdk.RuntimeStatus
|
type RuntimeStatus = sdk.RuntimeStatus
|
||||||
@ -59,7 +57,6 @@ func collectKernelStatus(
|
|||||||
docStore *document.Store,
|
docStore *document.Store,
|
||||||
textMem *text.Memory,
|
textMem *text.Memory,
|
||||||
socialStore *social.SocialStore,
|
socialStore *social.SocialStore,
|
||||||
skMgr *skill.Manager,
|
|
||||||
trk *tracker.Tracker,
|
trk *tracker.Tracker,
|
||||||
) *KernelStatus {
|
) *KernelStatus {
|
||||||
status := &KernelStatus{
|
status := &KernelStatus{
|
||||||
@ -146,15 +143,6 @@ func collectKernelStatus(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Skills
|
|
||||||
if skMgr != nil {
|
|
||||||
status.Skills.Available = true
|
|
||||||
skills := skMgr.List()
|
|
||||||
status.Skills.SkillList = make([]string, len(skills))
|
|
||||||
for i, sk := range skills {
|
|
||||||
status.Skills.SkillList[i] = sk.Name
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Tracker
|
// Tracker
|
||||||
if trk != nil {
|
if trk != nil {
|
||||||
@ -191,10 +179,6 @@ func (a *Agent) GetKernelStatus() *KernelStatus {
|
|||||||
socialStore = a.social
|
socialStore = a.social
|
||||||
}
|
}
|
||||||
|
|
||||||
var skMgr *skill.Manager
|
|
||||||
if a.skills != nil {
|
|
||||||
skMgr = a.skills
|
|
||||||
}
|
|
||||||
|
|
||||||
var trk *tracker.Tracker
|
var trk *tracker.Tracker
|
||||||
if a.tracker != nil {
|
if a.tracker != nil {
|
||||||
@ -214,7 +198,6 @@ func (a *Agent) GetKernelStatus() *KernelStatus {
|
|||||||
a.docStore,
|
a.docStore,
|
||||||
textMem,
|
textMem,
|
||||||
socialStore,
|
socialStore,
|
||||||
skMgr,
|
|
||||||
trk,
|
trk,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@ -56,12 +56,6 @@ func (a *Agent) buildSystemPrompt(memContext string, userInput string) string {
|
|||||||
prompt += "- 直接返回纯文本不会到达任何用户端。\n"
|
prompt += "- 直接返回纯文本不会到达任何用户端。\n"
|
||||||
prompt += "- 需要多步执行的长任务:**必须先**用 output_send__ 发一条确认消息告诉用户已收到(如「好的我去看看~」),**然后再**执行具体排查工具。确认消息不代表任务完成,发出后仍需继续执行实际工具并最终汇报结果。"
|
prompt += "- 需要多步执行的长任务:**必须先**用 output_send__ 发一条确认消息告诉用户已收到(如「好的我去看看~」),**然后再**执行具体排查工具。确认消息不代表任务完成,发出后仍需继续执行实际工具并最终汇报结果。"
|
||||||
|
|
||||||
if a.skills != nil {
|
|
||||||
if sp := a.skills.GetInjectedPrompt(); sp != "" {
|
|
||||||
prompt += "\n\n" + sp
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if a.indexer != nil {
|
if a.indexer != nil {
|
||||||
prompt += "\n\n" + a.indexer.BuildToolPrompt()
|
prompt += "\n\n" + a.indexer.BuildToolPrompt()
|
||||||
}
|
}
|
||||||
|
|||||||
@ -711,7 +711,6 @@ func (r *ConfigRegistry) ListPlugins() []string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (r *ConfigRegistry) PluginConfig(name string) *PluginSettings {
|
func (r *ConfigRegistry) PluginConfig(name string) *PluginSettings {
|
||||||
r.ensurePluginTable(name)
|
|
||||||
return &PluginSettings{
|
return &PluginSettings{
|
||||||
registry: r,
|
registry: r,
|
||||||
table: r.pluginTableName(name),
|
table: r.pluginTableName(name),
|
||||||
@ -768,6 +767,9 @@ func (p *PluginSettings) List(prefix string) ([]string, error) {
|
|||||||
func (p *PluginSettings) RegisterDef(def ConfigDef) {
|
func (p *PluginSettings) RegisterDef(def ConfigDef) {
|
||||||
p.registry.mu.Lock()
|
p.registry.mu.Lock()
|
||||||
defer p.registry.mu.Unlock()
|
defer p.registry.mu.Unlock()
|
||||||
|
// 只有注册配置定义才创建插件配置表:任意 scope 的读写不得隐式建表,
|
||||||
|
// 避免非插件(如 SKILL 目录名)被注册成配置命名空间。
|
||||||
|
p.registry.ensurePluginTable(p.name)
|
||||||
p.registry.db.Exec(fmt.Sprintf(`INSERT OR IGNORE INTO %s (key, value) VALUES (?, ?)`, p.table), def.Key, def.Default)
|
p.registry.db.Exec(fmt.Sprintf(`INSERT OR IGNORE INTO %s (key, value) VALUES (?, ?)`, p.table), def.Key, def.Default)
|
||||||
qualified := "plugin." + p.name + "." + def.Key
|
qualified := "plugin." + p.name + "." + def.Key
|
||||||
def.Key = qualified
|
def.Key = qualified
|
||||||
|
|||||||
@ -121,6 +121,7 @@ func TestPluginConfig(t *testing.T) {
|
|||||||
r := NewConfigRegistry(path)
|
r := NewConfigRegistry(path)
|
||||||
|
|
||||||
ps := r.PluginConfig("test_deepseek")
|
ps := r.PluginConfig("test_deepseek")
|
||||||
|
ps.RegisterDef(ConfigDef{Key: "api_key", Default: "sk-test123"})
|
||||||
if err := ps.Set("api_key", "sk-test123"); err != nil {
|
if err := ps.Set("api_key", "sk-test123"); err != nil {
|
||||||
t.Fatalf("PluginSettings.Set: %v", err)
|
t.Fatalf("PluginSettings.Set: %v", err)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -394,8 +394,8 @@ function plugin.start(sdk)
|
|||||||
sdk.settings.set_plugin("other", "okey", "oval")
|
sdk.settings.set_plugin("other", "okey", "oval")
|
||||||
_G.res.plugin_val = sdk.settings.get_plugin("other", "okey")
|
_G.res.plugin_val = sdk.settings.get_plugin("other", "okey")
|
||||||
_G.res.plugin_list = sdk.settings.list_plugin("other", "")
|
_G.res.plugin_list = sdk.settings.list_plugin("other", "")
|
||||||
_G.res.sett_list_type = type(sdk.settings.list(""))
|
|
||||||
sdk.settings.register_def({key="def_key", type="string", display_name="DK", default="dv"})
|
sdk.settings.register_def({key="def_key", type="string", display_name="DK", default="dv"})
|
||||||
|
_G.res.sett_list_type = type(sdk.settings.list(""))
|
||||||
_G.res.def_val = sdk.get_setting("def_key")
|
_G.res.def_val = sdk.get_setting("def_key")
|
||||||
_G.res.defs_type = type(sdk.settings.defs(""))
|
_G.res.defs_type = type(sdk.settings.defs(""))
|
||||||
_G.res.dump_type = type(sdk.settings.dump())
|
_G.res.dump_type = type(sdk.settings.dump())
|
||||||
@ -415,7 +415,10 @@ return plugin
|
|||||||
lp := plg.(*luaPlugin)
|
lp := plg.(*luaPlugin)
|
||||||
|
|
||||||
var capturedHandler sdk.StageHandler
|
var capturedHandler sdk.StageHandler
|
||||||
sett := sdk.NewSettings("aligned", internalConfig.NewConfigRegistry(""))
|
reg := internalConfig.NewConfigRegistry("")
|
||||||
|
sett := sdk.NewSettings("aligned", reg)
|
||||||
|
// 目标插件须先注册配置定义才会建表(任意 scope 不再隐式建表)
|
||||||
|
sdk.NewSettings("other", reg).RegisterDef(sdk.ConfigDef{Key: "okey", Default: "oval", Type: "string"})
|
||||||
s := sdk.New("aligned", sdk.SDKConfig{
|
s := sdk.New("aligned", sdk.SDKConfig{
|
||||||
Settings: sett,
|
Settings: sett,
|
||||||
RegStage: func(stage sdk.Stage, handler sdk.StageHandler) {
|
RegStage: func(stage sdk.Stage, handler sdk.StageHandler) {
|
||||||
|
|||||||
@ -19,7 +19,6 @@ import (
|
|||||||
agentAPI "gitcode.com/JianFeeeee/HomeAgent/internal/agent/api"
|
agentAPI "gitcode.com/JianFeeeee/HomeAgent/internal/agent/api"
|
||||||
internalConfig "gitcode.com/JianFeeeee/HomeAgent/internal/config"
|
internalConfig "gitcode.com/JianFeeeee/HomeAgent/internal/config"
|
||||||
luaVM "gitcode.com/JianFeeeee/HomeAgent/internal/lua"
|
luaVM "gitcode.com/JianFeeeee/HomeAgent/internal/lua"
|
||||||
"gitcode.com/JianFeeeee/HomeAgent/internal/skill"
|
|
||||||
"gitcode.com/JianFeeeee/HomeAgent/internal/tracker"
|
"gitcode.com/JianFeeeee/HomeAgent/internal/tracker"
|
||||||
"gitcode.com/JianFeeeee/HomeAgent/pkg/types"
|
"gitcode.com/JianFeeeee/HomeAgent/pkg/types"
|
||||||
)
|
)
|
||||||
@ -90,7 +89,6 @@ type Registry struct {
|
|||||||
|
|
||||||
status sdk.StatusAPI
|
status sdk.StatusAPI
|
||||||
sup sdk.SupervisorAPI
|
sup sdk.SupervisorAPI
|
||||||
skMgr *skill.Manager
|
|
||||||
trk *tracker.Tracker
|
trk *tracker.Tracker
|
||||||
cfg *types.Config
|
cfg *types.Config
|
||||||
stageHost sdk.ToolSource
|
stageHost sdk.ToolSource
|
||||||
@ -126,7 +124,6 @@ func (r *Registry) SetAPIRegistrar(fn sdk.APIRegistrar) { r.regAPI = fn
|
|||||||
func (r *Registry) SetToolCleaner(tc PluginToolCleaner) { r.toolCleaner = tc }
|
func (r *Registry) SetToolCleaner(tc PluginToolCleaner) { r.toolCleaner = tc }
|
||||||
func (r *Registry) SetStatusProvider(sp sdk.StatusAPI) { r.status = sp }
|
func (r *Registry) SetStatusProvider(sp sdk.StatusAPI) { r.status = sp }
|
||||||
func (r *Registry) SetSupervisor(sup sdk.SupervisorAPI) { r.sup = sup }
|
func (r *Registry) SetSupervisor(sup sdk.SupervisorAPI) { r.sup = sup }
|
||||||
func (r *Registry) SetSkillManager(skMgr *skill.Manager) { r.skMgr = skMgr }
|
|
||||||
func (r *Registry) SetTracker(trk *tracker.Tracker) { r.trk = trk }
|
func (r *Registry) SetTracker(trk *tracker.Tracker) { r.trk = trk }
|
||||||
func (r *Registry) SetConfig(cfg *types.Config) { r.cfg = cfg }
|
func (r *Registry) SetConfig(cfg *types.Config) { r.cfg = cfg }
|
||||||
func (r *Registry) SetStageHost(sh sdk.ToolSource) { r.stageHost = sh }
|
func (r *Registry) SetStageHost(sh sdk.ToolSource) { r.stageHost = sh }
|
||||||
@ -216,7 +213,6 @@ func (r *Registry) buildSDK(name string) *sdk.PluginSDK {
|
|||||||
|
|
||||||
Status: r.status,
|
Status: r.status,
|
||||||
Supervisor: r.sup,
|
Supervisor: r.sup,
|
||||||
Skill: r.skMgr,
|
|
||||||
Adapter: sdk.NewAdapter(r.lua),
|
Adapter: sdk.NewAdapter(r.lua),
|
||||||
Tracker: r.trk,
|
Tracker: r.trk,
|
||||||
Config: sdk.NewConfig(r.cfg),
|
Config: sdk.NewConfig(r.cfg),
|
||||||
|
|||||||
@ -38,7 +38,6 @@ type Handler struct {
|
|||||||
supervisor sdk.SupervisorAPI
|
supervisor sdk.SupervisorAPI
|
||||||
memory sdk.MemoryAPI
|
memory sdk.MemoryAPI
|
||||||
indexer sdk.IndexerAPI
|
indexer sdk.IndexerAPI
|
||||||
skills sdk.SkillAPI
|
|
||||||
adapter sdk.AdapterAPI
|
adapter sdk.AdapterAPI
|
||||||
config sdk.ConfigAPI
|
config sdk.ConfigAPI
|
||||||
startTime time.Time
|
startTime time.Time
|
||||||
@ -97,7 +96,6 @@ func NewHandler(s *sdk.PluginSDK) *Handler {
|
|||||||
sup sdk.SupervisorAPI
|
sup sdk.SupervisorAPI
|
||||||
mem sdk.MemoryAPI
|
mem sdk.MemoryAPI
|
||||||
idx sdk.IndexerAPI
|
idx sdk.IndexerAPI
|
||||||
sk sdk.SkillAPI
|
|
||||||
ad sdk.AdapterAPI
|
ad sdk.AdapterAPI
|
||||||
cfg sdk.ConfigAPI
|
cfg sdk.ConfigAPI
|
||||||
tm sdk.TextMemoryAPI
|
tm sdk.TextMemoryAPI
|
||||||
@ -110,7 +108,7 @@ func NewHandler(s *sdk.PluginSDK) *Handler {
|
|||||||
)
|
)
|
||||||
if s != nil {
|
if s != nil {
|
||||||
sup, mem, idx = s.Supervisor(), s.Memory(), s.Indexer()
|
sup, mem, idx = s.Supervisor(), s.Memory(), s.Indexer()
|
||||||
sk, ad, cfg = s.Skill(), s.Adapter(), s.Config()
|
ad, cfg = s.Adapter(), s.Config()
|
||||||
tm, ks, tr = s.TextMemory(), s.Knowledge(), s.Tracker()
|
tm, ks, tr = s.TextMemory(), s.Knowledge(), s.Tracker()
|
||||||
se, pm = s.Settings(), s.PluginMgr()
|
se, pm = s.Settings(), s.PluginMgr()
|
||||||
st, llm = s.Status(), s.LLM()
|
st, llm = s.Status(), s.LLM()
|
||||||
@ -120,7 +118,6 @@ func NewHandler(s *sdk.PluginSDK) *Handler {
|
|||||||
supervisor: sup,
|
supervisor: sup,
|
||||||
memory: mem,
|
memory: mem,
|
||||||
indexer: idx,
|
indexer: idx,
|
||||||
skills: sk,
|
|
||||||
adapter: ad,
|
adapter: ad,
|
||||||
config: cfg,
|
config: cfg,
|
||||||
startTime: time.Now(),
|
startTime: time.Now(),
|
||||||
@ -378,7 +375,6 @@ func (h *Handler) RegisterRoutes(mux *http.ServeMux) {
|
|||||||
mux.HandleFunc("/api/v1/status", h.requireAPI(h.handleStatus))
|
mux.HandleFunc("/api/v1/status", h.requireAPI(h.handleStatus))
|
||||||
mux.HandleFunc("/api/v1/agents", h.requireAPI(h.handleAgents))
|
mux.HandleFunc("/api/v1/agents", h.requireAPI(h.handleAgents))
|
||||||
mux.HandleFunc("/api/v1/agents/", h.requireAPI(h.handleAgentByID))
|
mux.HandleFunc("/api/v1/agents/", h.requireAPI(h.handleAgentByID))
|
||||||
mux.HandleFunc("/api/v1/skills", h.requireAPI(h.handleSkills))
|
|
||||||
mux.HandleFunc("/api/v1/memory", h.requireAPI(h.handleMemory))
|
mux.HandleFunc("/api/v1/memory", h.requireAPI(h.handleMemory))
|
||||||
mux.HandleFunc("/api/v1/memory/", h.requireAPI(h.handleMemory))
|
mux.HandleFunc("/api/v1/memory/", h.requireAPI(h.handleMemory))
|
||||||
mux.HandleFunc("/api/v1/memory/graph", h.requireAPI(h.handleMemoryGraph))
|
mux.HandleFunc("/api/v1/memory/graph", h.requireAPI(h.handleMemoryGraph))
|
||||||
@ -603,44 +599,6 @@ func (h *Handler) handleAgentAction(w http.ResponseWriter, r *http.Request, agen
|
|||||||
writeJSON(w, http.StatusOK, map[string]string{"status": fmt.Sprintf("%s_requested", action), "agent": string(agentID)})
|
writeJSON(w, http.StatusOK, map[string]string{"status": fmt.Sprintf("%s_requested", action), "agent": string(agentID)})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *Handler) handleSkills(w http.ResponseWriter, r *http.Request) {
|
|
||||||
if h.skills == nil {
|
|
||||||
writeJSON(w, http.StatusServiceUnavailable, map[string]string{"error": "skills not available"})
|
|
||||||
return
|
|
||||||
}
|
|
||||||
switch r.Method {
|
|
||||||
case http.MethodGet:
|
|
||||||
writeJSON(w, http.StatusOK, map[string]interface{}{"skills": h.skills.List()})
|
|
||||||
case http.MethodPost:
|
|
||||||
var req struct {
|
|
||||||
Name string `json:"name"`
|
|
||||||
Content string `json:"content"`
|
|
||||||
}
|
|
||||||
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
|
|
||||||
writeJSON(w, http.StatusBadRequest, map[string]string{"error": "invalid request"})
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if err := h.skills.Install(req.Name, req.Content); err != nil {
|
|
||||||
writeJSON(w, http.StatusInternalServerError, map[string]string{"error": err.Error()})
|
|
||||||
return
|
|
||||||
}
|
|
||||||
writeJSON(w, http.StatusCreated, map[string]string{"status": "installed", "name": req.Name})
|
|
||||||
case http.MethodDelete:
|
|
||||||
name := r.URL.Query().Get("name")
|
|
||||||
if name == "" {
|
|
||||||
writeJSON(w, http.StatusBadRequest, map[string]string{"error": "name query param required"})
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if err := h.skills.Uninstall(name); err != nil {
|
|
||||||
writeJSON(w, http.StatusNotFound, map[string]string{"error": err.Error()})
|
|
||||||
return
|
|
||||||
}
|
|
||||||
writeJSON(w, http.StatusOK, map[string]string{"status": "uninstalled", "name": name})
|
|
||||||
default:
|
|
||||||
http.Error(w, "method not allowed", http.StatusMethodNotAllowed)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (h *Handler) handleMemory(w http.ResponseWriter, r *http.Request) {
|
func (h *Handler) handleMemory(w http.ResponseWriter, r *http.Request) {
|
||||||
if h.memory == nil {
|
if h.memory == nil {
|
||||||
writeJSON(w, http.StatusServiceUnavailable, map[string]string{"error": "memory system not available"})
|
writeJSON(w, http.StatusServiceUnavailable, map[string]string{"error": "memory system not available"})
|
||||||
|
|||||||
@ -90,7 +90,6 @@ type PluginSDK struct {
|
|||||||
|
|
||||||
status StatusAPI
|
status StatusAPI
|
||||||
supervisor SupervisorAPI
|
supervisor SupervisorAPI
|
||||||
skill SkillAPI
|
|
||||||
adapter AdapterAPI
|
adapter AdapterAPI
|
||||||
tracker TrackerAPI
|
tracker TrackerAPI
|
||||||
config ConfigAPI
|
config ConfigAPI
|
||||||
@ -150,7 +149,6 @@ type SDKConfig struct {
|
|||||||
|
|
||||||
Status StatusAPI
|
Status StatusAPI
|
||||||
Supervisor SupervisorAPI
|
Supervisor SupervisorAPI
|
||||||
Skill SkillAPI
|
|
||||||
Adapter AdapterAPI
|
Adapter AdapterAPI
|
||||||
Tracker TrackerAPI
|
Tracker TrackerAPI
|
||||||
Config ConfigAPI
|
Config ConfigAPI
|
||||||
@ -187,7 +185,6 @@ func New(name string, cfg SDKConfig) *PluginSDK {
|
|||||||
|
|
||||||
status: cfg.Status,
|
status: cfg.Status,
|
||||||
supervisor: cfg.Supervisor,
|
supervisor: cfg.Supervisor,
|
||||||
skill: cfg.Skill,
|
|
||||||
adapter: cfg.Adapter,
|
adapter: cfg.Adapter,
|
||||||
tracker: cfg.Tracker,
|
tracker: cfg.Tracker,
|
||||||
config: cfg.Config,
|
config: cfg.Config,
|
||||||
@ -198,7 +195,6 @@ func New(name string, cfg SDKConfig) *PluginSDK {
|
|||||||
|
|
||||||
func (s *PluginSDK) Status() StatusAPI { return s.status }
|
func (s *PluginSDK) Status() StatusAPI { return s.status }
|
||||||
func (s *PluginSDK) Supervisor() SupervisorAPI { return s.supervisor }
|
func (s *PluginSDK) Supervisor() SupervisorAPI { return s.supervisor }
|
||||||
func (s *PluginSDK) Skill() SkillAPI { return s.skill }
|
|
||||||
func (s *PluginSDK) Adapter() AdapterAPI { return s.adapter }
|
func (s *PluginSDK) Adapter() AdapterAPI { return s.adapter }
|
||||||
func (s *PluginSDK) Tracker() TrackerAPI { return s.tracker }
|
func (s *PluginSDK) Tracker() TrackerAPI { return s.tracker }
|
||||||
func (s *PluginSDK) Config() ConfigAPI { return s.config }
|
func (s *PluginSDK) Config() ConfigAPI { return s.config }
|
||||||
|
|||||||
@ -1,22 +0,0 @@
|
|||||||
package sdk
|
|
||||||
|
|
||||||
// SkillAPI provides access to the skill manager.
|
|
||||||
type SkillAPI interface {
|
|
||||||
List() []*Skill
|
|
||||||
Get(name string) *Skill
|
|
||||||
Install(name, content string) error
|
|
||||||
Uninstall(name string) error
|
|
||||||
Toggle(name string, enabled bool) error
|
|
||||||
}
|
|
||||||
|
|
||||||
// Skill is a neutral description of an installed skill.
|
|
||||||
type Skill struct {
|
|
||||||
Name string `json:"name"`
|
|
||||||
Description string `json:"description"`
|
|
||||||
Version string `json:"version"`
|
|
||||||
Author string `json:"author,omitempty"`
|
|
||||||
Entry string `json:"entry,omitempty"`
|
|
||||||
Source string `json:"source,omitempty"`
|
|
||||||
Enabled bool `json:"enabled"`
|
|
||||||
RawContent string `json:"-"`
|
|
||||||
}
|
|
||||||
@ -21,7 +21,6 @@ type KernelStatus struct {
|
|||||||
Documents DocumentStatus `json:"documents"`
|
Documents DocumentStatus `json:"documents"`
|
||||||
TextMemory TextMemoryStatus `json:"text_memory"`
|
TextMemory TextMemoryStatus `json:"text_memory"`
|
||||||
Social SocialStatus `json:"social"`
|
Social SocialStatus `json:"social"`
|
||||||
Skills SkillsStatus `json:"skills"`
|
|
||||||
|
|
||||||
LLM LLMStatus `json:"llm"`
|
LLM LLMStatus `json:"llm"`
|
||||||
|
|
||||||
@ -72,11 +71,6 @@ type SocialStatus struct {
|
|||||||
PersonCount int `json:"person_count"`
|
PersonCount int `json:"person_count"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type SkillsStatus struct {
|
|
||||||
Available bool `json:"available"`
|
|
||||||
SkillList []string `json:"skill_list,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type LLMStatus struct {
|
type LLMStatus struct {
|
||||||
Available bool `json:"available"`
|
Available bool `json:"available"`
|
||||||
Provider string `json:"provider,omitempty"`
|
Provider string `json:"provider,omitempty"`
|
||||||
|
|||||||
@ -1,215 +0,0 @@
|
|||||||
package skill
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
|
||||||
"os"
|
|
||||||
"path/filepath"
|
|
||||||
"sort"
|
|
||||||
"strings"
|
|
||||||
"sync"
|
|
||||||
|
|
||||||
sdk "gitcode.com/JianFeeeee/HomeAgent/internal/sdk"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Skill 已迁入内置 SDK,此处保留别名以兼容现有调用方。
|
|
||||||
type Skill = sdk.Skill
|
|
||||||
|
|
||||||
type Manager struct {
|
|
||||||
mu sync.RWMutex
|
|
||||||
skillsDir string
|
|
||||||
skills map[string]*Skill
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewManager(skillsDir string) *Manager {
|
|
||||||
return &Manager{
|
|
||||||
skillsDir: skillsDir,
|
|
||||||
skills: make(map[string]*Skill),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *Manager) Init() error {
|
|
||||||
if err := os.MkdirAll(m.skillsDir, 0755); err != nil {
|
|
||||||
return fmt.Errorf("create skills dir: %w", err)
|
|
||||||
}
|
|
||||||
return m.loadAll()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *Manager) loadAll() error {
|
|
||||||
entries, err := os.ReadDir(m.skillsDir)
|
|
||||||
if err != nil {
|
|
||||||
if os.IsNotExist(err) {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, entry := range entries {
|
|
||||||
if !entry.IsDir() {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
skillDir := filepath.Join(m.skillsDir, entry.Name())
|
|
||||||
skill, err := m.loadSkill(skillDir)
|
|
||||||
if err != nil {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
m.skills[skill.Name] = skill
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *Manager) loadSkill(dir string) (*Skill, error) {
|
|
||||||
skill := &Skill{
|
|
||||||
Name: filepath.Base(dir),
|
|
||||||
Enabled: true,
|
|
||||||
}
|
|
||||||
|
|
||||||
skillFilePath := filepath.Join(dir, "SKILL.md")
|
|
||||||
if data, err := os.ReadFile(skillFilePath); err == nil {
|
|
||||||
skill.RawContent = string(data)
|
|
||||||
skill.Description = extractDescription(skill.RawContent)
|
|
||||||
skill.Version = extractField(skill.RawContent, "version")
|
|
||||||
skill.Author = extractField(skill.RawContent, "author")
|
|
||||||
}
|
|
||||||
|
|
||||||
metaPath := filepath.Join(dir, "skill.json")
|
|
||||||
if data, err := os.ReadFile(metaPath); err == nil {
|
|
||||||
var meta struct {
|
|
||||||
Name string `json:"name"`
|
|
||||||
Description string `json:"description"`
|
|
||||||
Version string `json:"version"`
|
|
||||||
Author string `json:"author"`
|
|
||||||
Entry string `json:"entry"`
|
|
||||||
}
|
|
||||||
if err := json.Unmarshal(data, &meta); err == nil {
|
|
||||||
if meta.Name != "" {
|
|
||||||
skill.Name = meta.Name
|
|
||||||
}
|
|
||||||
if meta.Description != "" {
|
|
||||||
skill.Description = meta.Description
|
|
||||||
}
|
|
||||||
if meta.Version != "" {
|
|
||||||
skill.Version = meta.Version
|
|
||||||
}
|
|
||||||
if meta.Author != "" {
|
|
||||||
skill.Author = meta.Author
|
|
||||||
}
|
|
||||||
if meta.Entry != "" {
|
|
||||||
skill.Entry = meta.Entry
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return skill, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *Manager) Install(name string, content string) error {
|
|
||||||
m.mu.Lock()
|
|
||||||
defer m.mu.Unlock()
|
|
||||||
|
|
||||||
skillDir := filepath.Join(m.skillsDir, name)
|
|
||||||
if err := os.MkdirAll(skillDir, 0755); err != nil {
|
|
||||||
return fmt.Errorf("create skill dir: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := os.WriteFile(filepath.Join(skillDir, "SKILL.md"), []byte(content), 0644); err != nil {
|
|
||||||
return fmt.Errorf("write SKILL.md: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
skill, err := m.loadSkill(skillDir)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("load installed skill: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
m.skills[name] = skill
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *Manager) Uninstall(name string) error {
|
|
||||||
m.mu.Lock()
|
|
||||||
defer m.mu.Unlock()
|
|
||||||
|
|
||||||
if _, ok := m.skills[name]; !ok {
|
|
||||||
return fmt.Errorf("skill %s not found", name)
|
|
||||||
}
|
|
||||||
|
|
||||||
skillDir := filepath.Join(m.skillsDir, name)
|
|
||||||
if err := os.RemoveAll(skillDir); err != nil {
|
|
||||||
return fmt.Errorf("remove skill dir: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
delete(m.skills, name)
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *Manager) List() []*Skill {
|
|
||||||
m.mu.RLock()
|
|
||||||
defer m.mu.RUnlock()
|
|
||||||
|
|
||||||
skills := make([]*Skill, 0, len(m.skills))
|
|
||||||
for _, s := range m.skills {
|
|
||||||
skills = append(skills, s)
|
|
||||||
}
|
|
||||||
|
|
||||||
sort.Slice(skills, func(i, j int) bool {
|
|
||||||
return skills[i].Name < skills[j].Name
|
|
||||||
})
|
|
||||||
|
|
||||||
return skills
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *Manager) Get(name string) *Skill {
|
|
||||||
m.mu.RLock()
|
|
||||||
defer m.mu.RUnlock()
|
|
||||||
return m.skills[name]
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *Manager) Toggle(name string, enabled bool) error {
|
|
||||||
m.mu.Lock()
|
|
||||||
defer m.mu.Unlock()
|
|
||||||
|
|
||||||
if _, ok := m.skills[name]; !ok {
|
|
||||||
return fmt.Errorf("skill %s not found", name)
|
|
||||||
}
|
|
||||||
m.skills[name].Enabled = enabled
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *Manager) GetInjectedPrompt() string {
|
|
||||||
m.mu.RLock()
|
|
||||||
defer m.mu.RUnlock()
|
|
||||||
|
|
||||||
var parts []string
|
|
||||||
for _, s := range m.skills {
|
|
||||||
if s.Enabled && s.RawContent != "" {
|
|
||||||
parts = append(parts, fmt.Sprintf("=== Skill: %s ===\n%s", s.Name, s.RawContent))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return strings.Join(parts, "\n\n")
|
|
||||||
}
|
|
||||||
|
|
||||||
// Manager 直接满足内置 SDK 的 SkillAPI(复用优先,无需独立适配器)。
|
|
||||||
var _ sdk.SkillAPI = (*Manager)(nil)
|
|
||||||
|
|
||||||
func extractDescription(content string) string {
|
|
||||||
lines := strings.Split(content, "\n")
|
|
||||||
for _, line := range lines {
|
|
||||||
line = strings.TrimSpace(line)
|
|
||||||
if line != "" && !strings.HasPrefix(line, "#") {
|
|
||||||
return line
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
|
|
||||||
func extractField(content string, field string) string {
|
|
||||||
prefix := fmt.Sprintf("%s:", field)
|
|
||||||
for _, line := range strings.Split(content, "\n") {
|
|
||||||
trimmed := strings.TrimSpace(line)
|
|
||||||
if strings.HasPrefix(strings.ToLower(trimmed), prefix) {
|
|
||||||
return strings.TrimSpace(strings.TrimPrefix(trimmed, prefix))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user