add unit tests for ollama thinking fallback (streaming and non-streaming)
This commit is contained in:
parent
2a34ef048c
commit
43de821b08
2 changed files with 136 additions and 0 deletions
|
|
@ -556,3 +556,65 @@ func TestParseResponse_WithThoughtSignature(t *testing.T) {
|
|||
out.ToolCalls[0].ExtraContent.Google.ThoughtSignature, "sig123")
|
||||
}
|
||||
}
|
||||
|
||||
func TestSerializeMessages_WithReasoningFallbackFields(t *testing.T) {
|
||||
messages := []Message{
|
||||
{
|
||||
Role: "assistant",
|
||||
Content: "final answer",
|
||||
ReasoningContent: "deepseek thinking",
|
||||
Reasoning: "ollama reasoning",
|
||||
Thinking: "ollama thinking",
|
||||
},
|
||||
}
|
||||
result := SerializeMessages(messages)
|
||||
|
||||
data, _ := json.Marshal(result)
|
||||
var msgs []map[string]any
|
||||
json.Unmarshal(data, &msgs)
|
||||
|
||||
if msgs[0]["reasoning_content"] != "deepseek thinking" {
|
||||
t.Errorf("reasoning_content mismatch, got %v", msgs[0]["reasoning_content"])
|
||||
}
|
||||
if msgs[0]["reasoning"] != "ollama reasoning" {
|
||||
t.Errorf("reasoning mismatch, got %v", msgs[0]["reasoning"])
|
||||
}
|
||||
if msgs[0]["thinking"] != "ollama thinking" {
|
||||
t.Errorf("thinking mismatch, got %v", msgs[0]["thinking"])
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseResponse_WithOllamaThinkingFallback(t *testing.T) {
|
||||
// Test thinking fallback
|
||||
body := `{"choices":[{"message":{"content":"","thinking":"I am thinking..."},"finish_reason":"stop"}]}`
|
||||
out, err := ParseResponse(strings.NewReader(body))
|
||||
if err != nil {
|
||||
t.Fatalf("ParseResponse() error = %v", err)
|
||||
}
|
||||
if out.Content != "I am thinking..." {
|
||||
t.Errorf("Content fallback to thinking failed, got %q", out.Content)
|
||||
}
|
||||
if out.Thinking != "I am thinking..." {
|
||||
t.Errorf("Thinking field not preserved, got %q", out.Thinking)
|
||||
}
|
||||
|
||||
// Test reasoning fallback
|
||||
body = `{"choices":[{"message":{"content":"","reasoning":"Ollama reasoning text"},"finish_reason":"stop"}]}`
|
||||
out, err = ParseResponse(strings.NewReader(body))
|
||||
if err != nil {
|
||||
t.Fatalf("ParseResponse() error = %v", err)
|
||||
}
|
||||
if out.Content != "Ollama reasoning text" {
|
||||
t.Errorf("Content fallback to reasoning failed, got %q", out.Content)
|
||||
}
|
||||
|
||||
// Test priority: content > thinking > reasoning
|
||||
body = `{"choices":[{"message":{"content":"real content","thinking":"hidden thinking"},"finish_reason":"stop"}]}`
|
||||
out, err = ParseResponse(strings.NewReader(body))
|
||||
if err != nil {
|
||||
t.Fatalf("ParseResponse() error = %v", err)
|
||||
}
|
||||
if out.Content != "real content" {
|
||||
t.Errorf("Content should have priority, got %q", out.Content)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1089,3 +1089,77 @@ func TestSerializeMessages_StripsSystemParts(t *testing.T) {
|
|||
t.Fatal("system_parts should not appear in serialized output")
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseStreamResponse_ThinkingFallback(t *testing.T) {
|
||||
// Mock SSE stream with thinking/reasoning chunks
|
||||
stream := `data: {"choices":[{"delta":{"thinking":"Let me "},"index":0}]}
|
||||
data: {"choices":[{"delta":{"thinking":"think... "},"index":0}]}
|
||||
data: {"choices":[{"delta":{"content":"The answer is "},"index":0}]}
|
||||
data: {"choices":[{"delta":{"content":"42"},"index":0}]}
|
||||
data: [DONE]
|
||||
`
|
||||
|
||||
var accumulated string
|
||||
onChunk := func(acc string) {
|
||||
accumulated = acc
|
||||
}
|
||||
|
||||
resp, err := parseStreamResponse(t.Context(), strings.NewReader(stream), onChunk)
|
||||
if err != nil {
|
||||
t.Fatalf("parseStreamResponse() error = %v", err)
|
||||
}
|
||||
|
||||
// Verify Thinking field is populated
|
||||
if resp.Thinking != "Let me think... " {
|
||||
t.Errorf("resp.Thinking = %q, want %q", resp.Thinking, "Let me think... ")
|
||||
}
|
||||
|
||||
// Verify Content contains BOTH thinking (as fallback/prefix) and content
|
||||
expectedContent := "Let me think... The answer is 42"
|
||||
if resp.Content != expectedContent {
|
||||
t.Errorf("resp.Content = %q, want %q", resp.Content, expectedContent)
|
||||
}
|
||||
|
||||
// Verify onChunk received the intermediate states
|
||||
if accumulated != expectedContent {
|
||||
t.Errorf("last onChunk = %q, want %q", accumulated, expectedContent)
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseStreamResponse_ReasoningFallback(t *testing.T) {
|
||||
// Mock SSE stream with reasoning (Ollama style)
|
||||
stream := `data: {"choices":[{"delta":{"reasoning":"I will calculate "},"index":0}]}
|
||||
data: {"choices":[{"delta":{"reasoning":"the sum."},"index":0}]}
|
||||
data: [DONE]
|
||||
`
|
||||
|
||||
resp, err := parseStreamResponse(t.Context(), strings.NewReader(stream), nil)
|
||||
if err != nil {
|
||||
t.Fatalf("parseStreamResponse() error = %v", err)
|
||||
}
|
||||
|
||||
if resp.Content != "I will calculate the sum." {
|
||||
t.Errorf("resp.Content = %q, want %q", resp.Content, "I will calculate the sum.")
|
||||
}
|
||||
}
|
||||
|
||||
func TestParseStreamResponse_MixedContent(t *testing.T) {
|
||||
// Verify that if both thinking and content are present, they are both captured correctly.
|
||||
// Some models might start with thinking and then send content.
|
||||
stream := `data: {"choices":[{"delta":{"thinking":"Wait, "},"index":0}]}
|
||||
data: {"choices":[{"delta":{"content":"Hello"},"index":0}]}
|
||||
data: [DONE]
|
||||
`
|
||||
|
||||
resp, err := parseStreamResponse(t.Context(), strings.NewReader(stream), nil)
|
||||
if err != nil {
|
||||
t.Fatalf("parseStreamResponse() error = %v", err)
|
||||
}
|
||||
|
||||
if resp.Thinking != "Wait, " {
|
||||
t.Errorf("Thinking = %q", resp.Thinking)
|
||||
}
|
||||
if resp.Content != "Wait, Hello" {
|
||||
t.Errorf("Content = %q", resp.Content)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue