rdesign/frontend/node_modules/@copilotkitnext/react/dist/components/chat/CopilotChatMessageView.mjs

244 lines
11 KiB
JavaScript

import { useCopilotChatConfiguration } from "../../providers/CopilotChatConfigurationProvider.mjs";
import { isReactComponentType, renderSlot } from "../../lib/slots.mjs";
import { useCopilotKit } from "../../providers/CopilotKitProvider.mjs";
import { useRenderCustomMessages } from "../../hooks/use-render-custom-messages.mjs";
import { useRenderActivityMessage } from "../../hooks/use-render-activity-message.mjs";
import "../../hooks/index.mjs";
import CopilotChatAssistantMessage_default from "./CopilotChatAssistantMessage.mjs";
import CopilotChatUserMessage_default from "./CopilotChatUserMessage.mjs";
import CopilotChatReasoningMessage_default from "./CopilotChatReasoningMessage.mjs";
import React, { useEffect, useReducer, useState } from "react";
import { twMerge } from "tailwind-merge";
import { jsx, jsxs } from "react/jsx-runtime";
//#region src/components/chat/CopilotChatMessageView.tsx
/**
* Memoized wrapper for assistant messages to prevent re-renders when other messages change.
*/
const MemoizedAssistantMessage = React.memo(function MemoizedAssistantMessage({ message, messages, isRunning, AssistantMessageComponent, slotProps }) {
return /* @__PURE__ */ jsx(AssistantMessageComponent, {
message,
messages,
isRunning,
...slotProps
});
}, (prevProps, nextProps) => {
if (prevProps.message.id !== nextProps.message.id) return false;
if (prevProps.message.content !== nextProps.message.content) return false;
const prevToolCalls = prevProps.message.toolCalls;
const nextToolCalls = nextProps.message.toolCalls;
if (prevToolCalls?.length !== nextToolCalls?.length) return false;
if (prevToolCalls && nextToolCalls) for (let i = 0; i < prevToolCalls.length; i++) {
const prevTc = prevToolCalls[i];
const nextTc = nextToolCalls[i];
if (!prevTc || !nextTc) return false;
if (prevTc.id !== nextTc.id) return false;
if (prevTc.function.arguments !== nextTc.function.arguments) return false;
}
if (prevToolCalls && prevToolCalls.length > 0) {
const toolCallIds = new Set(prevToolCalls.map((tc) => tc.id));
const prevToolResults = prevProps.messages.filter((m) => m.role === "tool" && toolCallIds.has(m.toolCallId));
const nextToolResults = nextProps.messages.filter((m) => m.role === "tool" && toolCallIds.has(m.toolCallId));
if (prevToolResults.length !== nextToolResults.length) return false;
for (let i = 0; i < prevToolResults.length; i++) if (prevToolResults[i].content !== nextToolResults[i].content) return false;
}
if (nextProps.messages[nextProps.messages.length - 1]?.id === nextProps.message.id && prevProps.isRunning !== nextProps.isRunning) return false;
if (prevProps.AssistantMessageComponent !== nextProps.AssistantMessageComponent) return false;
if (prevProps.slotProps !== nextProps.slotProps) return false;
return true;
});
/**
* Memoized wrapper for user messages to prevent re-renders when other messages change.
*/
const MemoizedUserMessage = React.memo(function MemoizedUserMessage({ message, UserMessageComponent, slotProps }) {
return /* @__PURE__ */ jsx(UserMessageComponent, {
message,
...slotProps
});
}, (prevProps, nextProps) => {
if (prevProps.message.id !== nextProps.message.id) return false;
if (prevProps.message.content !== nextProps.message.content) return false;
if (prevProps.UserMessageComponent !== nextProps.UserMessageComponent) return false;
if (prevProps.slotProps !== nextProps.slotProps) return false;
return true;
});
/**
* Memoized wrapper for activity messages to prevent re-renders when other messages change.
*/
const MemoizedActivityMessage = React.memo(function MemoizedActivityMessage({ message, renderActivityMessage }) {
return renderActivityMessage(message);
}, (prevProps, nextProps) => {
if (prevProps.message.id !== nextProps.message.id) return false;
if (prevProps.message.activityType !== nextProps.message.activityType) return false;
if (JSON.stringify(prevProps.message.content) !== JSON.stringify(nextProps.message.content)) return false;
return true;
});
/**
* Memoized wrapper for reasoning messages to prevent re-renders when other messages change.
*/
const MemoizedReasoningMessage = React.memo(function MemoizedReasoningMessage({ message, messages, isRunning, ReasoningMessageComponent, slotProps }) {
return /* @__PURE__ */ jsx(ReasoningMessageComponent, {
message,
messages,
isRunning,
...slotProps
});
}, (prevProps, nextProps) => {
if (prevProps.message.id !== nextProps.message.id) return false;
if (prevProps.message.content !== nextProps.message.content) return false;
const prevIsLatest = prevProps.messages[prevProps.messages.length - 1]?.id === prevProps.message.id;
const nextIsLatest = nextProps.messages[nextProps.messages.length - 1]?.id === nextProps.message.id;
if (prevIsLatest !== nextIsLatest) return false;
if (nextIsLatest && prevProps.isRunning !== nextProps.isRunning) return false;
if (prevProps.ReasoningMessageComponent !== nextProps.ReasoningMessageComponent) return false;
if (prevProps.slotProps !== nextProps.slotProps) return false;
return true;
});
/**
* Memoized wrapper for custom messages to prevent re-renders when other messages change.
*/
const MemoizedCustomMessage = React.memo(function MemoizedCustomMessage({ message, position, renderCustomMessage }) {
return renderCustomMessage({
message,
position
});
}, (prevProps, nextProps) => {
if (prevProps.message.id !== nextProps.message.id) return false;
if (prevProps.position !== nextProps.position) return false;
if (prevProps.message.content !== nextProps.message.content) return false;
if (prevProps.message.role !== nextProps.message.role) return false;
if (JSON.stringify(prevProps.stateSnapshot) !== JSON.stringify(nextProps.stateSnapshot)) return false;
return true;
});
function CopilotChatMessageView({ messages = [], assistantMessage, userMessage, reasoningMessage, cursor, isRunning = false, children, className, ...props }) {
const renderCustomMessage = useRenderCustomMessages();
const { renderActivityMessage } = useRenderActivityMessage();
const { copilotkit } = useCopilotKit();
const config = useCopilotChatConfiguration();
const [, forceUpdate] = useReducer((x) => x + 1, 0);
useEffect(() => {
if (!config?.agentId) return;
const agent = copilotkit.getAgent(config.agentId);
if (!agent) return;
const subscription = agent.subscribe({ onStateChanged: forceUpdate });
return () => subscription.unsubscribe();
}, [
config?.agentId,
copilotkit,
forceUpdate
]);
const [interruptElement, setInterruptElement] = useState(null);
useEffect(() => {
setInterruptElement(copilotkit.interruptElement);
const subscription = copilotkit.subscribe({ onInterruptElementChanged: ({ interruptElement }) => {
setInterruptElement(interruptElement);
} });
return () => subscription.unsubscribe();
}, [copilotkit]);
const getStateSnapshotForMessage = (messageId) => {
if (!config) return void 0;
const resolvedRunId = copilotkit.getRunIdForMessage(config.agentId, config.threadId, messageId) ?? copilotkit.getRunIdsForThread(config.agentId, config.threadId).slice(-1)[0];
if (!resolvedRunId) return void 0;
return copilotkit.getStateByRun(config.agentId, config.threadId, resolvedRunId);
};
const deduplicatedMessages = [...new Map(messages.map((m) => [m.id, m])).values()];
if (process.env.NODE_ENV === "development" && deduplicatedMessages.length < messages.length) console.warn(`CopilotChatMessageView: Deduplicated ${messages.length - deduplicatedMessages.length} message(s) with duplicate IDs.`);
const messageElements = deduplicatedMessages.flatMap((message) => {
const elements = [];
const stateSnapshot = getStateSnapshotForMessage(message.id);
if (renderCustomMessage) elements.push(/* @__PURE__ */ jsx(MemoizedCustomMessage, {
message,
position: "before",
renderCustomMessage,
stateSnapshot
}, `${message.id}-custom-before`));
if (message.role === "assistant") {
let AssistantComponent = CopilotChatAssistantMessage_default;
let assistantSlotProps;
if (isReactComponentType(assistantMessage)) AssistantComponent = assistantMessage;
else if (typeof assistantMessage === "string") assistantSlotProps = { className: assistantMessage };
else if (assistantMessage && typeof assistantMessage === "object") assistantSlotProps = assistantMessage;
elements.push(/* @__PURE__ */ jsx(MemoizedAssistantMessage, {
message,
messages,
isRunning,
AssistantMessageComponent: AssistantComponent,
slotProps: assistantSlotProps
}, message.id));
} else if (message.role === "user") {
let UserComponent = CopilotChatUserMessage_default;
let userSlotProps;
if (isReactComponentType(userMessage)) UserComponent = userMessage;
else if (typeof userMessage === "string") userSlotProps = { className: userMessage };
else if (userMessage && typeof userMessage === "object") userSlotProps = userMessage;
elements.push(/* @__PURE__ */ jsx(MemoizedUserMessage, {
message,
UserMessageComponent: UserComponent,
slotProps: userSlotProps
}, message.id));
} else if (message.role === "activity") {
const activityMsg = message;
elements.push(/* @__PURE__ */ jsx(MemoizedActivityMessage, {
message: activityMsg,
renderActivityMessage
}, message.id));
} else if (message.role === "reasoning") {
let ReasoningComponent = CopilotChatReasoningMessage_default;
let reasoningSlotProps;
if (isReactComponentType(reasoningMessage)) ReasoningComponent = reasoningMessage;
else if (typeof reasoningMessage === "string") reasoningSlotProps = { className: reasoningMessage };
else if (reasoningMessage && typeof reasoningMessage === "object") reasoningSlotProps = reasoningMessage;
elements.push(/* @__PURE__ */ jsx(MemoizedReasoningMessage, {
message,
messages,
isRunning,
ReasoningMessageComponent: ReasoningComponent,
slotProps: reasoningSlotProps
}, message.id));
}
if (renderCustomMessage) elements.push(/* @__PURE__ */ jsx(MemoizedCustomMessage, {
message,
position: "after",
renderCustomMessage,
stateSnapshot
}, `${message.id}-custom-after`));
return elements;
}).filter(Boolean);
if (children) return /* @__PURE__ */ jsx("div", {
"data-copilotkit": true,
style: { display: "contents" },
children: children({
messageElements,
messages,
isRunning,
interruptElement
})
});
const lastMessage = messages[messages.length - 1];
const showCursor = isRunning && lastMessage?.role !== "reasoning";
return /* @__PURE__ */ jsxs("div", {
"data-copilotkit": true,
"data-testid": "copilot-message-list",
className: twMerge("copilotKitMessages cpk:flex cpk:flex-col", className),
...props,
children: [
messageElements,
interruptElement,
showCursor && /* @__PURE__ */ jsx("div", {
className: "cpk:mt-2",
children: renderSlot(cursor, CopilotChatMessageView.Cursor, {})
})
]
});
}
CopilotChatMessageView.Cursor = function Cursor({ className, ...props }) {
return /* @__PURE__ */ jsx("div", {
"data-testid": "copilot-loading-cursor",
className: twMerge("cpk:w-[11px] cpk:h-[11px] cpk:rounded-full cpk:bg-foreground cpk:animate-pulse-cursor cpk:ml-1", className),
...props
});
};
//#endregion
export { CopilotChatMessageView as default };
//# sourceMappingURL=CopilotChatMessageView.mjs.map