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