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")
|
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")
|
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