Enha: privateMessageResp with resume

This commit is contained in:
Grail Finder
2026-02-04 11:22:17 +03:00
parent e3965db3c7
commit 79861e7c2b
3 changed files with 41 additions and 37 deletions

30
bot.go
View File

@@ -96,8 +96,10 @@ func parseKnownToTag(content string) []string {
if list == "" { if list == "" {
continue continue
} }
parts := strings.Split(list, ",") strings.SplitSeq(list, ",")
for _, p := range parts { // parts := strings.Split(list, ",")
// for _, p := range parts {
for p := range strings.SplitSeq(list, ",") {
p = strings.TrimSpace(p) p = strings.TrimSpace(p)
if p != "" { if p != "" {
knownTo = append(knownTo, p) knownTo = append(knownTo, p)
@@ -118,26 +120,18 @@ func processMessageTag(msg models.RoleMsg) models.RoleMsg {
// If KnownTo already set, assume tag already processed (content cleaned). // If KnownTo already set, assume tag already processed (content cleaned).
// However, we still check for new tags (maybe added later). // However, we still check for new tags (maybe added later).
knownTo := parseKnownToTag(msg.Content) 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?) // If tag found, replace KnownTo with new list (merge with existing?)
// For simplicity, if knownTo is not nil, replace. // For simplicity, if knownTo is not nil, replace.
if knownTo != nil { if knownTo == nil {
return msg
}
msg.KnownTo = knownTo msg.KnownTo = knownTo
// Only ensure sender role is in KnownTo if there was a tag if msg.Role == "" {
// This means the message is intended for specific characters return msg
if msg.Role != "" {
senderAdded := false
for _, k := range msg.KnownTo {
if k == msg.Role {
senderAdded = true
break
} }
} if !slices.Contains(msg.KnownTo, msg.Role) {
if !senderAdded {
msg.KnownTo = append(msg.KnownTo, msg.Role) msg.KnownTo = append(msg.KnownTo, msg.Role)
} }
}
}
return msg return msg
} }
@@ -781,9 +775,6 @@ func chatWatcher(ctx context.Context) {
} }
func chatRound(r *models.ChatRoundReq) error { func chatRound(r *models.ChatRoundReq) error {
// chunkChan := make(chan string, 10)
// openAIToolChan := make(chan string, 10)
// streamDone := make(chan bool, 1)
botRespMode = true botRespMode = true
botPersona := cfg.AssistantRole botPersona := cfg.AssistantRole
if cfg.WriteNextMsgAsCompletionAgent != "" { if cfg.WriteNextMsgAsCompletionAgent != "" {
@@ -1350,6 +1341,7 @@ func triggerPrivateMessageResponses(msg models.RoleMsg) {
crr := &models.ChatRoundReq{ crr := &models.ChatRoundReq{
UserMsg: triggerMsg, UserMsg: triggerMsg,
Role: recipient, Role: recipient,
Resume: true,
} }
chatRoundChan <- crr chatRoundChan <- crr
} }

View File

@@ -130,6 +130,9 @@ Card example:
Characterspecific context relies on the `/completion` endpoint (or other completionstyle endpoints) where the LLM is presented with a raw text prompt containing the entire filtered history. It does **not** work with OpenAIstyle `/v1/chat/completions` endpoints, because those endpoints enforce a fixed role set (`user`/`assistant`/`system`) and strip custom role names and metadata. Characterspecific context relies on the `/completion` endpoint (or other completionstyle endpoints) where the LLM is presented with a raw text prompt containing the entire filtered history. It does **not** work with OpenAIstyle `/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 ### Tag Parsing
- The tag is casesensitive. - The tag is casesensitive.

39
llm.go
View File

@@ -138,7 +138,8 @@ func (lcp LCPCompletion) FormMsg(msg, role string, resume bool) (io.Reader, erro
if localImageAttachmentPath != "" { if localImageAttachmentPath != "" {
imageURL, err := models.CreateImageURLFromPath(localImageAttachmentPath) imageURL, err := models.CreateImageURLFromPath(localImageAttachmentPath)
if err != nil { 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 return nil, err
} }
// Extract base64 part from data URL (e.g., "data:image/jpeg;base64,...") // 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) logger.Error("failed to form a rag msg", "error", err)
return nil, 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 // Use system role for RAG context to avoid conflicts with tool usage
ragMsg := models.RoleMsg{Role: "system", Content: RAGMsg + ragResp} ragMsg := models.RoleMsg{Role: "system", Content: RAGMsg + ragResp}
chatBody.Messages = append(chatBody.Messages, ragMsg) chatBody.Messages = append(chatBody.Messages, ragMsg)
logger.Debug("RAG message added to chat body", "message_count", len(chatBody.Messages)) 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() { if cfg.ToolUse && !resume && role == cfg.UserRole && !containsToolSysMsg() {
// add to chat body
chatBody.Messages = append(chatBody.Messages, models.RoleMsg{Role: cfg.ToolRole, Content: toolSysMsg}) chatBody.Messages = append(chatBody.Messages, models.RoleMsg{Role: cfg.ToolRole, Content: toolSysMsg})
} }
filteredMessages, botPersona := filterMessagesForCurrentCharacter(chatBody.Messages) filteredMessages, botPersona := filterMessagesForCurrentCharacter(chatBody.Messages)
@@ -310,7 +312,8 @@ func (op LCPChat) FormMsg(msg, role string, resume bool) (io.Reader, error) {
} }
newMsg = processMessageTag(newMsg) newMsg = processMessageTag(newMsg)
chatBody.Messages = append(chatBody.Messages, 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 !resume {
// if rag - add as system message to avoid conflicts with tool usage // 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) logger.Error("LCPChat: failed to form a rag msg", "error", err)
return nil, 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 // Use system role for RAG context to avoid conflicts with tool usage
ragMsg := models.RoleMsg{Role: "system", Content: RAGMsg + ragResp} ragMsg := models.RoleMsg{Role: "system", Content: RAGMsg + ragResp}
chatBody.Messages = append(chatBody.Messages, ragMsg) 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) 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) logger.Error("DeepSeekerCompletion: failed to form a rag msg", "error", err)
return nil, 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 // Use system role for RAG context to avoid conflicts with tool usage
ragMsg := models.RoleMsg{Role: "system", Content: RAGMsg + ragResp} ragMsg := models.RoleMsg{Role: "system", Content: RAGMsg + ragResp}
chatBody.Messages = append(chatBody.Messages, ragMsg) chatBody.Messages = append(chatBody.Messages, ragMsg)
logger.Debug("DeepSeekerCompletion: RAG message added to chat body", "message_count", len(chatBody.Messages)) 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() { if cfg.ToolUse && !resume && role == cfg.UserRole && !containsToolSysMsg() {
// add to chat body
chatBody.Messages = append(chatBody.Messages, models.RoleMsg{Role: cfg.ToolRole, Content: toolSysMsg}) chatBody.Messages = append(chatBody.Messages, models.RoleMsg{Role: cfg.ToolRole, Content: toolSysMsg})
} }
filteredMessages, botPersona := filterMessagesForCurrentCharacter(chatBody.Messages) 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) logger.Error("failed to form a rag msg", "error", err)
return nil, 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 // Use system role for RAG context to avoid conflicts with tool usage
ragMsg := models.RoleMsg{Role: "system", Content: RAGMsg + ragResp} ragMsg := models.RoleMsg{Role: "system", Content: RAGMsg + ragResp}
chatBody.Messages = append(chatBody.Messages, ragMsg) 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) logger.Error("failed to form a rag msg", "error", err)
return nil, 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 // Use system role for RAG context to avoid conflicts with tool usage
ragMsg := models.RoleMsg{Role: "system", Content: RAGMsg + ragResp} ragMsg := models.RoleMsg{Role: "system", Content: RAGMsg + ragResp}
chatBody.Messages = append(chatBody.Messages, ragMsg) chatBody.Messages = append(chatBody.Messages, ragMsg)
logger.Debug("RAG message added to chat body", "message_count", len(chatBody.Messages)) 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() { if cfg.ToolUse && !resume && role == cfg.UserRole && !containsToolSysMsg() {
// add to chat body
chatBody.Messages = append(chatBody.Messages, models.RoleMsg{Role: cfg.ToolRole, Content: toolSysMsg}) chatBody.Messages = append(chatBody.Messages, models.RoleMsg{Role: cfg.ToolRole, Content: toolSysMsg})
} }
filteredMessages, botPersona := filterMessagesForCurrentCharacter(chatBody.Messages) 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 { if cfg.ThinkUse && !cfg.ToolUse {
prompt += "<think>" prompt += "<think>"
} }
ss := chatBody.MakeStopSliceExcluding(botPersona, listChatRoles()) stopSlice := chatBody.MakeStopSliceExcluding(botPersona, listChatRoles())
logger.Debug("checking prompt for /completion", "tool_use", cfg.ToolUse, 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, payload := models.NewOpenRouterCompletionReq(chatBody.Model, prompt,
defaultLCPProps, ss) defaultLCPProps, stopSlice)
data, err := json.Marshal(payload) data, err := json.Marshal(payload)
if err != nil { if err != nil {
logger.Error("failed to form a msg", "error", err) 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) logger.Error("failed to form a rag msg", "error", err)
return nil, 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 // Use system role for RAG context to avoid conflicts with tool usage
ragMsg := models.RoleMsg{Role: "system", Content: RAGMsg + ragResp} ragMsg := models.RoleMsg{Role: "system", Content: RAGMsg + ragResp}
chatBody.Messages = append(chatBody.Messages, ragMsg) chatBody.Messages = append(chatBody.Messages, ragMsg)