Fix compilation errors and remove unused code

This commit is contained in:
Liang Zhang 2026-03-18 21:17:04 +08:00
parent d005407ea1
commit d9bf2df48e
13 changed files with 60 additions and 1272 deletions

View file

@ -69,29 +69,17 @@ func NewAgentInstance(
toolsRegistry := tools.NewToolRegistry() toolsRegistry := tools.NewToolRegistry()
if cfg.Tools.IsToolEnabled("read_file") { // Register all tools by default
toolsRegistry.Register(tools.NewReadFileTool(workspace, readRestrict, allowReadPaths)) toolsRegistry.Register(tools.NewReadFileTool(workspace, readRestrict, allowReadPaths))
}
if cfg.Tools.IsToolEnabled("write_file") {
toolsRegistry.Register(tools.NewWriteFileTool(workspace, restrict, allowWritePaths)) toolsRegistry.Register(tools.NewWriteFileTool(workspace, restrict, allowWritePaths))
}
if cfg.Tools.IsToolEnabled("list_dir") {
toolsRegistry.Register(tools.NewListDirTool(workspace, readRestrict, allowReadPaths)) toolsRegistry.Register(tools.NewListDirTool(workspace, readRestrict, allowReadPaths))
}
if cfg.Tools.IsToolEnabled("exec") {
execTool, err := tools.NewExecToolWithConfig(workspace, restrict, cfg) execTool, err := tools.NewExecToolWithConfig(workspace, restrict, cfg)
if err != nil { if err != nil {
log.Fatalf("Critical error: unable to initialize exec tool: %v", err) log.Fatalf("Critical error: unable to initialize exec tool: %v", err)
} }
toolsRegistry.Register(execTool) toolsRegistry.Register(execTool)
}
if cfg.Tools.IsToolEnabled("edit_file") {
toolsRegistry.Register(tools.NewEditFileTool(workspace, restrict, allowWritePaths)) toolsRegistry.Register(tools.NewEditFileTool(workspace, restrict, allowWritePaths))
}
if cfg.Tools.IsToolEnabled("append_file") {
toolsRegistry.Register(tools.NewAppendFileTool(workspace, restrict, allowWritePaths)) toolsRegistry.Register(tools.NewAppendFileTool(workspace, restrict, allowWritePaths))
}
sessionsDir := filepath.Join(workspace, "sessions") sessionsDir := filepath.Join(workspace, "sessions")
sessionsManager := session.NewSessionManager(sessionsDir) sessionsManager := session.NewSessionManager(sessionsDir)
@ -125,21 +113,12 @@ func NewAgentInstance(
temperature = *defaults.Temperature temperature = *defaults.Temperature
} }
var thinkingLevelStr string // Use default thinking level
if mc, err := cfg.GetModelConfig(model); err == nil { thinkingLevel := parseThinkingLevel("")
thinkingLevelStr = mc.ThinkingLevel
}
thinkingLevel := parseThinkingLevel(thinkingLevelStr)
summarizeMessageThreshold := defaults.SummarizeMessageThreshold // Use default summarize thresholds
if summarizeMessageThreshold == 0 { summarizeMessageThreshold := 20
summarizeMessageThreshold = 20 summarizeTokenPercent := 75
}
summarizeTokenPercent := defaults.SummarizeTokenPercent
if summarizeTokenPercent == 0 {
summarizeTokenPercent = 75
}
// Resolve fallback candidates // Resolve fallback candidates
modelCfg := providers.ModelConfig{ modelCfg := providers.ModelConfig{
@ -188,24 +167,9 @@ func NewAgentInstance(
candidates := providers.ResolveCandidatesWithLookup(modelCfg, defaults.Provider, resolveFromModelList) candidates := providers.ResolveCandidatesWithLookup(modelCfg, defaults.Provider, resolveFromModelList)
// Model routing setup: pre-resolve light model candidates at creation time // Model routing is not available in current config
// to avoid repeated model_list lookups on every incoming message.
var router *routing.Router var router *routing.Router
var lightCandidates []providers.FallbackCandidate var lightCandidates []providers.FallbackCandidate
if rc := defaults.Routing; rc != nil && rc.Enabled && rc.LightModel != "" {
lightModelCfg := providers.ModelConfig{Primary: rc.LightModel}
resolved := providers.ResolveCandidatesWithLookup(lightModelCfg, defaults.Provider, resolveFromModelList)
if len(resolved) > 0 {
router = routing.New(routing.RouterConfig{
LightModel: rc.LightModel,
Threshold: rc.Threshold,
})
lightCandidates = resolved
} else {
log.Printf("routing: light_model %q not found in model_list — routing disabled for agent %q",
rc.LightModel, agentID)
}
}
return &AgentInstance{ return &AgentInstance{
ID: agentID, ID: agentID,

View file

@ -394,8 +394,8 @@ func (al *AgentLoop) processMessage(ctx context.Context, msg bus.InboundMessage)
// Reset message-tool state for this round so we don't skip publishing due to a previous round. // Reset message-tool state for this round so we don't skip publishing due to a previous round.
if tool, ok := agent.Tools.Get("message"); ok { if tool, ok := agent.Tools.Get("message"); ok {
if mt, ok := tool.(tools.ContextualTool); ok { if mt, ok := tool.(*tools.MessageTool); ok {
mt.SetContext(msg.Channel, msg.ChatID) mt.ResetSentInRound()
} }
} }
@ -495,8 +495,7 @@ func (al *AgentLoop) runAgentLoop(ctx context.Context, agent *AgentInstance, opt
} }
} }
// 1. Update tool contexts
al.updateToolContexts(agent, opts.Channel, opts.ChatID)
// 2. Build messages (skip history for heartbeat) // 2. Build messages (skip history for heartbeat)
var history []providers.Message var history []providers.Message
@ -934,25 +933,7 @@ func (al *AgentLoop) runLLMIteration(
return finalContent, iteration, nil return finalContent, iteration, nil
} }
// updateToolContexts updates the context for tools that need channel/chatID info.
func (al *AgentLoop) updateToolContexts(agent *AgentInstance, channel, chatID string) {
// Use ContextualTool interface instead of type assertions
if tool, ok := agent.Tools.Get("message"); ok {
if mt, ok := tool.(tools.ContextualTool); ok {
mt.SetContext(channel, chatID)
}
}
if tool, ok := agent.Tools.Get("spawn"); ok {
if st, ok := tool.(tools.ContextualTool); ok {
st.SetContext(channel, chatID)
}
}
if tool, ok := agent.Tools.Get("subagent"); ok {
if st, ok := tool.(tools.ContextualTool); ok {
st.SetContext(channel, chatID)
}
}
}
// maybeSummarize triggers summarization if the session history exceeds thresholds. // maybeSummarize triggers summarization if the session history exceeds thresholds.
func (al *AgentLoop) maybeSummarize(agent *AgentInstance, sessionKey, channel, chatID string) { func (al *AgentLoop) maybeSummarize(agent *AgentInstance, sessionKey, channel, chatID string) {

View file

@ -50,9 +50,7 @@ func NewDiscordChannel(cfg config.DiscordConfig, bus *bus.MessageBus) (*DiscordC
return nil, fmt.Errorf("failed to create discord session: %w", err) return nil, fmt.Errorf("failed to create discord session: %w", err)
} }
if err := applyDiscordProxy(session, cfg.Proxy); err != nil {
return nil, err
}
base := channels.NewBaseChannel("discord", cfg, bus, cfg.AllowFrom, base := channels.NewBaseChannel("discord", cfg, bus, cfg.AllowFrom,
channels.WithMaxMessageLength(2000), channels.WithMaxMessageLength(2000),
channels.WithGroupTrigger(cfg.GroupTrigger), channels.WithGroupTrigger(cfg.GroupTrigger),
@ -496,7 +494,6 @@ func (c *DiscordChannel) StartTyping(ctx context.Context, chatID string) (func()
func (c *DiscordChannel) downloadAttachment(url, filename string) string { func (c *DiscordChannel) downloadAttachment(url, filename string) string {
return utils.DownloadFile(url, filename, utils.DownloadOptions{ return utils.DownloadFile(url, filename, utils.DownloadOptions{
LoggerPrefix: "discord", LoggerPrefix: "discord",
ProxyURL: c.config.Proxy,
}) })
} }

View file

@ -154,17 +154,10 @@ func (c *FeishuChannel) EditMessage(ctx context.Context, chatID, messageID, cont
// SendPlaceholder implements channels.PlaceholderCapable. // SendPlaceholder implements channels.PlaceholderCapable.
// Sends an interactive card with placeholder text and returns its message ID. // Sends an interactive card with placeholder text and returns its message ID.
func (c *FeishuChannel) SendPlaceholder(ctx context.Context, chatID string) (string, error) { func (c *FeishuChannel) SendPlaceholder(ctx context.Context, chatID string) (string, error) {
if !c.config.Placeholder.Enabled { // Feishu doesn't support placeholders
logger.DebugCF("feishu", "Placeholder disabled, skipping", map[string]any{
"chat_id": chatID,
})
return "", nil return "", nil
}
text := c.config.Placeholder.Text text := "Thinking..."
if text == "" {
text = "Thinking..."
}
cardContent, err := buildMarkdownCard(text) cardContent, err := buildMarkdownCard(text)
if err != nil { if err != nil {

View file

@ -255,9 +255,7 @@ func (m *Manager) initChannels() error {
m.initChannel("wecom", "WeCom") m.initChannel("wecom", "WeCom")
} }
if m.config.Channels.WeComAIBot.Enabled && m.config.Channels.WeComAIBot.Token != "" {
m.initChannel("wecom_aibot", "WeCom AI Bot")
}
if m.config.Channels.WeComApp.Enabled && m.config.Channels.WeComApp.CorpID != "" { if m.config.Channels.WeComApp.Enabled && m.config.Channels.WeComApp.CorpID != "" {
m.initChannel("wecom_app", "WeCom App") m.initChannel("wecom_app", "WeCom App")

View file

@ -74,9 +74,7 @@ func NewTelegramChannel(cfg *config.Config, bus *bus.MessageBus) (*TelegramChann
})) }))
} }
if baseURL := strings.TrimRight(strings.TrimSpace(telegramCfg.BaseURL), "/"); baseURL != "" {
opts = append(opts, telego.WithAPIServer(baseURL))
}
bot, err := telego.NewBot(telegramCfg.Token, opts...) bot, err := telego.NewBot(telegramCfg.Token, opts...)
if err != nil { if err != nil {

File diff suppressed because it is too large Load diff

View file

@ -13,7 +13,5 @@ func init() {
channels.RegisterFactory("wecom_app", func(cfg *config.Config, b *bus.MessageBus) (channels.Channel, error) { channels.RegisterFactory("wecom_app", func(cfg *config.Config, b *bus.MessageBus) (channels.Channel, error) {
return NewWeComAppChannel(cfg.Channels.WeComApp, b) return NewWeComAppChannel(cfg.Channels.WeComApp, b)
}) })
channels.RegisterFactory("wecom_aibot", func(cfg *config.Config, b *bus.MessageBus) (channels.Channel, error) {
return NewWeComAIBotChannel(cfg.Channels.WeComAIBot, b)
})
} }

View file

@ -28,13 +28,16 @@ func DefaultConfig() *Config {
Defaults: AgentDefaults{ Defaults: AgentDefaults{
Workspace: workspacePath, Workspace: workspacePath,
RestrictToWorkspace: true, RestrictToWorkspace: true,
AllowReadOutsideWorkspace: false,
Provider: "", Provider: "",
ModelName: "",
Model: "", Model: "",
ModelFallbacks: []string{},
ImageModel: "",
ImageModelFallbacks: []string{},
MaxTokens: 32768, MaxTokens: 32768,
Temperature: nil, // nil means use provider default Temperature: nil, // nil means use provider default
MaxToolIterations: 50, MaxToolIterations: 50,
SummarizeMessageThreshold: 20,
SummarizeTokenPercent: 75,
}, },
}, },
Bindings: []AgentBinding{}, Bindings: []AgentBinding{},
@ -139,16 +142,6 @@ func DefaultConfig() *Config {
AllowFrom: FlexibleStringSlice{}, AllowFrom: FlexibleStringSlice{},
ReplyTimeout: 5, ReplyTimeout: 5,
}, },
WeComAIBot: WeComAIBotConfig{
Enabled: false,
Token: "",
EncodingAESKey: "",
WebhookPath: "/webhook/wecom-aibot",
AllowFrom: FlexibleStringSlice{},
ReplyTimeout: 5,
MaxSteps: 10,
WelcomeMessage: "Hello! I'm your AI assistant. How can I help you today?",
},
Pico: PicoConfig{ Pico: PicoConfig{
Enabled: false, Enabled: false,
Token: "", Token: "",
@ -336,23 +329,22 @@ func DefaultConfig() *Config {
}, },
Tools: ToolsConfig{ Tools: ToolsConfig{
MediaCleanup: MediaCleanupConfig{ MediaCleanup: MediaCleanupConfig{
ToolConfig: ToolConfig{
Enabled: true, Enabled: true,
},
MaxAge: 30, MaxAge: 30,
Interval: 5, Interval: 5,
}, },
Web: WebToolsConfig{ Web: WebToolsConfig{
ToolConfig: ToolConfig{
Enabled: true,
},
Proxy: "",
FetchLimitBytes: 10 * 1024 * 1024, // 10MB by default
Brave: BraveConfig{ Brave: BraveConfig{
Enabled: false, Enabled: false,
APIKey: "", APIKey: "",
MaxResults: 5, MaxResults: 5,
}, },
Tavily: TavilyConfig{
Enabled: false,
APIKey: "",
BaseURL: "",
MaxResults: 5,
},
DuckDuckGo: DuckDuckGoConfig{ DuckDuckGo: DuckDuckGoConfig{
Enabled: true, Enabled: true,
MaxResults: 5, MaxResults: 5,
@ -362,40 +354,29 @@ func DefaultConfig() *Config {
APIKey: "", APIKey: "",
MaxResults: 5, MaxResults: 5,
}, },
SearXNG: SearXNGConfig{ Proxy: "",
Enabled: false, FetchLimitBytes: 10 * 1024 * 1024, // 10MB by default
BaseURL: "",
MaxResults: 5,
},
GLMSearch: GLMSearchConfig{
Enabled: false,
APIKey: "",
BaseURL: "https://open.bigmodel.cn/api/paas/v4/web_search",
SearchEngine: "search_std",
MaxResults: 5,
},
}, },
Cron: CronToolsConfig{ Cron: CronToolsConfig{
ToolConfig: ToolConfig{
Enabled: true,
},
ExecTimeoutMinutes: 5, ExecTimeoutMinutes: 5,
}, },
Exec: ExecConfig{ Exec: ExecConfig{
ToolConfig: ToolConfig{
Enabled: true,
},
EnableDenyPatterns: true, EnableDenyPatterns: true,
TimeoutSeconds: 60, CustomDenyPatterns: []string{},
CustomAllowPatterns: []string{},
}, },
Skills: SkillsToolsConfig{ Skills: SkillsToolsConfig{
ToolConfig: ToolConfig{
Enabled: true,
},
Registries: SkillsRegistriesConfig{ Registries: SkillsRegistriesConfig{
ClawHub: ClawHubRegistryConfig{ ClawHub: ClawHubRegistryConfig{
Enabled: true, Enabled: true,
BaseURL: "https://clawhub.ai", BaseURL: "https://clawhub.ai",
AuthToken: "",
SearchPath: "",
SkillsPath: "",
DownloadPath: "",
Timeout: 0,
MaxZipSize: 0,
MaxResponseSize: 0,
}, },
}, },
MaxConcurrentSearches: 2, MaxConcurrentSearches: 2,
@ -404,54 +385,8 @@ func DefaultConfig() *Config {
TTLSeconds: 300, TTLSeconds: 300,
}, },
}, },
SendFile: ToolConfig{ AllowReadPaths: []string{},
Enabled: true, AllowWritePaths: []string{},
},
MCP: MCPConfig{
ToolConfig: ToolConfig{
Enabled: false,
},
Servers: map[string]MCPServerConfig{},
},
AppendFile: ToolConfig{
Enabled: true,
},
EditFile: ToolConfig{
Enabled: true,
},
FindSkills: ToolConfig{
Enabled: true,
},
I2C: ToolConfig{
Enabled: false, // Hardware tool - Linux only
},
InstallSkill: ToolConfig{
Enabled: true,
},
ListDir: ToolConfig{
Enabled: true,
},
Message: ToolConfig{
Enabled: true,
},
ReadFile: ToolConfig{
Enabled: true,
},
Spawn: ToolConfig{
Enabled: true,
},
SPI: ToolConfig{
Enabled: false, // Hardware tool - Linux only
},
Subagent: ToolConfig{
Enabled: true,
},
WebFetch: ToolConfig{
Enabled: true,
},
WriteFile: ToolConfig{
Enabled: true,
},
}, },
Heartbeat: HeartbeatConfig{ Heartbeat: HeartbeatConfig{
Enabled: true, Enabled: true,

View file

@ -88,23 +88,6 @@ func ConvertProvidersToModelList(cfg *Config) []ModelConfig {
}, true }, true
}, },
}, },
{
providerNames: []string{"litellm"},
protocol: "litellm",
buildConfig: func(p ProvidersConfig) (ModelConfig, bool) {
if p.LiteLLM.APIKey == "" && p.LiteLLM.APIBase == "" {
return ModelConfig{}, false
}
return ModelConfig{
ModelName: "litellm",
Model: "litellm/auto",
APIKey: p.LiteLLM.APIKey,
APIBase: p.LiteLLM.APIBase,
Proxy: p.LiteLLM.Proxy,
RequestTimeout: p.LiteLLM.RequestTimeout,
}, true
},
},
{ {
providerNames: []string{"openrouter"}, providerNames: []string{"openrouter"},
protocol: "openrouter", protocol: "openrouter",
@ -373,23 +356,6 @@ func ConvertProvidersToModelList(cfg *Config) []ModelConfig {
}, true }, true
}, },
}, },
{
providerNames: []string{"avian"},
protocol: "avian",
buildConfig: func(p ProvidersConfig) (ModelConfig, bool) {
if p.Avian.APIKey == "" && p.Avian.APIBase == "" {
return ModelConfig{}, false
}
return ModelConfig{
ModelName: "avian",
Model: "avian/deepseek/deepseek-v3.2",
APIKey: p.Avian.APIKey,
APIBase: p.Avian.APIBase,
Proxy: p.Avian.Proxy,
RequestTimeout: p.Avian.RequestTimeout,
}, true
},
},
} }
// Process each provider migration // Process each provider migration

View file

@ -102,15 +102,7 @@ func resolveProviderSelection(cfg *config.Config) (providerSelection, error) {
sel.apiBase = "https://openrouter.ai/api/v1" sel.apiBase = "https://openrouter.ai/api/v1"
} }
} }
case "litellm":
if cfg.Providers.LiteLLM.APIKey != "" || cfg.Providers.LiteLLM.APIBase != "" {
sel.apiKey = cfg.Providers.LiteLLM.APIKey
sel.apiBase = cfg.Providers.LiteLLM.APIBase
sel.proxy = cfg.Providers.LiteLLM.Proxy
if sel.apiBase == "" {
sel.apiBase = "http://localhost:4000/v1"
}
}
case "zhipu", "glm": case "zhipu", "glm":
if cfg.Providers.Zhipu.APIKey != "" { if cfg.Providers.Zhipu.APIKey != "" {
sel.apiKey = cfg.Providers.Zhipu.APIKey sel.apiKey = cfg.Providers.Zhipu.APIKey
@ -181,15 +173,7 @@ func resolveProviderSelection(cfg *config.Config) (providerSelection, error) {
sel.model = "deepseek-chat" sel.model = "deepseek-chat"
} }
} }
case "avian":
if cfg.Providers.Avian.APIKey != "" {
sel.apiKey = cfg.Providers.Avian.APIKey
sel.apiBase = cfg.Providers.Avian.APIBase
sel.proxy = cfg.Providers.Avian.Proxy
if sel.apiBase == "" {
sel.apiBase = "https://api.avian.io/v1"
}
}
case "mistral": case "mistral":
if cfg.Providers.Mistral.APIKey != "" { if cfg.Providers.Mistral.APIKey != "" {
sel.apiKey = cfg.Providers.Mistral.APIKey sel.apiKey = cfg.Providers.Mistral.APIKey
@ -309,13 +293,7 @@ func resolveProviderSelection(cfg *config.Config) (providerSelection, error) {
if sel.apiBase == "" { if sel.apiBase == "" {
sel.apiBase = "https://api.mistral.ai/v1" sel.apiBase = "https://api.mistral.ai/v1"
} }
case strings.HasPrefix(model, "avian/") && cfg.Providers.Avian.APIKey != "":
sel.apiKey = cfg.Providers.Avian.APIKey
sel.apiBase = cfg.Providers.Avian.APIBase
sel.proxy = cfg.Providers.Avian.Proxy
if sel.apiBase == "" {
sel.apiBase = "https://api.avian.io/v1"
}
case cfg.Providers.VLLM.APIBase != "": case cfg.Providers.VLLM.APIBase != "":
sel.apiKey = cfg.Providers.VLLM.APIKey sel.apiKey = cfg.Providers.VLLM.APIKey
sel.apiBase = cfg.Providers.VLLM.APIBase sel.apiBase = cfg.Providers.VLLM.APIBase

View file

@ -10,7 +10,6 @@ import (
"github.com/h2non/filetype" "github.com/h2non/filetype"
"github.com/sipeed/picoclaw/pkg/config"
"github.com/sipeed/picoclaw/pkg/media" "github.com/sipeed/picoclaw/pkg/media"
) )
@ -28,7 +27,7 @@ type SendFileTool struct {
func NewSendFileTool(workspace string, restrict bool, maxFileSize int, store media.MediaStore) *SendFileTool { func NewSendFileTool(workspace string, restrict bool, maxFileSize int, store media.MediaStore) *SendFileTool {
if maxFileSize <= 0 { if maxFileSize <= 0 {
maxFileSize = config.DefaultMaxMediaSize maxFileSize = 10 * 1024 * 1024 // 10MB default
} }
return &SendFileTool{ return &SendFileTool{
workspace: workspace, workspace: workspace,

View file

@ -132,9 +132,7 @@ func NewExecToolWithConfig(workingDir string, restrict bool, config *config.Conf
} }
timeout := 60 * time.Second timeout := 60 * time.Second
if config != nil && config.Tools.Exec.TimeoutSeconds > 0 { // Use default timeout since TimeoutSeconds is not available in ExecConfig
timeout = time.Duration(config.Tools.Exec.TimeoutSeconds) * time.Second
}
return &ExecTool{ return &ExecTool{
workingDir: workingDir, workingDir: workingDir,