实现每机器人独立 LLM 消息队列

主要变更:
- 后端:为 botNodeRecord 添加 llm_queue_enabled 和 llm_include_channel_messages 字段
- 后端:移除全局 LLM 队列开关,完全基于每个机器人的设置
- 后端:重写 enqueueChannelMessageToLLM,为每个启用了「包含频道消息」的机器人创建独立队列记录
- 后端:频道消息不再使用 bot_id=0,每条消息都有明确的机器人归属
- 前端:AdminLLM.vue 完全重写,添加机器人 LLM 设置面板
- 前端:消息列表表格新增「机器人」列,显示机器人名称和节点 ID
- 前端:消息类型判断逻辑改为使用 channel_id 区分频道/私聊
- 前后端类型同步更新
This commit is contained in:
2026-06-17 20:51:22 +08:00
parent e2bf6aa173
commit b0a9f52096
6 changed files with 253 additions and 112 deletions
+39 -44
View File
@@ -27,24 +27,19 @@ type LLMMessageQueueInput struct {
// EnqueueLLMMessage 将消息添加到 LLM 队列
func (s *store) EnqueueLLMMessage(input LLMMessageQueueInput) (*llmMessageQueueRecord, error) {
// 检查 LLM 队列是否启用
enabled, err := s.GetBoolRuntimeSetting(runtimeSettingLLMQueueEnabled, true)
if err != nil {
return nil, fmt.Errorf("check llm queue enabled: %w", err)
}
if !enabled {
return nil, nil // 静默返回,不报错
var err error
if input.BotID == 0 {
return nil, nil // bot_id 为 0 的消息不再入队
}
// 如果是频道消息,检查是否启用了频道消息入队
if input.BotID == 0 {
includeChannel, err := s.GetBoolRuntimeSetting(runtimeSettingLLMQueueIncludeChannel, false)
if err != nil {
return nil, fmt.Errorf("check llm include channel: %w", err)
}
if !includeChannel {
return nil, nil // 频道消息入队未启用,静默返回
}
// 检查机器人级别的 LLM 队列设置
bot, err := s.GetBotNode(input.BotID)
if err != nil {
return nil, nil // 机器人不存在,静默返回
}
if !bot.LLMQueueEnabled {
return nil, nil // 机器人的 LLM 队列未启用,静默返回
}
if input.FromNodeID == "" {
@@ -54,21 +49,10 @@ func (s *store) EnqueueLLMMessage(input LLMMessageQueueInput) (*llmMessageQueueR
return nil, fmt.Errorf("text is required")
}
// 检查是否存在重复消息
// 检查是否存在重复消息:相同 bot_id + packet_id 且未删除
var existing llmMessageQueueRecord
if input.BotID > 0 {
// 机器人私聊:相同 bot_id + packet_id 且未删除
err = s.db.Where("bot_id = ? AND packet_id = ? AND deleted_at IS NULL", input.BotID, input.PacketID).
Take(&existing).Error
} else {
// 频道消息:相同 from_node_id + channel_id + packet_id 且未删除
channelID := ""
if input.ChannelID != nil {
channelID = *input.ChannelID
}
err = s.db.Where("bot_id = 0 AND from_node_id = ? AND channel_id = ? AND packet_id = ? AND deleted_at IS NULL", input.FromNodeID, channelID, input.PacketID).
Take(&existing).Error
}
err = s.db.Where("bot_id = ? AND packet_id = ? AND deleted_at IS NULL", input.BotID, input.PacketID).
Take(&existing).Error
if err == nil {
// 重复消息,直接返回已存在的
return &existing, nil
@@ -214,6 +198,7 @@ func llmMessageDTO(row llmMessageQueueRecord) map[string]any {
}
// enqueueChannelMessageToLLM 将频道消息添加到 LLM 队列
// 为每个启用了「包含频道消息」的机器人都创建一条独立的队列记录
func enqueueChannelMessageToLLM(s *store, record map[string]any) error {
if s == nil {
return nil
@@ -261,20 +246,30 @@ func enqueueChannelMessageToLLM(s *store, record map[string]any) error {
contentPtr = &s
}
_, _ = s.EnqueueLLMMessage(LLMMessageQueueInput{
BotID: 0, // 0 表示频道消息
BotNodeID: "",
BotNodeNum: 0,
FromNodeID: fromNodeID,
FromNodeNum: fromNodeNum,
LongName: longName,
ShortName: shortName,
Text: text,
PacketID: packetID,
ChannelID: channelID,
Topic: topic,
ContentJSON: contentPtr,
})
// 查询所有启用了 LLM 队列且包含频道消息的机器人
var bots []botNodeRecord
err = s.db.Where("llm_queue_enabled = ? AND llm_include_channel_messages = ?", true, true).Find(&bots).Error
if err != nil {
return fmt.Errorf("query bots for channel message enqueue: %w", err)
}
// 为每个符合条件的机器人创建一条队列记录
for _, bot := range bots {
_, _ = s.EnqueueLLMMessage(LLMMessageQueueInput{
BotID: bot.ID,
BotNodeID: bot.NodeID,
BotNodeNum: bot.NodeNum,
FromNodeID: fromNodeID,
FromNodeNum: fromNodeNum,
LongName: longName,
ShortName: shortName,
Text: text,
PacketID: packetID,
ChannelID: channelID,
Topic: topic,
ContentJSON: contentPtr,
})
}
return nil
}