AI 编程比较

DeepSeek R1

  • OpenAI 发布推理模型 O1 后,刻意隐藏了推理链,而 DeepSeek 通过纯粹的强化学习技术复现了 OpenAI o1 的能力;
  • DeepSeek 在非常有限的算力资源支持下,通过强大的算法创新,突破了算力瓶颈,对于 AI 发展具有里程碑意义;
  • 目前 DeepSeek 已经火出圈,所以忍不住想试一下 DeepSeek R1 的编程能力。

任务

  • CloudFlare workers 上部署一个简单的 DeepSeek R1 聊天程序;
  • 聊天模型是 CloudFlare 提供的 Workers AI 中的 @cf/deepseek-ai/deepseek-r1-distill-qwen-32b,这个不是671b大小的完全体 DeepSeek R1,只是一个采用千问模型蒸馏的32b大小的版本;
  • 也不是从头开始编写,是在之前已经部署的一个版本上进行修改,修改内容是增加 Latex 格式数学公式渲染的支持;
  • 由于是一个比较简单的任务,想看看哪个模型能够一次成功。

模型

  • 编程工具还是采用免费版的 Cursor
  • 由于是免费版,不能使用满血版的 O1 模型;
  • 对比的三个模型是:DeepSeek R1Claude 3.5 SonnetchatGPT O3 mini

结果

  • Claude 3.5 Sonnet 不但没有完成任务,还把程序改得不能工作了;
  • DeepSeek R1 虽然也没能完成任务,但程序还是能够工作的,并且用户输入的公式能够渲染,只是AI输出的内容没能渲染成功;
  • chatGPT O3 mini 成功完成任务;
  • 不得不说, OpenAI 还是标杆。

聊天程序网址

DeepSeek R1 聊天

源代码

  • 下面是用 Cursor 生成的代码,我不懂 JS,完全看不懂细节,只能根据变量名猜测功能🤣,所以优化是谈不上的,能工作就行😎
// 生成唯一ID的辅助函数
function generateId() {
  return Date.now().toString(36) + Math.random().toString(36).substr(2);
}

// 添加本地存储类
class LocalStorage {
  constructor() {
    this.store = new Map();
  }

  async put(key, value) {
    this.store.set(key, value);
  }

  async get(key) {
    return this.store.get(key);
  }

  async delete(key) {
    this.store.delete(key);
  }

  async list() {
    return {
      keys: Array.from(this.store.keys()).map(name => ({ name }))
    };
  }
}

// 修改主入口,添加本地存储
export default {
  async fetch(request, env) {
    // 如果环境中没有 CHAT_HISTORY,使用本地存储
    if (!env.CHAT_HISTORY) {
      env.CHAT_HISTORY = new LocalStorage();
    }
    
    try {
      if (request.method === 'GET') {
        return await handleGet(request, env);
      }
      if (request.method === 'POST') {
        return await handlePost(request, env);
      }
      return new Response('不支持的请求方法', { status: 405 });
    } catch (error) {
      console.error('请求处理错误:', error);
      return Response.json({ error: error.message }, { status: 500 });
    }
  }
};

// GET请求处理
async function handleGet(request, env) {
  const url = new URL(request.url);
  const path = url.pathname;

  // 未登录用户会被重定向到登录页
  if (path !== '/login' && !await checkAuth(request)) {
    return Response.redirect(`${url.origin}/login`, 302);
  }

  // 登录页面路由
  if (path === '/login') {
    return new Response(loginHtml, {
      headers: { 'content-type': 'text/html;charset=UTF-8' },
    });
  }

  switch (path) {
    case '/conversations':
      return await getConversations(env);
    case '/history':
      return await getHistory(url.searchParams.get('id'), env);
    default:
      return new Response(html, {
        headers: { 'content-type': 'text/html;charset=UTF-8' },
      });
  }
}

// POST请求处理
async function handlePost(request, env) {
  const { message, conversationId, action, title, password, history } = await request.json();
  
  // 处理登录请求
  if (action === 'login') {
    if (!env.APP_PASSWORD) {
      console.error('未设置 APP_PASSWORD 环境变量');
      return Response.json({ error: '系统配置错误' }, { status: 500 });
    }
    
    if (password === env.APP_PASSWORD) {
      return new Response(null, {
        status: 200,
        headers: {
          'Set-Cookie': 'auth_token=valid_token; Path=/; HttpOnly; SameSite=Strict',
        }
      });
    }
    return Response.json({ error: '密码错误' }, { status: 401 });
  }
  
  // 验证其他请求的访问权限
  if (!await checkAuth(request)) {
    return Response.json({ error: '未登录' }, { status: 401 });
  }

  switch (action) {
    case 'updateTitle':
      return await updateTitle(conversationId, title, env);
    case 'new':
      return Response.json({ conversationId: generateId() });
    case 'delete':
      return await deleteConversation(conversationId, env);
    case 'import':
      // 处理导入的对话历史
      if (!conversationId || !history) {
        return Response.json({ error: '缺少必要参数' }, { status: 400 });
      }
      await env.CHAT_HISTORY.put(conversationId, JSON.stringify(history));
      return Response.json({ success: true });
    default:
      return await handleChat(message, conversationId, env);
  }
}

// 对话相关的处理函数
async function getConversations(env) {
  const list = await env.CHAT_HISTORY.list();
  const conversations = [];
  
  for (const key of list.keys) {
    const data = await env.CHAT_HISTORY.get(key.name);
    const history = JSON.parse(data);
    conversations.push({
      id: key.name,
      title: history.title || '未命名对话',
      lastMessage: history.messages?.[history.messages.length - 1]?.content || '',
      timestamp: history.timestamp || Date.now()
    });
  }
  
  conversations.sort((a, b) => b.timestamp - a.timestamp);
  return Response.json(conversations);
}

async function getHistory(conversationId, env) {
  if (!conversationId) return Response.json([]);
  
  const savedHistory = await env.CHAT_HISTORY.get(conversationId);
  return Response.json(savedHistory ? JSON.parse(savedHistory) : []);
}

async function updateTitle(conversationId, title, env) {
  if (!conversationId || !title) {
    return Response.json({ error: '缺少必要参数' }, { status: 400 });
  }

  const savedHistory = await env.CHAT_HISTORY.get(conversationId);
  if (!savedHistory) {
    return Response.json({ error: '对话不存在' }, { status: 404 });
  }

  const history = JSON.parse(savedHistory);
  history.title = title;
  history.timestamp = Date.now();
  await env.CHAT_HISTORY.put(conversationId, JSON.stringify(history));
  return Response.json({ success: true });
}

async function deleteConversation(conversationId, env) {
  if (!conversationId) {
    return Response.json({ error: '缺少对话ID' }, { status: 400 });
  }

  await env.CHAT_HISTORY.delete(conversationId);
  return Response.json({ success: true });
}

// 处理聊天消息
async function handleChat(message, conversationId, env) {
  if (!message || !conversationId) {
    return Response.json({ error: '缺少必要参数' }, { status: 400 });
  }

  const chat = {
    messages: [
      { role: 'system', content: '你是一位博学的专家,用中文给出简洁而有用的答案,并采用latex格式输出数学公式。' },
      { role: 'user', content: message }
    ],
    stream: true,
    max_tokens: 4096,
    temperature: 0.5
  };

  try {
    return await processChat(message, conversationId, chat, env);
  } catch (error) {
    console.error('处理聊天消息错误:', error);
    return Response.json({ error: error.message }, { status: 500 });
  }
}

// 添加处理聊天消息的函数
async function processChat(message, conversationId, chat, env) {
  // 获取历史记录
  const savedHistory = await env.CHAT_HISTORY.get(conversationId) || '{"messages":[], "title":"未命名对话"}';
  const history = JSON.parse(savedHistory);
  if (!history.messages) history.messages = [];
  
  // 保存用户消息
  history.messages.push({ role: 'user', content: message });
  history.timestamp = Date.now();
  await env.CHAT_HISTORY.put(conversationId, JSON.stringify(history));

  // 获取 AI 响应
  const stream = await env.AI.run('@cf/deepseek-ai/deepseek-r1-distill-qwen-32b', chat);
  
  // 处理流式响应
  const transformStream = new TransformStream({
    async transform(chunk, controller) {
      const text = new TextDecoder().decode(chunk);
      controller.enqueue(chunk);
      
      const lines = text.split('\n').filter(line => line.trim() !== '');
      for (const line of lines) {
        if (line.startsWith('data: ')) {
          const jsonStr = line.slice(6);
          if (jsonStr.trim() === '[DONE]') continue;
          
          try {
            const data = JSON.parse(jsonStr);
            if (data.response) {
              // 累积 AI 响应
              if (!history.messages[history.messages.length - 1] || 
                  history.messages[history.messages.length - 1].role !== 'assistant') {
                history.messages.push({ role: 'assistant', content: data.response });
              } else {
                history.messages[history.messages.length - 1].content += data.response;
              }
              // 定期保存到KV
              await env.CHAT_HISTORY.put(conversationId, JSON.stringify(history));
            }
          } catch (e) {
            console.error('解析响应JSON失败:', e);
          }
        }
      }
    },
    async flush() {
      // 确保最后一次保存
      await env.CHAT_HISTORY.put(conversationId, JSON.stringify(history));
    }
  });

  // 返回转换后的流
  return new Response(stream.pipeThrough(transformStream), {
    headers: {
      'Content-Type': 'text/event-stream',
      'Cache-Control': 'no-cache',
      'Connection': 'keep-alive',
    },
  });
}

// HTML模板
const html = `<!DOCTYPE html>
<html lang="zh">
  <head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>DeepSeek R1 32B</title>
    <script src="https://cdn.jsdelivr.net/npm/marked/marked.min.js"><\/script>
    <link href="https://cdn.jsdelivr.net/npm/toastify-js/src/toastify.min.css" rel="stylesheet">
    <script src="https://cdn.jsdelivr.net/npm/toastify-js"><\/script>
    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/katex@0.16.4/dist/katex.min.css">
    <script defer src="https://cdn.jsdelivr.net/npm/katex@0.16.4/dist/katex.min.js"></script>
    <script defer src="https://cdn.jsdelivr.net/npm/katex@0.16.4/dist/contrib/auto-render.min.js"></script>
    <style>
        body {
            margin: 0;
            padding: 0;
            font-family: Arial, sans-serif;
            height: 100vh;
        }
        #app-container {
            display: flex;
            height: 100vh;
            width: 100%;
        }
        #sidebar {
            width: 280px;
            border-right: 1px solid #ccc;
            padding: 20px;
            background-color: #f8f9fa;
            height: 100%;
            overflow-y: auto;
            box-sizing: border-box;
        }
        #main-content {
            flex: 1;
            padding: 20px;
            display: flex;
            flex-direction: column;
            height: 100%;
            box-sizing: border-box;
        }
        #chat-container {
            flex: 1;
            border: 1px solid #ccc;
            border-radius: 5px;
            overflow-y: auto;
            padding: 20px;
            margin-bottom: 20px;
            background-color: white;
        }
        .message {
            margin: 10px 0;
            padding: 10px;
            border-radius: 5px;
        }
        .message pre {
            background-color: #f0f0f0;
            padding: 10px;
            border-radius: 4px;
            overflow-x: auto;
        }
        .message code {
            font-family: monospace;
            background-color: #f0f0f0;
            padding: 2px 4px;
            border-radius: 3px;
        }
        .user-message {
            background-color: #e3f2fd;
            margin-left: 20%;
        }
        .ai-message {
            background-color: #f5f5f5;
            margin-right: 20%;
        }
        #input-container {
            display: flex;
            gap: 10px;
            padding: 10px 0;
        }
        #message-input {
            flex: 1;
            padding: 10px;
            border: 1px solid #ccc;
            border-radius: 5px;
        }
        button {
            padding: 10px 20px;
            background-color: #0070f3;
            color: white;
            border: none;
            border-radius: 5px;
            cursor: pointer;
        }
        button:hover {
            background-color: #0051a2;
        }
        .loading {
            position: relative;
            opacity: 0.7;
            pointer-events: none;
        }
        .loading::after {
            content: '';
            position: absolute;
            top: 50%;
            left: 50%;
            width: 20px;
            height: 20px;
            margin: -10px 0 0 -10px;
            border: 2px solid #ccc;
            border-top-color: #333;
            border-radius: 50%;
            animation: spin 1s linear infinite;
        }
        @keyframes spin {
            to { transform: rotate(360deg); }
        }
        .search-container {
            padding: 10px 0;
            margin-bottom: 10px;
        }
        #search-input {
            width: 100%;
            padding: 8px;
            border: 1px solid #ccc;
            border-radius: 4px;
            box-sizing: border-box;
        }
        .conversation-item {
            padding: 10px;
            margin: 5px 0;
            border-radius: 5px;
            cursor: pointer;
            transition: background-color 0.2s;
            position: relative;
        }
        .conversation-item:hover {
            background-color: #f0f0f0;
        }
        .conversation-item.active {
            background-color: #e3f2fd;
        }
        .conversation-title {
            font-weight: bold;
            margin-bottom: 5px;
            padding-right: 25px;
        }
        .conversation-preview {
            font-size: 0.9em;
            color: #666;
            white-space: nowrap;
            overflow: hidden;
            text-overflow: ellipsis;
        }
        .delete-button {
            position: absolute;
            right: 5px;
            top: 5px;
            padding: 2px 6px;
            background: #ff4444;
            color: white;
            border: none;
            border-radius: 3px;
            cursor: pointer;
            opacity: 0;
            transition: opacity 0.2s;
        }
        .conversation-item:hover .delete-button {
            opacity: 1;
        }
        #new-chat-button {
            width: 100%;
            margin-bottom: 10px;
            background-color: #4CAF50;
        }
        #new-chat-button:hover {
            background-color: #45a049;
        }
        .edit-icon {
            margin-left: 5px;
            opacity: 0;
            cursor: pointer;
            font-size: 14px;
            color: #666;
            transition: opacity 0.2s;
        }
        
        .conversation-item:hover .edit-icon {
            opacity: 1;
        }
        
        .edit-title-input {
            width: calc(100% - 30px);
            padding: 4px 8px;
            border: 1px solid #0070f3;
            border-radius: 4px;
            font-size: inherit;
            font-weight: bold;
            outline: none;
        }
        
        .edit-title-input:focus {
            box-shadow: 0 0 0 2px rgba(0,112,243,0.2);
        }
        .import-export-buttons {
            display: flex;
            gap: 10px;
            margin-bottom: 10px;
        }
        .import-export-buttons button {
            flex: 1;
            padding: 8px;
            background-color: #4CAF50;
            color: white;
            border: none;
            border-radius: 4px;
            cursor: pointer;
        }
        .import-export-buttons button:hover {
            background-color: #45a049;
        }
    </style>
  </head>
  <body>
    <div id="app-container">
      <div id="sidebar">
        <button id="new-chat-button">新建对话</button>
        <div class="import-export-buttons">
          <button id="export-button" onclick="exportHistory()">导出对话</button>
          <input type="file" id="import-file" accept=".json" style="display: none" onchange="importHistory(event)">
          <button onclick="document.getElementById('import-file').click()">导入对话</button>
        </div>
        <div class="search-container">
          <input type="text" id="search-input" placeholder="搜索对话...">
        </div>
        <div id="conversations-list"></div>
      </div>
      <div id="main-content">
        <h1>DeepSeek R1 32B</h1>
        <div id="chat-container"></div>
        <div id="input-container">
          <input type="text" id="message-input" placeholder="输入您的消息...">
          <button id="send-button">发送</button>
        </div>
      </div>
    </div>
    <script>
      // 工具函数
      function showToast(message, type = 'info') {
        Toastify({
          text: message,
          duration: 3000,
          gravity: "top",
          position: "right",
          style: {
            background: type === 'error' ? "#ff4444" : "#4CAF50"
          }
        }).showToast();
      }

      function debounce(func, wait) {
        let timeout;
        return function(...args) {
          clearTimeout(timeout);
          timeout = setTimeout(() => func(...args), wait);
        };
      }

      // 初始化变量
      let currentConversationId = new URLSearchParams(window.location.search).get('id');
      const chatContainer = document.getElementById('chat-container');
      const messageInput = document.getElementById('message-input');
      const sendButton = document.getElementById('send-button');
      const searchInput = document.getElementById('search-input');

      // 消息处理函数
      function addMessage(text, type) {
        const messageDiv = document.createElement('div');
        messageDiv.className = 'message ' + (type === 'user' ? 'user-message' : 'ai-message');

        if (type === 'ai') {
          // 渲染 Markdown 内容
          messageDiv.innerHTML = marked.parse(text);
        } else {
          messageDiv.textContent = text;
        }

        // 使用KaTeX自动渲染数学公式
        if (typeof renderMathInElement === 'function') {
          renderMathInElement(messageDiv, {
            delimiters: [
              {left: "$$", right: "$$", display: true},
              {left: "$", right: "$", display: false},
              {left: "\\(", right: "\\)", display: false},
              {left: "\\[", right: "\\]", display: true}
            ]
          });
        }

        chatContainer.appendChild(messageDiv);
        chatContainer.scrollTop = chatContainer.scrollHeight;
      }

      // 对话列表管理
      async function loadConversations() {
        try {
          const response = await fetch('/conversations');
          const conversations = await response.json();
          const listElement = document.getElementById('conversations-list');
          listElement.innerHTML = '';
          
          conversations.forEach(conv => {
            const item = document.createElement('div');
            item.className = 'conversation-item' + (conv.id === currentConversationId ? ' active' : '');
            item.dataset.id = conv.id;
            
            item.innerHTML = '<button class="delete-button" onclick="deleteConversation(\\'' + conv.id + '\\', event)">×</button>' +
                '<div class="conversation-title" ondblclick="editTitle(\\'' + conv.id + '\\', this)">' +
                (conv.title || '未命名对话') +
                '<span class="edit-icon">✎</span></div>' +
                '<div class="conversation-preview">' + (conv.lastMessage || '') + '</div>';
            
            item.onclick = (e) => {
              if (!e.target.classList.contains('delete-button') && 
                  !e.target.classList.contains('edit-icon')) {
                loadConversation(conv.id);
              }
            };
            listElement.appendChild(item);
          });
        } catch (error) {
          console.error('加载对话列表失败:', error);
          showToast('加载对话列表失败', 'error');
        }
      }

      async function loadConversation(id) {
        if (id === currentConversationId) return;
        
        try {
          currentConversationId = id;
          const newUrl = \`\${window.location.pathname}?id=\${id}\`;
          window.history.pushState({ id }, '', newUrl);
          
          const response = await fetch(\`/history?id=\${id}\`);
          const history = await response.json();
          
          chatContainer.innerHTML = '';
          
          if (history.messages) {
            history.messages.forEach(msg => {
              if (msg.role !== 'system') {
                addMessage(msg.content, msg.role === 'user' ? 'user' : 'ai');
              }
            });
          }
          
          // 更新活动状态
          document.querySelectorAll('.conversation-item').forEach(item => {
            item.classList.toggle('active', item.dataset.id === id);
          });
        } catch (error) {
          console.error('加载对话失败:', error);
          showToast('加载对话失败', 'error');
        }
      }

      async function createNewConversation() {
        try {
          const response = await fetch('/', {
            method: 'POST',
            headers: { 'Content-Type': 'application/json' },
            body: JSON.stringify({ action: 'new' })
          });
          
          const data = await response.json();
          await loadConversation(data.conversationId);
          loadConversations();
        } catch (error) {
          console.error('创建新对话失败:', error);
          showToast('创建新对话失败', 'error');
        }
      }

      async function deleteConversation(id, event) {
        event.stopPropagation();
        if (!confirm('确定要删除这个对话吗?')) return;
        
        try {
          await fetch('/', {
            method: 'POST',
            headers: { 'Content-Type': 'application/json' },
            body: JSON.stringify({ action: 'delete', conversationId: id })
          });
          
          if (id === currentConversationId) {
            currentConversationId = null;
            chatContainer.innerHTML = '';
            window.history.pushState({}, '', window.location.pathname);
          }
          
          loadConversations();
          showToast('对话已删除');
        } catch (error) {
          console.error('删除对话失败:', error);
          showToast('删除对话失败', 'error');
        }
      }

      // 修改标题编辑函数
      async function editTitle(convId, element) {
        // 在函数最外层定义 currentTitle,确保在所有闭包中都可访问
        const currentTitle = element.textContent.trim().replace('✎', '').trim();
        
        // 创建输入框
        const input = document.createElement('input');
        input.type = 'text';
        input.className = 'edit-title-input';
        input.value = currentTitle;
        
        // 定义保存函数
        const saveTitle = async () => {
          const newTitle = input.value.trim();
          if (!newTitle) {
            showToast('标题不能为空', 'error');
            element.innerHTML = currentTitle + '<span class="edit-icon">✎</span>';
            return;
          }
          
          if (newTitle !== currentTitle) {
            try {
              const response = await fetch('/', {
                method: 'POST',
                headers: { 'Content-Type': 'application/json' },
                body: JSON.stringify({
                  action: 'updateTitle',
                  conversationId: convId,
                  title: newTitle
                })
              });
              
              if (!response.ok) throw new Error('更新失败');
              
              showToast('标题已更新');
              loadConversations();
            } catch (error) {
              console.error('更新标题失败:', error);
              showToast('更新标题失败', 'error');
              element.innerHTML = currentTitle + '<span class="edit-icon">✎</span>';
            }
          } else {
            element.innerHTML = currentTitle + '<span class="edit-icon">✎</span>';
          }
        };
        
        // 绑定事件
        input.onblur = saveTitle;
        input.onkeydown = (e) => {  // 改用 onkeydown 更可靠
          if (e.key === 'Enter') {
            e.preventDefault();
            input.blur();
          } else if (e.key === 'Escape') {
            element.innerHTML = currentTitle + '<span class="edit-icon">✎</span>';
          }
        };
        
        // 替换内容并聚焦
        element.textContent = '';
        element.appendChild(input);
        input.focus();
        input.select();
      }

      // 搜索功能
      const handleSearch = debounce((query) => {
        const items = document.querySelectorAll('.conversation-item');
        items.forEach(item => {
          const title = item.querySelector('.conversation-title').textContent;
          const preview = item.querySelector('.conversation-preview').textContent;
          const matches = title.toLowerCase().includes(query.toLowerCase()) ||
                        preview.toLowerCase().includes(query.toLowerCase());
          item.style.display = matches ? 'block' : 'none';
        });
      }, 300);

      // 发送消息
      async function sendMessage() {
        const message = messageInput.value.trim();
        if (!message) return;
        
        if (!currentConversationId) {
          await createNewConversation();
        }
        
        messageInput.value = '';
        addMessage(message, 'user');
        
        const aiMessageDiv = document.createElement('div');
        aiMessageDiv.className = 'message ai-message';
        chatContainer.appendChild(aiMessageDiv);
        let currentResponse = '';
        
        try {
          const response = await fetch('/', {
            method: 'POST',
            headers: { 'Content-Type': 'application/json' },
            body: JSON.stringify({ message, conversationId: currentConversationId })
          });

          if (!response.ok) throw new Error(\`HTTP error! status: \${response.status}\`);

          const reader = response.body.getReader();
          const decoder = new TextDecoder();

          while (true) {
            const { value, done } = await reader.read();
            if (done) break;

            const text = decoder.decode(value);
            const lines = text.split('\\n').filter(line => line.trim() !== '');
            
            for (const line of lines) {
              if (line.startsWith('data: ')) {
                const jsonStr = line.slice(6);
                if (jsonStr.trim() === '[DONE]') break;

                try {
                  const data = JSON.parse(jsonStr);
                  if (data.response) {
                    currentResponse += data.response;
                    aiMessageDiv.innerHTML = marked.parse(currentResponse);
                    // 调用KaTeX自动渲染数学公式
                    if (typeof renderMathInElement === 'function') {
                      renderMathInElement(aiMessageDiv, {
                        delimiters: [
                          {left: "$$", right: "$$", display: true},
                          {left: "$", right: "$", display: false},
                          {left: "\\(", right: "\\)", display: false},
                          {left: "\\[", right: "\\]", display: true}
                        ]
                      });
                    }
                    chatContainer.scrollTop = chatContainer.scrollHeight;
                  }
                } catch (e) {
                  console.error('解析JSON失败:', e);
                }
              }
            }
          }
          
          // 更新对话列表
          loadConversations();
        } catch (error) {
          console.error('发送消息失败:', error);
          aiMessageDiv.innerHTML = marked.parse('Error: ' + error.message);
          showToast('发送消息失败', 'error');
        }
      }

      // 事件监听
      document.getElementById('new-chat-button').onclick = createNewConversation;
      sendButton.onclick = sendMessage;
      messageInput.onkeypress = (e) => {
        if (e.key === 'Enter' && !e.shiftKey) {
          e.preventDefault();
          sendMessage();
        }
      };
      searchInput.oninput = (e) => handleSearch(e.target.value);

      // 初始化
      loadConversations();
      if (currentConversationId) {
        loadConversation(currentConversationId);
      }

      // 导出对话历史
      async function exportHistory() {
        try {
          const response = await fetch('/conversations');
          const conversations = await response.json();
          
          // 获取每个对话的完整历史
          const fullHistory = await Promise.all(
            conversations.map(async conv => {
              const historyResponse = await fetch(\`/history?id=\${conv.id}\`);
              const history = await historyResponse.json();
              return {
                id: conv.id,
                history: history
              };
            })
          );

          // 创建下载
          const blob = new Blob([JSON.stringify(fullHistory, null, 2)], { type: 'application/json' });
          const url = URL.createObjectURL(blob);
          const a = document.createElement('a');
          a.href = url;
          a.download = \`chat-history-\${new Date().toISOString().slice(0,10)}.json\`;
          document.body.appendChild(a);
          a.click();
          document.body.removeChild(a);
          URL.revokeObjectURL(url);
          
          showToast('对话历史已导出');
        } catch (error) {
          console.error('导出失败:', error);
          showToast('导出失败', 'error');
        }
      }

      // 导入对话历史
      async function importHistory(event) {
        const file = event.target.files[0];
        if (!file) return;

        try {
          const text = await file.text();
          const histories = JSON.parse(text);

          // 逐个导入对话
          for (const item of histories) {
            try {
              // 创建新对话
              const response = await fetch('/', {
                method: 'POST',
                headers: { 'Content-Type': 'application/json' },
                body: JSON.stringify({ action: 'new' })
              });
              const { conversationId } = await response.json();

              // 保存对话历史
              await fetch('/', {
                method: 'POST',
                headers: { 'Content-Type': 'application/json' },
                body: JSON.stringify({
                  action: 'import',
                  conversationId,
                  history: item.history
                })
              });
            } catch (e) {
              console.error('导入单个对话失败:', e);
            }
          }

          // 刷新对话列表
          loadConversations();
          showToast('对话历史已导入');
        } catch (error) {
          console.error('导入失败:', error);
          showToast('导入失败', 'error');
        }
        
        // 清除文件选择,允许重复导入同一文件
        event.target.value = '';
      }
    </script>
  </body>
</html>`;

// 添加密码验证中间件
async function checkAuth(request) {
const cookie = request.headers.get('cookie') || '';
const token = cookie.split(';')
  .find(c => c.trim().startsWith('auth_token='))
  ?.split('=')[1];

return token === 'valid_token';
}

// 添加登录页面 HTML
const loginHtml = `<!DOCTYPE html>
<html lang="zh">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>登录 - DeepSeek R1</title>
  <style>
    body {
      display: flex;
      justify-content: center;
      align-items: center;
      height: 100vh;
      margin: 0;
      background-color: #f5f5f5;
      font-family: Arial, sans-serif;
    }
    .login-container {
      background: white;
      padding: 2rem;
      border-radius: 8px;
      box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
      width: 100%;
      max-width: 320px;
    }
    h1 {
      text-align: center;
      margin-bottom: 2rem;
    }
    input {
      width: 100%;
      padding: 0.5rem;
      margin-bottom: 1rem;
      border: 1px solid #ddd;
      border-radius: 4px;
      box-sizing: border-box;
    }
    button {
      width: 100%;
      padding: 0.75rem;
      background-color: #0070f3;
      color: white;
      border: none;
      border-radius: 4px;
      cursor: pointer;
    }
    button:hover {
      background-color: #0051a2;
    }
    .error {
      color: red;
      text-align: center;
      margin-top: 1rem;
      display: none;
    }
  </style>
</head>
<body>
  <div class="login-container">
    <h1>登录</h1>
    <input type="password" id="password" placeholder="请输入密码">
    <button onclick="login()">登录</button>
    <div id="error" class="error"></div>
  </div>
  <script>
    async function login() {
      const password = document.getElementById('password').value;
      const error = document.getElementById('error');
      
      try {
        const response = await fetch('/', {
          method: 'POST',
          headers: { 'Content-Type': 'application/json' },
          body: JSON.stringify({ action: 'login', password })
        });
        
        if (response.ok) {
          window.location.href = '/';
        } else {
          error.textContent = '密码错误';
          error.style.display = 'block';
        }
      } catch (e) {
        error.textContent = '登录失败,请重试';
        error.style.display = 'block';
      }
    }

    // 支持回车键登录
    document.getElementById('password').addEventListener('keypress', (e) => {
      if (e.key === 'Enter') {
        login();
      }
    });
  </script>
</body>
</html>`;