Feat: add tools to tool map
This commit is contained in:
@@ -8,12 +8,13 @@
|
|||||||
- edit message? (including from bot); +
|
- edit message? (including from bot); +
|
||||||
- ability to copy message; +
|
- ability to copy message; +
|
||||||
- menu with old chats (chat files); +
|
- menu with old chats (chat files); +
|
||||||
- fullscreen textarea option (for long prompt);
|
|
||||||
- tab to switch selection between textview and textarea (input and chat); +
|
- tab to switch selection between textview and textarea (input and chat); +
|
||||||
- basic tools: memorize and recall;
|
- basic tools: memorize and recall;
|
||||||
- stop stream from the bot; +
|
- stop stream from the bot; +
|
||||||
- sqlitedb instead of chatfiles; +
|
- sqlitedb instead of chatfiles; +
|
||||||
|
- define tools and sys prompt for them to be used;
|
||||||
- sqlite for the bot memory;
|
- sqlite for the bot memory;
|
||||||
|
- fullscreen textarea option (bothersome to implement);
|
||||||
- option to switch between predefined sys prompts;
|
- option to switch between predefined sys prompts;
|
||||||
|
|
||||||
### FIX:
|
### FIX:
|
||||||
@@ -23,3 +24,6 @@
|
|||||||
- Tab is needed to copy paste text into textarea box, use shift+tab to switch focus; (changed tp pgup) +
|
- Tab is needed to copy paste text into textarea box, use shift+tab to switch focus; (changed tp pgup) +
|
||||||
- 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));
|
- 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));
|
||||||
- empty input to continue bot msg gens new msg index and bot icon;
|
- empty input to continue bot msg gens new msg index and bot icon;
|
||||||
|
- sometimes bots put additional info around the tool call, have a regexp to match tool call;
|
||||||
|
- remove all panics from code;
|
||||||
|
- new chat is not saved in db;
|
||||||
|
|||||||
8
bot.go
8
bot.go
@@ -33,11 +33,12 @@ var (
|
|||||||
historyDir = "./history/"
|
historyDir = "./history/"
|
||||||
// TODO: pass as an cli arg
|
// TODO: pass as an cli arg
|
||||||
showSystemMsgs bool
|
showSystemMsgs bool
|
||||||
|
chunkLimit = 1000
|
||||||
activeChatName string
|
activeChatName string
|
||||||
chunkChan = make(chan string, 10)
|
chunkChan = make(chan string, 10)
|
||||||
streamDone = make(chan bool, 1)
|
streamDone = make(chan bool, 1)
|
||||||
chatBody *models.ChatBody
|
chatBody *models.ChatBody
|
||||||
store storage.ChatHistory
|
store storage.FullRepo
|
||||||
defaultFirstMsg = "Hello! What can I do for you?"
|
defaultFirstMsg = "Hello! What can I do for you?"
|
||||||
defaultStarter = []models.MessagesStory{
|
defaultStarter = []models.MessagesStory{
|
||||||
{Role: "system", Content: systemMsg},
|
{Role: "system", Content: systemMsg},
|
||||||
@@ -89,14 +90,15 @@ func sendMsgToLLM(body io.Reader) (any, error) {
|
|||||||
break
|
break
|
||||||
}
|
}
|
||||||
llmchunk := models.LLMRespChunk{}
|
llmchunk := models.LLMRespChunk{}
|
||||||
if counter > 2000 {
|
if counter > chunkLimit {
|
||||||
|
logger.Warn("response hit chunk limit", "limit", chunkLimit)
|
||||||
streamDone <- true
|
streamDone <- true
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
line, err := reader.ReadBytes('\n')
|
line, err := reader.ReadBytes('\n')
|
||||||
if err != nil {
|
if err != nil {
|
||||||
streamDone <- true
|
streamDone <- true
|
||||||
panic(err)
|
logger.Error("error reading response body", "error", err)
|
||||||
}
|
}
|
||||||
// logger.Info("linecheck", "line", string(line), "len", len(line), "counter", counter)
|
// logger.Info("linecheck", "line", string(line), "len", len(line), "counter", counter)
|
||||||
if len(line) <= 1 {
|
if len(line) <= 1 {
|
||||||
|
|||||||
17
main.go
17
main.go
@@ -1,8 +1,9 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"elefant/models"
|
||||||
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"path"
|
|
||||||
"strconv"
|
"strconv"
|
||||||
"time"
|
"time"
|
||||||
"unicode"
|
"unicode"
|
||||||
@@ -73,8 +74,19 @@ func main() {
|
|||||||
case "new":
|
case "new":
|
||||||
// set chat body
|
// set chat body
|
||||||
chatBody.Messages = defaultStarter
|
chatBody.Messages = defaultStarter
|
||||||
|
// TODO: use predefined var since it is the same each time
|
||||||
|
msgsBytes, err := json.Marshal(chatBody.Messages)
|
||||||
|
if err != nil {
|
||||||
|
logger.Error(err.Error())
|
||||||
|
}
|
||||||
textView.SetText(chatToText(showSystemMsgs))
|
textView.SetText(chatToText(showSystemMsgs))
|
||||||
activeChatName = path.Join(historyDir, fmt.Sprintf("%d_chat.json", time.Now().Unix()))
|
newChat := &models.Chat{
|
||||||
|
Name: fmt.Sprintf("%v_%v", "new", time.Now().Unix()),
|
||||||
|
Msgs: string(msgsBytes),
|
||||||
|
}
|
||||||
|
// activeChatName = path.Join(historyDir, fmt.Sprintf("%d_chat.json", time.Now().Unix()))
|
||||||
|
activeChatName = newChat.Name
|
||||||
|
chatMap[newChat.Name] = newChat
|
||||||
pages.RemovePage("history")
|
pages.RemovePage("history")
|
||||||
return
|
return
|
||||||
// set text
|
// set text
|
||||||
@@ -141,7 +153,6 @@ func main() {
|
|||||||
editArea.SetText(m.Content, true)
|
editArea.SetText(m.Content, true)
|
||||||
}
|
}
|
||||||
if !editMode && event.Key() == tcell.KeyEnter {
|
if !editMode && event.Key() == tcell.KeyEnter {
|
||||||
// TODO: add notification that text was copied
|
|
||||||
copyToClipboard(m.Content)
|
copyToClipboard(m.Content)
|
||||||
notification := fmt.Sprintf("msg '%s' was copied to the clipboard", m.Content[:30])
|
notification := fmt.Sprintf("msg '%s' was copied to the clipboard", m.Content[:30])
|
||||||
notifyUser("copied", notification)
|
notifyUser("copied", notification)
|
||||||
|
|||||||
@@ -8,6 +8,11 @@ import (
|
|||||||
"github.com/jmoiron/sqlx"
|
"github.com/jmoiron/sqlx"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type FullRepo interface {
|
||||||
|
ChatHistory
|
||||||
|
Memories
|
||||||
|
}
|
||||||
|
|
||||||
type ChatHistory interface {
|
type ChatHistory interface {
|
||||||
ListChats() ([]models.Chat, error)
|
ListChats() ([]models.Chat, error)
|
||||||
GetChatByID(id uint32) (*models.Chat, error)
|
GetChatByID(id uint32) (*models.Chat, error)
|
||||||
@@ -61,7 +66,7 @@ func (p ProviderSQL) RemoveChat(id uint32) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewProviderSQL(dbPath string, logger *slog.Logger) ChatHistory {
|
func NewProviderSQL(dbPath string, logger *slog.Logger) FullRepo {
|
||||||
db, err := sqlx.Open("sqlite", dbPath)
|
db, err := sqlx.Open("sqlite", dbPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
|
|||||||
115
tools.go
115
tools.go
@@ -1,59 +1,144 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"elefant/models"
|
||||||
|
"encoding/json"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
// TODO: form that message based on existing funcs
|
// TODO: form that message based on existing funcs
|
||||||
|
// systemMsg = `You're a helpful assistant.
|
||||||
|
// # Tools
|
||||||
|
// You can do functions call if needed.
|
||||||
|
// Your current tools:
|
||||||
|
// <tools>
|
||||||
|
// {
|
||||||
|
// "name":"get_id",
|
||||||
|
// "args": "username"
|
||||||
|
// }
|
||||||
|
// </tools>
|
||||||
|
// To make a function call return a json object within __tool_call__ tags;
|
||||||
|
// Example:
|
||||||
|
// __tool_call__
|
||||||
|
// {
|
||||||
|
// "name":"get_id",
|
||||||
|
// "args": "Adam"
|
||||||
|
// }
|
||||||
|
// __tool_call__
|
||||||
|
// When making function call avoid typing anything else. 'tool' user will respond with the results of the call.
|
||||||
|
// After that you are free to respond to the user.
|
||||||
|
// `
|
||||||
systemMsg = `You're a helpful assistant.
|
systemMsg = `You're a helpful assistant.
|
||||||
# Tools
|
# Tools
|
||||||
You can do functions call if needed.
|
You can do functions call if needed.
|
||||||
Your current tools:
|
Your current tools:
|
||||||
<tools>
|
<tools>
|
||||||
|
[
|
||||||
{
|
{
|
||||||
"name":"get_id",
|
"name":"recall",
|
||||||
"args": "username"
|
"args": "topic",
|
||||||
|
"when_to_use": "when asked about topic that user previously asked to memorise"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"memorise",
|
||||||
|
"args": ["topic", "info"],
|
||||||
|
"when_to_use": "when asked to memorise something"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"recall_topics",
|
||||||
|
"args": null,
|
||||||
|
"when_to_use": "once in a while"
|
||||||
}
|
}
|
||||||
|
]
|
||||||
</tools>
|
</tools>
|
||||||
To make a function call return a json object within __tool_call__ tags;
|
To make a function call return a json object within __tool_call__ tags;
|
||||||
Example:
|
Example:
|
||||||
__tool_call__
|
__tool_call__
|
||||||
{
|
{
|
||||||
"name":"get_id",
|
"name":"recall",
|
||||||
"args": "Adam"
|
"args": "Adam"
|
||||||
}
|
}
|
||||||
__tool_call__
|
__tool_call__
|
||||||
When making function call avoid typing anything else. 'tool' user will respond with the results of the call.
|
When done right, tool call will be delivered to the 'tool' agent. 'tool' agent will respond with the results of the call.
|
||||||
After that you are free to respond to the user.
|
After that you are free to respond to the user.
|
||||||
`
|
`
|
||||||
)
|
)
|
||||||
|
|
||||||
func memorize(topic, info string) {
|
/*
|
||||||
//
|
consider cases:
|
||||||
|
- append mode (treat it like a journal appendix)
|
||||||
|
- replace mode (new info/mind invalidates old ones)
|
||||||
|
also:
|
||||||
|
- some writing can be done without consideration of previous data;
|
||||||
|
- others do;
|
||||||
|
*/
|
||||||
|
func memorise(args ...string) []byte {
|
||||||
|
agent := assistantRole
|
||||||
|
if len(args) < 1 {
|
||||||
|
// TODO: log
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
memory := &models.Memory{
|
||||||
|
Agent: agent,
|
||||||
|
Topic: args[0],
|
||||||
|
Mind: args[1],
|
||||||
|
UpdatedAt: time.Now(),
|
||||||
|
}
|
||||||
|
store.Memorise(memory)
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func recall(topic string) string {
|
func recall(args ...string) []byte {
|
||||||
//
|
agent := assistantRole
|
||||||
return ""
|
if len(args) < 1 {
|
||||||
|
// TODO: log
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
mind, err := store.Recall(agent, args[0])
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
return []byte(mind)
|
||||||
}
|
}
|
||||||
|
|
||||||
func recallTopics() []string {
|
func recallTopics(args ...string) []byte {
|
||||||
return []string{}
|
agent := assistantRole
|
||||||
|
topics, err := store.RecallTopics(agent)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
data, err := json.Marshal(topics)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
return data
|
||||||
}
|
}
|
||||||
|
|
||||||
func fullMemoryLoad() {}
|
func fullMemoryLoad() {}
|
||||||
|
|
||||||
// predifine funcs
|
// predifine funcs
|
||||||
func getUserDetails(id ...string) map[string]any {
|
func getUserDetails(id ...string) []byte {
|
||||||
// db query
|
// db query
|
||||||
// return DB[id[0]]
|
// return DB[id[0]]
|
||||||
return map[string]any{
|
m := map[string]any{
|
||||||
"username": "fm11",
|
"username": "fm11",
|
||||||
"id": 24983,
|
"id": 24983,
|
||||||
"reputation": 911,
|
"reputation": 911,
|
||||||
"balance": 214.73,
|
"balance": 214.73,
|
||||||
}
|
}
|
||||||
|
data, err := json.Marshal(m)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
return data
|
||||||
}
|
}
|
||||||
|
|
||||||
type fnSig func(...string) map[string]any
|
type fnSig func(...string) []byte
|
||||||
|
|
||||||
var fnMap = map[string]fnSig{
|
var fnMap = map[string]fnSig{
|
||||||
"get_id": getUserDetails,
|
"get_id": getUserDetails,
|
||||||
|
"recall": recall,
|
||||||
|
"recall_topics": recallTopics,
|
||||||
|
"memorise": memorise,
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user