Repository: sashabaranov/go-openai
Pull request #449 (2023-07-19 18:11:59)

feat: make finish reason nullable in json marshal
**Describe the change**

this PR added a `MarshalJSON` to `FinishReason`. 

**Describe your solution**

`MarshalJSON` implemented `Marshaler` interface in package `encoding/json` 

If `FinishReason` is `FinishReasonNull` or empty string, we can render it as `null` instead of `"null"`.

**Tests**

```
c := &ChatCompletionChoice{
	FinishReason: FinishReasonNull,
}
jsonBytes, _ := json.Marshal(c)
fmt.Println(string(jsonBytes)) // it will print { .... "finish_reason": null }
```

Since i am not sure is this PR expected, i didn't add a lot of test cases in *_test.go file in the PR. but it was test locally. 


codecov[bot]: ## [Codecov](https://app.codecov.io/gh/sashabaranov/go-openai/pull/449?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=None) Report
> Merging [#449](https://app.codecov.io/gh/sashabaranov/go-openai/pull/449?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=None) (cf89f4d) into [master](https://app.codecov.io/gh/sashabaranov/go-openai/commit/1876e0c20716afc4d012688bc393ccd5f28def79?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=None) (1876e0c) will **decrease** coverage by `0.25%`.
> The diff coverage is `75.00%`.

> :exclamation: Current head cf89f4d differs from pull request most recent head f7a22b5. Consider uploading reports for the commit f7a22b5 to get more accurate results

```diff
@@            Coverage Diff             @@
##           master     #449      +/-   ##
==========================================
- Coverage   97.03%   96.79%   -0.25%     
==========================================
  Files          17       17              
  Lines         709      717       +8     
==========================================
+ Hits          688      694       +6     
- Misses         15       17       +2     
  Partials        6        6              
```


| [Impacted Files](https://app.codecov.io/gh/sashabaranov/go-openai/pull/449?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=None) | Coverage Δ | |
|---|---|---|
| [chat.go](https://app.codecov.io/gh/sashabaranov/go-openai/pull/449?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=None#diff-Y2hhdC5nbw==) | `91.66% <75.00%> (-8.34%)` | :arrow_down: |



Patch:
diff --git a/chat.go b/chat.go
index 514aaee..8d29b32 100644
--- a/chat.go
+++ b/chat.go
@@ -114,6 +114,13 @@ const (
 	FinishReasonNull          FinishReason = "null"
 )
 
+func (r FinishReason) MarshalJSON() ([]byte, error) {
+	if r == FinishReasonNull || r == "" {
+		return []byte("null"), nil
+	}
+	return []byte(`"` + string(r) + `"`), nil // best effort to not break future API changes
+}
+
 type ChatCompletionChoice struct {
 	Index   int                   `json:"index"`
 	Message ChatCompletionMessage `json:"message"`


Test patch:
diff --git a/chat_test.go b/chat_test.go
index 5723d6c..38d66fa 100644
--- a/chat_test.go
+++ b/chat_test.go
@@ -298,3 +298,34 @@ func getChatCompletionBody(r *http.Request) (ChatCompletionRequest, error) {
 	}
 	return completion, nil
 }
+
+func TestFinishReason(t *testing.T) {
+	c := &ChatCompletionChoice{
+		FinishReason: FinishReasonNull,
+	}
+	resBytes, _ := json.Marshal(c)
+	if !strings.Contains(string(resBytes), `"finish_reason":null`) {
+		t.Error("null should not be quoted")
+	}
+
+	c.FinishReason = ""
+
+	resBytes, _ = json.Marshal(c)
+	if !strings.Contains(string(resBytes), `"finish_reason":null`) {
+		t.Error("null should not be quoted")
+	}
+
+	otherReasons := []FinishReason{
+		FinishReasonStop,
+		FinishReasonLength,
+		FinishReasonFunctionCall,
+		FinishReasonContentFilter,
+	}
+	for _, r := range otherReasons {
+		c.FinishReason = r
+		resBytes, _ = json.Marshal(c)
+		if !strings.Contains(string(resBytes), fmt.Sprintf(`"finish_reason":"%s"`, r)) {
+			t.Errorf("%s should be quoted", r)
+		}
+	}
+}