Feat: import chat on f11

This commit is contained in:
Grail Finder
2025-04-06 13:59:44 +03:00
parent 4a9c90af3f
commit d2e4846835
4 changed files with 120 additions and 4 deletions

1
.gitignore vendored
View File

@@ -9,3 +9,4 @@ sysprompts/*
!sysprompts/cluedo.json !sysprompts/cluedo.json
history_bak/ history_bak/
.aider* .aider*
tags

View File

@@ -7,6 +7,7 @@ import (
"fmt" "fmt"
"os" "os"
"os/exec" "os/exec"
"path/filepath"
"strings" "strings"
"time" "time"
) )
@@ -34,6 +35,24 @@ func exportChat() error {
return os.WriteFile(activeChatName+".json", data, 0666) return os.WriteFile(activeChatName+".json", data, 0666)
} }
func importChat(filename string) error {
data, err := os.ReadFile(filename)
if err != nil {
return err
}
messages := []models.RoleMsg{}
if err := json.Unmarshal(data, &messages); err != nil {
return err
}
activeChatName = filepath.Base(filename)
chatBody.Messages = messages
cfg.AssistantRole = messages[1].Role
if cfg.AssistantRole == cfg.UserRole {
cfg.AssistantRole = messages[2].Role
}
return nil
}
func updateStorageChat(name string, msgs []models.RoleMsg) error { func updateStorageChat(name string, msgs []models.RoleMsg) error {
var err error var err error
chat, ok := chatMap[name] chat, ok := chatMap[name]

View File

@@ -457,3 +457,79 @@ func makeCodeBlockTable(codeBlocks []string) *tview.Table {
}) })
return table return table
} }
func makeImportChatTable(filenames []string) *tview.Table {
actions := []string{"load"}
rows, cols := len(filenames), len(actions)+1
chatActTable := tview.NewTable().
SetBorders(true)
for r := 0; r < rows; r++ {
for c := 0; c < cols; c++ {
color := tcell.ColorWhite
if c < 1 {
chatActTable.SetCell(r, c,
tview.NewTableCell(filenames[r]).
SetTextColor(color).
SetAlign(tview.AlignCenter))
} else {
chatActTable.SetCell(r, c,
tview.NewTableCell(actions[c-1]).
SetTextColor(color).
SetAlign(tview.AlignCenter))
}
}
}
chatActTable.Select(0, 0).SetFixed(1, 1).SetDoneFunc(func(key tcell.Key) {
if key == tcell.KeyEsc || key == tcell.KeyF1 {
pages.RemovePage(historyPage)
return
}
if key == tcell.KeyEnter {
chatActTable.SetSelectable(true, true)
}
}).SetSelectedFunc(func(row int, column int) {
tc := chatActTable.GetCell(row, column)
tc.SetTextColor(tcell.ColorRed)
chatActTable.SetSelectable(false, false)
selected := filenames[row]
// notification := fmt.Sprintf("chat: %s; action: %s", selectedChat, tc.Text)
switch tc.Text {
case "load":
if err := importChat(selected); err != nil {
logger.Warn("failed to import chat", "filename", selected)
pages.RemovePage(historyPage)
return
}
colorText()
updateStatusLine()
// redraw the text in text area
textView.SetText(chatToText(cfg.ShowSys))
pages.RemovePage(historyPage)
app.SetFocus(textArea)
return
case "rename":
pages.RemovePage(historyPage)
pages.AddPage(renamePage, renameWindow, true, true)
return
case "delete":
sc, ok := chatMap[selected]
if !ok {
// no chat found
pages.RemovePage(historyPage)
return
}
if err := store.RemoveChat(sc.ID); err != nil {
logger.Error("failed to remove chat from db", "chat_id", sc.ID, "chat_name", sc.Name)
}
if err := notifyUser("chat deleted", selected+" was deleted"); err != nil {
logger.Error("failed to send notification", "error", err)
}
pages.RemovePage(historyPage)
return
default:
pages.RemovePage(historyPage)
return
}
})
return chatActTable
}

28
tui.go
View File

@@ -8,6 +8,7 @@ import (
_ "image/jpeg" _ "image/jpeg"
_ "image/png" _ "image/png"
"os" "os"
"path"
"slices" "slices"
"strconv" "strconv"
"strings" "strings"
@@ -54,7 +55,7 @@ var (
[yellow]F8[white]: copy n msg to clipboard (linux xclip) [yellow]F8[white]: copy n msg to clipboard (linux xclip)
[yellow]F9[white]: table to copy from; with all code blocks [yellow]F9[white]: table to copy from; with all code blocks
[yellow]F10[white]: manage loaded rag files (that already in vector db) [yellow]F10[white]: manage loaded rag files (that already in vector db)
[yellow]F11[white]: switch RAGEnabled boolean [yellow]F11[white]: import chat file
[yellow]F12[white]: show this help page [yellow]F12[white]: show this help page
[yellow]Ctrl+w[white]: resume generation on the last msg [yellow]Ctrl+w[white]: resume generation on the last msg
[yellow]Ctrl+s[white]: load new char/agent [yellow]Ctrl+s[white]: load new char/agent
@@ -201,7 +202,9 @@ func makePropsForm(props map[string]float32) *tview.Form {
AddTextView("Notes", "Props for llamacpp completion call", 40, 2, true, false). AddTextView("Notes", "Props for llamacpp completion call", 40, 2, true, false).
AddCheckbox("Insert <think> (/completion only)", cfg.ThinkUse, func(checked bool) { AddCheckbox("Insert <think> (/completion only)", cfg.ThinkUse, func(checked bool) {
cfg.ThinkUse = checked cfg.ThinkUse = checked
}).AddDropDown("Set log level (Enter): ", []string{"Debug", "Info", "Warn"}, 1, }).AddCheckbox("RAG use", cfg.RAGEnabled, func(checked bool) {
cfg.RAGEnabled = checked
}).AddDropDown("Set log level (Enter): ", []string{"Debug", "Info", "Warn"}, 1,
func(option string, optionIndex int) { func(option string, optionIndex int) {
setLogLevel(option) setLogLevel(option)
}).AddDropDown("Select an api: ", slices.Insert(cfg.ApiLinks, 0, cfg.CurrentAPI), 0, }).AddDropDown("Select an api: ", slices.Insert(cfg.ApiLinks, 0, cfg.CurrentAPI), 0,
@@ -555,8 +558,25 @@ func init() {
return nil return nil
} }
if event.Key() == tcell.KeyF11 { if event.Key() == tcell.KeyF11 {
// xor // read files in chat_exports
cfg.RAGEnabled = !cfg.RAGEnabled dirname := "chat_exports"
filelist, err := os.ReadDir(dirname)
if err != nil {
if err := notifyUser("failed to load exports", err.Error()); err != nil {
logger.Error("failed to send notification", "error", err)
}
}
fli := []string{}
for _, f := range filelist {
if f.IsDir() || !strings.HasSuffix(f.Name(), ".json") {
continue
}
fpath := path.Join(dirname, f.Name())
fli = append(fli, fpath)
}
// check error
exportsTable := makeImportChatTable(fli)
pages.AddPage(historyPage, exportsTable, true, true)
updateStatusLine() updateStatusLine()
return nil return nil
} }