package openai_compat import ( "encoding/json" "net/http" "net/http/httptest" "testing" ) func TestProviderChat_UsesMaxCompletionTokensForGLM(t *testing.T) { var requestBody map[string]interface{} server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { if r.URL.Path != "/chat/completions" { http.Error(w, "not found", http.StatusNotFound) return } if err := json.NewDecoder(r.Body).Decode(&requestBody); err != nil { http.Error(w, err.Error(), http.StatusBadRequest) return } resp := map[string]interface{}{ "choices": []map[string]interface{}{ { "message": map[string]interface{}{"content": "ok"}, "finish_reason": "stop", }, }, } w.Header().Set("Content-Type", "application/json") json.NewEncoder(w).Encode(resp) })) defer server.Close() p := NewProvider("key", server.URL) _, err := p.Chat(t.Context(), []Message{{Role: "user", Content: "hi"}}, nil, "glm-4.7", map[string]interface{}{"max_tokens": 1234}) if err != nil { t.Fatalf("Chat() error = %v", err) } if _, ok := requestBody["max_completion_tokens"]; !ok { t.Fatalf("expected max_completion_tokens in request body") } if _, ok := requestBody["max_tokens"]; ok { t.Fatalf("did not expect max_tokens key for glm model") } } func TestProviderChat_ParsesToolCalls(t *testing.T) { server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { resp := map[string]interface{}{ "choices": []map[string]interface{}{ { "message": map[string]interface{}{ "content": "", "tool_calls": []map[string]interface{}{ { "id": "call_1", "type": "function", "function": map[string]interface{}{ "name": "get_weather", "arguments": "{\"city\":\"SF\"}", }, }, }, }, "finish_reason": "tool_calls", }, }, "usage": map[string]interface{}{ "prompt_tokens": 10, "completion_tokens": 5, "total_tokens": 15, }, } w.Header().Set("Content-Type", "application/json") json.NewEncoder(w).Encode(resp) })) defer server.Close() p := NewProvider("key", server.URL) out, err := p.Chat(t.Context(), []Message{{Role: "user", Content: "hi"}}, nil, "gpt-4o", nil) if err != nil { t.Fatalf("Chat() error = %v", err) } if len(out.ToolCalls) != 1 { t.Fatalf("len(ToolCalls) = %d, want 1", len(out.ToolCalls)) } if out.ToolCalls[0].Name != "get_weather" { t.Fatalf("ToolCalls[0].Name = %q, want %q", out.ToolCalls[0].Name, "get_weather") } if out.ToolCalls[0].Arguments["city"] != "SF" { t.Fatalf("ToolCalls[0].Arguments[city] = %v, want SF", out.ToolCalls[0].Arguments["city"]) } } func TestProviderChat_HTTPError(t *testing.T) { server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { http.Error(w, "bad request", http.StatusBadRequest) })) defer server.Close() p := NewProvider("key", server.URL) _, err := p.Chat(t.Context(), []Message{{Role: "user", Content: "hi"}}, nil, "gpt-4o", nil) if err == nil { t.Fatal("expected error, got nil") } } func TestProviderChat_StripsMoonshotPrefixAndNormalizesKimiTemperature(t *testing.T) { var requestBody map[string]interface{} server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { if err := json.NewDecoder(r.Body).Decode(&requestBody); err != nil { http.Error(w, err.Error(), http.StatusBadRequest) return } resp := map[string]interface{}{ "choices": []map[string]interface{}{ { "message": map[string]interface{}{"content": "ok"}, "finish_reason": "stop", }, }, } w.Header().Set("Content-Type", "application/json") json.NewEncoder(w).Encode(resp) })) defer server.Close() p := NewProvider("key", server.URL) _, err := p.Chat( t.Context(), []Message{{Role: "user", Content: "hi"}}, nil, "moonshot/kimi-k2.5", map[string]interface{}{"temperature": 0.3}, ) if err != nil { t.Fatalf("Chat() error = %v", err) } if requestBody["model"] != "kimi-k2.5" { t.Fatalf("model = %v, want kimi-k2.5", requestBody["model"]) } if requestBody["temperature"] != 1.0 { t.Fatalf("temperature = %v, want 1.0", requestBody["temperature"]) } }