ARTICLE DETAIL

资讯详情

深耕网站建设、视觉设计与SEO优化的一线实战洞察。

Tambo AI React SDK 线程与会话输入完全指南:Threads、Suggestions、语音与图片附件

Tambo AI React SDK 线程与会话输入完全指南:Threads、Suggestions、语音与图片附件 Tambo AI React SDK 线程与会话输入完全指南Threads、Suggestions、语音与图片附件【免费下载链接】hydra-aiGenerative UI SDK for React项目地址: https://gitcode.com/GitHub_Trending/hy/hydra-ai导读本文是 plugins/tambo/skills/generative-ui/references/threads.md 的完整技术展开聚焦 Tambo Generative UI SDK for React 中最核心的会话层能力线程Thread管理、会话输入Thread Input、AI 建议Suggestions、语音输入Voice Input与图片附件Image Attachments。读完本文你将掌握useTambo()、useTamboThreadInput()、useTamboThread()、useTamboThreadList()、useTamboSuggestions()、useTamboVoice()六大 hook 的完整用法理解内容块content block类型、提交选项、分页游标与用户鉴权隔离机制并看到每个能力在react-sdk源码中的真实实现路径。Quick Start最小可运行示例在 Tambo 的架构里线程承载一段完整的多轮对话。最简起步只需要两个 hookimport { useTambo, useTamboThreadInput } from tambo-ai/react; const { thread, messages, isIdle } useTambo(); const { value, setValue, submit } useTamboThreadInput(); await submit(); // sends current input valueuseTambo()负责读取当前线程状态、消息列表与生成状态useTamboThreadInput()负责管理输入框的值与发送动作。二者通过共享的 Stream Context 与 Thread Input Context联动useTamboThreadInput在 react-sdk/src/v1/hooks/use-tambo-v1-thread-input.ts 中直接由 react-sdk/src/v1/providers/tambo-v1-thread-input-provider.tsx 重导出保证所有使用该 hook 的组件包括后续的 Suggestions共享同一份输入状态。从源码看submit()的完整执行链路是先校验鉴权状态未识别用户直接抛错→ trim 输入文本 → 校验非空文本与图片均为空时抛出Message cannot be empty→ 组装InputMessage文本 图片转resource内容块→ 乐观清空输入框让用户立即输入下一条 → 调用useTamboSendMessage的 mutation 发送 → 成功后按提交时的快照清理已提交图片若创建了新线程则同步更新currentThreadId发送失败时如果用户还没开始输入新内容会自动恢复被清空的文本避免输入丢失。Thread Management用 useTambo 管理当前线程useTambo()是 SDK 的主入口 hook在 react-sdk/src/v1/hooks/use-tambo-v1.ts 中把客户端、线程状态、流状态、组件/工具注册表、鉴权状态组合成一个统一返回值import { useTambo, useTamboThreadInput, ComponentRenderer, } from tambo-ai/react; function Chat() { const { thread, // Current thread state messages, // Messages with computed properties isIdle, // True when not generating isStreaming, // True when streaming response isWaiting, // True when waiting for server currentThreadId, // Active thread ID switchThread, // Switch to different thread startNewThread, // Create new thread, returns ID cancelRun, // Cancel active generation } useTambo(); const { value, // Current input value setValue, // Update input submit, // Send message isPending, // Submission in progress images, // Staged image files addImage, // Add single image removeImage, // Remove image by 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 ); }源码细节消息的计算属性useTambo()返回的messages不是原始消息而是经过转换的ReactTamboThreadMessage。从 use-tambo-v1.ts 可以看到三个关键转换跨消息关联 tool_resulthook 会扫描当前线程全部消息构建已完成工具调用 ID集合把tool_result与它对应的tool_use关联起来tool_use 状态推导为每个tool_use内容块计算hasCompleted与statusMessage进行中显示_tambo_statusMessage或默认Calling ${name}完成后显示_tambo_completionStatusMessage或默认Called ${name}并过滤掉输入参数中以_tambo_开头的内部字段让消费方只看到真实的工具参数组件渲染缓存对component内容块维护一个 props JSON → ReactElement 的缓存 Mapprops 未变化时复用缓存的渲染元素避免流式更新过程中组件被反复重建。此外useTambo()还额外暴露了cancelRun乐观更新本地流状态为RUN_ERROR并调用client.threads.runs.delete取消服务端 run占位线程或无运行中 run 时为 no-op与updateThreadName改名后做 best-effort 的 React Query 缓存失效以及isIdentified快捷判断等价于authState.status identified为 true 时 SDK 才可发起 API 调用。Streaming State 流状态useTambo()返回三个布尔值来自统一的streamingStatePropertyTypeDescriptionisIdlebooleanNot generatingisWaitingbooleanWaiting for server responseisStreamingbooleanActively streaming responsestreamingState对象本身提供更多细节在源码中定义为StreamingState类型const { streamingState } useTambo(); // streamingState.status: idle | waiting | streaming // streamingState.runId: current run ID // streamingState.error: { message, code } if error occurred三个布尔值就是status的派生量见 use-tambo-v1.tsisIdle status idle、isWaiting status waiting、isStreaming status streaming。当线程尚未加载任何 run 时streamingState会回退为{ status: idle }保证 UI 永远拿到一个完整对象。Content Block Types 内容块类型消息体是内容块的数组不同类型携带不同字段。渲染时必须逐块 switch 处理TypeDescriptionKey FieldstextPlain texttextcomponentAI-generated componentid,name,propstool_useTool invocationid,name,inputtool_resultTool responsetoolUseId,contentresourceMCP resourceuri,name,text补充两点源码事实tool_use内容块经useTambo()转换后会额外得到hasCompleted、statusMessage、tamboDisplayProps三个计算字段见上文而resource块同时也是图片附件在消息中的承载形式——useTamboThreadInput的提交逻辑会把每张图片转换为{ type: resource, resource: { name, mimeType, blob } }其中blob是 data URL 中 base64 载荷部分tambo-v1-thread-input-provider.tsx。Submit Options 提交选项submit()接受一个可选的选项对象类型定义见 tambo-v1-thread-input-provider.tsxconst { submit } useTamboThreadInput(); await submit({ toolChoice: auto, // auto | required | none | { name: toolName } debug: true, // Enable debug logging for the stream });toolChoice控制模型对工具的使用策略默认autoauto模型自行决定是否调用工具required模型必须至少调用一个工具none禁止调用工具{ name: toolName }强制调用指定工具。这两个选项会被原样透传给useTamboSendMessage的 mutation最终进入流式 run 请求。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; }这是一个基于TanStack React Query的只读 hook不要用它承载活跃会话——活跃会话的状态由 Stream Context 管理而该 hook 仅用于按 ID 拉取快照。从 use-tambo-v1-thread.ts 源码看查询键为[v1-threads, threadId]查询函数调用client.threads.retrieve(threadId)staleTime为1000ms注释明确说明这是实时数据场景下的短缓存适合消息频繁更新的线程详情在鉴权未就绪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, // Filter by user (defaults to providers userKey) limit: 20, // Max results cursor: nextCursor, // Pagination cursor }); // data.threads: TamboThread[] // data.hasMore: boolean // data.nextCursor: string源码细节use-tambo-v1-thread-list.tsuserKey 合并策略显式传入的userKey优先否则回退到TamboProvider上下文中的userKeystaleTime 为 5000ms即列表数据 5 秒后视为过期触发自动重取查询键[v1-threads, list, effectiveOptions]会随过滤条件变化不同 user/limit 的列表缓存相互隔离分页是游标式cursor-based响应中hasMore表示是否还有下一页nextCursor是取下一页时传给cursor的令牌比页码式更适合增量追加的会话列表。线程切换链路startNewThread()生成一个临时 ID源码中的 placeholder thread ID并把它设为当前线程首次提交消息后SDK 会在服务端创建真实线程并把currentThreadId更新为真实 ID见 submit 逻辑中对SET_CURRENT_THREAD的 dispatch。switchThread(t.id)则直接切换到已有线程。SuggestionsAI 生成的后续建议每条助手消息之后Tambo 可自动生成 1–10 条跟进建议以胶囊按钮形式呈现点击即接受import { useTamboSuggestions } from tambo-ai/react; function Suggestions() { const { suggestions, isLoading, accept, isAccepting } useTamboSuggestions({ maxSuggestions: 3, // 1-10, default 3 autoGenerate: true, // Auto-generate after assistant message }); if (isLoading) return Skeleton /; return ( div classNamesuggestions {suggestions.map((s) ( button key{s.id} onClick{() accept({ suggestion: s })} disabled{isAccepting} {s.title} /button ))} /div ); }从 use-tambo-v1-suggestions.ts 源码看该 hook 内部同时使用了列表查询与创建 mutation自动生成当最新一条消息来自 assistant 且autoGenerate为 true 时自动拉取建议手动生成generate()调用建议创建接口返回新生成的建议列表接受建议accept({ suggestion, shouldSubmit })会把建议的detailedSuggestion写入共享输入框shouldSubmit: true时立即作为消息提交。Suggestion Type 建议结构Suggestion类型字段全部为必填interface Suggestion { id: string; // Unique identifier title: string; // Short label shown in the pill/button detailedSuggestion: string; // Full text submitted when accepted messageId: string; // ID of the message this relates to (use for initial suggestions) }注意title是按钮上的短标签而detailedSuggestion是实际提交的完整文本——二者可以不同例如标题查我的预订提交内容是更完整的指令句子。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 自动生成的建议所取代此时messageId留空字符串表示它们不依附于任何具体消息。Auto-Submit Suggestion 一键直达// Accept and immediately submit as a message accept({ suggestion: s, shouldSubmit: true });适用于点击建议 → 直接发送的无摩擦交互不传shouldSubmit默认 false时只把建议填入输入框由用户编辑后再发送。Manual Generation 手动生成某些场景如建议生成成本高、或需要用户主动触发应关闭自动生成const { generate, isGenerating } useTamboSuggestions({ autoGenerate: false, // Disable auto-generation }); button onClick{() generate()} disabled{isGenerating} Get suggestions /button;Voice Input语音转文字useTamboVoice()封装了录音 → 停止 → 自动转写的完整链路转写由 Tambo 服务端音频接口完成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 Returns 返回值PropertyTypeDescriptionstartRecording() voidStart recording, reset transcriptstopRecording() voidStop and start transcriptionisRecordingbooleanCurrently recordingisTranscribingbooleanProcessing audiotranscriptstring \| nullTranscribed texttranscriptionErrorstring \| nullTranscription errormediaAccessErrorstring \| nullMic access error源码实现use-tambo-voice.tsx值得拆解底层基于react-media-recorder录音参数为audio: true, video: false输出格式audio/webm录音停止后一旦拿到mediaBlobUrlhook 会自动触发转写 mutation先fetch(blobUrl)取回音频 Blob包装成recording.webm文件再调用client.beta.audio.transcribe({ file })成功后把返回文本写入transcriptstartRecording()会重置 transcript 与上一次的转写状态避免新旧录音串扰mediaAccessError来自录音器的麦克风授权错误空字符串会被归一化为null。转写成功后通常的做法是把transcript填入useTamboThreadInput()的输入框setValue(transcript)由用户确认后发送。Image Attachments图片附件图片通过useTamboThreadInput()统一管理支持单张与批量添加、按 ID 移除、一键清空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 Properties图片在内存中先以StagedImage形式暂存提交时才随消息发送PropertyTypeDescriptionidstringUnique image IDnamestringFile namedataUrlstringBase64 data URLfileFileOriginal File objectsizenumberFile size in bytestypestringMIME type实现细节use-message-images.tsaddImage/addImages会先校验 MIME 类型必须以image/开头非法文件直接抛错Only image files are allowed/No valid image files provided通过FileReader.readAsDataURL把文件转成 base64 data URL 存入dataUrlid使用crypto.randomUUID()生成removeImage(id)按 ID 过滤clearImages()清空全部。提交时的图片处理链路submit()会把每张暂存图片经stagedImageToResourceContent转成resource内容块校验 data URL 头部与 MIME 一致性后提取 base64 载荷与文本一起组成InputMessage发送提交成功后只清理本次提交时已存在的图片提交期间新添加的图片会被保留避免并发操作误删。User Authentication按用户隔离线程默认所有请求归属于一个匿名/共享上下文。要实现每个用户只看到自己的会话通过TamboProvider传入用户标识import { TamboProvider } from tambo-ai/react; function App() { return ( TamboProvider apiKey{apiKey} userKeyuser_123 // Simple user identifier Chat / /TamboProvider ); }对于基于 OAuth 的鉴权改用userTokenfunction App() { const userToken useUserToken(); // From your auth provider return ( TamboProvider apiKey{apiKey} userToken{userToken} Chat / /TamboProvider ); }两条规则userKey简单的用户标识字符串适合内部系统、演示或自建账号体系userTokenOAuth JWT 令牌适合已有第三方登录Google、GitHub 等的应用二者不要同时使用。从源码看userKey会随 API 请求透传如client.threads.runs.delete(runId, { threadId, userKey })、client.threads.update(threadId, { name, userKey })并在useTamboThreadList中作为默认过滤条件显式传入时覆盖上下文值而鉴权状态authState驱动着整个 SDK 的可用性——TamboProvider在 react-sdk/src/v1/providers/tambo-v1-provider.tsx 中依次组合了客户端、注册表、上下文助手、MCP、流管理等 Provider只有authState.status identified时查询类 hook 才会真正发起请求、submit()才允许发送。结语线程能力的全景回顾本文覆盖的六大 hook 构成了 Tambo Generative UI 会话体验的完整闭环能力Hook典型场景会话读写与流状态useTambo()消息渲染、流式状态、取消生成、切换/新建线程输入与发送useTamboThreadInput()输入框、提交选项、图片暂存单线程只读useTamboThread()详情页、历史归档线程列表useTamboThreadList()侧边栏、游标分页AI 建议useTamboSuggestions()自动/手动建议、一键提交语音useTamboVoice()录音与自动转写所有源码证据均可直接在本仓库中验证核心 hook 位于 react-sdk/src/v1/hooks/ 目录输入 Provider 见 tambo-v1-thread-input-provider.tsx图片暂存见 use-message-images.ts语音转写见 use-tambo-voice.tsx。建议读者在动手集成时将本文示例与上述源码对照阅读理解每个 hook 背后的状态模型才能在设计复杂交互多线程切换、流式渲染、图片并发提交时游刃有余。【免费下载链接】hydra-aiGenerative UI SDK for React项目地址: https://gitcode.com/GitHub_Trending/hy/hydra-ai创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表