Hydra AI(Tambo React SDK)线程与输入管理完全指南:从会话控制到语音输入与图片附件
发布时间:2026/9/15 17:19:42来源:尧图网络
Hydra AITambo React SDK线程与输入管理完全指南从会话控制到语音输入与图片附件【免费下载链接】hydra-aiGenerative UI SDK for React项目地址: https://gitcode.com/GitHub_Trending/hy/hydra-ai导读本文是围绕 plugins/tambo/skills/building-with-tambo/references/threads.md 展开的实战指南系统讲解 Tambo React SDK当前仓库hydra-ai中的react-sdk包对外以tambo-ai/react发布如何管理对话线程Thread与输入。你将掌握会话的创建、切换、取消与流式状态监听消息内容块content block的渲染AI 建议Suggestions的自动/手动生成语音输入转写Voice Input以及图片附件Image Attachments的完整用法并理解每个 Hook 在 react-sdk/src 中的底层实现从而在 React 应用中搭建完整的生成式 UI 聊天体验。快速开始三个 Hook 跑通最小聊天Tambo 的线程能力建立在三个核心 Hook 之上useTambo()提供当前会话状态与消息useTamboThreadInput()提供输入框状态与提交动作。最小实现只需几行import { useTambo, useTamboThreadInput } from tambo-ai/react; const { thread, messages, isIdle } useTambo(); const { value, setValue, submit } useTamboThreadInput(); await submit(); // 发送当前输入内容从源码看useTambo()定义在 react-sdk/src/v1/hooks/use-tambo-v1.ts它组合了客户端useTamboClient、流状态useStreamState、组件/工具注册表TamboRegistryContext、线程管理useThreadManagement与认证状态useTamboAuthState等多个 ContextuseTamboThreadInput()则由 react-sdk/src/v1/hooks/use-tambo-v1-thread-input.ts 直接转发自 react-sdk/src/v1/providers/tambo-v1-thread-input-provider.tsx 中的共享输入 Provider。这意味着所有调用useTamboThreadInput()的组件共享同一份输入状态建议Suggestions组件可以直接改写输入框内容。Thread Management会话管理核心useTambo 的完整返回对象useTambo()返回当前会话的状态与控制函数覆盖会话的读取与操作import { useTambo, useTamboThreadInput, ComponentRenderer, } from tambo-ai/react; function Chat() { const { thread, // 当前线程状态 messages, // 带计算属性的消息列表 isIdle, // 未在生成时为 true isStreaming, // 正在流式输出响应时为 true isWaiting, // 等待服务端响应时为 true currentThreadId, // 当前活动线程 ID switchThread, // 切换到其他线程 startNewThread, // 创建新线程返回 ID cancelRun, // 取消正在进行的生成 } useTambo(); const { value, // 当前输入值 setValue, // 更新输入 submit, // 发送消息 isPending, // 提交进行中 images, // 已暂存staged的图片文件 addImage, // 添加单张图片 removeImage, // 按 ID 移除图片 } useTamboThreadInput(); const handleSend async () { await submit(); }; return ( div {messages.map((msg) ( div key{msg.id} {msg.content.map((block) { switch (block.type) { case text: return p key{${msg.id}:text}{block.text}/p; case component: return ( ComponentRenderer key{block.id} content{block} threadId{currentThreadId} messageId{msg.id} / ); case tool_use: return ( div key{block.id} {block.statusMessage ?? Running ${block.name}...} /div ); default: return null; } })} /div ))} input value{value} onChange{(e) setValue(e.target.value)} / button onClick{handleSend} disabled{!isIdle || isPending} Send /button /div ); }对照 use-tambo-v1.ts 的实现可以确认几个重要的底层细节消息是增强过的useTambo()返回的messages对原始线程消息做了变换——tool_use内容块会被附加上计算出的statusMessageCalling xxx / Called xxx、hasCompleted等属性并过滤掉_tambo_前缀的内部参数component内容块则会被缓存并挂上renderedComponent可以直接用{content.renderedComponent}渲染无需手动遍历。isStreaming/isWaiting/isIdle是由streamingState.status映射而来的三个布尔值见源码isStreaming: streamingState.status streaming等三者互斥任何时刻只有一个为true。cancelRun是乐观取消先向本地流状态派发一个RUN_ERRORcode 为CANCELLED事件再调用client.threads.runs.delete(runId, ...)通知服务端无活动 run 或处于占位线程placeholder时直接 no-op即使服务端取消失败也不会让本地状态卡住。Streaming State流式状态机三个布尔值之外streamingState对象提供了更细粒度的信息const { streamingState } useTambo(); // streamingState.status: idle | waiting | streaming // streamingState.runId: 当前 run ID // streamingState.error: { message, code } 若发生错误PropertyTypeDescriptionisIdleboolean未在生成idleisWaitingboolean等待服务端响应waitingisStreamingboolean正在流式接收响应streaming这是典型的提交 - 等待首字节 - 流式接收 - 结束三态机UI 上可以据此切换输入框禁用态、加载指示器与停止生成按钮。Content Block Types消息内容块线程消息由一组内容块content block组成每条消息需要按类型分别渲染TypeDescriptionKey Fieldstext纯文本textcomponentAI 生成的组件id,name,propstool_use工具调用id,name,inputtool_result工具调用结果toolUseId,contentresourceMCP 资源uri,name,textcomponent块通常交给ComponentRenderer渲染实现见 react-sdk/src/v1/components/v1-component-renderer.tsx它会从注册表中按name找到对应的 React 组件并用props实例化。tool_use块在useTambo()中已被增强出statusMessage可直接用于展示工具执行进度。Submit Options提交选项submit()支持传入两个可选参数控制本次请求const { submit } useTamboThreadInput(); await submit({ toolChoice: auto, // auto | required | none | { name: toolName } debug: true, // 为流开启调试日志 });SubmitOptions定义在 tambo-v1-thread-input-provider.tsxdebug?: boolean为该次流请求启用调试日志toolChoice?: ToolChoice控制模型如何使用工具默认auto模型自行决定required强制至少调用一个工具none禁止使用工具{ name: toolName }强制使用指定工具。提交前的校验同样发生在这个 Provider 中未完成认证authState.status ! identified会抛出认证未就绪错误输入为空且无图片时会抛出Message cannot be empty图片的 data URL 校验失败会抛出Invalid message format。提交成功后输入框会被乐观清空方便用户立刻输入下一条失败时会恢复原输入内容避免丢字。Fetching a Thread by ID只读获取单个会话如果只需要在详情页读取某个历史会话而不是参与活动会话使用useTamboThread(threadId)import { useTamboThread } from tambo-ai/react; function ThreadView({ threadId }: { threadId: string }) { const { data: thread, isLoading, isError } useTamboThread(threadId); if (isLoading) return Skeleton /; if (isError) return divFailed to load thread/div; return div{thread.name}/div; }这是一个基于 React Query 的 Hook适合只读的线程拉取不适合活动会话。从 react-sdk/src/v1/hooks/use-tambo-v1-thread.ts 可以看到其实现要点查询键为[v1-threads, threadId]查询函数调用client.threads.retrieve(threadId)staleTime设为 1000ms1 秒因为线程是实时数据需要相对频繁的自动刷新查询仅在authState.status identified认证完成时启用。Thread List会话列表与分页管理多会话侧边栏使用useTamboThreadList()配合useTambo()的线程切换能力import { useTambo, useTamboThreadList } from tambo-ai/react; function ThreadSidebar() { const { data, isLoading } useTamboThreadList(); const { currentThreadId, switchThread, startNewThread } useTambo(); if (isLoading) return Skeleton /; return ( div button onClick{() startNewThread()}New Thread/button ul {data?.threads.map((t) ( li key{t.id} button onClick{() switchThread(t.id)} className{currentThreadId t.id ? active : } {t.name || Untitled} /button /li ))} /ul /div ); }Thread List Options列表查询支持过滤与分页参数const { data } useTamboThreadList({ userKey: user_123, // 按用户过滤默认取 Provider 的 userKey limit: 20, // 最大返回条数 cursor: nextCursor, // 分页游标 }); // data.threads: TamboThread[] // data.hasMore: boolean // data.nextCursor: string底层实现react-sdk/src/v1/hooks/use-tambo-v1-thread-list.ts值得注意的两点userKey 合并策略显式传入的listOptions.userKey优先于 Provider 上下文中的userKey否则自动继承上下文值缓存策略查询键为[v1-threads, list, effectiveOptions]staleTime为 5000ms5 秒并且仅在isIdentified为true时才启用查询。配合useTambo()的startNewThread创建新线程并返回临时 ID对应源码中的 placeholder 机制与switchThread切换当前活动线程即可构建完整的多会话侧边栏交互。SuggestionsAI 驱动的后续建议每条 assistant 消息后可以自动生成后续操作建议import { useTamboSuggestions } from tambo-ai/react; function Suggestions() { const { suggestions, isLoading, accept, isAccepting } useTamboSuggestions({ maxSuggestions: 3, // 1-10默认 3 autoGenerate: true, // 在 assistant 消息后自动生成 }); if (isLoading) return Skeleton /; return ( div classNamesuggestions {suggestions.map((s) ( button key{s.id} onClick{() accept({ suggestion: s })} disabled{isAccepting} {s.title} /button ))} /div ); }Suggestion TypeSuggestion类型的字段全部必填interface Suggestion { id: string; // 唯一标识 title: string; // 按钮/胶囊上显示的短标签 detailedSuggestion: string; // 被采纳后提交的完整文本 messageId: string; // 关联消息的 ID初始建议传 }对照 react-sdk/src/v1/hooks/use-tambo-v1-suggestions.ts 的实现有几个值得注意的工程细节触发条件只有当最新消息来自 assistant、线程处于isIdle、且消息 ID 不是ephemeral_前缀的临时 ID如 reasoning 占位消息服务端不会持久化时才查询/生成建议404 重试assistant 消息刚落库时立即请求建议可能 404查询会对 404 做指数退避重试retryDelay: min(500 * 2^attempt, 3000)最多 3 次组件感知手动生成时会通过toAvailableComponents(componentList)把当前注册的组件列表传给服务端让建议更贴合应用能力accept的实现接受建议本质上是把suggestion.detailedSuggestion写入共享输入框利用useTamboThreadInput()的共享状态若shouldSubmit为true则随后调用submit()直接发送。Initial Suggestions空线程的初始建议可以将引导性建议传给聊天组件用于空线程的冷启动MessageThreadPanel initialSuggestions{[ { id: 1, title: Show my bookings, detailedSuggestion: Show me my upcoming bookings for this week, messageId: , }, { id: 2, title: Create event type, detailedSuggestion: Create a new 30 minute meeting event type, messageId: , }, ]} /初始建议只在线程还没有任何消息时出现。一旦用户发送了第一条消息它们就会被 AI 生成的建议取代。Auto-Submit Suggestion采纳并直接发送// 采纳建议并立即作为消息提交 accept({ suggestion: s, shouldSubmit: true });Manual Generation手动生成关闭自动生成后可以按需手动触发const { generate, isGenerating } useTamboSuggestions({ autoGenerate: false, // 关闭自动生成 }); button onClick{() generate()} disabled{isGenerating} Get suggestions /button;Voice Input语音转文本输入useTamboVoice()封装了录音 - 停止 - 自动转写的完整流程import { useTamboVoice } from tambo-ai/react; function VoiceButton() { const { startRecording, stopRecording, isRecording, isTranscribing, transcript, transcriptionError, mediaAccessError, } useTamboVoice(); return ( div button onClick{isRecording ? stopRecording : startRecording} {isRecording ? Stop : Record} /button {isTranscribing spanTranscribing.../span} {transcript p{transcript}/p} {transcriptionError p classNameerror{transcriptionError}/p} /div ); }Voice Hook ReturnsPropertyTypeDescriptionstartRecording() void开始录音并重置转录文本stopRecording() void停止录音并自动开始转写isRecordingboolean正在录音isTranscribingboolean正在处理音频transcriptstring \| null转写出的文本transcriptionErrorstring \| null转写错误信息mediaAccessErrorstring \| null麦克风权限错误从 react-sdk/src/hooks/use-tambo-voice.tsx 的实现可以了解底层链路录音基于react-media-recorderaudio: true, video: false音频以audio/webm格式产出停止录音后Hook 取出mediaBlobUrlfetch为 Blob、包装成File再调用client.beta.audio.transcribe({ file })走 Tambo 服务端的语音转写接口转写成功onSuccess后写入transcript状态。mediaAccessError为空字符串时会被归一化为null方便 UI 做条件渲染。Image Attachments图片附件图片通过useTamboThreadInput()管理内部由 react-sdk/src/hooks/use-message-images.ts 的useMessageImages实现import { useTamboThreadInput } from tambo-ai/react; function ImageInput() { const { images, addImage, addImages, removeImage, clearImages } useTamboThreadInput(); const handleFiles async (files: FileList) { await addImages(Array.from(files)); }; return ( div input typefile acceptimage/* multiple onChange{(e) handleFiles(e.target.files!)} / {images.map((img) ( div key{img.id} img src{img.dataUrl} alt{img.name} / button onClick{() removeImage(img.id)}Remove/button /div ))} /div ); }StagedImage PropertiesPropertyTypeDescriptionidstring唯一图片 IDnamestring文件名dataUrlstringBase64 data URLfileFile原始 File 对象sizenumber文件大小字节typestringMIME 类型实现细节与提交链路的对应关系类型校验addImage会拒绝非image/前缀的文件addImages会过滤掉非图片文件若全部不合法则抛出No valid image files provided读入方式通过FileReader.readAsDataURL把文件转为 base64 data URL图片预览直接使用img.dataUrlID 生成使用crypto.randomUUID()为每张暂存图生成唯一 IDremoveImage(id)按 ID 移除clearImages()一键清空提交时如何发送提交submit时tambo-v1-thread-input-provider.tsx 会把每张暂存图校验后转换为resource类型内容块{ type: resource, resource: { name, mimeType, blob } }blob 为 data URL 中逗号之后的 base64 载荷随文本一起发送提交成功后只清除本次已提交的图片提交期间新增的图片会保留。User Authentication按用户隔离线程开启按用户的线程隔离在TamboProvider上配置userKey即可import { TamboProvider } from tambo-ai/react; function App() { return ( TamboProvider apiKey{apiKey} userKeyuser_123 // 简单的用户标识 Chat / /TamboProvider ); }基于 OAuth 的场景改用userTokenfunction App() { const userToken useUserToken(); // 来自你的认证体系 return ( TamboProvider apiKey{apiKey} userToken{userToken} Chat / /TamboProvider ); }使用原则userKey用于简单的用户标识适合服务端或可信环境userToken用于 OAuth JWT令牌本身携带 userKey适合客户端应用。两者不要同时使用。从 react-sdk/src/v1/providers/tambo-v1-provider.tsx 的实现第 148-201 行可以确认Provider 要求必须提供userKey或userToken之一同时提供两者会告警所有线程操作创建、列表、拉取只返回属于该userKey的线程。userKey的隔离语义贯穿前面所有 Hook——useTamboThreadList的 userKey 合并策略、useTamboThread与useTamboSuggestions的isIdentified门控都建立在这套认证基础之上。总结一套 Hook 串起完整对话体验Tambo 的线程与输入体系由四个层次构成层级清晰、职责分明认证层TamboProvider的apiKeyuserKey/userToken决定谁能用、线程属于谁会话层useTambo()活动会话状态/切换/取消/重命名useTamboThread()只读详情useTamboThreadList()列表分页交互层useTamboThreadInput()输入、提交、toolChoice、图片附件useTamboSuggestions()建议生成与采纳useTamboVoice()语音转写渲染层messages中的text/component/tool_use/tool_result/resource内容块分类渲染ComponentRenderer负责 AI 生成组件的实例化。从消息提交、流式状态机到建议、语音与图片的完整链路均可在 react-sdk/src 的v1/hooks、v1/providers与hooks目录下找到对应实现与测试如use-tambo-v1-suggestions.test.tsx、use-tambo-v1-thread-input.test.tsx、use-tambo-voice.test.tsx可据此深入理解或二次定制。【免费下载链接】hydra-aiGenerative UI SDK for React项目地址: https://gitcode.com/GitHub_Trending/hy/hydra-ai创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
网站建设高端定制企业官网