SoFunction
Updated on 2025-04-11

Use Golang to implement streaming output

In-depth analysis of streaming output

I have been calling the openai key before, but I just do streaming calls according to the document. I only know that the streaming is different from the API, but I have not studied its implementation principle in a systematic way.

Let’s take the official streaming output of openai as the entry point.

Overview

Streaming Output is a pattern in HTTP responses, where the server can send some content to the client immediately when generating some content without waiting for the entire response content to be generated. This method is often used in real-time interaction, high-latency operations, or long-term tasks, such as OpenAI's GPT model to generate streaming conversations.

package main

import (
	"bufio"
	"bytes"
	"encoding/json"
	"fmt"
	"net/http"
	"strings"
	"time"
)

// Define the necessary data structuretype Message struct {
	Role    string `json:"role"`
	Content string `json:"content"`
}

type RequestBody struct {
	Model       string    `json:"model"`
	Messages    []Message `json:"messages"`
	Temperature float64   `json:"temperature"`
	Stream      bool      `json:"stream"`
}

type Choice struct {
	Delta struct {
		Content string `json:"content"`
	} `json:"delta"`
}

type ResponseBody struct {
	Choices []Choice `json:"choices"`
}

const (
	apiURL      = "/v1/chat/completions" // Replace with the actual API address	authToken   = "your-auth-token"                             // Replace with the actual token	model       = "gpt-3.5-turbo"
	temperature = 0.7
)

func StreamHandler(w , r *) {
	// Get input from query parameters	content := ().Get("content")
	if content == "" {
		(w, "Missing 'content' parameter", )
		return
	}

	// Construct the request body	message := Message{
		Role:    "user",
		Content: content,
	}
	requestBody := RequestBody{
		Model:       model,
		Messages:    []Message{message},
		Temperature: temperature,
		Stream:      true,
	}
	jsonData, err := (requestBody)
	if err != nil {
		(w, "Failed to marshal request body", )
		return
	}

	// Create HTTP request	req, err := ("POST", apiURL, (jsonData))
	if err != nil {
		(w, "Failed to create request", )
		return
	}
	("Content-Type", "application/json")
	("Authorization", "Bearer "+authToken)

	// Set up HTTP client	client := &{Timeout:  * 50}
	resp, err := (req)
	if err != nil {
		(w, "Failed to get response", )
		return
	}
	defer ()

	// Set the response header and turn on the streaming output	().Set("Content-Type", "text/event-stream; charset=utf-8")
	().Set("Cache-Control", "no-cache")
	().Set("Connection", "keep-alive")

	// Make sure ResponseWriter supports Flusher	flusher, ok := w.()
	if !ok {
		(w, "Streaming unsupported", )
		return
	}

	// Handle streaming response	scanner := ()
	for () {
		line := ()

		// Process lines starting with "data: "		if (line, "data: ") {
			line = (line, "data: ")
		}
		if line == "[DONE]" {
			break
		}
		if line == "" {
			continue
		}

		// parse the response content		var chunk ResponseBody
		if err := ([]byte(line), &chunk); err != nil {
			continue
		}

		// Send response data to the client step by step		for _, choice := range  {
			content := 
			_, err := ([]byte(content))
			if err != nil {
				(w, "Failed to write response", )
				return
			}
			() // Refresh the buffer		}
	}

	if err := (); err != nil {
		(w, "Scanner error", )
		return
	}
}

func main() {
	("/stream", StreamHandler)
	("Server started at :8080")
	(":8080", nil)
}

Core process

After receiving user input, it is sent to the target API as a content parameter.

Turn on streaming output mode and set Stream: true.

Use to send content received from the remote interface to the client step by step.

Key points

1. Streaming response head settings

().Set("Content-Type", "text/event-stream; charset=utf-8")
().Set("Cache-Control", "no-cache")
().Set("Connection", "keep-alive")

Real-time output: Call () after outputting the content to ensure that the data is sent in real time.

After starting the service, access URLs similar to the following through your browser:

http://localhost:8080/stream?content=Hello%20world

The client will gradually receive content, similar to real-time printing of the command line.

1. Streaming response in HTTP protocol

Streaming output uses the characteristics of the HTTP protocol, without closing the connection, and gradually sending data to the client. A typical streaming response will be set as follows:

Content-Type: text/event-stream means that this is an Event Stream, which is used to continuously send data fragments to the client.

Cache-Control: no-cache prevents responses from being cached to ensure that the client receives real-time content.

Connection: keep-alive Keep the connection active and supports multiple data transfers.

2. How streaming output works

The client initiates a request, and the server starts responding after receiving the request.

The server does not generate complete response content at once, but sends some of the generated data segment by segment.

The client processes the data immediately after receiving it without waiting for the complete response to end.

After the data transmission is complete, the server can choose to close the connection or keep the connection to send subsequent data.

Common application scenarios for streaming output

Live chat: When the chat model is generated word by word/sentence, data can be transmitted in real time.

Log monitoring: Push the server's real-time logs line by line to the front end.

Streaming file transfer: such as large file or video streaming.

Real-time progress update: such as task progress bar update.

This is the end of this article about using Golang to implement streaming output. For more related Golang streaming output content, please search for my previous articles or continue browsing the related articles below. I hope everyone will support me in the future!