Windmill SSE response format — emit one item per event?

Hi, I’m trying to use Server-Sent Events (SSE) with streamed responses in a script, but the Windmill SSE endpoint appears to “flush” multiple items into a single event. That doesn’t match the per-item yield format I expect. My main scenario uses the ai-sdk tooling for streaming. Here’s a simplified example:
import { createUIMessageStream, streamText } from "ai";
import { createOpenAI } from "@ai-sdk/openai";
import * as wmill from "windmill-client@1.545.0";

export async function main() {
return streamMe();
}

async function* streamMe(): AsyncGenerator<any> {
const prompt = "write me a poen about nature";

const { api_key } = (await wmill.getResource(
"f/openai_key"
)) as { api_key: string };

const openai = createOpenAI({ apiKey: api_key });

const result = streamText({
model: openai("gpt-4o-mini"),
prompt
});

const uiStream = createUIMessageStream({
execute: ({ writer }) => {
writer.merge(
result.toUIMessageStream({
sendReasoning: true,
onError: error =>
error instanceof Error ? error.message : String(error),
})
);
},
});

const reader = uiStream.getReader();
try {
while (true) {
const { value, done } = await reader.read();
if (done) {
break;
}
yield JSON.stringify(value);
}
} finally {
reader.releaseLock();
}

}
import { createUIMessageStream, streamText } from "ai";
import { createOpenAI } from "@ai-sdk/openai";
import * as wmill from "windmill-client@1.545.0";

export async function main() {
return streamMe();
}

async function* streamMe(): AsyncGenerator<any> {
const prompt = "write me a poen about nature";

const { api_key } = (await wmill.getResource(
"f/openai_key"
)) as { api_key: string };

const openai = createOpenAI({ apiKey: api_key });

const result = streamText({
model: openai("gpt-4o-mini"),
prompt
});

const uiStream = createUIMessageStream({
execute: ({ writer }) => {
writer.merge(
result.toUIMessageStream({
sendReasoning: true,
onError: error =>
error instanceof Error ? error.message : String(error),
})
);
},
});

const reader = uiStream.getReader();
try {
while (true) {
const { value, done } = await reader.read();
if (done) {
break;
}
yield JSON.stringify(value);
}
} finally {
reader.releaseLock();
}

}
A sample response from the Windmill SSE endpoint (attached) looks like this:
data: {"type":"update","new_result_stream":"{json object...}{json object...}{json object...}"}
data: {"type":"update","new_result_stream":"{json object...}{json object...}{json object...}"}
However, I need one JSON object per SSE event because I iterate over each item. The expected output would be:
data: {"type":"update","new_result_stream":"{json object...}"}

data: {"type":"update","new_result_stream":"{json object}"}

data: {"type":"update","new_result_stream":"{json object}"}
data: {"type":"update","new_result_stream":"{json object...}"}

data: {"type":"update","new_result_stream":"{json object}"}

data: {"type":"update","new_result_stream":"{json object}"}
is there a way for formating like that?
5 Replies
Müşkülpesent
MüşkülpesentOP2w ago
By the way, my windmill version is CE v1.558.1
Hugo
Hugo2w ago
can't you simply split on line breaks on the client side?
Müşkülpesent
MüşkülpesentOP2w ago
Hello @Hugo C. , thanks for your response Based on my research, there are off-the-shelf solutions for NDJSON, but I couldn’t find any for concatenated JSON. I’d prefer not to write my own parser for this format because the JSON content is complex and I don’t want to risk misparsing. it feels like a very custom solution. Do you have any recommendations?
Hugo
Hugo2w ago
you can yield the stringified object + a line break and then use an off the shelf solution for NDJSON
Müşkülpesent
MüşkülpesentOP2w ago
What you said makes a lot of sense! I’ll try it thanks 🙂

Did you find this page helpful?