Refactor: sql on conflict; fix unittest; page names to vars

This commit is contained in:
Grail Finder
2024-12-11 18:43:04 +03:00
parent 5f780287ae
commit ed5cb75134
5 changed files with 58 additions and 27 deletions

View File

@@ -48,6 +48,7 @@
- when bot generation ended with err: need a way to switch back to the bot_resp_false mode; + - when bot generation ended with err: need a way to switch back to the bot_resp_false mode; +
- no selection focus on modal sys buttons after opening it a second time; (cannot reproduce) + - no selection focus on modal sys buttons after opening it a second time; (cannot reproduce) +
- chat should contain char in it (one to many: char: []chats); + - chat should contain char in it (one to many: char: []chats); +
- all page names should be vars; - all page names should be vars; +
- normal case regen omits assistant icon; + - normal case regen omits assistant icon; +
- user icon (and role?) from config is not used; + - user icon (and role?) from config is not used; +
- F1 can load any chat, by loading chat of other agent it does not switch agents, if that chat is continued, it will rewrite agent in db; (either allow only chats from current agent OR switch agent on chat loading);

View File

@@ -53,11 +53,15 @@ func (p ProviderSQL) GetLastChatByAgent(agent string) (*models.Chat, error) {
return &resp, err return &resp, err
} }
// https://sqlite.org/lang_upsert.html
// on conflict was added
func (p ProviderSQL) UpsertChat(chat *models.Chat) (*models.Chat, error) { func (p ProviderSQL) UpsertChat(chat *models.Chat) (*models.Chat, error) {
// Prepare the SQL statement // Prepare the SQL statement
query := ` query := `
INSERT OR REPLACE INTO chats (id, name, msgs, agent, created_at, updated_at) INSERT INTO chats (id, name, msgs, agent, created_at, updated_at)
VALUES (:id, :name, :msgs, :agent, :created_at, :updated_at) VALUES (:id, :name, :msgs, :agent, :created_at, :updated_at)
ON CONFLICT(id) DO UPDATE SET msgs=excluded.msgs,
updated_at=excluded.updated_at
RETURNING *;` RETURNING *;`
stmt, err := p.db.PrepareNamed(query) stmt, err := p.db.PrepareNamed(query)
if err != nil { if err != nil {

View File

@@ -95,6 +95,7 @@ func TestChatHistory(t *testing.T) {
id INTEGER PRIMARY KEY AUTOINCREMENT, id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT NOT NULL, name TEXT NOT NULL,
msgs TEXT NOT NULL, msgs TEXT NOT NULL,
agent TEXT NOT NULL,
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP updated_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP
);`) );`)

View File

@@ -9,9 +9,10 @@ import (
) )
var ( var (
toolCallRE = regexp.MustCompile(`__tool_call__\s*([\s\S]*?)__tool_call__`) toolCallRE = regexp.MustCompile(`__tool_call__\s*([\s\S]*?)__tool_call__`)
quotesRE = regexp.MustCompile(`(".*?")`) quotesRE = regexp.MustCompile(`(".*?")`)
starRE = regexp.MustCompile(`(\*.*?\*)`) starRE = regexp.MustCompile(`(\*.*?\*)`)
// codeBlokRE = regexp.MustCompile(`(\x60\x60\x60.*?\x60\x60\x60)`)
basicSysMsg = `Large Language Model that helps user with any of his requests.` basicSysMsg = `Large Language Model that helps user with any of his requests.`
toolSysMsg = `You're a helpful assistant. toolSysMsg = `You're a helpful assistant.
# Tools # Tools

68
tui.go
View File

@@ -25,7 +25,15 @@ var (
sysModal *tview.Modal sysModal *tview.Modal
indexPickWindow *tview.InputField indexPickWindow *tview.InputField
renameWindow *tview.InputField renameWindow *tview.InputField
helpText = ` // pages
historyPage = "historyPage"
agentPage = "agentPage"
editMsgPage = "editMsgPage"
indexPage = "indexPage"
helpPage = "helpPage"
renamePage = "renamePage"
// help text
helpText = `
[yellow]Esc[white]: send msg [yellow]Esc[white]: send msg
[yellow]PgUp/Down[white]: switch focus [yellow]PgUp/Down[white]: switch focus
[yellow]F1[white]: manage chats [yellow]F1[white]: manage chats
@@ -39,15 +47,31 @@ var (
[yellow]Ctrl+s[white]: load new char/agent [yellow]Ctrl+s[white]: load new char/agent
[yellow]Ctrl+e[white]: export chat to json file [yellow]Ctrl+e[white]: export chat to json file
[yellow]Ctrl+n[white]: start a new chat [yellow]Ctrl+n[white]: start a new chat
[yellow]Ctrl+c[white]: close programm
Press Enter to go back Press Enter to go back
` `
) )
// // code block colors get interrupted by " & *
// func codeBlockColor(text string) string {
// fi := strings.Index(text, "```")
// if fi < 0 {
// return text
// }
// li := strings.LastIndex(text, "```")
// if li == fi { // only openning backticks
// return text
// }
// return strings.Replace(text, "```", "```[blue:black:i]", 1)
// }
func colorText() { func colorText() {
// INFO: is there a better way to markdown? // INFO: is there a better way to markdown?
tv := textView.GetText(false) tv := textView.GetText(false)
cq := quotesRE.ReplaceAllString(tv, `[orange:-:-]$1[-:-:-]`) cq := quotesRE.ReplaceAllString(tv, `[orange:-:-]$1[-:-:-]`)
// cb := codeBlockColor(cq)
// cb := codeBlockRE.ReplaceAllString(cq, `[blue:black:i]$1[-:-:-]`)
textView.SetText(starRE.ReplaceAllString(cq, `[turquoise::i]$1[-:-:-]`)) textView.SetText(starRE.ReplaceAllString(cq, `[turquoise::i]$1[-:-:-]`))
} }
@@ -143,29 +167,29 @@ func init() {
switch buttonLabel { switch buttonLabel {
case "new": case "new":
startNewChat() startNewChat()
pages.RemovePage("history") pages.RemovePage(historyPage)
return return
// set text // set text
case "cancel": case "cancel":
pages.RemovePage("history") pages.RemovePage(historyPage)
return return
case "rename current": case "rename current":
// add input field // add input field
pages.RemovePage("history") pages.RemovePage(historyPage)
pages.AddPage("renameW", renameWindow, true, true) pages.AddPage(renamePage, renameWindow, true, true)
return return
default: default:
fn := buttonLabel fn := buttonLabel
history, err := loadHistoryChat(fn) history, err := loadHistoryChat(fn)
if err != nil { if err != nil {
logger.Error("failed to read history file", "chat", fn) logger.Error("failed to read history file", "chat", fn)
pages.RemovePage("history") pages.RemovePage(historyPage)
return return
} }
chatBody.Messages = history chatBody.Messages = history
textView.SetText(chatToText(cfg.ShowSys)) textView.SetText(chatToText(cfg.ShowSys))
activeChatName = fn activeChatName = fn
pages.RemovePage("history") pages.RemovePage(historyPage)
colorText() colorText()
return return
} }
@@ -175,13 +199,13 @@ func init() {
SetDoneFunc(func(buttonIndex int, buttonLabel string) { SetDoneFunc(func(buttonIndex int, buttonLabel string) {
switch buttonLabel { switch buttonLabel {
case "cancel": case "cancel":
pages.RemovePage("sys") pages.RemovePage(agentPage)
sysModal.ClearButtons() sysModal.ClearButtons()
return return
default: default:
if ok := charToStart(buttonLabel); !ok { if ok := charToStart(buttonLabel); !ok {
logger.Warn("no such sys msg", "name", buttonLabel) logger.Warn("no such sys msg", "name", buttonLabel)
pages.RemovePage("sys") pages.RemovePage(agentPage)
return return
} }
// replace textview // replace textview
@@ -189,7 +213,7 @@ func init() {
colorText() colorText()
updateStatusLine() updateStatusLine()
sysModal.ClearButtons() sysModal.ClearButtons()
pages.RemovePage("sys") pages.RemovePage(agentPage)
app.SetFocus(textArea) app.SetFocus(textArea)
} }
}) })
@@ -203,14 +227,14 @@ func init() {
if err := notifyUser("edit", "no edit provided"); err != nil { if err := notifyUser("edit", "no edit provided"); err != nil {
logger.Error("failed to send notification", "error", err) logger.Error("failed to send notification", "error", err)
} }
pages.RemovePage("editArea") pages.RemovePage(editMsgPage)
editMode = false editMode = false
return nil return nil
} }
chatBody.Messages[selectedIndex].Content = editedMsg chatBody.Messages[selectedIndex].Content = editedMsg
// change textarea // change textarea
textView.SetText(chatToText(cfg.ShowSys)) textView.SetText(chatToText(cfg.ShowSys))
pages.RemovePage("editArea") pages.RemovePage(editMsgPage)
editMode = false editMode = false
return nil return nil
} }
@@ -221,7 +245,7 @@ func init() {
SetFieldWidth(4). SetFieldWidth(4).
SetAcceptanceFunc(tview.InputFieldInteger). SetAcceptanceFunc(tview.InputFieldInteger).
SetDoneFunc(func(key tcell.Key) { SetDoneFunc(func(key tcell.Key) {
pages.RemovePage("getIndex") pages.RemovePage(indexPage)
}) })
indexPickWindow.SetInputCapture(func(event *tcell.EventKey) *tcell.EventKey { indexPickWindow.SetInputCapture(func(event *tcell.EventKey) *tcell.EventKey {
switch event.Key() { switch event.Key() {
@@ -239,12 +263,12 @@ func init() {
if err := notifyUser("error", msg); err != nil { if err := notifyUser("error", msg); err != nil {
logger.Error("failed to send notification", "error", err) logger.Error("failed to send notification", "error", err)
} }
pages.RemovePage("getIndex") pages.RemovePage(indexPage)
return event return event
} }
m := chatBody.Messages[selectedIndex] m := chatBody.Messages[selectedIndex]
if editMode && event.Key() == tcell.KeyEnter { if editMode && event.Key() == tcell.KeyEnter {
pages.AddPage("editArea", editArea, true, true) pages.AddPage(editMsgPage, editArea, true, true)
editArea.SetText(m.Content, true) editArea.SetText(m.Content, true)
} }
if !editMode && event.Key() == tcell.KeyEnter { if !editMode && event.Key() == tcell.KeyEnter {
@@ -271,7 +295,7 @@ func init() {
SetFieldWidth(20). SetFieldWidth(20).
SetAcceptanceFunc(tview.InputFieldMaxLength(100)). SetAcceptanceFunc(tview.InputFieldMaxLength(100)).
SetDoneFunc(func(key tcell.Key) { SetDoneFunc(func(key tcell.Key) {
pages.RemovePage("renameW") pages.RemovePage(renamePage)
}) })
renameWindow.SetInputCapture(func(event *tcell.EventKey) *tcell.EventKey { renameWindow.SetInputCapture(func(event *tcell.EventKey) *tcell.EventKey {
if event.Key() == tcell.KeyEnter { if event.Key() == tcell.KeyEnter {
@@ -297,7 +321,7 @@ func init() {
}) })
// //
helpView = tview.NewTextView().SetDynamicColors(true).SetText(helpText).SetDoneFunc(func(key tcell.Key) { helpView = tview.NewTextView().SetDynamicColors(true).SetText(helpText).SetDoneFunc(func(key tcell.Key) {
pages.RemovePage("helpView") pages.RemovePage(helpPage)
}) })
helpView.SetInputCapture(func(event *tcell.EventKey) *tcell.EventKey { helpView.SetInputCapture(func(event *tcell.EventKey) *tcell.EventKey {
switch event.Key() { switch event.Key() {
@@ -326,7 +350,7 @@ func init() {
chatOpts := append(chatOpts, chatList...) chatOpts := append(chatOpts, chatList...)
chatActModal.ClearButtons() chatActModal.ClearButtons()
chatActModal.AddButtons(chatOpts) chatActModal.AddButtons(chatOpts)
pages.AddPage("history", chatActModal, true, true) pages.AddPage(historyPage, chatActModal, true, true)
return nil return nil
} }
if event.Key() == tcell.KeyF2 { if event.Key() == tcell.KeyF2 {
@@ -354,7 +378,7 @@ func init() {
if event.Key() == tcell.KeyF4 { if event.Key() == tcell.KeyF4 {
// edit msg // edit msg
editMode = true editMode = true
pages.AddPage("getIndex", indexPickWindow, true, true) pages.AddPage(indexPage, indexPickWindow, true, true)
return nil return nil
} }
if event.Key() == tcell.KeyF5 { if event.Key() == tcell.KeyF5 {
@@ -388,12 +412,12 @@ func init() {
if event.Key() == tcell.KeyF8 { if event.Key() == tcell.KeyF8 {
// copy msg to clipboard // copy msg to clipboard
editMode = false editMode = false
pages.AddPage("getIndex", indexPickWindow, true, true) pages.AddPage(indexPage, indexPickWindow, true, true)
return nil return nil
} }
if event.Key() == tcell.KeyF12 { if event.Key() == tcell.KeyF12 {
// help window cheatsheet // help window cheatsheet
pages.AddPage("helpView", helpView, true, true) pages.AddPage(helpPage, helpView, true, true)
return nil return nil
} }
if event.Key() == tcell.KeyCtrlE { if event.Key() == tcell.KeyCtrlE {
@@ -427,7 +451,7 @@ func init() {
} }
sysModal.AddButtons(labels) sysModal.AddButtons(labels)
// load all chars // load all chars
pages.AddPage("sys", sysModal, true, true) pages.AddPage(agentPage, sysModal, true, true)
updateStatusLine() updateStatusLine()
return nil return nil
} }