Chore: linter complaints

This commit is contained in:
Grail Finder
2026-02-27 20:03:47 +03:00
parent 1fcab8365e
commit 5b1cbb46fa
7 changed files with 71 additions and 140 deletions

View File

@@ -71,8 +71,8 @@ func (ag *AgentClient) buildRequest(sysprompt, msg string) ([]byte, error) {
// Build prompt for completion endpoints // Build prompt for completion endpoints
if isCompletion { if isCompletion {
var sb strings.Builder var sb strings.Builder
for _, m := range messages { for i := range messages {
sb.WriteString(m.ToPrompt()) sb.WriteString(messages[i].ToPrompt())
sb.WriteString("\n") sb.WriteString("\n")
} }
prompt := strings.TrimSpace(sb.String()) prompt := strings.TrimSpace(sb.String())

52
bot.go
View File

@@ -143,16 +143,16 @@ func filterMessagesForCharacter(messages []models.RoleMsg, character string) []m
return messages return messages
} }
filtered := make([]models.RoleMsg, 0, len(messages)) filtered := make([]models.RoleMsg, 0, len(messages))
for _, msg := range messages { for i := range messages {
// If KnownTo is nil or empty, message is visible to all // If KnownTo is nil or empty, message is visible to all
// system msg cannot be filtered // system msg cannot be filtered
if len(msg.KnownTo) == 0 || msg.Role == "system" { if len(messages[i].KnownTo) == 0 || messages[i].Role == "system" {
filtered = append(filtered, msg) filtered = append(filtered, messages[i])
continue continue
} }
if slices.Contains(msg.KnownTo, character) { if slices.Contains(messages[i].KnownTo, character) {
// Check if character is in KnownTo lis // Check if character is in KnownTo lis
filtered = append(filtered, msg) filtered = append(filtered, messages[i])
} }
} }
return filtered return filtered
@@ -164,11 +164,11 @@ func cleanToolCalls(messages []models.RoleMsg) []models.RoleMsg {
return consolidateAssistantMessages(messages) return consolidateAssistantMessages(messages)
} }
cleaned := make([]models.RoleMsg, 0, len(messages)) cleaned := make([]models.RoleMsg, 0, len(messages))
for i, msg := range messages { for i := range messages {
// recognize the message as the tool call and remove it // recognize the message as the tool call and remove it
// tool call in last msg should stay // tool call in last msg should stay
if msg.ToolCallID == "" || i == len(messages)-1 { if messages[i].ToolCallID == "" || i == len(messages)-1 {
cleaned = append(cleaned, msg) cleaned = append(cleaned, messages[i])
} }
} }
return consolidateAssistantMessages(cleaned) return consolidateAssistantMessages(cleaned)
@@ -1207,25 +1207,25 @@ func findCall(msg, toolCall string) bool {
func chatToTextSlice(messages []models.RoleMsg, showSys bool) []string { func chatToTextSlice(messages []models.RoleMsg, showSys bool) []string {
resp := make([]string, len(messages)) resp := make([]string, len(messages))
for i, msg := range messages { for i := range messages {
// Handle tool call indicators (assistant messages with tool call but empty content) // Handle tool call indicators (assistant messages with tool call but empty content)
if (msg.Role == cfg.AssistantRole || msg.Role == "assistant") && msg.ToolCallID != "" && msg.Content == "" && len(msg.ToolCalls) > 0 { if (messages[i].Role == cfg.AssistantRole || messages[i].Role == "assistant") && messages[i].ToolCallID != "" && messages[i].Content == "" && len(messages[i].ToolCalls) > 0 {
// This is a tool call indicator - show collapsed // This is a tool call indicator - show collapsed
if toolCollapsed { if toolCollapsed {
toolName := msg.ToolCalls[0].Name toolName := messages[i].ToolCalls[0].Name
resp[i] = fmt.Sprintf("[yellow::i][tool call: %s (press Ctrl+T to expand)][-:-:-]", toolName) resp[i] = fmt.Sprintf("[yellow::i][tool call: %s (press Ctrl+T to expand)][-:-:-]", toolName)
} else { } else {
// Show full tool call info // Show full tool call info
toolName := msg.ToolCalls[0].Name toolName := messages[i].ToolCalls[0].Name
resp[i] = fmt.Sprintf("[yellow::i][tool call: %s][-:-:-]\nargs: %s", toolName, msg.ToolCalls[0].Args) resp[i] = fmt.Sprintf("[yellow::i][tool call: %s][-:-:-]\nargs: %s", toolName, messages[i].ToolCalls[0].Args)
} }
continue continue
} }
// Handle tool responses // Handle tool responses
if msg.Role == cfg.ToolRole || msg.Role == "tool" { if messages[i].Role == cfg.ToolRole || messages[i].Role == "tool" {
// Always show shell commands // Always show shell commands
if msg.IsShellCommand { if messages[i].IsShellCommand {
resp[i] = msg.ToText(i) resp[i] = messages[i].ToText(i)
continue continue
} }
// Hide non-shell tool responses when collapsed // Hide non-shell tool responses when collapsed
@@ -1233,14 +1233,14 @@ func chatToTextSlice(messages []models.RoleMsg, showSys bool) []string {
continue continue
} }
// When expanded, show tool responses // When expanded, show tool responses
resp[i] = msg.ToText(i) resp[i] = messages[i].ToText(i)
continue continue
} }
// INFO: skips system msg when showSys is false // INFO: skips system msg when showSys is false
if !showSys && msg.Role == "system" { if !showSys && messages[i].Role == "system" {
continue continue
} }
resp[i] = msg.ToText(i) resp[i] = messages[i].ToText(i)
} }
return resp return resp
} }
@@ -1274,20 +1274,6 @@ func chatToText(messages []models.RoleMsg, showSys bool) string {
return text return text
} }
func removeThinking(chatBody *models.ChatBody) {
msgs := []models.RoleMsg{}
for _, msg := range chatBody.Messages {
// Filter out tool messages and thinking markers
if msg.Role == cfg.ToolRole {
continue
}
// find thinking and remove it - use SetText to preserve ContentParts
msg.SetText(thinkRE.ReplaceAllString(msg.GetText(), ""))
msgs = append(msgs, msg)
}
chatBody.Messages = msgs
}
func addNewChat(chatName string) { func addNewChat(chatName string) {
id, err := store.ChatGetMaxID() id, err := store.ChatGetMaxID()
if err != nil { if err != nil {

View File

@@ -15,8 +15,6 @@ import (
"time" "time"
"unicode" "unicode"
"math/rand/v2"
"github.com/rivo/tview" "github.com/rivo/tview"
) )
@@ -375,16 +373,6 @@ func makeStatusLine() string {
return statusLine + imageInfo + shellModeInfo return statusLine + imageInfo + shellModeInfo
} }
var letters = []rune("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ")
func randString(n int) string {
b := make([]rune, n)
for i := range b {
b[i] = letters[rand.IntN(len(letters))]
}
return string(b)
}
// set of roles within card definition and mention in chat history // set of roles within card definition and mention in chat history
func listChatRoles() []string { func listChatRoles() []string {
currentChat, ok := chatMap[activeChatName] currentChat, ok := chatMap[activeChatName]

81
llm.go
View File

@@ -14,8 +14,8 @@ var lastImg string // for ctrl+j
// containsToolSysMsg checks if the toolSysMsg already exists in the chat body // containsToolSysMsg checks if the toolSysMsg already exists in the chat body
func containsToolSysMsg() bool { func containsToolSysMsg() bool {
for _, msg := range chatBody.Messages { for i := range chatBody.Messages {
if msg.Role == cfg.ToolRole && msg.Content == toolSysMsg { if chatBody.Messages[i].Role == cfg.ToolRole && chatBody.Messages[i].Content == toolSysMsg {
return true return true
} }
} }
@@ -147,8 +147,8 @@ func (lcp LCPCompletion) FormMsg(msg, role string, resume bool) (io.Reader, erro
} }
filteredMessages, botPersona := filterMessagesForCurrentCharacter(chatBody.Messages) filteredMessages, botPersona := filterMessagesForCurrentCharacter(chatBody.Messages)
messages := make([]string, len(filteredMessages)) messages := make([]string, len(filteredMessages))
for i, m := range filteredMessages { for i := range filteredMessages {
messages[i] = stripThinkingFromMsg(&m).ToPrompt() messages[i] = stripThinkingFromMsg(&filteredMessages[i]).ToPrompt()
} }
prompt := strings.Join(messages, "\n") prompt := strings.Join(messages, "\n")
// Add multimodal media markers to the prompt text when multimodal data is present // Add multimodal media markers to the prompt text when multimodal data is present
@@ -284,12 +284,12 @@ func (op LCPChat) FormMsg(msg, role string, resume bool) (io.Reader, error) {
filteredMessages, _ := filterMessagesForCurrentCharacter(chatBody.Messages) filteredMessages, _ := filterMessagesForCurrentCharacter(chatBody.Messages)
// Filter out tool call indicators (assistant messages with ToolCallID but empty content) // Filter out tool call indicators (assistant messages with ToolCallID but empty content)
var filteredForLLM []models.RoleMsg var filteredForLLM []models.RoleMsg
for _, msg := range filteredMessages { for i := range filteredMessages {
isToolCallIndicator := msg.Role != "system" && msg.ToolCallID != "" && msg.Content == "" && len(msg.ToolCalls) > 0 isToolCallIndicator := filteredMessages[i].Role != "system" && filteredMessages[i].ToolCallID != "" && filteredMessages[i].Content == "" && len(filteredMessages[i].ToolCalls) > 0
if isToolCallIndicator { if isToolCallIndicator {
continue continue
} }
filteredForLLM = append(filteredForLLM, msg) filteredForLLM = append(filteredForLLM, filteredMessages[i])
} }
// openai /v1/chat does not support custom roles; needs to be user, assistant, system // 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 // Add persona suffix to the last user message to indicate who the assistant should reply as
@@ -298,18 +298,19 @@ func (op LCPChat) FormMsg(msg, role string, resume bool) (io.Reader, error) {
Model: chatBody.Model, Model: chatBody.Model,
Stream: chatBody.Stream, Stream: chatBody.Stream,
} }
for i, msg := range filteredForLLM { for i := range filteredForLLM {
strippedMsg := *stripThinkingFromMsg(&msg) strippedMsg := *stripThinkingFromMsg(&filteredForLLM[i])
if strippedMsg.Role == cfg.UserRole { switch strippedMsg.Role {
case cfg.UserRole:
bodyCopy.Messages[i] = strippedMsg bodyCopy.Messages[i] = strippedMsg
bodyCopy.Messages[i].Role = "user" bodyCopy.Messages[i].Role = "user"
} else if strippedMsg.Role == cfg.AssistantRole { case cfg.AssistantRole:
bodyCopy.Messages[i] = strippedMsg bodyCopy.Messages[i] = strippedMsg
bodyCopy.Messages[i].Role = "assistant" bodyCopy.Messages[i].Role = "assistant"
} else if strippedMsg.Role == cfg.ToolRole { case cfg.ToolRole:
bodyCopy.Messages[i] = strippedMsg bodyCopy.Messages[i] = strippedMsg
bodyCopy.Messages[i].Role = "tool" bodyCopy.Messages[i].Role = "tool"
} else { default:
bodyCopy.Messages[i] = strippedMsg bodyCopy.Messages[i] = strippedMsg
} }
// Clear ToolCalls - they're stored in chat history for display but not sent to LLM // Clear ToolCalls - they're stored in chat history for display but not sent to LLM
@@ -375,8 +376,8 @@ func (ds DeepSeekerCompletion) FormMsg(msg, role string, resume bool) (io.Reader
} }
filteredMessages, botPersona := filterMessagesForCurrentCharacter(chatBody.Messages) filteredMessages, botPersona := filterMessagesForCurrentCharacter(chatBody.Messages)
messages := make([]string, len(filteredMessages)) messages := make([]string, len(filteredMessages))
for i, m := range filteredMessages { for i := range filteredMessages {
messages[i] = stripThinkingFromMsg(&m).ToPrompt() messages[i] = stripThinkingFromMsg(&filteredMessages[i]).ToPrompt()
} }
prompt := strings.Join(messages, "\n") prompt := strings.Join(messages, "\n")
// strings builder? // strings builder?
@@ -442,12 +443,12 @@ func (ds DeepSeekerChat) FormMsg(msg, role string, resume bool) (io.Reader, erro
filteredMessages, _ := filterMessagesForCurrentCharacter(chatBody.Messages) filteredMessages, _ := filterMessagesForCurrentCharacter(chatBody.Messages)
// Filter out tool call indicators (assistant messages with ToolCallID but empty content) // Filter out tool call indicators (assistant messages with ToolCallID but empty content)
var filteredForLLM []models.RoleMsg var filteredForLLM []models.RoleMsg
for _, msg := range filteredMessages { for i := range filteredMessages {
isToolCallIndicator := msg.Role != "system" && msg.ToolCallID != "" && msg.Content == "" && len(msg.ToolCalls) > 0 isToolCallIndicator := filteredMessages[i].Role != "system" && filteredMessages[i].ToolCallID != "" && filteredMessages[i].Content == "" && len(filteredMessages[i].ToolCalls) > 0
if isToolCallIndicator { if isToolCallIndicator {
continue continue
} }
filteredForLLM = append(filteredForLLM, msg) filteredForLLM = append(filteredForLLM, filteredMessages[i])
} }
// Add persona suffix to the last user message to indicate who the assistant should reply as // Add persona suffix to the last user message to indicate who the assistant should reply as
bodyCopy := &models.ChatBody{ bodyCopy := &models.ChatBody{
@@ -455,18 +456,23 @@ func (ds DeepSeekerChat) FormMsg(msg, role string, resume bool) (io.Reader, erro
Model: chatBody.Model, Model: chatBody.Model,
Stream: chatBody.Stream, Stream: chatBody.Stream,
} }
for i, msg := range filteredForLLM { for i := range filteredForLLM {
strippedMsg := *stripThinkingFromMsg(&msg) strippedMsg := *stripThinkingFromMsg(&filteredForLLM[i])
if strippedMsg.Role == cfg.UserRole || i == 1 { switch strippedMsg.Role {
bodyCopy.Messages[i] = strippedMsg case cfg.UserRole:
bodyCopy.Messages[i].Role = "user" if i == 1 {
} else if strippedMsg.Role == cfg.AssistantRole { bodyCopy.Messages[i] = strippedMsg
bodyCopy.Messages[i].Role = "user"
} else {
bodyCopy.Messages[i] = strippedMsg
}
case cfg.AssistantRole:
bodyCopy.Messages[i] = strippedMsg bodyCopy.Messages[i] = strippedMsg
bodyCopy.Messages[i].Role = "assistant" bodyCopy.Messages[i].Role = "assistant"
} else if strippedMsg.Role == cfg.ToolRole { case cfg.ToolRole:
bodyCopy.Messages[i] = strippedMsg bodyCopy.Messages[i] = strippedMsg
bodyCopy.Messages[i].Role = "tool" bodyCopy.Messages[i].Role = "tool"
} else { default:
bodyCopy.Messages[i] = strippedMsg bodyCopy.Messages[i] = strippedMsg
} }
// Clear ToolCalls - they're stored in chat history for display but not sent to LLM // Clear ToolCalls - they're stored in chat history for display but not sent to LLM
@@ -523,8 +529,8 @@ func (or OpenRouterCompletion) FormMsg(msg, role string, resume bool) (io.Reader
} }
filteredMessages, botPersona := filterMessagesForCurrentCharacter(chatBody.Messages) filteredMessages, botPersona := filterMessagesForCurrentCharacter(chatBody.Messages)
messages := make([]string, len(filteredMessages)) messages := make([]string, len(filteredMessages))
for i, m := range filteredMessages { for i := range filteredMessages {
messages[i] = stripThinkingFromMsg(&m).ToPrompt() messages[i] = stripThinkingFromMsg(&filteredMessages[i]).ToPrompt()
} }
prompt := strings.Join(messages, "\n") prompt := strings.Join(messages, "\n")
// strings builder? // strings builder?
@@ -623,12 +629,12 @@ func (or OpenRouterChat) FormMsg(msg, role string, resume bool) (io.Reader, erro
filteredMessages, _ := filterMessagesForCurrentCharacter(chatBody.Messages) filteredMessages, _ := filterMessagesForCurrentCharacter(chatBody.Messages)
// Filter out tool call indicators (assistant messages with ToolCallID but empty content) // Filter out tool call indicators (assistant messages with ToolCallID but empty content)
var filteredForLLM []models.RoleMsg var filteredForLLM []models.RoleMsg
for _, msg := range filteredMessages { for i := range filteredMessages {
isToolCallIndicator := msg.Role != "system" && msg.ToolCallID != "" && msg.Content == "" && len(msg.ToolCalls) > 0 isToolCallIndicator := filteredMessages[i].Role != "system" && filteredMessages[i].ToolCallID != "" && filteredMessages[i].Content == "" && len(filteredMessages[i].ToolCalls) > 0
if isToolCallIndicator { if isToolCallIndicator {
continue continue
} }
filteredForLLM = append(filteredForLLM, msg) filteredForLLM = append(filteredForLLM, filteredMessages[i])
} }
// Add persona suffix to the last user message to indicate who the assistant should reply as // Add persona suffix to the last user message to indicate who the assistant should reply as
bodyCopy := &models.ChatBody{ bodyCopy := &models.ChatBody{
@@ -636,18 +642,19 @@ func (or OpenRouterChat) FormMsg(msg, role string, resume bool) (io.Reader, erro
Model: chatBody.Model, Model: chatBody.Model,
Stream: chatBody.Stream, Stream: chatBody.Stream,
} }
for i, msg := range filteredForLLM { for i := range filteredForLLM {
strippedMsg := *stripThinkingFromMsg(&msg) strippedMsg := *stripThinkingFromMsg(&filteredForLLM[i])
if strippedMsg.Role == cfg.UserRole { switch strippedMsg.Role {
case cfg.UserRole:
bodyCopy.Messages[i] = strippedMsg bodyCopy.Messages[i] = strippedMsg
bodyCopy.Messages[i].Role = "user" bodyCopy.Messages[i].Role = "user"
} else if strippedMsg.Role == cfg.AssistantRole { case cfg.AssistantRole:
bodyCopy.Messages[i] = strippedMsg bodyCopy.Messages[i] = strippedMsg
bodyCopy.Messages[i].Role = "assistant" bodyCopy.Messages[i].Role = "assistant"
} else if strippedMsg.Role == cfg.ToolRole { case cfg.ToolRole:
bodyCopy.Messages[i] = strippedMsg bodyCopy.Messages[i] = strippedMsg
bodyCopy.Messages[i].Role = "tool" bodyCopy.Messages[i].Role = "tool"
} else { default:
bodyCopy.Messages[i] = strippedMsg bodyCopy.Messages[i] = strippedMsg
} }
// Clear ToolCalls - they're stored in chat history for display but not sent to LLM // Clear ToolCalls - they're stored in chat history for display but not sent to LLM

View File

@@ -1,42 +0,0 @@
package main
import (
"fmt"
"gf-lt/config"
"gf-lt/models"
"strings"
"testing"
)
func TestRemoveThinking(t *testing.T) {
cases := []struct {
cb *models.ChatBody
toolMsgs uint8
}{
{cb: &models.ChatBody{
Stream: true,
Messages: []models.RoleMsg{
{Role: "tool", Content: "should be ommited"},
{Role: "system", Content: "should stay"},
{Role: "user", Content: "hello, how are you?"},
{Role: "assistant", Content: "Oh, hi. <think>I should thank user and continue the conversation</think> I am geat, thank you! How are you?"},
},
},
toolMsgs: uint8(1),
},
}
for i, tc := range cases {
t.Run(fmt.Sprintf("run_%d", i), func(t *testing.T) {
cfg = &config.Config{ToolRole: "tool"} // Initialize cfg.ToolRole for test
mNum := len(tc.cb.Messages)
removeThinking(tc.cb)
if len(tc.cb.Messages) != mNum-int(tc.toolMsgs) {
t.Errorf("failed to delete tools msg %v; expected %d, got %d", tc.cb.Messages, mNum-int(tc.toolMsgs), len(tc.cb.Messages))
}
for _, msg := range tc.cb.Messages {
if strings.Contains(msg.Content, "<think>") {
t.Errorf("msg contains think tag; msg: %s\n", msg.Content)
}
}
}) }
}

View File

@@ -523,16 +523,16 @@ type ChatBody struct {
} }
func (cb *ChatBody) Rename(oldname, newname string) { func (cb *ChatBody) Rename(oldname, newname string) {
for i, m := range cb.Messages { for i := range cb.Messages {
cb.Messages[i].Content = strings.ReplaceAll(m.Content, oldname, newname) cb.Messages[i].Content = strings.ReplaceAll(cb.Messages[i].Content, oldname, newname)
cb.Messages[i].Role = strings.ReplaceAll(m.Role, oldname, newname) cb.Messages[i].Role = strings.ReplaceAll(cb.Messages[i].Role, oldname, newname)
} }
} }
func (cb *ChatBody) ListRoles() []string { func (cb *ChatBody) ListRoles() []string {
namesMap := make(map[string]struct{}) namesMap := make(map[string]struct{})
for _, m := range cb.Messages { for i := range cb.Messages {
namesMap[m.Role] = struct{}{} namesMap[cb.Messages[i].Role] = struct{}{}
} }
resp := make([]string, len(namesMap)) resp := make([]string, len(namesMap))
i := 0 i := 0

10
tui.go
View File

@@ -565,7 +565,7 @@ func init() {
return nil return nil
} }
// Handle Ctrl+T to toggle tool call/response visibility // Handle Ctrl+T to toggle tool call/response visibility
if event.Key() == tcell.KeyRune && event.Rune() == 't' && event.Modifiers()&tcell.ModCtrl != 0 { if event.Key() == tcell.KeyCtrlT {
toolCollapsed = !toolCollapsed toolCollapsed = !toolCollapsed
textView.SetText(chatToText(chatBody.Messages, cfg.ShowSys)) textView.SetText(chatToText(chatBody.Messages, cfg.ShowSys))
colorText() colorText()
@@ -796,14 +796,6 @@ func init() {
showModelSelectionPopup() showModelSelectionPopup()
return nil return nil
} }
if event.Key() == tcell.KeyCtrlT {
// clear context
// remove tools and thinking
removeThinking(chatBody)
textView.SetText(chatToText(chatBody.Messages, cfg.ShowSys))
colorText()
return nil
}
if event.Key() == tcell.KeyCtrlV { if event.Key() == tcell.KeyCtrlV {
if isFullScreenPageActive() { if isFullScreenPageActive() {
return event return event