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 を、OAuth JWT トークンには userToken を使用してください。両方を同時に使用しないでください。
コメント
Markdown 対応