Improve error reporting (#68)

* Provide APIError and use Go's error wrapping

* Add generic request error

* Fix code formatting
This commit is contained in:
Marc Haisenko
2023-02-13 17:43:06 +01:00
committed by GitHub
parent 8fd81bc29a
commit ee3df71880
3 changed files with 88 additions and 9 deletions

9
api.go
View File

@@ -66,9 +66,14 @@ func (c *Client) sendRequest(req *http.Request, v interface{}) error {
var errRes ErrorResponse var errRes ErrorResponse
err = json.NewDecoder(res.Body).Decode(&errRes) err = json.NewDecoder(res.Body).Decode(&errRes)
if err != nil || errRes.Error == nil { if err != nil || errRes.Error == nil {
return fmt.Errorf("error, status code: %d", res.StatusCode) reqErr := RequestError{
StatusCode: res.StatusCode,
Err: err,
}
return fmt.Errorf("error, %w", &reqErr)
} }
return fmt.Errorf("error, status code: %d, message: %s", res.StatusCode, errRes.Error.Message) errRes.Error.StatusCode = res.StatusCode
return fmt.Errorf("error, status code: %d, message: %w", res.StatusCode, errRes.Error)
} }
if v != nil { if v != nil {

View File

@@ -81,6 +81,53 @@ func TestAPI(t *testing.T) {
} }
} }
func TestAPIError(t *testing.T) {
apiToken := os.Getenv("OPENAI_TOKEN")
if apiToken == "" {
t.Skip("Skipping testing against production OpenAI API. Set OPENAI_TOKEN environment variable to enable it.")
}
var err error
c := NewClient(apiToken + "_invalid")
ctx := context.Background()
_, err = c.ListEngines(ctx)
if err == nil {
t.Fatal("ListEngines did not fail")
}
var apiErr *APIError
if !errors.As(err, &apiErr) {
t.Fatalf("Error is not an APIError: %+v", err)
}
if apiErr.StatusCode != 401 {
t.Fatalf("Unexpected API error status code: %d", apiErr.StatusCode)
}
if *apiErr.Code != "invalid_api_key" {
t.Fatalf("Unexpected API error code: %s", *apiErr.Code)
}
}
func TestRequestError(t *testing.T) {
var err error
c := NewClient("dummy")
c.BaseURL = "https://httpbin.org/status/418?"
ctx := context.Background()
_, err = c.ListEngines(ctx)
if err == nil {
t.Fatal("ListEngines request did not fail")
}
var reqErr *RequestError
if !errors.As(err, &reqErr) {
t.Fatalf("Error is not a RequestError: %+v", err)
}
if reqErr.StatusCode != 418 {
t.Fatalf("Unexpected request error status code: %d", reqErr.StatusCode)
}
}
// numTokens Returns the number of GPT-3 encoded tokens in the given text. // numTokens Returns the number of GPT-3 encoded tokens in the given text.
// This function approximates based on the rule of thumb stated by OpenAI: // This function approximates based on the rule of thumb stated by OpenAI:
// https://beta.openai.com/tokenizer // https://beta.openai.com/tokenizer

View File

@@ -1,10 +1,37 @@
package gogpt package gogpt
type ErrorResponse struct { import "fmt"
Error *struct {
Code *int `json:"code,omitempty"` // APIError provides error information returned by the OpenAI API.
Message string `json:"message"` type APIError struct {
Param *string `json:"param,omitempty"` Code *string `json:"code,omitempty"`
Type string `json:"type"` Message string `json:"message"`
} `json:"error,omitempty"` Param *string `json:"param,omitempty"`
Type string `json:"type"`
StatusCode int `json:"-"`
}
// RequestError provides informations about generic request errors.
type RequestError struct {
StatusCode int
Err error
}
type ErrorResponse struct {
Error *APIError `json:"error,omitempty"`
}
func (e *APIError) Error() string {
return e.Message
}
func (e *RequestError) Error() string {
if e.Err != nil {
return e.Err.Error()
}
return fmt.Sprintf("status code %d", e.StatusCode)
}
func (e *RequestError) Unwrap() error {
return e.Err
} }