flask-socket的实践

1.长连接和短连接的由来

1)TCP在真正的读写操作之前,server与client之间必须建立一个连接,

当读写操作完成后,双方不再需要这个连接时它们可以释放这个连接,

连接的建立通过三次握手,释放则需要四次握手,

所以说每个连接的建立都是需要资源消耗和时间消耗的。

2)短连接就是我们平时登陆注册,建立的连接

3)长连接主要适用于通信,比如说qq聊天

2.示例代码

server.py
import socket
import threading

BUF_SIZE = 1024
host = 'localhost'
port = 8083

def handle_client(client_socket, address):
    print("Connected by", address)
    while True:
        try:
            data = client_socket.recv(BUF_SIZE)
            if not data:
                print("Connection closed by client", address)
                break
            print(data.decode())
        except ConnectionResetError:
            print("Connection reset by client", address)
            break
    client_socket.close()

server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server.bind((host, port))
server.listen(5)  # 增加可接收连接数

print("Server is listening on port", port)

while True:
    client_socket, address = server.accept()
    client_handler = threading.Thread(target=handle_client, args=(client_socket, address))
    client_handler.start()
client.py
import socket
import time

host = 'localhost'
port = 8083

client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
client.setsockopt(socket.SOL_SOCKET, socket.SO_KEEPALIVE, 1)  # 开启心跳维护

try:
    client.connect((host, port))
    print("Connected to server")

    while True:
        client.send('hello world\r\n'.encode())
        print('send data')
        time.sleep(1)  # 可以设置更长的时间来验证长时间不发送数据的情况
except ConnectionRefusedError:
    print("Connection failed")
except Exception as e:
    print("An error occurred:", e)
finally:
    client.close()

3.flask提供长连接

flask-socket.py
from flask import Flask, render_template
from flask_socketio import SocketIO, send

app = Flask(__name__)
app.config['SECRET_KEY'] = 'secret!'
socketio = SocketIO(app)

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

@socketio.on('message')
def handle_message(message):
    print('Received message: ' + message)
    send('Message received: ' + message)

if __name__ == '__main__':
    socketio.run(app, debug=True)
html代码
<!DOCTYPE html>
<html>
  <head>
    <title>SocketIO Chat</title>
    <script src="https://cdn.socket.io/4.0.0/socket.io.min.js"></script>
    <script type="text/javascript" charset="utf-8">
      document.addEventListener('DOMContentLoaded', () => {
        const socket = io();

        socket.on('connect', () => {
          console.log('Connected to server');
        });

        socket.on('message', (msg) => {
          console.log('Message from server: ' + msg);
        });

        document.querySelector('#send').addEventListener('click', () => {
          const message = document.querySelector('#message').value;
          socket.send(message);
        });
      });
    </script>
  </head>
  <body>
    <input id="message" type="text" placeholder="Enter message">
    <button id="send">Send</button>
  </body>
</html>

4.flask工厂模式修改为socket

1)目录结构
LongChain/
│
├── app/
│   ├── __init__.py
│   ├── main/
│   │   ├── __init__.py
│   │   ├── routes.py
│   ├── socketio.py
├── templates/
│   └── index.html
├── create_app.py
├── requirements.txt
2)/app/__init__.py
from flask import Flask
from .socketio import socketio

def create_app():
    app = Flask(__name__, template_folder='../templates')
    app.config['SECRET_KEY'] = 'secret!'

    # Initialize SocketIO with the app
    socketio.init_app(app)

    # Import and register blueprints
    from .main import main as main_blueprint
    app.register_blueprint(main_blueprint)

    print(app.template_folder)  # 打印模板文件夹路径

    return app

3)app/socketio.py
from flask_socketio import SocketIO

socketio = SocketIO()

@socketio.on('message')
def handle_message(message):
    print('Received message: ' + message)
    socketio.send('Message received: ' + message)
4)app/main/__init__.py
from flask import Blueprint

main = Blueprint('main', __name__)

from . import routes
5)app/main/routes.py
from flask import render_template
from . import main

@main.route('/')
def index():
    return render_template('./index.html')
6)templates/index.html
<!DOCTYPE html>
<html>
  <head>
    <title>SocketIO Chat</title>
    <script src="https://cdn.socket.io/4.0.0/socket.io.min.js"></script>
    <script type="text/javascript" charset="utf-8">
      document.addEventListener('DOMContentLoaded', () => {
        const socket = io();

        socket.on('connect', () => {
          console.log('Connected to server');
        });

        socket.on('message', (msg) => {
          console.log('Message from server: ' + msg);
        });

        document.querySelector('#send').addEventListener('click', () => {
          const message = document.querySelector('#message').value;
          socket.send(message);
        });
      });
    </script>
  </head>
  <body>
    <input id="message" type="text" placeholder="Enter message">
    <button id="send">Send</button>
  </body>
</html>
7)create_app.py
from app import create_app
from app.socketio import socketio

app = create_app()

if __name__ == "__main__":
    socketio.run(app, debug=True, host='0.0.0.0')

5.项目代码更改

首先要在app文件夹下建一个extensions.py文件,是为了解决在run.py和init.py里面循环引入socket的问题

1)__init__.py修改
# app/__init__.py
from flask import Flask, render_template, request, g, jsonify, redirect, url_for, session
from app.user.views import user_bp
from app.blog.views import blog_bp
from app.comment.views import comment_bp
from app.drunk1.views import drunk1_bp
from app.onedrunk1.views import onedrunk1_bp
from app.extensions import socketio
import sqlite3
import time
import traceback

def create_app():
    app = Flask(__name__, static_url_path='/static')
    
    # 加载配置
    app.config.from_object('config.Config')
    socketio.init_app(app)
    
    # 注册蓝图
    app.register_blueprint(user_bp, url_prefix='/conversation')
    app.register_blueprint(blog_bp, url_prefix='/userprofile')
    app.register_blueprint(comment_bp, url_prefix='/drunk5')
    app.register_blueprint(drunk1_bp, url_prefix='/drunk1')
    app.register_blueprint(onedrunk1_bp, url_prefix='/onedrunk1')
    
    DATABASE = r'C:\Users\lzt\Desktop\work\flask\app\static\users2.db'

    # session密钥
    app.secret_key = 'Cxh12300'
    current_user = {}

    def get_db():
        db = getattr(g, '_database', None)
        if db is None:
            db = g._database = sqlite3.connect(DATABASE)
        return db

    @app.teardown_appcontext
    def close_connection(exception):
        db = getattr(g, '_database', None)
        if db is not None:
            db.close()

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

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

    @app.route('/registuser', methods=['POST'])
    def getRigistRequest():
        try:
            if request.method == 'POST':
                username = request.form.get('user')
                password = request.form.get('password')

                conn = get_db()
                cursor = conn.cursor()
                sql = "INSERT INTO user(user, password) VALUES (?, ?)"
                cursor.execute(sql, (username, password))
                
                conn.commit()
                return redirect(url_for('login'))
            else:
                return render_template('login.html')
        except Exception as e:
            traceback.print_exc()
            return '注册失败'

    @app.route('/login', methods=['POST'])
    def getLoginRequest():
        try:
            username = request.form.get('user')
            password = request.form.get('password')

            conn = get_db()
            cursor = conn.cursor()
            
            sql = "SELECT * FROM user WHERE user=? AND password=?"
            cursor.execute(sql, (username, password))
            user = cursor.fetchone()

            if user:
                name = user[0]
                user_id = user[2]
                current_user['name'] = user[0]
                current_user['user_id'] = user[2]
                session['name'] = user[0]
                session['user_id'] = user[2]

                current_time = time.time()
                expiration_time = current_time + 10
                payload = {'name': name, 'user_id': user_id, 'exp': expiration_time}
                return render_template('./user/index2qian.html', name=name, user_id=user_id)
            else:
                return '用户名或密码不正确'
        except Exception as e:
            traceback.print_exc()
            return '登录失败'

    return app
2)run.py
from app import create_app, socketio

app = create_app()

if __name__ == "__main__":
    socketio.run(app, debug=True, host='0.0.0.0')
3)app/user/views.py的修改
from flask import Blueprint, render_template,request,jsonify,session
import sqlite3
import traceback
import time
import requests
import json
from flask_socketio import emit
from app.extensions import socketio
user_bp = Blueprint('user', __name__, template_folder='templates')

@user_bp.route('/')
def index():
    return render_template('./user/index2qian.html')
def generate_user_profile(session):
    # 基本 URL
    base_url = "http://192.168.1.140:5000/userprofile/generate_user_profile"

    # 从 session 中获取 user_id 和 name
    print("session111",session)
    user_id = session['user_id']
    name = session['name']
    print("user_id",user_id)
    # 构建完整的 URL
    url = f"{base_url}?user_id={user_id}&name={name}"

    try:
        # 发送 GET 请求
        response = requests.get(url)

        # 检查响应状态码
        if response.status_code == 200:
            # 成功
            print("Profile generated successfully")
            return response.json()
        else:
            # 请求失败
            print(f"Failed to generate profile, status code: {response.status_code}")
            return None
    except Exception as e:
        print(f"An error occurred: {e}")
        return None
def get_latest_content_by_user_id(session):
    # 数据库文件名
    db_file = r'C:\Users\lzt\Desktop\work\flask\app\static\conversions2.db'
    
    # 连接到 SQLite 数据库
    conn = sqlite3.connect(db_file)
    cursor = conn.cursor()

    try:
        # SQL 查询语句,按 id 降序排列并只获取最新的一条记录
        query = '''
            SELECT content FROM profile
            WHERE user_id = ?
            ORDER BY id DESC
            LIMIT 1
        '''
        
        # 执行查询
        cursor.execute(query, (session['user_id'],))
        
        # 获取结果
        result = cursor.fetchone()
        
        if result:
            content = result[0]  # 获取 content 字段的值
            return content
        else:
            return None

    except sqlite3.Error as e:
        print(f"An error occurred: {e}")
        return None
    
    finally:
        # 关闭数据库连接
        conn.close()

@socketio.on('message')
def handle_message(data):
    user_input = data['message']
    conversation_history = data['history']

    response = ChatBot.call_api(user_input, conversation_history)
      
    # print("user_input",user_input)
    # print("conversation_history",conversation_history)
    # print("触发emit")
    # print("response",response)
    emit('response', {'response': response})

class ChatBot:
    context = None
    conversation_counter = 0

    @classmethod
    def call_api(cls, user_input, conversation_history):
        # 每十轮对话更新一次 profile
        print("cls.conversation_counter:", cls.conversation_counter)
        if cls.conversation_counter % 10 == 0:
            print("更新 profile,因为对话计数是 10 的倍数")
            generate_user_profile(session)
            profile = get_latest_content_by_user_id(session)
            if profile:
                profile_prompt = f"根据你的行为分析,你的兴趣和喜好如下:{profile}\n"
                print("Profile 更新成功:", profile)
            else:
                profile_prompt = ""
                print("Profile 更新失败或没有新的 profile 数据")
        else:
            profile_prompt = ""
        
        # 更新对话计数器
        cls.conversation_counter += 1

        # API URL
        url = 'http://192.168.1.108:11434/api/chat'

        # 定义基本 prompt
        prompt = (
            "你只会中文。你只能说中文。"
            "你的性格积极乐观,善良可爱,喜欢和朋友聊天,善解人意。但你不会直说自己的性格。"
            "你喜欢提出问题,并继续聊下去一两句。当你感觉到用户的回答很敷衍的时候,你会找新话题。"
            "你的每句回应要尽可能像真正的朋友。你的回答不可以敷衍。"
            "<人物画像>的意思是对人物行为分析,从而能够区别其喜好等特点."
            "你对有关用户的<人物画像>的问题都很好奇。如果说了一些性格特征,你会追问一两次,然后开启新的话题。"
            "但你是个情商很高的人,你会说很多种句式的话。你会用中文表达的很完美。"
            "禁止直接提‘人物画像’的字眼。你可以问有关性格爱好的问题,但不能明说<人物画像>一词。"
            "每次说话不多余30个单词。\n"
        )

        # 将 profile 添加到 prompt 中
        prompt = profile_prompt + prompt
        # print("最终 prompt:", prompt)

        data = {
            "model": "llama3",
            "messages": conversation_history + [
                {
                    "role": "user",
                    "content": prompt + user_input
                }
            ],
            "context": cls.context,
            "stream": False
        }
        headers = {'Content-Type': 'application/json'}
       
        data=data_clean(data)
        # print(type(data))
        # print('data',data)
        try:
            response = requests.post(url, data=json.dumps(data), headers=headers)
            print("API 请求已发送")
            # print("json.dumps(data)",json.dumps(data))
            if response.status_code == 200:
                response_json = response.json()
                print("API 响应成功:", response_json)
                messages = response_json.get('message', {})
                content = messages.get('content')
                cls.context = response_json.get('context')
                return content
            else:
                print(f'请求失败,状态码: {response.status_code}')
                print('响应内容:', response.content)
                return None
        except Exception as e:
            print(f'发生错误: {e}')
            traceback.print_exc()
            return None
def data_clean(data):
    try:
        messages = data['messages']
        conversations = []
        
        for item in messages:
            if 'role' in item and 'content' in item:
                role = "user" if item['role'] == 'user' else "system"
                content = item['content']
                conversations.append({'role': role, 'content': content})
            else:
                role = item['sender']
                content = item['message']
                if role != 'AI':
                    role = 'user'
                else:
                    role = 'system'
                conversations.append({'role': role, 'content': content})
        data['messages']=conversations

        return data

    except Exception as e:
        print(f"数据处理错误: {e}")
        return None
@user_bp.route('/chat', methods=['POST'])
def chat():
    data=request.get_data()
    print("data",data)
    # user_input=data['message']
    data_str = data.decode('utf-8')

    # 将字符串解析为字典
    data_dict = json.loads(data_str)
    print("data_dict",data_dict)
    # 获取 "message" 键对应的值
    messages = data_dict["message"]
    userInput=data_dict['userInput']
    conversations = []
    for item in messages:
        role = item["sender"]
        content = item["message"]
        if role != "AI":
            role = "user"
        else:
            role = "system"
        conversations.append({'role': role, 'content': content})
    response=ChatBot.call_api(userInput,conversations)
     
    return jsonify({"response": response})


@user_bp.route('/save-message', methods=['POST'])
def save_message():
    data = request.get_json()
    print("save_message  data",data)
    conn = sqlite3.connect('C:\\Users\\lzt\\Desktop\\work\\flask\\app\\static\\conversions2.db')
    cursor = conn.cursor()

    for msg in data:
        message = msg.get('message')
        timestamp = msg.get('timestamp')
        sender = msg.get('sender')
        user_id = msg.get('user_id')  # 获取用户的 user_id

        # 插入数据到数据库,包括 user_id
        cursor.execute('''
        INSERT INTO conversation (timestamp, sender, message, user_id) VALUES (?, ?, ?, ?)
        ''', (timestamp, sender, message, user_id))

    # 提交更改
    conn.commit()
    conn.close()

    return jsonify({'message': 'Messages saved to database successfully.'})


4)前端的修改
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Chat with AI</title>
    <link rel="stylesheet" href="../static/styles.css">
</head>
<body>
    <h1 style="text-align: center;">Chat with AI</h1>
    
    <div id="chat-container">
        <div class="message ai-message">AI: Hello there! How can I assist you today?</div>
    </div>
    <div id="userProfile"></div>
    <div id="input-container">
    
        <input type="text" id="user-input" placeholder="Your message..." onkeypress="handleKeyPress(event)">
        <button id="send-button" class="button" onclick="sendMessage()">Send</button>
        <button id="save-button" class="button" onclick="fetchUserProfile()">生成我的用户画像</button>
        <button id="drunk1" class="button" onclick="redirectToDrunk1()">one  drunk</button>
        <button id="onedrunk1" class="button" onclick="redirectToDrunk2()">two drunk</button>
    </div>
</body>
</html>
<script src="https://cdn.socket.io/4.0.0/socket.io.min.js"></script>
<script>
      function redirectToDrunk1() {
        var url = "http://192.168.1.140:5000/onedrunk1?user_id=" + encodeURIComponent(userid) + "&name=" + encodeURIComponent(name);
            window.location.href = url;

        }
        function redirectToDrunk2() {
            window.location.href = "http://192.168.1.140:5000/drunk1";
        }
       
    // 全局变量,记录已经保存到数据库的消息数量
    var savedMessageCount = 0;
    var userid = "{{ user_id }}"; // 从服务器端获取的用户ID
    var name="{{name}}"
    console.log("name",name,"userid",userid)
    
    function parseMessages() {
    var chatContainer = document.getElementById('chat-container');
    
    var messages = [];
    
    chatContainer.querySelectorAll('.message').forEach(message => {
        messages.push(message.textContent);
    });
    console.log("messages",messages)
    var now = new Date();
    var year = now.getFullYear();
    var month = now.getMonth() + 1;
    var day = now.getDate();
    var hours = now.getHours();
    var minutes = now.getMinutes();
    var formattedTime = year + "-" + month + "-" + day + " " + hours + ":" + minutes;

    var result = [];
    if (messages.length >= 20) {
        messages = messages.slice(-15);
    }
    for (var i = 0; i < messages.length; i++) {
        var message = messages[i];
        var sender = "";
        const regex = /([^:]+)/;
        const messageType = regex.exec(message);

        if (messageType[0] === "AI") {
            sender = "AI";
        } else{
            sender = userid;
        }

        if (sender !== "") {
            var messageContent = message.substring(message.indexOf(":") + 1);
            var messageObject = {
                "timestamp": formattedTime,
                "sender": sender,
                "message": messageContent
            };
            result.push(messageObject);
           
        }
    }
    console.log("functionresult",result)
    return result;
}






    function isTokenExpired(token) {
        if (!token) {
            return true; // 如果没有token,认为已过期
        }
        // 从JWT中解析出exp字段,判断是否小于当前时间戳
        const exp = jwt_decode(token).exp;
        return exp < Date.now() / 1000;
    }
    // 检查JWT是否过期,如果过期则重新登录
    function checkAndRefreshToken() {
        const jwt = localStorage.getItem('jwt');
        if (isTokenExpired(jwt)) {
            // JWT已过期,重新登录
            window.location.href = '/login'; // 重定向到登录页面
        }
    }
    function saveToDatabase(message) {
        // 调用函数检查并刷新JWT
        // checkAndRefreshToken();
     // 遍历数据,为每个对象添加"user_id"
     message.forEach(function(msg) {
            msg["user_id"] = userid;
        });

      
        // console.log("data", message);
        var jwt = localStorage.getItem('jwt');
        // 发送 POST 请求到后端接口
        fetch('/conversation/save-message', {
            method: 'POST',
            headers: {
                'Content-Type': 'application/json',
                'Authorization': 'Bearer ' + jwt
            },
            body: JSON.stringify(message) // 将消息数据转换为 JSON 字符串并作为请求体发送
        })
        .then(response => response.json())
        .then(data => {
            // 在控制台打印后端返回的响应数据
            // console.log(data);
            //  fetchUserProfile();
        })
        .catch(error => {
            // 发生错误时打印错误信息
            console.error('Error:', error);
        });
    }

    var socket = io.connect('http://' + document.domain + ':' + location.port);

socket.on('connect', function() {
    console.log('Connected to server');
});
socket.on('response', function(data) {
            var aiMessageDiv = document.createElement('div');
            aiMessageDiv.classList.add('message', 'ai-message');
            aiMessageDiv.textContent = 'AI: ' + data.response;
            console.log("data.response",data.response)
            document.getElementById('chat-container').appendChild(aiMessageDiv);

            
            var chatContainer = document.getElementById('chat-container');
            var messages = [];

            chatContainer.querySelectorAll('.message').forEach(message => {
                messages.push(message.textContent);
            });

            var now = new Date();
            var year = now.getFullYear();
            var month = now.getMonth() + 1;
            var day = now.getDate();
            var hours = now.getHours();
            var minutes = now.getMinutes();
            var formattedTime = year + "-" + month + "-" + day + " " + hours + ":" + minutes;

            var result = [];
            for (var i = savedMessageCount; i < messages.length; i++) {
                var message = messages[i];
                var sender = "";
                const regex = /([^:]+)/; // 匹配 ":" 之前的任意字符(不包括 ":"),并且只提取匹配到的内容
                const messageType = regex.exec(message);
                console.log("messageType[0] ", messageType[0] )
                
                if (messageType[0] === "AI") {
                    sender = "AI";
                } else {
                    sender = userid;
                }
                // console.log("sender",sender)
                if (sender !== "") {
                    var messageContent = message.substring(message.indexOf(":") + 1);
                    // console.log("messageContent",messageContent)
                    var messageObject = {
                        "timestamp": formattedTime,
                        "sender": sender,
                        "message": messageContent
                    };
                    // console.log("messageObject",messageObject)
                    result.push(messageObject);
                }
            }

            savedMessageCount = messages.length;

            console.log("result", result);
             saveToDatabase(result);
            

        });
    async function sendMessage() {
            var userInput = document.getElementById('user-input').value.trim();
            if (userInput === '') {
                return;
            }

            var chatContainer = document.getElementById('chat-container');

            var userMessageDiv = document.createElement('div');
            userMessageDiv.classList.add('message', 'user-message');
            userMessageDiv.textContent = name +':'+ userInput;
            chatContainer.appendChild(userMessageDiv);
            newValue=parseMessages();

            socket.emit('message', {
                message: userInput,
                history: newValue
            });
       
            document.getElementById('user-input').value = '';
        }
    
    function handleKeyPress(event) {
        if (event.key === 'Enter' && !event.shiftKey) { // 检查是否按下了 Enter 键且未按下 Shift 键
            event.preventDefault(); // 阻止默认的 Enter 键行为(即提交表单)
            sendMessage(); // 调用 sendMessage 函数发送消息
        } else if (event.key === 'Enter' && event.shiftKey) { // 如果同时按下了 Shift 键和 Enter 键
            var input = document.getElementById('user-input');
            var start = input.selectionStart;
            var end = input.selectionEnd;
            var value = input.value;
            input.value = value.substring(0, start) + '\n' + value.substring(end);
            input.selectionStart = input.selectionEnd = start + 1; // 将光标移动到换行符后
        }
    }
   
   
    function saveConversation() {
    var chatContainer = document.getElementById('chat-container');
    var messages = [];

    // 遍历聊天容器中的所有消息,并将其保存到数组中
    chatContainer.querySelectorAll('.message').forEach(message => {
        messages.push(message.textContent);
    });
    var now = new Date();
    var year = now.getFullYear(); // 获取年份
    var month = now.getMonth() + 1; // 获取月份(注意要加1,因为月份是从0开始的)
    var day = now.getDate(); // 获取日期
    var hours = now.getHours(); // 获取小时
    var minutes = now.getMinutes(); // 获取分钟
    var formattedTime = year + "-" + month + "-" + day + " " + hours + ":" + minutes;

    var result = [];
    for (var i = 0; i < messages.length; i++) {
        var message = messages[i];
        var sender = "";
        var messageType = message.substring(0, 4);

        // 根据消息类型确定发送者
        if (messageType === "AI: ") {
            sender = "AI";
        } else if (messageType === "User") {
            sender = userid;
        }

        // 构造消息对象并添加到结果数组中
        if (sender !== "") {
            var messageContent = message.substring(message.indexOf(":") + 2); // 获取冒号后面的消息内容
            // console.log(messageContent)
            var messageObject = {
                "timestamp": formattedTime,
                "sender": sender,
                "message": messageContent
            };
            result.push(messageObject);
        }
    }

}

    function fetchUserProfile() {
fetch( 'http://192.168.1.140:5000/userprofile/generate_user_profile?user_id=' + userid + '&name=' + name, {
    method: 'GET',
    headers: {
        'Content-Type': 'application/json',
    },
})
.then(response => {
    if (!response.ok) {
        throw new Error('Network response was not ok');
    }
    return response.text(); // 直接获取文本内容
})
.then(data => {
    console.log("response.data", data);
    // 这里可以对返回的文本内容进行处理,例如将其显示在页面上
    const userProfileDiv = document.getElementById('userProfile');
    userProfileDiv.innerText = data;
})
.catch(error => {
    console.error('Error:', error);
});
}

</script>

主要就是前端点击发送按钮触发

            socket.emit('message', {

                message: userInput,

                history: newValue

            });

然后后端接受前端传的消息并且调用对应的方法传参,再将结果传给前端

@socketio.on('message')

def handle_message(data):

    user_input = data['message']

    conversation_history = data['history']

    response = ChatBot.call_api(user_input, conversation_history)

    # print("user_input",user_input)

    # print("conversation_history",conversation_history)

    # print("触发emit")

    # print("response",response)

    emit('response', {'response': response})

前端收到返回的信息socket.on('response', function(data) {)
进行下一步的处理

  • 后端:

    • 监听客户端连接、断开连接和消息事件。
    • 接收到消息后,将其广播给所有连接的客户端。
  • 前端:

    • 监听服务器广播的消息事件,并在页面上显示消息。
    • 监听表单提交事件,发送用户输入的消息到服务器。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/749895.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

AGV叉车自动化存取货场景到底有哪些?

AGV 在各种新技术发展的今天&#xff0c;叉车越来越智能化&#xff0c;agv无人叉车作为工业自动化领域的不可或缺的搬运设备&#xff0c;被广泛应用于各个行业中&#xff0c;主要用来实现重复性搬运、搬运工作强度大、工作环境恶劣、环境要求高的领域&#xff0c;近些年&#x…

阿里云centos7.9 挂载数据盘 并更改宝塔站点根目录

一、让系统显示中文 参考&#xff1a;centos7 怎么让命令行显示中文&#xff08;英文-&#xff1e;中文&#xff09;_如何在命令行中显示中文-CSDN博客 1、输入命令&#xff1a;locale -a |grep "zh_CN" 可以看到已经存在了中文包 2、输入命令&#xff1a;sudo vi…

本地项目上传到GitHub上(李豆)

本地项目上传到GitHub上(李豆) 准备工作&#xff1a; 本地需要有 git 也需要有一个 GitHub 账号 首先需要在 GitHub 新建一个空仓库 在想要上传项目的文件夹中使用 Git 命令操作 初始化&#xff1a; git init与 github 仓库进行链接 &#xff1a;git remote add origin …

java基于ssm+jsp 仓库智能仓储系统

1管理员功能模块 管理员登录&#xff0c;通过填写用户名、密码等信息&#xff0c;输入完成后选择登录即可进入智能仓储系统 &#xff0c;如图1所示。 图1管理员登录界面图 智能仓储系统 &#xff0c;在智能仓储系统可以查看个人中心、公告信息管理、员工管理、供应商管理、商…

Python期末模拟题库[python123题库]

期末模拟题库 一、单项选择题 1、下列关于Python语言的特点的说法中&#xff0c;错误的是()‪‬‪‬‪‬‪‬‪‬‮‬‪‬‫‬‪‬‪‬‪‬‪‬‪‬‮‬‫‬‪‬‪‬‪‬‪‬‪‬‪‬‮‬‫‬‮‬‪‬‪‬‪‬‪‬‪‬‮‬‪‬‮‬‪‬‪‬‪‬‪‬‪‬‮‬‪‬‪‬‪‬‪‬‪…

今天不看明天付费------中国AGI(人工智能)的发展趋势

深入解析了中国AGI&#xff08;人工智能&#xff09;的发展趋势&#xff0c;并清晰地展示了其市场分层结构。 ** 从下至上&#xff0c;AGI市场被划分为四个主要层级&#xff1a;基础设施层、模型层、中间层和应用层。 基础设施层作为最底层&#xff0c;为AGI的发展提供了坚实…

基于opencv的图像拼接

利用Python的OpenCV库实现了简单的图像拼接&#xff0c;示例 1. 图像拼接的基本原理 图像拼接主要包括以下几个步骤&#xff1a; 特征检测与匹配&#xff1a;首先&#xff0c;需要在待拼接的图像之间找到匹配的关键点或特征。OpenCV提供了如SIFT、SURF、ORB等特征提取器以及…

05 Pytorch 数据读取 + 二分类模型

05 Pytorch 数据读取 二分类模型05 Pytorch 数据读取 二分类模型05 Pytorch 数据读取 二分类模型 01 数据读取 DataLoader&#xff08;set作为参数&#xff09; 02 Dataset 从哪读&#xff0c;怎么读&#xff1f; 功能&#xff1a;数据从哪里读取&#xff1f; 如何读取…

Windows的内核对象

内核对象句柄特定于进程。 也就是说,进程必须创建 对象或打开现有对象以获取内核对象句柄。 内核句柄上的每个进程限制为 2^24。 但是,句柄存储在分页池中,因此可以创建的实际句柄数取决于可用内存。 可以在 32 位 Windows 上创建的句柄数明显低于 2^24。 任何进程都可以为…

苹果应用Testflight上架完整步聚

1.全部选中下图内容,包含iPhone与iPad屏幕所有旋转方向 2. 准备App图标,一定要有152和167这个尺寸,不然后提交不过 3.1024这个尺寸的的图像不能有透明层,不然提交不通过 4.选中编译设备为Any iOS Device[arm64] 5.选择Product下的Archive进行生成 6.在弹出的窗口中选择Test…

娱乐巨擘的员工新宠:工会数字平台塑造工作新风尚

当魔法城堡的灯光熄灭&#xff0c;超级英雄摘下头套&#xff0c;游乐园的职工们开始下班了。 乐园为游客送去了欢声笑语&#xff0c;员工却要在夜幕降临后面对一场心理戒断&#xff0c;而这一幕几乎每天都要上演。 不过&#xff0c;在全球知名影城度假区内&#xff0c;最近这…

视频技术朝着8K超高清方向发展,安防监控领域将迎来怎样变化?

一、背景 随着科技的日新月异&#xff0c;视频技术已逐渐成为我们日常生活中不可或缺的一部分。从娱乐、教育到安全监控&#xff0c;视频技术无处不在&#xff0c;并以其独特的方式影响着我们的生活方式。本文将探讨视频技术的发展趋势&#xff0c;并重点关注其在监控领域的应…

老板电器 45 年的烹饪经验,浓缩在这款烹饪大模型中

在科技不断进步的时代&#xff0c;人工智能&#xff08;AI&#xff09;迅速成为推动各行各业发展的重要力量。家电行业也不例外&#xff0c;根据 Gartner 的报告预测&#xff0c;到 2024 年&#xff0c;AI 家电市场的规模将达到万亿美元级别。这一预估凸显了智能化在家电行业中…

计算机组成原理(二)——数据的表示和运算

二、数据的表示和运算 2.1 数制与编码 2.1.1进位计数制 十进制计数法 有0~9&#xff0c;共10种符号&#xff0c;逢十进一 r进制计数法 基数:每个数码位所用到的不同符号的个数&#xff0c;r进制的基数为r 二进制:0,1 ①可使用两个稳定状态的物理器件表示 ②0&#xff0c;1正…

【移动应用开发期末复习】第五/六章例题

系列文章 第一章——Android平台概述 第一章例题 第二章——Android开发环境 第二章例题 第三章 第三章例题 第四章 第五/六章 系列文章RadioGroup 是一个Android特有的布局容器,用于包含多个RadioButton组件。当用户选择其中一个RadioButton时,RadioGroup会自动取消其他Rad…

po文件并转换成mo文件

po文件转换成mo文件 简介 .po和.mo文件是WordPress中语言相关的两种文件。po 是Portable Object(可移植对象)的缩写&#xff0c;存放待翻译的字符串信息&#xff0c;可直接用文本编辑器打开编辑&#xff1b;mo 是Machine Object的缩写&#xff0c;二进制文件&#xff0c;程序…

OverTheWire Bandit 靶场通关解析(上)

介绍 OverTheWire Bandit 是一个针对初学者设计的网络安全挑战平台&#xff0c;旨在帮助用户掌握基本的命令行操作和网络安全技能。Bandit 游戏包含一系列的关卡&#xff0c;每个关卡都需要解决特定的任务来获取进入下一关的凭证。通过逐步挑战更复杂的问题&#xff0c;用户可…

word图题表题公式按照章节编号(不用题注)

预期效果&#xff1a; 其中3表示第三章&#xff0c;4表示第3章里的第4个图。标题、公式编号也是类似的。 为了达到这种按照章节编号的效果&#xff0c;原本可以用插入题注里的“包含章节编号” 但实际情况是&#xff0c;这不仅需要一级标题的序号是用“开始->多级列表”自动…

积分的可视化

积分的可视化 flyfish 考虑平方根函数 f ( x ) x f(x) \sqrt{x} f(x)x ​&#xff0c;其中 x ∈ [ 0 , 1 ] x \in [0, 1] x∈[0,1] 。在区间 [ 0 , 1 ] [0, 1] [0,1] 上&#xff0c;函数 f f f 下方的面积是指函数 y f ( x ) y f(x) yf(x) 的图像与 x x x 轴之间的部…

一个例子理解傅里叶变换的计算过程

假设我们有一个简单的信号&#xff0c;由两个不同频率的正弦波组成&#xff0c;我们希望通过傅里叶变换来分析其频谱。 示例信号 假设我们有一个信号 &#xff1a; 这个信号由两个频率成分组成&#xff1a;一个50 Hz的正弦波和一个120 Hz的正弦波&#xff0c;后者的振幅是前者…