Python Flask项目方式接入阿里云通义AI大模型API 实现一个简单的AI聊天Web项目(流式传输+多轮对话+会话记录+代码高亮)----- noob学生

效果图

数据库记录:

流式输出就不展示了

准备工作

 前往阿里云大模型服务平台使用自己账号开通大模型服务平台百炼 

地址:大模型服务平台_通义大模型_自然语言处理_达摩院-阿里云 (aliyun.com)

1.进入自己的控制台--模型广场--通义千问--找到自己要使用的模型  我这里使用通义千问Max

一般是有免费额度可以使用的   后期可以自己买额度 很便宜 

然后到我的应用   创建好自己的应用  选择相应的模型 这时我们就哟APIkey和 自己的appid了  

2.在自己电脑安装好Redis (用于存储聊天缓存) 如果使用服务器就在服务器安装好并运行就行

3.安装好mysql数据库  推荐使用5.7版本

实施

        创建一个Python Flask项目并创建好虚拟环境解释器使用python3.8  项目结构如下图所示

key.py及其下方文件为我个人测试部署时使用  可以忽略  

app.py

运行项目的文件 也就是后端  

# -*- coding: utf-8 -*-
from flask import Flask, request, jsonify, Response, render_template, session
from flask_session import Session
from dashscope import Generation
from http import HTTPStatus
from flask_cors import CORS
import redis
import json
from database import save_conversation

app = Flask(__name__)
CORS(app)
#可以自己生成一串
app.secret_key = '3ddd8f0da764cb34850e1da48d03da24'  

# 配置 Redis
app.config['SESSION_TYPE'] = 'redis'
app.config['SESSION_REDIS'] = redis.Redis(host='localhost', port=6379)
app.config['SESSION_PERMANENT'] = False
app.config['SESSION_USE_SIGNER'] = True
Session(app)

@app.route('/ChatView')
def index():
    return render_template('index.html')

@app.route('/chat', methods=['POST'])
def chat():
    user_message = request.json.get('message', '')

    if not user_message:
        return jsonify({'error': '未提供消息'}), HTTPStatus.BAD_REQUEST

    # 从会话中加载消息或初始化一个新列表  可以自己自定义角色 修改'你是一个ai助手'内容就行
    messages = session.get('messages', [{'role': 'system', 'content': '你是一个ai助手'}])

    # 将用户的消息添加到列表中
    messages.append({'role': 'user', 'content': user_message})
    session['messages'] = messages  # 在添加用户消息后立即保存

    def generate():
        responses = Generation.call(
            "qwen-max",
            app_id='自己的应用id',
            api_key='自己的api key',
            messages=messages,
            result_format='message',
            stream=True,
            incremental_output=True
        )
        buffer = ''
        for response in responses:
            if response.status_code == HTTPStatus.OK:
                content = response.output.choices[0]['message']['content'].strip()
                print(content)
                buffer += content
                yield f"{content}"
            else:
                yield f"Error: {response.message}\n\n"
                break

    return Response(generate(), mimetype='text/event-stream')

@app.route('/update', methods=['POST'])
def update():
    try:
        data = request.json
        bot_message = data.get('bot_message', '')
        user_message = data.get('user_message', '')
        conversation_id = data.get('conversation_id', '')

        if not bot_message or not user_message or not conversation_id:
            app.logger.error('Missing bot_message, user_message, or conversation_id')
            return jsonify({'error': '未提供消息或对话ID'}), HTTPStatus.BAD_REQUEST

        messages = session.get('messages', [])
        messages.append({'role': 'assistant', 'content': bot_message})
        session['messages'] = messages

        save_conversation(conversation_id, user_message, bot_message)  # 保存对话

        return jsonify({'status': 'updated'}), HTTPStatus.OK
    except Exception as e:
        app.logger.error(f"Error in /update: {str(e)}")
        return jsonify({'error': str(e)}), HTTPStatus.INTERNAL_SERVER_ERROR

@app.route('/clear', methods=['POST'])
def clear():
    session.pop('messages', None)
    return jsonify({'status': 'cleared'}), HTTPStatus.OK

if __name__ == '__main__':
    app.run(debug=True)
    # 这里是自定义本地运行的端口号和ip地址
    # app.run(host='0.0.0.0', port=8080, debug=True)

​

database.py

指向数据库操作 保存记录的后端文件  需要修改为自己的数据库账户和密码  

# -*- coding: utf-8 -*-
import mysql.connector

def get_db_connection():
    return mysql.connector.connect(
        host="localhost",
        user="用户名",
        password="密码",
        database="数据库名"
    )

def save_conversation(conversation_id, user_message, bot_message):
    try:
        connection = get_db_connection()
        cursor = connection.cursor()

        # 检查对话是否存在
        conversation_query = "SELECT 1 FROM conversations WHERE conversation_id = %s"
        cursor.execute(conversation_query, (conversation_id,))
        conversation_exists = cursor.fetchone()
        
        if not conversation_exists:
            # 插入对话记录
            conversation_query = """
            INSERT INTO conversations (conversation_id)
            VALUES (%s)
            """
            cursor.execute(conversation_query, (conversation_id,))

        # 插入聊天记录
        chat_record_query = """
        INSERT INTO chat_records (conversation_id, user_message, bot_message)
        VALUES (%s, %s, %s)
        """
        cursor.execute(chat_record_query, (conversation_id, user_message, bot_message))

        connection.commit()
    except mysql.connector.Error as err:
        print(f"Error: {err}")
        raise
    finally:
        if connection.is_connected():
            cursor.close()
            connection.close()

数据库结构   sql语句   

SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;

-- ----------------------------
-- Table structure for chat_records
-- ----------------------------
DROP TABLE IF EXISTS `chat_records`;
CREATE TABLE `chat_records`  (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `conversation_id` varchar(36) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
  `user_message` text CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL,
  `bot_message` text CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NULL,
  PRIMARY KEY (`id`) USING BTREE,
  INDEX `conversation_id`(`conversation_id`) USING BTREE,
  CONSTRAINT `chat_records_ibfk_1` FOREIGN KEY (`conversation_id`) REFERENCES `conversations` (`conversation_id`) ON DELETE CASCADE ON UPDATE CASCADE
) ENGINE = InnoDB AUTO_INCREMENT = 1 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = Dynamic;

-- ----------------------------
-- Table structure for conversations
-- ----------------------------
DROP TABLE IF EXISTS `conversations`;
CREATE TABLE `conversations`  (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `conversation_id` varchar(36) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
  `conversation_time` timestamp NULL DEFAULT CURRENT_TIMESTAMP,
  PRIMARY KEY (`id`) USING BTREE,
  INDEX `conversation_id`(`conversation_id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 1 CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci ROW_FORMAT = Dynamic;

SET FOREIGN_KEY_CHECKS = 1;

前端页面和样式

/templates/index.html   

头像图片可以自定义  修改就行 

<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta charset="UTF-8"/>
  <meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no, maximum-scale=1.0"/>
  <title>顶顶顶顶顶</title>
  <script src="https://cdn.tailwindcss.com"></script>
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/gh/highlightjs/cdn-release@11.9.0/build/styles/github-dark.min.css"/>
  <script src="https://cdn.jsdelivr.net/gh/highlightjs/cdn-release@11.9.0/build/highlight.min.js"></script>
  <script src="https://cdn.jsdelivr.net/npm/highlightjs-line-numbers.js@2.8.0/dist/highlightjs-line-numbers.min.js"></script>
  <script src="https://cdn.jsdelivr.net/npm/clipboard@2.0.11/dist/clipboard.min.js"></script>
  <script src="https://cdn.jsdelivr.net/npm/jquery@3.7.1/dist/jquery.min.js"></script>
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.3/font/bootstrap-icons.min.css"/>
  <link rel="stylesheet" href="/static/css/style.css" />
  <link rel="icon" href="/static/images/favicon.ico" type="image/x-icon">
</head>
<body class="bg-blue-200 font-sans leading-normal tracking-normal">
  <div class="loader" id="loader">
    <div class="loader-text">Loading...</div>
    <h2>正在加载...请稍候</h2>
    <div class="loader-bar"></div>
  </div>  
  <div class="w-full h-full sm:container sm:mx-auto sm:my-8 sm:p-4 bg-white dark:bg-zinc-800 shadow-md rounded-lg sm:overflow-hidden max-w-4xl">
    <div class="flex flex-col h-[100vh] sm:h-[80vh] md:h-[85vh]">
      <div class="px-4 py-3 border-b dark:border-zinc-700">
        <div class="flex justify-between items-center">
          <h1 class="text-2xl font-semibold text-zinc-800 dark:text-white">👻Tongyi`Ai By Lwh<i class="bi bi-emoji-laughing"></i></h1>
          <button class="text-white font-bold py-2 px-3 rounded-full transition ease-in-out delay-150 bg-blue-500 hover:-translate-y-1 hover:scale-110 hover:bg-indigo-500 duration-300" onclick="clearMessages()"><i class="bi bi-chat-right-dots"></i>&nbsp;新对话</button>
        </div>
      </div>
      <div class="flex-1 p-3 overflow-y-auto flex flex-col space-y-2" id="chat-box">
        <div class="bot-message-container fade-in">
          <img src="/static/images/Lwh.jpeg" alt="Lwh" />
          <div class="message bot font-medium p-3 rounded-lg max-w-lg text-sm self-start bg-green-500 dark:bg-zinc-500 text-white">你好!有什么可以帮你的吗</div>
        </div>
      </div>
      <div class="px-3 py-2 border-t-2 dark:border-zinc-700">
        <div class="flex gap-2">
          <textarea placeholder="输入你的问题..." class="flex-1 p-2 border rounded-lg dark:bg-zinc-700 dark:text-white dark:border-zinc-600 text-md" id="user-input" rows="1"></textarea>
          <button class="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-3 rounded-full transition ease-in-out delay-150 bg-blue-500 hover:-translate-y-1 hover:scale-110 hover:bg-indigo-500 duration-300 text-sm flex items-center justify-center" id="sendButton" onclick="sendMessage()">
            <svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
              <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 10l7-7m0 0l7 7m-7-7v18"></path>
            </svg>
          </button>
        </div>
      </div>
    </div>
  </div>
  <script src="/static/js/lwhapp.js" defer></script>
</body>
</html>

/static/css/styles.css

.message {
  white-space: pre-wrap;
  word-wrap: break-word;
  max-width: 100%;
}

.bot-message-container {
  display: flex;
  align-items: flex-start;
}

.bot-message-container img {
  width: 40px;
  height: 40px;
  border-radius: 50%;
  margin-right: 10px;
  border: rgb(125, 125, 242) 3px solid;
}

pre {
  border-bottom-left-radius: 0.5rem;
  border-bottom-right-radius: 0.5rem;
}

code {
  font-family: 'Consolas', 'Courier New', monospace;
  border-bottom-left-radius: 0.5rem;
  border-bottom-right-radius: 0.5rem;
  /* 隐藏默认的滚动条样式 */
  scrollbar-width: none;
  /* Firefox */
  -ms-overflow-style: none;
  /* IE and Edge */
}

code::-webkit-scrollbar {
  display: none;
  /* Chrome, Safari, and Opera */
}

/* for block of numbers */
.hljs-ln-numbers {
  -webkit-touch-callout: none;
  -webkit-user-select: none;
  -khtml-user-select: none;
  -moz-user-select: none;
  -ms-user-select: none;
  user-select: none;

  text-align: center;
  color: #ccc;
  border-right: 2px dashed #8f0feb;
  vertical-align: top;
}

.hljs-ln td {
  padding-right: 10px;
  padding-left: 10px;
}

@keyframes fadeIn {
  from {
    opacity: 0;
    transform: translateY(10px);
  }

  to {
    opacity: 1;
    transform: translateY(0);
  }
}

.fade-in {
  animation: fadeIn 1s ease-out;
}

.copy {
  /* button */
  --button-bg: #353434;
  --button-hover-bg: #464646;
  --button-text-color: #CCCCCC;
  --button-hover-text-color: #8bb9fe;
  --button-border-radius: 10px;
  --button-diameter: 36px;
  --button-outline-width: 1px;
  --button-outline-color: rgb(141, 141, 141);
  /* tooltip */
  --tooltip-bg: #171212;
  --toolptip-border-radius: 4px;
  --tooltip-font-size:8px;
  --tooltip-transition-duration: 0.3s;
  --tootip-text-color: rgb(255, 255, 255);
  --tooltip-padding-x: 5px;
  --tooltip-padding-y: 5px;
  --tooltip-offset: 8px;
  font-weight: bold;
  font-family: 'YouYuan', sans-serif;
}

.copy {
  box-sizing: border-box;
  width: var(--button-diameter);
  height: var(--button-diameter);
  border-radius: var(--button-border-radius);
  background-color: var(--button-bg);
  color: var(--button-text-color);
  border: none;
  cursor: pointer;
  position: relative;
  outline: none;
}

.tooltip {
  position: absolute;
  opacity: 0;
  visibility: 0;
  top: 0;
  left: 50%;
  transform: translateX(-50%);
  white-space: nowrap;
  font: var(--tooltip-font-size) var(--tooltip-font-family);
  color: var(--tootip-text-color);
  background: var(--tooltip-bg);
  padding: var(--tooltip-padding-y) var(--tooltip-padding-x);
  border-radius: var(--toolptip-border-radius);
  pointer-events: none;
  transition: all var(--tooltip-transition-duration) cubic-bezier(0.68, -0.55, 0.265, 1.55);
}

.tooltip::before {
  content: attr(data-text-initial);
}

.tooltip::after {
  content: "";
  position: absolute;
  bottom: calc(var(--tooltip-padding-y) / 2 * -1);
  width: var(--tooltip-padding-y);
  height: var(--tooltip-padding-y);
  background: inherit;
  left: 50%;
  transform: translateX(-50%) rotate(45deg);
  z-index: -999;
  pointer-events: none;
}

.copy svg {
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
}

.checkmark {
  display: none;
}

/* actions */

.copy:hover .tooltip,
.copy:focus:not(:focus-visible) .tooltip {
  opacity: 1;
  visibility: visible;
  top: calc((100% + var(--tooltip-offset)) * -1);
}

.copy:focus:not(:focus-visible) .tooltip::before {
  content: attr(data-text-end);
}

.copy:focus:not(:focus-visible) .clipboard {
  display: none;
}

.copy:focus:not(:focus-visible) .checkmark {
  display: block;
}

.copy:hover,
.copy:focus {
  background-color: var(--button-hover-bg);
}

.copy:active {
  outline: var(--button-outline-width) solid var(--button-outline-color);
}

.copy:hover svg {
  color: var(--button-hover-text-color);
}

@media screen and (max-width: 600px) {
  pre {
    white-space: pre-wrap;
    /* 换行 */
    word-wrap: break-word;
    /* 防止超出屏幕宽度 */
  }

  .message.bot {
    max-width: 100%;
    /* 确保在小屏幕设备上不超出屏幕宽度 */
  }
}


/* Loader styles */
.loader {
  position: fixed;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  background: white;
  z-index: 9999;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
}

.loader-text {
  font-size: 24px;
  color: rgb(0, 0, 0);
  margin-bottom: 20px;
  align-self: center;
}

.loader-bar {
  width: 10%;
  height: 10px;
  border-radius: 5px;
  background-color: rgb(0, 0, 0);
  animation: loader-bar-animation 2s ease-in-out infinite;
}

@keyframes loader-bar-animation {
  0% {
    /* transform: translateX(-100%) rotate(270deg); */
    transform: translateX(-100%);
  }

  50% {
    /* transform: translateX(100%) rotate(-90deg); */
    transform: translateX(100%);
  }

  100% {
    /* transform: translateX(-100%) rotate(270deg); */
    transform: translateX(-100%);
  }
}

/static/js/lwhapp.js

这部分很重要!!!

可以修改每次对话可以对话几条  建议20以内

document.addEventListener("DOMContentLoaded", function() {
  
    hljs.highlightAll();
    hljs.initLineNumbersOnLoad();
  
    const chatBox = document.getElementById("chat-box");
    const userInput = document.getElementById("user-input");
    const maxMessages = 10; // 定义最大消息数
    let messageCount = 0;
  
    userInput.addEventListener("keydown", function (event) {
      if (event.keyCode === 13 && !event.shiftKey) {
        event.preventDefault();
        sendMessage();
      }
    });
  
    document.getElementById("sendButton").addEventListener("click", sendMessage);
    document.querySelector("button[onclick='clearMessages()']").addEventListener("click", clearMessages);
  
    function sendMessage() {
      if (messageCount >= maxMessages) {
        disableInput();
        return;
      }
  
      const message = userInput.value;
      if (message.trim() === "") return;
  
      addMessage("user", message, false);
      const conversationId = localStorage.getItem("conversation_id") || generateConversationId();
      saveMessage("user", message);
      userInput.value = "";
      messageCount++;
  
      if (messageCount >= maxMessages) {
        disableInput();
      }
  
      fetch("/chat", {
        method: "POST",
        headers: {
          "Content-Type": "application/json",
        },
        body: JSON.stringify({ message }),
      })
        .then((response) => {
          if (!response.ok) {
            throw new Error("网络回复未完成");
          }
          const reader = response.body.getReader();
          const decoder = new TextDecoder();
          let buffer = "";
          let botMessageDiv = addMessage("bot", "", true); // 用于显示机器人的回复
  
          function readStream() {
            reader.read().then(({ done, value }) => {
              if (done) {
                const botResponse = buffer.trim();
                saveMessage("bot", botResponse);
                // 向后端发送完整的回复以更新会话历史
                fetch("/update", {
                  method: "POST",
                  headers: {
                    "Content-Type": "application/json",
                  },
                  body: JSON.stringify({ conversation_id: conversationId, user_message: message, bot_message: botResponse }),
                }).then(response => {
                  if (!response.ok) {
                    console.error('Error updating conversation:', response.statusText);
                  }
                }).catch(error => {
                  console.error('Fetch error:', error);
                });
                return;
              }
  
              const text = decoder.decode(value, { stream: true }).trim();
              buffer += text;
              addTypingEffect(botMessageDiv, buffer);
              applyStyles(botMessageDiv);
              readStream();
            });
          }
  
          readStream();
        })
        .catch((error) => {
          console.error("Error:", error);
        });
    }
  
    function generateConversationId() {
      const conversationId = 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
        var r = Math.random() * 16 | 0,
        v = c == 'x' ? r : (r & 0x3 | 0x8);
        return v.toString(16);
      });
      localStorage.setItem("conversation_id", conversationId);
      return conversationId;
    }
  
    function addMessage(role, content, isTypingEffect = false) {
      let messageDiv;
      if (role === "bot") {
        const containerDiv = document.createElement("div");
        containerDiv.classList.add("bot-message-container", "fade-in");
  
        const avatar = document.createElement("img");
        avatar.src = "static/images/Lwh.jpeg";
        avatar.alt = "Lwh";
        containerDiv.appendChild(avatar);
  
        messageDiv = document.createElement("div");
        messageDiv.classList.add("message", role, "font-medium", "p-3", "rounded-lg", "max-w-lg", "text-sm", "self-start", "bg-green-500", "dark:bg-zinc-500", "text-white");
        containerDiv.appendChild(messageDiv);
  
        chatBox.appendChild(containerDiv);
      } else {
        messageDiv = document.createElement("div");
        messageDiv.classList.add("message", role, "p-3", "rounded-lg", "max-w-lg", "text-sm", "self-end", "bg-blue-500", "text-white");
        chatBox.appendChild(messageDiv);
      }
  
      if (isTypingEffect) {
        return messageDiv;
      }
      const codeRegex = /```([\s\S]*?)```/g;
      if (codeRegex.test(content)) {
        const parts = content.split(codeRegex);
        parts.forEach((part, index) => {
          if (index % 2 === 0) {
            const textNode = document.createTextNode(part);
            messageDiv.appendChild(textNode);
          } else {
            const pre = document.createElement("pre");
            const code = document.createElement("code");
            const lang = part.split("\n")[0].trim();
            const codeContent = part.split("\n").slice(1).join("\n");
            const codeHeader = document.createElement("div");
            codeHeader.classList.add(
              "flex",
              "justify-between",
              "items-center",
              "bg-gray-700",
              "text-white",
              "font-mono",
              "px-2",
              "py-1",
              "text-base",
              "rounded-t-lg"
            );
            codeHeader.innerHTML = `<span class="font-mono text-base">${lang}</span>
              <button class="copy" data-clipboard-text="${codeContent.replace(/"/g, "&quot;").replace(/'/g,"&#39;")}">
                  <span class="tooltip">复制代码</span>
                  <span>
                      <svg xml:space="preserve" style="enable-background:new 0 0 512 512" viewBox="0 0 6.35 6.35" y="0" x="0" height="20" width="20" xmlns="http://www.w3.org/2000/xlink" version="1.1" xmlns="http://www.w3.org/2000/xlink" class="clipboard">
                      <g>
                          <path fill="currentColor" d="M2.43.265c-.3 0-.548.236-.573.53h-.328a.74.74 0 0 0-.735.734v3.822a.74.74 0 0 0 .735.734H4.82a.74.74 0 0 0 .735-.734V1.529a.74.74 0 0 0-.735-.735h-.328a.58.58 0 0 0-.573-.53zm0 .529h1.49c.032 0 .049.017.049.049v.431c0 .032-.017.049-.049.049H2.43c-.032 0-.05-.017-.05-.049V.843c0-.032.018-.05.05-.05zm-.901.53h.328c.026.292.274.528.573.528h1.49a.58.58 0 0 0 .573-.529h.328a.2.2 0 0 1 .206.206v3.822a.2.2 0 0 1-.206.205H1.53a.2.2 0 0 1-.206-.205V1.529a.2.2 0 0 1 .206-.206z"></path>
                      </g>
                      </svg>
                      <svg xml:space="preserve" style="enable-background:new 0 0 512 512" viewBox="0 0 24 24" y="0" x="0" height="18" width="18" xmlns:xlink="http://www.w3.org/2000/xlink" version="1.1" xmlns="http://www.w3.org/2000/svg" class="checkmark">
                      <g>
                          <path data-original="#000000" fill="currentColor" d="M9.707 19.121a.997.997 0 0 1-1.414 0l-5.646-5.647a1.5 1.5 0 0 1 0-2.121l.707-.707a1.5 1.5 0 0 1 2.121 0L9 14.171l9.525-9.525a1.5 1.5 0 0 1 2.121 0l.707.707a1.5 1.5 0 0 1 0 2.121z"></path>
                      </g>
                      </svg>
                  </span>
              </button>`;
            code.textContent = codeContent;
            if (lang) {
              code.classList.add(...lang.split(/\s+/));
            }
  
            pre.appendChild(codeHeader);
            pre.appendChild(code);
            pre.classList.add("rounded-b-lg");
            messageDiv.appendChild(pre);

            new ClipboardJS(".copy");
          }
        });
      } else {
        messageDiv.appendChild(document.createTextNode(content));
      }
  
      chatBox.scrollTop = chatBox.scrollHeight;
      applyStyles(messageDiv);
      return messageDiv;
    }
  
    function addTypingEffect(messageDiv, text) {
      const codeRegex = /```([\s\S]*?)```/g;
      const parts = text.split(codeRegex);
  
      messageDiv.innerHTML = ""; 
      parts.forEach((part, index) => {
        if (index % 2 === 0) {
          const textNode = document.createTextNode(part);
          messageDiv.appendChild(textNode);
        } else {
          const pre = document.createElement("pre");
          const code = document.createElement("code");
          const lang = part.split("\n")[0].trim();
          const codeContent = part.split("\n").slice(1).join("\n");
          const codeHeader = document.createElement("div");
          codeHeader.classList.add(
            "flex",
            "justify-between",
            "items-center",
            "bg-gray-700",
            "text-white",
            "font-mono",
            "px-2",
            "py-1",
            "text-base",
            "rounded-t-lg"
          );
          codeHeader.innerHTML = `<span class="font-mono text-base">${lang}</span>
              <button class="copy" data-clipboard-text="${codeContent.replace(/"/g, "&quot;").replace(/'/g,"&#39;")}">
                  <span class="tooltip">复制代码</span>
                  <span>
                      <svg xml:space="preserve" style="enable-background:new 0 0 512 512" viewBox="0 0 6.35 6.35" y="0" x="0" height="20" width="20" xmlns="http://www.w3.org/2000/xlink" version="1.1" xmlns="http://www.w3.org/2000/xlink" class="clipboard">
                      <g>
                          <path fill="currentColor" d="M2.43.265c-.3 0-.548.236-.573.53h-.328a.74.74 0 0 0-.735.734v3.822a.74.74 0 0 0 .735.734H4.82a.74.74 0 0 0 .735-.734V1.529a.74.74 0 0 0-.735-.735h-.328a.58.58 0 0 0-.573-.53zm0 .529h1.49c.032 0 .049.017.049.049v.431c0 .032-.017.049-.049.049H2.43c-.032 0-.05-.017-.05-.049V.843c0-.032.018-.05.05-.05zm-.901.53h.328c.026.292.274.528.573.528h1.49a.58.58 0 0 0 .573-.529h.328a.2.2 0 0 1 .206.206v3.822a.2.2 0 0 1-.206.205H1.53a.2.2 0 0 1-.206-.205V1.529a.2.2 0 0 1 .206-.206z"></path>
                      </g>
                      </svg>
                      <svg xml:space="preserve" style="enable-background:new 0 0 512 512" viewBox="0 0 24 24" y="0" x="0" height="18" width="18" xmlns:xlink="http://www.w3.org/2000/xlink" version="1.1" xmlns="http://www.w3.org/2000/svg" class="checkmark">
                      <g>
                          <path data-original="#000000" fill="currentColor" d="M9.707 19.121a.997.997 0 0 1-1.414 0l-5.646-5.647a1.5 1.5 0 0 1 0-2.121l.707-.707a1.5 1.5 0 0 1 2.121 0L9 14.171l9.525-9.525a1.5 1.5 0 0 1 2.121 0l.707.707a1.5 1.5 0 0 1 0 2.121z"></path>
                      </g>
                      </svg>
                  </span>
              </button>`;
  
          code.textContent = codeContent;
          if (lang) {
            code.classList.add(...lang.split(/\s+/));
          }
  
          pre.appendChild(codeHeader);
          pre.appendChild(code);
          pre.classList.add("rounded-b-lg");
          messageDiv.appendChild(pre);
  
          new ClipboardJS(".copy");
        }
      });
  
      chatBox.scrollTop = chatBox.scrollHeight;
    }
  
    function clearMessages() {
      console.log('Clearing messages...');
      fetch("/clear", {
        method: "POST",
        headers: {
          "Content-Type": "application/json",
        },
      })
        .then(() => {
          console.log('Cleared messages from server.');
          localStorage.removeItem("conversation_id"); 
          localStorage.removeItem("messages"); 
          chatBox.innerHTML = "";
          generateConversationId(); 
          location.reload(); 
        })
        .catch((error) => {
          console.error("Error:", error);
        });
    }
  
    function disableInput() {
      userInput.disabled = true;
      userInput.placeholder = "对话上限喽!请开启新的对话";
    }
  
    function saveMessage(role, content) {
      let messages = JSON.parse(localStorage.getItem("messages")) || [];
      messages.push({ role, content });
      localStorage.setItem("messages", JSON.stringify(messages));
    }
  
    function loadMessages() {
      let messages = JSON.parse(localStorage.getItem("messages")) || [];
      messageCount = messages.filter(msg => msg.role === 'user').length;
      messages.forEach((msg) => {
        const messageDiv = addMessage(msg.role, msg.content);
        if (msg.role === "bot" && /```/.test(msg.content)) {
          applyStyles(messageDiv); 
        }
      });
      if (messageCount >= maxMessages) {
        disableInput();
      }
    }
  
    function applyStyles(container) {
      container.querySelectorAll("pre code").forEach((block) => {
        hljs.highlightElement(block);
        hljs.lineNumbersBlock(block);
      });
    }
  
    window.onload = loadMessages;

    const loader = document.getElementById('loader');

    window.addEventListener('load', function() {
      loader.style.display = 'none';
    });
  });
  

requirements.txt

需要安装的包  ,你需要在命令行中导航到该文件所在的目录,然后运行以下命令:

pip install -r requirements.txt

requirements.txt内容:

aiohttp==3.9.5
aiosignal==1.3.1
async-timeout==4.0.3
attrs==23.2.0
blinker==1.8.2
cachelib==0.13.0
certifi==2024.2.2
cffi==1.16.0
charset-normalizer==3.3.2
click==8.1.7
colorama==0.4.6
cors==1.0.1
dashscope==1.19.2
exceptiongroup==1.2.1
filelock==3.14.0
Flask==3.0.3
Flask-Cors==4.0.1
Flask-Session==0.8.0
frozenlist==1.4.1
future==1.0.0
gevent==24.2.1
greenlet==3.0.3
idna==3.7
importlib_metadata==7.1.0
iniconfig==2.0.0
itsdangerous==2.2.0
Jinja2==3.1.4
MarkupSafe==2.1.5
msgspec==0.18.6
multidict==6.0.5
mysql-connector-python==8.4.0
packaging==24.0
pluggy==1.5.0
pycparser==2.22
PySocks==1.7.1
pytest==8.2.1
redis==5.0.4
requests==2.32.2
requests-file==2.1.0
tldextract==5.1.2
tomli==2.0.1
urllib3==2.2.1
Werkzeug==3.0.3
yarl==1.9.4
zipp==3.18.2
zope.event==5.0
zope.interface==6.4.post2

确保安装完成编译完成后运行app.py使用浏览器进入相应页面就可以使用了    (本人菜鸟 也是学着做的 请见谅  大佬勿喷)

key.py  可以用来生成secret_key

import secrets
secret_key = secrets.token_hex(16)
print(secret_key)

  • 27
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
1. 登录阿里云控制台,进入ECS实例页面,选择需要部署Flask项目的实例。 2. 在实例页面中,点击左侧导航栏中的“安全组”。在安全组页面中,点击“添加安全组规则”按钮,添加一个新的安全组规则,将协议设置为“TCP”、端口设置为“5000”(Flask默认端口),并选择允许所有来源IP访问。 3. 在实例页面中,点击左侧导航栏中的“SSH密钥对”。如果你还没有创建SSH密钥对,点击“创建SSH密钥对”按钮创建一个。 4. 在Windows系统中,使用PuTTYgen生成私钥和公钥。将私钥下载到本地,将公钥复制到ECS实例的“SSH密钥对”页面中。 5. 在Windows系统中,使用PuTTY连接到ECS实例。输入实例的公网IP地址、端口号(默认为22)、用户名(默认为root),并选择刚才下载的私钥文件。 6. 在PuTTY连接成功后,输入以下命令更新系统软件包: sudo apt-get update sudo apt-get upgrade 7. 安装Python和pip: sudo apt-get install python3-pip 8. 安装Flask和其他需要的Python库: sudo pip3 install flask 9. 编写Flask项目代码,并将代码上传到ECS实例中。可以使用SFTP客户端(如FileZilla)将代码上传到ECS实例中。 10. 在ECS实例中,进入Flask代码所在的目录,运行以下命令启动Flask应用: export FLASK_APP=app.py flask run --host=0.0.0.0 11. 在浏览器中输入实例的公网IP地址和Flask应用的端口号(默认为5000),访问Flask应用。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值