Repository: sashabaranov/go-openai
Pull request #463 (2023-07-30 20:54:04)

docs: Add Contributing Guidelines
**Describe the change**

- Contributing Guidelines have been added. For details, refer to CONTRIBUTING.md.
- A link to CONTRIBUTING.md has been added in the README.md.
- The Integration Test section from README.md has been moved to CONTRIBUTING.md.
- The outlines for Installation and Usage in README.md have been aligned.

**Tests**
None

**Additional context**
None


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

```diff
@@           Coverage Diff           @@
##           master     #463   +/-   ##
=======================================
  Coverage   97.05%   97.05%           
=======================================
  Files          17       17           
  Lines         714      714           
=======================================
  Hits          693      693           
  Misses         15       15           
  Partials        6        6           
```




vvatanabe: @sashabaranov I've written the Contributing Guidelines. Please check them. If there are any rules you'd like to add, remove, or update, please let me know.
vvatanabe: @sashabaranov It looks like you're busy, so I'll go ahead and merge this for now. If there are any issues or feedback regarding this guideline, I'll respond right away.
vvatanabe: Oh, I see. I can't approve a pull request that I created myself.

Patch:
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
new file mode 100644
index 0000000..4dd1840
--- /dev/null
+++ b/CONTRIBUTING.md
@@ -0,0 +1,88 @@
+# Contributing Guidelines
+
+## Overview
+Thank you for your interest in contributing to the "Go OpenAI" project! By following this guideline, we hope to ensure that your contributions are made smoothly and efficiently. The Go OpenAI project is licensed under the [Apache 2.0 License](https://github.com/sashabaranov/go-openai/blob/master/LICENSE), and we welcome contributions through GitHub pull requests.
+
+## Reporting Bugs
+If you discover a bug, first check the [GitHub Issues page](https://github.com/sashabaranov/go-openai/issues) to see if the issue has already been reported. If you're reporting a new issue, please use the "Bug report" template and provide detailed information about the problem, including steps to reproduce it.
+
+## Suggesting Features
+If you want to suggest a new feature or improvement, first check the [GitHub Issues page](https://github.com/sashabaranov/go-openai/issues) to ensure a similar suggestion hasn't already been made. Use the "Feature request" template to provide a detailed description of your suggestion.
+
+## Reporting Vulnerabilities
+If you identify a security concern, please use the "Report a security vulnerability" template on the [GitHub Issues page](https://github.com/sashabaranov/go-openai/issues) to share the details. This report will only be viewable to repository maintainers. You will be credited if the advisory is published.
+
+## Questions for Users
+If you have questions, please utilize [StackOverflow](https://stackoverflow.com/) or the [GitHub Discussions page](https://github.com/sashabaranov/go-openai/discussions).
+
+## Contributing Code
+There might already be a similar pull requests submitted! Please search for [pull requests](https://github.com/sashabaranov/go-openai/pulls) before creating one.
+
+### Requirements for Merging a Pull Request
+
+The requirements to accept a pull request are as follows:
+
+- Features not provided by the OpenAI API will not be accepted.
+- The functionality of the feature must match that of the official OpenAI API.
+- All pull requests should be written in Go according to common conventions, formatted with `goimports`, and free of warnings from tools like `golangci-lint`.
+- Include tests and ensure all tests pass.
+- Maintain test coverage without any reduction.
+- All pull requests require approval from at least one Go OpenAI maintainer.
+
+**Note:**  
+The merging method for pull requests in this repository is squash merge.
+
+### Creating a Pull Request
+- Fork the repository.
+- Create a new branch and commit your changes.
+- Push that branch to GitHub.
+- Start a new Pull Request on GitHub. (Please use the pull request template to provide detailed information.)
+
+**Note:**  
+If your changes introduce breaking changes, please prefix your pull request title with "[BREAKING_CHANGES]".
+
+### Code Style
+In this project, we adhere to the standard coding style of Go. Your code should maintain consistency with the rest of the codebase. To achieve this, please format your code using tools like `goimports` and resolve any syntax or style issues with `golangci-lint`.
+
+**Run goimports:**
+```
+go install golang.org/x/tools/cmd/goimports@latest
+```
+
+```
+goimports -w .
+```
+
+**Run golangci-lint:**
+```
+go install github.com/golangci/golangci-lint/cmd/golangci-lint@latest
+```
+
+```
+golangci-lint run --out-format=github-actions
+```
+
+### Unit Test
+Please create or update tests relevant to your changes. Ensure all tests run successfully to verify that your modifications do not adversely affect other functionalities.
+
+**Run test:**
+```
+go test -v ./...
+```
+
+### Integration Test
+Integration tests are requested against the production version of the OpenAI API. These tests will verify that the library is properly coded against the actual behavior of the API, and will  fail upon any incompatible change in the API.
+
+**Notes:**
+These tests send real network traffic to the OpenAI API and may reach rate limits. Temporary network problems may also cause the test to fail.
+
+**Run integration test:**
+```
+OPENAI_TOKEN=XXX go test -v -tags=integration ./api_integration_test.go
+```
+
+If the `OPENAI_TOKEN` environment variable is not available, integration tests will be skipped.
+
+---
+
+We wholeheartedly welcome your active participation. Let's build an amazing project together!
diff --git a/README.md b/README.md
index d627a19..9714d89 100644
--- a/README.md
+++ b/README.md
@@ -10,12 +10,16 @@ This library provides unofficial Go clients for [OpenAI API](https://platform.op
 * DALL·E 2
 * Whisper
 
-### Installation:
+## Installation
+
 ```
 go get github.com/sashabaranov/go-openai
 ```
 Currently, go-openai requires Go version 1.18 or greater.
 
+
+## Usage
+
 ### ChatGPT example usage:
 
 ```go
@@ -680,20 +684,6 @@ func main() {
 </details>
 See the `examples/` folder for more.
 
-### Integration tests:
-
-Integration tests are requested against the production version of the OpenAI API. These tests will verify that the library is properly coded against the actual behavior of the API, and will  fail upon any incompatible change in the API.
-
-**Notes:**
-These tests send real network traffic to the OpenAI API and may reach rate limits. Temporary network problems may also cause the test to fail.
-
-**Run tests using:**
-```
-OPENAI_TOKEN=XXX go test -v -tags=integration ./api_integration_test.go
-```
-
-If the `OPENAI_TOKEN` environment variable is not available, integration tests will be skipped.
-
 ## Frequently Asked Questions
 
 ### Why don't we get the same answer when specifying a temperature field of 0 and asking the same question?
@@ -725,6 +715,10 @@ For counting tokens, you might find the following links helpful:
 **Related Issues:**  
 [Is it possible to join the implementation of GPT3 Tokenizer](https://github.com/sashabaranov/go-openai/issues/62)
 
+## Contributing
+
+By following [Contributing Guidelines](https://github.com/sashabaranov/go-openai/blob/master/CONTRIBUTING.md), we hope to ensure that your contributions are made smoothly and efficiently.
+
 ## Thank you
 
 We want to take a moment to express our deepest gratitude to the [contributors](https://github.com/sashabaranov/go-openai/graphs/contributors) and sponsors of this project:
diff --git a/error.go b/error.go
index f68e928..b2d01e2 100644
--- a/error.go
+++ b/error.go
@@ -7,12 +7,20 @@ import (
 )
 
 // APIError provides error information returned by the OpenAI API.
+// InnerError struct is only valid for Azure OpenAI Service.
 type APIError struct {
-	Code           any     `json:"code,omitempty"`
-	Message        string  `json:"message"`
-	Param          *string `json:"param,omitempty"`
-	Type           string  `json:"type"`
-	HTTPStatusCode int     `json:"-"`
+	Code           any         `json:"code,omitempty"`
+	Message        string      `json:"message"`
+	Param          *string     `json:"param,omitempty"`
+	Type           string      `json:"type"`
+	HTTPStatusCode int         `json:"-"`
+	InnerError     *InnerError `json:"innererror,omitempty"`
+}
+
+// InnerError Azure Content filtering. Only valid for Azure OpenAI Service.
+type InnerError struct {
+	Code                 string               `json:"code,omitempty"`
+	ContentFilterResults ContentFilterResults `json:"content_filter_result,omitempty"`
 }
 
 // RequestError provides informations about generic request errors.
@@ -61,6 +69,13 @@ func (e *APIError) UnmarshalJSON(data []byte) (err error) {
 		}
 	}
 
+	if _, ok := rawMap["innererror"]; ok {
+		err = json.Unmarshal(rawMap["innererror"], &e.InnerError)
+		if err != nil {
+			return
+		}
+	}
+
 	// optional fields
 	if _, ok := rawMap["param"]; ok {
 		err = json.Unmarshal(rawMap["param"], &e.Param)


Test patch:
diff --git a/error_test.go b/error_test.go
index e2309ab..a0806b7 100644
--- a/error_test.go
+++ b/error_test.go
@@ -3,6 +3,7 @@ package openai_test
 import (
 	"errors"
 	"net/http"
+	"reflect"
 	"testing"
 
 	. "github.com/sashabaranov/go-openai"
@@ -57,6 +58,77 @@ func TestAPIErrorUnmarshalJSON(t *testing.T) {
 				assertAPIErrorMessage(t, apiErr, "")
 			},
 		},
+		{
+			name: "parse succeeds when the innerError is not exists (Azure Openai)",
+			response: `{
+						"message": "test message",
+						"type": null,
+						"param": "prompt",
+						"code": "content_filter",
+						"status": 400,
+						"innererror": {
+							"code": "ResponsibleAIPolicyViolation",
+							"content_filter_result": {
+								"hate": {
+									"filtered": false,
+									"severity": "safe"
+								},
+								"self_harm": {
+									"filtered": false,
+									"severity": "safe"
+								},
+								"sexual": {
+									"filtered": true,
+									"severity": "medium"
+								},
+								"violence": {
+									"filtered": false,
+									"severity": "safe"
+								}
+							}
+						}
+					}`,
+			hasError: false,
+			checkFunc: func(t *testing.T, apiErr APIError) {
+				assertAPIErrorInnerError(t, apiErr, &InnerError{
+					Code: "ResponsibleAIPolicyViolation",
+					ContentFilterResults: ContentFilterResults{
+						Hate: Hate{
+							Filtered: false,
+							Severity: "safe",
+						},
+						SelfHarm: SelfHarm{
+							Filtered: false,
+							Severity: "safe",
+						},
+						Sexual: Sexual{
+							Filtered: true,
+							Severity: "medium",
+						},
+						Violence: Violence{
+							Filtered: false,
+							Severity: "safe",
+						},
+					},
+				})
+			},
+		},
+		{
+			name:     "parse succeeds when the innerError is empty (Azure Openai)",
+			response: `{"message": "","type": null,"param": "","code": "","status": 0,"innererror": {}}`,
+			hasError: false,
+			checkFunc: func(t *testing.T, apiErr APIError) {
+				assertAPIErrorInnerError(t, apiErr, &InnerError{})
+			},
+		},
+		{
+			name:     "parse succeeds when the innerError is not InnerError struct (Azure Openai)",
+			response: `{"message": "","type": null,"param": "","code": "","status": 0,"innererror": "test"}`,
+			hasError: true,
+			checkFunc: func(t *testing.T, apiErr APIError) {
+				assertAPIErrorInnerError(t, apiErr, &InnerError{})
+			},
+		},
 		{
 			name:     "parse failed when the message is object",
 			response: `{"message":{},"type":"invalid_request_error","param":null,"code":null}`,
@@ -152,6 +224,12 @@ func assertAPIErrorMessage(t *testing.T, apiErr APIError, expected string) {
 	}
 }
 
+func assertAPIErrorInnerError(t *testing.T, apiErr APIError, expected interface{}) {
+	if !reflect.DeepEqual(apiErr.InnerError, expected) {
+		t.Errorf("Unexpected APIError InnerError: %v; expected: %v; ", apiErr, expected)
+	}
+}
+
 func assertAPIErrorCode(t *testing.T, apiErr APIError, expected interface{}) {
 	switch v := apiErr.Code.(type) {
 	case int: