|
- package gogpt
-
- import (
- "bufio"
- "bytes"
- "context"
- "encoding/json"
- "io"
- "net/http"
- )
-
- type ChatCompletionStreamChoiceDelta struct {
- Content string `json:"content"`
- }
-
- type ChatCompletionStreamChoice struct {
- Index int `json:"index"`
- Delta ChatCompletionStreamChoiceDelta `json:"delta"`
- FinishReason string `json:"finish_reason"`
- }
-
- type ChatCompletionStreamResponse struct {
- ID string `json:"id"`
- Object string `json:"object"`
- Created int64 `json:"created"`
- Model string `json:"model"`
- Choices []ChatCompletionStreamChoice `json:"choices"`
- }
-
- // ChatCompletionStream
- // Note: Perhaps it is more elegant to abstract Stream using generics.
- type ChatCompletionStream struct {
- emptyMessagesLimit uint
- isFinished bool
-
- reader *bufio.Reader
- response *http.Response
- }
-
- func (stream *ChatCompletionStream) Recv() (response ChatCompletionStreamResponse, err error) {
- if stream.isFinished {
- err = io.EOF
- return
- }
-
- var emptyMessagesCount uint
-
- waitForData:
- line, err := stream.reader.ReadBytes('\n')
- if err != nil {
- return
- }
-
- var headerData = []byte("data: ")
- line = bytes.TrimSpace(line)
- if !bytes.HasPrefix(line, headerData) {
- emptyMessagesCount++
- if emptyMessagesCount > stream.emptyMessagesLimit {
- err = ErrTooManyEmptyStreamMessages
- return
- }
-
- goto waitForData
- }
-
- line = bytes.TrimPrefix(line, headerData)
- if string(line) == "[DONE]" {
- stream.isFinished = true
- err = io.EOF
- return
- }
-
- err = json.Unmarshal(line, &response)
- return
- }
-
- func (stream *ChatCompletionStream) Close() {
- stream.response.Body.Close()
- }
-
- // CreateChatCompletionStream — API call to create a chat completion w/ streaming
- // support. It sets whether to stream back partial progress. If set, tokens will be
- // sent as data-only server-sent events as they become available, with the
- // stream terminated by a data: [DONE] message.
- func (c *Client) CreateChatCompletionStream(
- ctx context.Context,
- request ChatCompletionRequest,
- ) (stream *ChatCompletionStream, err error) {
- request.Stream = true
- req, err := c.newStreamRequest(ctx, "POST", "/chat/completions", request)
- if err != nil {
- return
- }
-
- resp, err := c.config.HTTPClient.Do(req) //nolint:bodyclose // body is closed in stream.Close()
- if err != nil {
- return
- }
-
- stream = &ChatCompletionStream{
- emptyMessagesLimit: c.config.EmptyMessagesLimit,
- reader: bufio.NewReader(resp.Body),
- response: resp,
- }
- return
- }
|