246 lines
12 KiB
JavaScript
246 lines
12 KiB
JavaScript
const require_runtime = require('../../_virtual/_rolldown/runtime.cjs');
|
|
const require_CopilotChatConfigurationProvider = require('../../providers/CopilotChatConfigurationProvider.cjs');
|
|
const require_slots = require('../../lib/slots.cjs');
|
|
const require_CopilotKitProvider = require('../../providers/CopilotKitProvider.cjs');
|
|
const require_use_render_custom_messages = require('../../hooks/use-render-custom-messages.cjs');
|
|
const require_use_render_activity_message = require('../../hooks/use-render-activity-message.cjs');
|
|
require('../../hooks/index.cjs');
|
|
const require_CopilotChatAssistantMessage = require('./CopilotChatAssistantMessage.cjs');
|
|
const require_CopilotChatUserMessage = require('./CopilotChatUserMessage.cjs');
|
|
const require_CopilotChatReasoningMessage = require('./CopilotChatReasoningMessage.cjs');
|
|
let react = require("react");
|
|
react = require_runtime.__toESM(react);
|
|
let tailwind_merge = require("tailwind-merge");
|
|
let react_jsx_runtime = require("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.default.memo(function MemoizedAssistantMessage({ message, messages, isRunning, AssistantMessageComponent, slotProps }) {
|
|
return /* @__PURE__ */ (0, react_jsx_runtime.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.default.memo(function MemoizedUserMessage({ message, UserMessageComponent, slotProps }) {
|
|
return /* @__PURE__ */ (0, react_jsx_runtime.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.default.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.default.memo(function MemoizedReasoningMessage({ message, messages, isRunning, ReasoningMessageComponent, slotProps }) {
|
|
return /* @__PURE__ */ (0, react_jsx_runtime.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.default.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 = require_use_render_custom_messages.useRenderCustomMessages();
|
|
const { renderActivityMessage } = require_use_render_activity_message.useRenderActivityMessage();
|
|
const { copilotkit } = require_CopilotKitProvider.useCopilotKit();
|
|
const config = require_CopilotChatConfigurationProvider.useCopilotChatConfiguration();
|
|
const [, forceUpdate] = (0, react.useReducer)((x) => x + 1, 0);
|
|
(0, react.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] = (0, react.useState)(null);
|
|
(0, react.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__ */ (0, react_jsx_runtime.jsx)(MemoizedCustomMessage, {
|
|
message,
|
|
position: "before",
|
|
renderCustomMessage,
|
|
stateSnapshot
|
|
}, `${message.id}-custom-before`));
|
|
if (message.role === "assistant") {
|
|
let AssistantComponent = require_CopilotChatAssistantMessage.default;
|
|
let assistantSlotProps;
|
|
if (require_slots.isReactComponentType(assistantMessage)) AssistantComponent = assistantMessage;
|
|
else if (typeof assistantMessage === "string") assistantSlotProps = { className: assistantMessage };
|
|
else if (assistantMessage && typeof assistantMessage === "object") assistantSlotProps = assistantMessage;
|
|
elements.push(/* @__PURE__ */ (0, react_jsx_runtime.jsx)(MemoizedAssistantMessage, {
|
|
message,
|
|
messages,
|
|
isRunning,
|
|
AssistantMessageComponent: AssistantComponent,
|
|
slotProps: assistantSlotProps
|
|
}, message.id));
|
|
} else if (message.role === "user") {
|
|
let UserComponent = require_CopilotChatUserMessage.default;
|
|
let userSlotProps;
|
|
if (require_slots.isReactComponentType(userMessage)) UserComponent = userMessage;
|
|
else if (typeof userMessage === "string") userSlotProps = { className: userMessage };
|
|
else if (userMessage && typeof userMessage === "object") userSlotProps = userMessage;
|
|
elements.push(/* @__PURE__ */ (0, react_jsx_runtime.jsx)(MemoizedUserMessage, {
|
|
message,
|
|
UserMessageComponent: UserComponent,
|
|
slotProps: userSlotProps
|
|
}, message.id));
|
|
} else if (message.role === "activity") {
|
|
const activityMsg = message;
|
|
elements.push(/* @__PURE__ */ (0, react_jsx_runtime.jsx)(MemoizedActivityMessage, {
|
|
message: activityMsg,
|
|
renderActivityMessage
|
|
}, message.id));
|
|
} else if (message.role === "reasoning") {
|
|
let ReasoningComponent = require_CopilotChatReasoningMessage.default;
|
|
let reasoningSlotProps;
|
|
if (require_slots.isReactComponentType(reasoningMessage)) ReasoningComponent = reasoningMessage;
|
|
else if (typeof reasoningMessage === "string") reasoningSlotProps = { className: reasoningMessage };
|
|
else if (reasoningMessage && typeof reasoningMessage === "object") reasoningSlotProps = reasoningMessage;
|
|
elements.push(/* @__PURE__ */ (0, react_jsx_runtime.jsx)(MemoizedReasoningMessage, {
|
|
message,
|
|
messages,
|
|
isRunning,
|
|
ReasoningMessageComponent: ReasoningComponent,
|
|
slotProps: reasoningSlotProps
|
|
}, message.id));
|
|
}
|
|
if (renderCustomMessage) elements.push(/* @__PURE__ */ (0, react_jsx_runtime.jsx)(MemoizedCustomMessage, {
|
|
message,
|
|
position: "after",
|
|
renderCustomMessage,
|
|
stateSnapshot
|
|
}, `${message.id}-custom-after`));
|
|
return elements;
|
|
}).filter(Boolean);
|
|
if (children) return /* @__PURE__ */ (0, react_jsx_runtime.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__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
"data-copilotkit": true,
|
|
"data-testid": "copilot-message-list",
|
|
className: (0, tailwind_merge.twMerge)("copilotKitMessages cpk:flex cpk:flex-col", className),
|
|
...props,
|
|
children: [
|
|
messageElements,
|
|
interruptElement,
|
|
showCursor && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
className: "cpk:mt-2",
|
|
children: require_slots.renderSlot(cursor, CopilotChatMessageView.Cursor, {})
|
|
})
|
|
]
|
|
});
|
|
}
|
|
CopilotChatMessageView.Cursor = function Cursor({ className, ...props }) {
|
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
"data-testid": "copilot-loading-cursor",
|
|
className: (0, tailwind_merge.twMerge)("cpk:w-[11px] cpk:h-[11px] cpk:rounded-full cpk:bg-foreground cpk:animate-pulse-cursor cpk:ml-1", className),
|
|
...props
|
|
});
|
|
};
|
|
|
|
//#endregion
|
|
exports.default = CopilotChatMessageView;
|
|
//# sourceMappingURL=CopilotChatMessageView.cjs.map
|