Feat: add modal window to choose chat file to load
This commit is contained in:
43
bot.go
43
bot.go
@@ -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
64
main.go
@@ -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)
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user