tui: Error handling tweak
Moved errors to bottom of screen, fix infinite loop by typing errors properly
This commit is contained in:
parent
172bfc57e1
commit
3ec2675632
@ -8,7 +8,6 @@ import (
|
||||
"git.mlow.ca/mlow/lmcli/pkg/api"
|
||||
cmdutil "git.mlow.ca/mlow/lmcli/pkg/cmd/util"
|
||||
"git.mlow.ca/mlow/lmcli/pkg/lmcli"
|
||||
"git.mlow.ca/mlow/lmcli/pkg/tui/shared"
|
||||
)
|
||||
|
||||
type LoadedConversation struct {
|
||||
@ -59,7 +58,7 @@ func (m *AppModel) NewConversation() {
|
||||
func (m *AppModel) LoadConversations() (error, []LoadedConversation) {
|
||||
messages, err := m.Ctx.Store.LatestConversationMessages()
|
||||
if err != nil {
|
||||
return shared.MsgError(fmt.Errorf("Could not load conversations: %v", err)), nil
|
||||
return fmt.Errorf("Could not load conversations: %v", err), nil
|
||||
}
|
||||
|
||||
conversations := make([]LoadedConversation, len(messages))
|
||||
|
@ -32,7 +32,7 @@ type (
|
||||
// sent to a state when it is entered
|
||||
MsgViewEnter struct{}
|
||||
// sent when a recoverable error occurs (displayed to user)
|
||||
MsgError error
|
||||
MsgError struct { Err error }
|
||||
// sent when the view has handled a key input
|
||||
MsgKeyHandled tea.KeyMsg
|
||||
)
|
||||
@ -57,6 +57,10 @@ func KeyHandled(key tea.KeyMsg) tea.Cmd {
|
||||
|
||||
func WrapError(err error) tea.Cmd {
|
||||
return func() tea.Msg {
|
||||
return MsgError(err)
|
||||
return MsgError{ Err: err }
|
||||
}
|
||||
}
|
||||
|
||||
func AsMsgError(err error) MsgError {
|
||||
return MsgError{ Err: err }
|
||||
}
|
||||
|
@ -91,7 +91,8 @@ func (m *Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
||||
m.activeView = shared.View(msg)
|
||||
return m, tea.Batch(tea.WindowSize(), shared.ViewEnter())
|
||||
case shared.MsgError:
|
||||
m.errs = append(m.errs, msg)
|
||||
m.errs = append(m.errs, msg.Err)
|
||||
return m, nil
|
||||
}
|
||||
|
||||
view, cmd := m.views[m.activeView].Update(msg)
|
||||
@ -124,12 +125,12 @@ func (m *Model) View() string {
|
||||
if content != "" {
|
||||
sections = append(sections, content)
|
||||
}
|
||||
if len(errBanners) > 0 {
|
||||
sections = append(sections, lipgloss.JoinVertical(lipgloss.Left, errBanners...))
|
||||
}
|
||||
if footer != "" {
|
||||
sections = append(sections, footer)
|
||||
}
|
||||
for _, errBanner := range errBanners {
|
||||
sections = append(sections, errBanner)
|
||||
}
|
||||
return lipgloss.JoinVertical(lipgloss.Left, sections...)
|
||||
}
|
||||
|
||||
|
@ -29,7 +29,9 @@ type (
|
||||
rootMessages []api.Message
|
||||
}
|
||||
// a special case of common.MsgError that stops the response waiting animation
|
||||
msgChatResponseError error
|
||||
msgChatResponseError struct {
|
||||
Err error
|
||||
}
|
||||
// sent on each chunk received from LLM
|
||||
msgChatResponseChunk api.Chunk
|
||||
// sent on each completed reply
|
||||
|
@ -19,11 +19,11 @@ func (m *Model) loadConversationMessages() tea.Cmd {
|
||||
return func() tea.Msg {
|
||||
messages, err := m.App.LoadConversationMessages()
|
||||
if err != nil {
|
||||
return shared.MsgError(err)
|
||||
return shared.AsMsgError(err)
|
||||
}
|
||||
rootMessages, err := m.App.LoadConversationRootMessages()
|
||||
if err != nil {
|
||||
return shared.MsgError(err)
|
||||
return shared.AsMsgError(err)
|
||||
}
|
||||
return msgConversationMessagesLoaded{
|
||||
messages, rootMessages,
|
||||
@ -35,7 +35,7 @@ func (m *Model) generateConversationTitle() tea.Cmd {
|
||||
return func() tea.Msg {
|
||||
title, err := m.App.GenerateConversationTitle(m.App.Messages)
|
||||
if err != nil {
|
||||
return shared.MsgError(err)
|
||||
return shared.AsMsgError(err)
|
||||
}
|
||||
return msgConversationTitleGenerated(title)
|
||||
}
|
||||
@ -104,7 +104,7 @@ func (m *Model) persistConversation() tea.Cmd {
|
||||
return func() tea.Msg {
|
||||
conversation, messages, err := m.App.PersistConversation(m.App.Conversation, m.App.Messages)
|
||||
if err != nil {
|
||||
return shared.MsgError(err)
|
||||
return shared.AsMsgError(err)
|
||||
}
|
||||
return msgConversationPersisted{conversation.ID == 0, conversation, messages}
|
||||
}
|
||||
@ -114,7 +114,7 @@ func (m *Model) executeToolCalls(toolCalls []api.ToolCall) tea.Cmd {
|
||||
return func() tea.Msg {
|
||||
results, err := m.App.ExecuteToolCalls(toolCalls)
|
||||
if err != nil {
|
||||
return shared.MsgError(err)
|
||||
return shared.AsMsgError(err)
|
||||
}
|
||||
return msgToolResults(results)
|
||||
}
|
||||
@ -131,7 +131,7 @@ func (m *Model) promptLLM() tea.Cmd {
|
||||
return func() tea.Msg {
|
||||
resp, err := m.App.PromptLLM(m.App.Messages, m.chatReplyChunks, m.stopSignal)
|
||||
if err != nil {
|
||||
return msgChatResponseError(err)
|
||||
return msgChatResponseError{ Err: err }
|
||||
}
|
||||
return msgChatResponse(resp)
|
||||
}
|
||||
|
@ -170,7 +170,7 @@ func (m *Model) Update(msg tea.Msg) (shared.ViewModel, tea.Cmd) {
|
||||
case msgChatResponseError:
|
||||
m.state = idle
|
||||
m.updateContent()
|
||||
return m, shared.WrapError(msg)
|
||||
return m, shared.WrapError(msg.Err)
|
||||
case msgToolResults:
|
||||
last := len(m.App.Messages) - 1
|
||||
if last < 0 {
|
||||
|
@ -182,7 +182,7 @@ func (m *Model) loadConversations() tea.Cmd {
|
||||
return func() tea.Msg {
|
||||
err, conversations := m.App.LoadConversations()
|
||||
if err != nil {
|
||||
return shared.MsgError(fmt.Errorf("Could not load conversations: %v", err))
|
||||
return shared.AsMsgError(fmt.Errorf("Could not load conversations: %v", err))
|
||||
}
|
||||
return msgConversationsLoaded(conversations)
|
||||
}
|
||||
@ -192,7 +192,7 @@ func (m *Model) deleteConversation(conv api.Conversation) tea.Cmd {
|
||||
return func() tea.Msg {
|
||||
err := m.App.Ctx.Store.DeleteConversation(&conv)
|
||||
if err != nil {
|
||||
return shared.MsgError(fmt.Errorf("Could not delete conversation: %v", err))
|
||||
return shared.AsMsgError(fmt.Errorf("Could not delete conversation: %v", err))
|
||||
}
|
||||
return msgConversationDeleted{}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user