Skip to content

Commit 8a3e0a3

Browse files
committed
fix: handle duplicate messages and prevent re-addition
- Prevent concurrent duplicate additions in AddHistory - Skip last history item when user retries same prompt - Use rune-based truncation to avoid UTF-8 corruption in logs
1 parent 0c0b674 commit 8a3e0a3

4 files changed

Lines changed: 31 additions & 25 deletions

File tree

pkg/models/aigc/history.go

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -122,9 +122,10 @@ func (z *HistoryItem) previewText(n int) string {
122122
if text == "" {
123123
text = z.Text
124124
}
125-
// 截取前 n 个字
126-
if len(text) > n {
127-
return text[:n] + "..."
125+
// 按 rune(字符)截取前 n 个字,避免截断 UTF-8 多字节字符
126+
runes := []rune(text)
127+
if len(runes) > n {
128+
return string(runes[:n]) + "..."
128129
}
129130
return text
130131
}

pkg/services/llm/types.go

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -156,8 +156,10 @@ func (z *Message) previewText(n int) string {
156156
}
157157

158158
full := prefix + text
159-
if len(full) > n {
160-
return full[:n] + "..."
159+
// 按 rune(字符)截取,避免截断 UTF-8 多字节字符
160+
runes := []rune(full)
161+
if len(runes) > n {
162+
return string(runes[:n]) + "..."
161163
}
162164
return full
163165
}

pkg/services/stores/conversation.go

Lines changed: 9 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -76,19 +76,23 @@ func (s *conversation) GetOID() oid.OID {
7676
func (s *conversation) AddHistory(ctx context.Context, item *aigc.HistoryItem) error {
7777
key := s.getKey()
7878

79-
// 去重检查:获取最后一条消息,如果内容相同则跳过
79+
// 检查最后一条历史,如果 User 相同,则删除旧记录(保留最新答案)
8080
lastMsg, err := s.getLastUserMessage(ctx)
81-
if err == nil && lastMsg != nil {
82-
if s.isDuplicate(lastMsg, item) {
83-
logger().Debugw("duplicate message skipped", "key", key)
84-
return nil
81+
if err == nil && lastMsg != nil && lastMsg.ChatItem != nil && item.ChatItem != nil {
82+
if lastMsg.ChatItem.User == item.ChatItem.User {
83+
logger().Debugw("replace last history with same user", "key", key, "user", item.ChatItem.User)
84+
// 删除最后一条
85+
if err := s.rc.RPop(ctx, key).Err(); err != nil {
86+
logger().Infow("rpop last history fail", "key", key, "err", err)
87+
}
8588
}
8689
}
8790

8891
b, err := item.MarshalBinary()
8992
if err != nil {
9093
return err
9194
}
95+
9296
res := s.rc.RPush(ctx, key, b)
9397
err = res.Err()
9498
if err == nil {
@@ -110,7 +114,6 @@ func (s *conversation) AddHistory(ctx context.Context, item *aigc.HistoryItem) e
110114
// getLastUserMessage 获取列表中最后一条消息
111115
func (s *conversation) getLastUserMessage(ctx context.Context) (*aigc.HistoryItem, error) {
112116
key := s.getKey()
113-
// LLINDEX key -1 获取最后一条
114117
b, err := s.rc.LIndex(ctx, key, -1).Bytes()
115118
if err != nil {
116119
return nil, err
@@ -122,16 +125,6 @@ func (s *conversation) getLastUserMessage(ctx context.Context) (*aigc.HistoryIte
122125
return &item, nil
123126
}
124127

125-
// isDuplicate 检查新消息是否与最后一条消息重复
126-
func (s *conversation) isDuplicate(last, new *aigc.HistoryItem) bool {
127-
// 优先比较 ChatItem.User
128-
if last.ChatItem != nil && new.ChatItem != nil {
129-
return last.ChatItem.User == new.ChatItem.User
130-
}
131-
// 备用比较 Text
132-
return last.Text == new.Text
133-
}
134-
135128
func (s *conversation) ListHistory(ctx context.Context) (data aigc.HistoryItems, err error) {
136129
key := s.getKey()
137130
ss := s.rc.LRange(ctx, key, 0, -1)

pkg/web/api/handle_convo.go

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -120,20 +120,29 @@ func (a *api) prepareChatRequest(ctx context.Context, param *ChatRequest) *chatR
120120
if err == nil && len(data) > 0 {
121121
logger().Infow("found history", "size", len(data), "hist", aigc.HiLogged(data))
122122
data = data.RecentlyWithTokens(historyLimitToken)
123+
123124
for i, hi := range data {
124125
if hi.ChatItem != nil {
126+
isLast := i == len(data)-1
127+
isRetry := hi.ChatItem.User == param.Prompt
128+
129+
// 最后一条特殊处理:如果是重试或 Regenerate,完全跳过这一条历史
130+
if isLast && (isRetry || param.Regenerate) {
131+
logger().Debugw("skip last history", "retry", isRetry, "regenerate", param.Regenerate)
132+
break
133+
}
134+
135+
// 添加 User
125136
if len(hi.ChatItem.User) > 0 {
126137
messages = append(messages, llm.Message{
127138
Role: llm.RoleUser, Content: hi.ChatItem.User})
128139
}
140+
141+
// 添加 Assistant
129142
if len(hi.ChatItem.Assistant) > 0 {
130143
messages = append(messages, llm.Message{
131144
Role: llm.RoleAssistant, Content: hi.ChatItem.Assistant})
132145
}
133-
// skip the last one
134-
if i == len(data)-1 && param.Regenerate {
135-
break
136-
}
137146
}
138147
}
139148
}
@@ -534,6 +543,7 @@ func convertToolCallsForJSON(tcs []llm.ToolCall) []map[string]any {
534543
return result
535544
}
536545

546+
537547
// chatExecutor 定义聊天执行函数类型,支持流式/非流式
538548
type chatExecutor func(ctx context.Context, messages []llm.Message, tools []llm.ToolDefinition) (string, []llm.ToolCall, *llm.Usage, error)
539549

0 commit comments

Comments
 (0)