Enha: id for card map
This commit is contained in:
4
bot.go
4
bot.go
@@ -1382,8 +1382,8 @@ func applyCharCard(cc *models.CharCard, loadHistory bool) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func charToStart(agentName string, keepSysP bool) bool {
|
func charToStart(agentName string, keepSysP bool) bool {
|
||||||
cc, ok := sysMap[agentName]
|
cc := GetCardByRole(agentName)
|
||||||
if !ok {
|
if cc == nil {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
applyCharCard(cc, keepSysP)
|
applyCharCard(cc, keepSysP)
|
||||||
|
|||||||
20
helpfuncs.go
20
helpfuncs.go
@@ -198,7 +198,11 @@ func initSysCards() ([]string, error) {
|
|||||||
logger.Warn("empty role", "file", cc.FilePath)
|
logger.Warn("empty role", "file", cc.FilePath)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
sysMap[cc.Role] = cc
|
if cc.ID == "" {
|
||||||
|
cc.ID = models.ComputeCardID(cc.Role, cc.FilePath)
|
||||||
|
}
|
||||||
|
sysMap[cc.ID] = cc
|
||||||
|
roleToID[cc.Role] = cc.ID
|
||||||
labels = append(labels, cc.Role)
|
labels = append(labels, cc.Role)
|
||||||
}
|
}
|
||||||
return labels, nil
|
return labels, nil
|
||||||
@@ -289,8 +293,8 @@ func listRolesWithUser() []string {
|
|||||||
|
|
||||||
func loadImage() {
|
func loadImage() {
|
||||||
filepath := defaultImage
|
filepath := defaultImage
|
||||||
cc, ok := sysMap[cfg.AssistantRole]
|
cc := GetCardByRole(cfg.AssistantRole)
|
||||||
if ok {
|
if cc != nil {
|
||||||
if strings.HasSuffix(cc.FilePath, ".png") {
|
if strings.HasSuffix(cc.FilePath, ".png") {
|
||||||
filepath = cc.FilePath
|
filepath = cc.FilePath
|
||||||
}
|
}
|
||||||
@@ -468,13 +472,9 @@ func listChatRoles() []string {
|
|||||||
if !ok {
|
if !ok {
|
||||||
return cbc
|
return cbc
|
||||||
}
|
}
|
||||||
currentCard, ok := sysMap[currentChat.Agent]
|
currentCard := GetCardByRole(currentChat.Agent)
|
||||||
if !ok {
|
if currentCard == nil {
|
||||||
// case which won't let to switch roles:
|
logger.Warn("failed to find current card", "agent", currentChat.Agent)
|
||||||
// started new chat (basic_sys or any other), at the start it yet be saved or have chatbody
|
|
||||||
// if it does not have a card or chars, it'll return an empty slice
|
|
||||||
// log error
|
|
||||||
logger.Warn("failed to find current card in sysMap", "agent", currentChat.Agent, "sysMap", sysMap)
|
|
||||||
return cbc
|
return cbc
|
||||||
}
|
}
|
||||||
charset := []string{}
|
charset := []string{}
|
||||||
|
|||||||
@@ -1,6 +1,10 @@
|
|||||||
package models
|
package models
|
||||||
|
|
||||||
import "strings"
|
import (
|
||||||
|
"crypto/md5"
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
// https://github.com/malfoyslastname/character-card-spec-v2/blob/main/spec_v2.md
|
// https://github.com/malfoyslastname/character-card-spec-v2/blob/main/spec_v2.md
|
||||||
// what a bloat; trim to Role->Msg pair and first msg
|
// what a bloat; trim to Role->Msg pair and first msg
|
||||||
@@ -31,6 +35,7 @@ func (c *CharCardSpec) Simplify(userName, fpath string) *CharCard {
|
|||||||
fm := strings.ReplaceAll(strings.ReplaceAll(c.FirstMes, "{{char}}", c.Name), "{{user}}", userName)
|
fm := strings.ReplaceAll(strings.ReplaceAll(c.FirstMes, "{{char}}", c.Name), "{{user}}", userName)
|
||||||
sysPr := strings.ReplaceAll(strings.ReplaceAll(c.Description, "{{char}}", c.Name), "{{user}}", userName)
|
sysPr := strings.ReplaceAll(strings.ReplaceAll(c.Description, "{{char}}", c.Name), "{{user}}", userName)
|
||||||
return &CharCard{
|
return &CharCard{
|
||||||
|
ID: ComputeCardID(c.Name, fpath),
|
||||||
SysPrompt: sysPr,
|
SysPrompt: sysPr,
|
||||||
FirstMsg: fm,
|
FirstMsg: fm,
|
||||||
Role: c.Name,
|
Role: c.Name,
|
||||||
@@ -39,7 +44,12 @@ func (c *CharCardSpec) Simplify(userName, fpath string) *CharCard {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func ComputeCardID(role, filePath string) string {
|
||||||
|
return fmt.Sprintf("%x", md5.Sum([]byte(role+filePath)))
|
||||||
|
}
|
||||||
|
|
||||||
type CharCard struct {
|
type CharCard struct {
|
||||||
|
ID string `json:"id"`
|
||||||
SysPrompt string `json:"sys_prompt"`
|
SysPrompt string `json:"sys_prompt"`
|
||||||
FirstMsg string `json:"first_msg"`
|
FirstMsg string `json:"first_msg"`
|
||||||
Role string `json:"role"`
|
Role string `json:"role"`
|
||||||
|
|||||||
@@ -109,6 +109,12 @@ func ReadCardJson(fname string) (*models.CharCard, error) {
|
|||||||
if err := json.Unmarshal(data, &card); err != nil {
|
if err := json.Unmarshal(data, &card); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
if card.FilePath == "" {
|
||||||
|
card.FilePath = fname
|
||||||
|
}
|
||||||
|
if card.ID == "" {
|
||||||
|
card.ID = models.ComputeCardID(card.Role, card.FilePath)
|
||||||
|
}
|
||||||
return &card, nil
|
return &card, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
33
tables.go
33
tables.go
@@ -159,27 +159,18 @@ func makeChatTable(chatMap map[string]models.Chat) *tview.Table {
|
|||||||
// save updated card
|
// save updated card
|
||||||
fi := strings.Index(selectedChat, "_")
|
fi := strings.Index(selectedChat, "_")
|
||||||
agentName := selectedChat[fi+1:]
|
agentName := selectedChat[fi+1:]
|
||||||
cc, ok := sysMap[agentName]
|
cc := GetCardByRole(agentName)
|
||||||
if !ok {
|
if cc == nil {
|
||||||
logger.Warn("no such card", "agent", agentName)
|
logger.Warn("no such card", "agent", agentName)
|
||||||
//no:lint
|
|
||||||
if err := notifyUser("error", "no such card: "+agentName); err != nil {
|
if err := notifyUser("error", "no such card: "+agentName); err != nil {
|
||||||
logger.Warn("failed ot notify", "error", err)
|
logger.Warn("failed ot notify", "error", err)
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
// if chatBody.Messages[0].Role != "system" || chatBody.Messages[1].Role != agentName {
|
|
||||||
// if err := notifyUser("error", "unexpected chat structure; card: "+agentName); err != nil {
|
|
||||||
// logger.Warn("failed ot notify", "error", err)
|
|
||||||
// }
|
|
||||||
// return
|
|
||||||
// }
|
|
||||||
// change sys_prompt + first msg
|
|
||||||
cc.SysPrompt = chatBody.Messages[0].Content
|
cc.SysPrompt = chatBody.Messages[0].Content
|
||||||
cc.FirstMsg = chatBody.Messages[1].Content
|
cc.FirstMsg = chatBody.Messages[1].Content
|
||||||
if err := pngmeta.WriteToPng(cc.ToSpec(cfg.UserRole), cc.FilePath, cc.FilePath); err != nil {
|
if err := pngmeta.WriteToPng(cc.ToSpec(cfg.UserRole), cc.FilePath, cc.FilePath); err != nil {
|
||||||
logger.Error("failed to write charcard",
|
logger.Error("failed to write charcard", "error", err)
|
||||||
"error", err)
|
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
case "move sysprompt onto 1st msg":
|
case "move sysprompt onto 1st msg":
|
||||||
@@ -190,18 +181,16 @@ func makeChatTable(chatMap map[string]models.Chat) *tview.Table {
|
|||||||
pages.RemovePage(historyPage)
|
pages.RemovePage(historyPage)
|
||||||
return
|
return
|
||||||
case "new_chat_from_card":
|
case "new_chat_from_card":
|
||||||
// Reread card from file and start fresh chat
|
|
||||||
fi := strings.Index(selectedChat, "_")
|
fi := strings.Index(selectedChat, "_")
|
||||||
agentName := selectedChat[fi+1:]
|
agentName := selectedChat[fi+1:]
|
||||||
cc, ok := sysMap[agentName]
|
cc := GetCardByRole(agentName)
|
||||||
if !ok {
|
if cc == nil {
|
||||||
logger.Warn("no such card", "agent", agentName)
|
logger.Warn("no such card", "agent", agentName)
|
||||||
if err := notifyUser("error", "no such card: "+agentName); err != nil {
|
if err := notifyUser("error", "no such card: "+agentName); err != nil {
|
||||||
logger.Warn("failed to notify", "error", err)
|
logger.Warn("failed to notify", "error", err)
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
// Reload card from disk
|
|
||||||
newCard, err := pngmeta.ReadCard(cc.FilePath, cfg.UserRole)
|
newCard, err := pngmeta.ReadCard(cc.FilePath, cfg.UserRole)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.Error("failed to reload charcard", "path", cc.FilePath, "error", err)
|
logger.Error("failed to reload charcard", "path", cc.FilePath, "error", err)
|
||||||
@@ -214,9 +203,11 @@ func makeChatTable(chatMap map[string]models.Chat) *tview.Table {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Update sysMap with fresh card data
|
if newCard.ID == "" {
|
||||||
sysMap[agentName] = newCard
|
newCard.ID = models.ComputeCardID(newCard.Role, newCard.FilePath)
|
||||||
// fetching sysprompt and first message anew from the card
|
}
|
||||||
|
sysMap[newCard.ID] = newCard
|
||||||
|
roleToID[newCard.Role] = newCard.ID
|
||||||
startNewChat(false)
|
startNewChat(false)
|
||||||
pages.RemovePage(historyPage)
|
pages.RemovePage(historyPage)
|
||||||
return
|
return
|
||||||
@@ -529,8 +520,8 @@ func makeAgentTable(agentList []string) *tview.Table {
|
|||||||
SetSelectable(false))
|
SetSelectable(false))
|
||||||
case 1:
|
case 1:
|
||||||
if actions[c-1] == "filepath" {
|
if actions[c-1] == "filepath" {
|
||||||
cc, ok := sysMap[agentList[r]]
|
cc := GetCardByRole(agentList[r])
|
||||||
if !ok {
|
if cc == nil {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
chatActTable.SetCell(r, c,
|
chatActTable.SetCell(r, c,
|
||||||
|
|||||||
20
tools.go
20
tools.go
@@ -162,13 +162,15 @@ After that you are free to respond to the user.
|
|||||||
readURLSysPrompt = `Extract and summarize the content from the webpage. Provide key information, main points, and any relevant details.`
|
readURLSysPrompt = `Extract and summarize the content from the webpage. Provide key information, main points, and any relevant details.`
|
||||||
summarySysPrompt = `Please provide a concise summary of the following conversation. Focus on key points, decisions, and actions. Provide only the summary, no additional commentary.`
|
summarySysPrompt = `Please provide a concise summary of the following conversation. Focus on key points, decisions, and actions. Provide only the summary, no additional commentary.`
|
||||||
basicCard = &models.CharCard{
|
basicCard = &models.CharCard{
|
||||||
|
ID: models.ComputeCardID("assistant", "basic_sys"),
|
||||||
SysPrompt: basicSysMsg,
|
SysPrompt: basicSysMsg,
|
||||||
FirstMsg: defaultFirstMsg,
|
FirstMsg: defaultFirstMsg,
|
||||||
Role: "",
|
Role: "assistant",
|
||||||
FilePath: "",
|
FilePath: "basic_sys",
|
||||||
}
|
}
|
||||||
sysMap = map[string]*models.CharCard{"basic_sys": basicCard}
|
sysMap = map[string]*models.CharCard{}
|
||||||
sysLabels = []string{"basic_sys"}
|
roleToID = map[string]string{}
|
||||||
|
sysLabels = []string{"assistant"}
|
||||||
|
|
||||||
webAgentClient *agent.AgentClient
|
webAgentClient *agent.AgentClient
|
||||||
webAgentClientOnce sync.Once
|
webAgentClientOnce sync.Once
|
||||||
@@ -206,6 +208,8 @@ var (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
|
sysMap[basicCard.ID] = basicCard
|
||||||
|
roleToID["assistant"] = basicCard.ID
|
||||||
sa, err := searcher.NewWebSurfer(searcher.SearcherTypeScraper, "")
|
sa, err := searcher.NewWebSurfer(searcher.SearcherTypeScraper, "")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic("failed to init seachagent; error: " + err.Error())
|
panic("failed to init seachagent; error: " + err.Error())
|
||||||
@@ -218,6 +222,14 @@ func init() {
|
|||||||
registerWindowTools()
|
registerWindowTools()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func GetCardByRole(role string) *models.CharCard {
|
||||||
|
cardID, ok := roleToID[role]
|
||||||
|
if !ok {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return sysMap[cardID]
|
||||||
|
}
|
||||||
|
|
||||||
func checkWindowTools() {
|
func checkWindowTools() {
|
||||||
xdotoolPath, _ = exec.LookPath("xdotool")
|
xdotoolPath, _ = exec.LookPath("xdotool")
|
||||||
maimPath, _ = exec.LookPath("maim")
|
maimPath, _ = exec.LookPath("maim")
|
||||||
|
|||||||
Reference in New Issue
Block a user