package thinking import ( "testing" "text/template" ) func TestInferThinkingTags(t *testing.T) { cases := []struct { desc string tmplString string wantOpeningTag string wantClosingTag string }{ { desc: "basic", tmplString: ` {{ if .Thinking}} /think {{ end }} {{- range $i, $_ := .Messages }} {{- $last := eq (len (slice $.Messages $i)) 1 -}} {{ if and $last .Thinking }} {{ .Thinking }} {{ end }} {{ end }} `, wantOpeningTag: "", wantClosingTag: "", }, { desc: "doubly nested range", tmplString: ` {{ if .Thinking}} /think {{ end }} {{- range $i, $_ := .Messages }} {{- range $j, $_ := .NotMessages }} {{- $last := eq (len (slice $.Messages $i)) 1 -}} {{ if and $last .Thinking }} {{ .Thinking }} {{ end }} {{ end }} {{ end }} `, wantOpeningTag: "", wantClosingTag: "", }, { desc: "whitespace is trimmed", tmplString: ` {{ if .Thinking}} /think {{ end }} {{- range $i, $_ := .Messages }} {{- $last := eq (len (slice $.Messages $i)) 1 -}} {{ if and $last .Thinking }} Some text before {{ .Thinking }} Some text after {{ end }} {{ end }} `, wantOpeningTag: "Some text before", wantClosingTag: "Some text after", }, { desc: "qwen3", tmplString: ` {{- if or .System .Tools .Thinking }}<|im_start|>system {{- if .System }} {{ .System }} {{- end }} {{- if .Tools }} # Tools You may call one or more functions to assist with the user query. You are provided with function signatures within XML tags: {{- range .Tools }} {"type": "function", "function": {{ .Function }}} {{- end }} For each function call, return a json object with function name and arguments within XML tags: {"name": , "arguments": } {{- end }} {{- if .Thinking }} /think {{- else }} /no_think {{- end }}<|im_end|> {{ end }} {{- range $i, $_ := .Messages }} {{- $last := eq (len (slice $.Messages $i)) 1 -}} {{- if eq .Role "user" }}<|im_start|>user {{ .Content }}<|im_end|> {{ else if eq .Role "assistant" }}<|im_start|>assistant {{ if and $last .Thinking }} {{ .Thinking }} {{ end }} {{ if .Content }}{{ .Content }} {{- else if .ToolCalls }} {{ range .ToolCalls }}{"name": "{{ .Function.Name }}", "arguments": {{ .Function.Arguments }}} {{ end }} {{- end }}{{ if not $last }}<|im_end|> {{ end }} {{- else if eq .Role "tool" }}<|im_start|>user {{ .Content }} <|im_end|> {{ end }} {{- if and (ne .Role "assistant") $last }}<|im_start|>assistant {{ end }} {{- end }} `, wantOpeningTag: "", wantClosingTag: "", }, } for _, c := range cases { tmpl := template.Must(template.New("test").Parse(c.tmplString)) openingTag, closingTag := InferTags(tmpl) if openingTag != c.wantOpeningTag || closingTag != c.wantClosingTag { t.Errorf("case %q: got (%q,%q), want (%q,%q)", c.desc, openingTag, closingTag, c.wantOpeningTag, c.wantClosingTag) } } }