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}>
{/* 推理块(当模型发出思考令牌时显示) */}
<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>
);
}