CoolFace
Apppublic

lexlepty/functioncall

sourceHugging Facemitupdated 2y agoView on Hugging Face
0likes
index.html520 linesDownload Raw Back to root
1<!DOCTYPE html>2<html lang="zh-CN">3<head>4    <meta charset="UTF-8">5    <meta name="viewport" content="width=device-width, initial-scale=1.0">6    <title>AI 对话系统</title>7    <link href="https://cdnjs.cloudflare.com/ajax/libs/tailwindcss/2.2.19/tailwind.min.css" rel="stylesheet">8    <link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.4/css/all.min.css" rel="stylesheet">9    <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.7.0/styles/atom-one-dark.min.css">10   <style>11    :root {12        --primary-color: #3498db;13        --secondary-color: #2ecc71;14        --background-color: #f5f5f5;15        --chat-background: #ffffff;16        --text-color: #333333;17        --chat-bubble-user: #e8f5fe;18        --chat-bubble-ai: #f0f0f0;19        --sidebar-background: #ffffff;20        --input-background: #ffffff;21        --send-button-color: #4CAF50;22        --console-background: #f8f9fa;23        --console-text: #495057;24        --fold-background: #f8f9fa;25        --fold-text: #495057;26        --border-color: transparent;27        --search-result-background: #f8f9fa;28    }29    .dark-mode {30        --primary-color: #3498db;31        --secondary-color: #2ecc71;32        --background-color: #1e2124;33        --chat-background: #36393f;34        --text-color: #dcddde;35        --chat-bubble-user: #4e5d94;36        --chat-bubble-ai: #40444b;37        --sidebar-background: #2f3136;38        --input-background: #40444b;39        --send-button-color: #7289da;40        --console-background: #2f3136;41        --console-text: #dcddde;42        --fold-background: #2f3136;43        --fold-text: #dcddde;44        --border-color: #ffffff;45        --search-result-background: #2f3136;46    }47    body {48        background-color: var(--background-color);49        color: var(--text-color);50        transition: all 0.3s ease;51    }52    .chat-container {53        background-color: var(--chat-background);54        transition: all 0.3s ease;55        border-radius: 8px;56        box-shadow: 0 2px 10px rgba(0,0,0,0.1);57    }58    .chat-bubble {59        max-width: 80%;60        padding: 12px;61        border-radius: 12px;62        margin-bottom: 12px;63        box-shadow: 0 1px 2px rgba(0,0,0,0.1);64        transition: all 0.3s ease;65    }66    .user-bubble {67        background-color: var(--chat-bubble-user);68        color: var(--text-color);69        margin-left: auto;70        border-bottom-right-radius: 4px;71    }72    .ai-bubble {73        background-color: var(--chat-bubble-ai);74        color: var(--text-color);75        margin-right: auto;76        border-bottom-left-radius: 4px;77    }78    .dark-mode .chat-bubble {79        border: 1px solid var(--border-color);80    }81    .status-indicator {82        width: 12px;83        height: 12px;84        border-radius: 50%;85        display: inline-block;86        margin-right: 8px;87        transition: all 0.3s ease;88    }89    .status-active {90        background-color: var(--secondary-color);91    }92    .status-inactive {93        background-color: #95a5a6;94    }95    .sidebar {96        background-color: var(--sidebar-background);97        transition: all 0.3s ease;98        border-radius: 8px;99        box-shadow: 0 2px 10px rgba(0,0,0,0.1);100    }101    #user-input {102        background-color: var(--input-background);103        color: var(--text-color);104        transition: all 0.3s ease;105        border-radius: 8px;106        padding-right: 40px;107    }108    #send-btn {109        position: absolute;110        right: 10px;111        top: 50%;112        transform: translateY(-50%);113        background: none;114        border: none;115        color: var(--send-button-color);116        font-size: 20px;117        cursor: pointer;118        transition: all 0.3s ease;119    }120    #send-btn:hover {121        opacity: 0.8;122    }123    .console {124        background-color: var(--console-background);125        color: var(--console-text);126        transition: all 0.3s ease;127        border-radius: 8px;128        box-shadow: 0 2px 10px rgba(0,0,0,0.1);129    }130    #status-log {131        background-color: var(--console-background);132        color: var(--console-text);133        transition: all 0.3s ease;134        border: 1px solid var(--border-color);135    }136    /* 滚动条样式 */137    ::-webkit-scrollbar {138        width: 8px;139    }140    ::-webkit-scrollbar-track {141        background: var(--chat-background);142    }143    ::-webkit-scrollbar-thumb {144        background: var(--primary-color);145        border-radius: 4px;146    }147    ::-webkit-scrollbar-thumb:hover {148        background: var(--secondary-color);149    }150    /* 代码块样式 */151    .hljs {152        background: var(--fold-background) !important;153        color: var(--fold-text) !important;154    }155     .search-results {156        background-color: var(--search-result-background);157        color: var(--text-color);158        border: 1px solid var(--border-color);159    }160    .search-results summary {161        cursor: pointer;162        font-weight: bold;163    }164    .search-results ul {165        list-style-type: none;166        padding-left: 0;167    }168    .search-results li {169        background-color: var(--chat-bubble-ai);170        border: 1px solid var(--border-color);171    }172    /* 控制台消息条目样式 */173    #status-log div {174        background-color: var(--console-background);175        color: var(--console-text);176        border: 1px solid var(--border-color);177    }178    @media (max-width: 768px) {179        .sidebar {180            position: fixed;181            right: -300px;182            top: 0;183            bottom: 0;184            width: 300px;185            z-index: 1000;186            transition: right 0.3s ease-in-out;187        }188        .sidebar.open {189            right: 0;190        }191        .sidebar-overlay {192            display: none;193            position: fixed;194            top: 0;195            left: 0;196            right: 0;197            bottom: 0;198            background-color: rgba(0, 0, 0, 0.5);199            z-index: 999;200        }201        .sidebar-overlay.show {202            display: block;203        }204    }205</style>206<body class="h-screen flex flex-col md:flex-row p-4">207    <!-- 主聊天窗口 -->208    <div class="chat-container flex-grow md:w-3/4 p-4 flex flex-col h-screen mr-4">209        <div id="chat-window" class="flex-grow overflow-y-auto mb-4 p-4 rounded-lg shadow-inner"></div>210        <div class="relative">211            <input id="user-input" type="text" class="w-full border p-2" placeholder="输入您的问题...">212            <button id="send-btn" class="text-2xl">213                <i class="fas fa-paper-plane"></i>214            </button>215        </div>216    </div>217 218    <!-- 右侧控制台 -->219    <div class="sidebar console md:w-1/4 p-4 h-screen overflow-y-auto">220        <h2 class="text-xl font-bold mb-4">运行状态</h2>221        <div class="mb-4">222            <div class="model-status">223                <span class="status-indicator" id="main-model-status"></span>224                <span class="model-name">主模型</span>225                <span class="model-action" id="main-model-action"></span>226            </div>227            <div class="model-status">228                <span class="status-indicator" id="sub-model-1-status"></span>229                <span class="model-name">次级模型1</span>230                <span class="model-action" id="sub-model-1-action"></span>231            </div>232            <div class="model-status">233                <span class="status-indicator" id="sub-model-2-status"></span>234                <span class="model-name">次级模型2</span>235                <span class="model-action" id="sub-model-2-action"></span>236            </div>237            <div class="model-status">238                <span class="status-indicator" id="arbitration-model-status"></span>239                <span class="model-name">裁决模型</span>240                <span class="model-action" id="arbitration-model-action"></span>241            </div>242            <div class="model-status">243                <span class="status-indicator" id="search-status"></span>244                <span class="model-name">搜索</span>245                <span class="model-action" id="search-action"></span>246            </div>247            <div class="model-status">248                <span class="status-indicator" id="email-status"></span>249                <span class="model-name">邮件发送</span>250                <span class="model-action" id="email-action"></span>251            </div>252        </div>253        <div id="status-log" class="p-2 rounded h-64 overflow-y-auto mb-4"></div>254        <button id="settings-btn" class="w-full bg-gray-300 dark:bg-gray-600 text-gray-800 dark:text-white px-4 py-2 rounded hover:bg-gray-400 dark:hover:bg-gray-500 transition duration-200">255            <i class="fas fa-cog"></i> 设置256        </button>257    </div>258 259    <!-- 移动端侧边栏切换按钮 -->260    <button id="sidebar-toggle" class="md:hidden fixed top-4 right-4 bg-gray-300 dark:bg-gray-600 text-gray-800 dark:text-white p-2 rounded-full shadow-lg hover:bg-gray-400 dark:hover:bg-gray-500 transition duration-200 z-50">261        <i class="fas fa-bars"></i>262    </button>263 264    <!-- 移动端侧边栏遮罩 -->265    <div id="sidebar-overlay" class="sidebar-overlay"></div>266 267    <!-- 暗色模式切换按钮 -->268    <button id="dark-mode-toggle" class="fixed top-4 left-4 bg-gray-300 dark:bg-gray-600 text-gray-800 dark:text-white p-2 rounded-full shadow-lg hover:bg-gray-400 dark:hover:bg-gray-500 transition duration-200">269        <i class="fas fa-moon"></i>270    </button>271 272    <!-- 设置对话框 -->273    <div id="settings-modal" class="fixed inset-0 bg-gray-600 bg-opacity-50 hidden flex items-center justify-center z-50">274        <div class="bg-white dark:bg-gray-800 p-6 rounded-lg shadow-xl">275            <h2 class="text-xl font-bold mb-4">设置</h2>276            <div class="mb-4">277                <label for="max-history" class="block mb-2">保留对话轮数:</label>278                <input type="number" id="max-history" class="border rounded p-2 w-full dark:bg-gray-700 dark:text-white" min="1" max="50" value="10">279            </div>280            <div class="flex justify-end">281                <button id="save-settings" class="bg-blue-500 text-white px-4 py-2 rounded mr-2 hover:bg-blue-600 transition duration-200">保存</button>282                <button id="cancel-settings" class="bg-gray-300 dark:bg-gray-600 text-gray-800 dark:text-white px-4 py-2 rounded hover:bg-gray-400 dark:hover:bg-gray-500 transition duration-200">取消</button>283            </div>284        </div>285    </div>286 287    <script src="https://cdnjs.cloudflare.com/ajax/libs/marked/4.0.2/marked.min.js"></script>288    <script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.7.0/highlight.min.js"></script>289    <script>290        const chatWindow = document.getElementById('chat-window');291        const userInput = document.getElementById('user-input');292        const sendBtn = document.getElementById('send-btn');293        const statusLog = document.getElementById('status-log');294        const settingsBtn = document.getElementById('settings-btn');295        const settingsModal = document.getElementById('settings-modal');296        const saveSettingsBtn = document.getElementById('save-settings');297        const cancelSettingsBtn = document.getElementById('cancel-settings');298        const maxHistoryInput = document.getElementById('max-history');299        const darkModeToggle = document.getElementById('dark-mode-toggle');300        const sidebarToggle = document.getElementById('sidebar-toggle');301        const sidebar = document.querySelector('.sidebar');302        const sidebarOverlay = document.getElementById('sidebar-overlay');303        let conversationHistory = [];304        let maxHistory = 10;305        function addMessage(content, isUser) {306            const bubble = document.createElement('div');307            bubble.className = `chat-bubble ${isUser ? 'user-bubble' : 'ai-bubble'}`;308            bubble.innerHTML = isUser ? content : marked.parse(content);309            chatWindow.appendChild(bubble);310            chatWindow.scrollTop = chatWindow.scrollHeight;311            312            bubble.querySelectorAll('pre code').forEach((block) => {313                hljs.highlightBlock(block);314            });315            316            conversationHistory.push({317                role: isUser ? 'user' : 'assistant',318                content: content319            });320            321            if (conversationHistory.length > maxHistory * 2) {322                conversationHistory = conversationHistory.slice(-maxHistory * 2);323            }324        }325        function updateStatus(main, sub1, sub2, arbitration, search, email) {326            updateModelStatus('main-model', main);327            updateModelStatus('sub-model-1', sub1);328            updateModelStatus('sub-model-2', sub2);329            updateModelStatus('arbitration-model', arbitration);330            updateModelStatus('search', search);331            updateModelStatus('email', email);332        }333        function updateModelStatus(modelId, isActive, action = '') {334            const statusElement = document.getElementById(`${modelId}-status`);335            const actionElement = document.getElementById(`${modelId}-action`);336            statusElement.className = `status-indicator ${isActive ? 'status-active' : 'status-inactive'}`;337            actionElement.textContent = action;338        }339        function addStatusLog(message) {340            const logEntry = document.createElement('div');341            logEntry.className = 'mb-2 p-2 bg-white dark:bg-gray-700 rounded shadow';342            logEntry.textContent = message;343            statusLog.appendChild(logEntry);344            statusLog.scrollTop = statusLog.scrollHeight;345        }346        function displaySearchResults(results, type) {347            const searchResults = document.createElement('details');348            searchResults.className = 'search-results mb-2 p-2 rounded';349            const summary = document.createElement('summary');350            summary.textContent = type === 'papers' ? '论文搜索结果' : '搜索结果';351            searchResults.appendChild(summary);352            const resultsList = document.createElement('ul');353            resultsList.className = 'mt-2';354            results.forEach(result => {355                const li = document.createElement('li');356                li.className = 'mb-2 p-2 rounded';357                if (type === 'papers') {358                    li.innerHTML = `359                        <strong>标题:</strong> ${result.标题}<br>360                        <strong>作者:</strong> ${result.作者}<br>361                        <strong>DOI:</strong> ${result.DOI}<br>362                        <strong>ISBN:</strong> ${result.ISBN}<br>363                        <strong>摘要:</strong> ${result.摘要}364                    `;365                } else {366                    li.innerHTML = `<strong>${result.title}</strong><br>${result.body}`;367                }368                resultsList.appendChild(li);369            });370            searchResults.appendChild(resultsList);371            chatWindow.appendChild(searchResults);372        }373        function updateStatusFromLog(log) {374            if (log.includes("主模型:")) {375                updateModelStatus('main-model', true, log.split("主模型:")[1]);376            } else if (log.includes("次级模型1:")) {377                updateModelStatus('sub-model-1', true, log.split("次级模型1:")[1]);378            } else if (log.includes("次级模型2:")) {379                updateModelStatus('sub-model-2', true, log.split("次级模型2:")[1]);380            } else if (log.includes("裁决模型:")) {381                updateModelStatus('arbitration-model', true, log.split("裁决模型:")[1]);382            } else if (log.includes("搜索:")) {383                updateModelStatus('search', true, log.split("搜索:")[1]);384            } else if (log.includes("邮件:")) {385                updateModelStatus('email', true, log.split("邮件:")[1]);386            }387        }388        async function sendMessage() {389            const question = userInput.value.trim();390            if (!question) return;391            addMessage(question, true);392            userInput.value = '';393            updateStatus(false, false, false, false);394            statusLog.innerHTML = '';395            addStatusLog('开始处理问题...');396            try {397                const response = await fetch('/chat', {398                    method: 'POST',399                    headers: {400                        'Content-Type': 'application/json'401                    },402                    body: JSON.stringify({ 403                        question,404                        history: conversationHistory.slice(-maxHistory * 2 + 1)405                    })406                });407                const data = await response.json();408                data.status_log.forEach(log => addStatusLog(log));409                updateStatus(410                    data.status_log.includes("主模型:回答生成完成"),411                    data.status_log.some(log => log.includes("次级模型")),412                    data.search_used,413                    data.email_sent414                );415                if (data.search_results) {416                    if (Array.isArray(data.search_results) && data.search_results.length > 0 && 'DOI' in data.search_results[0]) {417                        displaySearchResults(data.search_results, 'papers');418                    } else {419                        displaySearchResults(data.search_results, 'web');420                    }421                }422                addMessage(data.response, false);423                addStatusLog('回答完成');424            } catch (error) {425                console.error('Error:', error);426                addMessage('发生错误,请稍后再试。', false);427                updateStatus(false, false, false, false);428                addStatusLog('发生错误');429            }430        }431        sendBtn.addEventListener('click', sendMessage);432        433        userInput.addEventListener('keypress', (e) => {434            if (e.key === 'Enter') {435                sendMessage();436            }437        });438        settingsBtn.addEventListener('click', () => {439            settingsModal.classList.remove('hidden');440            maxHistoryInput.value = maxHistory;441        });442        saveSettingsBtn.addEventListener('click', async () => {443            const newMaxHistory = parseInt(maxHistoryInput.value);444            if (newMaxHistory >= 1 && newMaxHistory <= 50) {445                maxHistory = newMaxHistory;446                try {447                    const response = await fetch('/settings', {448                        method: 'POST',449                        headers: {450                            'Content-Type': 'application/json'451                        },452                        body: JSON.stringify({ max_history: maxHistory })453                    });454                    const data = await response.json();455                    if (data.status === 'success') {456                        alert('设置已保存');457                    } else {458                        alert('保存设置时出错');459                    }460                } catch (error) {461                    console.error('Error:', error);462                    alert('保存设置时出错');463                }464                settingsModal.classList.add('hidden');465            } else {466                alert('请输入1到50之间的数字');467            }468        });469        cancelSettingsBtn.addEventListener('click', () => {470            settingsModal.classList.add('hidden');471        });472        darkModeToggle.addEventListener('click', () => {473            document.body.classList.toggle('dark-mode');474            updateDarkModeIcon();475        });476        function updateDarkModeIcon() {477            const icon = darkModeToggle.querySelector('i');478            if (document.body.classList.contains('dark-mode')) {479                icon.classList.remove('fa-moon');480                icon.classList.add('fa-sun');481            } else {482                icon.classList.remove('fa-sun');483                icon.classList.add('fa-moon');484            }485        }486        function initDarkMode() {487            if (window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches) {488                document.body.classList.add('dark-mode');489            }490            updateDarkModeIcon();491        }492        window.matchMedia('(prefers-color-scheme: dark)').addEventListener('change', e => {493            if (e.matches) {494                document.body.classList.add('dark-mode');495            } else {496                document.body.classList.remove('dark-mode');497            }498            updateDarkModeIcon();499        });500        sidebarToggle.addEventListener('click', () => {501            sidebar.classList.toggle('open');502            sidebarOverlay.classList.toggle('show');503        });504        sidebarOverlay.addEventListener('click', () => {505            sidebar.classList.remove('open');506            sidebarOverlay.classList.remove('show');507        });508        window.addEventListener('load', () => {509            initDarkMode();510        });511        window.addEventListener('resize', () => {512            if (window.innerWidth >= 768) {513                sidebar.classList.remove('open');514                sidebarOverlay.classList.remove('show');515            }516        });517        // 可以在这里添加任何其他需要的 JavaScript 功能518    </script>519</body>520</html>