Implement `lmcli ls`

This commit is contained in:
Matt Low 2023-11-12 07:19:45 +00:00
parent ae424530f9
commit b0a1299e0b
3 changed files with 118 additions and 4 deletions

View File

@ -3,6 +3,7 @@ package cli
import ( import (
"fmt" "fmt"
"strings" "strings"
"time"
"github.com/spf13/cobra" "github.com/spf13/cobra"
) )
@ -24,17 +25,21 @@ var lsCmd = &cobra.Command{
Short: "List existing conversations", Short: "List existing conversations",
Long: `List all existing conversations in descending order of recent activity.`, Long: `List all existing conversations in descending order of recent activity.`,
Run: func(cmd *cobra.Command, args []string) { Run: func(cmd *cobra.Command, args []string) {
fmt.Println("Listing conversations...") conversations, err := store.GetConversations()
// Example output, asterisk to indicate current converation if err != nil {
fmt.Println("Could not fetch conversations.")
return
}
// $ lm ls // Example output
// $ lmcli ls
// last hour: // last hour:
// 98sg - 12 minutes ago - Project discussion // 98sg - 12 minutes ago - Project discussion
// last day: // last day:
// tj3l - 10 hours ago - Deep learning concepts // tj3l - 10 hours ago - Deep learning concepts
// last week: // last week:
// bwfm - 2 days ago - Machine learning study // bwfm - 2 days ago - Machine learning study
// * 8n3h - 3 days ago - Weekend plans // 8n3h - 3 days ago - Weekend plans
// f3n7 - 6 days ago - CLI development // f3n7 - 6 days ago - CLI development
// last month: // last month:
// 5hn2 - 8 days ago - Book club discussion // 5hn2 - 8 days ago - Book club discussion
@ -45,6 +50,63 @@ var lsCmd = &cobra.Command{
// g8d9 - 3 months ago - History book club // g8d9 - 3 months ago - History book club
// 4lk3 - 4 months ago - Local events and meetups // 4lk3 - 4 months ago - Local events and meetups
// 43jn - 6 months ago - Mobile photography techniques // 43jn - 6 months ago - Mobile photography techniques
now := time.Now()
categories := []string{
"recent",
"last hour",
"last day",
"last week",
"last month",
"last 6 months",
"older",
}
categorized := map[string][]string{}
for _, conversation := range conversations {
lastMessage, err := store.GetLastMessage(&conversation)
if lastMessage == nil || err != nil {
continue
}
messageAge := now.Sub(lastMessage.CreatedAt)
var category string
switch {
case messageAge <= 10*time.Minute:
category = "recent"
case messageAge <= time.Hour:
category = "last hour"
case messageAge <= 24*time.Hour:
category = "last day"
case messageAge <= 7*24*time.Hour:
category = "last week"
case messageAge <= 30*24*time.Hour:
category = "last month"
case messageAge <= 6*30*24*time.Hour: // Approximate as 6 months
category = "last 6 months"
default:
category = "older"
}
formatted := fmt.Sprintf(
"%s - %s - %s",
conversation.ShortName.String,
humanTimeElapsedSince(messageAge),
conversation.Title,
)
categorized[category] = append(categorized[category], formatted)
}
for _, category := range categories {
conversations, ok := categorized[category]
if !ok {
continue
}
fmt.Printf("%s:\n", category)
for _, conv := range conversations {
fmt.Printf(" %s\n", conv)
}
}
}, },
} }
@ -174,6 +236,7 @@ var promptCmd = &cobra.Command{
func NewRootCmd() *cobra.Command { func NewRootCmd() *cobra.Command {
rootCmd.AddCommand( rootCmd.AddCommand(
lsCmd,
newCmd, newCmd,
promptCmd, promptCmd,
) )

View File

@ -5,6 +5,7 @@ import (
"fmt" "fmt"
"os" "os"
"path/filepath" "path/filepath"
"time"
sqids "github.com/sqids/sqids-go" sqids "github.com/sqids/sqids-go"
"gorm.io/driver/sqlite" "gorm.io/driver/sqlite"
@ -22,6 +23,7 @@ type Message struct {
Conversation Conversation Conversation Conversation
OriginalContent string OriginalContent string
Role string // 'user' or 'assistant' Role string // 'user' or 'assistant'
CreatedAt time.Time
} }
type Conversation struct { type Conversation struct {
@ -98,3 +100,9 @@ func (s *Store) GetMessages(conversation *Conversation) ([]Message, error) {
err := s.db.Where("conversation_id = ?", conversation.ID).Find(&messages).Error err := s.db.Where("conversation_id = ?", conversation.ID).Find(&messages).Error
return messages, err return messages, err
} }
func (s *Store) GetLastMessage(conversation *Conversation) (*Message, error) {
var message Message
err := s.db.Where("conversation_id = ?", conversation.ID).Last(&message).Error
return &message, err
}

View File

@ -1,9 +1,11 @@
package cli package cli
import ( import (
"fmt"
"os" "os"
"os/exec" "os/exec"
"strings" "strings"
"time"
) )
// InputFromEditor retrieves user input by opening an editor (one specified by // InputFromEditor retrieves user input by opening an editor (one specified by
@ -48,3 +50,44 @@ func InputFromEditor(placeholder string, pattern string) (string, error) {
return strings.Trim(content, "\n \t"), nil return strings.Trim(content, "\n \t"), nil
} }
// humanTimeElapsedSince returns a human-friendly representation of the given time
// duration.
func humanTimeElapsedSince(d time.Duration) string {
seconds := d.Seconds()
minutes := seconds / 60
hours := minutes / 60
days := hours / 24
weeks := days / 7
months := days / 30
years := days / 365
switch {
case seconds < 60:
return "seconds ago"
case minutes < 2:
return "1 minute ago"
case minutes < 60:
return fmt.Sprintf("%d minutes ago", int64(minutes))
case hours < 2:
return "1 hour ago"
case hours < 24:
return fmt.Sprintf("%d hours ago", int64(hours))
case days < 2:
return "1 day ago"
case days < 7:
return fmt.Sprintf("%d days ago", int64(days))
case weeks < 2:
return "1 week ago"
case weeks <= 4:
return fmt.Sprintf("%d weeks ago", int64(weeks))
case months < 2:
return "1 month ago"
case months < 12:
return fmt.Sprintf("%d months ago", int64(months))
case years < 2:
return "1 year ago"
default:
return fmt.Sprintf("%d years ago", int64(years))
}
}