package llm import ( "bytes" "context" "encoding/json" "io" "net/http" "testing" "github.com/mmmy/snapgo/internal/domain" ) type roundTripFunc func(*http.Request) (*http.Response, error) func (f roundTripFunc) RoundTrip(req *http.Request) (*http.Response, error) { return f(req) } func TestVisionClientSummarizeImageUsesChatCompletionImageURL(t *testing.T) { var requestBody map[string]any transport := roundTripFunc(func(r *http.Request) (*http.Response, error) { if r.URL.Path != "/v1/chat/completions" { t.Fatalf("expected /v1/chat/completions, got %s", r.URL.Path) } if got := r.Header.Get("Authorization"); got != "Bearer test-key" { t.Fatalf("unexpected auth header %q", got) } if err := json.NewDecoder(r.Body).Decode(&requestBody); err != nil { t.Fatalf("decode request: %v", err) } return &http.Response{ StatusCode: http.StatusOK, Header: http.Header{"Content-Type": []string{"application/json"}}, Body: io.NopCloser(bytes.NewBufferString(`{"choices":[{"message":{"content":"summary ok"}}]}`)), }, nil }) client, err := NewVisionClient(domain.LLMProviderQwen, domain.LLMProviderConfig{ BaseURL: "https://example.test/v1", APIKey: "test-key", Model: "qwen-vl-plus", MaxTokens: 321, Temperature: 0.2, TimeoutSecs: 5, }) if err != nil { t.Fatalf("new client: %v", err) } client.httpClient.Transport = transport text, err := client.SummarizeImage(context.Background(), "describe", "https://cdn.example.com/a.png") if err != nil { t.Fatalf("summarize image: %v", err) } if text != "summary ok" { t.Fatalf("expected response text, got %q", text) } if requestBody["model"] != "qwen-vl-plus" { t.Fatalf("expected model forwarded, got %#v", requestBody["model"]) } if requestBody["max_tokens"] != float64(321) { t.Fatalf("expected max_tokens forwarded, got %#v", requestBody["max_tokens"]) } messages := requestBody["messages"].([]any) content := messages[0].(map[string]any)["content"].([]any) if content[0].(map[string]any)["text"] != "describe" { t.Fatalf("expected prompt content, got %#v", content[0]) } image := content[1].(map[string]any) if image["type"] != "image_url" { t.Fatalf("expected image_url item, got %#v", image) } url := image["image_url"].(map[string]any)["url"] if url != "https://cdn.example.com/a.png" { t.Fatalf("expected image url forwarded, got %#v", url) } } func TestChatCompletionsURLAcceptsFullEndpoint(t *testing.T) { full := "https://example.com/v1/chat/completions" if got := chatCompletionsURL(full); got != full { t.Fatalf("expected full endpoint unchanged, got %q", got) } }