Skip to content

Instantly share code, notes, and snippets.

@MinSomai
Created July 21, 2025 08:21
Show Gist options
  • Save MinSomai/9e9ad9ece0e20127341acd28d4d68bb6 to your computer and use it in GitHub Desktop.
Save MinSomai/9e9ad9ece0e20127341acd28d4d68bb6 to your computer and use it in GitHub Desktop.
Custom Vercel AISDK text to stream
// from here
// https://ai-sdk.dev/docs/ai-sdk-ui/stream-protocol#data-stream-protocol
const DataStreamStringPrefixes = {
TEXT: '0',
JSON: '2',
ERROR: '3',
MESSAGE_ANNOTATIONS: '8',
TOOL_CALL: '9',
TOOL_RESULT: 'a',
TOOL_CALL_STREAMING_START: 'b',
TOOL_CALL_DELTA: 'c',
FINISH_MESSAGE: 'd',
FINISH_STEP: 'e',
START_STEP: 'f',
REASONING: 'g',
SOURCE: 'h',
REDACTED_REASONING: 'i',
REASONING_SIGNATURE: 'j',
FILE: 'k',
} as const
// inside POST route
return createDataStreamResponse({
headers: {
'x-vercel-ai-data-stream': 'v1',
},
execute: async (dataStream) => {
dataStream.onError = (error: unknown) => {
const errorMessage = error instanceof Error ? error.message : 'Unknown error'
return `${DataStreamStringPrefixes.ERROR}:${errorMessage}\n` as DataStreamString
}
// do you thing and get text
const chatResponse = { result: { response: "Hello I'm AI response" } };
// START_STEP
dataStream.write(
`${DataStreamStringPrefixes.START_STEP}:{"messageId":"${id}"}\n` as DataStreamString,
)
const responseText = chatResponse.result?.response?.trim()
const textChunks = responseText.split(' ')
const stream = new ReadableStream<DataStreamString>({
async start(controller) {
try {
for (let i = 0; i < textChunks.length; i++) {
const chunk = textChunks[i]
const chunkData =
`${DataStreamStringPrefixes.TEXT}:"${chunk}${i < textChunks.length - 1 ? ' ' : ''}"\n` as DataStreamString
console.log(`Enqueuing text chunk: '${chunkData}'`)
controller.enqueue(chunkData)
await new Promise((resolve) => setTimeout(resolve, 50))
}
controller.enqueue(
`${DataStreamStringPrefixes.FINISH_STEP}:\n` as DataStreamString,
)
controller.enqueue(
`${DataStreamStringPrefixes.FINISH_MESSAGE}:\n` as DataStreamString,
)
} catch (error) {
controller.error(error)
} finally {
controller.close()
}
},
})
await new Promise<void>((resolve, reject) => {
stream
.pipeTo(
new WritableStream({
write(chunk) {
dataStream.write(chunk)
},
close() {
resolve()
},
abort(err) {
reject(err)
},
}),
)
.catch((err) => reject(err))
})
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment