Skip to content

Commit 94bb5b3

Browse files
author
yaojiping
committed
Merge branch 'rebased-chore-ai-search' of https://github.com/infinilabs/coco-server into rebased-chore-ai-search
2 parents 2fe3fce + 1c1da75 commit 94bb5b3

4 files changed

Lines changed: 71 additions & 8 deletions

File tree

modules/assistant/api/session.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -411,9 +411,10 @@ func (h APIHandler) getChatHistoryBySession(w http.ResponseWriter, req *http.Req
411411
func (h APIHandler) cancelReplyMessage(w http.ResponseWriter, req *http.Request, ps httprouter.Params) {
412412
sessionID := ps.MustGetParameter("session_id")
413413
messageID := h.GetParameterOrDefault(req, "message_id", "")
414+
lang := h.GetParameterOrDefault(req, "lang", "")
414415
log.Info("cancel reply to message: ", messageID, ", session: ", sessionID)
415416
taskID := service.GetReplyMessageTaskID(sessionID, messageID)
416-
service.StopMessageReplyTask(taskID)
417+
service.StopMessageReplyTask(taskID, lang)
417418
h.WriteAckOKJSON(w)
418419
}
419420

modules/assistant/common/message_task.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,4 +3,10 @@ package common
33
type MessageTask struct {
44
SessionID string
55
CancelFunc func()
6+
// CancelLang is the user's UI language (e.g. "zh-CN", "en"), set by the
7+
// cancel API right before invoking CancelFunc. The async processor reads
8+
// this from the InflightMessages map so it can persist a localized
9+
// "task cancelled" message into the reply — the backend has no i18n
10+
// system, so the frontend supplies the language at cancel time.
11+
CancelLang string
612
}

modules/assistant/service/background_job.go

Lines changed: 28 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,19 @@ import (
2828
"infini.sh/framework/core/util"
2929
)
3030

31+
// getCancelledMessage returns a localized "task cancelled" message based on
32+
// the CancelLang stored in the inflight task. The cancel API writes the user's
33+
// UI language into the task right before triggering cancellation, so we can
34+
// read it here and produce an appropriate message without a full i18n system.
35+
func getCancelledMessage(taskID string) string {
36+
if v, ok := InflightMessages.Load(taskID); ok {
37+
if task, ok := v.(common2.MessageTask); ok && strings.HasPrefix(task.CancelLang, "zh") {
38+
return "~~该任务已被取消。~~"
39+
}
40+
}
41+
return "~~This task has been cancelled.~~"
42+
}
43+
3144
func CreateAssistantReplyMessage(sessionID, assistantID, requestMessageID string) *core.ChatMessage {
3245
msg := &core.ChatMessage{
3346
SessionID: sessionID,
@@ -71,6 +84,8 @@ func ProcessMessageAsync(ctx context.Context, userID string, reqMsg, replyMsg *c
7184
)
7285

7386
defer func() {
87+
taskID := GetReplyMessageTaskID(params.SessionID, reqMsg.ID)
88+
7489
if !global.Env().IsDebug {
7590
if r := recover(); r != nil {
7691
var v string
@@ -85,8 +100,11 @@ func ProcessMessageAsync(ctx context.Context, userID string, reqMsg, replyMsg *c
85100

86101
// If the context was cancelled (user switched chat or clicked cancel),
87102
// treat it as a graceful stop — no error message to the user.
88-
if ctx.Err() != nil {
103+
if ctx.Err() != nil || strings.Contains(v, "context canceled") || strings.Contains(v, "context deadline exceeded") {
89104
log.Infof("async processing cancelled by user: %v", v)
105+
if replyMsg.Message == "" {
106+
replyMsg.Message = getCancelledMessage(taskID)
107+
}
90108
} else {
91109
msg := fmt.Sprintf("⚠️ error in async processing message reply, %v", v)
92110
if replyMsg.Message != "" {
@@ -102,13 +120,19 @@ func ProcessMessageAsync(ctx context.Context, userID string, reqMsg, replyMsg *c
102120
}
103121

104122
if err != nil {
105-
log.Errorf("Failed to process message reply: %v", err)
106-
replyMsg.Message += err.Error()
123+
if ctx.Err() != nil || strings.Contains(err.Error(), "context canceled") || strings.Contains(err.Error(), "context deadline exceeded") {
124+
log.Infof("async processing cancelled: %v", err)
125+
if replyMsg.Message == "" {
126+
replyMsg.Message = getCancelledMessage(taskID)
127+
}
128+
} else {
129+
log.Errorf("Failed to process message reply: %v", err)
130+
replyMsg.Message += err.Error()
131+
}
107132
}
108133

109134
finalizeProcessing(ctx, params.SessionID, replyMsg, sender)
110135
// clear the inflight message task
111-
taskID := GetReplyMessageTaskID(params.SessionID, reqMsg.ID)
112136
InflightMessages.Delete(taskID)
113137

114138
log.Info("finished async processing message")

modules/assistant/service/sessioin.go

Lines changed: 35 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import (
55
"errors"
66
"fmt"
77
"net/http"
8+
"strings"
89
"sync"
910

1011
"github.com/cihub/seelog"
@@ -91,17 +92,48 @@ func GetChatHistoryBySessionInternal(sessionID string, size int) ([]core.ChatMes
9192
return docs, nil
9293
}
9394

94-
func StopMessageReplyTask(taskID string) {
95+
// StopMessageReplyTask cancels the in-flight async reply identified by taskID.
96+
// lang is the caller's UI language (e.g. "zh-CN"); it is written into the task
97+
// before calling CancelFunc so that the async processor (background_job.go) can
98+
// read it back and persist a localized cancellation message. This is necessary
99+
// because the backend has no i18n framework — the frontend is the only place
100+
// that knows the user's preferred language.
101+
func StopMessageReplyTask(taskID string, lang string) {
95102
v, ok := InflightMessages.Load(taskID)
96103
if ok {
97104
v1, ok := v.(common.MessageTask)
98105
if ok {
99106
seelog.Debug("stop task:", v1)
107+
// Store lang back before cancelling so the deferred cleanup
108+
// in background_job.go can read it from InflightMessages.
109+
v1.CancelLang = lang
110+
InflightMessages.Store(taskID, v1)
100111
v1.CancelFunc()
101112
}
102-
} else {
103-
_ = seelog.Warnf("task id [%s] was not found", taskID)
113+
return
114+
}
115+
116+
// If the exact taskID was not found and it looks like a bare sessionID
117+
// (no underscore), try to find any inflight task for this session.
118+
if !strings.Contains(taskID, "_") {
119+
InflightMessages.Range(func(key, value any) bool {
120+
k, _ := key.(string)
121+
if strings.HasPrefix(k, taskID+"_") {
122+
task, ok := value.(common.MessageTask)
123+
if ok && task.CancelFunc != nil {
124+
seelog.Debugf("stop task by session prefix: %s", k)
125+
task.CancelLang = lang
126+
InflightMessages.Store(k, task)
127+
task.CancelFunc()
128+
}
129+
return false // stop iteration after first match
130+
}
131+
return true
132+
})
133+
return
104134
}
135+
136+
_ = seelog.Warnf("task id [%s] was not found", taskID)
105137
}
106138

107139
func StopAllMessageReplyTasks() int {

0 commit comments

Comments
 (0)