Repository: sashabaranov/go-openai
Pull request #421 (2023-07-02 07:48:16)

docs: added instructions for obtaining OpenAI API key to README
@sashabaranov 

**Describe the change**
Adding instructions on how to obtain the OpenAI API key in README solves the following problems:

1. **Ease and Convenience**: New developers can quickly understand how to obtain the API key, enabling them to start using the library faster.
2. **Preventing Misunderstandings**: Without clear instructions, developers could potentially follow incorrect steps, leading to wasted time or potential security risks.
3. **Reducing Queries**: By providing clear instructions, there's less need for developers to ask questions or make inquiries about obtaining the API key, freeing up time for the maintainers of the library.

**Tests**
None.

**Additional context**
Issue: #73


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

```diff
@@           Coverage Diff           @@
##           master     #421   +/-   ##
=======================================
  Coverage   96.95%   96.95%           
=======================================
  Files          17       17           
  Lines         690      690           
=======================================
  Hits          669      669           
  Misses         15       15           
  Partials        6        6           
```





Patch:
diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md
index f7e4540..44bf697 100644
--- a/.github/PULL_REQUEST_TEMPLATE.md
+++ b/.github/PULL_REQUEST_TEMPLATE.md
@@ -1,12 +1,3 @@
----
-name: Pull Request
-about: Propose changes to the codebase
-title: ''
-labels: ''
-assignees: ''
-
----
-
 A similar PR may already be submitted!
 Please search among the [Pull request](https://github.com/sashabaranov/go-openai/pulls) before creating one.
 
diff --git a/README.md b/README.md
index da1a280..1f708af 100644
--- a/README.md
+++ b/README.md
@@ -52,6 +52,17 @@ func main() {
 
 ```
 
+### Getting an OpenAI API Key:
+
+1. Visit the OpenAI website at [https://platform.openai.com/account/api-keys](https://platform.openai.com/account/api-keys).
+2. If you don't have an account, click on "Sign Up" to create one. If you do, click "Log In".
+3. Once logged in, navigate to your API key management page.
+4. Click on "Create new secret key".
+5. Enter a name for your new key, then click "Create secret key".
+6. Your new API key will be displayed. Use this key to interact with the OpenAI API.
+
+**Note:** Your API key is sensitive information. Do not share it with anyone.
+
 ### Other examples:
 
 <details>
diff --git a/embeddings.go b/embeddings.go
index ba327ce..41af50b 100644
--- a/embeddings.go
+++ b/embeddings.go
@@ -113,10 +113,25 @@ type EmbeddingResponse struct {
 	Usage  Usage          `json:"usage"`
 }
 
-// EmbeddingRequest is the input to a Create embeddings request.
+type EmbeddingRequestConverter interface {
+	// Needs to be of type EmbeddingRequestStrings or EmbeddingRequestTokens
+	Convert() EmbeddingRequest
+}
+
 type EmbeddingRequest struct {
+	Input any            `json:"input"`
+	Model EmbeddingModel `json:"model"`
+	User  string         `json:"user"`
+}
+
+func (r EmbeddingRequest) Convert() EmbeddingRequest {
+	return r
+}
+
+// EmbeddingRequestStrings is the input to a create embeddings request with a slice of strings.
+type EmbeddingRequestStrings struct {
 	// Input is a slice of strings for which you want to generate an Embedding vector.
-	// Each input must not exceed 2048 tokens in length.
+	// Each input must not exceed 8192 tokens in length.
 	// OpenAPI suggests replacing newlines (\n) in your input with a single space, as they
 	// have observed inferior results when newlines are present.
 	// E.g.
@@ -129,15 +144,50 @@ type EmbeddingRequest struct {
 	User string `json:"user"`
 }
 
-// CreateEmbeddings returns an EmbeddingResponse which will contain an Embedding for every item in |request.Input|.
+func (r EmbeddingRequestStrings) Convert() EmbeddingRequest {
+	return EmbeddingRequest{
+		Input: r.Input,
+		Model: r.Model,
+		User:  r.User,
+	}
+}
+
+type EmbeddingRequestTokens struct {
+	// Input is a slice of slices of ints ([][]int) for which you want to generate an Embedding vector.
+	// Each input must not exceed 8192 tokens in length.
+	// OpenAPI suggests replacing newlines (\n) in your input with a single space, as they
+	// have observed inferior results when newlines are present.
+	// E.g.
+	//	"The food was delicious and the waiter..."
+	Input [][]int `json:"input"`
+	// ID of the model to use. You can use the List models API to see all of your available models,
+	// or see our Model overview for descriptions of them.
+	Model EmbeddingModel `json:"model"`
+	// A unique identifier representing your end-user, which will help OpenAI to monitor and detect abuse.
+	User string `json:"user"`
+}
+
+func (r EmbeddingRequestTokens) Convert() EmbeddingRequest {
+	return EmbeddingRequest{
+		Input: r.Input,
+		Model: r.Model,
+		User:  r.User,
+	}
+}
+
+// CreateEmbeddings returns an EmbeddingResponse which will contain an Embedding for every item in |body.Input|.
 // https://beta.openai.com/docs/api-reference/embeddings/create
-func (c *Client) CreateEmbeddings(ctx context.Context, request EmbeddingRequest) (resp EmbeddingResponse, err error) {
-	req, err := c.newRequest(ctx, http.MethodPost, c.fullURL("/embeddings", request.Model.String()), withBody(request))
+//
+// Body should be of type EmbeddingRequestStrings for embedding strings or EmbeddingRequestTokens
+// for embedding groups of text already converted to tokens.
+func (c *Client) CreateEmbeddings(ctx context.Context, conv EmbeddingRequestConverter) (res EmbeddingResponse, err error) { //nolint:lll
+	baseReq := conv.Convert()
+	req, err := c.newRequest(ctx, http.MethodPost, c.fullURL("/embeddings", baseReq.Model.String()), withBody(baseReq))
 	if err != nil {
 		return
 	}
 
-	err = c.sendRequest(req, &resp)
+	err = c.sendRequest(req, &res)
 
 	return
 }


Test patch:
diff --git a/embeddings_test.go b/embeddings_test.go
index d7892cd..47c4f51 100644
--- a/embeddings_test.go
+++ b/embeddings_test.go
@@ -32,6 +32,7 @@ func TestEmbedding(t *testing.T) {
 		BabbageCodeSearchText,
 	}
 	for _, model := range embeddedModels {
+		// test embedding request with strings (simple embedding request)
 		embeddingReq := EmbeddingRequest{
 			Input: []string{
 				"The food was delicious and the waiter",
@@ -46,6 +47,34 @@ func TestEmbedding(t *testing.T) {
 		if !bytes.Contains(marshaled, []byte(`"model":"`+model.String()+`"`)) {
 			t.Fatalf("Expected embedding request to contain model field")
 		}
+
+		// test embedding request with strings
+		embeddingReqStrings := EmbeddingRequestStrings{
+			Input: []string{
+				"The food was delicious and the waiter",
+				"Other examples of embedding request",
+			},
+			Model: model,
+		}
+		marshaled, err = json.Marshal(embeddingReqStrings)
+		checks.NoError(t, err, "Could not marshal embedding request")
+		if !bytes.Contains(marshaled, []byte(`"model":"`+model.String()+`"`)) {
+			t.Fatalf("Expected embedding request to contain model field")
+		}
+
+		// test embedding request with tokens
+		embeddingReqTokens := EmbeddingRequestTokens{
+			Input: [][]int{
+				{464, 2057, 373, 12625, 290, 262, 46612},
+				{6395, 6096, 286, 11525, 12083, 2581},
+			},
+			Model: model,
+		}
+		marshaled, err = json.Marshal(embeddingReqTokens)
+		checks.NoError(t, err, "Could not marshal embedding request")
+		if !bytes.Contains(marshaled, []byte(`"model":"`+model.String()+`"`)) {
+			t.Fatalf("Expected embedding request to contain model field")
+		}
 	}
 }
 
@@ -75,6 +104,15 @@ func TestEmbeddingEndpoint(t *testing.T) {
 			fmt.Fprintln(w, string(resBytes))
 		},
 	)
+	// test create embeddings with strings (simple embedding request)
 	_, err := client.CreateEmbeddings(context.Background(), EmbeddingRequest{})
 	checks.NoError(t, err, "CreateEmbeddings error")
+
+	// test create embeddings with strings
+	_, err = client.CreateEmbeddings(context.Background(), EmbeddingRequestStrings{})
+	checks.NoError(t, err, "CreateEmbeddings strings error")
+
+	// test create embeddings with tokens
+	_, err = client.CreateEmbeddings(context.Background(), EmbeddingRequestTokens{})
+	checks.NoError(t, err, "CreateEmbeddings tokens error")
 }