Streaming AI Responses
The AI Toolkit can consume a Server-Sent Events stream instead of waiting for the full response. Tokens appear as they arrive, the same way ChatGPT / Claude / Gemini surfaces do. The server implementation lives in RichTextBox.AspNetCore(MapRichTextBoxUploads() automatically maps /richtextbox/ai/stream); OpenAI and Anthropic resolvers implementIStreamingRichTextBoxAiResolver.
Click a button below to call the streaming endpoint. Tokens render live inside the editor.
Example code
<div id="stream_editor">
<p>Click a button below to call the streaming endpoint. Tokens render live inside the editor.</p>
</div>
<button type="button" onclick="runStream('Summarize this document in three bullet points.')">Summarize</button>
<button type="button" onclick="runStream('Proofread the document, fix only grammar and punctuation.')">Proofread</button>
<button type="button" id="abortBtn" onclick="abortStream()" disabled>Abort</button>
<div id="streamStatus"></div>
<script>
var streamEditor = new RichTextEditor("#stream_editor", { toolbar: "default" });
var currentStream = null;
function runStream(prompt) {
if (!streamEditor.aiToolkit || !streamEditor.aiToolkit.streamRequest) {
document.getElementById("streamStatus").textContent = "streamRequest helper not available.";
return;
}
var html = streamEditor.getHTMLCode();
var accumulator = "";
document.getElementById("streamStatus").textContent = "Opening stream...";
document.getElementById("abortBtn").disabled = false;
currentStream = streamEditor.aiToolkit.streamRequest({
url: "/richtextbox/ai/stream",
body: { mode: "chat", prompt: prompt, documentText: html, hasSelection: false },
onDelta: function (delta, full) {
accumulator += delta;
document.getElementById("streamStatus").textContent = "Streaming... " + accumulator.length + " chars";
},
onDone: function () {
streamEditor.insertHTML("<hr/><p><strong>AI — streamed response:</strong></p><p>" + accumulator.replace(/\n/g, "<br/>") + "</p>");
document.getElementById("streamStatus").textContent = "Done.";
document.getElementById("abortBtn").disabled = true;
currentStream = null;
},
onError: function (err) {
document.getElementById("streamStatus").textContent = "Error: " + err.message;
document.getElementById("abortBtn").disabled = true;
}
});
}
function abortStream() {
if (currentStream) currentStream.abort();
document.getElementById("streamStatus").textContent = "Aborted by user.";
document.getElementById("abortBtn").disabled = true;
}
</script>