203 lines
5.1 KiB
Go
203 lines
5.1 KiB
Go
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)
|
||
|
|
}
|
||
|
|
}
|