diff options
| author | Grail Finder <wohilas@gmail.com> | 2026-02-08 21:50:03 +0300 |
|---|---|---|
| committer | Grail Finder <wohilas@gmail.com> | 2026-02-08 21:50:03 +0300 |
| commit | 1bf9e6eef72ec2eec7282b1554b41a0dc3d8d1b8 (patch) | |
| tree | fc9a6d47d2a1c69ae6ec98baae300a241f06405c | |
| parent | 93284312cfdb5784654fa4817c726728739b1b34 (diff) | |
Enha: extract first valid recipient from knownto
| -rw-r--r-- | bot.go | 55 | ||||
| -rw-r--r-- | llm.go | 199 |
2 files changed, 128 insertions, 126 deletions
@@ -1305,15 +1305,17 @@ func init() { go chatWatcher(ctx) } -// triggerPrivateMessageResponses checks if a message was sent privately to specific characters -// and triggers those non-user characters to respond -func triggerPrivateMessageResponses(msg *models.RoleMsg) { +func getValidKnowToRecipient(msg *models.RoleMsg) (string, bool) { if cfg == nil || !cfg.CharSpecificContextEnabled { - return + return "", false } - userCharacter := cfg.UserRole - if cfg.WriteNextMsgAs != "" { - userCharacter = cfg.WriteNextMsgAs + // case where all roles are in the tag => public message + cr := listChatRoles() + slices.Sort(cr) + slices.Sort(msg.KnownTo) + if slices.Equal(cr, msg.KnownTo) { + logger.Info("got msg with tag mentioning every role") + return "", false } // Check each character in the KnownTo list for _, recipient := range msg.KnownTo { @@ -1323,20 +1325,31 @@ func triggerPrivateMessageResponses(msg *models.RoleMsg) { } // Skip if this is the user character (user handles their own turn) // If user is in KnownTo, stop processing - it's the user's turn - if recipient == cfg.UserRole || recipient == userCharacter { - return // user in known_to => user's turn - } - // Trigger the recipient character to respond - triggerMsg := recipient + ":\n" - // Send empty message so LLM continues naturally from the conversation - crr := &models.ChatRoundReq{ - UserMsg: triggerMsg, - Role: recipient, - Resume: true, + if recipient == cfg.UserRole || recipient == cfg.WriteNextMsgAs { + return "", false } - fmt.Fprintf(textView, "\n[-:-:b](%d) ", len(chatBody.Messages)) - fmt.Fprint(textView, roleToIcon(recipient)) - fmt.Fprint(textView, "[-:-:-]\n") - chatRoundChan <- crr + return recipient, true } + return "", false +} + +// triggerPrivateMessageResponses checks if a message was sent privately to specific characters +// and triggers those non-user characters to respond +func triggerPrivateMessageResponses(msg *models.RoleMsg) { + recipient, ok := getValidKnowToRecipient(msg) + if !ok || recipient == "" { + return + } + // Trigger the recipient character to respond + triggerMsg := recipient + ":\n" + // Send empty message so LLM continues naturally from the conversation + crr := &models.ChatRoundReq{ + UserMsg: triggerMsg, + Role: recipient, + Resume: true, + } + fmt.Fprintf(textView, "\n[-:-:b](%d) ", len(chatBody.Messages)) + fmt.Fprint(textView, roleToIcon(recipient)) + fmt.Fprint(textView, "[-:-:-]\n") + chatRoundChan <- crr } @@ -59,17 +59,19 @@ func ClearImageAttachment() { // filterMessagesForCurrentCharacter filters messages based on char-specific context. // Returns filtered messages and the bot persona role (target character). func filterMessagesForCurrentCharacter(messages []models.RoleMsg) ([]models.RoleMsg, string) { - if cfg == nil || !cfg.CharSpecificContextEnabled { - botPersona := cfg.AssistantRole - if cfg.WriteNextMsgAsCompletionAgent != "" { - botPersona = cfg.WriteNextMsgAsCompletionAgent - } - return messages, botPersona - } botPersona := cfg.AssistantRole if cfg.WriteNextMsgAsCompletionAgent != "" { botPersona = cfg.WriteNextMsgAsCompletionAgent } + if cfg == nil || !cfg.CharSpecificContextEnabled { + return messages, botPersona + } + // get last message (written by user) and checck if it has a tag + lm := messages[len(messages)-1] + recipient, ok := getValidKnowToRecipient(&lm) + if ok && recipient != "" { + botPersona = recipient + } filtered := filterMessagesForCharacter(messages, botPersona) return filtered, botPersona } @@ -162,23 +164,21 @@ func (lcp LCPCompletion) FormMsg(msg, role string, resume bool) (io.Reader, erro newMsg = *processMessageTag(&newMsg) chatBody.Messages = append(chatBody.Messages, newMsg) } - if !resume { - // if rag - add as system message to avoid conflicts with tool usage - if cfg.RAGEnabled { - um := chatBody.Messages[len(chatBody.Messages)-1].Content - logger.Debug("RAG is enabled, preparing RAG context", "user_message", um) - ragResp, err := chatRagUse(um) - if err != nil { - logger.Error("failed to form a rag msg", "error", err) - return nil, err - } - logger.Debug("RAG response received", "response_len", len(ragResp), - "response_preview", ragResp[:min(len(ragResp), 100)]) - // Use system role for RAG context to avoid conflicts with tool usage - ragMsg := models.RoleMsg{Role: "system", Content: RAGMsg + ragResp} - chatBody.Messages = append(chatBody.Messages, ragMsg) - logger.Debug("RAG message added to chat body", "message_count", len(chatBody.Messages)) + // if rag - add as system message to avoid conflicts with tool usage + if !resume && cfg.RAGEnabled { + um := chatBody.Messages[len(chatBody.Messages)-1].Content + logger.Debug("RAG is enabled, preparing RAG context", "user_message", um) + ragResp, err := chatRagUse(um) + if err != nil { + logger.Error("failed to form a rag msg", "error", err) + return nil, err } + logger.Debug("RAG response received", "response_len", len(ragResp), + "response_preview", ragResp[:min(len(ragResp), 100)]) + // Use system role for RAG context to avoid conflicts with tool usage + ragMsg := models.RoleMsg{Role: "system", Content: RAGMsg + ragResp} + chatBody.Messages = append(chatBody.Messages, ragMsg) + logger.Debug("RAG message added to chat body", "message_count", len(chatBody.Messages)) } // sending description of the tools and how to use them if cfg.ToolUse && !resume && role == cfg.UserRole && !containsToolSysMsg() { @@ -324,24 +324,22 @@ func (op LCPChat) FormMsg(msg, role string, resume bool) (io.Reader, error) { logger.Debug("LCPChat FormMsg: added message to chatBody", "role", newMsg.Role, "content_len", len(newMsg.Content), "message_count_after_add", len(chatBody.Messages)) } - if !resume { - // if rag - add as system message to avoid conflicts with tool usage - if cfg.RAGEnabled { - um := chatBody.Messages[len(chatBody.Messages)-1].Content - logger.Debug("LCPChat: RAG is enabled, preparing RAG context", "user_message", um) - ragResp, err := chatRagUse(um) - if err != nil { - logger.Error("LCPChat: failed to form a rag msg", "error", err) - return nil, err - } - logger.Debug("LCPChat: RAG response received", - "response_len", len(ragResp), "response_preview", ragResp[:min(len(ragResp), 100)]) - // Use system role for RAG context to avoid conflicts with tool usage - ragMsg := models.RoleMsg{Role: "system", Content: RAGMsg + ragResp} - chatBody.Messages = append(chatBody.Messages, ragMsg) - logger.Debug("LCPChat: RAG message added to chat body", "role", ragMsg.Role, - "rag_content_len", len(ragMsg.Content), "message_count_after_rag", len(chatBody.Messages)) + // if rag - add as system message to avoid conflicts with tool usage + if !resume && cfg.RAGEnabled { + um := chatBody.Messages[len(chatBody.Messages)-1].Content + logger.Debug("LCPChat: RAG is enabled, preparing RAG context", "user_message", um) + ragResp, err := chatRagUse(um) + if err != nil { + logger.Error("LCPChat: failed to form a rag msg", "error", err) + return nil, err } + logger.Debug("LCPChat: RAG response received", + "response_len", len(ragResp), "response_preview", ragResp[:min(len(ragResp), 100)]) + // Use system role for RAG context to avoid conflicts with tool usage + ragMsg := models.RoleMsg{Role: "system", Content: RAGMsg + ragResp} + chatBody.Messages = append(chatBody.Messages, ragMsg) + logger.Debug("LCPChat: RAG message added to chat body", "role", ragMsg.Role, + "rag_content_len", len(ragMsg.Content), "message_count_after_rag", len(chatBody.Messages)) } filteredMessages, botPersona := filterMessagesForCurrentCharacter(chatBody.Messages) // openai /v1/chat does not support custom roles; needs to be user, assistant, system @@ -416,24 +414,21 @@ func (ds DeepSeekerCompletion) FormMsg(msg, role string, resume bool) (io.Reader newMsg = *processMessageTag(&newMsg) chatBody.Messages = append(chatBody.Messages, newMsg) } - if !resume { - // if rag - add as system message to avoid conflicts with tool usage - // TODO: perhaps RAG should be a func/tool call instead? - if cfg.RAGEnabled { - um := chatBody.Messages[len(chatBody.Messages)-1].Content - logger.Debug("DeepSeekerCompletion: RAG is enabled, preparing RAG context", "user_message", um) - ragResp, err := chatRagUse(um) - if err != nil { - logger.Error("DeepSeekerCompletion: failed to form a rag msg", "error", err) - return nil, err - } - logger.Debug("DeepSeekerCompletion: RAG response received", - "response_len", len(ragResp), "response_preview", ragResp[:min(len(ragResp), 100)]) - // Use system role for RAG context to avoid conflicts with tool usage - ragMsg := models.RoleMsg{Role: "system", Content: RAGMsg + ragResp} - chatBody.Messages = append(chatBody.Messages, ragMsg) - logger.Debug("DeepSeekerCompletion: RAG message added to chat body", "message_count", len(chatBody.Messages)) + // if rag - add as system message to avoid conflicts with tool usage + if !resume && cfg.RAGEnabled { + um := chatBody.Messages[len(chatBody.Messages)-1].Content + logger.Debug("DeepSeekerCompletion: RAG is enabled, preparing RAG context", "user_message", um) + ragResp, err := chatRagUse(um) + if err != nil { + logger.Error("DeepSeekerCompletion: failed to form a rag msg", "error", err) + return nil, err } + logger.Debug("DeepSeekerCompletion: RAG response received", + "response_len", len(ragResp), "response_preview", ragResp[:min(len(ragResp), 100)]) + // Use system role for RAG context to avoid conflicts with tool usage + ragMsg := models.RoleMsg{Role: "system", Content: RAGMsg + ragResp} + chatBody.Messages = append(chatBody.Messages, ragMsg) + logger.Debug("DeepSeekerCompletion: RAG message added to chat body", "message_count", len(chatBody.Messages)) } // sending description of the tools and how to use them if cfg.ToolUse && !resume && role == cfg.UserRole && !containsToolSysMsg() { @@ -507,23 +502,21 @@ func (ds DeepSeekerChat) FormMsg(msg, role string, resume bool) (io.Reader, erro newMsg = *processMessageTag(&newMsg) chatBody.Messages = append(chatBody.Messages, newMsg) } - if !resume { - // if rag - add as system message to avoid conflicts with tool usage - if cfg.RAGEnabled { - um := chatBody.Messages[len(chatBody.Messages)-1].Content - logger.Debug("RAG is enabled, preparing RAG context", "user_message", um) - ragResp, err := chatRagUse(um) - if err != nil { - logger.Error("failed to form a rag msg", "error", err) - return nil, err - } - logger.Debug("RAG response received", "response_len", len(ragResp), - "response_preview", ragResp[:min(len(ragResp), 100)]) - // Use system role for RAG context to avoid conflicts with tool usage - ragMsg := models.RoleMsg{Role: "system", Content: RAGMsg + ragResp} - chatBody.Messages = append(chatBody.Messages, ragMsg) - logger.Debug("RAG message added to chat body", "message_count", len(chatBody.Messages)) + // if rag - add as system message to avoid conflicts with tool usage + if !resume && cfg.RAGEnabled { + um := chatBody.Messages[len(chatBody.Messages)-1].Content + logger.Debug("RAG is enabled, preparing RAG context", "user_message", um) + ragResp, err := chatRagUse(um) + if err != nil { + logger.Error("failed to form a rag msg", "error", err) + return nil, err } + logger.Debug("RAG response received", "response_len", len(ragResp), + "response_preview", ragResp[:min(len(ragResp), 100)]) + // Use system role for RAG context to avoid conflicts with tool usage + ragMsg := models.RoleMsg{Role: "system", Content: RAGMsg + ragResp} + chatBody.Messages = append(chatBody.Messages, ragMsg) + logger.Debug("RAG message added to chat body", "message_count", len(chatBody.Messages)) } // Create copy of chat body with standardized user role filteredMessages, botPersona := filterMessagesForCurrentCharacter(chatBody.Messages) @@ -589,23 +582,21 @@ func (or OpenRouterCompletion) FormMsg(msg, role string, resume bool) (io.Reader newMsg = *processMessageTag(&newMsg) chatBody.Messages = append(chatBody.Messages, newMsg) } - if !resume { - // if rag - add as system message to avoid conflicts with tool usage - if cfg.RAGEnabled { - um := chatBody.Messages[len(chatBody.Messages)-1].Content - logger.Debug("RAG is enabled, preparing RAG context", "user_message", um) - ragResp, err := chatRagUse(um) - if err != nil { - logger.Error("failed to form a rag msg", "error", err) - return nil, err - } - logger.Debug("RAG response received", "response_len", - len(ragResp), "response_preview", ragResp[:min(len(ragResp), 100)]) - // Use system role for RAG context to avoid conflicts with tool usage - ragMsg := models.RoleMsg{Role: "system", Content: RAGMsg + ragResp} - chatBody.Messages = append(chatBody.Messages, ragMsg) - logger.Debug("RAG message added to chat body", "message_count", len(chatBody.Messages)) + // if rag - add as system message to avoid conflicts with tool usage + if !resume && cfg.RAGEnabled { + um := chatBody.Messages[len(chatBody.Messages)-1].Content + logger.Debug("RAG is enabled, preparing RAG context", "user_message", um) + ragResp, err := chatRagUse(um) + if err != nil { + logger.Error("failed to form a rag msg", "error", err) + return nil, err } + logger.Debug("RAG response received", "response_len", + len(ragResp), "response_preview", ragResp[:min(len(ragResp), 100)]) + // Use system role for RAG context to avoid conflicts with tool usage + ragMsg := models.RoleMsg{Role: "system", Content: RAGMsg + ragResp} + chatBody.Messages = append(chatBody.Messages, ragMsg) + logger.Debug("RAG message added to chat body", "message_count", len(chatBody.Messages)) } // sending description of the tools and how to use them if cfg.ToolUse && !resume && role == cfg.UserRole && !containsToolSysMsg() { @@ -710,23 +701,21 @@ func (or OpenRouterChat) FormMsg(msg, role string, resume bool) (io.Reader, erro newMsg = *processMessageTag(&newMsg) chatBody.Messages = append(chatBody.Messages, newMsg) } - if !resume { - // if rag - add as system message to avoid conflicts with tool usage - if cfg.RAGEnabled { - um := chatBody.Messages[len(chatBody.Messages)-1].Content - logger.Debug("RAG is enabled, preparing RAG context", "user_message", um) - ragResp, err := chatRagUse(um) - if err != nil { - logger.Error("failed to form a rag msg", "error", err) - return nil, err - } - logger.Debug("RAG response received", "response_len", len(ragResp), - "response_preview", ragResp[:min(len(ragResp), 100)]) - // Use system role for RAG context to avoid conflicts with tool usage - ragMsg := models.RoleMsg{Role: "system", Content: RAGMsg + ragResp} - chatBody.Messages = append(chatBody.Messages, ragMsg) - logger.Debug("RAG message added to chat body", "message_count", len(chatBody.Messages)) + // if rag - add as system message to avoid conflicts with tool usage + if !resume && cfg.RAGEnabled { + um := chatBody.Messages[len(chatBody.Messages)-1].Content + logger.Debug("RAG is enabled, preparing RAG context", "user_message", um) + ragResp, err := chatRagUse(um) + if err != nil { + logger.Error("failed to form a rag msg", "error", err) + return nil, err } + logger.Debug("RAG response received", "response_len", len(ragResp), + "response_preview", ragResp[:min(len(ragResp), 100)]) + // Use system role for RAG context to avoid conflicts with tool usage + ragMsg := models.RoleMsg{Role: "system", Content: RAGMsg + ragResp} + chatBody.Messages = append(chatBody.Messages, ragMsg) + logger.Debug("RAG message added to chat body", "message_count", len(chatBody.Messages)) } // Create copy of chat body with standardized user role filteredMessages, botPersona := filterMessagesForCurrentCharacter(chatBody.Messages) |
