Feat: add msg index

This commit is contained in:
Grail Finder
2024-11-16 16:19:27 +03:00
parent 1fe807de8e
commit b2c8698926
4 changed files with 64 additions and 22 deletions

View File

@@ -1,12 +1,12 @@
### TODO: ### TODO:
- scrolling chat history; (somewhat works out of box); - scrolling chat history; (somewhat works out of box); +
- log errors to file; - log errors to file; +
- give serial id to each msg in chat to track it; - give serial id to each msg in chat to track it; (use slice index) +
- regen last message; - show msg id next to the msg; +
- delete last message - regen last message; +
- delete last message; +
- edit message? (including from bot); - edit message? (including from bot);
- use chatml template (but do not show it to the user); - use chatml template (but do not show it to the user);
- use mistral template;
- ability to copy message; - ability to copy message;
- aility to copy selected text; - aility to copy selected text;
- menu with old chats (chat files); - menu with old chats (chat files); +

5
bot.go
View File

@@ -162,6 +162,7 @@ func chatRound(userMsg, role string, tv *tview.TextView) {
botRespMode = true botRespMode = true
reader := formMsg(chatBody, userMsg, role) reader := formMsg(chatBody, userMsg, role)
go sendMsgToLLM(reader) go sendMsgToLLM(reader)
fmt.Fprintf(tv, fmt.Sprintf("(%d) ", len(chatBody.Messages)))
fmt.Fprintf(tv, assistantIcon) fmt.Fprintf(tv, assistantIcon)
respText := strings.Builder{} respText := strings.Builder{}
out: out:
@@ -171,6 +172,7 @@ out:
// fmt.Printf(chunk) // fmt.Printf(chunk)
fmt.Fprintf(tv, chunk) fmt.Fprintf(tv, chunk)
respText.WriteString(chunk) respText.WriteString(chunk)
tv.ScrollToEnd()
case <-streamDone: case <-streamDone:
break out break out
} }
@@ -179,7 +181,6 @@ out:
chatBody.Messages = append(chatBody.Messages, models.MessagesStory{ chatBody.Messages = append(chatBody.Messages, models.MessagesStory{
Role: assistantRole, Content: respText.String(), Role: assistantRole, Content: respText.String(),
}) })
// TODO:
// bot msg is done; // bot msg is done;
// now check it for func call // now check it for func call
logChat(chatFileLoaded, chatBody.Messages) logChat(chatFileLoaded, chatBody.Messages)
@@ -299,7 +300,7 @@ func chatToTextSlice(showSys bool) []string {
if !showSys && (msg.Role != assistantRole && msg.Role != userRole) { if !showSys && (msg.Role != assistantRole && msg.Role != userRole) {
continue continue
} }
resp[i] = msg.ToText() resp[i] = msg.ToText(i)
} }
return resp return resp
} }

57
main.go
View File

@@ -3,6 +3,7 @@ package main
import ( import (
"fmt" "fmt"
"path" "path"
"strconv"
"time" "time"
"unicode" "unicode"
@@ -11,10 +12,11 @@ import (
) )
var ( var (
normalMode = false normalMode = false
botRespMode = false botRespMode = false
botMsg = "no" botMsg = "no"
indexLine = "Row: [yellow]%d[white], Column: [yellow]%d; normal mode: %v" selectedIndex = int(-1)
indexLine = "manage chats: F1; regen last: F2; delete msg menu: F3; Row: [yellow]%d[white], Column: [yellow]%d; normal mode: %v"
) )
func isASCII(s string) bool { func isASCII(s string) bool {
@@ -51,7 +53,7 @@ func main() {
if fromRow == toRow && fromColumn == toColumn { if fromRow == toRow && fromColumn == toColumn {
position.SetText(fmt.Sprintf(indexLine, fromRow, fromColumn, normalMode)) position.SetText(fmt.Sprintf(indexLine, fromRow, fromColumn, normalMode))
} else { } else {
position.SetText(fmt.Sprintf("[red]From[white] Row: [yellow]%d[white], Column: [yellow]%d[white] - [red]To[white] Row: [yellow]%d[white], To Column: [yellow]%d; normal mode: %v", fromRow, fromColumn, toRow, toColumn, normalMode)) position.SetText(fmt.Sprintf("manage chats: F1; regen last: F2; delete msg menu: F3; [red]From[white] Row: [yellow]%d[white], Column: [yellow]%d[white] - [red]To[white] Row: [yellow]%d[white], To Column: [yellow]%d; normal mode: %v", fromRow, fromColumn, toRow, toColumn, normalMode))
} }
} }
chatOpts := []string{"cancel", "new"} chatOpts := []string{"cancel", "new"}
@@ -60,7 +62,7 @@ func main() {
panic(err) panic(err)
} }
chatOpts = append(chatOpts, fList...) chatOpts = append(chatOpts, fList...)
modal := tview.NewModal(). chatActModal := tview.NewModal().
SetText("Chat actions:"). SetText("Chat actions:").
AddButtons(chatOpts). AddButtons(chatOpts).
SetDoneFunc(func(buttonIndex int, buttonLabel string) { SetDoneFunc(func(buttonIndex int, buttonLabel string) {
@@ -93,9 +95,29 @@ func main() {
return return
} }
}) })
indexPickWindow := tview.NewInputField().
SetLabel("Enter a msg index: ").
SetFieldWidth(4).
SetAcceptanceFunc(tview.InputFieldInteger).
SetDoneFunc(func(key tcell.Key) {
pages.RemovePage("getIndex")
return
})
indexPickWindow.SetInputCapture(func(event *tcell.EventKey) *tcell.EventKey {
if event.Key() == tcell.KeyEnter {
si := indexPickWindow.GetText()
selectedIndex, err = strconv.Atoi(si)
if err != nil {
logger.Error("failed to convert provided index", "error", err, "si", si)
}
}
return event
})
//
textArea.SetMovedFunc(updateStatusLine) textArea.SetMovedFunc(updateStatusLine)
updateStatusLine() updateStatusLine()
textView.SetText(chatToText(showSystemMsgs)) textView.SetText(chatToText(showSystemMsgs))
textView.ScrollToEnd()
app.SetInputCapture(func(event *tcell.EventKey) *tcell.EventKey { app.SetInputCapture(func(event *tcell.EventKey) *tcell.EventKey {
if botRespMode { if botRespMode {
// do nothing while bot typing // do nothing while bot typing
@@ -107,17 +129,36 @@ func main() {
panic(err) panic(err)
} }
chatOpts = append(chatOpts, fList...) chatOpts = append(chatOpts, fList...)
pages.AddPage("history", modal, true, true) pages.AddPage("history", chatActModal, true, true)
return nil return nil
} }
if event.Key() == tcell.KeyF2 {
// regen last msg
chatBody.Messages = chatBody.Messages[:len(chatBody.Messages)-1]
textView.SetText(chatToText(showSystemMsgs))
go chatRound("", userRole, textView)
return nil
}
if event.Key() == tcell.KeyF3 {
// TODO: delete last n messages
// modal window with input field
chatBody.Messages = chatBody.Messages[:len(chatBody.Messages)-1]
textView.SetText(chatToText(showSystemMsgs))
return nil
}
if event.Key() == tcell.KeyF4 {
// edit msg
pages.AddPage("getIndex", indexPickWindow, true, true)
}
if event.Key() == tcell.KeyEscape { if event.Key() == tcell.KeyEscape {
fromRow, fromColumn, _, _ := textArea.GetCursor() fromRow, fromColumn, _, _ := textArea.GetCursor()
position.SetText(fmt.Sprintf(indexLine, fromRow, fromColumn, normalMode)) position.SetText(fmt.Sprintf(indexLine, fromRow, fromColumn, normalMode))
// read all text into buffer // read all text into buffer
msgText := textArea.GetText() msgText := textArea.GetText()
if msgText != "" { if msgText != "" {
fmt.Fprintf(textView, "\n<user>: %s\n", msgText) fmt.Fprintf(textView, "\n(%d) <user>: %s\n", len(chatBody.Messages), msgText)
textArea.SetText("", true) textArea.SetText("", true)
textView.ScrollToEnd()
} }
// update statue line // update statue line
go chatRound(msgText, userRole, textView) go chatRound(msgText, userRole, textView)

View File

@@ -61,17 +61,17 @@ type MessagesStory struct {
Content string `json:"content"` Content string `json:"content"`
} }
func (m MessagesStory) ToText() string { func (m MessagesStory) ToText(i int) string {
icon := "" icon := ""
switch m.Role { switch m.Role {
case "assistant": case "assistant":
icon = "<🤖>: " icon = fmt.Sprintf("(%d) <🤖>: ", i)
case "user": case "user":
icon = "<user>: " icon = fmt.Sprintf("(%d) <user>: ", i)
case "system": case "system":
icon = "<system>: " icon = fmt.Sprintf("(%d) <system>: ", i)
case "tool": case "tool":
icon = "<tool>: " icon = fmt.Sprintf("(%d) <tool>: ", i)
} }
textMsg := fmt.Sprintf("%s%s\n", icon, m.Content) textMsg := fmt.Sprintf("%s%s\n", icon, m.Content)
return strings.ReplaceAll(textMsg, "\n\n", "\n") return strings.ReplaceAll(textMsg, "\n\n", "\n")