fix(agents): preserve origin context for async follow-ups
This commit is contained in:
parent
01280eaa53
commit
77c1890765
3 changed files with 136 additions and 4 deletions
|
|
@ -15,6 +15,15 @@ import (
|
|||
"github.com/sipeed/picoclaw/pkg/utils"
|
||||
)
|
||||
|
||||
const (
|
||||
systemFollowUpOriginChannelKey = "origin_channel"
|
||||
systemFollowUpOriginChatIDKey = "origin_chat_id"
|
||||
systemFollowUpOriginChatTypeKey = "origin_chat_type"
|
||||
systemFollowUpOriginTopicIDKey = "origin_topic_id"
|
||||
systemFollowUpOriginMessageIDKey = "origin_message_id"
|
||||
systemFollowUpOriginReplyToMessageIDKey = "origin_reply_to_message_id"
|
||||
)
|
||||
|
||||
func (al *AgentLoop) buildContinuationTarget(msg bus.InboundMessage) (*continuationTarget, error) {
|
||||
if msg.Channel == "system" {
|
||||
return nil, nil
|
||||
|
|
@ -227,6 +236,42 @@ func (al *AgentLoop) allocateRouteSession(route routing.ResolvedRoute, msg bus.I
|
|||
})
|
||||
}
|
||||
|
||||
func originTopicID(origin *bus.InboundContext) string {
|
||||
if origin == nil {
|
||||
return ""
|
||||
}
|
||||
return strings.TrimSpace(origin.TopicID)
|
||||
}
|
||||
|
||||
func systemFollowUpOriginRaw(origin *bus.InboundContext, channel, chatID string) map[string]string {
|
||||
raw := map[string]string{
|
||||
systemFollowUpOriginChannelKey: strings.TrimSpace(channel),
|
||||
systemFollowUpOriginChatIDKey: strings.TrimSpace(chatID),
|
||||
}
|
||||
if origin == nil {
|
||||
return raw
|
||||
}
|
||||
if origin.Channel != "" {
|
||||
raw[systemFollowUpOriginChannelKey] = strings.TrimSpace(origin.Channel)
|
||||
}
|
||||
if origin.ChatID != "" {
|
||||
raw[systemFollowUpOriginChatIDKey] = strings.TrimSpace(origin.ChatID)
|
||||
}
|
||||
if origin.ChatType != "" {
|
||||
raw[systemFollowUpOriginChatTypeKey] = strings.TrimSpace(origin.ChatType)
|
||||
}
|
||||
if origin.TopicID != "" {
|
||||
raw[systemFollowUpOriginTopicIDKey] = strings.TrimSpace(origin.TopicID)
|
||||
}
|
||||
if origin.MessageID != "" {
|
||||
raw[systemFollowUpOriginMessageIDKey] = strings.TrimSpace(origin.MessageID)
|
||||
}
|
||||
if origin.ReplyToMessageID != "" {
|
||||
raw[systemFollowUpOriginReplyToMessageIDKey] = strings.TrimSpace(origin.ReplyToMessageID)
|
||||
}
|
||||
return raw
|
||||
}
|
||||
|
||||
func (al *AgentLoop) processSystemMessage(
|
||||
ctx context.Context,
|
||||
msg bus.InboundMessage,
|
||||
|
|
@ -253,6 +298,30 @@ func (al *AgentLoop) processSystemMessage(
|
|||
originChannel = "cli"
|
||||
originChatID = msg.ChatID
|
||||
}
|
||||
originChatType := "direct"
|
||||
originTopicID := strings.TrimSpace(msg.Context.TopicID)
|
||||
originMessageID := strings.TrimSpace(msg.Context.MessageID)
|
||||
originReplyToMessageID := strings.TrimSpace(msg.Context.ReplyToMessageID)
|
||||
if raw := msg.Context.Raw; len(raw) > 0 {
|
||||
if value := strings.TrimSpace(raw[systemFollowUpOriginChannelKey]); value != "" {
|
||||
originChannel = value
|
||||
}
|
||||
if value := strings.TrimSpace(raw[systemFollowUpOriginChatIDKey]); value != "" {
|
||||
originChatID = value
|
||||
}
|
||||
if value := strings.TrimSpace(raw[systemFollowUpOriginChatTypeKey]); value != "" {
|
||||
originChatType = value
|
||||
}
|
||||
if value := strings.TrimSpace(raw[systemFollowUpOriginTopicIDKey]); value != "" {
|
||||
originTopicID = value
|
||||
}
|
||||
if value := strings.TrimSpace(raw[systemFollowUpOriginMessageIDKey]); value != "" {
|
||||
originMessageID = value
|
||||
}
|
||||
if value := strings.TrimSpace(raw[systemFollowUpOriginReplyToMessageIDKey]); value != "" {
|
||||
originReplyToMessageID = value
|
||||
}
|
||||
}
|
||||
|
||||
// Extract subagent result from message content
|
||||
// Format: "Task 'label' completed.\n\nResult:\n<actual content>"
|
||||
|
|
@ -286,10 +355,13 @@ func (al *AgentLoop) processSystemMessage(
|
|||
}
|
||||
if originChannel != "" || originChatID != "" {
|
||||
dispatch.InboundContext = &bus.InboundContext{
|
||||
Channel: originChannel,
|
||||
ChatID: originChatID,
|
||||
ChatType: "direct",
|
||||
SenderID: msg.SenderID,
|
||||
Channel: originChannel,
|
||||
ChatID: originChatID,
|
||||
ChatType: originChatType,
|
||||
TopicID: originTopicID,
|
||||
SenderID: msg.SenderID,
|
||||
MessageID: originMessageID,
|
||||
ReplyToMessageID: originReplyToMessageID,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -2518,6 +2518,64 @@ func TestProcessMessage_UsesRouteSessionKey(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
func TestProcessSystemMessage_PreservesOriginTopicOnFinalResponse(t *testing.T) {
|
||||
tmpDir := t.TempDir()
|
||||
|
||||
cfg := &config.Config{
|
||||
Agents: config.AgentsConfig{
|
||||
Defaults: config.AgentDefaults{
|
||||
Workspace: tmpDir,
|
||||
ModelName: "test-model",
|
||||
MaxTokens: 4096,
|
||||
MaxToolIterations: 10,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
msgBus := bus.NewMessageBus()
|
||||
provider := &simpleMockProvider{response: "follow-up response"}
|
||||
al := NewAgentLoop(cfg, msgBus, provider)
|
||||
|
||||
msg := testInboundMessage(bus.InboundMessage{
|
||||
Context: bus.InboundContext{
|
||||
Channel: "system",
|
||||
ChatID: "telegram:-1001234567890",
|
||||
ChatType: "direct",
|
||||
TopicID: "42",
|
||||
SenderID: "async:spawn",
|
||||
Raw: map[string]string{
|
||||
systemFollowUpOriginChannelKey: "telegram",
|
||||
systemFollowUpOriginChatIDKey: "-1001234567890",
|
||||
systemFollowUpOriginChatTypeKey: "group",
|
||||
systemFollowUpOriginTopicIDKey: "42",
|
||||
},
|
||||
},
|
||||
Content: "Task 'deep-research' completed.\n\nResult:\nreport URL",
|
||||
})
|
||||
|
||||
if _, err := al.processSystemMessage(context.Background(), msg); err != nil {
|
||||
t.Fatalf("processSystemMessage() error = %v", err)
|
||||
}
|
||||
|
||||
select {
|
||||
case outbound := <-msgBus.OutboundChan():
|
||||
if outbound.Content != "follow-up response" {
|
||||
t.Fatalf("outbound content = %q, want follow-up response", outbound.Content)
|
||||
}
|
||||
if outbound.Channel != "telegram" || outbound.ChatID != "-1001234567890" {
|
||||
t.Fatalf("outbound route = %s/%s, want telegram/-1001234567890", outbound.Channel, outbound.ChatID)
|
||||
}
|
||||
if outbound.Context.ChatType != "group" {
|
||||
t.Fatalf("outbound chat type = %q, want group; context=%+v", outbound.Context.ChatType, outbound.Context)
|
||||
}
|
||||
if outbound.Context.TopicID != "42" {
|
||||
t.Fatalf("outbound topic = %q, want 42; context=%+v", outbound.Context.TopicID, outbound.Context)
|
||||
}
|
||||
case <-time.After(responseTimeout):
|
||||
t.Fatal("timed out waiting for outbound response")
|
||||
}
|
||||
}
|
||||
|
||||
func TestProcessMessage_CommandOutcomes(t *testing.T) {
|
||||
tmpDir, err := os.MkdirTemp("", "agent-test-*")
|
||||
if err != nil {
|
||||
|
|
|
|||
|
|
@ -417,6 +417,8 @@ toolLoop:
|
|||
ChatID: fmt.Sprintf("%s:%s", ts.channel, ts.chatID),
|
||||
ChatType: "direct",
|
||||
SenderID: fmt.Sprintf("async:%s", asyncToolName),
|
||||
TopicID: originTopicID(ts.opts.Dispatch.InboundContext),
|
||||
Raw: systemFollowUpOriginRaw(ts.opts.Dispatch.InboundContext, ts.channel, ts.chatID),
|
||||
},
|
||||
Content: content,
|
||||
})
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue