fix(agent): use routed agent for model commands, restore Telegram command diff
- Remove modelMu: message processing is serial, no concurrent writes - Pass routed agent to handleCommand/buildCommandsRuntime instead of always using default agent - GetModelInfo/SwitchModel are nil when agent is nil (route failed), handlers reply "unavailable" - Restore GetMyCommands + slices.Equal check before SetMyCommands to avoid unnecessary Telegram API calls on restart Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
c87533eb7a
commit
75f1b58ca0
2 changed files with 31 additions and 27 deletions
|
|
@ -48,7 +48,6 @@ type AgentLoop struct {
|
||||||
mediaStore media.MediaStore
|
mediaStore media.MediaStore
|
||||||
transcriber voice.Transcriber
|
transcriber voice.Transcriber
|
||||||
cmdRegistry *commands.Registry
|
cmdRegistry *commands.Registry
|
||||||
modelMu sync.Mutex // protects AgentInstance.Model writes in SwitchModel
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// processOptions configures how a message is processed
|
// processOptions configures how a message is processed
|
||||||
|
|
@ -538,7 +537,7 @@ func (al *AgentLoop) processMessage(ctx context.Context, msg bus.InboundMessage)
|
||||||
// Global commands (/help, /show, /switch) work even when routing fails;
|
// Global commands (/help, /show, /switch) work even when routing fails;
|
||||||
// context-dependent commands check their own Runtime fields and report
|
// context-dependent commands check their own Runtime fields and report
|
||||||
// "unavailable" when the required capability is nil.
|
// "unavailable" when the required capability is nil.
|
||||||
if response, handled := al.handleCommand(ctx, msg); handled {
|
if response, handled := al.handleCommand(ctx, msg, agent); handled {
|
||||||
return response, nil
|
return response, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1466,6 +1465,7 @@ func (al *AgentLoop) estimateTokens(messages []providers.Message) int {
|
||||||
func (al *AgentLoop) handleCommand(
|
func (al *AgentLoop) handleCommand(
|
||||||
ctx context.Context,
|
ctx context.Context,
|
||||||
msg bus.InboundMessage,
|
msg bus.InboundMessage,
|
||||||
|
agent *AgentInstance,
|
||||||
) (string, bool) {
|
) (string, bool) {
|
||||||
if !commands.HasCommandPrefix(msg.Content) {
|
if !commands.HasCommandPrefix(msg.Content) {
|
||||||
return "", false
|
return "", false
|
||||||
|
|
@ -1475,7 +1475,7 @@ func (al *AgentLoop) handleCommand(
|
||||||
return "", false
|
return "", false
|
||||||
}
|
}
|
||||||
|
|
||||||
rt := al.buildCommandsRuntime()
|
rt := al.buildCommandsRuntime(agent)
|
||||||
executor := commands.NewExecutor(al.cmdRegistry, rt)
|
executor := commands.NewExecutor(al.cmdRegistry, rt)
|
||||||
|
|
||||||
var commandReply string
|
var commandReply string
|
||||||
|
|
@ -1504,16 +1504,9 @@ func (al *AgentLoop) handleCommand(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (al *AgentLoop) buildCommandsRuntime() *commands.Runtime {
|
func (al *AgentLoop) buildCommandsRuntime(agent *AgentInstance) *commands.Runtime {
|
||||||
return &commands.Runtime{
|
rt := &commands.Runtime{
|
||||||
Config: al.cfg,
|
Config: al.cfg,
|
||||||
GetModelInfo: func() (string, string) {
|
|
||||||
agent := al.registry.GetDefaultAgent()
|
|
||||||
if agent == nil {
|
|
||||||
return al.cfg.Agents.Defaults.GetModelName(), al.cfg.Agents.Defaults.Provider
|
|
||||||
}
|
|
||||||
return agent.Model, al.cfg.Agents.Defaults.Provider
|
|
||||||
},
|
|
||||||
ListAgentIDs: al.registry.ListAgentIDs,
|
ListAgentIDs: al.registry.ListAgentIDs,
|
||||||
ListDefinitions: al.cmdRegistry.Definitions,
|
ListDefinitions: al.cmdRegistry.Definitions,
|
||||||
GetEnabledChannels: func() []string {
|
GetEnabledChannels: func() []string {
|
||||||
|
|
@ -1522,20 +1515,6 @@ func (al *AgentLoop) buildCommandsRuntime() *commands.Runtime {
|
||||||
}
|
}
|
||||||
return al.channelManager.GetEnabledChannels()
|
return al.channelManager.GetEnabledChannels()
|
||||||
},
|
},
|
||||||
SwitchModel: func(value string) (string, error) {
|
|
||||||
al.modelMu.Lock()
|
|
||||||
defer al.modelMu.Unlock()
|
|
||||||
defaultAgent := al.registry.GetDefaultAgent()
|
|
||||||
if defaultAgent == nil {
|
|
||||||
return "", fmt.Errorf("no default agent configured")
|
|
||||||
}
|
|
||||||
oldModel := defaultAgent.Model
|
|
||||||
defaultAgent.Model = value
|
|
||||||
if al.cfg != nil {
|
|
||||||
al.cfg.Agents.Defaults.ModelName = value
|
|
||||||
}
|
|
||||||
return oldModel, nil
|
|
||||||
},
|
|
||||||
SwitchChannel: func(value string) error {
|
SwitchChannel: func(value string) error {
|
||||||
if al.channelManager == nil {
|
if al.channelManager == nil {
|
||||||
return fmt.Errorf("channel manager not initialized")
|
return fmt.Errorf("channel manager not initialized")
|
||||||
|
|
@ -1546,6 +1525,20 @@ func (al *AgentLoop) buildCommandsRuntime() *commands.Runtime {
|
||||||
return nil
|
return nil
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
if agent != nil {
|
||||||
|
rt.GetModelInfo = func() (string, string) {
|
||||||
|
return agent.Model, al.cfg.Agents.Defaults.Provider
|
||||||
|
}
|
||||||
|
rt.SwitchModel = func(value string) (string, error) {
|
||||||
|
oldModel := agent.Model
|
||||||
|
agent.Model = value
|
||||||
|
if al.cfg != nil {
|
||||||
|
al.cfg.Agents.Defaults.ModelName = value
|
||||||
|
}
|
||||||
|
return oldModel, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return rt
|
||||||
}
|
}
|
||||||
|
|
||||||
func mapCommandError(result commands.ExecuteResult) string {
|
func mapCommandError(result commands.ExecuteResult) string {
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@ package telegram
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"math/rand"
|
"math/rand"
|
||||||
|
"slices"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/mymmrac/telego"
|
"github.com/mymmrac/telego"
|
||||||
|
|
@ -41,6 +42,16 @@ func (c *TelegramChannel) RegisterCommands(ctx context.Context, defs []commands.
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
current, err := c.bot.GetMyCommands(ctx, &telego.GetMyCommandsParams{})
|
||||||
|
if err != nil {
|
||||||
|
// If we can't read current commands, fall through to set them.
|
||||||
|
logger.WarnCF("telegram", "Failed to get current commands, will set unconditionally",
|
||||||
|
map[string]any{"error": err.Error()})
|
||||||
|
} else if slices.Equal(current, botCommands) {
|
||||||
|
logger.DebugCF("telegram", "Bot commands are up to date", nil)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
return c.bot.SetMyCommands(ctx, &telego.SetMyCommandsParams{
|
return c.bot.SetMyCommands(ctx, &telego.SetMyCommandsParams{
|
||||||
Commands: botCommands,
|
Commands: botCommands,
|
||||||
})
|
})
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue