221 lines
8.2 KiB
JavaScript
221 lines
8.2 KiB
JavaScript
import { CopilotChatConfigurationProvider, useCopilotChatConfiguration } from "../../providers/CopilotChatConfigurationProvider.mjs";
|
|
import { renderSlot } from "../../lib/slots.mjs";
|
|
import { useCopilotKit } from "../../providers/CopilotKitProvider.mjs";
|
|
import { useAgent } from "../../hooks/use-agent.mjs";
|
|
import { useSuggestions } from "../../hooks/use-suggestions.mjs";
|
|
import { CopilotChatView } from "./CopilotChatView.mjs";
|
|
import { TranscriptionError, transcribeAudio } from "../../lib/transcription-client.mjs";
|
|
import { HttpAgent } from "@ag-ui/client";
|
|
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
|
|
import { DEFAULT_AGENT_ID, TranscriptionErrorCode, randomUUID } from "@copilotkitnext/shared";
|
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
import { merge } from "ts-deepmerge";
|
|
|
|
//#region src/components/chat/CopilotChat.tsx
|
|
function CopilotChat({ agentId, threadId, labels, chatView, onError, ...props }) {
|
|
const existingConfig = useCopilotChatConfiguration();
|
|
const resolvedAgentId = agentId ?? existingConfig?.agentId ?? DEFAULT_AGENT_ID;
|
|
const resolvedThreadId = useMemo(() => threadId ?? existingConfig?.threadId ?? randomUUID(), [threadId, existingConfig?.threadId]);
|
|
const { agent } = useAgent({ agentId: resolvedAgentId });
|
|
const { copilotkit } = useCopilotKit();
|
|
const { suggestions: autoSuggestions } = useSuggestions({ agentId: resolvedAgentId });
|
|
const onErrorRef = useRef(onError);
|
|
useEffect(() => {
|
|
onErrorRef.current = onError;
|
|
}, [onError]);
|
|
useEffect(() => {
|
|
if (!onErrorRef.current) return;
|
|
const subscription = copilotkit.subscribe({ onError: (event) => {
|
|
if (event.context?.agentId === resolvedAgentId || !event.context?.agentId) onErrorRef.current?.({
|
|
error: event.error,
|
|
code: event.code,
|
|
context: event.context
|
|
});
|
|
} });
|
|
return () => {
|
|
subscription.unsubscribe();
|
|
};
|
|
}, [copilotkit, resolvedAgentId]);
|
|
const [transcribeMode, setTranscribeMode] = useState("input");
|
|
const [inputValue, setInputValue] = useState("");
|
|
const [transcriptionError, setTranscriptionError] = useState(null);
|
|
const [isTranscribing, setIsTranscribing] = useState(false);
|
|
const isTranscriptionEnabled = copilotkit.audioFileTranscriptionEnabled;
|
|
const isMediaRecorderSupported = typeof window !== "undefined" && typeof MediaRecorder !== "undefined";
|
|
const { messageView: providedMessageView, suggestionView: providedSuggestionView, onStop: providedStopHandler, ...restProps } = props;
|
|
useEffect(() => {
|
|
let detached = false;
|
|
const connectAbortController = new AbortController();
|
|
if (agent instanceof HttpAgent) agent.abortController = connectAbortController;
|
|
const connect = async (agent) => {
|
|
try {
|
|
await copilotkit.connectAgent({ agent });
|
|
} catch (error) {
|
|
if (detached) return;
|
|
console.error("CopilotChat: connectAgent failed", error);
|
|
}
|
|
};
|
|
agent.threadId = resolvedThreadId;
|
|
connect(agent);
|
|
return () => {
|
|
detached = true;
|
|
connectAbortController.abort();
|
|
agent.detachActiveRun();
|
|
};
|
|
}, [
|
|
resolvedThreadId,
|
|
agent,
|
|
resolvedAgentId
|
|
]);
|
|
const onSubmitInput = useCallback(async (value) => {
|
|
agent.addMessage({
|
|
id: randomUUID(),
|
|
role: "user",
|
|
content: value
|
|
});
|
|
setInputValue("");
|
|
try {
|
|
await copilotkit.runAgent({ agent });
|
|
} catch (error) {
|
|
console.error("CopilotChat: runAgent failed", error);
|
|
}
|
|
}, [agent]);
|
|
const handleSelectSuggestion = useCallback(async (suggestion) => {
|
|
agent.addMessage({
|
|
id: randomUUID(),
|
|
role: "user",
|
|
content: suggestion.message
|
|
});
|
|
try {
|
|
await copilotkit.runAgent({ agent });
|
|
} catch (error) {
|
|
console.error("CopilotChat: runAgent failed after selecting suggestion", error);
|
|
}
|
|
}, [agent]);
|
|
const stopCurrentRun = useCallback(() => {
|
|
try {
|
|
copilotkit.stopAgent({ agent });
|
|
} catch (error) {
|
|
console.error("CopilotChat: stopAgent failed", error);
|
|
try {
|
|
agent.abortRun();
|
|
} catch (abortError) {
|
|
console.error("CopilotChat: abortRun fallback failed", abortError);
|
|
}
|
|
}
|
|
}, [agent]);
|
|
const handleStartTranscribe = useCallback(() => {
|
|
setTranscriptionError(null);
|
|
setTranscribeMode("transcribe");
|
|
}, []);
|
|
const handleCancelTranscribe = useCallback(() => {
|
|
setTranscriptionError(null);
|
|
setTranscribeMode("input");
|
|
}, []);
|
|
const handleFinishTranscribe = useCallback(() => {
|
|
setTranscribeMode("input");
|
|
}, []);
|
|
const handleFinishTranscribeWithAudio = useCallback(async (audioBlob) => {
|
|
setIsTranscribing(true);
|
|
try {
|
|
setTranscriptionError(null);
|
|
const result = await transcribeAudio(copilotkit, audioBlob);
|
|
setInputValue((prev) => {
|
|
const trimmedPrev = prev.trim();
|
|
if (trimmedPrev) return `${trimmedPrev} ${result.text}`;
|
|
return result.text;
|
|
});
|
|
} catch (error) {
|
|
console.error("CopilotChat: Transcription failed", error);
|
|
if (error instanceof TranscriptionError) {
|
|
const { code, retryable, message } = error.info;
|
|
switch (code) {
|
|
case TranscriptionErrorCode.RATE_LIMITED:
|
|
setTranscriptionError("Too many requests. Please wait a moment.");
|
|
break;
|
|
case TranscriptionErrorCode.AUTH_FAILED:
|
|
setTranscriptionError("Authentication error. Please check your configuration.");
|
|
break;
|
|
case TranscriptionErrorCode.AUDIO_TOO_LONG:
|
|
setTranscriptionError("Recording is too long. Please try a shorter recording.");
|
|
break;
|
|
case TranscriptionErrorCode.AUDIO_TOO_SHORT:
|
|
setTranscriptionError("Recording is too short. Please try again.");
|
|
break;
|
|
case TranscriptionErrorCode.INVALID_AUDIO_FORMAT:
|
|
setTranscriptionError("Audio format not supported.");
|
|
break;
|
|
case TranscriptionErrorCode.SERVICE_NOT_CONFIGURED:
|
|
setTranscriptionError("Transcription service is not available.");
|
|
break;
|
|
case TranscriptionErrorCode.NETWORK_ERROR:
|
|
setTranscriptionError("Network error. Please check your connection.");
|
|
break;
|
|
default: setTranscriptionError(retryable ? "Transcription failed. Please try again." : message);
|
|
}
|
|
} else setTranscriptionError("Transcription failed. Please try again.");
|
|
} finally {
|
|
setIsTranscribing(false);
|
|
}
|
|
}, []);
|
|
useEffect(() => {
|
|
if (transcriptionError) {
|
|
const timer = setTimeout(() => {
|
|
setTranscriptionError(null);
|
|
}, 5e3);
|
|
return () => clearTimeout(timer);
|
|
}
|
|
}, [transcriptionError]);
|
|
const mergedProps = merge({
|
|
isRunning: agent.isRunning,
|
|
suggestions: autoSuggestions,
|
|
onSelectSuggestion: handleSelectSuggestion,
|
|
suggestionView: providedSuggestionView
|
|
}, {
|
|
...restProps,
|
|
...typeof providedMessageView === "string" ? { messageView: { className: providedMessageView } } : providedMessageView !== void 0 ? { messageView: providedMessageView } : {}
|
|
});
|
|
const hasMessages = agent.messages.length > 0;
|
|
const effectiveStopHandler = agent.isRunning && hasMessages ? providedStopHandler ?? stopCurrentRun : providedStopHandler;
|
|
const showTranscription = isTranscriptionEnabled && isMediaRecorderSupported;
|
|
const effectiveMode = isTranscribing ? "processing" : transcribeMode;
|
|
const RenderedChatView = renderSlot(chatView, CopilotChatView, merge(mergedProps, {
|
|
messages: useMemo(() => [...agent.messages], [JSON.stringify(agent.messages)]),
|
|
onSubmitMessage: onSubmitInput,
|
|
onStop: effectiveStopHandler,
|
|
inputMode: effectiveMode,
|
|
inputValue,
|
|
onInputChange: setInputValue,
|
|
onStartTranscribe: showTranscription ? handleStartTranscribe : void 0,
|
|
onCancelTranscribe: showTranscription ? handleCancelTranscribe : void 0,
|
|
onFinishTranscribe: showTranscription ? handleFinishTranscribe : void 0,
|
|
onFinishTranscribeWithAudio: showTranscription ? handleFinishTranscribeWithAudio : void 0
|
|
}));
|
|
return /* @__PURE__ */ jsxs(CopilotChatConfigurationProvider, {
|
|
agentId: resolvedAgentId,
|
|
threadId: resolvedThreadId,
|
|
labels,
|
|
children: [transcriptionError && /* @__PURE__ */ jsx("div", {
|
|
style: {
|
|
position: "absolute",
|
|
bottom: "100px",
|
|
left: "50%",
|
|
transform: "translateX(-50%)",
|
|
backgroundColor: "#ef4444",
|
|
color: "white",
|
|
padding: "8px 16px",
|
|
borderRadius: "8px",
|
|
fontSize: "14px",
|
|
zIndex: 50
|
|
},
|
|
children: transcriptionError
|
|
}), RenderedChatView]
|
|
});
|
|
}
|
|
(function(_CopilotChat) {
|
|
_CopilotChat.View = CopilotChatView;
|
|
})(CopilotChat || (CopilotChat = {}));
|
|
|
|
//#endregion
|
|
export { CopilotChat };
|
|
//# sourceMappingURL=CopilotChat.mjs.map
|