Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

maintain underlying error structs to allow for type conversion #293

Merged
merged 6 commits into from
May 3, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 29 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,13 @@ This library provides Go clients for [OpenAI API](https://platform.openai.com/).
* DALL·E 2
* Whisper

Installation:
### Installation:
```
go get github.com/sashabaranov/go-openai
```


ChatGPT example usage:
### ChatGPT example usage:

```go
package main
Expand Down Expand Up @@ -52,9 +52,7 @@ func main() {

```



Other examples:
### Other examples:

<details>
<summary>ChatGPT streaming completion</summary>
Expand Down Expand Up @@ -462,3 +460,29 @@ func main() {
}
```
</details>

<details>
<summary>Error handling</summary>

Open-AI maintains clear documentation on how to [handle API errors](https://platform.openai.com/docs/guides/error-codes/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
}
}

```
</details>


7 changes: 4 additions & 3 deletions client.go
Original file line number Diff line number Diff line change
Expand Up @@ -144,15 +144,16 @@ func (c *Client) handleErrorResp(resp *http.Response) error {
var errRes ErrorResponse
err := json.NewDecoder(resp.Body).Decode(&errRes)
if err != nil || errRes.Error == nil {
reqErr := RequestError{
reqErr := &RequestError{
HTTPStatusCode: resp.StatusCode,
Err: err,
}
if errRes.Error != nil {
reqErr.Err = errRes.Error
}
return fmt.Errorf("error, %w", &reqErr)
return reqErr
}

errRes.Error.HTTPStatusCode = resp.StatusCode
return fmt.Errorf("error, status code: %d, message: %w", resp.StatusCode, errRes.Error)
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wonder if the previous version was working with the errors.As due to %w usage

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes you are right. I ran into the issue because I was using type conversion instead of errors.As. I suppose the extra information in the documentation would have negated the need for this PR. However I still think it is an improvement for the error type to handle its own messaging and formatting. Also it doesn't hurt to also allow type conversion even if it is no longer the idiomatic methodology

return errRes.Error
}
9 changes: 8 additions & 1 deletion client_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package openai //nolint:testpackage // testing private field

import (
"bytes"
"errors"
"fmt"
"io"
"net/http"
Expand Down Expand Up @@ -106,7 +107,7 @@ func TestHandleErrorResp(t *testing.T) {
}
}`,
)),
expected: "error, status code 401, message: Access denied due to Virtual Network/Firewall rules.",
expected: "error, status code: 401, message: Access denied due to Virtual Network/Firewall rules.",
},
{
name: "503 Model Overloaded",
Expand Down Expand Up @@ -135,6 +136,12 @@ func TestHandleErrorResp(t *testing.T) {
t.Errorf("Unexpected error: %v , expected: %s", err, tc.expected)
t.Fail()
}

e := &APIError{}
if !errors.As(err, &e) {
sashabaranov marked this conversation as resolved.
Show resolved Hide resolved
t.Errorf("(%s) Expected error to be of type APIError", tc.name)
t.Fail()
}
})
}
}
6 changes: 5 additions & 1 deletion error.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,10 @@ type ErrorResponse struct {
}

func (e *APIError) Error() string {
if e.HTTPStatusCode > 0 {
return fmt.Sprintf("error, status code: %d, message: %s", e.HTTPStatusCode, e.Message)
}

return e.Message
}

Expand Down Expand Up @@ -70,7 +74,7 @@ func (e *APIError) UnmarshalJSON(data []byte) (err error) {
}

func (e *RequestError) Error() string {
return fmt.Sprintf("status code %d, message: %s", e.HTTPStatusCode, e.Err)
return fmt.Sprintf("error, status code: %d, message: %s", e.HTTPStatusCode, e.Err)
}

func (e *RequestError) Unwrap() error {
Expand Down