Repository: sashabaranov/go-openai
Pull request #817 (2024-08-07 15:07:59)

fix: fullURL endpoint generation
fix: generation azure fullURL #794
feat: Use setters pattern for fullURL method #636 

codecov[bot]: ## [Codecov](https://app.codecov.io/gh/sashabaranov/go-openai/pull/817?dropdown=coverage&src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Alex+Baranov) Report
All modified and coverable lines are covered by tests :white_check_mark:
> Project coverage is 98.96%. Comparing base [(`774fc9d`)](https://app.codecov.io/gh/sashabaranov/go-openai/commit/774fc9dd12ed60c10a9f9f03319ddb9cd5f8780c?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Alex+Baranov) to head [(`dfb20a1`)](https://app.codecov.io/gh/sashabaranov/go-openai/commit/dfb20a1bfdc0595da9d8073bec60512c19330274?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Alex+Baranov).
> Report is 36 commits behind head on master.

<details><summary>Additional details and impacted files</summary>


```diff
@@            Coverage Diff             @@
##           master     #817      +/-   ##
==========================================
+ Coverage   98.46%   98.96%   +0.50%     
==========================================
  Files          24       26       +2     
  Lines        1364     1357       -7     
==========================================
  Hits         1343     1343              
+ Misses         15        8       -7     
  Partials        6        6              
```

</details>

[:umbrella: View full report in Codecov by Sentry](https://app.codecov.io/gh/sashabaranov/go-openai/pull/817?dropdown=coverage&src=pr&el=continue&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Alex+Baranov).   
:loudspeaker: Have feedback on the report? [Share it here](https://about.codecov.io/codecov-pr-comment-feedback/?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Alex+Baranov).


Patch:
diff --git a/audio.go b/audio.go
index dbc26d1..f321f93 100644
--- a/audio.go
+++ b/audio.go
@@ -122,8 +122,13 @@ func (c *Client) callAudioAPI(
 	}
 
 	urlSuffix := fmt.Sprintf("/audio/%s", endpointSuffix)
-	req, err := c.newRequest(ctx, http.MethodPost, c.fullURL(urlSuffix, request.Model),
-		withBody(&formBody), withContentType(builder.FormDataContentType()))
+	req, err := c.newRequest(
+		ctx,
+		http.MethodPost,
+		c.fullURL(urlSuffix, withModel(request.Model)),
+		withBody(&formBody),
+		withContentType(builder.FormDataContentType()),
+	)
 	if err != nil {
 		return AudioResponse{}, err
 	}
diff --git a/chat.go b/chat.go
index 8bfe558..826fd3b 100644
--- a/chat.go
+++ b/chat.go
@@ -182,8 +182,8 @@ const (
 )
 
 type ChatCompletionResponseFormat struct {
-	Type       ChatCompletionResponseFormatType       `json:"type,omitempty"`
-	JSONSchema ChatCompletionResponseFormatJSONSchema `json:"json_schema,omitempty"`
+	Type       ChatCompletionResponseFormatType        `json:"type,omitempty"`
+	JSONSchema *ChatCompletionResponseFormatJSONSchema `json:"json_schema,omitempty"`
 }
 
 type ChatCompletionResponseFormatJSONSchema struct {
@@ -358,7 +358,12 @@ func (c *Client) CreateChatCompletion(
 		return
 	}
 
-	req, err := c.newRequest(ctx, http.MethodPost, c.fullURL(urlSuffix, request.Model), withBody(request))
+	req, err := c.newRequest(
+		ctx,
+		http.MethodPost,
+		c.fullURL(urlSuffix, withModel(request.Model)),
+		withBody(request),
+	)
 	if err != nil {
 		return
 	}
diff --git a/chat_stream.go b/chat_stream.go
index ffd512f..3f90bc0 100644
--- a/chat_stream.go
+++ b/chat_stream.go
@@ -60,7 +60,12 @@ func (c *Client) CreateChatCompletionStream(
 	}
 
 	request.Stream = true
-	req, err := c.newRequest(ctx, http.MethodPost, c.fullURL(urlSuffix, request.Model), withBody(request))
+	req, err := c.newRequest(
+		ctx,
+		http.MethodPost,
+		c.fullURL(urlSuffix, withModel(request.Model)),
+		withBody(request),
+	)
 	if err != nil {
 		return nil, err
 	}
diff --git a/client.go b/client.go
index d5d555c..9f547e7 100644
--- a/client.go
+++ b/client.go
@@ -222,42 +222,66 @@ func decodeString(body io.Reader, output *string) error {
 	return nil
 }
 
+type fullURLOptions struct {
+	model string
+}
+
+type fullURLOption func(*fullURLOptions)
+
+func withModel(model string) fullURLOption {
+	return func(args *fullURLOptions) {
+		args.model = model
+	}
+}
+
+var azureDeploymentsEndpoints = []string{
+	"/completions",
+	"/embeddings",
+	"/chat/completions",
+	"/audio/transcriptions",
+	"/audio/translations",
+	"/audio/speech",
+	"/images/generations",
+}
+
 // fullURL returns full URL for request.
-// args[0] is model name, if API type is Azure, model name is required to get deployment name.
-func (c *Client) fullURL(suffix string, args ...any) string {
-	// /openai/deployments/{model}/chat/completions?api-version={api_version}
+func (c *Client) fullURL(suffix string, setters ...fullURLOption) string {
+	baseURL := strings.TrimRight(c.config.BaseURL, "/")
+	args := fullURLOptions{}
+	for _, setter := range setters {
+		setter(&args)
+	}
+
 	if c.config.APIType == APITypeAzure || c.config.APIType == APITypeAzureAD {
-		baseURL := c.config.BaseURL
-		baseURL = strings.TrimRight(baseURL, "/")
-		parseURL, _ := url.Parse(baseURL)
-		query := parseURL.Query()
-		query.Add("api-version", c.config.APIVersion)
-		// if suffix is /models change to {endpoint}/openai/models?api-version=2022-12-01
-		// https://learn.microsoft.com/en-us/rest/api/cognitiveservices/azureopenaistable/models/list?tabs=HTTP
-		if containsSubstr([]string{"/models", "/assistants", "/threads", "/files"}, suffix) {
-			return fmt.Sprintf("%s/%s%s?%s", baseURL, azureAPIPrefix, suffix, query.Encode())
-		}
-		azureDeploymentName := "UNKNOWN"
-		if len(args) > 0 {
-			model, ok := args[0].(string)
-			if ok {
-				azureDeploymentName = c.config.GetAzureDeploymentByModel(model)
-			}
-		}
-		return fmt.Sprintf("%s/%s/%s/%s%s?%s",
-			baseURL, azureAPIPrefix, azureDeploymentsPrefix,
-			azureDeploymentName, suffix, query.Encode(),
-		)
+		baseURL = c.baseURLWithAzureDeployment(baseURL, suffix, args.model)
+	}
+
+	if c.config.APIVersion != "" {
+		suffix = c.suffixWithAPIVersion(suffix)
 	}
+	return fmt.Sprintf("%s%s", baseURL, suffix)
+}
 
-	// https://developers.cloudflare.com/ai-gateway/providers/azureopenai/
-	if c.config.APIType == APITypeCloudflareAzure {
-		baseURL := c.config.BaseURL
-		baseURL = strings.TrimRight(baseURL, "/")
-		return fmt.Sprintf("%s%s?api-version=%s", baseURL, suffix, c.config.APIVersion)
+func (c *Client) suffixWithAPIVersion(suffix string) string {
+	parsedSuffix, err := url.Parse(suffix)
+	if err != nil {
+		panic("failed to parse url suffix")
 	}
+	query := parsedSuffix.Query()
+	query.Add("api-version", c.config.APIVersion)
+	return fmt.Sprintf("%s?%s", parsedSuffix.Path, query.Encode())
+}
 
-	return fmt.Sprintf("%s%s", c.config.BaseURL, suffix)
+func (c *Client) baseURLWithAzureDeployment(baseURL, suffix, model string) (newBaseURL string) {
+	baseURL = fmt.Sprintf("%s/%s", strings.TrimRight(baseURL, "/"), azureAPIPrefix)
+	if containsSubstr(azureDeploymentsEndpoints, suffix) {
+		azureDeploymentName := c.config.GetAzureDeploymentByModel(model)
+		if azureDeploymentName == "" {
+			azureDeploymentName = "UNKNOWN"
+		}
+		baseURL = fmt.Sprintf("%s/%s/%s", baseURL, azureDeploymentsPrefix, azureDeploymentName)
+	}
+	return baseURL
 }
 
 func (c *Client) handleErrorResp(resp *http.Response) error {
diff --git a/completion.go b/completion.go
index d435eb3..e8e9242 100644
--- a/completion.go
+++ b/completion.go
@@ -138,25 +138,26 @@ func checkPromptType(prompt any) bool {
 
 // CompletionRequest represents a request structure for completion API.
 type CompletionRequest struct {
-	Model            string   `json:"model"`
-	Prompt           any      `json:"prompt,omitempty"`
-	Suffix           string   `json:"suffix,omitempty"`
-	MaxTokens        int      `json:"max_tokens,omitempty"`
-	Temperature      float32  `json:"temperature,omitempty"`
-	TopP             float32  `json:"top_p,omitempty"`
-	N                int      `json:"n,omitempty"`
-	Stream           bool     `json:"stream,omitempty"`
-	LogProbs         int      `json:"logprobs,omitempty"`
-	Echo             bool     `json:"echo,omitempty"`
-	Stop             []string `json:"stop,omitempty"`
-	PresencePenalty  float32  `json:"presence_penalty,omitempty"`
-	FrequencyPenalty float32  `json:"frequency_penalty,omitempty"`
-	BestOf           int      `json:"best_of,omitempty"`
+	Model            string  `json:"model"`
+	Prompt           any     `json:"prompt,omitempty"`
+	BestOf           int     `json:"best_of,omitempty"`
+	Echo             bool    `json:"echo,omitempty"`
+	FrequencyPenalty float32 `json:"frequency_penalty,omitempty"`
 	// LogitBias is must be a token id string (specified by their token ID in the tokenizer), not a word string.
 	// incorrect: `"logit_bias":{"You": 6}`, correct: `"logit_bias":{"1639": 6}`
 	// refs: https://platform.openai.com/docs/api-reference/completions/create#completions/create-logit_bias
-	LogitBias map[string]int `json:"logit_bias,omitempty"`
-	User      string         `json:"user,omitempty"`
+	LogitBias       map[string]int `json:"logit_bias,omitempty"`
+	LogProbs        int            `json:"logprobs,omitempty"`
+	MaxTokens       int            `json:"max_tokens,omitempty"`
+	N               int            `json:"n,omitempty"`
+	PresencePenalty float32        `json:"presence_penalty,omitempty"`
+	Seed            *int           `json:"seed,omitempty"`
+	Stop            []string       `json:"stop,omitempty"`
+	Stream          bool           `json:"stream,omitempty"`
+	Suffix          string         `json:"suffix,omitempty"`
+	Temperature     float32        `json:"temperature,omitempty"`
+	TopP            float32        `json:"top_p,omitempty"`
+	User            string         `json:"user,omitempty"`
 }
 
 // CompletionChoice represents one of possible completions.
@@ -212,7 +213,12 @@ func (c *Client) CreateCompletion(
 		return
 	}
 
-	req, err := c.newRequest(ctx, http.MethodPost, c.fullURL(urlSuffix, request.Model), withBody(request))
+	req, err := c.newRequest(
+		ctx,
+		http.MethodPost,
+		c.fullURL(urlSuffix, withModel(request.Model)),
+		withBody(request),
+	)
 	if err != nil {
 		return
 	}
diff --git a/edits.go b/edits.go
index 97d0260..fe8ecd0 100644
--- a/edits.go
+++ b/edits.go
@@ -38,7 +38,12 @@ will need to migrate to GPT-3.5 Turbo by January 4, 2024.
 You can use CreateChatCompletion or CreateChatCompletionStream instead.
 */
 func (c *Client) Edits(ctx context.Context, request EditsRequest) (response EditsResponse, err error) {
-	req, err := c.newRequest(ctx, http.MethodPost, c.fullURL("/edits", fmt.Sprint(request.Model)), withBody(request))
+	req, err := c.newRequest(
+		ctx,
+		http.MethodPost,
+		c.fullURL("/edits", withModel(fmt.Sprint(request.Model))),
+		withBody(request),
+	)
 	if err != nil {
 		return
 	}
diff --git a/embeddings.go b/embeddings.go
index b513ba6..74eb8aa 100644
--- a/embeddings.go
+++ b/embeddings.go
@@ -241,7 +241,12 @@ func (c *Client) CreateEmbeddings(
 	conv EmbeddingRequestConverter,
 ) (res EmbeddingResponse, err error) {
 	baseReq := conv.Convert()
-	req, err := c.newRequest(ctx, http.MethodPost, c.fullURL("/embeddings", string(baseReq.Model)), withBody(baseReq))
+	req, err := c.newRequest(
+		ctx,
+		http.MethodPost,
+		c.fullURL("/embeddings", withModel(string(baseReq.Model))),
+		withBody(baseReq),
+	)
 	if err != nil {
 		return
 	}
diff --git a/image.go b/image.go
index 665de1a..577d7db 100644
--- a/image.go
+++ b/image.go
@@ -68,7 +68,12 @@ type ImageResponseDataInner struct {
 // CreateImage - API call to create an image. This is the main endpoint of the DALL-E API.
 func (c *Client) CreateImage(ctx context.Context, request ImageRequest) (response ImageResponse, err error) {
 	urlSuffix := "/images/generations"
-	req, err := c.newRequest(ctx, http.MethodPost, c.fullURL(urlSuffix, request.Model), withBody(request))
+	req, err := c.newRequest(
+		ctx,
+		http.MethodPost,
+		c.fullURL(urlSuffix, withModel(request.Model)),
+		withBody(request),
+	)
 	if err != nil {
 		return
 	}
@@ -132,8 +137,13 @@ func (c *Client) CreateEditImage(ctx context.Context, request ImageEditRequest)
 		return
 	}
 
-	req, err := c.newRequest(ctx, http.MethodPost, c.fullURL("/images/edits", request.Model),
-		withBody(body), withContentType(builder.FormDataContentType()))
+	req, err := c.newRequest(
+		ctx,
+		http.MethodPost,
+		c.fullURL("/images/edits", withModel(request.Model)),
+		withBody(body),
+		withContentType(builder.FormDataContentType()),
+	)
 	if err != nil {
 		return
 	}
@@ -183,8 +193,13 @@ func (c *Client) CreateVariImage(ctx context.Context, request ImageVariRequest)
 		return
 	}
 
-	req, err := c.newRequest(ctx, http.MethodPost, c.fullURL("/images/variations", request.Model),
-		withBody(body), withContentType(builder.FormDataContentType()))
+	req, err := c.newRequest(
+		ctx,
+		http.MethodPost,
+		c.fullURL("/images/variations", withModel(request.Model)),
+		withBody(body),
+		withContentType(builder.FormDataContentType()),
+	)
 	if err != nil {
 		return
 	}
diff --git a/moderation.go b/moderation.go
index ae285ef..c8652ef 100644
--- a/moderation.go
+++ b/moderation.go
@@ -88,7 +88,12 @@ func (c *Client) Moderations(ctx context.Context, request ModerationRequest) (re
 		err = ErrModerationInvalidModel
 		return
 	}
-	req, err := c.newRequest(ctx, http.MethodPost, c.fullURL("/moderations", request.Model), withBody(&request))
+	req, err := c.newRequest(
+		ctx,
+		http.MethodPost,
+		c.fullURL("/moderations", withModel(request.Model)),
+		withBody(&request),
+	)
 	if err != nil {
 		return
 	}
diff --git a/speech.go b/speech.go
index 19b21bd..20b52e3 100644
--- a/speech.go
+++ b/speech.go
@@ -44,7 +44,10 @@ type CreateSpeechRequest struct {
 }
 
 func (c *Client) CreateSpeech(ctx context.Context, request CreateSpeechRequest) (response RawResponse, err error) {
-	req, err := c.newRequest(ctx, http.MethodPost, c.fullURL("/audio/speech", string(request.Model)),
+	req, err := c.newRequest(
+		ctx,
+		http.MethodPost,
+		c.fullURL("/audio/speech", withModel(string(request.Model))),
 		withBody(request),
 		withContentType("application/json"),
 	)
diff --git a/stream.go b/stream.go
index b277f3c..a61c7c9 100644
--- a/stream.go
+++ b/stream.go
@@ -3,6 +3,7 @@ package openai
 import (
 	"context"
 	"errors"
+	"net/http"
 )
 
 var (
@@ -33,7 +34,12 @@ func (c *Client) CreateCompletionStream(
 	}
 
 	request.Stream = true
-	req, err := c.newRequest(ctx, "POST", c.fullURL(urlSuffix, request.Model), withBody(request))
+	req, err := c.newRequest(
+		ctx,
+		http.MethodPost,
+		c.fullURL(urlSuffix, withModel(request.Model)),
+		withBody(request),
+	)
 	if err != nil {
 		return nil, err
 	}


Test patch:
diff --git a/api_integration_test.go b/api_integration_test.go
index a487f58..3084268 100644
--- a/api_integration_test.go
+++ b/api_integration_test.go
@@ -211,7 +211,7 @@ func TestChatCompletionResponseFormat_JSONSchema(t *testing.T) {
 			},
 			ResponseFormat: &openai.ChatCompletionResponseFormat{
 				Type: openai.ChatCompletionResponseFormatTypeJSONSchema,
-				JSONSchema: openai.ChatCompletionResponseFormatJSONSchema{
+				JSONSchema: &openai.ChatCompletionResponseFormatJSONSchema{
 					Name: "cases",
 					Schema: jsonschema.Definition{
 						Type: jsonschema.Object,
diff --git a/api_internal_test.go b/api_internal_test.go
index a590ec9..0967796 100644
--- a/api_internal_test.go
+++ b/api_internal_test.go
@@ -112,6 +112,7 @@ func TestAzureFullURL(t *testing.T) {
 		Name             string
 		BaseURL          string
 		AzureModelMapper map[string]string
+		Suffix           string
 		Model            string
 		Expect           string
 	}{
@@ -119,6 +120,7 @@ func TestAzureFullURL(t *testing.T) {
 			"AzureBaseURLWithSlashAutoStrip",
 			"https://httpbin.org/",
 			nil,
+			"/chat/completions",
 			"chatgpt-demo",
 			"https://httpbin.org/" +
 				"openai/deployments/chatgpt-demo" +
@@ -128,11 +130,20 @@ func TestAzureFullURL(t *testing.T) {
 			"AzureBaseURLWithoutSlashOK",
 			"https://httpbin.org",
 			nil,
+			"/chat/completions",
 			"chatgpt-demo",
 			"https://httpbin.org/" +
 				"openai/deployments/chatgpt-demo" +
 				"/chat/completions?api-version=2023-05-15",
 		},
+		{
+			"",
+			"https://httpbin.org",
+			nil,
+			"/assistants?limit=10",
+			"chatgpt-demo",
+			"https://httpbin.org/openai/assistants?api-version=2023-05-15&limit=10",
+		},
 	}
 
 	for _, c := range cases {
@@ -140,7 +151,7 @@ func TestAzureFullURL(t *testing.T) {
 			az := DefaultAzureConfig("dummy", c.BaseURL)
 			cli := NewClientWithConfig(az)
 			// /openai/deployments/{engine}/chat/completions?api-version={api_version}
-			actual := cli.fullURL("/chat/completions", c.Model)
+			actual := cli.fullURL(c.Suffix, withModel(c.Model))
 			if actual != c.Expect {
 				t.Errorf("Expected %s, got %s", c.Expect, actual)
 			}
@@ -153,19 +164,22 @@ func TestCloudflareAzureFullURL(t *testing.T) {
 	cases := []struct {
 		Name    string
 		BaseURL string
+		Suffix  string
 		Expect  string
 	}{
 		{
 			"CloudflareAzureBaseURLWithSlashAutoStrip",
 			"https://gateway.ai.cloudflare.com/v1/dnekeim2i39dmm4mldemakiem3i4mkw3/demo/azure-openai/resource/chatgpt-demo/",
+			"/chat/completions",
 			"https://gateway.ai.cloudflare.com/v1/dnekeim2i39dmm4mldemakiem3i4mkw3/demo/azure-openai/resource/chatgpt-demo/" +
 				"chat/completions?api-version=2023-05-15",
 		},
 		{
-			"CloudflareAzureBaseURLWithoutSlashOK",
+			"",
 			"https://gateway.ai.cloudflare.com/v1/dnekeim2i39dmm4mldemakiem3i4mkw3/demo/azure-openai/resource/chatgpt-demo",
-			"https://gateway.ai.cloudflare.com/v1/dnekeim2i39dmm4mldemakiem3i4mkw3/demo/azure-openai/resource/chatgpt-demo/" +
-				"chat/completions?api-version=2023-05-15",
+			"/assistants?limit=10",
+			"https://gateway.ai.cloudflare.com/v1/dnekeim2i39dmm4mldemakiem3i4mkw3/demo/azure-openai/resource/chatgpt-demo" +
+				"/assistants?api-version=2023-05-15&limit=10",
 		},
 	}
 
@@ -176,7 +190,7 @@ func TestCloudflareAzureFullURL(t *testing.T) {
 
 			cli := NewClientWithConfig(az)
 
-			actual := cli.fullURL("/chat/completions")
+			actual := cli.fullURL(c.Suffix)
 			if actual != c.Expect {
 				t.Errorf("Expected %s, got %s", c.Expect, actual)
 			}
diff --git a/client_test.go b/client_test.go
index e49da9b..a0d3bb3 100644
--- a/client_test.go
+++ b/client_test.go
@@ -431,3 +431,99 @@ func TestClientReturnsRequestBuilderErrorsAddition(t *testing.T) {
 		t.Fatalf("Did not return error when request builder failed: %v", err)
 	}
 }
+
+func TestClient_suffixWithAPIVersion(t *testing.T) {
+	type fields struct {
+		apiVersion string
+	}
+	type args struct {
+		suffix string
+	}
+	tests := []struct {
+		name      string
+		fields    fields
+		args      args
+		want      string
+		wantPanic string
+	}{
+		{
+			"",
+			fields{apiVersion: "2023-05"},
+			args{suffix: "/assistants"},
+			"/assistants?api-version=2023-05",
+			"",
+		},
+		{
+			"",
+			fields{apiVersion: "2023-05"},
+			args{suffix: "/assistants?limit=5"},
+			"/assistants?api-version=2023-05&limit=5",
+			"",
+		},
+		{
+			"",
+			fields{apiVersion: "2023-05"},
+			args{suffix: "123:assistants?limit=5"},
+			"/assistants?api-version=2023-05&limit=5",
+			"failed to parse url suffix",
+		},
+	}
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			c := &Client{
+				config: ClientConfig{APIVersion: tt.fields.apiVersion},
+			}
+			defer func() {
+				if r := recover(); r != nil {
+					if r.(string) != tt.wantPanic {
+						t.Errorf("suffixWithAPIVersion() = %v, want %v", r, tt.wantPanic)
+					}
+				}
+			}()
+			if got := c.suffixWithAPIVersion(tt.args.suffix); got != tt.want {
+				t.Errorf("suffixWithAPIVersion() = %v, want %v", got, tt.want)
+			}
+		})
+	}
+}
+
+func TestClient_baseURLWithAzureDeployment(t *testing.T) {
+	type args struct {
+		baseURL string
+		suffix  string
+		model   string
+	}
+	tests := []struct {
+		name           string
+		args           args
+		wantNewBaseURL string
+	}{
+		{
+			"",
+			args{baseURL: "https://test.openai.azure.com/", suffix: assistantsSuffix, model: GPT4oMini},
+			"https://test.openai.azure.com/openai",
+		},
+		{
+			"",
+			args{baseURL: "https://test.openai.azure.com/", suffix: chatCompletionsSuffix, model: GPT4oMini},
+			"https://test.openai.azure.com/openai/deployments/gpt-4o-mini",
+		},
+		{
+			"",
+			args{baseURL: "https://test.openai.azure.com/", suffix: chatCompletionsSuffix, model: ""},
+			"https://test.openai.azure.com/openai/deployments/UNKNOWN",
+		},
+	}
+	client := NewClient("")
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			if gotNewBaseURL := client.baseURLWithAzureDeployment(
+				tt.args.baseURL,
+				tt.args.suffix,
+				tt.args.model,
+			); gotNewBaseURL != tt.wantNewBaseURL {
+				t.Errorf("baseURLWithAzureDeployment() = %v, want %v", gotNewBaseURL, tt.wantNewBaseURL)
+			}
+		})
+	}
+}
diff --git a/example_test.go b/example_test.go
index de67c57..1bdb849 100644
--- a/example_test.go
+++ b/example_test.go
@@ -73,7 +73,7 @@ func ExampleClient_CreateChatCompletionStream() {
 			return
 		}
 
-		fmt.Printf(response.Choices[0].Delta.Content)
+		fmt.Println(response.Choices[0].Delta.Content)
 	}
 }