feat(buffer): refine step retrieval for resume functionality

- Updated the GetStepsForResume method to return only steps with valid resume statuses (failed or interrupted), improving recovery context handling.
- Enhanced error handling in message processing across various integrations, including friendly error messages for users in different locales.
- Modified file handling in message integrations (Dingtalk, Discord, Feishu, Telegram, Weixin) to differentiate between image and file types, ensuring appropriate content delivery.
This commit is contained in:
Max 2026-05-08 10:14:26 +08:00
parent 4376ac9dad
commit 421d946971
7 changed files with 110 additions and 33 deletions

View file

@ -382,9 +382,14 @@ func (b *ChatBuffer) GetStepsForResume(finalStatus string) []*BufferedStep {
b.currentStep.Status = finalStatus
}
// Return all steps (they will all have the context for recovery)
result := make([]*BufferedStep, len(b.steps))
copy(result, b.steps)
// Only return steps with valid resume status (failed or interrupted)
result := make([]*BufferedStep, 0, len(b.steps))
for _, step := range b.steps {
if step.Status != ResumeStatusFailed && step.Status != ResumeStatusInterrupted {
continue
}
result = append(result, step)
}
return result
}

View file

@ -91,12 +91,23 @@ func buildContentParts(cm *dtapi.ConvertedMessage) []interface{} {
if url == "" {
url = mi.URL
}
parts = append(parts, map[string]interface{}{
"type": "file",
"file_url": url,
"mime_type": mi.MimeType,
"file_name": mi.FileName,
})
if strings.HasPrefix(mi.MimeType, "image/") {
parts = append(parts, map[string]interface{}{
"type": "image_url",
"image_url": map[string]interface{}{
"url": url,
"detail": "auto",
},
})
} else {
parts = append(parts, map[string]interface{}{
"type": "file",
"file": map[string]interface{}{
"url": url,
"filename": mi.FileName,
},
})
}
}
return parts

View file

@ -96,12 +96,23 @@ func buildContentParts(cm *dcapi.ConvertedMessage) []interface{} {
if url == "" {
continue
}
parts = append(parts, map[string]interface{}{
"type": "file",
"file_url": url,
"mime_type": mi.ContentType,
"file_name": mi.FileName,
})
if strings.HasPrefix(mi.ContentType, "image/") {
parts = append(parts, map[string]interface{}{
"type": "image_url",
"image_url": map[string]interface{}{
"url": url,
"detail": "auto",
},
})
} else {
parts = append(parts, map[string]interface{}{
"type": "file",
"file": map[string]interface{}{
"url": url,
"filename": mi.FileName,
},
})
}
}
return parts

View file

@ -85,12 +85,23 @@ func buildContentParts(cm *fsapi.ConvertedMessage) []interface{} {
if mi.Wrapper == "" {
continue
}
parts = append(parts, map[string]interface{}{
"type": "file",
"file_url": mi.Wrapper,
"mime_type": mi.MimeType,
"file_name": mi.FileName,
})
if strings.HasPrefix(mi.MimeType, "image/") {
parts = append(parts, map[string]interface{}{
"type": "image_url",
"image_url": map[string]interface{}{
"url": mi.Wrapper,
"detail": "auto",
},
})
} else {
parts = append(parts, map[string]interface{}{
"type": "file",
"file": map[string]interface{}{
"url": mi.Wrapper,
"filename": mi.FileName,
},
})
}
}
return parts

View file

@ -93,12 +93,23 @@ func buildContentParts(cm *tgapi.ConvertedMessage) []interface{} {
if mi.Wrapper == "" {
continue
}
parts = append(parts, map[string]interface{}{
"type": "file",
"file_url": mi.Wrapper,
"mime_type": mi.MimeType,
"file_name": mi.FileName,
})
if strings.HasPrefix(mi.MimeType, "image/") {
parts = append(parts, map[string]interface{}{
"type": "image_url",
"image_url": map[string]interface{}{
"url": mi.Wrapper,
"detail": "auto",
},
})
} else {
parts = append(parts, map[string]interface{}{
"type": "file",
"file": map[string]interface{}{
"url": mi.Wrapper,
"filename": mi.FileName,
},
})
}
}
return parts

View file

@ -3,6 +3,7 @@ package weixin
import (
"context"
"fmt"
"strings"
"time"
agentcontext "github.com/yaoapp/yao/agent/context"
@ -113,12 +114,23 @@ func (a *Adapter) handleMessage(ctx context.Context, entry *botEntry, msg *weixi
parts = append(parts, map[string]interface{}{"type": "text", "text": content})
}
for _, m := range mediaItems {
parts = append(parts, map[string]interface{}{
"type": "file",
"file_url": m.Wrapper,
"mime_type": m.MimeType,
"file_name": m.FileName,
})
if strings.HasPrefix(m.MimeType, "image/") {
parts = append(parts, map[string]interface{}{
"type": "image_url",
"image_url": map[string]interface{}{
"url": m.Wrapper,
"detail": "auto",
},
})
} else {
parts = append(parts, map[string]interface{}{
"type": "file",
"file": map[string]interface{}{
"url": m.Wrapper,
"filename": m.FileName,
},
})
}
}
msgContent = parts
}

View file

@ -33,6 +33,15 @@ func (h *robotHandler) handleMessage(ctx context.Context, ev *eventtypes.Event,
result, err := callHostAgent(ctx, &payload)
if err != nil {
log.Error("message handler: host agent call failed robot=%s: %v", payload.RobotID, err)
if reply := getReplyFunc(); reply != nil {
errMsg := friendlyErrorMessage(payload.Metadata.Locale)
_ = reply(ctx, &agentcontext.Message{
Role: agentcontext.RoleAssistant,
Content: errMsg,
}, payload.Metadata)
}
if ev.IsCall {
resp <- eventtypes.Result{Err: err}
}
@ -233,6 +242,13 @@ func resolveHostAssistantID(ctx context.Context, memberID string) (string, *robo
return hostID, record, nil
}
func friendlyErrorMessage(locale string) string {
if strings.HasPrefix(locale, "zh") {
return "抱歉,处理您的消息时出现了问题,请稍后重试。"
}
return "Sorry, there was a problem processing your message. Please try again later."
}
func taskDeployedMessage(execID string, locale string) string {
if strings.HasPrefix(locale, "zh") {
return fmt.Sprintf("任务已部署(执行编号: %s完成后会将结果发送给你。", execID)