Files
hack/tools/scripts/llm-key-hunter/results/_cache/content/3290779070li-spec_chatbot.json
T

1 line
20 KiB
JSON

{"repo": "3290779070li-spec/chatbot", "files": {"static/app.js": {"edc651ae172a45aebe747f18bd69459f84b10b61": {"t": 1785746666.0305166, "n": 9527, "h": "9c736f1c574035a7", "c": "const messagesEl = document.getElementById('messages');\nconst composerEl = document.getElementById('composer');\nconst messageInputEl = document.getElementById('messageInput');\nconst statusEl = document.getElementById('status');\nconst sendButtonEl = document.getElementById('sendButton');\nconst clearChatEl = document.getElementById('clearChat');\nconst saveSettingsEl = document.getElementById('saveSettings');\nconst providerEl = document.getElementById('provider');\nconst ollamaModelRowEl = document.getElementById('ollamaModelRow');\nconst openaiModelRowEl = document.getElementById('openaiModelRow');\nconst modelSelectEl = document.getElementById('modelSelect');\nconst modelInputEl = document.getElementById('modelInput');\nconst baseUrlEl = document.getElementById('baseUrl');\nconst apiKeyEl = document.getElementById('apiKey');\nconst systemPromptEl = document.getElementById('systemPrompt');\nconst temperatureEl = document.getElementById('temperature');\nconst temperatureValueEl = document.getElementById('temperatureValue');\nconst maxTokensEl = document.getElementById('maxTokens');\nconst baseUrlHintEl = document.getElementById('baseUrlHint');\n\nconst STORAGE_KEYS = {\n settings: 'chatbot.settings.v1',\n messages: 'chatbot.messages.v1',\n};\n\nlet messages = loadMessages();\nlet isSending = false;\nlet modelCache = [];\n\nfunction loadSettings() {\n const raw = localStorage.getItem(STORAGE_KEYS.settings);\n if (!raw) {\n return;\n }\n\n try {\n const settings = JSON.parse(raw);\n providerEl.value = settings.provider || providerEl.value;\n modelInputEl.value = settings.model || modelInputEl.value;\n modelSelectEl.dataset.selected = settings.model || '';\n baseUrlEl.value = settings.baseUrl || baseUrlEl.value;\n apiKeyEl.value = settings.apiKey || '';\n systemPromptEl.value = settings.systemPrompt || systemPromptEl.value;\n temperatureEl.value = settings.temperature ?? temperatureEl.value;\n maxTokensEl.value = settings.maxTokens ?? maxTokensEl.value;\n } catch (error) {\n console.warn('Failed to load settings', error);\n }\n}\n\nfunction saveSettings() {\n const settings = {\n provider: providerEl.value,\n model: getModelValue(),\n baseUrl: baseUrlEl.value.trim(),\n apiKey: apiKeyEl.value,\n systemPrompt: systemPromptEl.value,\n temperature: Number(temperatureEl.value),\n maxTokens: Number(maxTokensEl.value),\n };\n localStorage.setItem(STORAGE_KEYS.settings, JSON.stringify(settings));\n setStatus('设置已保存');\n}\n\nfunction loadMessages() {\n const raw = localStorage.getItem(STORAGE_KEYS.messages);\n if (!raw) {\n return [];\n }\n\n try {\n const parsed = JSON.parse(raw);\n return Array.isArray(parsed) ? parsed : [];\n } catch (error) {\n console.warn('Failed to load messages', error);\n return [];\n }\n}\n\nfunction persistMessages() {\n localStorage.setItem(STORAGE_KEYS.messages, JSON.stringify(messages));\n}\n\nfunction setStatus(text) {\n statusEl.textContent = text;\n}\n\nfunction getModelValue() {\n if (providerEl.value === 'ollama') {\n return modelSelectEl.value.trim();\n }\n\n return modelInputEl.value.trim();\n}\n\nfunction setModelValue(value) {\n if (providerEl.value === 'ollama') {\n if (!value) {\n return;\n }\n\n const optionExists = Array.from(modelSelectEl.options).some((option) => option.value === value);\n if (optionExists) {\n modelSelectEl.value = value;\n } else {\n modelSelectEl.dataset.selected = value;\n }\n return;\n }\n\n modelInputEl.value = value;\n}\n\nfunction renderModelSelect(models) {\n modelSelectEl.innerHTML = '';\n\n if (!models.length) {\n const option = document.createElement('option');\n option.value = '';\n option.textContent = '暂无可用 Ollama 模型,请先拉取模型';\n modelSelectEl.appendChild(option);\n modelSelectEl.disabled = true;\n return;\n }\n\n modelSelectEl.disabled = false;\n\n for (const model of models) {\n const option = document.createElement('option');\n option.value = model.name;\n option.textContent = model.name;\n modelSelectEl.appendChild(option);\n }\n\n const selected = modelSelectEl.dataset.selected;\n const preferred = models.some((model) => model.name === selected) ? selected : models[0].name;\n modelSelectEl.value = preferred;\n modelSelectEl.dataset.selected = preferred;\n}\n\nfunction updateModelUI() {\n const isOllama = providerEl.value === 'ollama';\n ollamaModelRowEl.classList.toggle('hidden', !isOllama);\n openaiModelRowEl.classList.toggle('hidden', isOllama);\n if (isOllama) {\n if (!baseUrlEl.value.trim() || baseUrlEl.value === 'https://api.deepseek.com/chat/completions') {\n baseUrlEl.value = 'http://localhost:11434';\n }\n baseUrlHintEl.textContent = 'Ollama:填写本地服务地址,例如 http://localhost:11434';\n } else {\n if (!baseUrlEl.value.trim() || baseUrlEl.value === 'http://localhost:11434') {\n baseUrlEl.value = 'https://api.deepseek.com/chat/completions';\n }\n baseUrlHintEl.textContent = 'OpenAI-compatible:填写完整接口地址,例如 https://api.deepseek.com/chat/completions';\n }\n apiKeyEl.value = isOllama ? '' : apiKeyEl.value;\n}\n\nasync function loadOllamaModels() {\n if (providerEl.value !== 'ollama') {\n return;\n }\n\n setStatus('正在获取 Ollama 模型列表...');\n\n try {\n const response = await fetch(`/api/ollama/models?base_url=${encodeURIComponent(baseUrlEl.value.trim() || 'http://localhost:11434')}`);\n const data = await response.json();\n\n if (!response.ok) {\n throw new Error(data.detail || '获取模型列表失败');\n }\n\n modelCache = data.models || [];\n renderModelSelect(modelCache);\n setStatus(`已加载 ${modelCache.length} 个 Ollama 模型`);\n } catch (error) {\n modelCache = [];\n renderModelSelect(modelCache);\n setStatus(`模型列表获取失败:${error.message}`);\n }\n}\n\nfunction renderMessages() {\n messagesEl.innerHTML = '';\n\n if (!messages.length) {\n const empty = document.createElement('div');\n empty.className = 'message meta';\n empty.textContent = '从左侧配置模型后,开始一段新的对话。';\n messagesEl.appendChild(empty);\n return;\n }\n\n for (const item of messages) {\n const messageEl = document.createElement('div');\n messageEl.className = `message ${item.role}${item.error ? ' error' : ''}`;\n messageEl.textContent = item.content;\n messagesEl.appendChild(messageEl);\n }\n\n messagesEl.scrollTop = messagesEl.scrollHeight;\n}\n\nfunction getConversationMessages() {\n return messages\n .filter((item) => item.role === 'user' || item.role === 'assistant')\n .map((item) => ({ role: item.role, content: item.content }));\n}\n\nfunction buildRequestPayload() {\n const systemPrompt = systemPromptEl.value.trim();\n const conversation = getConversationMessages();\n\n return {\n provider: providerEl.value,\n model: getModelValue(),\n base_url: baseUrlEl.value.trim(),\n api_key: apiKeyEl.value,\n system_prompt: systemPrompt,\n temperature: Number(temperatureEl.value),\n max_tokens: Number(maxTokensEl.value),\n messages: conversation,\n };\n}\n\nfunction appendMessage(role, content, extra = {}) {\n messages.push({ role, content, ...extra });\n persistMessages();\n renderMessages();\n}\n\nasync function sendMessage(text) {\n if (isSending) {\n return;\n }\n\n const trimmed = text.trim();\n if (!trimmed) {\n return;\n }\n\n appendMessage('user', trimmed);\n const payload = buildRequestPayload();\n messageInputEl.value = '';\n isSending = true;\n sendButtonEl.disabled = true;\n setStatus('模型生成中...');\n\n const loadingIndex = messages.push({ role: 'assistant', content: '思考中...' }) - 1;\n renderMessages();\n\n try {\n const response = await fetch('/api/chat', {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify(payload),\n });\n\n const data = await response.json();\n if (!response.ok) {\n throw new Error(data.detail || '请求失败');\n }\n\n messages[loadingIndex] = { role: 'assistant', content: data.reply };\n persistMessages();\n renderMessages();\n setStatus(`已回复 · ${data.provider} / ${data.model}`);\n } catch (error) {\n messages[loadingIndex] = { role: 'assistant', content: `请求失败:${error.message}`, error: true };\n persistMessages();\n renderMessages();\n setStatus('请求失败');\n } finally {\n isSending = false;\n sendButtonEl.disabled = false;\n messageInputEl.focus();\n }\n}\n\ncomposerEl.addEventListener('submit', async (event) => {\n event.preventDefault();\n await sendMessage(messageInputEl.value);\n});\n\nmessageInputEl.addEventListener('keydown', (event) => {\n if (event.key === 'Enter' && !event.shiftKey) {\n event.preventDefault();\n composerEl.requestSubmit();\n }\n});\n\nclearChatEl.addEventListener('click', () => {\n messages = [];\n persistMessages();\n renderMessages();\n setStatus('会话已清空');\n});\n\nsaveSettingsEl.addEventListener('click', saveSettings);\n\ntemperatureEl.addEventListener('input', () => {\n temperatureValueEl.textContent = Number(temperatureEl.value).toFixed(1);\n});\n\nproviderEl.addEventListener('change', () => {\n updateModelUI();\n if (providerEl.value === 'ollama') {\n baseUrlEl.value = 'http://localhost:11434';\n apiKeyEl.value = '';\n loadOllamaModels();\n }\n});\n\nmodelSelectEl.addEventListener('change', () => {\n modelSelectEl.dataset.selected = modelSelectEl.value;\n});\n\nbaseUrlEl.addEventListener('change', () => {\n if (providerEl.value === 'ollama') {\n loadOllamaModels();\n }\n});\n\nloadSettings();\ntemperatureValueEl.textContent = Number(temperatureEl.value).toFixed(1);\nupdateModelUI();\nif (providerEl.value === 'ollama') {\n loadOllamaModels();\n} else {\n setModelValue(modelInputEl.value.trim());\n}\nrenderMessages();\nmessageInputEl.focus();\n"}, "40926e448dfbab154abdb2f0af437e919d111746": {"t": 1785766698.0093246, "n": 9527, "h": "9c736f1c574035a7", "c": "const messagesEl = document.getElementById('messages');\nconst composerEl = document.getElementById('composer');\nconst messageInputEl = document.getElementById('messageInput');\nconst statusEl = document.getElementById('status');\nconst sendButtonEl = document.getElementById('sendButton');\nconst clearChatEl = document.getElementById('clearChat');\nconst saveSettingsEl = document.getElementById('saveSettings');\nconst providerEl = document.getElementById('provider');\nconst ollamaModelRowEl = document.getElementById('ollamaModelRow');\nconst openaiModelRowEl = document.getElementById('openaiModelRow');\nconst modelSelectEl = document.getElementById('modelSelect');\nconst modelInputEl = document.getElementById('modelInput');\nconst baseUrlEl = document.getElementById('baseUrl');\nconst apiKeyEl = document.getElementById('apiKey');\nconst systemPromptEl = document.getElementById('systemPrompt');\nconst temperatureEl = document.getElementById('temperature');\nconst temperatureValueEl = document.getElementById('temperatureValue');\nconst maxTokensEl = document.getElementById('maxTokens');\nconst baseUrlHintEl = document.getElementById('baseUrlHint');\n\nconst STORAGE_KEYS = {\n settings: 'chatbot.settings.v1',\n messages: 'chatbot.messages.v1',\n};\n\nlet messages = loadMessages();\nlet isSending = false;\nlet modelCache = [];\n\nfunction loadSettings() {\n const raw = localStorage.getItem(STORAGE_KEYS.settings);\n if (!raw) {\n return;\n }\n\n try {\n const settings = JSON.parse(raw);\n providerEl.value = settings.provider || providerEl.value;\n modelInputEl.value = settings.model || modelInputEl.value;\n modelSelectEl.dataset.selected = settings.model || '';\n baseUrlEl.value = settings.baseUrl || baseUrlEl.value;\n apiKeyEl.value = settings.apiKey || '';\n systemPromptEl.value = settings.systemPrompt || systemPromptEl.value;\n temperatureEl.value = settings.temperature ?? temperatureEl.value;\n maxTokensEl.value = settings.maxTokens ?? maxTokensEl.value;\n } catch (error) {\n console.warn('Failed to load settings', error);\n }\n}\n\nfunction saveSettings() {\n const settings = {\n provider: providerEl.value,\n model: getModelValue(),\n baseUrl: baseUrlEl.value.trim(),\n apiKey: apiKeyEl.value,\n systemPrompt: systemPromptEl.value,\n temperature: Number(temperatureEl.value),\n maxTokens: Number(maxTokensEl.value),\n };\n localStorage.setItem(STORAGE_KEYS.settings, JSON.stringify(settings));\n setStatus('设置已保存');\n}\n\nfunction loadMessages() {\n const raw = localStorage.getItem(STORAGE_KEYS.messages);\n if (!raw) {\n return [];\n }\n\n try {\n const parsed = JSON.parse(raw);\n return Array.isArray(parsed) ? parsed : [];\n } catch (error) {\n console.warn('Failed to load messages', error);\n return [];\n }\n}\n\nfunction persistMessages() {\n localStorage.setItem(STORAGE_KEYS.messages, JSON.stringify(messages));\n}\n\nfunction setStatus(text) {\n statusEl.textContent = text;\n}\n\nfunction getModelValue() {\n if (providerEl.value === 'ollama') {\n return modelSelectEl.value.trim();\n }\n\n return modelInputEl.value.trim();\n}\n\nfunction setModelValue(value) {\n if (providerEl.value === 'ollama') {\n if (!value) {\n return;\n }\n\n const optionExists = Array.from(modelSelectEl.options).some((option) => option.value === value);\n if (optionExists) {\n modelSelectEl.value = value;\n } else {\n modelSelectEl.dataset.selected = value;\n }\n return;\n }\n\n modelInputEl.value = value;\n}\n\nfunction renderModelSelect(models) {\n modelSelectEl.innerHTML = '';\n\n if (!models.length) {\n const option = document.createElement('option');\n option.value = '';\n option.textContent = '暂无可用 Ollama 模型,请先拉取模型';\n modelSelectEl.appendChild(option);\n modelSelectEl.disabled = true;\n return;\n }\n\n modelSelectEl.disabled = false;\n\n for (const model of models) {\n const option = document.createElement('option');\n option.value = model.name;\n option.textContent = model.name;\n modelSelectEl.appendChild(option);\n }\n\n const selected = modelSelectEl.dataset.selected;\n const preferred = models.some((model) => model.name === selected) ? selected : models[0].name;\n modelSelectEl.value = preferred;\n modelSelectEl.dataset.selected = preferred;\n}\n\nfunction updateModelUI() {\n const isOllama = providerEl.value === 'ollama';\n ollamaModelRowEl.classList.toggle('hidden', !isOllama);\n openaiModelRowEl.classList.toggle('hidden', isOllama);\n if (isOllama) {\n if (!baseUrlEl.value.trim() || baseUrlEl.value === 'https://api.deepseek.com/chat/completions') {\n baseUrlEl.value = 'http://localhost:11434';\n }\n baseUrlHintEl.textContent = 'Ollama:填写本地服务地址,例如 http://localhost:11434';\n } else {\n if (!baseUrlEl.value.trim() || baseUrlEl.value === 'http://localhost:11434') {\n baseUrlEl.value = 'https://api.deepseek.com/chat/completions';\n }\n baseUrlHintEl.textContent = 'OpenAI-compatible:填写完整接口地址,例如 https://api.deepseek.com/chat/completions';\n }\n apiKeyEl.value = isOllama ? '' : apiKeyEl.value;\n}\n\nasync function loadOllamaModels() {\n if (providerEl.value !== 'ollama') {\n return;\n }\n\n setStatus('正在获取 Ollama 模型列表...');\n\n try {\n const response = await fetch(`/api/ollama/models?base_url=${encodeURIComponent(baseUrlEl.value.trim() || 'http://localhost:11434')}`);\n const data = await response.json();\n\n if (!response.ok) {\n throw new Error(data.detail || '获取模型列表失败');\n }\n\n modelCache = data.models || [];\n renderModelSelect(modelCache);\n setStatus(`已加载 ${modelCache.length} 个 Ollama 模型`);\n } catch (error) {\n modelCache = [];\n renderModelSelect(modelCache);\n setStatus(`模型列表获取失败:${error.message}`);\n }\n}\n\nfunction renderMessages() {\n messagesEl.innerHTML = '';\n\n if (!messages.length) {\n const empty = document.createElement('div');\n empty.className = 'message meta';\n empty.textContent = '从左侧配置模型后,开始一段新的对话。';\n messagesEl.appendChild(empty);\n return;\n }\n\n for (const item of messages) {\n const messageEl = document.createElement('div');\n messageEl.className = `message ${item.role}${item.error ? ' error' : ''}`;\n messageEl.textContent = item.content;\n messagesEl.appendChild(messageEl);\n }\n\n messagesEl.scrollTop = messagesEl.scrollHeight;\n}\n\nfunction getConversationMessages() {\n return messages\n .filter((item) => item.role === 'user' || item.role === 'assistant')\n .map((item) => ({ role: item.role, content: item.content }));\n}\n\nfunction buildRequestPayload() {\n const systemPrompt = systemPromptEl.value.trim();\n const conversation = getConversationMessages();\n\n return {\n provider: providerEl.value,\n model: getModelValue(),\n base_url: baseUrlEl.value.trim(),\n api_key: apiKeyEl.value,\n system_prompt: systemPrompt,\n temperature: Number(temperatureEl.value),\n max_tokens: Number(maxTokensEl.value),\n messages: conversation,\n };\n}\n\nfunction appendMessage(role, content, extra = {}) {\n messages.push({ role, content, ...extra });\n persistMessages();\n renderMessages();\n}\n\nasync function sendMessage(text) {\n if (isSending) {\n return;\n }\n\n const trimmed = text.trim();\n if (!trimmed) {\n return;\n }\n\n appendMessage('user', trimmed);\n const payload = buildRequestPayload();\n messageInputEl.value = '';\n isSending = true;\n sendButtonEl.disabled = true;\n setStatus('模型生成中...');\n\n const loadingIndex = messages.push({ role: 'assistant', content: '思考中...' }) - 1;\n renderMessages();\n\n try {\n const response = await fetch('/api/chat', {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify(payload),\n });\n\n const data = await response.json();\n if (!response.ok) {\n throw new Error(data.detail || '请求失败');\n }\n\n messages[loadingIndex] = { role: 'assistant', content: data.reply };\n persistMessages();\n renderMessages();\n setStatus(`已回复 · ${data.provider} / ${data.model}`);\n } catch (error) {\n messages[loadingIndex] = { role: 'assistant', content: `请求失败:${error.message}`, error: true };\n persistMessages();\n renderMessages();\n setStatus('请求失败');\n } finally {\n isSending = false;\n sendButtonEl.disabled = false;\n messageInputEl.focus();\n }\n}\n\ncomposerEl.addEventListener('submit', async (event) => {\n event.preventDefault();\n await sendMessage(messageInputEl.value);\n});\n\nmessageInputEl.addEventListener('keydown', (event) => {\n if (event.key === 'Enter' && !event.shiftKey) {\n event.preventDefault();\n composerEl.requestSubmit();\n }\n});\n\nclearChatEl.addEventListener('click', () => {\n messages = [];\n persistMessages();\n renderMessages();\n setStatus('会话已清空');\n});\n\nsaveSettingsEl.addEventListener('click', saveSettings);\n\ntemperatureEl.addEventListener('input', () => {\n temperatureValueEl.textContent = Number(temperatureEl.value).toFixed(1);\n});\n\nproviderEl.addEventListener('change', () => {\n updateModelUI();\n if (providerEl.value === 'ollama') {\n baseUrlEl.value = 'http://localhost:11434';\n apiKeyEl.value = '';\n loadOllamaModels();\n }\n});\n\nmodelSelectEl.addEventListener('change', () => {\n modelSelectEl.dataset.selected = modelSelectEl.value;\n});\n\nbaseUrlEl.addEventListener('change', () => {\n if (providerEl.value === 'ollama') {\n loadOllamaModels();\n }\n});\n\nloadSettings();\ntemperatureValueEl.textContent = Number(temperatureEl.value).toFixed(1);\nupdateModelUI();\nif (providerEl.value === 'ollama') {\n loadOllamaModels();\n} else {\n setModelValue(modelInputEl.value.trim());\n}\nrenderMessages();\nmessageInputEl.focus();\n"}}}}