diff options
-rw-r--r-- | bot.go | 59 | ||||
-rw-r--r-- | main.go | 64 |
2 files changed, 96 insertions, 27 deletions
@@ -10,6 +10,7 @@ import ( "log/slog" "net/http" "os" + "path" "strings" "time" @@ -21,14 +22,18 @@ var httpClient = http.Client{ } var ( - logger *slog.Logger - APIURL = "http://localhost:8080/v1/chat/completions" - DB = map[string]map[string]any{} - userRole = "user" - assistantRole = "assistant" - toolRole = "tool" - assistantIcon = "<🤖>: " - userIcon = "<user>: " + logger *slog.Logger + APIURL = "http://localhost:8080/v1/chat/completions" + DB = map[string]map[string]any{} + userRole = "user" + assistantRole = "assistant" + toolRole = "tool" + assistantIcon = "<🤖>: " + userIcon = "<user>: " + historyDir = "./history/" + // TODO: pass as an cli arg + showSystemMsgs bool + chatFileLoaded string chunkChan = make(chan string, 10) streamDone = make(chan bool, 1) chatBody *models.ChatBody @@ -177,7 +182,7 @@ out: // TODO: // bot msg is done; // now check it for func call - logChat("testlog", chatBody.Messages) + logChat(chatFileLoaded, chatBody.Messages) findCall(respText.String(), tv) } @@ -220,11 +225,22 @@ func findCall(msg string, tv *tview.TextView) { // return func result to the llm } -func findLatestChat() string { - dir := "./history/" +func listHistoryFiles(dir string) ([]string, error) { files, err := os.ReadDir(dir) if err != nil { 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) } var ( @@ -232,16 +248,16 @@ func findLatestChat() string { newestTime int64 ) logger.Info("filelist", "list", files) - for _, f := range files { - fi, err := os.Stat(dir + f.Name()) + for _, fn := range files { + fi, err := os.Stat(fn) 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) } currTime := fi.ModTime().Unix() if currTime > newestTime { newestTime = currTime - latestF = f.Name() + latestF = fn } } return latestF @@ -258,12 +274,13 @@ func readHistoryChat(fn string) ([]models.MessagesStory, error) { logger.Error("failed to unmarshal", "error", err, "name", fn) return nil, err } + chatFileLoaded = fn return resp, nil } func loadOldChatOrGetNew(fns ...string) []models.MessagesStory { // find last chat - fn := findLatestChat() + fn := findLatestChat(historyDir) if len(fns) > 0 { fn = fns[0] } @@ -276,14 +293,22 @@ func loadOldChatOrGetNew(fns ...string) []models.MessagesStory { return history } -func chatToText() []string { +func chatToTextSlice(showSys bool) []string { resp := make([]string, len(chatBody.Messages)) for i, msg := range chatBody.Messages { + if !showSys && (msg.Role != assistantRole && msg.Role != userRole) { + continue + } resp[i] = msg.ToText() } return resp } +func chatToText(showSys bool) string { + s := chatToTextSlice(showSys) + return strings.Join(s, "") +} + func textToChat(chat []string) []models.MessagesStory { resp := make([]models.MessagesStory, len(chat)) for i, rawMsg := range chat { @@ -2,7 +2,8 @@ package main import ( "fmt" - "strings" + "path" + "time" "unicode" "github.com/gdamore/tcell/v2" @@ -27,6 +28,7 @@ func isASCII(s string) bool { func main() { app := tview.NewApplication() + pages := tview.NewPages() textArea := tview.NewTextArea(). SetPlaceholder("Type your prompt...") 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)) } } + 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) updateStatusLine() - history := chatToText() - 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?") + textView.SetText(chatToText(showSystemMsgs)) app.SetInputCapture(func(event *tcell.EventKey) *tcell.EventKey { if botRespMode { // do nothing while bot typing 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 { fromRow, fromColumn, _, _ := textArea.GetCursor() position.SetText(fmt.Sprintf(indexLine, fromRow, fromColumn, normalMode)) @@ -88,7 +131,8 @@ func main() { } return event }) - if err := app.SetRoot(flex, + pages.AddPage("main", flex, true, true) + if err := app.SetRoot(pages, true).EnableMouse(true).Run(); err != nil { panic(err) } |