TestChatFallsBackWhenOllamaRejectsThinking: Ollama rejects reasoning_effort on non-thinking models with a 400 saying ` does not support thinking`: different shape from OpenAI's message, same remedy. postChat must drop the field, retry once, and stay sticky so we don't re-trip the 400 every tu
(t *testing.T)
| 733 | for range c.Chat(context.Background(), nil, nil) { |
| 734 | } |
| 735 | }() |
| 736 | } |
| 737 | wg.Wait() |
| 738 | } |
| 739 | |
| 740 | // TestChatFallsBackWhenOllamaRejectsThinking: Ollama rejects reasoning_effort on |
| 741 | // non-thinking models with a 400 saying `<model> does not support thinking`: |
| 742 | // different shape from OpenAI's message, same remedy. postChat must drop the |
| 743 | // field, retry once, and stay sticky so we don't re-trip the 400 every turn. |
| 744 | func TestChatFallsBackWhenOllamaRejectsThinking(t *testing.T) { |
| 745 | var bodies []string |
| 746 | srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { |
| 747 | b, _ := io.ReadAll(r.Body) |
| 748 | bodies = append(bodies, string(b)) |
| 749 | if strings.Contains(string(b), `"reasoning_effort"`) { |
| 750 | w.WriteHeader(400) |
| 751 | fmt.Fprintln(w, `{"error":"\"test-model:latest\" does not support thinking"}`) |
| 752 | return |
| 753 | } |
| 754 | sseOK(w, []string{ |
| 755 | `{"choices":[{"delta":{"content":"ok"}}],"usage":{"completion_tokens":1}}`, |
| 756 | }) |
| 757 | })) |
| 758 | defer srv.Close() |
| 759 | |
| 760 | c := New(srv.URL, "test-model:latest", "") |
| 761 | |
| 762 | // First turn: 400 → fallback → success. |
| 763 | for _, e := range collect(c.Chat(context.Background(), nil, nil)) { |
| 764 | if e.Kind == EventError { |
| 765 | t.Fatalf("first turn must succeed via fallback, got error: %v", e.Err) |
| 766 | } |
| 767 | } |
| 768 | if len(bodies) != 2 { |
| 769 | t.Fatalf("first turn should send initial + retry (2 requests), got %d", len(bodies)) |
| 770 | } |
| 771 | if !strings.Contains(bodies[0], `"reasoning_effort"`) { |
| 772 | t.Fatalf("first attempt should send reasoning_effort: %s", bodies[0]) |
| 773 | } |
| 774 | if strings.Contains(bodies[1], `"reasoning_effort"`) { |
| 775 | t.Fatalf("retry must drop reasoning_effort: %s", bodies[1]) |
| 776 | } |
| 777 | |
| 778 | // Second turn on the same Client: flag is sticky, no 400, no retry. |
| 779 | bodies = nil |
| 780 | for _, e := range collect(c.Chat(context.Background(), nil, nil)) { |
| 781 | if e.Kind == EventError { |
| 782 | t.Fatalf("second turn must not error: %v", e.Err) |
| 783 | } |
| 784 | } |
| 785 | if len(bodies) != 1 { |