package chatcmpl import ( "bufio" "encoding/json" "fmt" "io" "strings" ) // Stream reads Server-Sent Events carrying Chat Completions chunks. type Stream struct { scanner *bufio.Scanner } // NewStream wraps a reader of upstream SSE data. func NewStream(r io.Reader) *Stream { sc := bufio.NewScanner(r) sc.Buffer(make([]byte, 0, 64*1024), 16*1024*1024) return &Stream{scanner: sc} } // Next returns the next chunk. It reports io.EOF once the upstream // stream is finished (either by "[DONE]" or by closing). func (s *Stream) Next() (*StreamChunk, error) { for s.scanner.Scan() { line := strings.TrimRight(s.scanner.Text(), "\r") if line == "" || strings.HasPrefix(line, ":") { continue } if !strings.HasPrefix(line, "data:") { continue } data := strings.TrimSpace(strings.TrimPrefix(line, "data:")) if data == "" { continue } if data == "[DONE]" { return nil, io.EOF } var chunk StreamChunk if err := json.Unmarshal([]byte(data), &chunk); err != nil { return nil, fmt.Errorf("decode upstream chunk: %w", err) } return &chunk, nil } if err := s.scanner.Err(); err != nil { return nil, err } return nil, io.EOF }