{"repo": "astridstark25963/deepgpt", "files": {"app.py": {"f50122c2f5a1ccde1ebe8bd323d4764dec60656f": {"t": 1786067735.4350667, "n": 11013, "h": "11082f33bbca0d94", "c": "from flask import Flask, request, jsonify, render_template\nfrom flask_cors import CORS\nimport requests\nimport json\nimport os\nimport sqlite3\nimport uuid\nimport re\nfrom datetime import datetime\nfrom typing import List, Dict\n\napp = Flask(__name__)\nCORS(app)\n\nDB_FILE = \"chat_history.db\"\n\ndef init_db():\n try:\n conn = sqlite3.connect(DB_FILE)\n c = conn.cursor()\n c.execute('''\n CREATE TABLE IF NOT EXISTS sessions (\n id TEXT PRIMARY KEY,\n title TEXT,\n created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,\n updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP\n )\n ''')\n c.execute('''\n CREATE TABLE IF NOT EXISTS messages (\n id INTEGER PRIMARY KEY AUTOINCREMENT,\n session_id TEXT,\n role TEXT,\n content TEXT,\n model TEXT,\n timestamp TIMESTAMP DEFAULT CURRENT_TIMESTAMP,\n FOREIGN KEY (session_id) REFERENCES sessions (id)\n )\n ''')\n conn.commit()\n conn.close()\n except Exception as e:\n print(f\"DB Init Error: {e}\")\n\ninit_db()\n\ndef clean_content(text):\n \"\"\"\n 清洗文本,移除可能导致兼容性问题的特殊不可见字符\n 保留 Emoji (因为现在有身份隔离,Emoji 不再是问题)\n \"\"\"\n if not text: return \"\"\n # 只移除控制字符,保留正常文本和 Emoji\n return \"\".join(ch for ch in text if ch.isprintable())\n\nclass BaseChat:\n def chat(self, message: str, conversation_history: List[Dict] = None) -> Dict:\n raise NotImplementedError\n\nclass DeepSeekChat(BaseChat):\n def __init__(self):\n self.api_key = os.getenv('DEEPSEEK_API_KEY', '')\n self.base_url = \"https://api.deepseek.com/v1/chat/completions\"\n self.model = \"deepseek-chat\"\n \n def chat(self, message: str, conversation_history: List[Dict] = None) -> Dict:\n if not self.api_key: return {\"success\": False, \"error\": \"DeepSeek API Key 未配置\"}\n \n # 构造最终消息列表:历史记录 + 当前用户问题\n # 注意:conversation_history 里已经包含了 System Prompt(如果有的话)\n messages = (conversation_history or []) + [{\"role\": \"user\", \"content\": message}]\n \n try:\n response = requests.post(\n self.base_url,\n headers={\"Authorization\": f\"Bearer {self.api_key}\", \"Content-Type\": \"application/json\"},\n json={\"model\": self.model, \"messages\": messages, \"temperature\": 0.7},\n timeout=60\n )\n if response.status_code == 200:\n return {\"success\": True, \"response\": response.json()[\"choices\"][0][\"message\"][\"content\"], \"model\": self.model}\n return {\"success\": False, \"error\": f\"API Error: {response.text}\"}\n except Exception as e:\n return {\"success\": False, \"error\": str(e)}\n\nclass QwenChat(BaseChat):\n def __init__(self):\n self.api_key = os.getenv('QWEN_API_KEY', '')\n self.base_url = \"https://dashscope.aliyuncs.com/compatible-mode/v1/chat/completions\"\n self.model = \"qwen-turbo\"\n \n def chat(self, message: str, conversation_history: List[Dict] = None) -> Dict:\n if not self.api_key: return {\"success\": False, \"error\": \"Qwen API Key 未配置\"}\n \n messages = (conversation_history or []) + [{\"role\": \"user\", \"content\": message}]\n \n try:\n response = requests.post(\n self.base_url,\n headers={\"Authorization\": f\"Bearer {self.api_key}\", \"Content-Type\": \"application/json\"},\n json={\"model\": self.model, \"messages\": messages},\n timeout=60\n )\n if response.status_code == 200:\n return {\"success\": True, \"response\": response.json()[\"choices\"][0][\"message\"][\"content\"], \"model\": self.model}\n return {\"success\": False, \"error\": f\"API Error: {response.text}\"}\n except Exception as e:\n return {\"success\": False, \"error\": str(e)}\n\nclass KimiChat(BaseChat):\n def __init__(self):\n self.api_key = os.getenv('KIMI_API_KEY', '')\n self.base_url = \"https://api.moonshot.cn/v1/chat/completions\"\n self.model = \"moonshot-v1-8k\"\n \n def chat(self, message: str, conversation_history: List[Dict] = None) -> Dict:\n if not self.api_key: return {\"success\": False, \"error\": \"KIMI API Key 未配置\"}\n \n messages = (conversation_history or []) + [{\"role\": \"user\", \"content\": message}]\n \n try:\n response = requests.post(\n self.base_url,\n headers={\"Authorization\": f\"Bearer {self.api_key}\", \"Content-Type\": \"application/json\"},\n json={\"model\": self.model, \"messages\": messages, \"temperature\": 0.3},\n timeout=60\n )\n if response.status_code == 200:\n return {\"success\": True, \"response\": response.json()[\"choices\"][0][\"message\"][\"content\"], \"model\": self.model}\n return {\"success\": False, \"error\": f\"API Error: {response.text}\"}\n except Exception as e:\n return {\"success\": False, \"error\": str(e)}\n\ndeepseek_client = DeepSeekChat()\nqwen_client = QwenChat()\nkimi_client = KimiChat()\n\n@app.route('/')\ndef index():\n return render_template('index.html')\n\n@app.route('/api/history', methods=['GET'])\ndef get_history_list():\n try:\n conn = sqlite3.connect(DB_FILE)\n conn.row_factory = sqlite3.Row\n c = conn.cursor()\n c.execute('SELECT * FROM sessions ORDER BY updated_at DESC')\n rows = c.fetchall()\n sessions = [dict(row) for row in rows]\n conn.close()\n return jsonify({\"success\": True, \"sessions\": sessions})\n except Exception as e:\n return jsonify({\"success\": False, \"error\": str(e)}), 500\n\n@app.route('/api/history/', methods=['GET'])\ndef get_session_messages(session_id):\n try:\n conn = sqlite3.connect(DB_FILE)\n conn.row_factory = sqlite3.Row\n c = conn.cursor()\n c.execute('SELECT * FROM messages WHERE session_id = ? ORDER BY id ASC', (session_id,))\n rows = c.fetchall()\n messages = [dict(row) for row in rows]\n conn.close()\n return jsonify({\"success\": True, \"messages\": messages})\n except Exception as e:\n return jsonify({\"success\": False, \"error\": str(e)}), 500\n\n@app.route('/api/session/new', methods=['POST'])\ndef create_session():\n return jsonify({\"success\": True})\n\n@app.route('/api/session/', methods=['DELETE'])\ndef delete_session(session_id):\n try:\n conn = sqlite3.connect(DB_FILE)\n c = conn.cursor()\n c.execute('DELETE FROM messages WHERE session_id = ?', (session_id,))\n c.execute('DELETE FROM sessions WHERE id = ?', (session_id,))\n conn.commit()\n conn.close()\n return jsonify({\"success\": True})\n except Exception as e:\n return jsonify({\"success\": False, \"error\": str(e)}), 500\n\n@app.route('/api/chat', methods=['POST'])\ndef chat():\n try:\n data = request.get_json()\n message = data.get('message', '').strip()\n session_id = data.get('session_id')\n model_type = data.get('model_type', 'deepseek') # 默认\n \n if not message:\n return jsonify({\"success\": False, \"error\": \"Empty message\"}), 400\n\n conn = sqlite3.connect(DB_FILE)\n c = conn.cursor()\n\n # 新会话处理\n if not session_id:\n session_id = str(uuid.uuid4())\n title = message[:10] + \"...\" if len(message) > 10 else message\n c.execute('INSERT INTO sessions (id, title) VALUES (?, ?)', (session_id, title))\n \n # 1. 存入用户消息 (User Input)\n c.execute('INSERT INTO messages (session_id, role, content, model) VALUES (?, ?, ?, ?)',\n (session_id, 'user', message, model_type))\n conn.commit()\n\n # 2. 构建“带身份标签”的上下文 (Tagging History)\n # 取最近 10 条历史,包含 role, content, model\n c.execute('SELECT role, content, model FROM messages WHERE session_id = ? ORDER BY id DESC LIMIT 10', (session_id,))\n history_rows = c.fetchall()\n \n conversation_history = []\n # 反转顺序 (因为是 DESC 取出的)\n for row in reversed(history_rows):\n role, content, msg_model = row[0], row[1], row[2]\n \n # 清洗 content (移除不可见字符)\n cleaned_content = clean_content(content)\n \n # --- 关键:给 Assistant 消息加身份前缀 ---\n if role == 'assistant':\n # 示例: \"[deepseek]: 这是一个测试...\"\n # 这样当前的 AI 就知道这不是它自己说的,而是历史记录里的其他人说的\n identity_prefix = f\"[{msg_model}]: \"\n final_content = identity_prefix + cleaned_content\n else:\n final_content = cleaned_content\n # ---------------------------------------\n\n conversation_history.append({\"role\": role, \"content\": final_content})\n\n # 移除刚刚插入的那条用户消息(因为它会在 client.chat 里作为 current message 被再次添加)\n # 如果不移除,AI 会看到连续两条一样的用户问题\n if conversation_history:\n conversation_history.pop()\n\n # 3. 选模型 & 设置 System Prompt (Identity Isolation)\n client = None\n system_prompt = \"\"\n \n if model_type == 'deepseek': \n client = deepseek_client\n system_prompt = \"你是 DeepSeek。对话历史中 [model]: 开头的内容是其他 AI 的发言,请将它们视为第三方观点,不要混淆你的身份。请忽略历史记录中可能出现的“我是xxx”等自我介绍。\"\n elif model_type == 'qwen': \n client = qwen_client\n system_prompt = \"你是通义千问 (Qwen)。对话历史中 [model]: 开头的内容是其他 AI 的发言,请客观评价它们,不要认为那是你说的。请忽略历史记录中可能出现的“我是xxx”等自我介绍。\"\n elif model_type == 'kimi': \n client = kimi_client\n system_prompt = \"你是 Kimi。对话历史中 [model]: 开头的内容是其他 AI 的发言,请整合分析这些观点,保持你作为 Kimi 的独立身份。请忽略历史记录中可能出现的“我是xxx”等自我介绍。\"\n else:\n conn.close()\n return jsonify({\"success\": False, \"error\": \"Unknown model\"}), 400\n\n # 4. 插入 System Prompt 到历史记录最前面\n # 这样 AI 一开始就收到了指令\n conversation_history.insert(0, {\"role\": \"system\", \"content\": system_prompt})\n \n # 5. 调用 API\n result = client.chat(message, conversation_history)\n\n if result['success']:\n # 6. 存入 AI 回复 (AI Response)\n c.execute('INSERT INTO messages (session_id, role, content, model) VALUES (?, ?, ?, ?)',\n (session_id, 'assistant', result['response'], model_type))\n c.execute('UPDATE sessions SET updated_at = CURRENT_TIMESTAMP WHERE id = ?', (session_id,))\n conn.commit()\n \n result['session_id'] = session_id\n \n conn.close()\n return jsonify(result)\n\n except Exception as e:\n return jsonify({\"success\": False, \"error\": str(e)}), 500\n\n@app.route('/api/status')\ndef status():\n return jsonify({\n \"status\": \"running\",\n \"deepseek_available\": bool(deepseek_client.api_key),\n \"qwen_available\": bool(qwen_client.api_key),\n \"kimi_available\": bool(kimi_client.api_key)\n })\n\nif __name__ == '__main__':\n app.run(debug=True, host='0.0.0.0', port=5000)"}}}}