68 lines
1.6 KiB
Go
68 lines
1.6 KiB
Go
package conversation
|
|
|
|
import (
|
|
"context"
|
|
"database/sql"
|
|
"errors"
|
|
"fmt"
|
|
"strings"
|
|
"unicode/utf8"
|
|
|
|
"code.chimeric.al/chimerical/odidere/internal/db"
|
|
"code.chimeric.al/chimerical/odidere/internal/llm"
|
|
)
|
|
|
|
// MaxTitleLength is the maximum length of a conversation title in
|
|
// Unicode code points.
|
|
const MaxTitleLength = 256
|
|
|
|
// deriveTitle returns a title from the first line of text that
|
|
// contains non-whitespace characters. It replaces runs of
|
|
// whitespace on that line with single spaces and truncates to
|
|
// MaxTitleLength runes. It returns "" if text has no
|
|
// non-whitespace characters.
|
|
func deriveTitle(text string) string {
|
|
for line := range strings.SplitSeq(text, "\n") {
|
|
title := strings.Join(strings.Fields(line), " ")
|
|
if title == "" {
|
|
continue
|
|
}
|
|
if utf8.RuneCountInString(title) <= MaxTitleLength {
|
|
return title
|
|
}
|
|
runes := []rune(title)
|
|
return string(runes[:MaxTitleLength])
|
|
}
|
|
return ""
|
|
}
|
|
|
|
// setTitle derives a title from msg and sets the conversation
|
|
// title if the conversation has no title yet. It does nothing if
|
|
// msg has no non-whitespace text or if the conversation already
|
|
// has a title.
|
|
func (c *Conversation) setTitle(
|
|
ctx context.Context,
|
|
msg llm.Message,
|
|
) error {
|
|
title := deriveTitle(msg.Text())
|
|
if title == "" {
|
|
return nil
|
|
}
|
|
_, err := c.dbw.UpdateConversationTitleIfUnset(
|
|
ctx,
|
|
db.UpdateConversationTitleIfUnsetParams{
|
|
Id: c.id,
|
|
Title: title,
|
|
},
|
|
)
|
|
// sql.ErrNoRows means the conversation already has a title (or
|
|
// does not exist).
|
|
if errors.Is(err, sql.ErrNoRows) {
|
|
return nil
|
|
}
|
|
if err != nil {
|
|
return fmt.Errorf("update conversation title: %w", err)
|
|
}
|
|
return nil
|
|
}
|