Feat: add msg index
This commit is contained in:
14
README.md
14
README.md
@@ -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
5
bot.go
@@ -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
57
main.go
@@ -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)
|
||||||
|
|||||||
@@ -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")
|
||||||
|
|||||||
Reference in New Issue
Block a user