feat(session): 重构会话管理和消息存储功能
This commit is contained in:
@@ -9,6 +9,7 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/gogf/gf/v2/frame/g"
|
||||
"github.com/gogf/gf/v2/util/gconv"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -51,33 +52,34 @@ func SaveToRedis(ctx context.Context, tenantID uint64, sessionID string, round *
|
||||
return nil
|
||||
}
|
||||
|
||||
// DeleteSingleMessage 删除 Redis 中单条消息(按消息ID)
|
||||
func DeleteSingleMessage(ctx context.Context, tenantID uint64, sessionID string, msgID int64) error {
|
||||
// DeleteRedisMessages 批量删除 Redis 中多条消息(按消息ID列表)
|
||||
func DeleteRedisMessages(ctx context.Context, tenantID uint64, sessionID string, msgIDs []int64) error {
|
||||
key := formatRedisKey(tenantID, sessionID)
|
||||
|
||||
cursor := "0"
|
||||
for {
|
||||
result, err := g.Redis().Do(ctx, "ZSCAN", key, cursor, "MATCH", fmt.Sprintf("*\"id\":%d*", msgID), "COUNT", 10)
|
||||
if err != nil {
|
||||
return fmt.Errorf("ZSCAN失败: %w", err)
|
||||
}
|
||||
|
||||
parts := result.Strings()
|
||||
if len(parts) < 2 {
|
||||
break
|
||||
}
|
||||
|
||||
cursor = parts[0]
|
||||
members := parts[1:]
|
||||
|
||||
for _, member := range members {
|
||||
if _, err := g.Redis().Do(ctx, "ZREM", key, member); err != nil {
|
||||
g.Log().Warningf(ctx, "[会话Redis] ZREM单条失败 key=%s err=%v", key, err)
|
||||
for _, msgID := range msgIDs {
|
||||
cursor := "0"
|
||||
for {
|
||||
result, err := g.Redis().Do(ctx, "ZSCAN", key, cursor, "MATCH", fmt.Sprintf("*\"id\":%d*", msgID), "COUNT", 10)
|
||||
if err != nil {
|
||||
g.Log().Warningf(ctx, "[会话Redis] ZSCAN失败 msgID=%d err=%v", msgID, err)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if cursor == "0" {
|
||||
break
|
||||
parts := result.Strings()
|
||||
if len(parts) < 2 {
|
||||
break
|
||||
}
|
||||
|
||||
cursor = parts[0]
|
||||
for _, member := range parts[1:] {
|
||||
if _, err := g.Redis().Do(ctx, "ZREM", key, member); err != nil {
|
||||
g.Log().Warningf(ctx, "[会话Redis] ZREM失败 err=%v", err)
|
||||
}
|
||||
}
|
||||
|
||||
if cursor == "0" {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -95,8 +97,8 @@ func DeleteSessionHistory(ctx context.Context, tenantID uint64, sessionID string
|
||||
// 读操作
|
||||
// ============================================
|
||||
|
||||
// GetFromRedis 从 Redis ZSET 获取会话历史
|
||||
func GetFromRedis(ctx context.Context, tenantID uint64, sessionID string) ([]map[string]any, error) {
|
||||
// GetFromRedis 从 Redis ZSET 获取会话历史,返回 HistoryRound 切片
|
||||
func GetFromRedis(ctx context.Context, tenantID uint64, sessionID string) ([]dto.HistoryRound, error) {
|
||||
key := formatRedisKey(tenantID, sessionID)
|
||||
maxRounds := util.GetMaxRounds(ctx)
|
||||
|
||||
@@ -106,64 +108,46 @@ func GetFromRedis(ctx context.Context, tenantID uint64, sessionID string) ([]map
|
||||
}
|
||||
|
||||
if result == nil || result.IsNil() {
|
||||
return []map[string]any{}, nil
|
||||
return []dto.HistoryRound{}, nil
|
||||
}
|
||||
|
||||
return parseRedisRounds(ctx, result.Strings()), nil
|
||||
}
|
||||
|
||||
// GetSessionHistoryForInference 获取扁平消息数组(给推理用)
|
||||
func GetSessionHistoryForInference(ctx context.Context, tenantID uint64, sessionID string) ([]map[string]any, error) {
|
||||
rounds, err := GetFromRedis(ctx, tenantID, sessionID)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("获取历史会话失败: %w", err)
|
||||
}
|
||||
|
||||
if len(rounds) == 0 {
|
||||
return []map[string]any{}, nil
|
||||
}
|
||||
|
||||
return flattenRounds(rounds), nil
|
||||
return parseRounds(result.Strings()), nil
|
||||
}
|
||||
|
||||
// ============================================
|
||||
// 解析
|
||||
// ============================================
|
||||
|
||||
func parseRedisRounds(ctx context.Context, members []string) []map[string]any {
|
||||
rounds := make([]map[string]any, 0, len(members))
|
||||
// parseRounds 解析 Redis ZSET members 为 HistoryRound 切片
|
||||
func parseRounds(members []string) []dto.HistoryRound {
|
||||
rounds := make([]dto.HistoryRound, 0, len(members))
|
||||
for _, member := range members {
|
||||
var data map[string]any
|
||||
if err := json.Unmarshal([]byte(member), &data); err != nil {
|
||||
g.Log().Warningf(ctx, "[会话Redis] 解析数据失败 err=%v", err)
|
||||
var round dto.HistoryRound
|
||||
if err := json.Unmarshal([]byte(member), &round); err != nil {
|
||||
continue
|
||||
}
|
||||
rounds = append(rounds, data)
|
||||
if round.User != nil || round.Assistant != nil {
|
||||
rounds = append(rounds, round)
|
||||
}
|
||||
}
|
||||
return rounds
|
||||
}
|
||||
|
||||
func flattenRounds(rounds []map[string]any) []map[string]any {
|
||||
var messages []map[string]any
|
||||
func flattenRounds(rounds []dto.HistoryRound) []dto.FlatMessage {
|
||||
var messages []dto.FlatMessage
|
||||
for i := len(rounds) - 1; i >= 0; i-- {
|
||||
if user, ok := rounds[i]["user"].(map[string]any); ok && len(user) > 0 {
|
||||
messages = append(messages, user)
|
||||
if rounds[i].User != nil && gconv.String(rounds[i].User["content"]) != "" {
|
||||
messages = append(messages, dto.FlatMessage{
|
||||
Role: gconv.String(rounds[i].User["role"]),
|
||||
Content: gconv.String(rounds[i].User["content"]),
|
||||
})
|
||||
}
|
||||
if assistant, ok := rounds[i]["assistant"].(map[string]any); ok && len(assistant) > 0 {
|
||||
messages = append(messages, assistant)
|
||||
if rounds[i].Assistant != nil && gconv.String(rounds[i].Assistant["content"]) != "" {
|
||||
messages = append(messages, dto.FlatMessage{
|
||||
Role: gconv.String(rounds[i].Assistant["role"]),
|
||||
Content: gconv.String(rounds[i].Assistant["content"]),
|
||||
})
|
||||
}
|
||||
}
|
||||
return messages
|
||||
}
|
||||
|
||||
func appendFieldToMessages(data map[string]any, field string, messages *[]map[string]any) {
|
||||
msgs, ok := data[field].([]any)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
for _, m := range msgs {
|
||||
if msg, ok := m.(map[string]any); ok {
|
||||
*messages = append(*messages, msg)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user