mcpField
T
threads

threads

github.com/tambo-ai开发11.2k103 次安装3 周前更新
代码仓库

关于 threads

用于构建 Tambo React 对话界面的技能,涵盖会话状态、输入、建议、语音转写和图像附件,也介绍会话查询、列表及按用户隔离。

主要功能

  1. 01读取会话与流式状态
  2. 02提交并管理消息输入
  3. 03生成和接受后续建议
  4. 04转写语音输入
  5. 05添加和管理图像
  6. 06获取会话并列出对话

适用场景

  1. 01构建可显示流式状态的聊天界面
  2. 02让用户提交后续建议
  3. 03为对话添加语音和图像输入
  4. 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>
  );
}
流式传输状态
属性类型描述
isIdleboolean未生成
isWaitingboolean正在等待服务端响应
isStreamingboolean正在流式传输响应

streamingState 对象提供更多详细信息:

const { streamingState } = useTambo();
// streamingState.status: "idle" | "waiting" | "streaming"
// streamingState.runId: current run ID
// streamingState.error: { message, code } if error occurred
内容块类型

消息包含一个内容块数组。处理每种类型:

类型描述关键字段
text纯文本text
componentAI 生成的组件id、name、props
tool_use工具调用id、name、input
tool_result工具响应toolUseId、content
resourceMCP 资源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停止录音并开始转录
isRecordingboolean当前是否正在录音
isTranscribingboolean正在处理音频
transcriptstring | null转录的文本
transcriptionErrorstring | null转录错误
mediaAccessErrorstring | 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 属性
属性类型描述
idstring唯一图像 ID
namestring文件名
dataUrlstringBase64 数据 URL
fileFile原始 File 对象
sizenumber文件大小(字节)
typestringMIME 类型

用户身份验证

启用按用户隔离线程:

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