Feat: read from chat files
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -2,3 +2,4 @@
|
|||||||
*.json
|
*.json
|
||||||
testlog
|
testlog
|
||||||
elefant
|
elefant
|
||||||
|
history/
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
### TODO:
|
### TODO:
|
||||||
- scrolling chat history; (somewhat works out of box);
|
- scrolling chat history; (somewhat works out of box);
|
||||||
- log errors to file;
|
- log errors to file;
|
||||||
|
- give serial id to each msg in chat to track it;
|
||||||
- regen last message;
|
- regen last message;
|
||||||
- delete last message
|
- delete last message
|
||||||
- edit message? (including from bot);
|
- edit message? (including from bot);
|
||||||
|
|||||||
117
bot.go
117
bot.go
@@ -28,9 +28,15 @@ var (
|
|||||||
assistantRole = "assistant"
|
assistantRole = "assistant"
|
||||||
toolRole = "tool"
|
toolRole = "tool"
|
||||||
assistantIcon = "<🤖>: "
|
assistantIcon = "<🤖>: "
|
||||||
|
userIcon = "<user>: "
|
||||||
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
|
||||||
|
defaultFirstMsg = "Hello! What can I do for you?"
|
||||||
|
defaultStarter = []models.MessagesStory{
|
||||||
|
{Role: "system", Content: systemMsg},
|
||||||
|
{Role: assistantRole, Content: defaultFirstMsg},
|
||||||
|
}
|
||||||
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.
|
||||||
@@ -213,30 +219,111 @@ func findCall(msg string, tv *tview.TextView) {
|
|||||||
chatRound(toolMsg, toolRole, tv)
|
chatRound(toolMsg, toolRole, tv)
|
||||||
// return func result to the llm
|
// return func result to the llm
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func findLatestChat() string {
|
||||||
|
dir := "./history/"
|
||||||
|
files, err := os.ReadDir(dir)
|
||||||
|
if err != nil {
|
||||||
|
logger.Error("failed to readdir", "error", err)
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
var (
|
||||||
|
latestF string
|
||||||
|
newestTime int64
|
||||||
|
)
|
||||||
|
logger.Info("filelist", "list", files)
|
||||||
|
for _, f := range files {
|
||||||
|
fi, err := os.Stat(dir + f.Name())
|
||||||
|
if err != nil {
|
||||||
|
logger.Error("failed to get stat", "error", err, "name", f.Name())
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
currTime := fi.ModTime().Unix()
|
||||||
|
if currTime > newestTime {
|
||||||
|
newestTime = currTime
|
||||||
|
latestF = f.Name()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return latestF
|
||||||
|
}
|
||||||
|
|
||||||
|
func readHistoryChat(fn string) ([]models.MessagesStory, error) {
|
||||||
|
content, err := os.ReadFile(fn)
|
||||||
|
if err != nil {
|
||||||
|
logger.Error("failed to read file", "error", err, "name", fn)
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
resp := []models.MessagesStory{}
|
||||||
|
if err := json.Unmarshal(content, &resp); err != nil {
|
||||||
|
logger.Error("failed to unmarshal", "error", err, "name", fn)
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return resp, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func loadOldChatOrGetNew(fns ...string) []models.MessagesStory {
|
||||||
|
// find last chat
|
||||||
|
fn := findLatestChat()
|
||||||
|
if len(fns) > 0 {
|
||||||
|
fn = fns[0]
|
||||||
|
}
|
||||||
|
logger.Info("reading history from file", "filename", fn)
|
||||||
|
history, err := readHistoryChat(fn)
|
||||||
|
if err != nil {
|
||||||
|
logger.Warn("faield to load history chat", "error", err)
|
||||||
|
return defaultStarter
|
||||||
|
}
|
||||||
|
return history
|
||||||
|
}
|
||||||
|
|
||||||
|
func chatToText() []string {
|
||||||
|
resp := make([]string, len(chatBody.Messages))
|
||||||
|
for i, msg := range chatBody.Messages {
|
||||||
|
resp[i] = msg.ToText()
|
||||||
|
}
|
||||||
|
return resp
|
||||||
|
}
|
||||||
|
|
||||||
|
func textToChat(chat []string) []models.MessagesStory {
|
||||||
|
resp := make([]models.MessagesStory, len(chat))
|
||||||
|
for i, rawMsg := range chat {
|
||||||
|
// trim icon
|
||||||
|
var (
|
||||||
|
role string
|
||||||
|
msg string
|
||||||
|
)
|
||||||
|
// system and tool?
|
||||||
|
if strings.HasPrefix(rawMsg, assistantIcon) {
|
||||||
|
role = assistantRole
|
||||||
|
msg = strings.TrimPrefix(rawMsg, assistantIcon)
|
||||||
|
goto messagebuild
|
||||||
|
}
|
||||||
|
if strings.HasPrefix(rawMsg, userIcon) {
|
||||||
|
role = assistantRole
|
||||||
|
msg = strings.TrimPrefix(rawMsg, userIcon)
|
||||||
|
goto messagebuild
|
||||||
|
}
|
||||||
|
messagebuild:
|
||||||
|
resp[i].Role = role
|
||||||
|
resp[i].Content = msg
|
||||||
|
}
|
||||||
|
return resp
|
||||||
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
file, err := os.OpenFile("log.txt", os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
|
file, err := os.OpenFile("log.txt", os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
defer file.Close()
|
// defer file.Close()
|
||||||
logger = slog.New(slog.NewTextHandler(file, &slog.HandlerOptions{}))
|
logger = slog.New(slog.NewTextHandler(file, nil))
|
||||||
logger.Info("test msg")
|
logger.Info("test msg")
|
||||||
firstMsg := "Hello! What can I do for you?"
|
|
||||||
// fm, err := fillTempl("chatml", chatml)
|
|
||||||
// if err != nil {
|
|
||||||
// panic(err)
|
|
||||||
// }
|
|
||||||
// https://github.com/coreydaley/ggerganov-llama.cpp/blob/master/examples/server/README.md
|
// https://github.com/coreydaley/ggerganov-llama.cpp/blob/master/examples/server/README.md
|
||||||
|
lastChat := loadOldChatOrGetNew()
|
||||||
|
logger.Info("loaded history", "chat", lastChat)
|
||||||
chatBody = &models.ChatBody{
|
chatBody = &models.ChatBody{
|
||||||
Model: "modl_name",
|
Model: "modl_name",
|
||||||
Stream: true,
|
Stream: true,
|
||||||
Messages: []models.MessagesStory{
|
Messages: lastChat,
|
||||||
{Role: "system", Content: systemMsg},
|
|
||||||
{Role: assistantRole, Content: firstMsg},
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
// fmt.Printf("<🤖>: Hello! How can I help?")
|
|
||||||
// for {
|
|
||||||
// chatLoop()
|
|
||||||
// }
|
|
||||||
}
|
}
|
||||||
|
|||||||
10
main.go
10
main.go
@@ -2,6 +2,7 @@ package main
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"strings"
|
||||||
"unicode"
|
"unicode"
|
||||||
|
|
||||||
"github.com/gdamore/tcell/v2"
|
"github.com/gdamore/tcell/v2"
|
||||||
@@ -53,7 +54,14 @@ func main() {
|
|||||||
}
|
}
|
||||||
textArea.SetMovedFunc(updateStatusLine)
|
textArea.SetMovedFunc(updateStatusLine)
|
||||||
updateStatusLine()
|
updateStatusLine()
|
||||||
textView.SetText("<🤖>: Hello! What can I do for you?")
|
history := chatToText()
|
||||||
|
chatHistory := strings.Builder{}
|
||||||
|
for _, m := range history {
|
||||||
|
chatHistory.WriteString(m)
|
||||||
|
// textView.SetText(m)
|
||||||
|
}
|
||||||
|
textView.SetText(chatHistory.String())
|
||||||
|
// textView.SetText("<🤖>: Hello! What can I do for you?")
|
||||||
app.SetInputCapture(func(event *tcell.EventKey) *tcell.EventKey {
|
app.SetInputCapture(func(event *tcell.EventKey) *tcell.EventKey {
|
||||||
if botRespMode {
|
if botRespMode {
|
||||||
// do nothing while bot typing
|
// do nothing while bot typing
|
||||||
|
|||||||
@@ -1,5 +1,10 @@
|
|||||||
package models
|
package models
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
// type FuncCall struct {
|
// type FuncCall struct {
|
||||||
// XMLName xml.Name `xml:"tool_call"`
|
// XMLName xml.Name `xml:"tool_call"`
|
||||||
// Name string `xml:"name"`
|
// Name string `xml:"name"`
|
||||||
@@ -56,6 +61,22 @@ type MessagesStory struct {
|
|||||||
Content string `json:"content"`
|
Content string `json:"content"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (m MessagesStory) ToText() string {
|
||||||
|
icon := ""
|
||||||
|
switch m.Role {
|
||||||
|
case "assistant":
|
||||||
|
icon = "<🤖>: "
|
||||||
|
case "user":
|
||||||
|
icon = "<user>: "
|
||||||
|
case "system":
|
||||||
|
icon = "<system>: "
|
||||||
|
case "tool":
|
||||||
|
icon = "<tool>: "
|
||||||
|
}
|
||||||
|
textMsg := fmt.Sprintf("%s%s\n", icon, m.Content)
|
||||||
|
return strings.ReplaceAll(textMsg, "\n\n", "\n")
|
||||||
|
}
|
||||||
|
|
||||||
type ChatBody struct {
|
type ChatBody struct {
|
||||||
Model string `json:"model"`
|
Model string `json:"model"`
|
||||||
Stream bool `json:"stream"`
|
Stream bool `json:"stream"`
|
||||||
|
|||||||
Reference in New Issue
Block a user