Repository: tmc/langchaingo
Pull request #303 (2023-09-27 13:44:20)

bugfix: add missing FinishReason when streaming
This Pull Request addresses an existing issue in the `parseStreamingChatResponse` function. Specifically, the `FinishReason` field in the `ChatResponse` struct was not being populated based on the incoming streamed HTTP response. 

### Changes
- Added logic to capture the `FinishReason` from the `StreamedChatResponsePayload` and assign it to the `ChatResponse`.
- Implemented a unit test to validate this specific behavior, ensuring the `FinishReason` field is correctly populated.

### PR Checklist

- [x] Read the [Contributing documentation](https://github.com/tmc/langchaingo/blob/main/CONTRIBUTING.md).
- [x] Read the [Code of conduct documentation](https://github.com/tmc/langchaingo/blob/main/CODE_OF_CONDUCT.md).
- [x] Name your Pull Request title clearly, concisely, and prefixed with the name of the primarily affected package you changed according to [Good commit messages](https://go.dev/doc/contribute#commit_messages) (such as `memory: add interfaces for X, Y` or `util: add whizzbang helpers`).
- [x] Check that there isn't already a PR that solves the problem the same way to avoid creating a duplicate.
- [x] Provide a description in this PR that addresses **what** the PR is solving, or reference the issue that it solves (e.g. `Fixes #123`).
- [x] Describes the source of new concepts.
- [x] References existing implementations as appropriate.
- [x] Contains test coverage for new functions.
- [x] Passes all [`golangci-lint`](https://golangci-lint.run/) checks.



Patch:
diff --git a/llms/openai/internal/openaiclient/chat.go b/llms/openai/internal/openaiclient/chat.go
index 50001c9b..ea660856 100644
--- a/llms/openai/internal/openaiclient/chat.go
+++ b/llms/openai/internal/openaiclient/chat.go
@@ -221,6 +221,7 @@ func parseStreamingChatResponse(ctx context.Context, r *http.Response, payload *
 		}
 		chunk := []byte(streamResponse.Choices[0].Delta.Content)
 		response.Choices[0].Message.Content += streamResponse.Choices[0].Delta.Content
+		response.Choices[0].FinishReason = streamResponse.Choices[0].FinishReason
 		if streamResponse.Choices[0].Delta.FunctionCall != nil {
 			if response.Choices[0].Message.FunctionCall == nil {
 				response.Choices[0].Message.FunctionCall = streamResponse.Choices[0].Delta.FunctionCall


Test patch:
diff --git a/llms/openai/internal/openaiclient/chat_test.go b/llms/openai/internal/openaiclient/chat_test.go
new file mode 100644
index 00000000..fc6bc977
--- /dev/null
+++ b/llms/openai/internal/openaiclient/chat_test.go
@@ -0,0 +1,32 @@
+package openaiclient
+
+import (
+	"bytes"
+	"context"
+	"io"
+	"net/http"
+	"testing"
+
+	"github.com/stretchr/testify/assert"
+)
+
+func TestParseStreamingChatResponse_FinishReason(t *testing.T) {
+	t.Parallel()
+	mockBody := `data: {"choices":[{"index":0,"delta":{"role":"assistant","content":"hello"},"finish_reason":"stop"}]}`
+	r := &http.Response{
+		StatusCode: http.StatusOK,
+		Body:       io.NopCloser(bytes.NewBufferString(mockBody)),
+	}
+
+	req := &ChatRequest{
+		StreamingFunc: func(ctx context.Context, chunk []byte) error {
+			return nil
+		},
+	}
+
+	resp, err := parseStreamingChatResponse(context.Background(), r, req)
+
+	assert.NoError(t, err)
+	assert.NotNil(t, resp)
+	assert.Equal(t, "stop", resp.Choices[0].FinishReason)
+}