Streaming Chatbot on CloudBase: Three Implementation Paths
In one sentence: There are three main paths to connect a "stream as it generates" chat interface to a CloudBase backend — the official
cloudbase-agent-uiMini Program component, hand-written SSE + Cloud Function, or Vercel AI SDK. This guide helps you pick one based on your frontend type and protocol requirements, and provides minimal runnable code for each.Estimated time: 30–60 minutes (depending on which path you choose) | Difficulty: Advanced
Applicable Scenarios
- You want a streaming chat experience where users read output as it generates, not waiting several seconds for a full response
- Your backend is on CloudBase (Cloud Function / CloudBase Run)
- Your frontend can be WeChat Mini Program / Web / Next.js / Vue / React Native
- Your LLM can be Hunyuan / DeepSeek / any OpenAI-compatible provider
Three Paths Overview
| Path | Best for | Strengths | Limitations |
|---|---|---|---|
| A. cloudbase-agent-ui (official component) | WeChat Mini Program developers | Official component; streaming response, multi-turn conversation, file upload, voice, and tool calling all encapsulated; three chatMode options: Hunyuan / DeepSeek / Agent | Currently only the WeChat Mini Program version is released — no Web or React version yet |
| B. Hand-written SSE + Cloud Function | Existing Web / Vue / Next frontend with custom protocol requirements | Fully controllable; free choice of frontend-backend protocol; compatible with any client | Stream parsing, reconnection, and abort handling are your responsibility |
| C. Vercel AI SDK | Existing Next.js / Vue projects; full-stack React teams | useChat hook provides great DX; error handling and abort are built into the SDK | Not usable in Mini Programs; ai@4 and ai@5+ have incompatible protocols — pin the version |
Each path has its own dedicated section below. Read only the one you need.
Path A: Use cloudbase-agent-ui (recommended for Mini Program)
Important:
cloudbase-agent-uiis currently distributed as WeChat Mini Program source code components (not an npm package, and no React/Web version exists). This section covers Mini Program scenarios only. If your frontend is not a Mini Program, skip to Path B or C.
Step 1: Create an AI service in the CloudBase Console
Open WeChat DevTools and click "CloudBase" at the top to enable the service, or go to the CloudBase Platform. Two AI service types are available:
- Direct LLM access (
chatMode: 'model'): connects directly to Hunyuan / DeepSeek, works out of the box - Framework-based Agent (
chatMode: 'bot'): build an Agent on the Agent platform, bind a prompt / tools / Knowledge Base, and get abotIdlikeagent-xxxxxxx
See Agent Development Documentation for details.
Step 2: Copy the component into your Mini Program project
cloudbase-agent-ui is not published to npm — it is distributed as source code:
-
git clone https://github.com/TencentCloudBase/cloudbase-agent-uior downloadagent-ui.zipfrom the GitHub Releases page -
Copy the
components/agent-uidirectory from the repo into your Mini Program project atcomponents/agent-ui/ -
Initialize the CloudBase environment in
miniprogram/app.js:App({onLaunch() {wx.cloud.init({env: 'your-env-id', // CloudBase environment IDtraceUser: true,});},});
Step 3: Register and use the component in a page
Register in the page .json:
{
"usingComponents": {
"agent-ui": "/components/agent-ui/index"
}
}
Reference in the page .wxml:
<view>
<agent-ui
chatMode="{{chatMode}}"
agentConfig="{{agentConfig}}"
modelConfig="{{modelConfig}}"
showBotAvatar="{{showBotAvatar}}"
></agent-ui>
</view>
Configure in the page .js (pick one of the two chatMode options):
// Option 1: Direct LLM access (simplest)
Page({
data: {
chatMode: 'model',
showBotAvatar: true,
modelConfig: {
modelProvider: 'hunyuan-exp', // or 'hunyuan-open' / 'deepseek'
quickResponseModel: 'hunyuan-turbos-latest', // for DeepSeek: 'deepseek-v3.2' / 'deepseek-r1'
logo: '',
welcomeMsg: 'Hello, I am your assistant',
},
},
});
// Option 2: Framework-based Agent (create the Agent in the Console first)
Page({
data: {
chatMode: 'bot',
showBotAvatar: true,
agentConfig: {
botId: 'agent-xxxxxxx', // copy from the Agent list in the Console
tools: [
// optional: frontend tools that the Agent can call
{
name: 'get_weather',
description: 'Get the weather for a specified city',
parameters: {
type: 'object',
properties: { city: { type: 'string' } },
required: ['city'],
},
handler: ({ city }) => `${city}: sunny, 25°C`,
},
],
},
},
});
The full props (chatMode / agentConfig / modelConfig / showBotAvatar) and their accepted values are defined in the repo README for the current version. Streaming response, multi-turn conversation, file upload, voice input, and tool calling are all handled inside the component — your business code never touches SSE.
Step 4: Configure the Mini Program server domain (allowlist)
In WeChat Public Platform admin: Development Management → Server domain → request legal domain, add:
https://{your-envid}.api.tcloudbasegateway.com
Without this, file upload and multi-session features will not work.
Path B: Hand-written SSE + Cloud Function (custom scenario)
Use this path when: the protocol is not AG-UI protocol, the frontend is not a Mini Program, or you need deep customization of the frontend-backend communication.
Backend Web Cloud Function
mkdir chat-backend && cd chat-backend
npm init -y
npm install express
index.js:
const express = require('express');
const app = express();
app.use(express.json());
app.post('/api/chat', async (req, res) => {
const { messages } = req.body;
// Call upstream LLM (reuse the proxy URL from connect-openai-api-cloud-function)
const upstream = await fetch(process.env.LLM_PROXY_URL + '/chat/completions', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${process.env.LLM_PROXY_TOKEN}`,
},
body: JSON.stringify({
model: 'gpt-4o-mini',
messages,
stream: true,
}),
});
if (!upstream.ok) {
res.status(upstream.status).json({ error: await upstream.text() });
return;
}
res.setHeader('Content-Type', 'text/event-stream');
res.setHeader('Cache-Control', 'no-cache');
res.setHeader('Connection', 'keep-alive');
const reader = upstream.body.getReader();
while (true) {
const { done, value } = await reader.read();
if (done) break;
res.write(value); // upstream is already in SSE format, proxy it as-is
}
res.end();
});
app.listen(process.env.PORT || 9000);
Deploy to CloudBase Web Cloud Function:
tcb fn deploy chat-backend --httpFn -e your-env-id
After deployment, configure LLM_PROXY_URL / LLM_PROXY_TOKEN in the Console under "Cloud Functions → chat-backend". Note the HTTP trigger URL, e.g. https://your-env.service.tcloudbase.com/chat-backend/api/chat.
Frontend
Web (plain React / Vue, using fetch + ReadableStream):
const res = await fetch('/api/chat', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ messages: [{ role: 'user', content: 'Hello' }] }),
});
const reader = res.body.getReader();
const decoder = new TextDecoder();
let buffer = '';
while (true) {
const { done, value } = await reader.read();
if (done) break;
buffer += decoder.decode(value, { stream: true });
// SSE events are separated by \n\n
const events = buffer.split('\n\n');
buffer = events.pop(); // last chunk may be incomplete — carry it to the next iteration
for (const event of events) {
for (const line of event.split('\n')) {
if (!line.startsWith('data:')) continue;
const data = line.slice(5).trim();
if (data === '[DONE]') return;
try {
const obj = JSON.parse(data);
const delta = obj.choices?.[0]?.delta?.content;
if (delta) console.log(delta); // accumulate into state
} catch {}
}
}
}
WeChat Mini Program (wx.request + enableChunked, when not using cloudbase-agent-ui):
const requestTask = wx.request({
url: 'https://your-env.service.tcloudbase.com/chat-backend/api/chat',
method: 'POST',
enableChunked: true,
data: { messages: [{ role: 'user', content: 'Hello' }] },
responseType: 'arraybuffer',
});
requestTask.onChunkReceived((res) => {
const text = String.fromCharCode.apply(null, new Uint8Array(res.data));
// parse SSE as above, accumulate delta into page data
});
See the wx.request enableChunked documentation.
Path C: Vercel AI SDK (existing Next.js projects)
Version note: This code is based on
ai@4.x.ai@5+is released and introduces breaking changes touseChat/streamTextprotocols. For new projects, refer to the current version of the Vercel AI SDK official docs.
Use this path when: you have an existing Next.js / Vue project using Vercel AI SDK 4.x, or you are migrating from Vercel to CloudBase.
Install dependencies
npm install ai@4 @ai-sdk/openai
Backend: Next.js Route Handler
app/api/chat/route.ts:
import { createOpenAI } from '@ai-sdk/openai';
import { streamText } from 'ai';
export const runtime = 'nodejs'; // Do not use edge — CloudBase Run runs a Node container
export const maxDuration = 60;
const llm = createOpenAI({
baseURL: process.env.LLM_PROXY_URL, // https://xxx.service.tcloudbase.com/llm-proxy/v1
apiKey: process.env.LLM_PROXY_TOKEN, // proxy auth token, not a real OpenAI key
});
export async function POST(req: Request) {
const { messages } = await req.json();
const result = await streamText({
model: llm('gpt-4o-mini'),
system: 'You are a helpful assistant.',
messages,
});
return result.toDataStreamResponse();
}
createOpenAI({ baseURL }) redirects requests to your own proxy. The real OpenAI key lives inside the Cloud Function proxy layer (see connect-openai-api-cloud-function) — invisible to the frontend or Next.js.
Backend: Web Cloud Function version (for non-Next.js frontends)
const express = require('express');
const { createOpenAI } = require('@ai-sdk/openai');
const { streamText } = require('ai');
const app = express();
app.use(express.json({ limit: '5mb' }));
const llm = createOpenAI({
baseURL: process.env.LLM_PROXY_URL,
apiKey: process.env.LLM_PROXY_TOKEN,
});
app.post('/api/chat', async (req, res) => {
const { messages } = req.body;
const result = await streamText({
model: llm('gpt-4o-mini'),
messages,
onFinish: async ({ text }) => {
// optional: persist to database after streaming ends
},
});
result.pipeDataStreamToResponse(res);
});
app.listen(process.env.PORT || 9000);
Deploy:
tcb fn deploy chat-backend --httpFn -e your-env-id
Configure LLM_PROXY_URL / LLM_PROXY_TOKEN in the Console.
Frontend: useChat
app/chat/page.tsx:
'use client';
import { useChat } from 'ai/react';
export default function ChatPage() {
const { messages, input, handleInputChange, handleSubmit, isLoading, error, stop } = useChat({
api: '/api/chat', // for cross-service calls, use the Web Cloud Function URL
});
return (
<div style={{ maxWidth: 720, margin: '0 auto', padding: 24 }}>
{messages.map((m) => (
<div key={m.id} style={{ margin: '12px 0' }}>
<strong>{m.role === 'user' ? 'You' : 'Assistant'}:</strong>
<span>{m.content}</span>
</div>
))}
{error && <div style={{ color: 'red' }}>Error: {error.message}</div>}
<form onSubmit={handleSubmit} style={{ display: 'flex', gap: 8 }}>
<input
value={input}
onChange={handleInputChange}
placeholder="Say something..."
disabled={isLoading}
style={{ flex: 1, padding: 8 }}
/>
<button type="submit" disabled={isLoading}>Send</button>
{isLoading && <button type="button" onClick={stop}>Stop</button>}
</form>
</div>
);
}
For Vue, use useChat from @ai-sdk/vue — the API shape is identical.
Deploy
Deploy Next.js as a single unit to CloudBase Run:
tcb cloudrun deploy --port 3000
See Deploy Next.js to CloudBase Run. In CloudBase Run "Service Settings → Environment Variables", add LLM_PROXY_URL (must end with /v1 because @ai-sdk/openai automatically appends /chat/completions) and LLM_PROXY_TOKEN.
How to Choose
| Your situation | Pick |
|---|---|
| WeChat Mini Program frontend | A (cloudbase-agent-ui) |
| Want the fastest chat UI without implementing the protocol yourself | A (Mini Program) or C (Web) |
| Protocol must be custom, or frontend is non-Next.js Web / React Native | B |
| Existing Next.js + Vercel AI SDK project | C |
| Trying everything — where to start | Mini Program: A; Web: start with C, switch to B if customization is needed |
Common Errors
| Symptom | Cause | Fix |
|---|---|---|
| No token-by-token display — output appears all at once after several seconds | SSE not actually active / intermediate layer is buffering the stream | Confirm backend sends Content-Type: text/event-stream + Cache-Control: no-cache; disable response buffering on custom domain / CDN; verify first with the default *.service.tcloudbase.com domain |
| Path A: component not showing or "agent-ui not registered" error | Page .json missing usingComponents declaration, or components/agent-ui not copied | Check directory structure + page usingComponents path |
Path A: botId wrong, Agent not found | Agent not created in Console, or ID mistyped | CloudBase Console → AI → Agent list, copy the actual ID (format: agent-xxxxxxx) |
Path A: request not in domain list | Server domain not added to allowlist | WeChat Public Platform admin → Server domain → add https://{envid}.api.tcloudbasegateway.com |
Path B: Mini Program wx.request error url not in domain list | Same as above | Same fix; also add the Web Cloud Function access domain (*.service.tcloudbase.com) |
| Path B: backend stream drops without the frontend knowing | Upstream 5xx causes SSE to break | In the backend catch, write data: {"error":"..."}\n\n then call end() to notify the frontend |
Path C: useChat is not exported from 'ai/react' | ai is not v4 (v5+ changed import paths) | npm install ai@^4 @ai-sdk/openai@^1; do not mix v5 import paths |
Path C: useChat sends GET instead of POST, returns 404 | App Router Route Handler missing the POST export | Check route.ts for export async function POST spelling |
Path C: Failed to fetch cross-origin error | Frontend domain != backend domain, CORS not configured | Add CORS middleware to Express: Access-Control-Allow-Origin / Methods / Headers, handle OPTIONS |
Path C: long conversation hits context_length_exceeded | Full message history sent every time | Frontend limits to the last N messages, or backend compresses with summarization |
Related Documentation
- cloudbase-agent-ui GitHub — official streaming chatbot Mini Program component
- CloudBase Agent Development — Agent protocol (AG-UI protocol) and development framework overview
- SSE Protocol Support — underlying SSE streaming response mechanism for Web Cloud Functions
- HTTP Cloud Functions Node.js — Web Cloud Function HTTP trigger quick start
- Deploy Next.js to CloudBase Run — Next.js deployment on CloudBase Run
- Vercel AI SDK Official Docs —
useChat/streamTextcurrent version (v5+)
Next Steps
add-rag-with-pgvector-cloudbase— enable the chatbot to answer from your own documents (RAG)connect-openai-api-cloud-function— isolate the LLM API key to a Cloud Function proxy (all three paths benefit)secure-secrets-in-cloud-function— layered management ofLLM_PROXY_TOKEN/ Bot API Key across dev / staging / prod