mirror of
https://gitcode.com/JianFeeeee/HomeAgent.git
synced 2026-10-15 12:15:16 +00:00
refactor: rename openclaw -> clawhubadapter, add ClawHub search/install, fix agent interrupt
- Rename internal/plugins/openclaw/ -> internal/plugins/clawhubadapter/ - Add RegistryDispatcher with 5 sub-registries (Tool, Provider, Channel, Stage, Cap) - Add clawhubadapter_search tool for ClawHub marketplace search - Add clawhub: prefix support for installing from ClawHub (ZIP/tgz auto-detect) - Add CallProvider RPC and provider/call routing - Fix QQ interrupt: check interceptCh before/after each tool execution
This commit is contained in:
251
internal/plugins/clawhubadapter/registry.go
Normal file
251
internal/plugins/clawhubadapter/registry.go
Normal file
@ -0,0 +1,251 @@
|
||||
package clawhubadapter
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"log"
|
||||
"sync"
|
||||
|
||||
agentIO "gitcode.com/JianFeeeee/HomeAgent/internal/agent/io"
|
||||
sdk "gitcode.com/JianFeeeee/HomeAgent/internal/sdk"
|
||||
)
|
||||
|
||||
type ToolRegistry struct{}
|
||||
|
||||
func (r *ToolRegistry) Dispatch(data json.RawMessage, pluginName string, sp *sidecarProcess, s *sdk.PluginSDK) {
|
||||
var d struct {
|
||||
Name string `json:"name"`
|
||||
Label string `json:"label"`
|
||||
Description string `json:"description"`
|
||||
Parameters map[string]interface{} `json:"parameters"`
|
||||
}
|
||||
if err := json.Unmarshal(data, &d); err != nil || d.Name == "" {
|
||||
return
|
||||
}
|
||||
toolName := fmt.Sprintf("%s_%s", pluginName, d.Name)
|
||||
tDef := sdk.ToolDef{
|
||||
Name: toolName,
|
||||
Description: d.Description,
|
||||
Parameters: d.Parameters,
|
||||
}
|
||||
handler := func(sp *sidecarProcess, ocToolName string) sdk.ToolHandler {
|
||||
return func(args map[string]interface{}) (interface{}, error) {
|
||||
return sp.CallTool(ocToolName, args)
|
||||
}
|
||||
}(sp, d.Name)
|
||||
if err := s.RegisterTool(toolName, tDef, handler); err != nil {
|
||||
log.Printf("[clawhubadapter] register tool %s: %v", toolName, err)
|
||||
}
|
||||
}
|
||||
|
||||
type providerDef struct {
|
||||
ocType string
|
||||
toolSuffix string
|
||||
desc string
|
||||
}
|
||||
|
||||
var providerMap = []providerDef{
|
||||
{"image_generation", "generate_image", "根据文本描述生成图片,返回图片 URL"},
|
||||
{"music_generation", "generate_music", "根据描述生成音乐"},
|
||||
{"video_generation", "generate_video", "根据描述生成视频"},
|
||||
{"speech", "synthesize_speech", "将文本合成为语音"},
|
||||
{"web_search", "web_search", "搜索互联网信息"},
|
||||
{"web_fetch", "web_fetch", "获取指定网页的内容"},
|
||||
{"media_understanding", "analyze_media", "分析图片、音频或视频内容"},
|
||||
{"realtime_transcription", "transcribe_audio", "将音频转写为文字"},
|
||||
{"realtime_voice", "voice_io", "实时语音输入输出"},
|
||||
}
|
||||
|
||||
type ProviderRegistry struct{}
|
||||
|
||||
func (r *ProviderRegistry) Dispatch(typeStr string, data json.RawMessage, pluginName string, sp *sidecarProcess, s *sdk.PluginSDK) {
|
||||
var d struct {
|
||||
Name string `json:"name"`
|
||||
Description string `json:"description"`
|
||||
}
|
||||
json.Unmarshal(data, &d)
|
||||
|
||||
for _, p := range providerMap {
|
||||
if p.ocType == typeStr {
|
||||
toolName := fmt.Sprintf("%s_%s", pluginName, p.toolSuffix)
|
||||
desc := p.desc
|
||||
if d.Name != "" {
|
||||
desc = fmt.Sprintf("[%s] %s", d.Name, desc)
|
||||
}
|
||||
tDef := sdk.ToolDef{
|
||||
Name: toolName,
|
||||
Description: desc,
|
||||
Parameters: map[string]interface{}{
|
||||
"type": "object",
|
||||
"properties": map[string]interface{}{},
|
||||
},
|
||||
}
|
||||
handler := func(sp *sidecarProcess, ocType string) sdk.ToolHandler {
|
||||
return func(args map[string]interface{}) (interface{}, error) {
|
||||
return sp.CallProvider(ocType, args)
|
||||
}
|
||||
}(sp, typeStr)
|
||||
if err := s.RegisterTool(toolName, tDef, handler); err != nil {
|
||||
log.Printf("[clawhubadapter] register provider tool %s: %v", toolName, err)
|
||||
}
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
log.Printf("[clawhubadapter] unknown provider type: %s (plugin: %s)", typeStr, pluginName)
|
||||
}
|
||||
|
||||
type channelDevice struct {
|
||||
name string
|
||||
pluginName string
|
||||
sp *sidecarProcess
|
||||
ocType string
|
||||
}
|
||||
|
||||
func (d *channelDevice) Name() string { return d.name }
|
||||
func (d *channelDevice) Type() agentIO.DeviceType { return agentIO.DeviceIO }
|
||||
func (d *channelDevice) Description() string { return fmt.Sprintf("OC channel %s (from %s)", d.name, d.pluginName) }
|
||||
func (d *channelDevice) OutputCapabilities() agentIO.OutputCapability { return ocTypeToCap(d.ocType) }
|
||||
func (d *channelDevice) Start() error { return nil }
|
||||
func (d *channelDevice) Stop() error { return nil }
|
||||
func (d *channelDevice) Tools() []agentIO.ToolDef { return nil }
|
||||
func (d *channelDevice) Execute(tool string, args map[string]interface{}) (interface{}, error) {
|
||||
return d.sp.CallTool(tool, args)
|
||||
}
|
||||
|
||||
func ocTypeToCap(t string) agentIO.OutputCapability {
|
||||
switch t {
|
||||
case "text":
|
||||
return agentIO.CapText
|
||||
case "file":
|
||||
return agentIO.CapFile
|
||||
case "image":
|
||||
return agentIO.CapImage
|
||||
case "audio":
|
||||
return agentIO.CapAudio
|
||||
default:
|
||||
return agentIO.CapText
|
||||
}
|
||||
}
|
||||
|
||||
type ChannelRegistry struct{}
|
||||
|
||||
func (r *ChannelRegistry) Dispatch(data json.RawMessage, pluginName string, sp *sidecarProcess, s *sdk.PluginSDK) {
|
||||
var d struct {
|
||||
Name string `json:"name"`
|
||||
Type string `json:"type"`
|
||||
}
|
||||
if err := json.Unmarshal(data, &d); err != nil || d.Name == "" {
|
||||
return
|
||||
}
|
||||
dev := &channelDevice{
|
||||
name: d.Name,
|
||||
pluginName: pluginName,
|
||||
sp: sp,
|
||||
ocType: d.Type,
|
||||
}
|
||||
if err := s.RegisterChannel(d.Name, dev); err != nil {
|
||||
log.Printf("[clawhubadapter] register channel %s: %v", d.Name, err)
|
||||
}
|
||||
}
|
||||
|
||||
type StageRegistry struct{}
|
||||
|
||||
func (r *StageRegistry) Dispatch(data json.RawMessage, pluginName string, sp *sidecarProcess, s *sdk.PluginSDK) {
|
||||
var d struct {
|
||||
Name string `json:"name"`
|
||||
Event string `json:"event"`
|
||||
}
|
||||
json.Unmarshal(data, &d)
|
||||
log.Printf("[clawhubadapter] hook %s/%s (plugin: %s) — stub: OC hooks need bidirectional bridge",
|
||||
d.Name, d.Event, pluginName)
|
||||
}
|
||||
|
||||
type CapRecorder struct {
|
||||
mu sync.Mutex
|
||||
caps []string
|
||||
}
|
||||
|
||||
func (r *CapRecorder) Record(typeStr string, data json.RawMessage, pluginName string) {
|
||||
var d struct {
|
||||
Name string `json:"name"`
|
||||
Description string `json:"description"`
|
||||
}
|
||||
json.Unmarshal(data, &d)
|
||||
capStr := fmt.Sprintf("[%s] capability: %s", pluginName, typeStr)
|
||||
if d.Name != "" {
|
||||
capStr += " (" + d.Name + ")"
|
||||
}
|
||||
if d.Description != "" {
|
||||
capStr += ": " + d.Description
|
||||
}
|
||||
r.mu.Lock()
|
||||
r.caps = append(r.caps, capStr)
|
||||
r.mu.Unlock()
|
||||
}
|
||||
|
||||
func (r *CapRecorder) Snapshot() []string {
|
||||
r.mu.Lock()
|
||||
defer r.mu.Unlock()
|
||||
out := make([]string, len(r.caps))
|
||||
copy(out, r.caps)
|
||||
return out
|
||||
}
|
||||
|
||||
type RegistryDispatcher struct {
|
||||
toolReg *ToolRegistry
|
||||
providerReg *ProviderRegistry
|
||||
channelReg *ChannelRegistry
|
||||
stageReg *StageRegistry
|
||||
capRecorder *CapRecorder
|
||||
}
|
||||
|
||||
func NewDispatcher() *RegistryDispatcher {
|
||||
return &RegistryDispatcher{
|
||||
toolReg: &ToolRegistry{},
|
||||
providerReg: &ProviderRegistry{},
|
||||
channelReg: &ChannelRegistry{},
|
||||
stageReg: &StageRegistry{},
|
||||
capRecorder: &CapRecorder{},
|
||||
}
|
||||
}
|
||||
|
||||
func (d *RegistryDispatcher) Dispatch(typeStr string, data json.RawMessage, pluginName string, sp *sidecarProcess, s *sdk.PluginSDK) {
|
||||
switch typeStr {
|
||||
case "tool":
|
||||
d.toolReg.Dispatch(data, pluginName, sp, s)
|
||||
case "channel":
|
||||
d.channelReg.Dispatch(data, pluginName, sp, s)
|
||||
case "provider", "image_generation_provider", "music_generation_provider",
|
||||
"video_generation_provider", "speech_provider",
|
||||
"realtime_transcription_provider", "realtime_voice_provider",
|
||||
"media_understanding_provider",
|
||||
"web_fetch_provider", "web_search_provider",
|
||||
"embedding_provider", "memory_embedding_provider":
|
||||
d.providerReg.Dispatch(typeStr, data, pluginName, sp, s)
|
||||
d.capRecorder.Record(typeStr, data, pluginName)
|
||||
case "hook", "runtime_lifecycle", "lifecycle",
|
||||
"agent_event_subscription", "agent_harness",
|
||||
"session_event", "conversation_binding_resolved",
|
||||
"interactive_handler",
|
||||
"cli", "cli_backend", "node_cli_feature",
|
||||
"command", "http_route", "service",
|
||||
"gateway_method", "gateway_discovery_service",
|
||||
"trusted_tool_policy", "tool_metadata",
|
||||
"context_engine",
|
||||
"memory_capability", "memory_prompt_section",
|
||||
"memory_flush_plan", "memory_runtime",
|
||||
"memory_prompt_supplement", "memory_corpus_supplement",
|
||||
"session_extension", "session_scheduler_job",
|
||||
"session_action", "control_ui_descriptor",
|
||||
"agent_tool_result_middleware":
|
||||
d.stageReg.Dispatch(data, pluginName, sp, s)
|
||||
d.capRecorder.Record(typeStr, data, pluginName)
|
||||
default:
|
||||
d.capRecorder.Record(typeStr, data, pluginName)
|
||||
}
|
||||
}
|
||||
|
||||
func (d *RegistryDispatcher) Capabilities() []string {
|
||||
return d.capRecorder.Snapshot()
|
||||
}
|
||||
Reference in New Issue
Block a user