This library provides unofficial Go clients for OpenAI API. We support:
- ChatGPT
- GPT-3, GPT-4
- DALL·E 2, DALL·E 3
- Whisper
go get github.com/sashabaranov/go-openai
Currently, go-openai requires Go version 1.18 or greater.
package main
import (
"context""fmt"
openai "github.com/sashabaranov/go-openai"
)
funcmain() {
client:=openai.NewClient("your token")
resp, err:=client.CreateChatCompletion(
context.Background(),
openai.ChatCompletionRequest{
Model: openai.GPT3Dot5Turbo,
Messages: []openai.ChatCompletionMessage{
{
Role: openai.ChatMessageRoleUser,
Content: "Hello!",
},
},
},
)
iferr!=nil {
fmt.Printf("ChatCompletion error: %v\n", err)
return
}
fmt.Println(resp.Choices[0].Message.Content)
}- Visit the OpenAI website at https://platform.openai.com/account/api-keys.
- If you don't have an account, click on "Sign Up" to create one. If you do, click "Log In".
- Once logged in, navigate to your API key management page.
- Click on "Create new secret key".
- Enter a name for your new key, then click "Create secret key".
- 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.
ChatGPT streaming completion
package main
import (
"context""errors""fmt""io"
openai "github.com/sashabaranov/go-openai"
)
funcmain() {
c:=openai.NewClient("your token")
ctx:=context.Background()
req:= openai.ChatCompletionRequest{
Model: openai.GPT3Dot5Turbo,
MaxTokens: 20,
Messages: []openai.ChatCompletionMessage{
{
Role: openai.ChatMessageRoleUser,
Content: "Lorem ipsum",
},
},
Stream: true,
}
stream, err:=c.CreateChatCompletionStream(ctx, req)
iferr!=nil {
fmt.Printf("ChatCompletionStream error: %v\n", err)
return
}
deferstream.Close()
fmt.Printf("Stream response: ")
for {
response, err:=stream.Recv()
iferrors.Is(err, io.EOF) {
fmt.Println("\nStream finished")
return
}
iferr!=nil {
fmt.Printf("\nStream error: %v\n", err)
return
}
fmt.Printf(response.Choices[0].Delta.Content)
}
}GPT-3 completion
package main
import (
"context""fmt"
openai "github.com/sashabaranov/go-openai"
)
funcmain() {
c:=openai.NewClient("your token")
ctx:=context.Background()
req:= openai.CompletionRequest{
Model: openai.GPT3Ada,
MaxTokens: 5,
Prompt: "Lorem ipsum",
}
resp, err:=c.CreateCompletion(ctx, req)
iferr!=nil {
fmt.Printf("Completion error: %v\n", err)
return
}
fmt.Println(resp.Choices[0].Text)
}GPT-3 streaming completion
package main
import (
"errors""context""fmt""io"
openai "github.com/sashabaranov/go-openai"
)
funcmain() {
c:=openai.NewClient("your token")
ctx:=context.Background()
req:= openai.CompletionRequest{
Model: openai.GPT3Ada,
MaxTokens: 5,
Prompt: "Lorem ipsum",
Stream: true,
}
stream, err:=c.CreateCompletionStream(ctx, req)
iferr!=nil {
fmt.Printf("CompletionStream error: %v\n", err)
return
}
deferstream.Close()
for {
response, err:=stream.Recv()
iferrors.Is(err, io.EOF) {
fmt.Println("Stream finished")
return
}
iferr!=nil {
fmt.Printf("Stream error: %v\n", err)
return
}
fmt.Printf("Stream response: %v\n", response)
}
}Audio Speech-To-Text
package main
import (
"context""fmt"
openai "github.com/sashabaranov/go-openai"
)
funcmain() {
c:=openai.NewClient("your token")
ctx:=context.Background()
req:= openai.AudioRequest{
Model: openai.Whisper1,
FilePath: "recording.mp3",
}
resp, err:=c.CreateTranscription(ctx, req)
iferr!=nil {
fmt.Printf("Transcription error: %v\n", err)
return
}
fmt.Println(resp.Text)
}Audio Captions
package main
import (
"context""fmt""os"
openai "github.com/sashabaranov/go-openai"
)
funcmain() {
c:=openai.NewClient(os.Getenv("OPENAI_KEY"))
req:= openai.AudioRequest{
Model: openai.Whisper1,
FilePath: os.Args[1],
Format: openai.AudioResponseFormatSRT,
}
resp, err:=c.CreateTranscription(context.Background(), req)
iferr!=nil {
fmt.Printf("Transcription error: %v\n", err)
return
}
f, err:=os.Create(os.Args[1] +".srt")
iferr!=nil {
fmt.Printf("Could not open file: %v\n", err)
return
}
deferf.Close()
if_, err:=f.WriteString(resp.Text); err!=nil {
fmt.Printf("Error writing to file: %v\n", err)
return
}
}DALL-E 2 image generation
package main
import (
"bytes""context""encoding/base64""fmt"
openai "github.com/sashabaranov/go-openai""image/png""os"
)
funcmain() {
c:=openai.NewClient("your token")
ctx:=context.Background()
// Sample image by linkreqUrl:= openai.ImageRequest{
Prompt: "Parrot on a skateboard performs a trick, cartoon style, natural light, high detail",
Size: openai.CreateImageSize256x256,
ResponseFormat: openai.CreateImageResponseFormatURL,
N: 1,
}
respUrl, err:=c.CreateImage(ctx, reqUrl)
iferr!=nil {
fmt.Printf("Image creation error: %v\n", err)
return
}
fmt.Println(respUrl.Data[0].URL)
// Example image as base64reqBase64:= openai.ImageRequest{
Prompt: "Portrait of a humanoid parrot in a classic costume, high detail, realistic light, unreal engine",
Size: openai.CreateImageSize256x256,
ResponseFormat: openai.CreateImageResponseFormatB64JSON,
N: 1,
}
respBase64, err:=c.CreateImage(ctx, reqBase64)
iferr!=nil {
fmt.Printf("Image creation error: %v\n", err)
return
}
imgBytes, err:=base64.StdEncoding.DecodeString(respBase64.Data[0].B64JSON)
iferr!=nil {
fmt.Printf("Base64 decode error: %v\n", err)
return
}
r:=bytes.NewReader(imgBytes)
imgData, err:=png.Decode(r)
iferr!=nil {
fmt.Printf("PNG decode error: %v\n", err)
return
}
file, err:=os.Create("example.png")
iferr!=nil {
fmt.Printf("File creation error: %v\n", err)
return
}
deferfile.Close()
iferr:=png.Encode(file, imgData); err!=nil {
fmt.Printf("PNG encode error: %v\n", err)
return
}
fmt.Println("The image was saved as example.png")
}Configuring proxy
config:=openai.DefaultConfig("token")
proxyUrl, err:=url.Parse("http://localhost:{port}")
iferr!=nil {
panic(err)
}
transport:=&http.Transport{
Proxy: http.ProxyURL(proxyUrl),
}
config.HTTPClient=&http.Client{
Transport: transport,
}
c:=openai.NewClientWithConfig(config)See also: https://pkg.go.dev/github.com/sashabaranov/go-openai#ClientConfig
ChatGPT support context
package main
import (
"bufio""context""fmt""os""strings""github.com/sashabaranov/go-openai"
)
funcmain() {
client:=openai.NewClient("your token")
messages:=make([]openai.ChatCompletionMessage, 0)
reader:=bufio.NewReader(os.Stdin)
fmt.Println("Conversation")
fmt.Println("---------------------")
for {
fmt.Print("-> ")
text, _:=reader.ReadString('\n')
// convert CRLF to LFtext=strings.Replace(text, "\n", "", -1)
messages=append(messages, openai.ChatCompletionMessage{
Role: openai.ChatMessageRoleUser,
Content: text,
})
resp, err:=client.CreateChatCompletion(
context.Background(),
openai.ChatCompletionRequest{
Model: openai.GPT3Dot5Turbo,
Messages: messages,
},
)
iferr!=nil {
fmt.Printf("ChatCompletion error: %v\n", err)
continue
}
content:=resp.Choices[0].Message.Contentmessages=append(messages, openai.ChatCompletionMessage{
Role: openai.ChatMessageRoleAssistant,
Content: content,
})
fmt.Println(content)
}
}Azure OpenAI ChatGPT
package main
import (
"context""fmt"
openai "github.com/sashabaranov/go-openai"
)
funcmain() {
config:=openai.DefaultAzureConfig("your Azure OpenAI Key", "https://your Azure OpenAI Endpoint")
// If you use a deployment name different from the model name, you can customize the AzureModelMapperFunc function// config.AzureModelMapperFunc = func(model string) string {// azureModelMapping := map[string]string{// "gpt-3.5-turbo": "your gpt-3.5-turbo deployment name",// }// return azureModelMapping[model]// }client:=openai.NewClientWithConfig(config)
resp, err:=client.CreateChatCompletion(
context.Background(),
openai.ChatCompletionRequest{
Model: openai.GPT3Dot5Turbo,
Messages: []openai.ChatCompletionMessage{
{
Role: openai.ChatMessageRoleUser,
Content: "Hello Azure OpenAI!",
},
},
},
)
iferr!=nil {
fmt.Printf("ChatCompletion error: %v\n", err)
return
}
fmt.Println(resp.Choices[0].Message.Content)
}Embedding Semantic Similarity
package main
import (
"context""log"
openai "github.com/sashabaranov/go-openai"
)
funcmain() {
client:=openai.NewClient("your-token")
// Create an EmbeddingRequest for the user queryqueryReq:= openai.EmbeddingRequest{
Input: []string{"How many chucks would a woodchuck chuck"},
Model: openai.AdaEmbeddingV2,
}
// Create an embedding for the user queryqueryResponse, err:=client.CreateEmbeddings(context.Background(), queryReq)
iferr!=nil {
log.Fatal("Error creating query embedding:", err)
}
// Create an EmbeddingRequest for the target texttargetReq:= openai.EmbeddingRequest{
Input: []string{"How many chucks would a woodchuck chuck if the woodchuck could chuck wood"},
Model: openai.AdaEmbeddingV2,
}
// Create an embedding for the target texttargetResponse, err:=client.CreateEmbeddings(context.Background(), targetReq)
iferr!=nil {
log.Fatal("Error creating target embedding:", err)
}
// Now that we have the embeddings for the user query and the target text, we// can calculate their similarity.queryEmbedding:=queryResponse.Data[0]
targetEmbedding:=targetResponse.Data[0]
similarity, err:=queryEmbedding.DotProduct(&targetEmbedding)
iferr!=nil {
log.Fatal("Error calculating dot product:", err)
}
log.Printf("The similarity score between the query and the target is %f", similarity)
}Azure OpenAI Embeddings
package main
import (
"context""fmt"
openai "github.com/sashabaranov/go-openai"
)
funcmain() {
config:=openai.DefaultAzureConfig("your Azure OpenAI Key", "https://your Azure OpenAI Endpoint")
config.APIVersion="2023-05-15"// optional update to latest API version//If you use a deployment name different from the model name, you can customize the AzureModelMapperFunc function//config.AzureModelMapperFunc = func(model string) string {// azureModelMapping := map[string]string{// "gpt-3.5-turbo":"your gpt-3.5-turbo deployment name",// }// return azureModelMapping[model]//}input:="Text to vectorize"client:=openai.NewClientWithConfig(config)
resp, err:=client.CreateEmbeddings(
context.Background(),
openai.EmbeddingRequest{
Input: []string{input},
Model: openai.AdaEmbeddingV2,
})
iferr!=nil {
fmt.Printf("CreateEmbeddings error: %v\n", err)
return
}
vectors:=resp.Data[0].Embedding// []float32 with 1536 dimensionsfmt.Println(vectors[:10], "...", vectors[len(vectors)-10:])
}JSON Schema for function calling
It is now possible for chat completion to choose to call a function for more information (see developer docs here).
In order to describe the type of functions that can be called, a JSON schema must be provided. Many JSON schema libraries exist and are more advanced than what we can offer in this library, however we have included a simple jsonschema package for those who want to use this feature without formatting their own JSON schema payload.
The developer documents give this JSON schema definition as an example:
{
"name":"get_current_weather",
"description":"Get the current weather in a given location",
"parameters":{
"type":"object",
"properties":{
"location":{
"type":"string",
"description":"The city and state, e.g. San Francisco, CA"
},
"unit":{
"type":"string",
"enum":[
"celsius",
"fahrenheit"
]
}
},
"required":[
"location"
]
}
}Using the jsonschema package, this schema could be created using structs as such:
FunctionDefinition{
Name: "get_current_weather",
Parameters: jsonschema.Definition{
Type: jsonschema.Object,
Properties: map[string]jsonschema.Definition{
"location": {
Type: jsonschema.String,
Description: "The city and state, e.g. San Francisco, CA",
},
"unit": {
Type: jsonschema.String,
Enum: []string{"celsius", "fahrenheit"},
},
},
Required: []string{"location"},
},
}The Parameters field of a FunctionDefinition can accept either of the above styles, or even a nested struct from another library (as long as it can be marshalled into JSON).
Error handling
Open-AI maintains clear documentation on how to handle API errors
example:
e := &openai.APIError{}
if errors.As(err, &e) {
switch e.HTTPStatusCode {
case 401:
// invalid auth or key (do not retry)
case 429:
// rate limiting or engine overload (wait and retry) case 500:
// openai server error (retry)
default:
// unhandled
}
}
Fine Tune Model
package main
import (
"context""fmt""github.com/sashabaranov/go-openai"
)
funcmain() {
client:=openai.NewClient("your token")
ctx:=context.Background()
// create a .jsonl file with your training data for conversational model// {"prompt": "<prompt text>", "completion": "<ideal generated text>"}// {"prompt": "<prompt text>", "completion": "<ideal generated text>"}// {"prompt": "<prompt text>", "completion": "<ideal generated text>"}// chat models are trained using the following file format:// {"messages": [{"role": "system", "content": "Marv is a factual chatbot that is also sarcastic."}, {"role": "user", "content": "What's the capital of France?"}, {"role": "assistant", "content": "Paris, as if everyone doesn't know that already."}]}// {"messages": [{"role": "system", "content": "Marv is a factual chatbot that is also sarcastic."}, {"role": "user", "content": "Who wrote 'Romeo and Juliet'?"}, {"role": "assistant", "content": "Oh, just some guy named William Shakespeare. Ever heard of him?"}]}// {"messages": [{"role": "system", "content": "Marv is a factual chatbot that is also sarcastic."}, {"role": "user", "content": "How far is the Moon from Earth?"}, {"role": "assistant", "content": "Around 384,400 kilometers. Give or take a few, like that really matters."}]}// you can use openai cli tool to validate the data// For more info - https://platform.openai.com/docs/guides/fine-tuningfile, err:=client.CreateFile(ctx, openai.FileRequest{
FilePath: "training_prepared.jsonl",
Purpose: "fine-tune",
})
iferr!=nil {
fmt.Printf("Upload JSONL file error: %v\n", err)
return
}
// create a fine tuning 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 modelfineTuningJob, err:=client.CreateFineTuningJob(ctx, openai.FineTuningJobRequest{
TrainingFile: file.ID,
Model: "davinci-002", // gpt-3.5-turbo-0613, babbage-002.
})
iferr!=nil {
fmt.Printf("Creating new fine tune model error: %v\n", err)
return
}
fineTuningJob, err=client.RetrieveFineTuningJob(ctx, fineTuningJob.ID)
iferr!=nil {
fmt.Printf("Getting fine tune model error: %v\n", err)
return
}
fmt.Println(fineTuningJob.FineTunedModel)
// once the status of fineTuningJob is `succeeded`, you can use your fine tune model in Completion Request or Chat Completion Request// resp, err := client.CreateCompletion(ctx, openai.CompletionRequest{// Model: fineTuningJob.FineTunedModel,// Prompt: "your prompt",// })// if err != nil {// fmt.Printf("Create completion error %v\n", err)// return// }//// fmt.Println(resp.Choices[0].Text)
}Why don't we get the same answer when specifying a temperature field of 0 and asking the same question?
Even when specifying a temperature field of 0, it doesn't guarantee that you'll always get the same response. Several factors come into play.
- Go OpenAI Behavior: When you specify a temperature field of 0 in Go OpenAI, the omitempty tag causes that field to be removed from the request. Consequently, the OpenAI API applies the default value of 1.
- Token Count for Input/Output: If there's a large number of tokens in the input and output, setting the temperature to 0 can still result in non-deterministic behavior. In particular, when using around 32k tokens, the likelihood of non-deterministic behavior becomes highest even with a temperature of 0.
Due to the factors mentioned above, different answers may be returned even for the same question.
Workarounds:
- As of November 2023, use the new
seedparameter in conjunction with thesystem_fingerprintresponse field, alongside Temperature management. - Try using
math.SmallestNonzeroFloat32: By specifyingmath.SmallestNonzeroFloat32in the temperature field instead of 0, you can mimic the behavior of setting it to 0. - Limiting Token Count: By limiting the number of tokens in the input and output and especially avoiding large requests close to 32k tokens, you can reduce the risk of non-deterministic behavior.
By adopting these strategies, you can expect more consistent results.
Related Issues:
omitempty option of request struct will generate incorrect request when parameter is 0.
No, Go OpenAI does not offer a feature to count tokens, and there are no plans to provide such a feature in the future. However, if there's a way to implement a token counting feature with zero dependencies, it might be possible to merge that feature into Go OpenAI. Otherwise, it would be more appropriate to implement it in a dedicated library or repository.
For counting tokens, you might find the following links helpful:
Related Issues:
Is it possible to join the implementation of GPT3 Tokenizer
By following Contributing Guidelines, we hope to ensure that your contributions are made smoothly and efficiently.
We want to take a moment to express our deepest gratitude to the contributors and sponsors of this project:
To all of you: thank you. You've helped us achieve more than we ever imagined possible. Can't wait to see where we go next, together!