feat(chat): stream paseo tool calls
This commit is contained in:
@@ -7,13 +7,14 @@ import { getMessageText, isMessageStreaming } from "@/lib/chat/transforms";
|
||||
import type { ChatMessageProps } from "@/lib/chat/types";
|
||||
|
||||
import { AssistantIdentity } from "./assistant-identity";
|
||||
import { ChatToolCall } from "./chat-tool-call";
|
||||
|
||||
export const ChatMessage = ({ message }: ChatMessageProps) => {
|
||||
const isUser = message.role === "user";
|
||||
const isStreaming = isMessageStreaming(message);
|
||||
const text = getMessageText(message);
|
||||
|
||||
if (!text && !isStreaming) {
|
||||
if (message.parts.length === 0 && !isStreaming) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -63,6 +64,11 @@ export const ChatMessage = ({ message }: ChatMessageProps) => {
|
||||
: "w-full max-w-none overflow-visible leading-7 text-foreground/95"
|
||||
}
|
||||
>
|
||||
{message.parts.map((part) =>
|
||||
part.type === "dynamic-tool" ? (
|
||||
<ChatToolCall key={part.toolCallId} part={part} />
|
||||
) : null
|
||||
)}
|
||||
{content}
|
||||
</BubbleContent>
|
||||
</Bubble>
|
||||
|
||||
41
apps/web/src/components/chat/chat-tool-call.tsx
Normal file
41
apps/web/src/components/chat/chat-tool-call.tsx
Normal file
@@ -0,0 +1,41 @@
|
||||
import { Check, LoaderCircle, Terminal, TriangleAlert } from "lucide-react";
|
||||
|
||||
import type { ChatToolCallProps } from "@/lib/chat/types";
|
||||
|
||||
export const ChatToolCall = ({ part }: ChatToolCallProps) => {
|
||||
let detail =
|
||||
typeof part.input === "string"
|
||||
? part.input
|
||||
: JSON.stringify(part.input, null, 2);
|
||||
let Icon = LoaderCircle;
|
||||
let status = "Running";
|
||||
if (part.state === "output-available") {
|
||||
detail =
|
||||
typeof part.output === "string"
|
||||
? part.output
|
||||
: JSON.stringify(part.output, null, 2);
|
||||
Icon = Check;
|
||||
status = "Completed";
|
||||
} else if (part.state === "output-error") {
|
||||
detail = part.errorText;
|
||||
Icon = TriangleAlert;
|
||||
status = "Failed";
|
||||
}
|
||||
const isRunning = part.state === "input-available";
|
||||
|
||||
return (
|
||||
<details className="group/tool overflow-hidden rounded-xl border border-border/60 bg-card/45 text-xs">
|
||||
<summary className="flex cursor-pointer list-none items-center gap-2 px-3 py-2 text-muted-foreground marker:hidden">
|
||||
<Terminal className="size-3.5 shrink-0" />
|
||||
<span className="min-w-0 flex-1 truncate font-medium text-foreground/85">
|
||||
{part.toolName}
|
||||
</span>
|
||||
<span>{status}</span>
|
||||
<Icon className={`size-3.5 ${isRunning ? "animate-spin" : ""}`} />
|
||||
</summary>
|
||||
<pre className="max-h-72 overflow-auto border-t border-border/50 bg-background/60 px-3 py-2 font-mono text-[11px] leading-5 whitespace-pre-wrap text-muted-foreground">
|
||||
{detail}
|
||||
</pre>
|
||||
</details>
|
||||
);
|
||||
};
|
||||
@@ -6,7 +6,7 @@ export const CHAT_AGENT = {
|
||||
name: "zopu",
|
||||
} as const;
|
||||
|
||||
export const MODEL_LABEL = "MiniMax M3";
|
||||
export const MODEL_LABEL = "GLM-5.2";
|
||||
|
||||
export const SUGGESTIONS = [
|
||||
["Think it through", "Help me make a difficult decision"],
|
||||
|
||||
@@ -24,11 +24,15 @@ export const getMessageText = (message: FlueConversationMessage): string =>
|
||||
);
|
||||
|
||||
export const isMessageStreaming = (message: FlueConversationMessage): boolean =>
|
||||
message.parts.some(
|
||||
(part) =>
|
||||
message.parts.some((part) => {
|
||||
if (part.type === "dynamic-tool") {
|
||||
return part.state === "input-available";
|
||||
}
|
||||
return (
|
||||
(part.type === "text" || part.type === "reasoning") &&
|
||||
part.state === "streaming"
|
||||
);
|
||||
);
|
||||
});
|
||||
|
||||
export const getStatusDotClass = (status: AgentStatus): string => {
|
||||
if (status === "error") {
|
||||
|
||||
@@ -1,4 +1,8 @@
|
||||
import type { AgentStatus, FlueConversationMessage } from "@flue/react";
|
||||
import type {
|
||||
AgentStatus,
|
||||
FlueConversationMessage,
|
||||
FlueConversationPart,
|
||||
} from "@flue/react";
|
||||
|
||||
export interface ChatAgentState {
|
||||
error?: Error;
|
||||
@@ -29,4 +33,8 @@ export interface ChatMessageProps {
|
||||
message: FlueConversationMessage;
|
||||
}
|
||||
|
||||
export interface ChatToolCallProps {
|
||||
part: Extract<FlueConversationPart, { type: "dynamic-tool" }>;
|
||||
}
|
||||
|
||||
export type AssistantResponseState = "thinking" | "writing";
|
||||
|
||||
Reference in New Issue
Block a user