关于 threads
用于构建 Tambo React 对话界面的技能,涵盖会话状态、输入、建议、语音转写和图像附件,也介绍会话查询、列表及按用户隔离。
主要功能
- 01读取会话与流式状态
- 02提交并管理消息输入
- 03生成和接受后续建议
- 04转写语音输入
- 05添加和管理图像
- 06获取会话并列出对话
适用场景
- 01构建可显示流式状态的聊天界面
- 02让用户提交后续建议
- 03为对话添加语音和图像输入
- 04展示会话历史或指定会话
MCP 相关
该技能说明消息中的 MCP 资源内容块,但未介绍连接或管理 MCP 服务。
安装
复制下面的命令,在终端中运行。
$
npx skills add https:/ / github.com/ tambo-ai/ tambo --skill threads安装后在 Claude Code 中输入/threads确认已加载。
详细介绍
线程和输入
管理对话、建议、语音输入和图像附件。
快速入门
import { useTambo, useTamboThreadInput } from "@tambo-ai/react";
const { thread, messages, isIdle } = useTambo();
const { value, setValue, submit } = useTamboThreadInput();
await submit(); // sends current input value
线程管理
使用 useTambo() 和 useTamboThreadInput() 访问和管理当前线程:
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>
);
}
流式传输状态
| 属性 | 类型 | 描述 |
|---|---|---|
isIdle | boolean | 未生成 |
isWaiting | boolean | 正在等待服务端响应 |
isStreaming | boolean | 正在流式传输响应 |
streamingState 对象提供更多详细信息:
const { streamingState } = useTambo();
// streamingState.status: "idle" | "waiting" | "streaming"
// streamingState.runId: current run ID
// streamingState.error: { message, code } if error occurred
内容块类型
消息包含一个内容块数组。处理每种类型:
| 类型 | 描述 | 关键字段 |
|---|---|---|
text | 纯文本 | text |
component | AI 生成的组件 | id、name、props |
tool_use | 工具调用 | id、name、input |
tool_result | 工具响应 | toolUseId、content |
resource | MCP 资源 | uri、name、text |
提交选项
const { submit } = useTamboThreadInput();
await submit({
toolChoice: "auto", // "auto" | "required" | "none" | { name: "toolName" }
debug: true, // Enable debug logging for the stream
});
按 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 <div>Failed to load thread</div>;
return <div>{thread.name}</div>;
}
这是一个 React Query 钩子,用于只读线程获取,不适用于当前活动对话。
线程列表
管理多个对话:
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>
);
}
线程列表选项
const { data } = useTamboThreadList({
userKey: "user_123", // Filter by user (defaults to provider's userKey)
limit: 20, // Max results
cursor: nextCursor, // Pagination cursor
});
// data.threads: TamboThread[]
// data.hasMore: boolean
// data.nextCursor: string
建议
每条助手消息之后生成的 AI 后续建议:
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 className="suggestions">
{suggestions.map((s) => (
<button
key={s.id}
onClick={() => accept({ suggestion: s })}
disabled={isAccepting}
>
{s.title}
</button>
))}
</div>
);
}
自动提交建议
// Accept and immediately submit as a message
accept({ suggestion: s, shouldSubmit: true });
手动生成
const { generate, isGenerating } = useTamboSuggestions({
autoGenerate: false, // Disable auto-generation
});
<button onClick={() => generate()} disabled={isGenerating}>
Get suggestions
</button>;
语音输入
语音转文本:
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 && <span>Transcribing...</span>}
{transcript && <p>{transcript}</p>}
{transcriptionError && <p className="error">{transcriptionError}</p>}
</div>
);
}
语音钩子返回值
| 属性 | 类型 | 描述 |
|---|---|---|
startRecording | () => void | 开始录音并重置转录文本 |
stopRecording | () => void | 停止录音并开始转录 |
isRecording | boolean | 当前是否正在录音 |
isTranscribing | boolean | 正在处理音频 |
transcript | string | null | 转录的文本 |
transcriptionError | string | null | 转录错误 |
mediaAccessError | string | null | 麦克风访问错误 |
图像附件
通过 useTamboThreadInput() 管理图像:
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
type="file"
accept="image/*"
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 属性
| 属性 | 类型 | 描述 |
|---|---|---|
id | string | 唯一图像 ID |
name | string | 文件名 |
dataUrl | string | Base64 数据 URL |
file | File | 原始 File 对象 |
size | number | 文件大小(字节) |
type | string | MIME 类型 |
用户身份验证
启用按用户隔离线程:
import { TamboProvider } from "@tambo-ai/react";
function App() {
return (
<TamboProvider
apiKey={apiKey}
userKey="user_123" // Simple user identifier
>
<Chat />
</TamboProvider>
);
}
对于基于 OAuth 的身份验证,请改用 userToken:
function App() {
const userToken = useUserToken(); // From your auth provider
return (
<TamboProvider apiKey={apiKey} userToken={userToken}>
<Chat />
</TamboProvider>
);
}
使用 userKey 进行简单的用户识别,或使用 userToken 提供 OAuth JWT 令牌。不要同时使用两者。
评论
支持 Markdown