Repository: sashabaranov/go-openai
Pull request #545 (2023-11-07 18:43:07)

Fix Refactor assistant api
**Describe the change**
- OpenAI Assistant API needs a [Beta header](https://platform.openai.com/docs/assistants/overview/step-1-create-an-assistant)
- AssistantTool types can be simplified

**Describe your solution**
- Add the header on Assistant APIs
- use only `AssistantTool` type with optional sub fields.


codecov[bot]: ## [Codecov](https://app.codecov.io/gh/sashabaranov/go-openai/pull/545?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=None) Report
> Merging [#545](https://app.codecov.io/gh/sashabaranov/go-openai/pull/545?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=None) (b5e75ca) into [master](https://app.codecov.io/gh/sashabaranov/go-openai/commit/3063e676bf5932024d76be8e8d9e41df06d4e8cc?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=None) (3063e67) will **increase** coverage by `0.02%`.
> Report is 1 commits behind head on master.
> The diff coverage is `100.00%`.

```diff
@@            Coverage Diff             @@
##           master     #545      +/-   ##
==========================================
+ Coverage   97.85%   97.88%   +0.02%     
==========================================
  Files          20       20              
  Lines         978      991      +13     
==========================================
+ Hits          957      970      +13     
  Misses         15       15              
  Partials        6        6              
```


| [Files](https://app.codecov.io/gh/sashabaranov/go-openai/pull/545?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=None) | Coverage Δ | |
|---|---|---|
| [assistant.go](https://app.codecov.io/gh/sashabaranov/go-openai/pull/545?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=None#diff-YXNzaXN0YW50Lmdv) | `100.00% <100.00%> (ø)` | |
| [chat.go](https://app.codecov.io/gh/sashabaranov/go-openai/pull/545?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=None#diff-Y2hhdC5nbw==) | `100.00% <ø> (ø)` | |
| [chat\_stream.go](https://app.codecov.io/gh/sashabaranov/go-openai/pull/545?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=None#diff-Y2hhdF9zdHJlYW0uZ28=) | `100.00% <ø> (ø)` | |
| [client.go](https://app.codecov.io/gh/sashabaranov/go-openai/pull/545?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=None#diff-Y2xpZW50Lmdv) | `100.00% <100.00%> (ø)` | |



Patch:
diff --git a/assistant.go b/assistant.go
index d75eebe..de49be6 100644
--- a/assistant.go
+++ b/assistant.go
@@ -10,46 +10,43 @@ import (
 const (
 	assistantsSuffix      = "/assistants"
 	assistantsFilesSuffix = "/files"
+	openaiAssistantsV1    = "assistants=v1"
 )
 
 type Assistant struct {
-	ID           string  `json:"id"`
-	Object       string  `json:"object"`
-	CreatedAt    int64   `json:"created_at"`
-	Name         *string `json:"name,omitempty"`
-	Description  *string `json:"description,omitempty"`
-	Model        string  `json:"model"`
-	Instructions *string `json:"instructions,omitempty"`
-	Tools        []any   `json:"tools,omitempty"`
+	ID           string          `json:"id"`
+	Object       string          `json:"object"`
+	CreatedAt    int64           `json:"created_at"`
+	Name         *string         `json:"name,omitempty"`
+	Description  *string         `json:"description,omitempty"`
+	Model        string          `json:"model"`
+	Instructions *string         `json:"instructions,omitempty"`
+	Tools        []AssistantTool `json:"tools,omitempty"`
 
 	httpHeader
 }
 
-type AssistantTool struct {
-	Type string `json:"type"`
-}
-
-type AssistantToolCodeInterpreter struct {
-	AssistantTool
-}
+type AssistantToolType string
 
-type AssistantToolRetrieval struct {
-	AssistantTool
-}
+const (
+	AssistantToolTypeCodeInterpreter AssistantToolType = "code_interpreter"
+	AssistantToolTypeRetrieval       AssistantToolType = "retrieval"
+	AssistantToolTypeFunction        AssistantToolType = "function"
+)
 
-type AssistantToolFunction struct {
-	AssistantTool
-	Function FunctionDefinition `json:"function"`
+type AssistantTool struct {
+	Type     AssistantToolType   `json:"type"`
+	Function *FunctionDefinition `json:"function,omitempty"`
 }
 
 type AssistantRequest struct {
-	Model        string         `json:"model"`
-	Name         *string        `json:"name,omitempty"`
-	Description  *string        `json:"description,omitempty"`
-	Instructions *string        `json:"instructions,omitempty"`
-	Tools        []any          `json:"tools,omitempty"`
-	FileIDs      []string       `json:"file_ids,omitempty"`
-	Metadata     map[string]any `json:"metadata,omitempty"`
+	Model        string          `json:"model"`
+	Name         *string         `json:"name,omitempty"`
+	Description  *string         `json:"description,omitempty"`
+	Instructions *string         `json:"instructions,omitempty"`
+	Tools        []AssistantTool `json:"tools,omitempty"`
+	FileIDs      []string        `json:"file_ids,omitempty"`
+	Metadata     map[string]any  `json:"metadata,omitempty"`
 }
 
 // AssistantsList is a list of assistants.
@@ -59,6 +56,14 @@ type AssistantsList struct {
 	httpHeader
 }
 
+type AssistantDeleteResponse struct {
+	ID      string `json:"id"`
+	Object  string `json:"object"`
+	Deleted bool   `json:"deleted"`
+
+	httpHeader
+}
+
 type AssistantFile struct {
 	ID          string `json:"id"`
 	Object      string `json:"object"`
@@ -80,7 +85,8 @@ type AssistantFilesList struct {
 
 // CreateAssistant creates a new assistant.
 func (c *Client) CreateAssistant(ctx context.Context, request AssistantRequest) (response Assistant, err error) {
-	req, err := c.newRequest(ctx, http.MethodPost, c.fullURL(assistantsSuffix), withBody(request))
+	req, err := c.newRequest(ctx, http.MethodPost, c.fullURL(assistantsSuffix), withBody(request),
+		withBetaAssistantV1())
 	if err != nil {
 		return
 	}
@@ -95,7 +101,8 @@ func (c *Client) RetrieveAssistant(
 	assistantID string,
 ) (response Assistant, err error) {
 	urlSuffix := fmt.Sprintf("%s/%s", assistantsSuffix, assistantID)
-	req, err := c.newRequest(ctx, http.MethodGet, c.fullURL(urlSuffix))
+	req, err := c.newRequest(ctx, http.MethodGet, c.fullURL(urlSuffix),
+		withBetaAssistantV1())
 	if err != nil {
 		return
 	}
@@ -111,7 +118,8 @@ func (c *Client) ModifyAssistant(
 	request AssistantRequest,
 ) (response Assistant, err error) {
 	urlSuffix := fmt.Sprintf("%s/%s", assistantsSuffix, assistantID)
-	req, err := c.newRequest(ctx, http.MethodPost, c.fullURL(urlSuffix), withBody(request))
+	req, err := c.newRequest(ctx, http.MethodPost, c.fullURL(urlSuffix), withBody(request),
+		withBetaAssistantV1())
 	if err != nil {
 		return
 	}
@@ -124,9 +132,10 @@ func (c *Client) ModifyAssistant(
 func (c *Client) DeleteAssistant(
 	ctx context.Context,
 	assistantID string,
-) (response Assistant, err error) {
+) (response AssistantDeleteResponse, err error) {
 	urlSuffix := fmt.Sprintf("%s/%s", assistantsSuffix, assistantID)
-	req, err := c.newRequest(ctx, http.MethodDelete, c.fullURL(urlSuffix))
+	req, err := c.newRequest(ctx, http.MethodDelete, c.fullURL(urlSuffix),
+		withBetaAssistantV1())
 	if err != nil {
 		return
 	}
@@ -163,7 +172,8 @@ func (c *Client) ListAssistants(
 	}
 
 	urlSuffix := fmt.Sprintf("%s%s", assistantsSuffix, encodedValues)
-	req, err := c.newRequest(ctx, http.MethodGet, c.fullURL(urlSuffix))
+	req, err := c.newRequest(ctx, http.MethodGet, c.fullURL(urlSuffix),
+		withBetaAssistantV1())
 	if err != nil {
 		return
 	}
@@ -180,7 +190,8 @@ func (c *Client) CreateAssistantFile(
 ) (response AssistantFile, err error) {
 	urlSuffix := fmt.Sprintf("%s/%s%s", assistantsSuffix, assistantID, assistantsFilesSuffix)
 	req, err := c.newRequest(ctx, http.MethodPost, c.fullURL(urlSuffix),
-		withBody(request))
+		withBody(request),
+		withBetaAssistantV1())
 	if err != nil {
 		return
 	}
@@ -196,7 +207,8 @@ func (c *Client) RetrieveAssistantFile(
 	fileID string,
 ) (response AssistantFile, err error) {
 	urlSuffix := fmt.Sprintf("%s/%s%s/%s", assistantsSuffix, assistantID, assistantsFilesSuffix, fileID)
-	req, err := c.newRequest(ctx, http.MethodGet, c.fullURL(urlSuffix))
+	req, err := c.newRequest(ctx, http.MethodGet, c.fullURL(urlSuffix),
+		withBetaAssistantV1())
 	if err != nil {
 		return
 	}
@@ -212,7 +224,8 @@ func (c *Client) DeleteAssistantFile(
 	fileID string,
 ) (err error) {
 	urlSuffix := fmt.Sprintf("%s/%s%s/%s", assistantsSuffix, assistantID, assistantsFilesSuffix, fileID)
-	req, err := c.newRequest(ctx, http.MethodDelete, c.fullURL(urlSuffix))
+	req, err := c.newRequest(ctx, http.MethodDelete, c.fullURL(urlSuffix),
+		withBetaAssistantV1())
 	if err != nil {
 		return
 	}
@@ -250,7 +263,8 @@ func (c *Client) ListAssistantFiles(
 	}
 
 	urlSuffix := fmt.Sprintf("%s/%s%s%s", assistantsSuffix, assistantID, assistantsFilesSuffix, encodedValues)
-	req, err := c.newRequest(ctx, http.MethodGet, c.fullURL(urlSuffix))
+	req, err := c.newRequest(ctx, http.MethodGet, c.fullURL(urlSuffix),
+		withBetaAssistantV1())
 	if err != nil {
 		return
 	}
diff --git a/chat.go b/chat.go
index 0430318..609e0c3 100644
--- a/chat.go
+++ b/chat.go
@@ -89,18 +89,18 @@ type ChatCompletionResponseFormat struct {
 
 // ChatCompletionRequest represents a request structure for chat completion API.
 type ChatCompletionRequest struct {
-	Model            string                       `json:"model"`
-	Messages         []ChatCompletionMessage      `json:"messages"`
-	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"`
-	Stop             []string                     `json:"stop,omitempty"`
-	PresencePenalty  float32                      `json:"presence_penalty,omitempty"`
-	ResponseFormat   ChatCompletionResponseFormat `json:"response_format,omitempty"`
-	Seed             *int                         `json:"seed,omitempty"`
-	FrequencyPenalty float32                      `json:"frequency_penalty,omitempty"`
+	Model            string                        `json:"model"`
+	Messages         []ChatCompletionMessage       `json:"messages"`
+	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"`
+	Stop             []string                      `json:"stop,omitempty"`
+	PresencePenalty  float32                       `json:"presence_penalty,omitempty"`
+	ResponseFormat   *ChatCompletionResponseFormat `json:"response_format,omitempty"`
+	Seed             *int                          `json:"seed,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/chat/create#chat/create-logit_bias
diff --git a/client.go b/client.go
index 65ece81..056226c 100644
--- a/client.go
+++ b/client.go
@@ -83,6 +83,12 @@ func withContentType(contentType string) requestOption {
 	}
 }
 
+func withBetaAssistantV1() requestOption {
+	return func(args *requestOptions) {
+		args.header.Set("OpenAI-Beta", "assistants=v1")
+	}
+}
+
 func (c *Client) newRequest(ctx context.Context, method, url string, setters ...requestOption) (*http.Request, error) {
 	// Default Options
 	args := &requestOptions{
diff --git a/image.go b/image.go
index 4addcdb..4fe8b3a 100644
--- a/image.go
+++ b/image.go
@@ -13,6 +13,9 @@ const (
 	CreateImageSize256x256   = "256x256"
 	CreateImageSize512x512   = "512x512"
 	CreateImageSize1024x1024 = "1024x1024"
+	// dall-e-3 supported only.
+	CreateImageSize1792x1024 = "1792x1024"
+	CreateImageSize1024x1792 = "1024x1792"
 )
 
 const (
@@ -20,11 +23,29 @@ const (
 	CreateImageResponseFormatB64JSON = "b64_json"
 )
 
+const (
+	CreateImageModelDallE2 = "dall-e-2"
+	CreateImageModelDallE3 = "dall-e-3"
+)
+
+const (
+	CreateImageQualityHD       = "hd"
+	CreateImageQualityStandard = "standard"
+)
+
+const (
+	CreateImageStyleVivid   = "vivid"
+	CreateImageStyleNatural = "natural"
+)
+
 // ImageRequest represents the request structure for the image API.
 type ImageRequest struct {
 	Prompt         string `json:"prompt,omitempty"`
+	Model          string `json:"model,omitempty"`
 	N              int    `json:"n,omitempty"`
+	Quality        string `json:"quality,omitempty"`
 	Size           string `json:"size,omitempty"`
+	Style          string `json:"style,omitempty"`
 	ResponseFormat string `json:"response_format,omitempty"`
 	User           string `json:"user,omitempty"`
 }
@@ -39,8 +60,9 @@ type ImageResponse struct {
 
 // ImageResponseDataInner represents a response data structure for image API.
 type ImageResponseDataInner struct {
-	URL     string `json:"url,omitempty"`
-	B64JSON string `json:"b64_json,omitempty"`
+	URL           string `json:"url,omitempty"`
+	B64JSON       string `json:"b64_json,omitempty"`
+	RevisedPrompt string `json:"revised_prompt,omitempty"`
 }
 
 // CreateImage - API call to create an image. This is the main endpoint of the DALL-E API.


Test patch:
diff --git a/image_api_test.go b/image_api_test.go
index 422f831..2eb46f2 100644
--- a/image_api_test.go
+++ b/image_api_test.go
@@ -19,7 +19,14 @@ func TestImages(t *testing.T) {
 	defer teardown()
 	server.RegisterHandler("/v1/images/generations", handleImageEndpoint)
 	_, err := client.CreateImage(context.Background(), openai.ImageRequest{
-		Prompt: "Lorem ipsum",
+		Prompt:         "Lorem ipsum",
+		Model:          openai.CreateImageModelDallE3,
+		N:              1,
+		Quality:        openai.CreateImageQualityHD,
+		Size:           openai.CreateImageSize1024x1024,
+		Style:          openai.CreateImageStyleVivid,
+		ResponseFormat: openai.CreateImageResponseFormatURL,
+		User:           "user",
 	})
 	checks.NoError(t, err, "CreateImage error")
 }