Created
July 21, 2025 08:21
-
-
Save MinSomai/9e9ad9ece0e20127341acd28d4d68bb6 to your computer and use it in GitHub Desktop.
Custom Vercel AISDK text to stream
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 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