diff options
author | Grail Finder <wohilas@gmail.com> | 2025-02-11 19:41:14 +0300 |
---|---|---|
committer | Grail Finder <wohilas@gmail.com> | 2025-02-11 19:41:14 +0300 |
commit | 2b9c44eff9becf15e73c159349c5aeaa4ce68a0c (patch) | |
tree | 342d3ef3a1c8297d8a900bf682358bd826e8b561 | |
parent | f0fb6a31370024f7bd4a711856f9af21e6e322c4 (diff) |
Feat: show card's img
-rw-r--r-- | README.md | 76 | ||||
-rw-r--r-- | bot.go | 23 | ||||
-rw-r--r-- | tui.go | 65 |
3 files changed, 57 insertions, 107 deletions
@@ -1,75 +1 @@ -### TODO: -- scrolling chat history; (somewhat works out of the box); + -- log errors to file; + -- give serial id to each msg in chat to track it; (use slice index) + -- show msg id next to the msg; + -- regen last message; + -- delete last message; + -- edit message? (including from bot); + -- ability to copy message; + -- menu with old chats (chat files); + -- tab to switch selection between textview and textarea (input and chat); + -- basic tools: memorize and recall; + -- stop stream from the bot; + -- sqlitedb instead of chatfiles; + -- define tools and sys prompt for them to be used; + -- add system prompt without tools (for mistral); + -- option to switch between predefined sys prompts; + -- sqlite for the bot memory; + -- rename current chat; + -- help page with all key bindings; + -- default config file (api url, path to sysprompts, path to log, limits, etc); + -- ctrl+n to start new chat; + -- export whole chat into a json file; + -- directory with sys prompts (charcards png & json); + -- colourschemes, colours or markdown of quotes and styles; (partially done) + -- source file name to group by rag vectors; + -- RAG support|implementation; + -- delete chat option; + -- RAG file loading status/progress; + -- in chat management table add preview of the last message; + -===== /llamacpp specific (it has a different body -> interface instead of global var) + -- edit syscards; + -- consider adding use /completion of llamacpp, since openai endpoint clearly has template|format issues; + -- change temp, min-p and other params from tui; + -- DRY; + -- keybind to switch between openai and llamacpp endpoints (chat vs completion); + -======= -- separate messages that are stored and chat and send to the bot, i.e. option to omit tool calls and thinking (there might be a point where they are no longer needed in ctx); + -- option to remove <thinking> from chat history; + -- connection to a model status; (need to be tied to some event, perhaps its own shortcut even) + -- char card is the sys message, but how about giving tools to char that does not have it? + -- boolean flag to use/not use tools. I see it as a msg from a tool to an llm "Hey, it might be good idea to use me!"; + -- lets say we have two (or more) agents with the same name across multiple chats. These agents go and ask db for topics they memorised. Now they can access topics that aren't meant for them. (so memory should have an option: shareable; that indicates if that memory can be shared across chats); -- server mode: no tui but api calls with the func calling, rag, other middleware; -- multirole support? - -### FIX: -- bot responding (or hanging) blocks everything; + -- program requires history folder, but it is .gitignore; + -- at first run chat table does not exist; run migrations sql on startup; + -- Tab is needed to copy paste text into textarea box, use shift+tab to switch focus; (changed tp pgup) + -- sometimes bots put additional info around the tool call, have a regexp to match tool call; + -- remove all panics from code; + -- new chat replaces old ones in db; + -- empty input to continue bot msg gens new msg index and bot icon; + -- if option to show sys msg enabled: it show display new tool responses; + -- delete last msg: can have unexpected behavior (deletes what appears to be two messages if last bot msg was not generated (should only delete icon in that case)); + -- 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) + -- chat should contain char in it (one to many: char: []chats); + -- all page names should be vars; + -- normal case regen omits assistant icon; + -- user icon (and role?) from config is not used; + -- RAG: encode multiple sentences (~5-10) to embeddings a piece. + -- number of sentences in a batch should depend on number of words there. + -- 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); + -- after chat is deleted: load undeleted chat; + -- table selection does not work; (ctrl+m is enter, it breakes all the tables) + -- name split for llamacpp completion. user msg should end with 'bot_name:'; + -- remove icons for agents/user; use only <role>: + -- F4 after edit mode no colors; + -- sql memory upsert fails with msg="failed to insert memory" query="INSERT INTO memories (agent, topic, mind) VALUES (:agent, :topic, :mind) RETURNING *;" error="constraint failed: UNIQUE constraint failed: memories.agent, memories.topic (1555); + -- model info shold be an event and show disconnect status when fails; + -- add retry on failed call (and EOF); -- F5 broke formatting and messages somehow; +#### tui with middleware for LLM use @@ -67,34 +67,13 @@ func fetchModelName() *models.LLMModels { return &llmModel } -// func fetchProps() { -// api := "http://localhost:8080/props" -// resp, err := httpClient.Get(api) -// if err != nil { -// logger.Warn("failed to get model", "link", api, "error", err) -// return -// } -// defer resp.Body.Close() -// llmModel := models.LLMModels{} -// if err := json.NewDecoder(resp.Body).Decode(&llmModel); err != nil { -// logger.Warn("failed to decode resp", "link", api, "error", err) -// return -// } -// if resp.StatusCode != 200 { -// currentModel = "none" -// return -// } -// currentModel = path.Base(llmModel.Data[0].ID) -// updateStatusLine() -// } - // TODO: should be a part of server? func sendMsgToLLM(body io.Reader) { // nolint resp, err := httpClient.Post(cfg.CurrentAPI, "application/json", body) if err != nil { logger.Error("llamacpp api", "error", err) - if err := notifyUser("error", "apicall failed"); err != nil { + if err := notifyUser("error", "apicall failed:"+err.Error()); err != nil { logger.Error("failed to notify", "error", err) } streamDone <- true @@ -4,6 +4,9 @@ import ( "elefant/models" "elefant/pngmeta" "fmt" + "image" + _ "image/jpeg" + _ "image/png" "os" "strconv" "strings" @@ -13,16 +16,16 @@ import ( ) var ( - app *tview.Application - pages *tview.Pages - textArea *tview.TextArea - editArea *tview.TextArea - textView *tview.TextView - position *tview.TextView - helpView *tview.TextView - flex *tview.Flex - // chatActModal *tview.Modal - // sysModal *tview.Modal + app *tview.Application + pages *tview.Pages + textArea *tview.TextArea + editArea *tview.TextArea + textView *tview.TextView + position *tview.TextView + helpView *tview.TextView + flex *tview.Flex + imgView *tview.Image + defaultImage = "sysprompts/llama.png" indexPickWindow *tview.InputField renameWindow *tview.InputField // pages @@ -35,6 +38,7 @@ var ( RAGPage = "RAGPage " propsPage = "propsPage" codeBlockPage = "codeBlockPage" + imgPage = "imgPage" // help text helpText = ` [yellow]Esc[white]: send msg @@ -61,11 +65,32 @@ var ( [yellow]Ctrl+t[white]: remove thinking (<think>) and tool messages from context (delete from chat) [yellow]Ctrl+l[white]: update connected model name (llamacpp) [yellow]Ctrl+k[white]: switch tool use (recommend tool use to llm after user msg) +[yellow]Ctrl+i[white]: if chat agent is char.png will show the image; then any key to return Press Enter to go back ` ) +func loadImage() { + filepath := defaultImage + cc, ok := sysMap[cfg.AssistantRole] + if ok { + if strings.HasSuffix(cc.FilePath, ".png") { + filepath = cc.FilePath + } + } + file, err := os.Open(filepath) + if err != nil { + panic(err) + } + defer file.Close() + img, _, err := image.Decode(file) + if err != nil { + panic(err) + } + imgView.SetImage(img) +} + func colorText() { text := textView.GetText(false) // Step 1: Extract code blocks and replace them with unique placeholders @@ -341,6 +366,20 @@ func init() { return nil }) // + imgView = tview.NewImage() + imgView.SetInputCapture(func(event *tcell.EventKey) *tcell.EventKey { + switch event.Key() { + case tcell.KeyEnter: + pages.RemovePage(imgPage) + return event + } + if isASCII(string(event.Rune())) { + pages.RemovePage(imgPage) + return event + } + return nil + }) + // textArea.SetMovedFunc(updateStatusLine) updateStatusLine() textView.SetText(chatToText(cfg.ShowSys)) @@ -549,6 +588,12 @@ func init() { updateStatusLine() return nil } + if event.Key() == tcell.KeyCtrlI { + // show image + loadImage() + pages.AddPage(imgPage, imgView, true, true) + return nil + } if event.Key() == tcell.KeyCtrlR && cfg.HFToken != "" { // rag load // menu of the text files from defined rag directory |