Add message branching
Updated the behaviour of commands: - `lmcli edit` - by default create a new branch/message branch with the edited contents - add --in-place to avoid creating a branch - no longer delete messages after the edited message - only do the edit, don't fetch a new response - `lmcli retry` - create a new branch rather than replacing old messages - add --offset to change where to retry from
This commit is contained in:
@@ -5,7 +5,6 @@ import (
|
||||
|
||||
cmdutil "git.mlow.ca/mlow/lmcli/pkg/cmd/util"
|
||||
"git.mlow.ca/mlow/lmcli/pkg/lmcli"
|
||||
"git.mlow.ca/mlow/lmcli/pkg/lmcli/model"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
@@ -28,36 +27,12 @@ func CloneCmd(ctx *lmcli.Context) *cobra.Command {
|
||||
return err
|
||||
}
|
||||
|
||||
messagesToCopy, err := ctx.Store.Messages(toClone)
|
||||
clone, messageCnt, err := ctx.Store.CloneConversation(*toClone)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Could not retrieve messages for conversation: %s", toClone.ShortName.String)
|
||||
return fmt.Errorf("Failed to clone conversation: %v", err)
|
||||
}
|
||||
|
||||
clone := &model.Conversation{
|
||||
Title: toClone.Title + " - Clone",
|
||||
}
|
||||
if err := ctx.Store.SaveConversation(clone); err != nil {
|
||||
return fmt.Errorf("Cloud not create clone: %s", err)
|
||||
}
|
||||
|
||||
var errors []error
|
||||
messageCnt := 0
|
||||
for _, message := range messagesToCopy {
|
||||
newMessage := message
|
||||
newMessage.ConversationID = clone.ID
|
||||
newMessage.ID = 0
|
||||
if err := ctx.Store.SaveMessage(&newMessage); err != nil {
|
||||
errors = append(errors, err)
|
||||
} else {
|
||||
messageCnt++
|
||||
}
|
||||
}
|
||||
|
||||
if len(errors) > 0 {
|
||||
return fmt.Errorf("Messages failed to be cloned: %v", errors)
|
||||
}
|
||||
|
||||
fmt.Printf("Cloned %d messages to: %s\n", messageCnt, clone.Title)
|
||||
fmt.Printf("Cloned %d messages to: %s - %s\n", messageCnt, clone.ShortName.String, clone.Title)
|
||||
return nil
|
||||
},
|
||||
ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
|
||||
|
||||
@@ -26,7 +26,7 @@ func ContinueCmd(ctx *lmcli.Context) *cobra.Command {
|
||||
shortName := args[0]
|
||||
conversation := cmdutil.LookupConversation(ctx, shortName)
|
||||
|
||||
messages, err := ctx.Store.Messages(conversation)
|
||||
messages, err := ctx.Store.PathToLeaf(conversation.SelectedRoot)
|
||||
if err != nil {
|
||||
return fmt.Errorf("could not retrieve conversation messages: %v", err)
|
||||
}
|
||||
|
||||
@@ -24,7 +24,7 @@ func EditCmd(ctx *lmcli.Context) *cobra.Command {
|
||||
shortName := args[0]
|
||||
conversation := cmdutil.LookupConversation(ctx, shortName)
|
||||
|
||||
messages, err := ctx.Store.Messages(conversation)
|
||||
messages, err := ctx.Store.PathToLeaf(conversation.SelectedRoot)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Could not retrieve messages for conversation: %s", conversation.Title)
|
||||
}
|
||||
@@ -39,21 +39,7 @@ func EditCmd(ctx *lmcli.Context) *cobra.Command {
|
||||
}
|
||||
|
||||
desiredIdx := len(messages) - 1 - offset
|
||||
|
||||
// walk backwards through the conversation deleting messages until and
|
||||
// including the last user message
|
||||
toRemove := []model.Message{}
|
||||
var toEdit *model.Message
|
||||
for i := len(messages) - 1; i >= 0; i-- {
|
||||
if i == desiredIdx {
|
||||
toEdit = &messages[i]
|
||||
}
|
||||
toRemove = append(toRemove, messages[i])
|
||||
messages = messages[:i]
|
||||
if toEdit != nil {
|
||||
break
|
||||
}
|
||||
}
|
||||
toEdit := messages[desiredIdx]
|
||||
|
||||
newContents := inputFromArgsOrEditor(args[1:], "# Save when finished editing\n", toEdit.Content)
|
||||
switch newContents {
|
||||
@@ -63,26 +49,38 @@ func EditCmd(ctx *lmcli.Context) *cobra.Command {
|
||||
return fmt.Errorf("No message was provided.")
|
||||
}
|
||||
|
||||
toEdit.Content = newContents
|
||||
|
||||
role, _ := cmd.Flags().GetString("role")
|
||||
if role == "" {
|
||||
role = string(toEdit.Role)
|
||||
} else if role != string(model.MessageRoleUser) && role != string(model.MessageRoleAssistant) {
|
||||
return fmt.Errorf("Invalid role specified. Please use 'user' or 'assistant'.")
|
||||
}
|
||||
|
||||
for _, message := range toRemove {
|
||||
err = ctx.Store.DeleteMessage(&message)
|
||||
if err != nil {
|
||||
lmcli.Warn("Could not delete message: %v\n", err)
|
||||
if role != "" {
|
||||
if role != string(model.MessageRoleUser) && role != string(model.MessageRoleAssistant) {
|
||||
return fmt.Errorf("Invalid role specified. Please use 'user' or 'assistant'.")
|
||||
}
|
||||
toEdit.Role = model.MessageRole(role)
|
||||
}
|
||||
|
||||
cmdutil.HandleConversationReply(ctx, conversation, true, model.Message{
|
||||
ConversationID: conversation.ID,
|
||||
Role: model.MessageRole(role),
|
||||
Content: newContents,
|
||||
})
|
||||
return nil
|
||||
// Update the message in-place
|
||||
inplace, _ := cmd.Flags().GetBool("in-place")
|
||||
if inplace {
|
||||
return ctx.Store.UpdateMessage(&toEdit)
|
||||
}
|
||||
|
||||
// Otherwise, create a branch for the edited message
|
||||
message, _, err := ctx.Store.CloneBranch(toEdit)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if desiredIdx > 0 {
|
||||
// update selected reply
|
||||
messages[desiredIdx-1].SelectedReply = message
|
||||
err = ctx.Store.UpdateMessage(&messages[desiredIdx-1])
|
||||
} else {
|
||||
// update selected root
|
||||
conversation.SelectedRoot = message
|
||||
err = ctx.Store.UpdateConversation(conversation)
|
||||
}
|
||||
return err
|
||||
},
|
||||
ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
|
||||
compMode := cobra.ShellCompDirectiveNoFileComp
|
||||
@@ -93,8 +91,9 @@ func EditCmd(ctx *lmcli.Context) *cobra.Command {
|
||||
},
|
||||
}
|
||||
|
||||
cmd.Flags().BoolP("in-place", "i", true, "Edit the message in-place, rather than creating a branch")
|
||||
cmd.Flags().Int("offset", 1, "Offset from the last message to edit")
|
||||
cmd.Flags().StringP("role", "r", "", "Role of the edited message (user or assistant)")
|
||||
cmd.Flags().StringP("role", "r", "", "Change the role of the edited message (user or assistant)")
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
@@ -2,7 +2,6 @@ package cmd
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"slices"
|
||||
"time"
|
||||
|
||||
"git.mlow.ca/mlow/lmcli/pkg/lmcli"
|
||||
@@ -21,7 +20,7 @@ func ListCmd(ctx *lmcli.Context) *cobra.Command {
|
||||
Short: "List conversations",
|
||||
Long: `List conversations in order of recent activity`,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
conversations, err := ctx.Store.Conversations()
|
||||
messages, err := ctx.Store.LatestConversationMessages()
|
||||
if err != nil {
|
||||
return fmt.Errorf("Could not fetch conversations: %v", err)
|
||||
}
|
||||
@@ -58,13 +57,8 @@ func ListCmd(ctx *lmcli.Context) *cobra.Command {
|
||||
|
||||
all, _ := cmd.Flags().GetBool("all")
|
||||
|
||||
for _, conversation := range conversations {
|
||||
lastMessage, err := ctx.Store.LastMessage(&conversation)
|
||||
if lastMessage == nil || err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
messageAge := now.Sub(lastMessage.CreatedAt)
|
||||
for _, message := range messages {
|
||||
messageAge := now.Sub(message.CreatedAt)
|
||||
|
||||
var category string
|
||||
for _, c := range categories {
|
||||
@@ -76,9 +70,9 @@ func ListCmd(ctx *lmcli.Context) *cobra.Command {
|
||||
|
||||
formatted := fmt.Sprintf(
|
||||
"%s - %s - %s",
|
||||
conversation.ShortName.String,
|
||||
message.Conversation.ShortName.String,
|
||||
util.HumanTimeElapsedSince(messageAge),
|
||||
conversation.Title,
|
||||
message.Conversation.Title,
|
||||
)
|
||||
|
||||
categorized[category] = append(
|
||||
@@ -96,14 +90,10 @@ func ListCmd(ctx *lmcli.Context) *cobra.Command {
|
||||
continue
|
||||
}
|
||||
|
||||
slices.SortFunc(conversationLines, func(a, b ConversationLine) int {
|
||||
return int(a.timeSinceReply - b.timeSinceReply)
|
||||
})
|
||||
|
||||
fmt.Printf("%s:\n", category.name)
|
||||
for _, conv := range conversationLines {
|
||||
if conversationsPrinted >= count && !all {
|
||||
fmt.Printf("%d remaining message(s), use --all to view.\n", len(conversations)-conversationsPrinted)
|
||||
fmt.Printf("%d remaining conversation(s), use --all to view.\n", len(messages)-conversationsPrinted)
|
||||
break outer
|
||||
}
|
||||
|
||||
|
||||
@@ -15,42 +15,43 @@ func NewCmd(ctx *lmcli.Context) *cobra.Command {
|
||||
Short: "Start a new conversation",
|
||||
Long: `Start a new conversation with the Large Language Model.`,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
messageContents := inputFromArgsOrEditor(args, "# What would you like to say?\n", "")
|
||||
if messageContents == "" {
|
||||
input := inputFromArgsOrEditor(args, "# Start a new conversation below\n", "")
|
||||
if input == "" {
|
||||
return fmt.Errorf("No message was provided.")
|
||||
}
|
||||
|
||||
conversation := &model.Conversation{}
|
||||
err := ctx.Store.SaveConversation(conversation)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Could not save new conversation: %v", err)
|
||||
var messages []model.Message
|
||||
|
||||
// TODO: probably just make this part of the conversation
|
||||
system := ctx.GetSystemPrompt()
|
||||
if system != "" {
|
||||
messages = append(messages, model.Message{
|
||||
Role: model.MessageRoleSystem,
|
||||
Content: system,
|
||||
})
|
||||
}
|
||||
|
||||
messages := []model.Message{
|
||||
{
|
||||
ConversationID: conversation.ID,
|
||||
Role: model.MessageRoleSystem,
|
||||
Content: ctx.GetSystemPrompt(),
|
||||
},
|
||||
{
|
||||
ConversationID: conversation.ID,
|
||||
Role: model.MessageRoleUser,
|
||||
Content: messageContents,
|
||||
},
|
||||
messages = append(messages, model.Message{
|
||||
Role: model.MessageRoleUser,
|
||||
Content: input,
|
||||
})
|
||||
|
||||
conversation, messages, err := ctx.Store.StartConversation(messages...)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Could not start a new conversation: %v", err)
|
||||
}
|
||||
|
||||
cmdutil.HandleConversationReply(ctx, conversation, true, messages...)
|
||||
cmdutil.HandleReply(ctx, &messages[len(messages)-1], true)
|
||||
|
||||
title, err := cmdutil.GenerateTitle(ctx, conversation)
|
||||
title, err := cmdutil.GenerateTitle(ctx, messages)
|
||||
if err != nil {
|
||||
lmcli.Warn("Could not generate title for conversation: %v\n", err)
|
||||
lmcli.Warn("Could not generate title for conversation %s: %v\n", conversation.ShortName.String, err)
|
||||
}
|
||||
|
||||
conversation.Title = title
|
||||
|
||||
err = ctx.Store.SaveConversation(conversation)
|
||||
err = ctx.Store.UpdateConversation(conversation)
|
||||
if err != nil {
|
||||
lmcli.Warn("Could not save conversation after generating title: %v\n", err)
|
||||
lmcli.Warn("Could not save conversation title: %v\n", err)
|
||||
}
|
||||
return nil
|
||||
},
|
||||
|
||||
@@ -15,22 +15,27 @@ func PromptCmd(ctx *lmcli.Context) *cobra.Command {
|
||||
Short: "Do a one-shot prompt",
|
||||
Long: `Prompt the Large Language Model and get a response.`,
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
message := inputFromArgsOrEditor(args, "# What would you like to say?\n", "")
|
||||
if message == "" {
|
||||
input := inputFromArgsOrEditor(args, "# Write your prompt below\n", "")
|
||||
if input == "" {
|
||||
return fmt.Errorf("No message was provided.")
|
||||
}
|
||||
|
||||
messages := []model.Message{
|
||||
{
|
||||
var messages []model.Message
|
||||
|
||||
// TODO: stop supplying system prompt as a message
|
||||
system := ctx.GetSystemPrompt()
|
||||
if system != "" {
|
||||
messages = append(messages, model.Message{
|
||||
Role: model.MessageRoleSystem,
|
||||
Content: ctx.GetSystemPrompt(),
|
||||
},
|
||||
{
|
||||
Role: model.MessageRoleUser,
|
||||
Content: message,
|
||||
},
|
||||
Content: system,
|
||||
})
|
||||
}
|
||||
|
||||
messages = append(messages, model.Message{
|
||||
Role: model.MessageRoleUser,
|
||||
Content: input,
|
||||
})
|
||||
|
||||
_, err := cmdutil.Prompt(ctx, messages, nil)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Error fetching LLM response: %v", err)
|
||||
|
||||
@@ -24,12 +24,17 @@ func RenameCmd(ctx *lmcli.Context) *cobra.Command {
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
shortName := args[0]
|
||||
conversation := cmdutil.LookupConversation(ctx, shortName)
|
||||
|
||||
var err error
|
||||
var title string
|
||||
|
||||
generate, _ := cmd.Flags().GetBool("generate")
|
||||
var title string
|
||||
if generate {
|
||||
title, err = cmdutil.GenerateTitle(ctx, conversation)
|
||||
messages, err := ctx.Store.PathToLeaf(conversation.SelectedRoot)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Could not retrieve conversation messages: %v", err)
|
||||
}
|
||||
title, err = cmdutil.GenerateTitle(ctx, messages)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Could not generate conversation title: %v", err)
|
||||
}
|
||||
@@ -41,9 +46,9 @@ func RenameCmd(ctx *lmcli.Context) *cobra.Command {
|
||||
}
|
||||
|
||||
conversation.Title = title
|
||||
err = ctx.Store.SaveConversation(conversation)
|
||||
err = ctx.Store.UpdateConversation(conversation)
|
||||
if err != nil {
|
||||
lmcli.Warn("Could not save conversation with new title: %v\n", err)
|
||||
lmcli.Warn("Could not update conversation title: %v\n", err)
|
||||
}
|
||||
return nil
|
||||
},
|
||||
|
||||
@@ -31,9 +31,9 @@ func ReplyCmd(ctx *lmcli.Context) *cobra.Command {
|
||||
}
|
||||
|
||||
cmdutil.HandleConversationReply(ctx, conversation, true, model.Message{
|
||||
ConversationID: conversation.ID,
|
||||
Role: model.MessageRoleUser,
|
||||
Content: reply,
|
||||
ConversationID: conversation.ID,
|
||||
Role: model.MessageRoleUser,
|
||||
Content: reply,
|
||||
})
|
||||
return nil
|
||||
},
|
||||
|
||||
@@ -13,7 +13,7 @@ func RetryCmd(ctx *lmcli.Context) *cobra.Command {
|
||||
cmd := &cobra.Command{
|
||||
Use: "retry <conversation>",
|
||||
Short: "Retry the last user reply in a conversation",
|
||||
Long: `Re-prompt the conversation up to the last user response. Can be used to regenerate the last assistant reply, or simply generate one if an error occurred.`,
|
||||
Long: `Prompt the conversation from the last user response.`,
|
||||
Args: func(cmd *cobra.Command, args []string) error {
|
||||
argCount := 1
|
||||
if err := cobra.MinimumNArgs(argCount)(cmd, args); err != nil {
|
||||
@@ -25,25 +25,36 @@ func RetryCmd(ctx *lmcli.Context) *cobra.Command {
|
||||
shortName := args[0]
|
||||
conversation := cmdutil.LookupConversation(ctx, shortName)
|
||||
|
||||
messages, err := ctx.Store.Messages(conversation)
|
||||
// Load the complete thread from the root message
|
||||
messages, err := ctx.Store.PathToLeaf(conversation.SelectedRoot)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Could not retrieve messages for conversation: %s", conversation.Title)
|
||||
}
|
||||
|
||||
// walk backwards through the conversation and delete messages, break
|
||||
// when we find the latest user response
|
||||
for i := len(messages) - 1; i >= 0; i-- {
|
||||
if messages[i].Role == model.MessageRoleUser {
|
||||
break
|
||||
}
|
||||
|
||||
err = ctx.Store.DeleteMessage(&messages[i])
|
||||
if err != nil {
|
||||
lmcli.Warn("Could not delete previous reply: %v\n", err)
|
||||
}
|
||||
offset, _ := cmd.Flags().GetInt("offset")
|
||||
if offset < 0 {
|
||||
offset = -offset
|
||||
}
|
||||
|
||||
cmdutil.HandleConversationReply(ctx, conversation, true)
|
||||
if offset > len(messages)-1 {
|
||||
return fmt.Errorf("Offset %d is before the start of the conversation.", offset)
|
||||
}
|
||||
|
||||
retryFromIdx := len(messages) - 1 - offset
|
||||
|
||||
// decrease retryFromIdx until we hit a user message
|
||||
for retryFromIdx >= 0 && messages[retryFromIdx].Role != model.MessageRoleUser {
|
||||
retryFromIdx--
|
||||
}
|
||||
|
||||
if messages[retryFromIdx].Role != model.MessageRoleUser {
|
||||
return fmt.Errorf("No user messages to retry")
|
||||
}
|
||||
|
||||
fmt.Printf("Idx: %d Message: %v\n", retryFromIdx, messages[retryFromIdx])
|
||||
|
||||
// Start a new branch at the last user message
|
||||
cmdutil.HandleReply(ctx, &messages[retryFromIdx], true)
|
||||
return nil
|
||||
},
|
||||
ValidArgsFunction: func(cmd *cobra.Command, args []string, toComplete string) ([]string, cobra.ShellCompDirective) {
|
||||
@@ -55,6 +66,8 @@ func RetryCmd(ctx *lmcli.Context) *cobra.Command {
|
||||
},
|
||||
}
|
||||
|
||||
cmd.Flags().Int("offset", 1, "Offset from the last message retry from.")
|
||||
|
||||
applyPromptFlags(ctx, cmd)
|
||||
return cmd
|
||||
}
|
||||
|
||||
@@ -73,43 +73,57 @@ func LookupConversationE(ctx *lmcli.Context, shortName string) (*model.Conversat
|
||||
return c, nil
|
||||
}
|
||||
|
||||
func HandleConversationReply(ctx *lmcli.Context, c *model.Conversation, persist bool, toSend ...model.Message) {
|
||||
messages, err := ctx.Store.PathToLeaf(c.SelectedRoot)
|
||||
if err != nil {
|
||||
lmcli.Fatal("Could not load messages: %v\n", err)
|
||||
}
|
||||
HandleReply(ctx, &messages[len(messages)-1], persist, toSend...)
|
||||
}
|
||||
|
||||
// handleConversationReply handles sending messages to an existing
|
||||
// conversation, optionally persisting both the sent replies and responses.
|
||||
func HandleConversationReply(ctx *lmcli.Context, c *model.Conversation, persist bool, toSend ...model.Message) {
|
||||
existing, err := ctx.Store.Messages(c)
|
||||
if err != nil {
|
||||
lmcli.Fatal("Could not retrieve messages for conversation: %s\n", c.Title)
|
||||
func HandleReply(ctx *lmcli.Context, to *model.Message, persist bool, messages ...model.Message) {
|
||||
if to == nil {
|
||||
lmcli.Fatal("Can't prompt from an empty message.")
|
||||
}
|
||||
|
||||
if persist {
|
||||
for _, message := range toSend {
|
||||
err = ctx.Store.SaveMessage(&message)
|
||||
if err != nil {
|
||||
lmcli.Warn("Could not save %s message: %v\n", message.Role, err)
|
||||
}
|
||||
existing, err := ctx.Store.PathToRoot(to)
|
||||
if err != nil {
|
||||
lmcli.Fatal("Could not load messages: %v\n", err)
|
||||
}
|
||||
|
||||
RenderConversation(ctx, append(existing, messages...), true)
|
||||
|
||||
var savedReplies []model.Message
|
||||
if persist && len(messages) > 0 {
|
||||
savedReplies, err = ctx.Store.Reply(to, messages...)
|
||||
if err != nil {
|
||||
lmcli.Warn("Could not save messages: %v\n", err)
|
||||
}
|
||||
}
|
||||
|
||||
allMessages := append(existing, toSend...)
|
||||
|
||||
RenderConversation(ctx, allMessages, true)
|
||||
|
||||
// render a message header with no contents
|
||||
RenderMessage(ctx, (&model.Message{Role: model.MessageRoleAssistant}))
|
||||
|
||||
var lastSavedMessage *model.Message
|
||||
lastSavedMessage = to
|
||||
if len(savedReplies) > 0 {
|
||||
lastSavedMessage = &savedReplies[len(savedReplies)-1]
|
||||
}
|
||||
|
||||
replyCallback := func(reply model.Message) {
|
||||
if !persist {
|
||||
return
|
||||
}
|
||||
|
||||
reply.ConversationID = c.ID
|
||||
err = ctx.Store.SaveMessage(&reply)
|
||||
savedReplies, err = ctx.Store.Reply(lastSavedMessage, reply)
|
||||
if err != nil {
|
||||
lmcli.Warn("Could not save reply: %v\n", err)
|
||||
}
|
||||
lastSavedMessage = &savedReplies[0]
|
||||
}
|
||||
|
||||
_, err = Prompt(ctx, allMessages, replyCallback)
|
||||
_, err = Prompt(ctx, append(existing, messages...), replyCallback)
|
||||
if err != nil {
|
||||
lmcli.Fatal("Error fetching LLM response: %v\n", err)
|
||||
}
|
||||
@@ -134,12 +148,7 @@ func FormatForExternalPrompt(messages []model.Message, system bool) string {
|
||||
return sb.String()
|
||||
}
|
||||
|
||||
func GenerateTitle(ctx *lmcli.Context, c *model.Conversation) (string, error) {
|
||||
messages, err := ctx.Store.Messages(c)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
func GenerateTitle(ctx *lmcli.Context, messages []model.Message) (string, error) {
|
||||
const prompt = `Above is an excerpt from a conversation between a user and AI assistant. Please reply with a short title (no more than 8 words) that reflects the topic of the conversation, read from the user's perspective.
|
||||
|
||||
Example conversation:
|
||||
|
||||
@@ -24,9 +24,9 @@ func ViewCmd(ctx *lmcli.Context) *cobra.Command {
|
||||
shortName := args[0]
|
||||
conversation := cmdutil.LookupConversation(ctx, shortName)
|
||||
|
||||
messages, err := ctx.Store.Messages(conversation)
|
||||
messages, err := ctx.Store.PathToLeaf(conversation.SelectedRoot)
|
||||
if err != nil {
|
||||
return fmt.Errorf("Could not retrieve messages for conversation: %s", conversation.Title)
|
||||
return fmt.Errorf("Could not retrieve messages for conversation %s: %v", conversation.ShortName.String, err)
|
||||
}
|
||||
|
||||
cmdutil.RenderConversation(ctx, messages, false)
|
||||
|
||||
Reference in New Issue
Block a user