Product Updates
Enhanced Server-Sent Events support
David Adler
October 1, 2025 - 3 min read
SDK generation now includes major improvements to Server-Sent Events (SSE) support, making it easier to work with streaming APIs across TypeScript, Python, Java, and C#.
Why SSE matters
Server-Sent Events enable servers to push real-time updates to clients over a single HTTP connection. This pattern is essential for streaming APIs, particularly AI and LLM services like OpenAI, Anthropic, and similar platforms where responses stream token-by-token.
These improvements eliminate boilerplate code and provide better type safety when working with streaming responses.
🎯 Type-safe SSE overloads
TypeScript and Python SDKs now generate type-safe overloads for operations that support both streaming and non-streaming responses.
Previously, operations with a stream: true/false parameter returned a union type. The type system couldn’t determine the return type even though the stream parameter value provided the relevant context, leaving developers to write their own handlers.
We’ve revamped generated SDKs to now include overloads that return the correct type based on the stream parameter. IntelliSense and type checking now work correctly without manual type guards.
Old behavior: union return type
const response = await sdk.chat.completions({
stream: true,
messages: [...]
});
// Type system doesn't know which type was returned
if (response instanceof EventStream) {
// Handle streaming response
} else {
// Handle JSON response
}New behavior: overloaded methods
const streamResponse = await sdk.chat.completions({
stream: true, // Returns EventStream
messages: [...]
});
// TypeScript knows this is EventStream
const jsonResponse = await sdk.chat.completions({
stream: false, // Returns CompletionResponse
messages: [...]
});
// TypeScript knows this is CompletionResponseThere is no configuration required to enable SSE overloads. They are automatically generated when a streaming operation follows the established pattern, i.e has a required request body containing a boolean stream field and returns different response types based on the stream value.
📦 SSE response flattening
TypeScript and Python SDKs support flattening SSE responses to eliminate unnecessary nesting.
SSE responses include metadata fields (event, data, retry, id). When the event field acts as a discriminator, it forces nested access patterns. Developers must always access .data to reach the actual response content, even when the outer event field already determined the type.
Response flattening abstracts away the event, retry and id fields from the server sent event and just yields the data in an un-nested fashion.
Without flattening: nested access required
for await (const response of stream) {
if (response.event === "message") {
console.log(response.data.content);
} else if (response.event === "error") {
console.error(response.data.message);
}
}With flattening: direct access
for await (const response of stream) {
if (response.type === "message") {
console.log(response.content); // No .data nesting
} else if (response.type === "error") {
console.error(response.message); // No .data nesting
}
}To configure flattening in your gen.yaml:
typescript:
version: 1.0.0
sseResponseFlattening: true
python:
version: 1.0.0
sseResponseFlattening: true🔷 C# SSE support
C# SDKs now include full Server-Sent Events support, bringing streaming capabilities to .NET applications.
// C# streaming example
var request = new CompletionRequest() {
Stream = true,
Messages = messages
};
var res = await sdk.Chat.Completions(request)
using (var eventStream = res.Events!)
{
CompletionEvent? eventData;
while ((eventData = await eventStream.Next()) != null)
{
Console.Write(eventData);
}
}C# joins TypeScript, Python, Go, Java, PHP, and Ruby with complete SSE support.
☕ Java for-each support for SSE
Java SDKs now support for-each iteration over SSE streams, providing a more idiomatic way to process streaming responses.
// Java streaming with for-each
CompletionRequest request = CompletionRequest.builder()
.stream(true)
.messages(messages)
.build();
CompletionResponse res = sdk.chat().completions(request);
try (EventStream<ChatStream> events = res.events()) {
for (ChatStream event : events) {
System.out.println(event);
}
}This eliminates the need for manual iterator handling and makes streaming code cleaner.