jeongsoo's picture
Initial setup
2382288
<!DOCTYPE html>
<html lang="ko">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>RAG 검색 챗봇</title>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0-beta3/css/all.min.css">
<link rel="stylesheet" href="{{ url_for('static', filename='css/style.css') }}">
<link rel="stylesheet" href="{{ url_for('static', filename='css/device-style.css') }}">
</head>
<body>
<div class="container">
<header>
<h1>RAG 검색 챗봇</h1>
<div class="header-actions">
<div class="llm-selector">
<label for="llmSelect">LLM 선택:</label>
<select id="llmSelect">
<!-- μ˜΅μ…˜μ€ JavaScriptμ—μ„œ λ™μ μœΌλ‘œ λ‘œλ“œλ©λ‹ˆλ‹€ -->
</select>
</div>
<div class="user-info">
<span>μ‚¬μš©μž: {% if session.username %}{{ session.username }}{% else %}μ†λ‹˜{% endif %}</span>
<a href="{{ url_for('logout') }}" class="logout-button">
<i class="fas fa-sign-out-alt"></i> λ‘œκ·Έμ•„μ›ƒ
</a>
</div>
</div>
<div class="tabs">
<button id="chatTab" class="tab active">λŒ€ν™”</button>
<button id="docsTab" class="tab">λ¬Έμ„œκ΄€λ¦¬</button>
<button id="deviceTab" class="tab">μž₯μΉ˜μ œμ–΄</button>
</div>
</header>
<main>
<!-- λŒ€ν™” νƒ­ -->
<section id="chatSection" class="tab-content active">
<div class="chat-container">
<div class="chat-messages" id="chatMessages">
<div class="message system">
<div class="message-content">
<p>μ•ˆλ…•ν•˜μ„Έμš”! μ§€μ‹λ² μ΄μŠ€μ— λŒ€ν•΄ κΆκΈˆν•œ 점을 λ¬Όμ–΄λ³΄μ„Έμš”. μŒμ„±μœΌλ‘œ μ§ˆλ¬Έν•˜μ‹œλ €λ©΄ 마이크 λ²„νŠΌμ„ λˆ„λ₯΄μ„Έμš”.</p>
</div>
</div>
</div>
<div class="chat-input-container">
<textarea id="userInput" placeholder="λ©”μ‹œμ§€λ₯Ό μž…λ ₯ν•˜μ„Έμš”..." rows="1"></textarea>
<button id="micButton" class="mic-button">
<i class="fas fa-microphone"></i>
</button>
<button id="sendButton" class="send-button">
<i class="fas fa-paper-plane"></i>
</button>
</div>
<div id="recordingStatus" class="recording-status hidden">
<div class="recording-indicator">
<div class="recording-pulse"></div>
</div>
<span>λ…ΉμŒ 쀑...</span>
<button id="stopRecordingButton" class="stop-recording-button">
<i class="fas fa-stop"></i>
</button>
</div>
</div>
</section>
<!-- λ¬Έμ„œκ΄€λ¦¬ νƒ­ -->
<section id="docsSection" class="tab-content">
<div class="docs-container">
<div class="upload-section">
<h2>λ¬Έμ„œ μ—…λ‘œλ“œ</h2>
<p>μ§€μ‹λ² μ΄μŠ€μ— μΆ”κ°€ν•  λ¬Έμ„œλ₯Ό μ—…λ‘œλ“œν•˜μ„Έμš”. (지원 ν˜•μ‹: .txt, .md, .csv)</p>
<form id="uploadForm" enctype="multipart/form-data">
<div class="file-upload">
<input type="file" id="documentFile" name="document" accept=".txt,.md,.csv">
<label for="documentFile">파일 선택</label>
<span id="fileName">μ„ νƒλœ 파일 μ—†μŒ</span>
</div>
<button type="submit" id="uploadButton" class="upload-button">
<i class="fas fa-upload"></i> μ—…λ‘œλ“œ
</button>
</form>
<div id="uploadStatus" class="upload-status hidden"></div>
</div>
<div class="docs-list-section">
<h2>λ¬Έμ„œ λͺ©λ‘</h2>
<button id="refreshDocsButton" class="refresh-button">
<i class="fas fa-sync-alt"></i> μƒˆλ‘œκ³ μΉ¨
</button>
<div class="docs-list-container">
<table id="docsList" class="docs-list">
<thead>
<tr>
<th>파일λͺ…</th>
<th>청크 수</th>
<th>μœ ν˜•</th>
</tr>
</thead>
<tbody>
<!-- λ¬Έμ„œ λͺ©λ‘μ΄ 여기에 λ™μ μœΌλ‘œ μΆ”κ°€λ©λ‹ˆλ‹€ -->
</tbody>
</table>
<div id="docsLoading" class="loading-indicator">
<div class="spinner"></div>
<p>λ¬Έμ„œ λ‘œλ”© 쀑...</p>
</div>
<div id="noDocsMessage" class="no-docs-message hidden">
<p>μ§€μ‹λ² μ΄μŠ€μ— λ“±λ‘λœ λ¬Έμ„œκ°€ μ—†μŠ΅λ‹ˆλ‹€. λ¬Έμ„œλ₯Ό μ—…λ‘œλ“œν•΄ μ£Όμ„Έμš”.</p>
</div>
</div>
</div>
</div>
</section>
<!-- μž₯치 μ œμ–΄ νƒ­ -->
<section id="deviceSection" class="tab-content">
<div class="device-connection">
<h3>1. μž₯치 μ„œλ²„ μ—°κ²°</h3>
<div class="device-connection-form">
<input type="text" id="deviceServerUrlInput" placeholder="LocalPCAgent Ngrok URL μž…λ ₯ (https://xxxx-xx-xxx-xxx.ngrok-free.app ν˜•μ‹)">
<button id="connectDeviceServerBtn">μ—°κ²°</button>
</div>
<div id="deviceConnectionStatus" class="connection-status disconnected">μ—°κ²° μƒνƒœ: μ—°κ²°λ˜μ§€ μ•ŠμŒ</div>
</div>
<div id="deviceBasicFunctions" class="device-functions">
<h3>2. κΈ°λ³Έ κΈ°λŠ₯</h3>
<div class="function-buttons">
<button id="checkDeviceStatusBtn">μž₯치 μƒνƒœ 확인</button>
</div>
<textarea id="deviceStatusResult" class="device-status-result" readonly></textarea>
</div>
<div id="deviceProgramControl" class="program-control">
<h3>3. ν”„λ‘œκ·Έλž¨ μ‹€ν–‰</h3>
<div class="function-buttons">
<button id="getProgramsBtn">ν”„λ‘œκ·Έλž¨ λͺ©λ‘ μƒˆλ‘œκ³ μΉ¨</button>
</div>
<div id="programsList" class="program-list-container">
<div class="no-programs-message">ν”„λ‘œκ·Έλž¨ λͺ©λ‘μ΄ 여기에 ν‘œμ‹œλ©λ‹ˆλ‹€.</div>
</div>
<div class="program-select-container">
<select id="programSelectDropdown">
<option value="">-- λͺ©λ‘ μƒˆλ‘œκ³ μΉ¨ ν›„ 선택 --</option>
</select>
</div>
<button id="executeProgramBtn" class="execute-btn" disabled>μ„ νƒν•œ ν”„λ‘œκ·Έλž¨ μ‹€ν–‰</button>
<div id="executeResult" class="execute-result"></div>
</div>
</section>
</main>
<footer>
<p>Β© 2025 RAG 검색 챗봇 | OpenAI/DeepSeek LLM & VITO STT ν™œμš©</p>
<div class="current-llm">
<span>Current LLM: </span>
<span id="currentLLMInfo">-</span>
</div>
</footer>
</div>
<script src="{{ url_for('static', filename='js/app-core.js') }}"></script>
<script src="{{ url_for('static', filename='js/app-device.js') }}"></script>
<script src="{{ url_for('static', filename='js/app.js') }}"></script>
</body>
</html>