Generation and streaming
Stream an agent's answer as a conversation with saved history, or as one-off text.
Get text from your agent as it is written. Use client.chat() for a conversation: Blazing Agents saves the history in a session, and each answer streams in the AI SDK UI message format your frontend can render directly. Use client.completion() for one-off text, such as a summary or a rewrite, where you want no history at all.
Stream one-off text
const result = await client.completion({
agentId,
prompt: "Write a two-sentence release note for passkey login.",
});
for await (const delta of result.textStream) {
process.stdout.write(delta);
}
process.stdout.write("\n");If you only need the finished text, skip the loop: await result.text in TypeScript, or client.completion(...) in Python, which returns a string.
Choose chat or completion
client.chat() | client.completion() | |
|---|---|---|
| Input | One user message in AI SDK UIMessage shape, or a saved prompt | Plain text, or a saved prompt |
| History | Starts a session, or continues one when you pass its ID | None |
| Output | An AI SDK UI message stream, plus the session ID | Plain text deltas |
| Good for | Chat interfaces and multi-step conversations | Summaries, rewrites, and other standalone text |
Both run the same agent with the same tools, skills, MCP connections, memory, and workspace, and both record usage. To get JSON in a fixed shape instead of text, use structured output.
Relay chat to your frontend
Your backend holds the API key and passes the stream through unchanged:
export async function POST(request: Request) {
const { message, sessionId } = await request.json();
const result = await client.chat({
agentId,
message,
...(sessionId ? { sessionId } : {}),
});
return result.toResponse();
}Without a sessionId, the call starts a new session and the response carries its ID in the location header. On the frontend, useChat with BlazingAgentsChatTransport renders the stream, reads that header, and sends the session ID back on the next message. Build a chatbot shows the full frontend.
What to add next
Before real users reach this endpoint, add the checks below. In TypeScript, createChatRelay does all of them for you; connect Blazing Agents to your app shows it, and the same steps by hand in Python.
- Sign-in. Find out who the user is before you call Blazing Agents, and reject the request if you cannot.
- Session ownership. Before you continue a session, check that it belongs to the signed-in user. The session ID is ready before the answer finishes (
await result.sessionIdin TypeScript,stream.session_idin Python), so save its owner right away. - Usage labels. Pass the user's ID as
userId(user_idin Python) to label usage per end user. It does not grant access; your ownership check does. - Stopping. In TypeScript, pass
abortSignal: request.signalso a user who closes the tab stops the turn.
A chat stream has one reader. In TypeScript, call either toResponse() or toStream() once; a second call throws stream_error. sessionId stays available either way.
Send an image
A user message can mix text parts with image file parts. Each image needs a URL and an image/* media type:
const result = await client.chat({
agentId,
message: {
id: crypto.randomUUID(),
role: "user",
parts: [
{ type: "text", text: "Describe this diagram." },
{ type: "file", mediaType: "image/png", url: imageUrl },
],
},
});The image is part of the message only. It is not saved to the workspace or published as an artifact. Check the media type, source, and size in your backend before you send it.
Regenerate an answer
Pass trigger: "regenerate-message" on an existing session to replace an answer. Without messageId, the latest answer is replaced. With the ID of an earlier assistant message, that answer and everything after it is replaced. With the ID of a user message, that message is removed and the one you send takes its place.
const result = await client.chat({
agentId,
sessionId,
message,
trigger: "regenerate-message",
messageId: assistantMessageId,
});The old answer stays until the new one succeeds. If regeneration fails or you stop it, the session is unchanged.
Stops, errors, and what gets saved
- Stopping. Pass an
AbortSignalasabortSignal, or cancel the stream you are reading. The turn stops, and usage up to that point is recorded. - Errors before streaming. Problems such as an unknown agent, a bad request, or an exhausted quota reject the call with a typed error. Nothing is saved and nothing is charged.
- Errors during streaming. A chat stream ends with an
{ type: "error", errorText }event, which AI SDK clients already understand. For completion,textStreamthrows, andawait result.textrejects with astream_error. - Saved history. A successful chat turn adds the exchange to the session. A failed or stopped turn leaves the history as it was. Completion never saves history.
Tools that need approval cannot wait for a person during client.completion(), because there is no session to resume. Those calls are blocked, and the agent carries on with the work it is allowed to do.
Next
- Build a chatbot to render the stream with
useChat. - Sessions and turns to reload and continue conversations.
chat()andcompletion()in the TypeScript SDK and Python SDK.