AbortController 取消生成、按 chunk 解码流式
响应、轮询长任务、分页历史记录。如果不抽象出来,每个 demo 或 feature 都会带上同样的
30 行模板代码。
vibestrap 在 @/ai/hooks 下提供 五个 React hook,把这些全封掉。你的组件专心写
UI。每个 hook 都纯客户端,服务端契约是普通的 JSON / text 端点,可以替换。
前置条件
- 看过 providers 文档 —— 这些 hook 调的就是 manager 暴露出来的 后端。
- API 路由挂在
/api/ai/chat、/api/ai/history、/api/credits/balance、 (默认值;每个 hook 都接受自定义endpoint)。 - 标准 UI 示例在
src/components/demos/{chat-demo,image-demo,document-demo}.tsx。
API 参考
useGeneration(options?)
从一个吐 UTF-8 chunk 的服务端端点流式拉文本。
签名
AbortController 和响应体解码。
src/components/demos/chat-demo.tsx。
useTask(options)
长任务:POST 启动 → 轮询到结束态。
签名
parse 把 hook 和
具体响应结构解耦。
src/components/demos/image-demo.tsx。
useCredits(endpoint?)
读当前登录用户的积分余额。
签名
/api/credits/balance。
<CreditsBadge /> 已经包了这个 hook,大多数业务不直接用。
useHistory(endpoint?)
当前用户的 ai_call 历史,游标分页。
签名
AICallHistoryRow 包含 provider、model、operation、status、
inputTokens(nullable —— 图像 / 视频 provider 不返回 token)、outputTokens(同)、
totalMs、createdAt。每页 20 条,游标是末行的 createdAt(ISO 字符串)。
src/components/demos/document-demo.tsx。
验证生效
- 起 dev server,登录。
- 打开
/playground/chat,输入。看text一字一字出来 —— 那就是useGeneration在流。 - 打开
/playground/image,提交 prompt。状态从queued → running → succeeded— 那就是useTask。 - 打开
/dashboard/usage。列表刷新,Load more能加更多行 —— 那就是useHistory。
常见坑
- 流式端点返回 JSON 而不是 text。
useGeneration是按 UTF-8 chunk 读 body 的。 如果路由res.json(),你会一次拿到全文而不是流。请用new Response(stream)或text/plaincontent type。 - 忘记
await generate()。 它返回的是最终文本的 Promise,发完不管也行,但不try/catch就看不到错误。 useTask.parse返回了错的状态字符串。 Hook 只在'succeeded' | 'failed' | 'cancelled'时停止轮询,其他都当「继续轮」。useCredits显示旧余额。 任何 AI 调用之后都要refetch(),manager 是异步扣 积分的,徽章不会自己更新。
官方文档
- React refs / AbortController:react.dev
- Streams API:developer.mozilla.org
- 源码:
src/ai/hooks/、src/components/demos/