Feat Add headers to openai responses (#506)

* feat: add headers to http response

* chore: add test

* fix: rename to httpHeader
This commit is contained in:
Simone Vellei
2023-10-09 17:41:54 +02:00
committed by GitHub
parent 533935e4fc
commit 8e165dc9aa
14 changed files with 107 additions and 2 deletions

View File

@@ -63,6 +63,21 @@ type AudioResponse struct {
Transient bool `json:"transient"` Transient bool `json:"transient"`
} `json:"segments"` } `json:"segments"`
Text string `json:"text"` Text string `json:"text"`
httpHeader
}
type audioTextResponse struct {
Text string `json:"text"`
httpHeader
}
func (r *audioTextResponse) ToAudioResponse() AudioResponse {
return AudioResponse{
Text: r.Text,
httpHeader: r.httpHeader,
}
} }
// CreateTranscription — API call to create a transcription. Returns transcribed text. // CreateTranscription — API call to create a transcription. Returns transcribed text.
@@ -104,7 +119,9 @@ func (c *Client) callAudioAPI(
if request.HasJSONResponse() { if request.HasJSONResponse() {
err = c.sendRequest(req, &response) err = c.sendRequest(req, &response)
} else { } else {
err = c.sendRequest(req, &response.Text) var textResponse audioTextResponse
err = c.sendRequest(req, &textResponse)
response = textResponse.ToAudioResponse()
} }
if err != nil { if err != nil {
return AudioResponse{}, err return AudioResponse{}, err

View File

@@ -142,6 +142,8 @@ type ChatCompletionResponse struct {
Model string `json:"model"` Model string `json:"model"`
Choices []ChatCompletionChoice `json:"choices"` Choices []ChatCompletionChoice `json:"choices"`
Usage Usage `json:"usage"` Usage Usage `json:"usage"`
httpHeader
} }
// CreateChatCompletion — API call to Create a completion for the chat message. // CreateChatCompletion — API call to Create a completion for the chat message.

View File

@@ -16,6 +16,11 @@ import (
"github.com/sashabaranov/go-openai/jsonschema" "github.com/sashabaranov/go-openai/jsonschema"
) )
const (
xCustomHeader = "X-CUSTOM-HEADER"
xCustomHeaderValue = "test"
)
func TestChatCompletionsWrongModel(t *testing.T) { func TestChatCompletionsWrongModel(t *testing.T) {
config := DefaultConfig("whatever") config := DefaultConfig("whatever")
config.BaseURL = "http://localhost/v1" config.BaseURL = "http://localhost/v1"
@@ -68,6 +73,30 @@ func TestChatCompletions(t *testing.T) {
checks.NoError(t, err, "CreateChatCompletion error") checks.NoError(t, err, "CreateChatCompletion error")
} }
// TestCompletions Tests the completions endpoint of the API using the mocked server.
func TestChatCompletionsWithHeaders(t *testing.T) {
client, server, teardown := setupOpenAITestServer()
defer teardown()
server.RegisterHandler("/v1/chat/completions", handleChatCompletionEndpoint)
resp, err := client.CreateChatCompletion(context.Background(), ChatCompletionRequest{
MaxTokens: 5,
Model: GPT3Dot5Turbo,
Messages: []ChatCompletionMessage{
{
Role: ChatMessageRoleUser,
Content: "Hello!",
},
},
})
checks.NoError(t, err, "CreateChatCompletion error")
a := resp.Header().Get(xCustomHeader)
_ = a
if resp.Header().Get(xCustomHeader) != xCustomHeaderValue {
t.Errorf("expected header %s to be %s", xCustomHeader, xCustomHeaderValue)
}
}
// TestChatCompletionsFunctions tests including a function call. // TestChatCompletionsFunctions tests including a function call.
func TestChatCompletionsFunctions(t *testing.T) { func TestChatCompletionsFunctions(t *testing.T) {
client, server, teardown := setupOpenAITestServer() client, server, teardown := setupOpenAITestServer()
@@ -281,6 +310,7 @@ func handleChatCompletionEndpoint(w http.ResponseWriter, r *http.Request) {
TotalTokens: inputTokens + completionTokens, TotalTokens: inputTokens + completionTokens,
} }
resBytes, _ = json.Marshal(res) resBytes, _ = json.Marshal(res)
w.Header().Set(xCustomHeader, xCustomHeaderValue)
fmt.Fprintln(w, string(resBytes)) fmt.Fprintln(w, string(resBytes))
} }

View File

@@ -20,6 +20,20 @@ type Client struct {
createFormBuilder func(io.Writer) utils.FormBuilder createFormBuilder func(io.Writer) utils.FormBuilder
} }
type Response interface {
SetHeader(http.Header)
}
type httpHeader http.Header
func (h *httpHeader) SetHeader(header http.Header) {
*h = httpHeader(header)
}
func (h httpHeader) Header() http.Header {
return http.Header(h)
}
// NewClient creates new OpenAI API client. // NewClient creates new OpenAI API client.
func NewClient(authToken string) *Client { func NewClient(authToken string) *Client {
config := DefaultConfig(authToken) config := DefaultConfig(authToken)
@@ -82,7 +96,7 @@ func (c *Client) newRequest(ctx context.Context, method, url string, setters ...
return req, nil return req, nil
} }
func (c *Client) sendRequest(req *http.Request, v any) error { func (c *Client) sendRequest(req *http.Request, v Response) error {
req.Header.Set("Accept", "application/json; charset=utf-8") req.Header.Set("Accept", "application/json; charset=utf-8")
// Check whether Content-Type is already set, Upload Files API requires // Check whether Content-Type is already set, Upload Files API requires
@@ -103,6 +117,10 @@ func (c *Client) sendRequest(req *http.Request, v any) error {
return c.handleErrorResp(res) return c.handleErrorResp(res)
} }
if v != nil {
v.SetHeader(res.Header)
}
return decodeResponse(res.Body, v) return decodeResponse(res.Body, v)
} }

View File

@@ -154,6 +154,8 @@ type CompletionResponse struct {
Model string `json:"model"` Model string `json:"model"`
Choices []CompletionChoice `json:"choices"` Choices []CompletionChoice `json:"choices"`
Usage Usage `json:"usage"` Usage Usage `json:"usage"`
httpHeader
} }
// CreateCompletion — API call to create a completion. This is the main endpoint of the API. Returns new text as well // CreateCompletion — API call to create a completion. This is the main endpoint of the API. Returns new text as well

View File

@@ -28,6 +28,8 @@ type EditsResponse struct {
Created int64 `json:"created"` Created int64 `json:"created"`
Usage Usage `json:"usage"` Usage Usage `json:"usage"`
Choices []EditsChoice `json:"choices"` Choices []EditsChoice `json:"choices"`
httpHeader
} }
// Edits Perform an API call to the Edits endpoint. // Edits Perform an API call to the Edits endpoint.

View File

@@ -150,6 +150,8 @@ type EmbeddingResponse struct {
Data []Embedding `json:"data"` Data []Embedding `json:"data"`
Model EmbeddingModel `json:"model"` Model EmbeddingModel `json:"model"`
Usage Usage `json:"usage"` Usage Usage `json:"usage"`
httpHeader
} }
type base64String string type base64String string
@@ -182,6 +184,8 @@ type EmbeddingResponseBase64 struct {
Data []Base64Embedding `json:"data"` Data []Base64Embedding `json:"data"`
Model EmbeddingModel `json:"model"` Model EmbeddingModel `json:"model"`
Usage Usage `json:"usage"` Usage Usage `json:"usage"`
httpHeader
} }
// ToEmbeddingResponse converts an embeddingResponseBase64 to an EmbeddingResponse. // ToEmbeddingResponse converts an embeddingResponseBase64 to an EmbeddingResponse.

View File

@@ -12,11 +12,15 @@ type Engine struct {
Object string `json:"object"` Object string `json:"object"`
Owner string `json:"owner"` Owner string `json:"owner"`
Ready bool `json:"ready"` Ready bool `json:"ready"`
httpHeader
} }
// EnginesList is a list of engines. // EnginesList is a list of engines.
type EnginesList struct { type EnginesList struct {
Engines []Engine `json:"data"` Engines []Engine `json:"data"`
httpHeader
} }
// ListEngines Lists the currently available engines, and provides basic // ListEngines Lists the currently available engines, and provides basic

View File

@@ -25,11 +25,15 @@ type File struct {
Status string `json:"status"` Status string `json:"status"`
Purpose string `json:"purpose"` Purpose string `json:"purpose"`
StatusDetails string `json:"status_details"` StatusDetails string `json:"status_details"`
httpHeader
} }
// FilesList is a list of files that belong to the user or organization. // FilesList is a list of files that belong to the user or organization.
type FilesList struct { type FilesList struct {
Files []File `json:"data"` Files []File `json:"data"`
httpHeader
} }
// CreateFile uploads a jsonl file to GPT3 // CreateFile uploads a jsonl file to GPT3

View File

@@ -41,6 +41,8 @@ type FineTune struct {
ValidationFiles []File `json:"validation_files"` ValidationFiles []File `json:"validation_files"`
TrainingFiles []File `json:"training_files"` TrainingFiles []File `json:"training_files"`
UpdatedAt int64 `json:"updated_at"` UpdatedAt int64 `json:"updated_at"`
httpHeader
} }
// Deprecated: On August 22nd, 2023, OpenAI announced the deprecation of the /v1/fine-tunes API. // Deprecated: On August 22nd, 2023, OpenAI announced the deprecation of the /v1/fine-tunes API.
@@ -69,6 +71,8 @@ type FineTuneHyperParams struct {
type FineTuneList struct { type FineTuneList struct {
Object string `json:"object"` Object string `json:"object"`
Data []FineTune `json:"data"` Data []FineTune `json:"data"`
httpHeader
} }
// Deprecated: On August 22nd, 2023, OpenAI announced the deprecation of the /v1/fine-tunes API. // Deprecated: On August 22nd, 2023, OpenAI announced the deprecation of the /v1/fine-tunes API.
@@ -77,6 +81,8 @@ type FineTuneList struct {
type FineTuneEventList struct { type FineTuneEventList struct {
Object string `json:"object"` Object string `json:"object"`
Data []FineTuneEvent `json:"data"` Data []FineTuneEvent `json:"data"`
httpHeader
} }
// Deprecated: On August 22nd, 2023, OpenAI announced the deprecation of the /v1/fine-tunes API. // Deprecated: On August 22nd, 2023, OpenAI announced the deprecation of the /v1/fine-tunes API.
@@ -86,6 +92,8 @@ type FineTuneDeleteResponse struct {
ID string `json:"id"` ID string `json:"id"`
Object string `json:"object"` Object string `json:"object"`
Deleted bool `json:"deleted"` Deleted bool `json:"deleted"`
httpHeader
} }
// Deprecated: On August 22nd, 2023, OpenAI announced the deprecation of the /v1/fine-tunes API. // Deprecated: On August 22nd, 2023, OpenAI announced the deprecation of the /v1/fine-tunes API.

View File

@@ -21,6 +21,8 @@ type FineTuningJob struct {
ValidationFile string `json:"validation_file,omitempty"` ValidationFile string `json:"validation_file,omitempty"`
ResultFiles []string `json:"result_files"` ResultFiles []string `json:"result_files"`
TrainedTokens int `json:"trained_tokens"` TrainedTokens int `json:"trained_tokens"`
httpHeader
} }
type Hyperparameters struct { type Hyperparameters struct {
@@ -39,6 +41,8 @@ type FineTuningJobEventList struct {
Object string `json:"object"` Object string `json:"object"`
Data []FineTuneEvent `json:"data"` Data []FineTuneEvent `json:"data"`
HasMore bool `json:"has_more"` HasMore bool `json:"has_more"`
httpHeader
} }
type FineTuningJobEvent struct { type FineTuningJobEvent struct {

View File

@@ -33,6 +33,8 @@ type ImageRequest struct {
type ImageResponse struct { type ImageResponse struct {
Created int64 `json:"created,omitempty"` Created int64 `json:"created,omitempty"`
Data []ImageResponseDataInner `json:"data,omitempty"` Data []ImageResponseDataInner `json:"data,omitempty"`
httpHeader
} }
// ImageResponseDataInner represents a response data structure for image API. // ImageResponseDataInner represents a response data structure for image API.

View File

@@ -15,6 +15,8 @@ type Model struct {
Permission []Permission `json:"permission"` Permission []Permission `json:"permission"`
Root string `json:"root"` Root string `json:"root"`
Parent string `json:"parent"` Parent string `json:"parent"`
httpHeader
} }
// Permission struct represents an OpenAPI permission. // Permission struct represents an OpenAPI permission.
@@ -38,11 +40,15 @@ type FineTuneModelDeleteResponse struct {
ID string `json:"id"` ID string `json:"id"`
Object string `json:"object"` Object string `json:"object"`
Deleted bool `json:"deleted"` Deleted bool `json:"deleted"`
httpHeader
} }
// ModelsList is a list of models, including those that belong to the user or organization. // ModelsList is a list of models, including those that belong to the user or organization.
type ModelsList struct { type ModelsList struct {
Models []Model `json:"data"` Models []Model `json:"data"`
httpHeader
} }
// ListModels Lists the currently available models, // ListModels Lists the currently available models,

View File

@@ -69,6 +69,8 @@ type ModerationResponse struct {
ID string `json:"id"` ID string `json:"id"`
Model string `json:"model"` Model string `json:"model"`
Results []Result `json:"results"` Results []Result `json:"results"`
httpHeader
} }
// Moderations — perform a moderation api call over a string. // Moderations — perform a moderation api call over a string.