Add default conversation title
This commit is contained in:
@@ -0,0 +1,202 @@
|
||||
package conversation
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"code.chimeric.al/chimerical/odidere/internal/db"
|
||||
"code.chimeric.al/chimerical/odidere/internal/llm"
|
||||
)
|
||||
|
||||
func TestDeriveTitle(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
text string
|
||||
want string
|
||||
}{
|
||||
{
|
||||
name: "single line",
|
||||
text: "Check the cameras",
|
||||
want: "Check the cameras",
|
||||
},
|
||||
{
|
||||
name: "blank lines come before the first line",
|
||||
text: "\n \n\t\nCheck the cameras\nSecond line",
|
||||
want: "Check the cameras",
|
||||
},
|
||||
{
|
||||
name: "spaces inside the line collapse to one",
|
||||
text: "Can you\tcheck the cameras? ",
|
||||
want: "Can you check the cameras?",
|
||||
},
|
||||
{
|
||||
name: "leading indentation is trimmed",
|
||||
text: " def main():",
|
||||
want: "def main():",
|
||||
},
|
||||
{
|
||||
name: "a line of MaxTitleLength characters stays whole",
|
||||
text: strings.Repeat("x", MaxTitleLength),
|
||||
want: strings.Repeat("x", MaxTitleLength),
|
||||
},
|
||||
{
|
||||
name: "a longer line cuts to MaxTitleLength characters",
|
||||
text: strings.Repeat("x", 300),
|
||||
want: strings.Repeat("x", MaxTitleLength),
|
||||
},
|
||||
{
|
||||
name: "the cut counts characters, not bytes",
|
||||
text: strings.Repeat("文", 300),
|
||||
want: strings.Repeat("文", MaxTitleLength),
|
||||
},
|
||||
{
|
||||
name: "empty text",
|
||||
text: "",
|
||||
want: "",
|
||||
},
|
||||
{
|
||||
name: "spaces only",
|
||||
text: " \n\t \n",
|
||||
want: "",
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
if got := deriveTitle(tt.text); got != tt.want {
|
||||
t.Fatalf("deriveTitle(%q) = %q, want %q", tt.text, got, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func title(t *testing.T, c *Conversation) *string {
|
||||
t.Helper()
|
||||
conv, err := c.dbr.SelectConversationId(t.Context(), c.id)
|
||||
if err != nil {
|
||||
t.Fatalf("SelectConversationId: %v", err)
|
||||
}
|
||||
return conv.Title
|
||||
}
|
||||
|
||||
func TestInsertMessageTitlesConversation(t *testing.T) {
|
||||
ctx := t.Context()
|
||||
|
||||
// The first user message sets the title from its first non-empty
|
||||
// line.
|
||||
c := newTestConversation(t, nil)
|
||||
if _, err := c.InsertMessage(
|
||||
ctx, *userMessage(" Check the cameras\nplease"),
|
||||
); err != nil {
|
||||
t.Fatalf("InsertMessage: %v", err)
|
||||
}
|
||||
if got := title(t, c); got == nil || *got != "Check the cameras" {
|
||||
t.Fatalf("Title = %v, want Check the cameras", got)
|
||||
}
|
||||
|
||||
// A later user message does not overwrite an existing title.
|
||||
if _, err := c.InsertMessage(
|
||||
ctx, *userMessage("A different subject"),
|
||||
); err != nil {
|
||||
t.Fatalf("InsertMessage: %v", err)
|
||||
}
|
||||
if got := title(t, c); got == nil || *got != "Check the cameras" {
|
||||
t.Fatalf("Title = %v, want Check the cameras", got)
|
||||
}
|
||||
|
||||
// Non-user messages do not set the title.
|
||||
c2 := newTestConversation(t, nil)
|
||||
assistant := llm.Message{
|
||||
Role: llm.RoleAssistant,
|
||||
ContentParts: []llm.ContentPart{{
|
||||
Type: llm.ContentTypeText,
|
||||
Text: "Hello there",
|
||||
}},
|
||||
}
|
||||
if _, err := c2.InsertMessage(ctx, assistant); err != nil {
|
||||
t.Fatalf("InsertMessage: %v", err)
|
||||
}
|
||||
if got := title(t, c2); got != nil {
|
||||
t.Fatalf("Title = %v, want nil", got)
|
||||
}
|
||||
|
||||
// A title set before the first user message is kept.
|
||||
c3 := newTestConversation(t, nil)
|
||||
if _, err := c3.dbw.UpdateConversationTitle(
|
||||
ctx,
|
||||
db.UpdateConversationTitleParams{
|
||||
Id: c3.id,
|
||||
Title: "Nightly report",
|
||||
},
|
||||
); err != nil {
|
||||
t.Fatalf("UpdateConversationTitle: %v", err)
|
||||
}
|
||||
if _, err := c3.InsertMessage(
|
||||
ctx, *userMessage("A different subject"),
|
||||
); err != nil {
|
||||
t.Fatalf("InsertMessage: %v", err)
|
||||
}
|
||||
if got := title(t, c3); got == nil || *got != "Nightly report" {
|
||||
t.Fatalf("Title = %v, want Nightly report", got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestInferenceTitlesConversation(t *testing.T) {
|
||||
var (
|
||||
ctx = t.Context()
|
||||
client = newTestModelClient(
|
||||
t,
|
||||
func(w http.ResponseWriter, r *http.Request) {
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
fmt.Fprint(w, `{
|
||||
"choices":[{
|
||||
"message":{"role":"assistant","content":"ok"},
|
||||
"finish_reason":"stop"
|
||||
}]
|
||||
}`)
|
||||
},
|
||||
)
|
||||
)
|
||||
|
||||
// Inference with a new user message sets an unset title.
|
||||
c := newTestConversation(t, nil)
|
||||
if err := infer(ctx, c, client, InferenceRequest{
|
||||
Message: userMessage("Turn off the porch light"),
|
||||
}); err != nil {
|
||||
t.Fatalf("infer: %v", err)
|
||||
}
|
||||
if got := title(t, c); got == nil || *got != "Turn off the porch light" {
|
||||
t.Fatalf("Title = %v, want Turn off the porch light", got)
|
||||
}
|
||||
|
||||
// Later inference does not overwrite the title.
|
||||
if err := infer(ctx, c, client, InferenceRequest{
|
||||
Message: userMessage("A different subject"),
|
||||
}); err != nil {
|
||||
t.Fatalf("infer: %v", err)
|
||||
}
|
||||
if got := title(t, c); got == nil || *got != "Turn off the porch light" {
|
||||
t.Fatalf("Title = %v, want Turn off the porch light", got)
|
||||
}
|
||||
|
||||
// Inference does not overwrite a title that was already set.
|
||||
c2 := newTestConversation(t, nil)
|
||||
if _, err := c2.dbw.UpdateConversationTitle(
|
||||
ctx,
|
||||
db.UpdateConversationTitleParams{
|
||||
Id: c2.id,
|
||||
Title: "Nightly report",
|
||||
},
|
||||
); err != nil {
|
||||
t.Fatalf("UpdateConversationTitle: %v", err)
|
||||
}
|
||||
if err := infer(ctx, c2, client, InferenceRequest{
|
||||
Message: userMessage("A different subject"),
|
||||
}); err != nil {
|
||||
t.Fatalf("infer: %v", err)
|
||||
}
|
||||
if got := title(t, c2); got == nil || *got != "Nightly report" {
|
||||
t.Fatalf("Title = %v, want Nightly report", got)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user