diff options
| author | Grail Finder <wohilas@gmail.com> | 2026-01-17 11:42:35 +0300 |
|---|---|---|
| committer | Grail Finder <wohilas@gmail.com> | 2026-01-17 11:42:35 +0300 |
| commit | 8b162ef34f0755e2224c43499218def16d4b6845 (patch) | |
| tree | 06c5a00c1f6680fbcf50dc5519887c5ff6a6f5c5 | |
| parent | 12be6036902ba9fa1b403310422e5f6f3e6c1875 (diff) | |
Enha: change textview chat history based on current user persona
| -rw-r--r-- | bot.go | 15 | ||||
| -rw-r--r-- | char-specific-context.md | 3 | ||||
| -rw-r--r-- | helpfuncs.go | 2 | ||||
| -rw-r--r-- | llm.go | 1 | ||||
| -rw-r--r-- | tables.go | 10 | ||||
| -rw-r--r-- | tools.go | 4 | ||||
| -rw-r--r-- | tui.go | 37 |
7 files changed, 41 insertions, 31 deletions
@@ -153,7 +153,8 @@ func filterMessagesForCharacter(messages []models.RoleMsg, character string) []m return messages } filtered := make([]models.RoleMsg, 0, len(messages)) - for _, msg := range messages { + for i, msg := range messages { + logger.Info("filtering messages", "character", character, "index", i, "known_to", msg.KnownTo) // If KnownTo is nil or empty, message is visible to all if len(msg.KnownTo) == 0 { filtered = append(filtered, msg) @@ -1003,9 +1004,9 @@ func findCall(msg, toolCall string, tv *tview.TextView) { chatRound("", cfg.AssistantRole, tv, false, false) } -func chatToTextSlice(showSys bool) []string { - resp := make([]string, len(chatBody.Messages)) - for i, msg := range chatBody.Messages { +func chatToTextSlice(messages []models.RoleMsg, showSys bool) []string { + resp := make([]string, len(messages)) + for i, msg := range messages { // INFO: skips system msg and tool msg if !showSys && (msg.Role == cfg.ToolRole || msg.Role == "system") { continue @@ -1015,8 +1016,8 @@ func chatToTextSlice(showSys bool) []string { return resp } -func chatToText(showSys bool) string { - s := chatToTextSlice(showSys) +func chatToText(messages []models.RoleMsg, showSys bool) string { + s := chatToTextSlice(messages, showSys) return strings.Join(s, "\n") } @@ -1140,7 +1141,7 @@ func summarizeAndStartNewChat() { } chatBody.Messages = append(chatBody.Messages, toolMsg) // Update UI - textView.SetText(chatToText(cfg.ShowSys)) + textView.SetText(chatToText(chatBody.Messages, cfg.ShowSys)) colorText() // Update storage if err := updateStorageChat(activeChatName, chatBody.Messages); err != nil { diff --git a/char-specific-context.md b/char-specific-context.md index 93c08c5..c1a7bd6 100644 --- a/char-specific-context.md +++ b/char-specific-context.md @@ -34,3 +34,6 @@ Again, this is not going to work with openais /v1/chat endpoint since it convert alternative approach to the tag string would be to have a judge agent to determine after each message what characters should hae access to it. but it means to make an additional call to llm after each msg. + + +need to update character card loader to support multiple characters diff --git a/helpfuncs.go b/helpfuncs.go index 73f8fb0..ccb5858 100644 --- a/helpfuncs.go +++ b/helpfuncs.go @@ -109,7 +109,7 @@ func startNewChat() { } // set chat body chatBody.Messages = chatBody.Messages[:2] - textView.SetText(chatToText(cfg.ShowSys)) + textView.SetText(chatToText(chatBody.Messages, cfg.ShowSys)) newChat := &models.Chat{ ID: id + 1, Name: fmt.Sprintf("%d_%s", id+1, cfg.AssistantRole), @@ -180,7 +180,6 @@ func (lcp LCPCompletion) FormMsg(msg, role string, resume bool) (io.Reader, erro } prompt = sb.String() } - 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, defaultLCPProps, chatBody.MakeStopSlice()) @@ -119,7 +119,7 @@ func makeChatTable(chatMap map[string]models.Chat) *tview.Table { return } chatBody.Messages = history - textView.SetText(chatToText(cfg.ShowSys)) + textView.SetText(chatToText(chatBody.Messages, cfg.ShowSys)) activeChatName = selectedChat pages.RemovePage(historyPage) return @@ -142,7 +142,7 @@ func makeChatTable(chatMap map[string]models.Chat) *tview.Table { } // load last chat chatBody.Messages = loadOldChatOrGetNew() - textView.SetText(chatToText(cfg.ShowSys)) + textView.SetText(chatToText(chatBody.Messages, cfg.ShowSys)) pages.RemovePage(historyPage) return case "update card": @@ -175,7 +175,7 @@ func makeChatTable(chatMap map[string]models.Chat) *tview.Table { case "move sysprompt onto 1st msg": chatBody.Messages[1].Content = chatBody.Messages[0].Content + chatBody.Messages[1].Content chatBody.Messages[0].Content = rpDefenitionSysMsg - textView.SetText(chatToText(cfg.ShowSys)) + textView.SetText(chatToText(chatBody.Messages, cfg.ShowSys)) activeChatName = selectedChat pages.RemovePage(historyPage) return @@ -546,7 +546,7 @@ func makeAgentTable(agentList []string) *tview.Table { return } // replace textview - textView.SetText(chatToText(cfg.ShowSys)) + textView.SetText(chatToText(chatBody.Messages, cfg.ShowSys)) colorText() updateStatusLine() // sysModal.ClearButtons() @@ -715,7 +715,7 @@ func makeImportChatTable(filenames []string) *tview.Table { colorText() updateStatusLine() // redraw the text in text area - textView.SetText(chatToText(cfg.ShowSys)) + textView.SetText(chatToText(chatBody.Messages, cfg.ShowSys)) pages.RemovePage(historyPage) app.SetFocus(textArea) return @@ -24,7 +24,7 @@ var ( starRE = regexp.MustCompile(`(\*.*?\*)`) thinkRE = regexp.MustCompile(`(<think>\s*([\s\S]*?)</think>)`) codeBlockRE = regexp.MustCompile(`(?s)\x60{3}(?:.*?)\n(.*?)\n\s*\x60{3}\s*`) - singleBacktickRE = regexp.MustCompile(`\x60([^\x60]*)\x60`) + singleBacktickRE = regexp.MustCompile(`\x60([^\x60]*)\x60`) roleRE = regexp.MustCompile(`^(\w+):`) rpDefenitionSysMsg = ` For this roleplay immersion is at most importance. @@ -945,7 +945,7 @@ func summarizeChat(args map[string]string) []byte { return []byte("No chat history to summarize.") } // Format chat history for the agent - chatText := chatToText(true) // include system and tool messages + chatText := chatToText(chatBody.Messages, true) // include system and tool messages return []byte(chatText) } @@ -310,7 +310,7 @@ func performSearch(term string) { searchResultLengths = nil originalTextForSearch = "" // Re-render text without highlights - textView.SetText(chatToText(cfg.ShowSys)) + textView.SetText(chatToText(chatBody.Messages, cfg.ShowSys)) colorText() return } @@ -517,8 +517,8 @@ func init() { searchResults = nil // Clear search results searchResultLengths = nil // Clear search result lengths originalTextForSearch = "" - textView.SetText(chatToText(cfg.ShowSys)) // Reset text without search regions - colorText() // Apply normal chat coloring + textView.SetText(chatToText(chatBody.Messages, cfg.ShowSys)) // Reset text without search regions + colorText() // Apply normal chat coloring } else { // Original logic if no search is active currentSelection := textView.GetHighlights() @@ -594,7 +594,7 @@ func init() { } chatBody.Messages[selectedIndex].Content = editedMsg // change textarea - textView.SetText(chatToText(cfg.ShowSys)) + textView.SetText(chatToText(chatBody.Messages, cfg.ShowSys)) pages.RemovePage(editMsgPage) editMode = false return nil @@ -627,7 +627,7 @@ func init() { } if selectedIndex >= 0 && selectedIndex < len(chatBody.Messages) { chatBody.Messages[selectedIndex].Role = newRole - textView.SetText(chatToText(cfg.ShowSys)) + textView.SetText(chatToText(chatBody.Messages, cfg.ShowSys)) colorText() pages.RemovePage(roleEditPage) } @@ -739,7 +739,7 @@ func init() { searchResults = nil searchResultLengths = nil originalTextForSearch = "" - textView.SetText(chatToText(cfg.ShowSys)) + textView.SetText(chatToText(chatBody.Messages, cfg.ShowSys)) colorText() return } else { @@ -787,7 +787,7 @@ func init() { // textArea.SetMovedFunc(updateStatusLine) updateStatusLine() - textView.SetText(chatToText(cfg.ShowSys)) + textView.SetText(chatToText(chatBody.Messages, cfg.ShowSys)) colorText() if scrollToEndEnabled { textView.ScrollToEnd() @@ -801,7 +801,7 @@ func init() { if event.Key() == tcell.KeyRune && event.Rune() == '5' && event.Modifiers()&tcell.ModAlt != 0 { // switch cfg.ShowSys cfg.ShowSys = !cfg.ShowSys - textView.SetText(chatToText(cfg.ShowSys)) + textView.SetText(chatToText(chatBody.Messages, cfg.ShowSys)) colorText() } if event.Key() == tcell.KeyRune && event.Rune() == '3' && event.Modifiers()&tcell.ModAlt != 0 { @@ -866,7 +866,7 @@ func init() { chatBody.Messages = chatBody.Messages[:len(chatBody.Messages)-1] // there is no case where user msg is regenerated // lastRole := chatBody.Messages[len(chatBody.Messages)-1].Role - textView.SetText(chatToText(cfg.ShowSys)) + textView.SetText(chatToText(chatBody.Messages, cfg.ShowSys)) go chatRound("", cfg.UserRole, textView, true, false) return nil } @@ -888,7 +888,7 @@ func init() { return nil } chatBody.Messages = chatBody.Messages[:len(chatBody.Messages)-1] - textView.SetText(chatToText(cfg.ShowSys)) + textView.SetText(chatToText(chatBody.Messages, cfg.ShowSys)) colorText() return nil } @@ -1052,7 +1052,7 @@ func init() { // clear context // remove tools and thinking removeThinking(chatBody) - textView.SetText(chatToText(cfg.ShowSys)) + textView.SetText(chatToText(chatBody.Messages, cfg.ShowSys)) colorText() return nil } @@ -1184,20 +1184,26 @@ func init() { if strings.EqualFold(role, persona) { if i == len(roles)-1 { cfg.WriteNextMsgAs = roles[0] // reached last, get first + persona = cfg.WriteNextMsgAs break } cfg.WriteNextMsgAs = roles[i+1] // get next role + persona = cfg.WriteNextMsgAs logger.Info("picked role", "roles", roles, "index", i+1) break } } + // role got switch, update textview with character specific context for user + filtered := filterMessagesForCharacter(chatBody.Messages, persona) + textView.SetText(chatToText(filtered, cfg.ShowSys)) updateStatusLine() + colorText() return nil } if event.Key() == tcell.KeyCtrlX { - persona := cfg.AssistantRole + botPersona := cfg.AssistantRole if cfg.WriteNextMsgAsCompletionAgent != "" { - persona = cfg.WriteNextMsgAsCompletionAgent + botPersona = cfg.WriteNextMsgAsCompletionAgent } roles := chatBody.ListRoles() if len(roles) == 0 { @@ -1207,12 +1213,14 @@ func init() { roles = append(roles, cfg.AssistantRole) } for i, role := range roles { - if strings.EqualFold(role, persona) { + if strings.EqualFold(role, botPersona) { if i == len(roles)-1 { cfg.WriteNextMsgAsCompletionAgent = roles[0] // reached last, get first + botPersona = cfg.WriteNextMsgAsCompletionAgent break } cfg.WriteNextMsgAsCompletionAgent = roles[i+1] // get next role + botPersona = cfg.WriteNextMsgAsCompletionAgent logger.Info("picked role", "roles", roles, "index", i+1) break } @@ -1295,7 +1303,6 @@ func init() { // cannot send msg in editMode or botRespMode if event.Key() == tcell.KeyEscape && !editMode && !botRespMode { msgText := textArea.GetText() - // TODO: add shellmode command -> output to the chat history, or at least have an option if shellMode && msgText != "" { // In shell mode, execute command instead of sending to LLM executeCommandAndDisplay(msgText) |
