Repository: sashabaranov/go-openai
Pull request #438 (2023-07-12 17:51:41)

add example: fine tune
**Describe the change**
Added an example for usage of fine tune

**Describe your solution**
example in readme file

Issue: #315 


codecov[bot]: ## [Codecov](https://app.codecov.io/gh/sashabaranov/go-openai/pull/438?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=None) Report
> Merging [#438](https://app.codecov.io/gh/sashabaranov/go-openai/pull/438?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=None) (fab20b0) into [master](https://app.codecov.io/gh/sashabaranov/go-openai/commit/c3b2451f7c7dc477d98e1baa10993ac55392c7dd?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=None) (c3b2451) will **not change** coverage.
> The diff coverage is `n/a`.

```diff
@@           Coverage Diff           @@
##           master     #438   +/-   ##
=======================================
  Coverage   97.02%   97.02%           
=======================================
  Files          17       17           
  Lines         705      705           
=======================================
  Hits          684      684           
  Misses         15       15           
  Partials        6        6           
```





Patch:
diff --git a/README.md b/README.md
index 1f708af..19aadde 100644
--- a/README.md
+++ b/README.md
@@ -611,6 +611,73 @@ if errors.As(err, &e) {
 ```
 </details>
 
+<details>
+<summary>Fine Tune Model</summary>
+
+```go
+package main
+
+import (
+	"context"
+	"fmt"
+	"github.com/sashabaranov/go-openai"
+)
+
+func main() {
+	client := openai.NewClient("your token")
+	ctx := context.Background()
+
+	// create a .jsonl file with your training data
+	// {"prompt": "<prompt text>", "completion": "<ideal generated text>"}
+	// {"prompt": "<prompt text>", "completion": "<ideal generated text>"}
+	// {"prompt": "<prompt text>", "completion": "<ideal generated text>"}
+
+	// you can use openai cli tool to validate the data
+	// For more info - https://platform.openai.com/docs/guides/fine-tuning
+
+	file, err := client.CreateFile(ctx, openai.FileRequest{
+		FilePath: "training_prepared.jsonl",
+		Purpose:  "fine-tune",
+	})
+	if err != nil {
+		fmt.Printf("Upload JSONL file error: %v\n", err)
+		return
+	}
+
+	// create a fine tune job
+	// Streams events until the job is done (this often takes minutes, but can take hours if there are many jobs in the queue or your dataset is large)
+	// use below get method to know the status of your model
+	tune, err := client.CreateFineTune(ctx, openai.FineTuneRequest{
+		TrainingFile: file.ID,
+		Model:        "ada", // babbage, curie, davinci, or a fine-tuned model created after 2022-04-21.
+	})
+	if err != nil {
+		fmt.Printf("Creating new fine tune model error: %v\n", err)
+		return
+	}
+
+	getTune, err := client.GetFineTune(ctx, tune.ID)
+	if err != nil {
+		fmt.Printf("Getting fine tune model error: %v\n", err)
+		return
+	}
+	fmt.Println(getTune.FineTunedModel)
+
+	// once the status of getTune is `succeeded`, you can use your fine tune model in Completion Request
+
+	// resp, err := client.CreateCompletion(ctx, openai.CompletionRequest{
+	//	 Model:  getTune.FineTunedModel,
+	//	 Prompt: "your prompt",
+	// })
+	// if err != nil {
+	//	 fmt.Printf("Create completion error %v\n", err)
+	//	 return
+	// }
+	//
+	// fmt.Println(resp.Choices[0].Text)
+}
+```
+</details>
 See the `examples/` folder for more.
 
 ### Integration tests:
diff --git a/moderation.go b/moderation.go
index a58d759..a32f123 100644
--- a/moderation.go
+++ b/moderation.go
@@ -2,6 +2,7 @@ package openai
 
 import (
 	"context"
+	"errors"
 	"net/http"
 )
 
@@ -15,9 +16,19 @@ import (
 const (
 	ModerationTextStable = "text-moderation-stable"
 	ModerationTextLatest = "text-moderation-latest"
-	ModerationText001    = "text-moderation-001"
+	// Deprecated: use ModerationTextStable and ModerationTextLatest instead.
+	ModerationText001 = "text-moderation-001"
 )
 
+var (
+	ErrModerationInvalidModel = errors.New("this model is not supported with moderation, please use text-moderation-stable or text-moderation-latest instead") //nolint:lll
+)
+
+var validModerationModel = map[string]struct{}{
+	ModerationTextStable: {},
+	ModerationTextLatest: {},
+}
+
 // ModerationRequest represents a request structure for moderation API.
 type ModerationRequest struct {
 	Input string `json:"input,omitempty"`
@@ -63,6 +74,10 @@ type ModerationResponse struct {
 // Moderations — perform a moderation api call over a string.
 // Input can be an array or slice but a string will reduce the complexity.
 func (c *Client) Moderations(ctx context.Context, request ModerationRequest) (response ModerationResponse, err error) {
+	if _, ok := validModerationModel[request.Model]; len(request.Model) > 0 && !ok {
+		err = ErrModerationInvalidModel
+		return
+	}
 	req, err := c.newRequest(ctx, http.MethodPost, c.fullURL("/moderations", request.Model), withBody(&request))
 	if err != nil {
 		return


Test patch:
diff --git a/moderation_test.go b/moderation_test.go
index 4e75613..68f9565 100644
--- a/moderation_test.go
+++ b/moderation_test.go
@@ -27,6 +27,41 @@ func TestModerations(t *testing.T) {
 	checks.NoError(t, err, "Moderation error")
 }
 
+// TestModerationsWithIncorrectModel Tests passing valid and invalid models to moderations endpoint.
+func TestModerationsWithDifferentModelOptions(t *testing.T) {
+	var modelOptions []struct {
+		model  string
+		expect error
+	}
+	modelOptions = append(modelOptions,
+		getModerationModelTestOption(GPT3Dot5Turbo, ErrModerationInvalidModel),
+		getModerationModelTestOption(ModerationTextStable, nil),
+		getModerationModelTestOption(ModerationTextLatest, nil),
+		getModerationModelTestOption("", nil),
+	)
+	client, server, teardown := setupOpenAITestServer()
+	defer teardown()
+	server.RegisterHandler("/v1/moderations", handleModerationEndpoint)
+	for _, modelTest := range modelOptions {
+		_, err := client.Moderations(context.Background(), ModerationRequest{
+			Model: modelTest.model,
+			Input: "I want to kill them.",
+		})
+		checks.ErrorIs(t, err, modelTest.expect,
+			fmt.Sprintf("Moderations(..) expects err: %v, actual err:%v", modelTest.expect, err))
+	}
+}
+
+func getModerationModelTestOption(model string, expect error) struct {
+	model  string
+	expect error
+} {
+	return struct {
+		model  string
+		expect error
+	}{model: model, expect: expect}
+}
+
 // handleModerationEndpoint Handles the moderation endpoint by the test server.
 func handleModerationEndpoint(w http.ResponseWriter, r *http.Request) {
 	var err error