2024-06-02 16:40:46 -06:00
|
|
|
package chat
|
|
|
|
|
|
|
|
import (
|
|
|
|
"time"
|
|
|
|
|
2024-06-12 02:35:07 -06:00
|
|
|
"git.mlow.ca/mlow/lmcli/pkg/api"
|
2024-10-19 20:38:42 -06:00
|
|
|
"git.mlow.ca/mlow/lmcli/pkg/conversation"
|
2024-09-15 18:48:45 -06:00
|
|
|
"git.mlow.ca/mlow/lmcli/pkg/tui/model"
|
2024-06-02 16:40:46 -06:00
|
|
|
"git.mlow.ca/mlow/lmcli/pkg/tui/shared"
|
|
|
|
tea "github.com/charmbracelet/bubbletea"
|
|
|
|
)
|
|
|
|
|
2024-09-16 08:04:08 -06:00
|
|
|
func (m *Model) waitForResponseChunk() tea.Cmd {
|
|
|
|
return func() tea.Msg {
|
|
|
|
return msgChatResponseChunk(<-m.chatReplyChunks)
|
2024-06-02 16:40:46 -06:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-06-08 15:28:29 -06:00
|
|
|
func (m *Model) loadConversationMessages() tea.Cmd {
|
2024-06-02 16:40:46 -06:00
|
|
|
return func() tea.Msg {
|
2024-09-15 18:48:45 -06:00
|
|
|
messages, err := m.App.LoadConversationMessages()
|
2024-06-02 16:40:46 -06:00
|
|
|
if err != nil {
|
2024-09-22 21:37:01 -06:00
|
|
|
return shared.AsMsgError(err)
|
2024-06-02 16:40:46 -06:00
|
|
|
}
|
2024-10-19 20:38:42 -06:00
|
|
|
return msgConversationMessagesLoaded{messages}
|
2024-06-02 16:40:46 -06:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (m *Model) generateConversationTitle() tea.Cmd {
|
|
|
|
return func() tea.Msg {
|
2024-09-15 18:48:45 -06:00
|
|
|
title, err := m.App.GenerateConversationTitle(m.App.Messages)
|
2024-06-02 16:40:46 -06:00
|
|
|
if err != nil {
|
2024-09-22 21:37:01 -06:00
|
|
|
return shared.AsMsgError(err)
|
2024-06-02 16:40:46 -06:00
|
|
|
}
|
2024-06-08 15:28:29 -06:00
|
|
|
return msgConversationTitleGenerated(title)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-10-19 20:38:42 -06:00
|
|
|
func (m *Model) updateConversationTitle(conversation *conversation.Conversation) tea.Cmd {
|
2024-06-08 15:28:29 -06:00
|
|
|
return func() tea.Msg {
|
2024-09-15 18:48:45 -06:00
|
|
|
err := m.App.UpdateConversationTitle(conversation)
|
2024-06-08 15:28:29 -06:00
|
|
|
if err != nil {
|
|
|
|
return shared.WrapError(err)
|
|
|
|
}
|
|
|
|
return nil
|
2024-06-02 16:40:46 -06:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-10-19 20:38:42 -06:00
|
|
|
func (m *Model) cloneMessage(message conversation.Message, selected bool) tea.Cmd {
|
2024-06-08 15:28:29 -06:00
|
|
|
return func() tea.Msg {
|
2024-09-15 18:48:45 -06:00
|
|
|
msg, err := m.App.CloneMessage(message, selected)
|
2024-06-08 15:28:29 -06:00
|
|
|
if err != nil {
|
2024-09-15 18:48:45 -06:00
|
|
|
return shared.WrapError(err)
|
2024-06-08 15:28:29 -06:00
|
|
|
}
|
|
|
|
return msgMessageCloned(msg)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-10-19 20:38:42 -06:00
|
|
|
func (m *Model) updateMessageContent(message *conversation.Message) tea.Cmd {
|
2024-06-08 15:28:29 -06:00
|
|
|
return func() tea.Msg {
|
2024-09-15 18:48:45 -06:00
|
|
|
err := m.App.UpdateMessageContent(message)
|
2024-06-08 15:28:29 -06:00
|
|
|
if err != nil {
|
2024-09-15 18:48:45 -06:00
|
|
|
return shared.WrapError(err)
|
2024-06-08 15:28:29 -06:00
|
|
|
}
|
|
|
|
return msgMessageUpdated(message)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-10-19 20:38:42 -06:00
|
|
|
func (m *Model) cycleSelectedRoot(conv *conversation.Conversation, dir model.MessageCycleDirection) tea.Cmd {
|
|
|
|
if len(conv.RootMessages) < 2 {
|
2024-06-08 15:28:29 -06:00
|
|
|
return nil
|
2024-06-02 16:40:46 -06:00
|
|
|
}
|
|
|
|
|
2024-06-08 15:28:29 -06:00
|
|
|
return func() tea.Msg {
|
2024-10-19 20:38:42 -06:00
|
|
|
nextRoot, err := m.App.CycleSelectedRoot(conv, dir)
|
2024-06-08 15:28:29 -06:00
|
|
|
if err != nil {
|
|
|
|
return shared.WrapError(err)
|
|
|
|
}
|
|
|
|
return msgSelectedRootCycled(nextRoot)
|
2024-06-02 16:40:46 -06:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-10-19 20:38:42 -06:00
|
|
|
func (m *Model) cycleSelectedReply(message *conversation.Message, dir model.MessageCycleDirection) tea.Cmd {
|
2024-06-02 16:40:46 -06:00
|
|
|
if len(message.Replies) < 2 {
|
2024-06-08 15:28:29 -06:00
|
|
|
return nil
|
2024-06-02 16:40:46 -06:00
|
|
|
}
|
|
|
|
|
2024-06-08 15:28:29 -06:00
|
|
|
return func() tea.Msg {
|
2024-09-15 18:48:45 -06:00
|
|
|
nextReply, err := m.App.CycleSelectedReply(message, dir)
|
2024-06-08 15:28:29 -06:00
|
|
|
if err != nil {
|
|
|
|
return shared.WrapError(err)
|
|
|
|
}
|
|
|
|
return msgSelectedReplyCycled(nextReply)
|
2024-06-02 16:40:46 -06:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-06-08 15:28:29 -06:00
|
|
|
func (m *Model) persistConversation() tea.Cmd {
|
|
|
|
return func() tea.Msg {
|
2024-09-15 18:48:45 -06:00
|
|
|
conversation, messages, err := m.App.PersistConversation(m.App.Conversation, m.App.Messages)
|
|
|
|
if err != nil {
|
2024-09-22 21:37:01 -06:00
|
|
|
return shared.AsMsgError(err)
|
2024-06-02 16:40:46 -06:00
|
|
|
}
|
2024-10-19 20:38:42 -06:00
|
|
|
return msgConversationPersisted{conversation, messages}
|
2024-06-02 16:40:46 -06:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-06-12 02:35:07 -06:00
|
|
|
func (m *Model) executeToolCalls(toolCalls []api.ToolCall) tea.Cmd {
|
|
|
|
return func() tea.Msg {
|
2024-09-15 18:48:45 -06:00
|
|
|
results, err := m.App.ExecuteToolCalls(toolCalls)
|
2024-06-12 02:35:07 -06:00
|
|
|
if err != nil {
|
2024-09-22 21:37:01 -06:00
|
|
|
return shared.AsMsgError(err)
|
2024-06-12 02:35:07 -06:00
|
|
|
}
|
|
|
|
return msgToolResults(results)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-06-02 16:40:46 -06:00
|
|
|
func (m *Model) promptLLM() tea.Cmd {
|
2024-06-08 15:58:39 -06:00
|
|
|
m.state = pendingResponse
|
2024-09-30 21:38:15 -06:00
|
|
|
m.spinner = getSpinner()
|
2024-06-02 16:40:46 -06:00
|
|
|
m.replyCursor.Blink = false
|
|
|
|
|
|
|
|
m.startTime = time.Now()
|
|
|
|
m.elapsed = 0
|
2024-06-12 02:35:07 -06:00
|
|
|
m.tokenCount = 0
|
2024-06-02 16:40:46 -06:00
|
|
|
|
2024-09-30 21:38:15 -06:00
|
|
|
return tea.Batch(
|
|
|
|
m.spinner.Tick,
|
|
|
|
func() tea.Msg {
|
|
|
|
resp, err := m.App.Prompt(m.App.Messages, m.chatReplyChunks, m.stopSignal)
|
|
|
|
if err != nil {
|
|
|
|
return msgChatResponseError{Err: err}
|
|
|
|
}
|
|
|
|
return msgChatResponse(resp)
|
|
|
|
},
|
|
|
|
)
|
2024-06-02 16:40:46 -06:00
|
|
|
}
|