/** * RAG 검색 챗봇 LLM 관련 JavaScript */ /** * LLM 목록 로드 함수 */ async function loadLLMs() { try { console.log('LLM 목록 로드 시작'); // API 요청 const response = await fetch('/api/llm'); if (!response.ok) { throw new Error(`HTTP error! status: ${response.status}`); } const data = await response.json(); supportedLLMs = data.supported_llms; currentLLM = data.current_llm.id; console.log(`로드된 LLM 수: ${supportedLLMs.length}, 현재 LLM: ${currentLLM}`); // LLM 선택 드롭다운 업데이트 llmSelect.innerHTML = ''; supportedLLMs.forEach(llm => { const option = document.createElement('option'); option.value = llm.id; option.textContent = llm.name; option.selected = llm.current; llmSelect.appendChild(option); }); // 현재 LLM 표시 updateCurrentLLMInfo(data.current_llm); console.log('LLM 목록 로드 완료'); } catch (error) { console.error('LLM 목록 로드 실패:', error); } } /** * LLM 변경 함수 * @param {string} llmId - 변경할 LLM ID */ async function changeLLM(llmId) { try { console.log(`LLM 변경 시작: ${llmId}`); // API 요청 const response = await fetch('/api/llm', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ llm_id: llmId }) }); if (!response.ok) { throw new Error(`HTTP error! status: ${response.status}`); } const data = await response.json(); if (data.success) { currentLLM = llmId; updateCurrentLLMInfo(data.current_llm); console.log(`LLM 변경 성공: ${data.current_llm.name}`); // 시스템 메시지 추가 const systemMessage = `LLM이 ${data.current_llm.name}(으)로 변경되었습니다. 모델: ${data.current_llm.model}`; addSystemNotification(systemMessage); } else if (data.error) { console.error('LLM 변경 오류:', data.error); alert(`LLM 변경 오류: ${data.error}`); } } catch (error) { console.error('LLM 변경 실패:', error); alert('LLM 변경 중 오류가 발생했습니다.'); } } /** * 현재 LLM 정보 표시 업데이트 * @param {Object} llmInfo - LLM 정보 객체 */ function updateCurrentLLMInfo(llmInfo) { console.log(`현재 LLM 정보 업데이트: ${llmInfo.name} (${llmInfo.model})`); if (currentLLMInfo) { currentLLMInfo.textContent = `${llmInfo.name} (${llmInfo.model})`; } } /** * 채팅 메시지 전송 함수 */ async function sendMessage() { const message = userInput.value.trim(); if (!message) return; console.log(`메시지 전송: "${message}"`); // UI 업데이트 addMessage(message, 'user'); userInput.value = ''; adjustTextareaHeight(); // 로딩 메시지 추가 const loadingMessageId = addLoadingMessage(); try { console.log('채팅 API 요청 시작'); // API 요청 const response = await fetch('/api/chat', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ query: message, llm_id: currentLLM // 현재 선택된 LLM 전송 }) }); if (!response.ok) { throw new Error(`HTTP error! status: ${response.status}`); } const data = await response.json(); console.log('채팅 API 응답 수신 완료'); // 로딩 메시지 제거 removeLoadingMessage(loadingMessageId); // 응답 표시 if (data.error) { console.error('채팅 응답 오류:', data.error); addErrorMessage(data.error); } else { // LLM 정보 업데이트 if (data.llm) { updateCurrentLLMInfo(data.llm); } console.log('봇 응답 표시'); addMessage(data.answer, 'bot', null, data.sources); } } catch (error) { console.error('채팅 요청 오류:', error); removeLoadingMessage(loadingMessageId); addErrorMessage('오류가 발생했습니다. 다시 시도해 주세요.'); } } /** * 음성 녹음 시작 함수 */ async function startRecording() { if (isRecording) return; console.log('음성 녹음 시작 요청'); try { const stream = await navigator.mediaDevices.getUserMedia({ audio: true }); isRecording = true; audioChunks = []; mediaRecorder = new MediaRecorder(stream); mediaRecorder.addEventListener('dataavailable', (event) => { if (event.data.size > 0) audioChunks.push(event.data); console.log('오디오 데이터 청크 수신됨'); }); mediaRecorder.addEventListener('stop', sendAudioMessage); // 녹음 시작 mediaRecorder.start(); console.log('MediaRecorder 시작됨'); // UI 업데이트 micButton.style.display = 'none'; recordingStatus.classList.remove('hidden'); } catch (error) { console.error('음성 녹음 권한을 얻을 수 없습니다:', error); alert('마이크 접근 권한이 필요합니다.'); } } /** * 음성 녹음 중지 함수 */ function stopRecording() { if (!isRecording || !mediaRecorder) return; console.log('음성 녹음 중지 요청'); mediaRecorder.stop(); isRecording = false; // UI 업데이트 micButton.style.display = 'flex'; recordingStatus.classList.add('hidden'); console.log('MediaRecorder 중지됨'); } /** * 녹음된 오디오 메시지 전송 함수 */ async function sendAudioMessage() { if (audioChunks.length === 0) return; console.log(`오디오 메시지 전송 준비, ${audioChunks.length}개 청크`); // 오디오 Blob 생성 const audioBlob = new Blob(audioChunks, { type: 'audio/wav' }); // 로딩 메시지 추가 const loadingMessageId = addLoadingMessage(); try { // FormData에 오디오 추가 const formData = new FormData(); formData.append('audio', audioBlob, 'recording.wav'); // 현재 선택된 LLM 추가 formData.append('llm_id', currentLLM); console.log('음성 API 요청 시작'); // API 요청 const response = await fetch('/api/voice', { method: 'POST', body: formData }); if (!response.ok) { throw new Error(`HTTP error! status: ${response.status}`); } const data = await response.json(); console.log('음성 API 응답 수신 완료'); // 로딩 메시지 제거 removeLoadingMessage(loadingMessageId); // 응답 표시 if (data.error) { console.error('음성 응답 오류:', data.error); addErrorMessage(data.error); } else { // LLM 정보 업데이트 if (data.llm) { updateCurrentLLMInfo(data.llm); } // 사용자 메시지(음성 텍스트) 추가 if (data.transcription) { console.log(`음성 인식 결과: "${data.transcription}"`); addMessage(data.transcription, 'user'); } // 봇 응답 추가 console.log('봇 응답 표시'); addMessage(data.answer, 'bot', data.transcription, data.sources); } } catch (error) { console.error('음성 요청 오류:', error); removeLoadingMessage(loadingMessageId); addErrorMessage('오디오 처리 중 오류가 발생했습니다. 다시 시도해 주세요.'); } }