Skip to main content

Documentation Index

Fetch the complete documentation index at: https://nvd-54.mintlify.app/llms.txt

Use this file to discover all available pages before exploring further.

AI Elements 是一个基于 shadcn/ui 的可组合组件库,专为 AI 聊天界面构建。ConversationMessageToolReasoningPromptInput 等组件设计为可直接嵌入任何 React 项目,并通过极少的胶水代码连接到 stream.messages
克隆并运行完整的 AI Elements 示例,查看工具调用渲染、推理显示、流式消息等功能的工作项目。

工作原理

  1. **以源文件形式安装组件:**AI Elements 通过 CLI 将组件直接添加到你的项目中(shadcn/ui 注册表风格)
  2. **将消息映射到组件:**遍历 stream.messages,将 HumanMessage 实例渲染为用户气泡,将 AIMessage 实例渲染为助手回复
  3. **组合更丰富的 UI:**用 <Tool> 包裹工具调用,用 <Reasoning> 包裹推理内容,用 <Conversation> 包裹全部内容以管理滚动

安装

通过 CLI 安装 AI Elements 组件。它们作为可编辑的源文件添加到你的项目中:
npm install @langchain/react @ai-elements/react
npx ai-elements@latest add conversation message prompt-input tool reasoning suggestion

连接 useStream

直接从 stream.messages 渲染 AI Elements 组件。每个 LangChain BaseMessage 映射到一个组件:
import { useStream } from "@langchain/react";
import { HumanMessage, AIMessage } from "@langchain/core/messages";

import {
  Conversation,
  ConversationContent,
  ConversationScrollButton,
} from "@/components/ai-elements/conversation";
import {
  Message,
  MessageContent,
  MessageResponse,
} from "@/components/ai-elements/message";
import {
  Tool,
  ToolHeader,
  ToolContent,
  ToolInput,
  ToolOutput,
} from "@/components/ai-elements/tool";
import {
  Reasoning,
  ReasoningTrigger,
  ReasoningContent,
} from "@/components/ai-elements/reasoning";
import {
  PromptInput,
  PromptInputBody,
  PromptInputTextarea,
  PromptInputFooter,
  PromptInputSubmit,
} from "@/components/ai-elements/prompt-input";

export function Chat() {
  const stream = useStream({
    apiUrl: "http://localhost:2024",
    assistantId: "agent",
  });

  return (
    <div className="flex flex-col h-dvh">
      <Conversation className="flex-1">
        <ConversationContent>
          {stream.messages.map((msg, i) => {
            if (HumanMessage.isInstance(msg)) {
              return (
                <Message key={i} from="user">
                  <MessageContent>{msg.content as string}</MessageContent>
                </Message>
              );
            }
            if (AIMessage.isInstance(msg)) {
              return (
                <div key={i}>
                  {/* 推理块(当模型发出思考 Token 时显示) */}
                  <Reasoning>
                    <ReasoningTrigger />
                    <ReasoningContent>{getReasoningText(msg)}</ReasoningContent>
                  </Reasoning>

                  {/* 内联工具调用,带有输入/输出显示 */}
                  {getToolCalls(msg).map((tc) => (
                    <Tool key={tc.id} defaultOpen>
                      <ToolHeader type={`tool-${tc.name}`} state={tc.state} />
                      <ToolContent>
                        <ToolInput input={tc.args} />
                        {tc.output && (
                          <ToolOutput output={tc.output} errorText={undefined} />
                        )}
                      </ToolContent>
                    </Tool>
                  ))}

                  {/* 流式文本回复 */}
                  <Message from="assistant">
                    <MessageContent>
                      <MessageResponse>{getTextContent(msg)}</MessageResponse>
                    </MessageContent>
                  </Message>
                </div>
              );
            }
          })}
        </ConversationContent>
        <ConversationScrollButton />
      </Conversation>

      <PromptInput
        onSubmit={({ text }) =>
          stream.submit({ messages: [{ type: "human", content: text }] })
        }
      >
        <PromptInputBody>
          <PromptInputTextarea placeholder="问我些什么..." />
        </PromptInputBody>
        <PromptInputFooter>
          <PromptInputSubmit
            status={stream.isLoading ? "streaming" : "ready"}
          />
        </PromptInputFooter>
      </PromptInput>
    </div>
  );
}

最佳实践

  • **自由编辑源文件:**组件存在于你的项目中,而非作为外部包依赖,因此你可以随意修改而无需 fork
  • **使用 MessageResponse 进行流式输出:**它能正确处理流式部分 Token;避免在流式输出期间直接渲染原始 msg.content
  • Conversation 包裹:Conversation 组件管理滚动行为,使新消息自动滚动到视图中
  • **使用 isInstance 判断:**使用 HumanMessage.isInstance(msg)AIMessage.isInstance(msg) 而非检查 msg.getType(),以获得正确的 TypeScript 类型缩窄