package llm import ( "strings" "testing" ) func run(chunks ...string) (string, string) { var f thinkFilter var streamed strings.Builder for _, c := range chunks { streamed.WriteString(f.push(c)) } streamed.WriteString(f.finish()) return streamed.String(), f.out.String() } func TestThinkFilter(t *testing.T) { cases := []struct { name string chunks []string want string }{ {"plain", []string{"Hallo ", "Welt"}, "Hallo Welt"}, {"block", []string{"reasoning\n\n\nHallo!"}, "Hallo!"}, {"split tags", []string{"abc", "\n\nHal", "lo"}, "Hallo"}, {"char by char", strings.Split("xJa", ""), "Ja"}, {"stray close", []string{"Hallo!\n\nHallo!"}, "Hallo!\n\nHallo!"}, {"unclosed with gap (Dahl)", []string{"The user asks.\n\n\nHallo! Wie geht's?"}, "Hallo! Wie geht's?"}, {"gap streams live", []string{"a\n", "\n", "\nAntw", "ort"}, "Antwort"}, {"gap outside block kept", []string{"A\n\n\nB"}, "A\n\n\nB"}, {"unclosed no gap", []string{"nur Gedanken"}, "nur Gedanken"}, {"lt not a tag", []string{"a c <", "3"}, "a c <3"}, {"trailing partial", []string{"x The user"}}]} data: {"choices":[{"delta":{"content":" greets.\n\n\nHallo!"}}]} data: {"choices":[{"delta":{},"finish_reason":"stop"}]} data: [DONE] ` var deltas []string resp, err := parseStream(strings.NewReader(sse), func(s string) { deltas = append(deltas, s) }) if err != nil || resp.Content != "Hallo!" || strings.Join(deltas, "") != "Hallo!" { t.Fatalf("%q %v %v", resp.Content, deltas, err) } } // The answer after a swallowed must be streamed incrementally, not only at the end. func TestThinkGapStreamsIncrementally(t *testing.T) { var f thinkFilter if v := f.push("Überlegung\n\n\nHal"); v != "Hal" { t.Fatalf("got %q", v) } if v := f.push("lo"); v != "lo" { t.Fatalf("got %q", v) } }