From 79861e7c2bc6f2ed95309ca6e83577ddc4e2c63a Mon Sep 17 00:00:00 2001 From: Grail Finder Date: Wed, 4 Feb 2026 11:22:17 +0300 Subject: Enha: privateMessageResp with resume --- bot.go | 36 ++++++++++++++---------------------- char-specific-context.md | 3 +++ llm.go | 39 ++++++++++++++++++++++++--------------- 3 files changed, 41 insertions(+), 37 deletions(-) diff --git a/bot.go b/bot.go index a28097f..d195431 100644 --- a/bot.go +++ b/bot.go @@ -96,8 +96,10 @@ func parseKnownToTag(content string) []string { if list == "" { continue } - parts := strings.Split(list, ",") - for _, p := range parts { + strings.SplitSeq(list, ",") + // parts := strings.Split(list, ",") + // for _, p := range parts { + for p := range strings.SplitSeq(list, ",") { p = strings.TrimSpace(p) if p != "" { knownTo = append(knownTo, p) @@ -118,25 +120,17 @@ func processMessageTag(msg models.RoleMsg) models.RoleMsg { // If KnownTo already set, assume tag already processed (content cleaned). // However, we still check for new tags (maybe added later). knownTo := parseKnownToTag(msg.Content) - // logger.Info("processing tags", "msg", msg.Content, "known_to", knownTo) // If tag found, replace KnownTo with new list (merge with existing?) // For simplicity, if knownTo is not nil, replace. - if knownTo != nil { - msg.KnownTo = knownTo - // Only ensure sender role is in KnownTo if there was a tag - // This means the message is intended for specific characters - if msg.Role != "" { - senderAdded := false - for _, k := range msg.KnownTo { - if k == msg.Role { - senderAdded = true - break - } - } - if !senderAdded { - msg.KnownTo = append(msg.KnownTo, msg.Role) - } - } + if knownTo == nil { + return msg + } + msg.KnownTo = knownTo + if msg.Role == "" { + return msg + } + if !slices.Contains(msg.KnownTo, msg.Role) { + msg.KnownTo = append(msg.KnownTo, msg.Role) } return msg } @@ -781,9 +775,6 @@ func chatWatcher(ctx context.Context) { } func chatRound(r *models.ChatRoundReq) error { - // chunkChan := make(chan string, 10) - // openAIToolChan := make(chan string, 10) - // streamDone := make(chan bool, 1) botRespMode = true botPersona := cfg.AssistantRole if cfg.WriteNextMsgAsCompletionAgent != "" { @@ -1350,6 +1341,7 @@ func triggerPrivateMessageResponses(msg models.RoleMsg) { crr := &models.ChatRoundReq{ UserMsg: triggerMsg, Role: recipient, + Resume: true, } chatRoundChan <- crr } diff --git a/char-specific-context.md b/char-specific-context.md index 423572b..f06fd75 100644 --- a/char-specific-context.md +++ b/char-specific-context.md @@ -130,6 +130,9 @@ Card example: Character‑specific context relies on the `/completion` endpoint (or other completion‑style endpoints) where the LLM is presented with a raw text prompt containing the entire filtered history. It does **not** work with OpenAI‑style `/v1/chat/completions` endpoints, because those endpoints enforce a fixed role set (`user`/`assistant`/`system`) and strip custom role names and metadata. +### TTS +Although text message might be hidden from user character. If TTS is enabled it will be read. + ### Tag Parsing - The tag is case‑sensitive. diff --git a/llm.go b/llm.go index e43cc71..30fc0ec 100644 --- a/llm.go +++ b/llm.go @@ -138,7 +138,8 @@ func (lcp LCPCompletion) FormMsg(msg, role string, resume bool) (io.Reader, erro if localImageAttachmentPath != "" { imageURL, err := models.CreateImageURLFromPath(localImageAttachmentPath) if err != nil { - logger.Error("failed to create image URL from path for completion", "error", err, "path", localImageAttachmentPath) + logger.Error("failed to create image URL from path for completion", + "error", err, "path", localImageAttachmentPath) return nil, err } // Extract base64 part from data URL (e.g., "data:image/jpeg;base64,...") @@ -166,15 +167,16 @@ func (lcp LCPCompletion) FormMsg(msg, role string, resume bool) (io.Reader, erro 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)]) + 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() { - // add to chat body chatBody.Messages = append(chatBody.Messages, models.RoleMsg{Role: cfg.ToolRole, Content: toolSysMsg}) } filteredMessages, botPersona := filterMessagesForCurrentCharacter(chatBody.Messages) @@ -310,7 +312,8 @@ func (op LCPChat) FormMsg(msg, role string, resume bool) (io.Reader, error) { } newMsg = processMessageTag(newMsg) chatBody.Messages = append(chatBody.Messages, newMsg) - logger.Debug("LCPChat FormMsg: added message to chatBody", "role", newMsg.Role, "content_len", len(newMsg.Content), "message_count_after_add", len(chatBody.Messages)) + 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 @@ -322,11 +325,13 @@ func (op LCPChat) FormMsg(msg, role string, resume bool) (io.Reader, error) { 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)]) + 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)) + 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) @@ -409,15 +414,16 @@ func (ds DeepSeekerCompletion) FormMsg(msg, role string, resume bool) (io.Reader 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)]) + 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() { - // add to chat body chatBody.Messages = append(chatBody.Messages, models.RoleMsg{Role: cfg.ToolRole, Content: toolSysMsg}) } filteredMessages, botPersona := filterMessagesForCurrentCharacter(chatBody.Messages) @@ -494,7 +500,8 @@ func (ds DeepSeekerChat) FormMsg(msg, role string, resume bool) (io.Reader, erro 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)]) + 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) @@ -571,15 +578,16 @@ func (or OpenRouterCompletion) FormMsg(msg, role string, resume bool) (io.Reader 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)]) + 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() { - // add to chat body chatBody.Messages = append(chatBody.Messages, models.RoleMsg{Role: cfg.ToolRole, Content: toolSysMsg}) } filteredMessages, botPersona := filterMessagesForCurrentCharacter(chatBody.Messages) @@ -596,11 +604,11 @@ func (or OpenRouterCompletion) FormMsg(msg, role string, resume bool) (io.Reader if cfg.ThinkUse && !cfg.ToolUse { prompt += "" } - ss := chatBody.MakeStopSliceExcluding(botPersona, listChatRoles()) + stopSlice := chatBody.MakeStopSliceExcluding(botPersona, listChatRoles()) logger.Debug("checking prompt for /completion", "tool_use", cfg.ToolUse, - "msg", msg, "resume", resume, "prompt", prompt, "stop_strings", ss) + "msg", msg, "resume", resume, "prompt", prompt, "stop_strings", stopSlice) payload := models.NewOpenRouterCompletionReq(chatBody.Model, prompt, - defaultLCPProps, ss) + defaultLCPProps, stopSlice) data, err := json.Marshal(payload) if err != nil { logger.Error("failed to form a msg", "error", err) @@ -687,7 +695,8 @@ func (or OpenRouterChat) FormMsg(msg, role string, resume bool) (io.Reader, erro 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)]) + 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) -- cgit v1.2.3