Feat: add modal window to choose chat file to load

This commit is contained in:
Grail Finder
2024-11-16 14:01:11 +03:00
parent 84b06c0da5
commit 1fe807de8e
2 changed files with 96 additions and 27 deletions

43
bot.go
View File

@@ -10,6 +10,7 @@ import (
"log/slog" "log/slog"
"net/http" "net/http"
"os" "os"
"path"
"strings" "strings"
"time" "time"
@@ -29,6 +30,10 @@ var (
toolRole = "tool" toolRole = "tool"
assistantIcon = "<🤖>: " assistantIcon = "<🤖>: "
userIcon = "<user>: " userIcon = "<user>: "
historyDir = "./history/"
// TODO: pass as an cli arg
showSystemMsgs bool
chatFileLoaded string
chunkChan = make(chan string, 10) chunkChan = make(chan string, 10)
streamDone = make(chan bool, 1) streamDone = make(chan bool, 1)
chatBody *models.ChatBody chatBody *models.ChatBody
@@ -177,7 +182,7 @@ out:
// TODO: // TODO:
// bot msg is done; // bot msg is done;
// now check it for func call // now check it for func call
logChat("testlog", chatBody.Messages) logChat(chatFileLoaded, chatBody.Messages)
findCall(respText.String(), tv) findCall(respText.String(), tv)
} }
@@ -220,11 +225,22 @@ func findCall(msg string, tv *tview.TextView) {
// return func result to the llm // return func result to the llm
} }
func findLatestChat() string { func listHistoryFiles(dir string) ([]string, error) {
dir := "./history/"
files, err := os.ReadDir(dir) files, err := os.ReadDir(dir)
if err != nil { if err != nil {
logger.Error("failed to readdir", "error", err) logger.Error("failed to readdir", "error", err)
return nil, err
}
resp := make([]string, len(files))
for i, f := range files {
resp[i] = path.Join(dir, f.Name())
}
return resp, nil
}
func findLatestChat(dir string) string {
files, err := listHistoryFiles(dir)
if err != nil {
panic(err) panic(err)
} }
var ( var (
@@ -232,16 +248,16 @@ func findLatestChat() string {
newestTime int64 newestTime int64
) )
logger.Info("filelist", "list", files) logger.Info("filelist", "list", files)
for _, f := range files { for _, fn := range files {
fi, err := os.Stat(dir + f.Name()) fi, err := os.Stat(fn)
if err != nil { if err != nil {
logger.Error("failed to get stat", "error", err, "name", f.Name()) logger.Error("failed to get stat", "error", err, "name", fn)
panic(err) panic(err)
} }
currTime := fi.ModTime().Unix() currTime := fi.ModTime().Unix()
if currTime > newestTime { if currTime > newestTime {
newestTime = currTime newestTime = currTime
latestF = f.Name() latestF = fn
} }
} }
return latestF return latestF
@@ -258,12 +274,13 @@ func readHistoryChat(fn string) ([]models.MessagesStory, error) {
logger.Error("failed to unmarshal", "error", err, "name", fn) logger.Error("failed to unmarshal", "error", err, "name", fn)
return nil, err return nil, err
} }
chatFileLoaded = fn
return resp, nil return resp, nil
} }
func loadOldChatOrGetNew(fns ...string) []models.MessagesStory { func loadOldChatOrGetNew(fns ...string) []models.MessagesStory {
// find last chat // find last chat
fn := findLatestChat() fn := findLatestChat(historyDir)
if len(fns) > 0 { if len(fns) > 0 {
fn = fns[0] fn = fns[0]
} }
@@ -276,14 +293,22 @@ func loadOldChatOrGetNew(fns ...string) []models.MessagesStory {
return history return history
} }
func chatToText() []string { func chatToTextSlice(showSys bool) []string {
resp := make([]string, len(chatBody.Messages)) resp := make([]string, len(chatBody.Messages))
for i, msg := range chatBody.Messages { for i, msg := range chatBody.Messages {
if !showSys && (msg.Role != assistantRole && msg.Role != userRole) {
continue
}
resp[i] = msg.ToText() resp[i] = msg.ToText()
} }
return resp return resp
} }
func chatToText(showSys bool) string {
s := chatToTextSlice(showSys)
return strings.Join(s, "")
}
func textToChat(chat []string) []models.MessagesStory { func textToChat(chat []string) []models.MessagesStory {
resp := make([]models.MessagesStory, len(chat)) resp := make([]models.MessagesStory, len(chat))
for i, rawMsg := range chat { for i, rawMsg := range chat {

64
main.go
View File

@@ -2,7 +2,8 @@ package main
import ( import (
"fmt" "fmt"
"strings" "path"
"time"
"unicode" "unicode"
"github.com/gdamore/tcell/v2" "github.com/gdamore/tcell/v2"
@@ -27,6 +28,7 @@ func isASCII(s string) bool {
func main() { func main() {
app := tview.NewApplication() app := tview.NewApplication()
pages := tview.NewPages()
textArea := tview.NewTextArea(). textArea := tview.NewTextArea().
SetPlaceholder("Type your prompt...") SetPlaceholder("Type your prompt...")
textArea.SetBorder(true).SetTitle("input") textArea.SetBorder(true).SetTitle("input")
@@ -52,21 +54,62 @@ func main() {
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("[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"}
fList, err := listHistoryFiles(historyDir)
if err != nil {
panic(err)
}
chatOpts = append(chatOpts, fList...)
modal := tview.NewModal().
SetText("Chat actions:").
AddButtons(chatOpts).
SetDoneFunc(func(buttonIndex int, buttonLabel string) {
switch buttonLabel {
case "new":
// set chat body
chatBody.Messages = defaultStarter
textView.SetText(chatToText(showSystemMsgs))
chatFileLoaded = path.Join(historyDir, fmt.Sprintf("%d_chat.json", time.Now().Unix()))
pages.RemovePage("history")
return
// set text
case "cancel":
pages.RemovePage("history")
// pages.ShowPage("main")
return
default:
// fn := path.Join(historyDir, buttonLabel)
fn := buttonLabel
history, err := readHistoryChat(fn)
if err != nil {
logger.Error("failed to read history file", "filename", fn)
pages.RemovePage("history")
return
}
chatBody.Messages = history
textView.SetText(chatToText(showSystemMsgs))
chatFileLoaded = fn
pages.RemovePage("history")
return
}
})
textArea.SetMovedFunc(updateStatusLine) textArea.SetMovedFunc(updateStatusLine)
updateStatusLine() updateStatusLine()
history := chatToText() textView.SetText(chatToText(showSystemMsgs))
chatHistory := strings.Builder{}
for _, m := range history {
chatHistory.WriteString(m)
// textView.SetText(m)
}
textView.SetText(chatHistory.String())
// textView.SetText("<🤖>: Hello! What can I do for you?")
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
return nil return nil
} }
if event.Key() == tcell.KeyF1 {
fList, err := listHistoryFiles(historyDir)
if err != nil {
panic(err)
}
chatOpts = append(chatOpts, fList...)
pages.AddPage("history", modal, true, true)
return nil
}
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))
@@ -88,7 +131,8 @@ func main() {
} }
return event return event
}) })
if err := app.SetRoot(flex, pages.AddPage("main", flex, true, true)
if err := app.SetRoot(pages,
true).EnableMouse(true).Run(); err != nil { true).EnableMouse(true).Run(); err != nil {
panic(err) panic(err)
} }