Examples

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 implement IStreamingRichTextBoxAiResolver.

Click a button below to call the streaming endpoint. Tokens render live inside the editor.

Example code

<link rel="stylesheet" href="/richtexteditor/rte_theme_default.css" />
        <script type="text/javascript" src="/richtexteditor/rte.js"></script>
        <script type="text/javascript" src='/richtexteditor/plugins/all_plugins.js'></script>

        <div id="stream_editor">
            <p>Click a button below to call the streaming endpoint. Tokens render live inside the editor.</p>
        </div>

        <div style="display:flex; gap:8px; margin-top:10px;">
            <button type="button" onclick="runStream('Summarize this document in three bullet points.')" style="padding:6px 14px; border-radius:999px; background:#0f172a; color:#fff; border:0; cursor:pointer;">Summarize</button>
            <button type="button" onclick="runStream('Proofread the document, fix only grammar and punctuation.')" style="padding:6px 14px; border-radius:999px; background:#fff; color:#0f172a; border:1px solid #e2e8f0; cursor:pointer;">Proofread</button>
            <button type="button" id="abortBtn" onclick="abortStream()" disabled style="padding:6px 14px; border-radius:999px; background:#fff; color:#64748b; border:1px solid #e2e8f0; cursor:pointer;">Abort</button>
        </div>
        <div id="streamStatus" style="margin-top:10px; font-size:12.5px; color:#64748b;"></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 &mdash; 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>