Feat: add tool reminder bind
This commit is contained in:
@@ -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);
|
- 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;
|
- 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!";
|
- 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:
|
### FIX:
|
||||||
- bot responding (or hanging) blocks everything; +
|
- bot responding (or hanging) blocks everything; +
|
||||||
@@ -70,3 +71,6 @@
|
|||||||
- add retry on failed call (and EOF);
|
- add retry on failed call (and EOF);
|
||||||
- model info shold be an event and show disconnect status when fails;
|
- 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;
|
- 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;
|
||||||
|
|||||||
39
bot.go
39
bot.go
@@ -13,7 +13,6 @@ import (
|
|||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
"path"
|
"path"
|
||||||
"regexp"
|
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
@@ -44,6 +43,7 @@ var (
|
|||||||
"min_p": 0.05,
|
"min_p": 0.05,
|
||||||
"n_predict": -1.0,
|
"n_predict": -1.0,
|
||||||
}
|
}
|
||||||
|
toolUseText = "consider making a tool call."
|
||||||
)
|
)
|
||||||
|
|
||||||
func fetchModelName() *models.LLMModels {
|
func fetchModelName() *models.LLMModels {
|
||||||
@@ -290,35 +290,6 @@ func removeThinking(chatBody *models.ChatBody) {
|
|||||||
chatBody.Messages = msgs
|
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) {
|
func applyCharCard(cc *models.CharCard) {
|
||||||
cfg.AssistantRole = cc.Role
|
cfg.AssistantRole = cc.Role
|
||||||
history, err := loadAgentsLastChat(cfg.AssistantRole)
|
history, err := loadAgentsLastChat(cfg.AssistantRole)
|
||||||
@@ -355,14 +326,6 @@ func charToStart(agentName string) bool {
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
func runModelNameTicker(n time.Duration) {
|
|
||||||
ticker := time.NewTicker(n)
|
|
||||||
for {
|
|
||||||
fetchModelName()
|
|
||||||
<-ticker.C
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
cfg = config.LoadConfigOrDefault("config.toml")
|
cfg = config.LoadConfigOrDefault("config.toml")
|
||||||
defaultStarter = []models.RoleMsg{
|
defaultStarter = []models.RoleMsg{
|
||||||
|
|||||||
10
llm.go
10
llm.go
@@ -51,8 +51,11 @@ func (lcp LlamaCPPeer) FormMsg(msg, role string) (io.Reader, error) {
|
|||||||
messages[i] = m.ToPrompt()
|
messages[i] = m.ToPrompt()
|
||||||
}
|
}
|
||||||
prompt := strings.Join(messages, "\n")
|
prompt := strings.Join(messages, "\n")
|
||||||
|
if cfg.ToolUse && msg != "" {
|
||||||
|
prompt += "\n" + cfg.ToolRole + ":\n" + toolSysMsg
|
||||||
|
}
|
||||||
botMsgStart := "\n" + cfg.AssistantRole + ":\n"
|
botMsgStart := "\n" + cfg.AssistantRole + ":\n"
|
||||||
payload := models.NewLCPReq(prompt+botMsgStart, role, defaultLCPProps)
|
payload := models.NewLCPReq(prompt+botMsgStart, cfg, defaultLCPProps)
|
||||||
data, err := json.Marshal(payload)
|
data, err := json.Marshal(payload)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.Error("failed to form a msg", "error", err)
|
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}
|
ragMsg := models.RoleMsg{Role: cfg.ToolRole, Content: ragResp}
|
||||||
chatBody.Messages = append(chatBody.Messages, ragMsg)
|
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)
|
data, err := json.Marshal(chatBody)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
2
main.go
2
main.go
@@ -12,7 +12,7 @@ var (
|
|||||||
botRespMode = false
|
botRespMode = false
|
||||||
editMode = false
|
editMode = false
|
||||||
selectedIndex = int(-1)
|
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{}
|
focusSwitcher = map[tview.Primitive]tview.Primitive{}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -58,19 +58,9 @@ type RoleMsg struct {
|
|||||||
|
|
||||||
func (m RoleMsg) ToText(i int, cfg *config.Config) string {
|
func (m RoleMsg) ToText(i int, cfg *config.Config) string {
|
||||||
icon := fmt.Sprintf("(%d)", i)
|
icon := fmt.Sprintf("(%d)", i)
|
||||||
if !strings.HasPrefix(m.Content, cfg.UserRole+":") && !strings.HasPrefix(m.Content, cfg.AssistantRole+":") {
|
// check if already has role annotation (/completion makes them)
|
||||||
switch m.Role {
|
if !strings.HasPrefix(m.Content, m.Role+":") {
|
||||||
case "assistant":
|
icon = fmt.Sprintf("(%d) <%s>: ", i, m.Role)
|
||||||
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)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
textMsg := fmt.Sprintf("[-:-:b]%s[-:-:-]\n%s\n", icon, m.Content)
|
textMsg := fmt.Sprintf("[-:-:b]%s[-:-:-]\n%s\n", icon, m.Content)
|
||||||
return strings.ReplaceAll(textMsg, "\n\n", "\n")
|
return strings.ReplaceAll(textMsg, "\n\n", "\n")
|
||||||
@@ -178,7 +168,7 @@ type LlamaCPPReq struct {
|
|||||||
// Samplers string `json:"samplers"`
|
// 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{
|
return LlamaCPPReq{
|
||||||
Stream: true,
|
Stream: true,
|
||||||
Prompt: prompt,
|
Prompt: prompt,
|
||||||
@@ -188,7 +178,10 @@ func NewLCPReq(prompt, role string, props map[string]float32) LlamaCPPReq {
|
|||||||
DryMultiplier: props["dry_multiplier"],
|
DryMultiplier: props["dry_multiplier"],
|
||||||
MinP: props["min_p"],
|
MinP: props["min_p"],
|
||||||
NPredict: int32(props["n_predict"]),
|
NPredict: int32(props["n_predict"]),
|
||||||
Stop: []string{role + ":\n", "<|im_end|>"},
|
Stop: []string{
|
||||||
|
cfg.UserRole + ":\n", "<|im_end|>",
|
||||||
|
cfg.ToolRole + ":\n",
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
6
tools.go
6
tools.go
@@ -14,12 +14,8 @@ var (
|
|||||||
starRE = regexp.MustCompile(`(\*.*?\*)`)
|
starRE = regexp.MustCompile(`(\*.*?\*)`)
|
||||||
thinkRE = regexp.MustCompile(`(<think>.*?</think>)`)
|
thinkRE = regexp.MustCompile(`(<think>.*?</think>)`)
|
||||||
codeBlockRE = regexp.MustCompile(`(?s)\x60{3}(?:.*?)\n(.*?)\n\s*\x60{3}\s*`)
|
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.`
|
basicSysMsg = `Large Language Model that helps user with any of his requests.`
|
||||||
toolSysMsg = `You're a helpful assistant.
|
toolSysMsg = `You can do functions call if needed.
|
||||||
# Tools
|
|
||||||
You can do functions call if needed.
|
|
||||||
Your current tools:
|
Your current tools:
|
||||||
<tools>
|
<tools>
|
||||||
[
|
[
|
||||||
|
|||||||
10
tui.go
10
tui.go
@@ -63,6 +63,7 @@ var (
|
|||||||
[yellow]Ctrl+r[white]: menu of files that can be loaded in vector db (RAG)
|
[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+t[white]: remove thinking (<think>) and tool messages from context (delete from chat)
|
||||||
[yellow]Ctrl+l[white]: update connected model name (llamacpp)
|
[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
|
Press Enter to go back
|
||||||
`
|
`
|
||||||
@@ -218,12 +219,13 @@ func init() {
|
|||||||
flex = tview.NewFlex().SetDirection(tview.FlexRow).
|
flex = tview.NewFlex().SetDirection(tview.FlexRow).
|
||||||
AddItem(textView, 0, 40, false).
|
AddItem(textView, 0, 40, false).
|
||||||
AddItem(textArea, 0, 10, true).
|
AddItem(textArea, 0, 10, true).
|
||||||
AddItem(position, 0, 1, false)
|
AddItem(position, 0, 2, false)
|
||||||
editArea = tview.NewTextArea().
|
editArea = tview.NewTextArea().
|
||||||
SetPlaceholder("Replace msg...")
|
SetPlaceholder("Replace msg...")
|
||||||
editArea.SetBorder(true).SetTitle("input")
|
editArea.SetBorder(true).SetTitle("input")
|
||||||
editArea.SetInputCapture(func(event *tcell.EventKey) *tcell.EventKey {
|
editArea.SetInputCapture(func(event *tcell.EventKey) *tcell.EventKey {
|
||||||
if event.Key() == tcell.KeyEscape && editMode {
|
if event.Key() == tcell.KeyEscape && editMode {
|
||||||
|
defer colorText()
|
||||||
editedMsg := editArea.GetText()
|
editedMsg := editArea.GetText()
|
||||||
if editedMsg == "" {
|
if editedMsg == "" {
|
||||||
if err := notifyUser("edit", "no edit provided"); err != nil {
|
if err := notifyUser("edit", "no edit provided"); err != nil {
|
||||||
@@ -543,6 +545,12 @@ func init() {
|
|||||||
updateStatusLine()
|
updateStatusLine()
|
||||||
return nil
|
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 != "" {
|
if event.Key() == tcell.KeyCtrlR && cfg.HFToken != "" {
|
||||||
// rag load
|
// rag load
|
||||||
// menu of the text files from defined rag directory
|
// menu of the text files from defined rag directory
|
||||||
|
|||||||
Reference in New Issue
Block a user