Repository: sashabaranov/go-openai
Pull request #793 (2024-07-16 18:47:44)

add finetuning hyperparams
Adds `learning_rate_multiplier` and `batch_size` parameters.

Supported by both OpenAI and Azure, see:
* https://platform.openai.com/docs/api-reference/fine-tuning/create
* https://learn.microsoft.com/en-us/rest/api/azureopenai/fine-tuning/create?view=rest-azureopenai-2024-03-01-preview&tabs=HTTP#finetuninghyperparameters


codecov[bot]: ## [Codecov](https://app.codecov.io/gh/sashabaranov/go-openai/pull/793?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.88%. 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 [(`e9132c6`)](https://app.codecov.io/gh/sashabaranov/go-openai/commit/e9132c6fbe2ceb4e85ad8cf2d12ba3267ae0272e?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=Alex+Baranov).
> Report is 27 commits behind head on master.

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


```diff
@@            Coverage Diff             @@
##           master     #793      +/-   ##
==========================================
+ Coverage   98.46%   98.88%   +0.42%     
==========================================
  Files          24       26       +2     
  Lines        1364     1343      -21     
==========================================
- Hits         1343     1328      -15     
+ Misses         15        9       -6     
  Partials        6        6              
```

</details>

[:umbrella: View full report in Codecov by Sentry](https://app.codecov.io/gh/sashabaranov/go-openai/pull/793?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/client.go b/client.go
index 7bc28e9..d5d555c 100644
--- a/client.go
+++ b/client.go
@@ -7,6 +7,7 @@ import (
 	"fmt"
 	"io"
 	"net/http"
+	"net/url"
 	"strings"
 
 	utils "github.com/sashabaranov/go-openai/internal"
@@ -228,10 +229,13 @@ func (c *Client) fullURL(suffix string, args ...any) string {
 	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?api-version=%s", baseURL, azureAPIPrefix, suffix, c.config.APIVersion)
+			return fmt.Sprintf("%s/%s%s?%s", baseURL, azureAPIPrefix, suffix, query.Encode())
 		}
 		azureDeploymentName := "UNKNOWN"
 		if len(args) > 0 {
@@ -240,9 +244,9 @@ func (c *Client) fullURL(suffix string, args ...any) string {
 				azureDeploymentName = c.config.GetAzureDeploymentByModel(model)
 			}
 		}
-		return fmt.Sprintf("%s/%s/%s/%s%s?api-version=%s",
+		return fmt.Sprintf("%s/%s/%s/%s%s?%s",
 			baseURL, azureAPIPrefix, azureDeploymentsPrefix,
-			azureDeploymentName, suffix, c.config.APIVersion,
+			azureDeploymentName, suffix, query.Encode(),
 		)
 	}
 
diff --git a/completion.go b/completion.go
index 024f09b..4ff1123 100644
--- a/completion.go
+++ b/completion.go
@@ -24,6 +24,8 @@ const (
 	GPT40314              = "gpt-4-0314"
 	GPT4o                 = "gpt-4o"
 	GPT4o20240513         = "gpt-4o-2024-05-13"
+	GPT4oMini             = "gpt-4o-mini"
+	GPT4oMini20240718     = "gpt-4o-mini-2024-07-18"
 	GPT4Turbo             = "gpt-4-turbo"
 	GPT4Turbo20240409     = "gpt-4-turbo-2024-04-09"
 	GPT4Turbo0125         = "gpt-4-0125-preview"
@@ -89,6 +91,8 @@ var disabledModelsForEndpoints = map[string]map[string]bool{
 		GPT4:                 true,
 		GPT4o:                true,
 		GPT4o20240513:        true,
+		GPT4oMini:            true,
+		GPT4oMini20240718:    true,
 		GPT4TurboPreview:     true,
 		GPT4VisionPreview:    true,
 		GPT4Turbo1106:        true,
diff --git a/files.go b/files.go
index 26ad6bd..edc9f2a 100644
--- a/files.go
+++ b/files.go
@@ -102,6 +102,7 @@ func (c *Client) CreateFile(ctx context.Context, request FileRequest) (file File
 	if err != nil {
 		return
 	}
+	defer fileData.Close()
 
 	err = builder.CreateFormFile("file", fileData)
 	if err != nil {
diff --git a/fine_tuning_job.go b/fine_tuning_job.go
index 9dcb49d..5a9f54a 100644
--- a/fine_tuning_job.go
+++ b/fine_tuning_job.go
@@ -26,7 +26,9 @@ type FineTuningJob struct {
 }
 
 type Hyperparameters struct {
-	Epochs any `json:"n_epochs,omitempty"`
+	Epochs                 any `json:"n_epochs,omitempty"`
+	LearningRateMultiplier any `json:"learning_rate_multiplier,omitempty"`
+	BatchSize              any `json:"batch_size,omitempty"`
 }
 
 type FineTuningJobRequest struct {
diff --git a/thread.go b/thread.go
index 6f75214..bc08e2b 100644
--- a/thread.go
+++ b/thread.go
@@ -83,14 +83,25 @@ type ModifyThreadRequest struct {
 type ThreadMessageRole string
 
 const (
-	ThreadMessageRoleUser ThreadMessageRole = "user"
+	ThreadMessageRoleAssistant ThreadMessageRole = "assistant"
+	ThreadMessageRoleUser      ThreadMessageRole = "user"
 )
 
 type ThreadMessage struct {
-	Role     ThreadMessageRole `json:"role"`
-	Content  string            `json:"content"`
-	FileIDs  []string          `json:"file_ids,omitempty"`
-	Metadata map[string]any    `json:"metadata,omitempty"`
+	Role        ThreadMessageRole  `json:"role"`
+	Content     string             `json:"content"`
+	FileIDs     []string           `json:"file_ids,omitempty"`
+	Attachments []ThreadAttachment `json:"attachments,omitempty"`
+	Metadata    map[string]any     `json:"metadata,omitempty"`
+}
+
+type ThreadAttachment struct {
+	FileID string                 `json:"file_id"`
+	Tools  []ThreadAttachmentTool `json:"tools"`
+}
+
+type ThreadAttachmentTool struct {
+	Type string `json:"type"`
 }
 
 type ThreadDeleteResponse struct {


Test patch:
diff --git a/fine_tuning_job_test.go b/fine_tuning_job_test.go
index d2fbcd4..5f63ef2 100644
--- a/fine_tuning_job_test.go
+++ b/fine_tuning_job_test.go
@@ -33,7 +33,9 @@ func TestFineTuningJob(t *testing.T) {
 				ValidationFile: "",
 				TrainingFile:   "file-abc123",
 				Hyperparameters: openai.Hyperparameters{
-					Epochs: "auto",
+					Epochs:                 "auto",
+					LearningRateMultiplier: "auto",
+					BatchSize:              "auto",
 				},
 				TrainedTokens: 5768,
 			})