summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGrail Finder <wohilas@gmail.com>2025-02-08 18:28:47 +0300
committerGrail Finder <wohilas@gmail.com>2025-02-08 18:28:47 +0300
commitc85766139371bb4324826fa8716b3478eea898c1 (patch)
tree2b58fcff3c79751a4d7e5034e035f6f270cb8bc8
parent884004a855980444319769d9b10f9cf6e3ba33cd (diff)
Feat: add tool reminder bind
-rw-r--r--README.md4
-rw-r--r--bot.go39
-rw-r--r--llm.go10
-rw-r--r--main.go2
-rw-r--r--models/models.go23
-rw-r--r--tools.go6
-rw-r--r--tui.go10
7 files changed, 33 insertions, 61 deletions
diff --git a/README.md b/README.md
index b7c8bd8..d119f5a 100644
--- a/README.md
+++ b/README.md
@@ -42,6 +42,7 @@
- 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;
- 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!";
+- multirole support?
### FIX:
- bot responding (or hanging) blocks everything; +
@@ -70,3 +71,6 @@
- add retry on failed call (and EOF);
- model info shold be an event and show disconnect status when fails;
- message editing broke ( runtime error: index out of range [-1]); out of index;
+- 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);
+- F5 broke formatting and messages somehow;
+- F4 after edit mode no colors;
diff --git a/bot.go b/bot.go
index a61128f..4e080ee 100644
--- a/bot.go
+++ b/bot.go
@@ -13,7 +13,6 @@ import (
"net/http"
"os"
"path"
- "regexp"
"strings"
"time"
@@ -44,6 +43,7 @@ var (
"min_p": 0.05,
"n_predict": -1.0,
}
+ toolUseText = "consider making a tool call."
)
func fetchModelName() *models.LLMModels {
@@ -290,35 +290,6 @@ func removeThinking(chatBody *models.ChatBody) {
chatBody.Messages = msgs
}
-// what is the purpose of this func?
-// is there a case where using text from widget is more appropriate than chatbody.messages?
-func textToMsgs(text string) []models.RoleMsg {
- lines := strings.Split(text, "\n")
- roleRE := regexp.MustCompile(`^\(\d+\) <.*>:`)
- resp := []models.RoleMsg{}
- oldrole := ""
- for _, line := range lines {
- if roleRE.MatchString(line) {
- // extract role
- role := ""
- // if role changes
- if role != oldrole {
- oldrole = role
- // newmsg
- msg := models.RoleMsg{
- Role: role,
- }
- resp = append(resp, msg)
- }
- resp[len(resp)-1].Content += "\n" + line
- }
- }
- if len(resp) != 0 {
- resp[0].Content = strings.TrimPrefix(resp[0].Content, "\n")
- }
- return resp
-}
-
func applyCharCard(cc *models.CharCard) {
cfg.AssistantRole = cc.Role
history, err := loadAgentsLastChat(cfg.AssistantRole)
@@ -355,14 +326,6 @@ func charToStart(agentName string) bool {
return true
}
-func runModelNameTicker(n time.Duration) {
- ticker := time.NewTicker(n)
- for {
- fetchModelName()
- <-ticker.C
- }
-}
-
func init() {
cfg = config.LoadConfigOrDefault("config.toml")
defaultStarter = []models.RoleMsg{
diff --git a/llm.go b/llm.go
index be7f418..a5f70bf 100644
--- a/llm.go
+++ b/llm.go
@@ -51,8 +51,11 @@ func (lcp LlamaCPPeer) FormMsg(msg, role string) (io.Reader, error) {
messages[i] = m.ToPrompt()
}
prompt := strings.Join(messages, "\n")
+ if cfg.ToolUse && msg != "" {
+ prompt += "\n" + cfg.ToolRole + ":\n" + toolSysMsg
+ }
botMsgStart := "\n" + cfg.AssistantRole + ":\n"
- payload := models.NewLCPReq(prompt+botMsgStart, role, defaultLCPProps)
+ payload := models.NewLCPReq(prompt+botMsgStart, cfg, defaultLCPProps)
data, err := json.Marshal(payload)
if err != nil {
logger.Error("failed to form a msg", "error", err)
@@ -106,6 +109,11 @@ func (op OpenAIer) FormMsg(msg, role string) (io.Reader, error) {
ragMsg := models.RoleMsg{Role: cfg.ToolRole, Content: ragResp}
chatBody.Messages = append(chatBody.Messages, ragMsg)
}
+ if cfg.ToolUse {
+ toolMsg := models.RoleMsg{Role: cfg.ToolRole,
+ Content: toolSysMsg}
+ chatBody.Messages = append(chatBody.Messages, toolMsg)
+ }
}
data, err := json.Marshal(chatBody)
if err != nil {
diff --git a/main.go b/main.go
index a95db19..df9e5ba 100644
--- a/main.go
+++ b/main.go
@@ -12,7 +12,7 @@ var (
botRespMode = false
editMode = false
selectedIndex = int(-1)
- indexLine = "F12 to show keys help; bot resp mode: %v; char: %s; chat: %s; RAGEnabled: %v; toolUseAdviced: %v; model: %s\nAPI_URL: %s"
+ indexLine = "F12 to show keys help | bot resp mode: %v (F6) | char: %s (ctrl+s) | chat: %s (F1) | RAGEnabled: %v (F11) | toolUseAdviced: %v (ctrl+k) | model: %s (ctrl+l)\nAPI_URL: %s (ctrl+v)"
focusSwitcher = map[tview.Primitive]tview.Primitive{}
)
diff --git a/models/models.go b/models/models.go
index c760569..ceb98fd 100644
--- a/models/models.go
+++ b/models/models.go
@@ -58,19 +58,9 @@ type RoleMsg struct {
func (m RoleMsg) ToText(i int, cfg *config.Config) string {
icon := fmt.Sprintf("(%d)", i)
- if !strings.HasPrefix(m.Content, cfg.UserRole+":") && !strings.HasPrefix(m.Content, cfg.AssistantRole+":") {
- switch m.Role {
- case "assistant":
- icon = fmt.Sprintf("(%d) <%s>: ", i, cfg.AssistantRole)
- case "user":
- icon = fmt.Sprintf("(%d) <%s>: ", i, cfg.UserRole)
- case "system":
- icon = fmt.Sprintf("(%d) <system>: ", i)
- case "tool":
- icon = fmt.Sprintf("(%d) <%s>: ", i, cfg.ToolRole)
- default:
- icon = fmt.Sprintf("(%d) <%s>: ", i, m.Role)
- }
+ // check if already has role annotation (/completion makes them)
+ if !strings.HasPrefix(m.Content, m.Role+":") {
+ icon = fmt.Sprintf("(%d) <%s>: ", i, m.Role)
}
textMsg := fmt.Sprintf("[-:-:b]%s[-:-:-]\n%s\n", icon, m.Content)
return strings.ReplaceAll(textMsg, "\n\n", "\n")
@@ -178,7 +168,7 @@ type LlamaCPPReq struct {
// Samplers string `json:"samplers"`
}
-func NewLCPReq(prompt, role string, props map[string]float32) LlamaCPPReq {
+func NewLCPReq(prompt string, cfg *config.Config, props map[string]float32) LlamaCPPReq {
return LlamaCPPReq{
Stream: true,
Prompt: prompt,
@@ -188,7 +178,10 @@ func NewLCPReq(prompt, role string, props map[string]float32) LlamaCPPReq {
DryMultiplier: props["dry_multiplier"],
MinP: props["min_p"],
NPredict: int32(props["n_predict"]),
- Stop: []string{role + ":\n", "<|im_end|>"},
+ Stop: []string{
+ cfg.UserRole + ":\n", "<|im_end|>",
+ cfg.ToolRole + ":\n",
+ },
}
}
diff --git a/tools.go b/tools.go
index 94c1b16..d0f7eaf 100644
--- a/tools.go
+++ b/tools.go
@@ -14,12 +14,8 @@ var (
starRE = regexp.MustCompile(`(\*.*?\*)`)
thinkRE = regexp.MustCompile(`(<think>.*?</think>)`)
codeBlockRE = regexp.MustCompile(`(?s)\x60{3}(?:.*?)\n(.*?)\n\s*\x60{3}\s*`)
- // codeBlockRE = regexp.MustCompile("```\s*([\s\S]*?)```")
- // codeBlockRE = regexp.MustCompile(`(\x60\x60\x60.*?\x60\x60\x60)`)
basicSysMsg = `Large Language Model that helps user with any of his requests.`
- toolSysMsg = `You're a helpful assistant.
-# Tools
-You can do functions call if needed.
+ toolSysMsg = `You can do functions call if needed.
Your current tools:
<tools>
[
diff --git a/tui.go b/tui.go
index 7af56d2..db7a62e 100644
--- a/tui.go
+++ b/tui.go
@@ -63,6 +63,7 @@ var (
[yellow]Ctrl+r[white]: menu of files that can be loaded in vector db (RAG)
[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)
Press Enter to go back
`
@@ -218,12 +219,13 @@ func init() {
flex = tview.NewFlex().SetDirection(tview.FlexRow).
AddItem(textView, 0, 40, false).
AddItem(textArea, 0, 10, true).
- AddItem(position, 0, 1, false)
+ AddItem(position, 0, 2, false)
editArea = tview.NewTextArea().
SetPlaceholder("Replace msg...")
editArea.SetBorder(true).SetTitle("input")
editArea.SetInputCapture(func(event *tcell.EventKey) *tcell.EventKey {
if event.Key() == tcell.KeyEscape && editMode {
+ defer colorText()
editedMsg := editArea.GetText()
if editedMsg == "" {
if err := notifyUser("edit", "no edit provided"); err != nil {
@@ -543,6 +545,12 @@ func init() {
updateStatusLine()
return nil
}
+ if event.Key() == tcell.KeyCtrlK {
+ // add message from tools
+ cfg.ToolUse = !cfg.ToolUse
+ updateStatusLine()
+ return nil
+ }
if event.Key() == tcell.KeyCtrlR && cfg.HFToken != "" {
// rag load
// menu of the text files from defined rag directory