기본적으로 OpenAI API에 요청을 보내면 모델이 출력을 모두 생성한 후 API가 단일 HTTP 응답으로 결과를 반환합니다. 출력이 길면 응답을 받기까지 시간이 걸릴 수 있습니다. 응답을 스트리밍하면 모델이 응답을 계속 생성하는 동안 먼저 도착한 부분부터 표시하거나 처리할 수 있습니다.
이 가이드에서는 서버 전송 이벤트(SSE)를 사용하는 HTTP 스트리밍(stream=true)을 다룹니다. WebSocket 연결을 유지하면서 previous_response_id로 입력을 점진적으로 추가하는 방법은 Responses API의 WebSocket 모드를 참고하세요.
응답 스트리밍을 시작하려면 Responses 엔드포인트에 보내는 요청에 stream=True를 설정하세요.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17import { OpenAI } from "openai";
const client = new OpenAI();
const stream = await client.responses.create({
model: "gpt-6-astra",
input: [
{
role: "user",
content: "Say 'double bubble bath' ten times fast.",
},
],
stream: true,
});
for await (const event of stream) {
console.log(event);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17from openai import OpenAI
client = OpenAI()
stream = client.responses.create(
model="gpt-6-astra",
input=[
{
"role": "user",
"content": "Say 'double bubble bath' ten times fast.",
},
],
stream=True,
)
for event in stream:
print(event)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23package main
import (
"context"
"fmt"
"github.com/openai/openai-go/v3"
"github.com/openai/openai-go/v3/responses"
)
func main() {
client := openai.NewClient()
stream := client.Responses.NewStreaming(context.Background(), responses.ResponseNewParams{
Model: "gpt-6-astra",
Input: responses.ResponseNewParamsInputUnion{OfString: openai.String("Say 'double bubble bath' ten times fast.")},
})
for stream.Next() {
fmt.Println(stream.Current().Type)
}
if err := stream.Err(); err != nil {
panic(err)
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15import com.openai.client.OpenAIClient;
import com.openai.client.okhttp.OpenAIOkHttpClient;
import com.openai.core.http.StreamResponse;
import com.openai.models.responses.ResponseCreateParams;
import com.openai.models.responses.ResponseStreamEvent;
ResponseCreateParams params =
ResponseCreateParams.builder()
.model("gpt-6-astra")
.input("Say 'double bubble bath' ten times fast.")
.build();
try (StreamResponse<ResponseStreamEvent> stream = client.responses().createStreaming(params)) {
stream.stream().forEach(System.out::println);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18using OpenAI.Responses;
#pragma warning disable OPENAI001
string key = Environment.GetEnvironmentVariable("OPENAI_API_KEY")!;
ResponsesClient client = new(key);
var responses = client.CreateResponseStreamingAsync(
"gpt-6-astra",
"Say 'double bubble bath' ten times fast."
);
await foreach (StreamingResponseUpdate response in responses)
{
if (response is StreamingResponseOutputTextDeltaUpdate delta)
{
Console.Write(delta.Delta);
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17require "openai"
openai = OpenAI::Client.new
stream = openai.responses.stream(
model: "gpt-6-astra",
input: [
{
role: "user",
content: "Say 'double bubble bath' ten times fast."
}
]
)
stream.each do |event|
puts(event)
end
Responses API는 스트리밍에 시맨틱 이벤트를 사용합니다. 각 이벤트의 타입은 미리 정의된 스키마를 따르므로 필요한 이벤트만 골라 수신할 수 있습니다.
전체 이벤트 타입 목록은 스트리밍 API 레퍼런스를 참고하세요. 다음은 몇 가지 예시입니다.
1
2
3
4
5
6
7
8
9for await (const event of stream) {
if (event.type === "response.output_text.delta") {
process.stdout.write(event.delta);
} else if (event.type === "response.completed") {
console.log("\nResponse completed.");
} else if (event.type === "error") {
console.error(event.message);
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26StreamingEvent = (
ResponseCreatedEvent
| ResponseInProgressEvent
| ResponseFailedEvent
| ResponseCompletedEvent
| ResponseOutputItemAdded
| ResponseOutputItemDone
| ResponseContentPartAdded
| ResponseContentPartDone
| ResponseOutputTextDelta
| ResponseOutputTextAnnotationAdded
| ResponseTextDone
| ResponseRefusalDelta
| ResponseRefusalDone
| ResponseFunctionCallArgumentsDelta
| ResponseFunctionCallArgumentsDone
| ResponseFileSearchCallInProgress
| ResponseFileSearchCallSearching
| ResponseFileSearchCallCompleted
| ResponseCodeInterpreterInProgress
| ResponseCodeInterpreterCallCodeDelta
| ResponseCodeInterpreterCallCodeDone
| ResponseCodeInterpreterCallInterpreting
| ResponseCodeInterpreterCallCompleted
| Error
)
1type StreamingEvent = responses.ResponseStreamEventUnion
1
2
3
4
5
6
7
8
9
10
11
12import com.openai.client.OpenAIClient;
import com.openai.client.okhttp.OpenAIOkHttpClient;
import com.openai.core.http.StreamResponse;
import com.openai.models.responses.ResponseCreateParams;
import com.openai.models.responses.ResponseStreamEvent;
ResponseCreateParams params =
ResponseCreateParams.builder().model("gpt-5.5").input("Say hello.").build();
try (StreamResponse<ResponseStreamEvent> stream = client.responses().createStreaming(params)) {
stream.stream().forEach(System.out::println);
}
1
2
3
4
5require "openai"
client = OpenAI::Client.new
stream = client.responses.stream(model: "gpt-5.5", input: "Say hello.")
stream.each { |event| puts(event) }
Chat Completions에서도 간단히 응답을 스트리밍할 수 있습니다. 다만 스트리밍에는 Responses API를 권장합니다. 스트리밍을 고려해 설계된 Responses API는 시맨틱 이벤트를 사용하며 타입 안전성을 보장합니다.
응답을 스트리밍하려면 Chat Completions 엔드포인트나 레거시 Completions 엔드포인트를 호출할 때 stream=True를 설정하세요. 그러면 데이터만 담긴 서버 전송 이벤트로 응답을 스트리밍하는 객체가 반환됩니다.
응답은 이벤트 스트림을 통해 청크 단위로 순차적으로 전달됩니다. 다음과 같이 for 루프로 이벤트 스트림을 순회할 수 있습니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19import OpenAI from "openai";
const openai = new OpenAI();
const stream = await openai.chat.completions.create({
model: "gpt-6-astra",
messages: [
{
role: "user",
content: "Say 'double bubble bath' ten times fast.",
},
],
stream: true,
});
for await (const chunk of stream) {
console.log(chunk);
console.log(chunk.choices[0].delta);
console.log("****************");
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19from openai import OpenAI
client = OpenAI()
stream = client.chat.completions.create(
model="gpt-6-astra",
messages=[
{
"role": "user",
"content": "Say 'double bubble bath' ten times fast.",
},
],
stream=True,
)
for chunk in stream:
print(chunk)
print(chunk.choices[0].delta)
print("****************")
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24package main
import (
"context"
"fmt"
"github.com/openai/openai-go/v3"
)
func main() {
client := openai.NewClient()
stream := client.Chat.Completions.NewStreaming(context.Background(), openai.ChatCompletionNewParams{
Model: "gpt-6-astra",
Messages: []openai.ChatCompletionMessageParamUnion{
openai.UserMessage("Say 'double bubble bath' ten times fast."),
},
})
for stream.Next() {
fmt.Println(stream.Current())
}
if err := stream.Err(); err != nil {
panic(err)
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16import com.openai.client.OpenAIClient;
import com.openai.client.okhttp.OpenAIOkHttpClient;
import com.openai.core.http.StreamResponse;
import com.openai.models.chat.completions.ChatCompletionChunk;
import com.openai.models.chat.completions.ChatCompletionCreateParams;
ChatCompletionCreateParams params =
ChatCompletionCreateParams.builder()
.model("gpt-6-astra")
.addUserMessage("Say hello.")
.build();
try (StreamResponse<ChatCompletionChunk> stream =
client.chat().completions().createStreaming(params)) {
stream.stream().forEach(System.out::println);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15using System.ClientModel.Primitives;
using OpenAI.Chat;
string key = Environment.GetEnvironmentVariable("OPENAI_API_KEY")!;
string model = "gpt-6-astra";
ChatClient client = new(model, key);
await foreach (
StreamingChatCompletionUpdate update in client.CompleteChatStreamingAsync(
new UserChatMessage("Say double bubble bath ten times fast.")
)
)
{
Console.WriteLine(ModelReaderWriter.Write(update));
}
1
2
3
4
5
6
7
8
9
10
11
12require "openai"
client = OpenAI::Client.new
stream = client.chat.completions.stream(
model: "gpt-6-astra", messages: [
{
role: :user,
content: "Say hello."
}
]
)
stream.each { |event| puts(event) }
OpenAI SDK를 사용하면 각 이벤트를 해당 타입의 인스턴스로 받습니다. 이벤트의 type 속성으로도 개별 이벤트를 구분할 수 있습니다.
응답 수명 주기의 주요 이벤트 중에는 한 번만 발생하는 이벤트도 있고, 응답을 생성하는 동안 여러 번 발생하는 이벤트도 있습니다. 텍스트를 스트리밍할 때 주로 수신하는 이벤트는 다음과 같습니다.
- `response.created`
- `response.output_text.delta`
- `response.completed`
- `error`
수신할 수 있는 전체 이벤트 목록은 스트리밍 API 레퍼런스를 참고하세요.
채팅 응답을 스트리밍하면 응답에 delta 필드가 message 필드 대신 포함됩니다. delta 필드에는 역할 토큰이나 콘텐츠 토큰이 들어갈 수 있으며, 비어 있을 수도 있습니다.
{ role: 'assistant', content: '', refusal: null }
****************
{ content: 'Why' }
****************
{ content: " don't" }
****************
{ content: ' scientists' }
****************
{ content: ' trust' }
****************
{ content: ' atoms' }
****************
{ content: '?\n\n' }
****************
{ content: 'Because' }
****************
{ content: ' they' }
****************
{ content: ' make' }
****************
{ content: ' up' }
****************
{ content: ' everything' }
****************
{ content: '!' }
****************
{}
****************
채팅 응답에서 텍스트만 스트리밍하려면 다음과 같이 작성하세요.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17import OpenAI from "openai";
const client = new OpenAI();
const stream = await client.chat.completions.create({
model: "gpt-6-astra",
messages: [
{
role: "user",
content: "Say 'double bubble bath' ten times fast.",
},
],
stream: true,
});
for await (const chunk of stream) {
process.stdout.write(chunk.choices[0]?.delta?.content || "");
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18from openai import OpenAI
client = OpenAI()
stream = client.chat.completions.create(
model="gpt-6-astra",
messages=[
{
"role": "user",
"content": "Say 'double bubble bath' ten times fast.",
},
],
stream=True,
)
for chunk in stream:
if chunk.choices[0].delta.content is not None:
print(chunk.choices[0].delta.content, end="")
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26package main
import (
"context"
"fmt"
"github.com/openai/openai-go/v3"
)
func main() {
client := openai.NewClient()
stream := client.Chat.Completions.NewStreaming(context.Background(), openai.ChatCompletionNewParams{
Model: "gpt-6-astra",
Messages: []openai.ChatCompletionMessageParamUnion{
openai.UserMessage("Say 'double bubble bath' ten times fast."),
},
})
for stream.Next() {
if len(stream.Current().Choices) > 0 {
fmt.Print(stream.Current().Choices[0].Delta.Content)
}
}
if err := stream.Err(); err != nil {
panic(err)
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19import com.openai.client.OpenAIClient;
import com.openai.client.okhttp.OpenAIOkHttpClient;
import com.openai.core.http.StreamResponse;
import com.openai.models.chat.completions.ChatCompletionChunk;
import com.openai.models.chat.completions.ChatCompletionCreateParams;
ChatCompletionCreateParams params =
ChatCompletionCreateParams.builder()
.model("gpt-6-astra")
.addUserMessage("Say 'double bubble bath' ten times fast.")
.build();
try (StreamResponse<ChatCompletionChunk> stream =
client.chat().completions().createStreaming(params)) {
stream.stream()
.flatMap(chunk -> chunk.choices().stream())
.flatMap(choice -> choice.delta().content().stream())
.forEach(System.out::print);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17using OpenAI.Chat;
string key = Environment.GetEnvironmentVariable("OPENAI_API_KEY")!;
string model = "gpt-6-astra";
ChatClient client = new(model, key);
await foreach (
StreamingChatCompletionUpdate update in client.CompleteChatStreamingAsync(
new UserChatMessage("Say double bubble bath ten times fast.")
)
)
{
foreach (ChatMessageContentPart part in update.ContentUpdate)
{
Console.Write(part.Text);
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13require "openai"
client = OpenAI::Client.new
stream = client.chat.completions.stream(
model: "gpt-6-astra",
messages: [
{
role: :user,
content: "Say 'double bubble bath' ten times fast."
}
]
)
stream.text.each { |text| print(text) }
도구 호출 스트리밍과 같은 고급 활용 사례는 다음 가이드를 참고하세요.
프로덕션 애플리케이션에서 모델 출력을 스트리밍하면 응답 내용을 검토하기가 더 어렵습니다. 아직 완성되지 않은 응답은 평가하기 어려울 수 있기 때문입니다. 이는 애플리케이션의 사용 방식이 승인된 범위에 해당하는지에도 영향을 줄 수 있습니다.
생성 요청과 함께 콘텐츠 검토 점수를 요청하면 전체 출력이 생성된 후에 점수가 전달됩니다. 스트리밍 중에 전달되는 개별 출력 델타에는 점수가 포함되지 않습니다.