Fix (race): mutex chatbody

This commit is contained in:
Grail Finder
2026-03-07 10:46:18 +03:00
parent 014e297ae3
commit a842b00e96
9 changed files with 422 additions and 142 deletions

55
llm.go
View File

@@ -13,8 +13,9 @@ var lastImg string // for ctrl+j
// containsToolSysMsg checks if the toolSysMsg already exists in the chat body
func containsToolSysMsg() bool {
for i := range chatBody.Messages {
if chatBody.Messages[i].Role == cfg.ToolRole && chatBody.Messages[i].Content == toolSysMsg {
messages := chatBody.GetMessages()
for i := range messages {
if messages[i].Role == cfg.ToolRole && messages[i].Content == toolSysMsg {
return true
}
}
@@ -135,13 +136,13 @@ func (lcp LCPCompletion) FormMsg(msg, role string, resume bool) (io.Reader, erro
newMsg = models.RoleMsg{Role: role, Content: msg}
}
newMsg = *processMessageTag(&newMsg)
chatBody.Messages = append(chatBody.Messages, newMsg)
chatBody.AppendMessage(newMsg)
}
// sending description of the tools and how to use them
if cfg.ToolUse && !resume && role == cfg.UserRole && !containsToolSysMsg() {
chatBody.Messages = append(chatBody.Messages, models.RoleMsg{Role: cfg.ToolRole, Content: toolSysMsg})
chatBody.AppendMessage(models.RoleMsg{Role: cfg.ToolRole, Content: toolSysMsg})
}
filteredMessages, botPersona := filterMessagesForCurrentCharacter(chatBody.Messages)
filteredMessages, botPersona := filterMessagesForCurrentCharacter(chatBody.GetMessages())
// Build prompt and extract images inline as we process each message
messages := make([]string, len(filteredMessages))
for i := range filteredMessages {
@@ -183,7 +184,7 @@ func (lcp LCPCompletion) FormMsg(msg, role string, resume bool) (io.Reader, erro
}
logger.Debug("checking prompt for /completion", "tool_use", cfg.ToolUse,
"msg", msg, "resume", resume, "prompt", prompt, "multimodal_data_count", len(multimodalData))
payload := models.NewLCPReq(prompt, chatBody.Model, multimodalData,
payload := models.NewLCPReq(prompt, chatBody.GetModel(), multimodalData,
defaultLCPProps, chatBody.MakeStopSliceExcluding("", listChatRoles()))
data, err := json.Marshal(payload)
if err != nil {
@@ -289,17 +290,17 @@ func (op LCPChat) FormMsg(msg, role string, resume bool) (io.Reader, error) {
newMsg = models.NewRoleMsg(role, msg)
}
newMsg = *processMessageTag(&newMsg)
chatBody.Messages = append(chatBody.Messages, newMsg)
chatBody.AppendMessage(newMsg)
logger.Debug("LCPChat FormMsg: added message to chatBody", "role", newMsg.Role,
"content_len", len(newMsg.Content), "message_count_after_add", len(chatBody.Messages))
"content_len", len(newMsg.Content), "message_count_after_add", chatBody.GetMessageCount())
}
filteredMessages, _ := filterMessagesForCurrentCharacter(chatBody.Messages)
filteredMessages, _ := filterMessagesForCurrentCharacter(chatBody.GetMessages())
// openai /v1/chat does not support custom roles; needs to be user, assistant, system
// Add persona suffix to the last user message to indicate who the assistant should reply as
bodyCopy := &models.ChatBody{
Messages: make([]models.RoleMsg, len(filteredMessages)),
Model: chatBody.Model,
Stream: chatBody.Stream,
Model: chatBody.GetModel(),
Stream: chatBody.GetStream(),
}
for i := range filteredMessages {
strippedMsg := *stripThinkingFromMsg(&filteredMessages[i])
@@ -375,13 +376,13 @@ func (ds DeepSeekerCompletion) FormMsg(msg, role string, resume bool) (io.Reader
if msg != "" { // otherwise let the bot to continue
newMsg := models.RoleMsg{Role: role, Content: msg}
newMsg = *processMessageTag(&newMsg)
chatBody.Messages = append(chatBody.Messages, newMsg)
chatBody.AppendMessage(newMsg)
}
// sending description of the tools and how to use them
if cfg.ToolUse && !resume && role == cfg.UserRole && !containsToolSysMsg() {
chatBody.Messages = append(chatBody.Messages, models.RoleMsg{Role: cfg.ToolRole, Content: toolSysMsg})
chatBody.AppendMessage(models.RoleMsg{Role: cfg.ToolRole, Content: toolSysMsg})
}
filteredMessages, botPersona := filterMessagesForCurrentCharacter(chatBody.Messages)
filteredMessages, botPersona := filterMessagesForCurrentCharacter(chatBody.GetMessages())
messages := make([]string, len(filteredMessages))
for i := range filteredMessages {
messages[i] = stripThinkingFromMsg(&filteredMessages[i]).ToPrompt()
@@ -394,7 +395,7 @@ func (ds DeepSeekerCompletion) FormMsg(msg, role string, resume bool) (io.Reader
}
logger.Debug("checking prompt for /completion", "tool_use", cfg.ToolUse,
"msg", msg, "resume", resume, "prompt", prompt)
payload := models.NewDSCompletionReq(prompt, chatBody.Model,
payload := models.NewDSCompletionReq(prompt, chatBody.GetModel(),
defaultLCPProps["temp"],
chatBody.MakeStopSliceExcluding("", listChatRoles()))
data, err := json.Marshal(payload)
@@ -448,15 +449,15 @@ func (ds DeepSeekerChat) FormMsg(msg, role string, resume bool) (io.Reader, erro
if msg != "" { // otherwise let the bot continue
newMsg := models.RoleMsg{Role: role, Content: msg}
newMsg = *processMessageTag(&newMsg)
chatBody.Messages = append(chatBody.Messages, newMsg)
chatBody.AppendMessage(newMsg)
}
// Create copy of chat body with standardized user role
filteredMessages, _ := filterMessagesForCurrentCharacter(chatBody.Messages)
filteredMessages, _ := filterMessagesForCurrentCharacter(chatBody.GetMessages())
// Add persona suffix to the last user message to indicate who the assistant should reply as
bodyCopy := &models.ChatBody{
Messages: make([]models.RoleMsg, len(filteredMessages)),
Model: chatBody.Model,
Stream: chatBody.Stream,
Model: chatBody.GetModel(),
Stream: chatBody.GetStream(),
}
for i := range filteredMessages {
strippedMsg := *stripThinkingFromMsg(&filteredMessages[i])
@@ -527,13 +528,13 @@ func (or OpenRouterCompletion) FormMsg(msg, role string, resume bool) (io.Reader
if msg != "" { // otherwise let the bot to continue
newMsg := models.RoleMsg{Role: role, Content: msg}
newMsg = *processMessageTag(&newMsg)
chatBody.Messages = append(chatBody.Messages, newMsg)
chatBody.AppendMessage(newMsg)
}
// sending description of the tools and how to use them
if cfg.ToolUse && !resume && role == cfg.UserRole && !containsToolSysMsg() {
chatBody.Messages = append(chatBody.Messages, models.RoleMsg{Role: cfg.ToolRole, Content: toolSysMsg})
chatBody.AppendMessage(models.RoleMsg{Role: cfg.ToolRole, Content: toolSysMsg})
}
filteredMessages, botPersona := filterMessagesForCurrentCharacter(chatBody.Messages)
filteredMessages, botPersona := filterMessagesForCurrentCharacter(chatBody.GetMessages())
messages := make([]string, len(filteredMessages))
for i := range filteredMessages {
messages[i] = stripThinkingFromMsg(&filteredMessages[i]).ToPrompt()
@@ -547,7 +548,7 @@ func (or OpenRouterCompletion) FormMsg(msg, role string, resume bool) (io.Reader
stopSlice := chatBody.MakeStopSliceExcluding("", listChatRoles())
logger.Debug("checking prompt for /completion", "tool_use", cfg.ToolUse,
"msg", msg, "resume", resume, "prompt", prompt, "stop_strings", stopSlice)
payload := models.NewOpenRouterCompletionReq(chatBody.Model, prompt,
payload := models.NewOpenRouterCompletionReq(chatBody.GetModel(), prompt,
defaultLCPProps, stopSlice)
data, err := json.Marshal(payload)
if err != nil {
@@ -633,15 +634,15 @@ func (or OpenRouterChat) FormMsg(msg, role string, resume bool) (io.Reader, erro
newMsg = models.NewRoleMsg(role, msg)
}
newMsg = *processMessageTag(&newMsg)
chatBody.Messages = append(chatBody.Messages, newMsg)
chatBody.AppendMessage(newMsg)
}
// Create copy of chat body with standardized user role
filteredMessages, _ := filterMessagesForCurrentCharacter(chatBody.Messages)
filteredMessages, _ := filterMessagesForCurrentCharacter(chatBody.GetMessages())
// Add persona suffix to the last user message to indicate who the assistant should reply as
bodyCopy := &models.ChatBody{
Messages: make([]models.RoleMsg, len(filteredMessages)),
Model: chatBody.Model,
Stream: chatBody.Stream,
Model: chatBody.GetModel(),
Stream: chatBody.GetStream(),
}
for i := range filteredMessages {
strippedMsg := *stripThinkingFromMsg(&filteredMessages[i])