Feat: divide continue-gen and next-msg-gen
This commit is contained in:
55
bot.go
55
bot.go
@@ -83,12 +83,12 @@ func sendMsgToLLM(body io.Reader) {
|
|||||||
reader := bufio.NewReader(resp.Body)
|
reader := bufio.NewReader(resp.Body)
|
||||||
counter := uint32(0)
|
counter := uint32(0)
|
||||||
for {
|
for {
|
||||||
|
var (
|
||||||
|
answerText string
|
||||||
|
content string
|
||||||
|
stop bool
|
||||||
|
)
|
||||||
counter++
|
counter++
|
||||||
if interruptResp {
|
|
||||||
interruptResp = false
|
|
||||||
logger.Info("interrupted bot response", "chunk_counter", counter)
|
|
||||||
break
|
|
||||||
}
|
|
||||||
// to stop from spiriling in infinity read of bad bytes that happens with poor connection
|
// to stop from spiriling in infinity read of bad bytes that happens with poor connection
|
||||||
if cfg.ChunkLimit > 0 && counter > cfg.ChunkLimit {
|
if cfg.ChunkLimit > 0 && counter > cfg.ChunkLimit {
|
||||||
logger.Warn("response hit chunk limit", "limit", cfg.ChunkLimit)
|
logger.Warn("response hit chunk limit", "limit", cfg.ChunkLimit)
|
||||||
@@ -105,12 +105,15 @@ func sendMsgToLLM(body io.Reader) {
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if len(line) <= 1 {
|
if len(line) <= 1 {
|
||||||
|
if interruptResp {
|
||||||
|
goto interrupt // get unstuck from bad connection
|
||||||
|
}
|
||||||
continue // skip \n
|
continue // skip \n
|
||||||
}
|
}
|
||||||
// starts with -> data:
|
// starts with -> data:
|
||||||
line = line[6:]
|
line = line[6:]
|
||||||
logger.Debug("debugging resp", "line", string(line))
|
logger.Debug("debugging resp", "line", string(line))
|
||||||
content, stop, err := chunkParser.ParseChunk(line)
|
content, stop, err = chunkParser.ParseChunk(line)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.Error("error parsing response body", "error", err, "line", string(line), "url", cfg.CurrentAPI)
|
logger.Error("error parsing response body", "error", err, "line", string(line), "url", cfg.CurrentAPI)
|
||||||
streamDone <- true
|
streamDone <- true
|
||||||
@@ -127,8 +130,15 @@ func sendMsgToLLM(body io.Reader) {
|
|||||||
content = strings.TrimPrefix(content, " ")
|
content = strings.TrimPrefix(content, " ")
|
||||||
}
|
}
|
||||||
// bot sends way too many \n
|
// bot sends way too many \n
|
||||||
answerText := strings.ReplaceAll(content, "\n\n", "\n")
|
answerText = strings.ReplaceAll(content, "\n\n", "\n")
|
||||||
chunkChan <- answerText
|
chunkChan <- answerText
|
||||||
|
interrupt:
|
||||||
|
if interruptResp { // read bytes, so it would not get into beginning of the next req
|
||||||
|
interruptResp = false
|
||||||
|
logger.Info("interrupted bot response", "chunk_counter", counter)
|
||||||
|
streamDone <- true
|
||||||
|
break
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -173,20 +183,21 @@ func roleToIcon(role string) string {
|
|||||||
return "<" + role + ">: "
|
return "<" + role + ">: "
|
||||||
}
|
}
|
||||||
|
|
||||||
func chatRound(userMsg, role string, tv *tview.TextView, regen bool) {
|
func chatRound(userMsg, role string, tv *tview.TextView, regen, resume bool) {
|
||||||
botRespMode = true
|
botRespMode = true
|
||||||
// reader := formMsg(chatBody, userMsg, role)
|
// reader := formMsg(chatBody, userMsg, role)
|
||||||
reader, err := chunkParser.FormMsg(userMsg, role)
|
reader, err := chunkParser.FormMsg(userMsg, role, resume)
|
||||||
if reader == nil || err != nil {
|
if reader == nil || err != nil {
|
||||||
logger.Error("empty reader from msgs", "role", role, "error", err)
|
logger.Error("empty reader from msgs", "role", role, "error", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
go sendMsgToLLM(reader)
|
go sendMsgToLLM(reader)
|
||||||
// if userMsg != "" && !regen { // no need to write assistant icon since we continue old message
|
logger.Debug("looking at vars in chatRound", "msg", userMsg, "regen", regen, "resume", resume)
|
||||||
if userMsg != "" || regen {
|
// TODO: consider case where user msg is regened (not assistant one)
|
||||||
fmt.Fprintf(tv, "(%d) ", len(chatBody.Messages))
|
if !resume {
|
||||||
|
fmt.Fprintf(tv, "[-:-:b](%d) ", len(chatBody.Messages))
|
||||||
fmt.Fprint(tv, roleToIcon(cfg.AssistantRole))
|
fmt.Fprint(tv, roleToIcon(cfg.AssistantRole))
|
||||||
fmt.Fprint(tv, "\n")
|
fmt.Fprint(tv, "[-:-:-]\n")
|
||||||
if cfg.ThinkUse && !strings.Contains(cfg.CurrentAPI, "v1") {
|
if cfg.ThinkUse && !strings.Contains(cfg.CurrentAPI, "v1") {
|
||||||
// fmt.Fprint(tv, "<think>")
|
// fmt.Fprint(tv, "<think>")
|
||||||
chunkChan <- "<think>"
|
chunkChan <- "<think>"
|
||||||
@@ -197,7 +208,6 @@ out:
|
|||||||
for {
|
for {
|
||||||
select {
|
select {
|
||||||
case chunk := <-chunkChan:
|
case chunk := <-chunkChan:
|
||||||
// fmt.Printf(chunk)
|
|
||||||
fmt.Fprint(tv, chunk)
|
fmt.Fprint(tv, chunk)
|
||||||
respText.WriteString(chunk)
|
respText.WriteString(chunk)
|
||||||
tv.ScrollToEnd()
|
tv.ScrollToEnd()
|
||||||
@@ -207,10 +217,15 @@ out:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
botRespMode = false
|
botRespMode = false
|
||||||
// how can previous messages be affected?
|
// numbers in chatbody and displayed must be the same
|
||||||
chatBody.Messages = append(chatBody.Messages, models.RoleMsg{
|
if resume {
|
||||||
Role: cfg.AssistantRole, Content: respText.String(),
|
chatBody.Messages[len(chatBody.Messages)-1].Content += respText.String()
|
||||||
})
|
// lastM.Content = lastM.Content + respText.String()
|
||||||
|
} else {
|
||||||
|
chatBody.Messages = append(chatBody.Messages, models.RoleMsg{
|
||||||
|
Role: cfg.AssistantRole, Content: respText.String(),
|
||||||
|
})
|
||||||
|
}
|
||||||
colorText()
|
colorText()
|
||||||
updateStatusLine()
|
updateStatusLine()
|
||||||
// bot msg is done;
|
// bot msg is done;
|
||||||
@@ -239,12 +254,12 @@ func findCall(msg string, tv *tview.TextView) {
|
|||||||
f, ok := fnMap[fc.Name]
|
f, ok := fnMap[fc.Name]
|
||||||
if !ok {
|
if !ok {
|
||||||
m := fc.Name + "%s is not implemented"
|
m := fc.Name + "%s is not implemented"
|
||||||
chatRound(m, cfg.ToolRole, tv, false)
|
chatRound(m, cfg.ToolRole, tv, false, false)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
resp := f(fc.Args...)
|
resp := f(fc.Args...)
|
||||||
toolMsg := fmt.Sprintf("tool response: %+v", string(resp))
|
toolMsg := fmt.Sprintf("tool response: %+v", string(resp))
|
||||||
chatRound(toolMsg, cfg.ToolRole, tv, false)
|
chatRound(toolMsg, cfg.ToolRole, tv, false, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
func chatToTextSlice(showSys bool) []string {
|
func chatToTextSlice(showSys bool) []string {
|
||||||
|
|||||||
14
llm.go
14
llm.go
@@ -10,7 +10,7 @@ import (
|
|||||||
|
|
||||||
type ChunkParser interface {
|
type ChunkParser interface {
|
||||||
ParseChunk([]byte) (string, bool, error)
|
ParseChunk([]byte) (string, bool, error)
|
||||||
FormMsg(msg, role string) (io.Reader, error)
|
FormMsg(msg, role string, cont bool) (io.Reader, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
func initChunkParser() {
|
func initChunkParser() {
|
||||||
@@ -28,7 +28,7 @@ type LlamaCPPeer struct {
|
|||||||
type OpenAIer struct {
|
type OpenAIer struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (lcp LlamaCPPeer) FormMsg(msg, role string) (io.Reader, error) {
|
func (lcp LlamaCPPeer) FormMsg(msg, role string, cont bool) (io.Reader, error) {
|
||||||
if msg != "" { // otherwise let the bot continue
|
if msg != "" { // otherwise let the bot continue
|
||||||
newMsg := models.RoleMsg{Role: role, Content: msg}
|
newMsg := models.RoleMsg{Role: role, Content: msg}
|
||||||
chatBody.Messages = append(chatBody.Messages, newMsg)
|
chatBody.Messages = append(chatBody.Messages, newMsg)
|
||||||
@@ -49,11 +49,13 @@ func (lcp LlamaCPPeer) FormMsg(msg, role string) (io.Reader, error) {
|
|||||||
}
|
}
|
||||||
prompt := strings.Join(messages, "\n")
|
prompt := strings.Join(messages, "\n")
|
||||||
// strings builder?
|
// strings builder?
|
||||||
if cfg.ToolUse && msg != "" {
|
if cfg.ToolUse && msg != "" && !cont {
|
||||||
prompt += "\n" + cfg.ToolRole + ":\n" + toolSysMsg
|
prompt += "\n" + cfg.ToolRole + ":\n" + toolSysMsg
|
||||||
}
|
}
|
||||||
botMsgStart := "\n" + cfg.AssistantRole + ":\n"
|
if !cont {
|
||||||
prompt += botMsgStart
|
botMsgStart := "\n" + cfg.AssistantRole + ":\n"
|
||||||
|
prompt += botMsgStart
|
||||||
|
}
|
||||||
// if cfg.ThinkUse && msg != "" && !cfg.ToolUse {
|
// if cfg.ThinkUse && msg != "" && !cfg.ToolUse {
|
||||||
if cfg.ThinkUse && !cfg.ToolUse {
|
if cfg.ThinkUse && !cfg.ToolUse {
|
||||||
prompt += "<think>"
|
prompt += "<think>"
|
||||||
@@ -98,7 +100,7 @@ func (op OpenAIer) ParseChunk(data []byte) (string, bool, error) {
|
|||||||
return content, false, nil
|
return content, false, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (op OpenAIer) FormMsg(msg, role string) (io.Reader, error) {
|
func (op OpenAIer) FormMsg(msg, role string, resume bool) (io.Reader, error) {
|
||||||
if msg != "" { // otherwise let the bot continue
|
if msg != "" { // otherwise let the bot continue
|
||||||
newMsg := models.RoleMsg{Role: role, Content: msg}
|
newMsg := models.RoleMsg{Role: role, Content: msg}
|
||||||
chatBody.Messages = append(chatBody.Messages, newMsg)
|
chatBody.Messages = append(chatBody.Messages, newMsg)
|
||||||
|
|||||||
@@ -181,6 +181,7 @@ func NewLCPReq(prompt string, cfg *config.Config, props map[string]float32) Llam
|
|||||||
Stop: []string{
|
Stop: []string{
|
||||||
cfg.UserRole + ":\n", "<|im_end|>",
|
cfg.UserRole + ":\n", "<|im_end|>",
|
||||||
cfg.ToolRole + ":\n",
|
cfg.ToolRole + ":\n",
|
||||||
|
cfg.AssistantRole + ":\n",
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
15
tui.go
15
tui.go
@@ -55,6 +55,7 @@ var (
|
|||||||
[yellow]F10[white]: manage loaded rag files (that already in vector db)
|
[yellow]F10[white]: manage loaded rag files (that already in vector db)
|
||||||
[yellow]F11[white]: switch RAGEnabled boolean
|
[yellow]F11[white]: switch RAGEnabled boolean
|
||||||
[yellow]F12[white]: show this help page
|
[yellow]F12[white]: show this help page
|
||||||
|
[yellow]Ctrl+w[white]: resume generation on the last msg
|
||||||
[yellow]Ctrl+s[white]: load new char/agent
|
[yellow]Ctrl+s[white]: load new char/agent
|
||||||
[yellow]Ctrl+e[white]: export chat to json file
|
[yellow]Ctrl+e[white]: export chat to json file
|
||||||
[yellow]Ctrl+n[white]: start a new chat
|
[yellow]Ctrl+n[white]: start a new chat
|
||||||
@@ -450,7 +451,7 @@ func init() {
|
|||||||
// regen last msg
|
// regen last msg
|
||||||
chatBody.Messages = chatBody.Messages[:len(chatBody.Messages)-1]
|
chatBody.Messages = chatBody.Messages[:len(chatBody.Messages)-1]
|
||||||
textView.SetText(chatToText(cfg.ShowSys))
|
textView.SetText(chatToText(cfg.ShowSys))
|
||||||
go chatRound("", cfg.UserRole, textView, true)
|
go chatRound("", cfg.UserRole, textView, true, false)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
if event.Key() == tcell.KeyF3 && !botRespMode {
|
if event.Key() == tcell.KeyF3 && !botRespMode {
|
||||||
@@ -649,6 +650,13 @@ func init() {
|
|||||||
pages.AddPage(RAGPage, chatRAGTable, true, true)
|
pages.AddPage(RAGPage, chatRAGTable, true, true)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
if event.Key() == tcell.KeyCtrlW {
|
||||||
|
// INFO: continue bot/text message
|
||||||
|
// without new role
|
||||||
|
lastRole := chatBody.Messages[len(chatBody.Messages)-1].Role
|
||||||
|
go chatRound("", lastRole, textView, false, true)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
// cannot send msg in editMode or botRespMode
|
// cannot send msg in editMode or botRespMode
|
||||||
if event.Key() == tcell.KeyEscape && !editMode && !botRespMode {
|
if event.Key() == tcell.KeyEscape && !editMode && !botRespMode {
|
||||||
// read all text into buffer
|
// read all text into buffer
|
||||||
@@ -660,7 +668,8 @@ func init() {
|
|||||||
if strings.HasSuffix(prevText, nl) {
|
if strings.HasSuffix(prevText, nl) {
|
||||||
nl = ""
|
nl = ""
|
||||||
}
|
}
|
||||||
if msgText != "" { // continue
|
if msgText != "" {
|
||||||
|
// add user icon before user msg
|
||||||
fmt.Fprintf(textView, "%s[-:-:b](%d) <%s>: [-:-:-]\n%s\n",
|
fmt.Fprintf(textView, "%s[-:-:b](%d) <%s>: [-:-:-]\n%s\n",
|
||||||
nl, len(chatBody.Messages), cfg.UserRole, msgText)
|
nl, len(chatBody.Messages), cfg.UserRole, msgText)
|
||||||
textArea.SetText("", true)
|
textArea.SetText("", true)
|
||||||
@@ -668,7 +677,7 @@ func init() {
|
|||||||
colorText()
|
colorText()
|
||||||
}
|
}
|
||||||
// update statue line
|
// update statue line
|
||||||
go chatRound(msgText, cfg.UserRole, textView, false)
|
go chatRound(msgText, cfg.UserRole, textView, false, false)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
if event.Key() == tcell.KeyPgUp || event.Key() == tcell.KeyPgDn {
|
if event.Key() == tcell.KeyPgUp || event.Key() == tcell.KeyPgDn {
|
||||||
|
|||||||
Reference in New Issue
Block a user