Repository: sashabaranov/go-openai
Pull request #220 (2023-04-02 13:00:53)

CompletionBatchingRequestSupport
solve iusse #201   i think this feature is useful
i closed  pr #210   because  I think there are too many unnecessary changes, here is a more concise change to implement
```go
c := openai.NewClient("your token")
	ctx := context.Background()

	
	// test CreateCompletion
	reqNor := CompletionRequest{
		Prompt:    []string{"Lorem ipsum", "Lorem ipsum", "Lorem ipsum"}, //use string or []string 
		Model:     GPT3Curie,
		MaxTokens: 5,
	}
	resp, err := c.CreateCompletion(ctx, reqNor)
	checks.NoError(t, err, "CreateCompletion returned error")
	if err == nil {
		for _, c := range resp.Choices {
			t.Logf("index =`%d`,text is `%s` \n", c.Index, c.Text)
		}
	}
```
`Prompt`   can be assigned `string` or `[]string` type
Streaming is also supported
```go
c := openai.NewClient("your token")
	ctx := context.Background()

	// test CreateCompletionStream
	streq := CompletionRequest{
		Model:     GPT3Curie,
		MaxTokens: 5,
		Prompt:   []string{ "a dog entered the alley", "a dog entered the alley", "a dog entered the alley"},
		Stream:    true,
	}
	stream, err := c.CreateCompletionStream(ctx, streq)
	if err != nil {
		t.Logf("CompletionStream error: %v\n", err)
		return
	}
	
	defer stream.Close()
	type index = int
	choicesMap := make(map[index]string)
	for {
		response, streamErr := stream.Recv()
		if errors.Is(streamErr, io.EOF) {
			t.Logf("CompletionBatch Stream finished")
			break
		}

		if streamErr != nil {
			checks.NoError(t, streamErr, "CompletionBatchStream error:Stream error")
		}
		t.Logf("Stream response: %#+v\n", response.Choices)
		choicesMap[response.Choices[0].Index] += response.Choices[0].Text
	}
	for k, v := range choicesMap {
		t.Logf("choice_index:%d,text:%s", k, v)
	}
```

codecov[bot]: ## [Codecov](https://codecov.io/gh/sashabaranov/go-openai/pull/220?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=None) Report
> Merging [#220](https://codecov.io/gh/sashabaranov/go-openai/pull/220?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=None) (1d77862) into [master](https://codecov.io/gh/sashabaranov/go-openai/commit/b542086cbb22806645d660cbdc2f0f2414aabf86?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=None) (b542086) will **decrease** coverage by `0.55%`.
> The diff coverage is `40.00%`.

```diff
@@            Coverage Diff             @@
##           master     #220      +/-   ##
==========================================
- Coverage   71.94%   71.40%   -0.55%     
==========================================
  Files          21       21              
  Lines         581      591      +10     
==========================================
+ Hits          418      422       +4     
- Misses        124      130       +6     
  Partials       39       39              
```


| [Impacted Files](https://codecov.io/gh/sashabaranov/go-openai/pull/220?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=None) | Coverage Δ | |
|---|---|---|
| [completion.go](https://codecov.io/gh/sashabaranov/go-openai/pull/220?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=None#diff-Y29tcGxldGlvbi5nbw==) | `86.95% <40.00%> (-13.05%)` | :arrow_down: |
| [stream.go](https://codecov.io/gh/sashabaranov/go-openai/pull/220?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=None#diff-c3RyZWFtLmdv) | `70.00% <40.00%> (-6.00%)` | :arrow_down: |

Help us with your feedback. Take ten seconds to tell us [how you rate us](https://about.codecov.io/nps?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=None). Have a feature suggestion? [Share it here.](https://app.codecov.io/gh/feedback/?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=None)


Patch:
diff --git a/completion.go b/completion.go
index 6617e5a..4405761 100644
--- a/completion.go
+++ b/completion.go
@@ -7,8 +7,9 @@ import (
 )
 
 var (
-	ErrCompletionUnsupportedModel   = errors.New("this model is not supported with this method, please use CreateChatCompletion client method instead") //nolint:lll
-	ErrCompletionStreamNotSupported = errors.New("streaming is not supported with this method, please use CreateCompletionStream")                      //nolint:lll
+	ErrCompletionUnsupportedModel              = errors.New("this model is not supported with this method, please use CreateChatCompletion client method instead") //nolint:lll
+	ErrCompletionStreamNotSupported            = errors.New("streaming is not supported with this method, please use CreateCompletionStream")                      //nolint:lll
+	ErrCompletionRequestPromptTypeNotSupported = errors.New("the type of CompletionRequest.Promp only supports string and []string")                               //nolint:lll
 )
 
 // GPT3 Defines the models provided by OpenAI to use when generating
@@ -77,10 +78,16 @@ func checkEndpointSupportsModel(endpoint, model string) bool {
 	return !disabledModelsForEndpoints[endpoint][model]
 }
 
+func checkPromptType(prompt any) bool {
+	_, isString := prompt.(string)
+	_, isStringSlice := prompt.([]string)
+	return isString || isStringSlice
+}
+
 // CompletionRequest represents a request structure for completion API.
 type CompletionRequest struct {
 	Model            string         `json:"model"`
-	Prompt           string         `json:"prompt,omitempty"`
+	Prompt           any            `json:"prompt,omitempty"`
 	Suffix           string         `json:"suffix,omitempty"`
 	MaxTokens        int            `json:"max_tokens,omitempty"`
 	Temperature      float32        `json:"temperature,omitempty"`
@@ -143,6 +150,11 @@ func (c *Client) CreateCompletion(
 		return
 	}
 
+	if !checkPromptType(request.Prompt) {
+		err = ErrCompletionRequestPromptTypeNotSupported
+		return
+	}
+
 	req, err := c.requestBuilder.build(ctx, http.MethodPost, c.fullURL(urlSuffix), request)
 	if err != nil {
 		return
diff --git a/stream.go b/stream.go
index 944546a..64688cd 100644
--- a/stream.go
+++ b/stream.go
@@ -28,6 +28,11 @@ func (c *Client) CreateCompletionStream(
 		return
 	}
 
+	if !checkPromptType(request.Prompt) {
+		err = ErrCompletionRequestPromptTypeNotSupported
+		return
+	}
+
 	request.Stream = true
 	req, err := c.newStreamRequest(ctx, "POST", urlSuffix, request)
 	if err != nil {


Test patch:
diff --git a/completion_test.go b/completion_test.go
index ce95faf..2e30259 100644
--- a/completion_test.go
+++ b/completion_test.go
@@ -98,14 +98,14 @@ func handleCompletionEndpoint(w http.ResponseWriter, r *http.Request) {
 		// generate a random string of length completionReq.Length
 		completionStr := strings.Repeat("a", completionReq.MaxTokens)
 		if completionReq.Echo {
-			completionStr = completionReq.Prompt + completionStr
+			completionStr = completionReq.Prompt.(string) + completionStr
 		}
 		res.Choices = append(res.Choices, CompletionChoice{
 			Text:  completionStr,
 			Index: i,
 		})
 	}
-	inputTokens := numTokens(completionReq.Prompt) * completionReq.N
+	inputTokens := numTokens(completionReq.Prompt.(string)) * completionReq.N
 	completionTokens := completionReq.MaxTokens * completionReq.N
 	res.Usage = Usage{
 		PromptTokens:     inputTokens,
diff --git a/request_builder_test.go b/request_builder_test.go
index 0f14f93..e5b65df 100644
--- a/request_builder_test.go
+++ b/request_builder_test.go
@@ -51,7 +51,7 @@ func TestClientReturnsRequestBuilderErrors(t *testing.T) {
 
 	ctx := context.Background()
 
-	_, err = client.CreateCompletion(ctx, CompletionRequest{})
+	_, err = client.CreateCompletion(ctx, CompletionRequest{Prompt: "testing"})
 	if !errors.Is(err, errTestRequestBuilderFailed) {
 		t.Fatalf("Did not return error when request builder failed: %v", err)
 	}
@@ -146,3 +146,27 @@ func TestClientReturnsRequestBuilderErrors(t *testing.T) {
 		t.Fatalf("Did not return error when request builder failed: %v", err)
 	}
 }
+
+func TestReturnsRequestBuilderErrorsAddtion(t *testing.T) {
+	var err error
+	ts := test.NewTestServer().OpenAITestServer()
+	ts.Start()
+	defer ts.Close()
+
+	config := DefaultConfig(test.GetTestToken())
+	config.BaseURL = ts.URL + "/v1"
+	client := NewClientWithConfig(config)
+	client.requestBuilder = &failingRequestBuilder{}
+
+	ctx := context.Background()
+
+	_, err = client.CreateCompletion(ctx, CompletionRequest{Prompt: 1})
+	if !errors.Is(err, ErrCompletionRequestPromptTypeNotSupported) {
+		t.Fatalf("Did not return error when request builder failed: %v", err)
+	}
+
+	_, err = client.CreateCompletionStream(ctx, CompletionRequest{Prompt: 1})
+	if !errors.Is(err, ErrCompletionRequestPromptTypeNotSupported) {
+		t.Fatalf("Did not return error when request builder failed: %v", err)
+	}
+}