fix(agent): preserve /btw immediate reply metadata
This commit is contained in:
parent
0cb5bdee0d
commit
537cc35589
2 changed files with 61 additions and 21 deletions
|
|
@ -654,19 +654,9 @@ func (al *AgentLoop) drainBusToSteering(ctx context.Context, activeScope, active
|
||||||
msg, _ = al.transcribeAudioInMessage(ctx, msg)
|
msg, _ = al.transcribeAudioInMessage(ctx, msg)
|
||||||
|
|
||||||
// Handle priority commands (e.g. /btw) immediately instead of queueing them.
|
// Handle priority commands (e.g. /btw) immediately instead of queueing them.
|
||||||
if handled, response := al.tryHandlePriorityCommand(ctx, msg); handled {
|
if handled, outbound := al.tryHandlePriorityCommand(ctx, msg); handled {
|
||||||
if response != "" {
|
if outbound.Content != "" {
|
||||||
al.bus.PublishOutbound(ctx, bus.OutboundMessage{
|
al.bus.PublishOutbound(ctx, outbound)
|
||||||
Channel: msg.Channel,
|
|
||||||
ChatID: msg.ChatID,
|
|
||||||
Context: outboundContextFromInbound(
|
|
||||||
&msg.Context,
|
|
||||||
msg.Channel,
|
|
||||||
msg.ChatID,
|
|
||||||
msg.Context.ReplyToMessageID,
|
|
||||||
),
|
|
||||||
Content: response,
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
@ -1586,7 +1576,7 @@ func (al *AgentLoop) askSideQuestion(
|
||||||
}
|
}
|
||||||
|
|
||||||
messages := agent.ContextBuilder.BuildMessages(
|
messages := agent.ContextBuilder.BuildMessages(
|
||||||
nil, // system instructions are not relevant for side questions
|
nil,
|
||||||
summary,
|
summary,
|
||||||
question,
|
question,
|
||||||
media,
|
media,
|
||||||
|
|
@ -4065,25 +4055,47 @@ func mapCommandError(result commands.ExecuteResult) string {
|
||||||
return fmt.Sprintf("Failed to execute /%s: %v", result.Command, result.Err)
|
return fmt.Sprintf("Failed to execute /%s: %v", result.Command, result.Err)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (al *AgentLoop) tryHandlePriorityCommand(ctx context.Context, msg bus.InboundMessage) (bool, string) {
|
func (al *AgentLoop) tryHandlePriorityCommand(ctx context.Context, msg bus.InboundMessage) (bool, bus.OutboundMessage) {
|
||||||
cmdName, ok := commands.CommandName(msg.Content)
|
cmdName, ok := commands.CommandName(msg.Content)
|
||||||
if !ok || cmdName != "btw" {
|
if !ok || cmdName != "btw" {
|
||||||
return false, ""
|
return false, bus.OutboundMessage{}
|
||||||
}
|
}
|
||||||
|
|
||||||
route, agent, err := al.resolveMessageRoute(msg)
|
route, agent, err := al.resolveMessageRoute(msg)
|
||||||
if err != nil || agent == nil {
|
if err != nil || agent == nil {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.ErrorCF("agent", fmt.Sprintf("Error resolving route for /btw: %v", err), nil)
|
logger.ErrorCF("agent", fmt.Sprintf("Error resolving route for /btw: %v", err), nil)
|
||||||
return true, fmt.Sprintf("Error processing message: %v", err)
|
return true, bus.OutboundMessage{
|
||||||
|
Channel: msg.Channel,
|
||||||
|
ChatID: msg.ChatID,
|
||||||
|
Context: outboundContextFromInbound(
|
||||||
|
&msg.Context,
|
||||||
|
msg.Channel,
|
||||||
|
msg.ChatID,
|
||||||
|
msg.Context.ReplyToMessageID,
|
||||||
|
),
|
||||||
|
Content: fmt.Sprintf("Error processing message: %v", err),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
logger.WarnCF("agent", "/btw command unavailable: no agent resolved", nil)
|
logger.WarnCF("agent", "/btw command unavailable: no agent resolved", nil)
|
||||||
return true, "Command unavailable in current context."
|
return true, bus.OutboundMessage{
|
||||||
|
Channel: msg.Channel,
|
||||||
|
ChatID: msg.ChatID,
|
||||||
|
Context: outboundContextFromInbound(
|
||||||
|
&msg.Context,
|
||||||
|
msg.Channel,
|
||||||
|
msg.ChatID,
|
||||||
|
msg.Context.ReplyToMessageID,
|
||||||
|
),
|
||||||
|
Content: "Command unavailable in current context.",
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
allocation := al.allocateRouteSession(route, msg)
|
allocation := al.allocateRouteSession(route, msg)
|
||||||
|
sessionKey := resolveScopeKey(allocation.SessionKey, msg.SessionKey)
|
||||||
|
msg.SessionKey = sessionKey
|
||||||
opts := processOptions{
|
opts := processOptions{
|
||||||
SessionKey: resolveScopeKey(allocation.SessionKey, msg.SessionKey),
|
SessionKey: sessionKey,
|
||||||
Channel: msg.Channel,
|
Channel: msg.Channel,
|
||||||
ChatID: msg.ChatID,
|
ChatID: msg.ChatID,
|
||||||
SenderID: msg.SenderID,
|
SenderID: msg.SenderID,
|
||||||
|
|
@ -4096,9 +4108,23 @@ func (al *AgentLoop) tryHandlePriorityCommand(ctx context.Context, msg bus.Inbou
|
||||||
|
|
||||||
response, handled := al.handleCommand(ctx, msg, agent, &opts)
|
response, handled := al.handleCommand(ctx, msg, agent, &opts)
|
||||||
if !handled {
|
if !handled {
|
||||||
return false, ""
|
return false, bus.OutboundMessage{}
|
||||||
|
}
|
||||||
|
agentID, outboundSessionKey, scope := outboundTurnMetadata(agent.ID, sessionKey, &allocation.Scope)
|
||||||
|
return true, bus.OutboundMessage{
|
||||||
|
Channel: msg.Channel,
|
||||||
|
ChatID: msg.ChatID,
|
||||||
|
Context: outboundContextFromInbound(
|
||||||
|
&msg.Context,
|
||||||
|
msg.Channel,
|
||||||
|
msg.ChatID,
|
||||||
|
msg.Context.ReplyToMessageID,
|
||||||
|
),
|
||||||
|
AgentID: agentID,
|
||||||
|
SessionKey: outboundSessionKey,
|
||||||
|
Scope: scope,
|
||||||
|
Content: response,
|
||||||
}
|
}
|
||||||
return true, response
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// isNativeSearchProvider reports whether the given LLM provider implements
|
// isNativeSearchProvider reports whether the given LLM provider implements
|
||||||
|
|
|
||||||
|
|
@ -1108,6 +1108,20 @@ func TestAgentLoop_Steering_BtwCommandBypassesQueuedTurn(t *testing.T) {
|
||||||
if outbound.Content != "btw immediate reply" {
|
if outbound.Content != "btw immediate reply" {
|
||||||
t.Fatalf("expected /btw reply before long turn completion, got %q", outbound.Content)
|
t.Fatalf("expected /btw reply before long turn completion, got %q", outbound.Content)
|
||||||
}
|
}
|
||||||
|
if outbound.AgentID != routing.DefaultAgentID {
|
||||||
|
t.Fatalf("expected /btw outbound agent_id %q, got %q", routing.DefaultAgentID, outbound.AgentID)
|
||||||
|
}
|
||||||
|
route, _, err := al.resolveMessageRoute(btw)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("resolveMessageRoute(/btw) error = %v", err)
|
||||||
|
}
|
||||||
|
expectedSessionKey := resolveScopeKey(al.allocateRouteSession(route, btw).SessionKey, btw.SessionKey)
|
||||||
|
if outbound.SessionKey != expectedSessionKey {
|
||||||
|
t.Fatalf("expected /btw outbound session_key %q, got %q", expectedSessionKey, outbound.SessionKey)
|
||||||
|
}
|
||||||
|
if outbound.Scope == nil || outbound.Scope.AgentID != routing.DefaultAgentID || outbound.Scope.Channel != "test" {
|
||||||
|
t.Fatalf("expected /btw outbound scope for agent %q on test channel, got %+v", routing.DefaultAgentID, outbound.Scope)
|
||||||
|
}
|
||||||
case <-time.After(2 * time.Second):
|
case <-time.After(2 * time.Second):
|
||||||
t.Fatal("timeout waiting for /btw outbound response")
|
t.Fatal("timeout waiting for /btw outbound response")
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue