Convex 组件:持续文本流
这个 Convex 组件允许持续文本流 。 它提供了反应 hTTP 动作中流出文本的钩子, 同时存储数据 在数据库中。 此持久性允许文本在 流尾或由其他用户.
最常见的使用案例是AI聊天应用程序. 示例应用程序( 发现于
页:1 example 目录)只是一个简单的聊天应用程序,可以显示用途
构成部分.
这就是你最后的结果! 左侧浏览器窗口正在流出聊天内容 对象到客户端,右浏览器窗口则订阅到聊天正文 通过数据库查询。 消息只在句子数据库中更新 边框,而 HTTP 流会随其来时发出符号 :

预先要求: Convex
你需要一个现有的 Convex 项目来使用组件。 Convex 是一个主机 后端平台,包括数据库、无服务器功能和多一个吨 可以学习 这里。 。 。 .
运行 npm create convex 或遵循其中任何一条
快速启动 来设置一个.
安装
见 example/ 一个工作演示.
- 安装持续文本流组件 :
npm install @convex-dev/persistent-text-streaming
- 创建一个
convex.config.ts文档中 应用程序convex/通过调用文件夹并安装组件use数字 :
// convex/convex.config.ts
import { defineApp } from "convex/server";
import persistentTextStreaming from "@convex-dev/persistent-text-streaming/convex.config.js";
const app = defineApp();
app.use(persistentTextStreaming);
export default app;
使用量
以下是如何使用组件的简单例子:
单位 convex/chat.ts数字 :
const persistentTextStreaming = new PersistentTextStreaming(
components.persistentTextStreaming,
);
// Create a stream using the component and store the id in the database with
// our chat message.
export const createChat = mutation({
args: {
prompt: v.string(),
},
handler: async (ctx, args) => {
const streamId = await persistentTextStreaming.createStream(ctx);
const chatId = await ctx.db.insert("chats", {
title: "...",
prompt: args.prompt,
stream: streamId,
});
return chatId;
},
});
// Create a query that returns the chat body.
export const getChatBody = query({
args: {
streamId: StreamIdValidator,
},
handler: async (ctx, args) => {
return await persistentTextStreaming.getStreamBody(
ctx,
args.streamId as StreamId,
);
},
});
// Create an HTTP action that generates chunks of the chat body
// and uses the component to stream them to the client and save them to the database.
export const streamChat = httpAction(async (ctx, request) => {
const body = (await request.json()) as { streamId: string };
const generateChat = async (ctx, request, streamId, chunkAppender) => {
await chunkAppender("Hi there!");
await chunkAppender("How are you?");
await chunkAppender("Pretend I'm an AI or something!");
};
const response = await persistentTextStreaming.stream(
ctx,
request,
body.streamId as StreamId,
generateChat,
);
// Set CORS headers appropriately.
response.headers.set("Access-Control-Allow-Origin", "*");
response.headers.set("Vary", "Origin");
return response;
});
您需要在您的后端显示此 HTTP 端点, 所以 convex/http.ts数字 :
http.route({
path: "/chat-stream",
method: "POST",
handler: streamChat,
});
// Handle CORS preflight requests so browsers will allow the POST above when
// your app is served from a different origin than your Convex deployment.
http.route({
path: "/chat-stream",
method: "OPTIONS",
handler: httpAction(async (_, request) => {
const headers = request.headers;
if (
headers.get("Origin") !== null &&
headers.get("Access-Control-Request-Method") !== null &&
headers.get("Access-Control-Request-Headers") !== null
) {
return new Response(null, {
headers: new Headers({
"Access-Control-Allow-Origin": "*",
"Access-Control-Allow-Methods": "POST",
"Access-Control-Allow-Headers": "Content-Type, Digest, Authorization",
"Access-Control-Max-Age": "86400",
}),
});
} else {
return new Response();
}
}),
});
最后,在您的应用程序中,你可以创建聊天,他们通过 最佳流程和/或数据库查询 :
// chat-input.tsx, maybe?
const createChat = useMutation(api.chat.createChat);
const formSubmit = async (e: React.FormEvent) => {
e.preventDefault();
const chatId = await createChat({
prompt: inputValue,
});
};
// chat-message.tsx, maybe?
import { useStream } from "@convex-dev/persistent-text-streaming/react";
// ...
// In our component:
const { text, status } = useStream(
api.chat.getChatBody, // The query to call for the full stream body
new URL(`${convexSiteUrl}/chat-stream`), // The HTTP endpoint for streaming
driven, // True if this browser session created this chat and should generate the stream
chat.streamId as StreamId, // The streamId from the chat database record
);
斯韦尔特 (β)
Svelte 5捆绑由社区支持并用β. 他们需要 svelte 财务报告和审定财务报表
convex-svelte,与
setupConvex 在父组件中调用。 反应输入以
获取器。 见 example-svelte/ 为了聊天的应用.
<script lang="ts">
import { useStream } from "@convex-dev/persistent-text-streaming/svelte";
let { chat, driven }: { chat: Doc<"chats">; driven: boolean } = $props();
const stream = useStream(
api.chat.getChatBody,
new URL(`${convexSiteUrl}/chat-stream`),
() => driven,
() => chat.streamId as StreamId,
// Optional, read when a stream starts:
// () => ({ authToken, headers }),
);
</script>
<p>{stream.text}</p>
stream.text 财务报告和审定财务报表 stream.status 随着溪流的进展而更新。 HTTP 软件
请求仅在浏览器中开始,在服务器侧渲染时从未开始.
设计哲学
此组件平衡了 HTTP 流与数据库的持久性,以尝试 尽量扩大两者的好处。 要了解为什么这种平衡是有利的, 让我们孤立地检查每一个方法.
-
仅流出 HTTP: 如果您的应用程序 仅限 使用 HTTP 流线,然后 提出请求的原始浏览器将具有出色的高性能 流经. 但如果HTTP连接丢失, 如果浏览器 窗口被重新装入,如果其他用户想要查看相同的聊天,或者这个用户 等会再谈,不可能的 这个 对话只是暂时的,因为它从未被存储在服务器上.
-
只有数据库持久性: 如果您的应用程序 仅限 使用数据库持久性, 谈话会随你便. 此外,Convex的订阅将确保聊天消息更新 随着新文本块的生成。 然而,有一些缺点: 整个聊天机构需要 怨恨每次改变,这是很多 将冗余带宽推入数据库并覆盖到所有 websockets 连接客户端. 第二,你需要做一个艰难的权衡 互动和效率。 如果你把每一个小块写到 数据库中,这将变得相当缓慢和昂贵。 但是,如果你批量起来 插入,比如说,段落, 然后用户体验会感觉糟糕.
这个部分结合了两个世界中最好的. 制作的原始浏览器 请求仍将有出色的高性能流畅的经验。 不过 聊天机构也存储在数据库中,因此可以被 客户端即使在流完成之后,或者其他用户等等.
背景情况
这一构成部分主要基于Stack员额 AI 与 HTTP 流线聊天。 。 。 .