Private
Public Access
1
0

Add LastMessageAt field to conversation

Replaced `LatestConversationMessages` with `LoadConversationList`, which
utilizes `LastMessageAt` for much faster conversation loading in the
conversation listing TUI and `lmcli list` command.
This commit is contained in:
2024-10-21 15:33:20 +00:00
parent 0384c7cb66
commit 07c96082e7
5 changed files with 88 additions and 78 deletions

View File

@@ -13,14 +13,9 @@ import (
"github.com/charmbracelet/lipgloss"
)
type LoadedConversation struct {
Conv conversation.Conversation
LastReply conversation.Message
}
type AppModel struct {
Ctx *lmcli.Context
Conversations []LoadedConversation
Conversations conversation.ConversationList
Conversation *conversation.Conversation
Messages []conversation.Message
Model string
@@ -89,22 +84,6 @@ func (m *AppModel) NewConversation() {
m.ApplySystemPrompt()
}
func (m *AppModel) LoadConversations() (error, []LoadedConversation) {
messages, err := m.Ctx.Conversations.LatestConversationMessages()
if err != nil {
return fmt.Errorf("Could not load conversations: %v", err), nil
}
conversations := make([]LoadedConversation, len(messages))
for i, msg := range messages {
conversations[i] = LoadedConversation{
Conv: *msg.Conversation,
LastReply: msg,
}
}
return nil, conversations
}
func (a *AppModel) LoadConversationMessages() ([]conversation.Message, error) {
messages, err := a.Ctx.Conversations.PathToLeaf(a.Conversation.SelectedRoot)
if err != nil {

View File

@@ -19,9 +19,9 @@ import (
type (
// sent when conversation list is loaded
msgConversationsLoaded ([]model.LoadedConversation)
// sent when a conversation is selected
msgConversationSelected conversation.Conversation
msgConversationsLoaded conversation.ConversationList
// sent when a single conversation is loaded
msgConversationLoaded *conversation.Conversation
// sent when a conversation is deleted
msgConversationDeleted struct{}
)
@@ -56,19 +56,17 @@ func (m *Model) handleInput(msg tea.KeyMsg) tea.Cmd {
}
}
conversations := m.App.Conversations.Items
switch msg.String() {
case "enter":
if len(m.App.Conversations) > 0 && m.cursor < len(m.App.Conversations) {
m.App.ClearConversation()
m.App.Conversation = &m.App.Conversations[m.cursor].Conv
return func() tea.Msg {
return shared.MsgViewChange(shared.ViewChat)
}
if len(conversations) > 0 && m.cursor < len(conversations) {
return m.loadConversation(conversations[m.cursor].ID)
}
case "j", "down":
if m.cursor < len(m.App.Conversations)-1 {
if m.cursor < len(conversations)-1 {
m.cursor++
if m.cursor == len(m.App.Conversations)-1 {
if m.cursor == len(conversations)-1 {
m.content.GotoBottom()
} else {
// this hack positions the *next* conversatoin slightly
@@ -78,7 +76,7 @@ func (m *Model) handleInput(msg tea.KeyMsg) tea.Cmd {
}
m.content.SetContent(m.renderConversationList())
} else {
m.cursor = len(m.App.Conversations) - 1
m.cursor = len(conversations) - 1
m.content.GotoBottom()
}
return shared.KeyHandled(msg)
@@ -100,14 +98,14 @@ func (m *Model) handleInput(msg tea.KeyMsg) tea.Cmd {
m.App.NewConversation()
return shared.ChangeView(shared.ViewChat)
case "d":
if !m.confirmPrompt.Focused() && len(m.App.Conversations) > 0 && m.cursor < len(m.App.Conversations) {
title := m.App.Conversations[m.cursor].Conv.Title
if !m.confirmPrompt.Focused() && len(conversations) > 0 && m.cursor < len(conversations) {
title := conversations[m.cursor].Title
if title == "" {
title = "(untitled)"
}
m.confirmPrompt = bubbles.NewConfirmPrompt(
fmt.Sprintf("Delete '%s'?", title),
m.App.Conversations[m.cursor].Conv,
conversations[m.cursor],
)
m.confirmPrompt.Style = lipgloss.NewStyle().
Bold(true).
@@ -148,9 +146,15 @@ func (m *Model) Update(msg tea.Msg) (shared.ViewModel, tea.Cmd) {
m.width, m.height = msg.Width, msg.Height
m.content.SetContent(m.renderConversationList())
case msgConversationsLoaded:
m.App.Conversations = msg
m.cursor = max(0, min(len(m.App.Conversations), m.cursor))
m.App.Conversations = conversation.ConversationList(msg)
m.cursor = max(0, min(len(m.App.Conversations.Items), m.cursor))
m.content.SetContent(m.renderConversationList())
case msgConversationLoaded:
m.App.ClearConversation()
m.App.Conversation = msg
cmds = append(cmds, func() tea.Msg {
return shared.MsgViewChange(shared.ViewChat)
})
case bubbles.MsgConfirmPromptAnswered:
m.confirmPrompt.Blur()
if msg.Value {
@@ -180,11 +184,21 @@ func (m *Model) Update(msg tea.Msg) (shared.ViewModel, tea.Cmd) {
func (m *Model) loadConversations() tea.Cmd {
return func() tea.Msg {
err, conversations := m.App.LoadConversations()
list, err := m.App.Ctx.Conversations.LoadConversationList()
if err != nil {
return shared.AsMsgError(fmt.Errorf("Could not load conversations: %v", err))
}
return msgConversationsLoaded(conversations)
return msgConversationsLoaded(list)
}
}
func (m *Model) loadConversation(conversationID uint) tea.Cmd {
return func() tea.Msg {
conversation, err := m.App.Ctx.Conversations.GetConversationByID(conversationID)
if err != nil {
return shared.AsMsgError(fmt.Errorf("Could not load conversation %d: %v", conversationID, err))
}
return msgConversationLoaded(conversation)
}
}
@@ -259,12 +273,12 @@ func (m *Model) renderConversationList() string {
sb strings.Builder
)
m.itemOffsets = make([]int, len(m.App.Conversations))
m.itemOffsets = make([]int, len(m.App.Conversations.Items))
sb.WriteRune('\n')
currentOffset += 1
for i, c := range m.App.Conversations {
lastReplyAge := now.Sub(c.LastReply.CreatedAt)
for i, c := range m.App.Conversations.Items {
lastReplyAge := now.Sub(c.LastMessageAt)
var category string
for _, g := range categories {
@@ -284,14 +298,14 @@ func (m *Model) renderConversationList() string {
}
tStyle := titleStyle
if c.Conv.Title == "" {
if c.Title == "" {
tStyle = tStyle.Inherit(untitledStyle).SetString("(untitled)")
}
if i == m.cursor {
tStyle = tStyle.Inherit(selectedStyle)
}
title := tStyle.Width(m.width - 3).PaddingLeft(2).Render(c.Conv.Title)
title := tStyle.Width(m.width - 3).PaddingLeft(2).Render(c.Title)
if i == m.cursor {
title = ">" + title[1:]
}
@@ -304,7 +318,7 @@ func (m *Model) renderConversationList() string {
))
sb.WriteString(item)
currentOffset += tuiutil.Height(item)
if i < len(m.App.Conversations)-1 {
if i < len(m.App.Conversations.Items)-1 {
sb.WriteRune('\n')
}
}