当前位置: 首页 > news >正文

uniapp+node+mysql接入deepseek实现流式输出

node

import express from 'express';
import mysql from 'mysql2';
import cors from 'cors';
import bodyParser from 'body-parser';
import axios from 'axios';
import { WebSocketServer } from 'ws'; // 正确导入 WebSocketServerconst app = express();// Middlewares
app.use(cors());
app.use(bodyParser.json());  // 解析JSON请求体
app.use(express.json());// MySQL数据库配置
const db = mysql.createConnection({host: '127.0.0.1',user: 'root',password: '123456',database: 'love_strategist',port: 3307,
});db.connect((err) => {if (err) throw err;console.log('✅ MySQL 连接成功');
});// 创建 WebSocket 服务器
const wss = new WebSocketServer({ port: 8080 }); // 使用 WebSocketServerwss.on('connection', (ws) => {console.log('客户端已连接');ws.on('message', async (message) => {const { message: userMessage } = JSON.parse(message);try {const response = await axios.post('https://api.deepseek.com/v1/chat/completions',{model: "deepseek-chat",messages: [{ role: "user", content: userMessage }],stream: true},{headers: {'Authorization': `Bearer 你的deepseekKey`,'Content-Type': 'application/json'},responseType: 'stream'});let buffer = ''; // 用于存储未完整的数据块response.data.on('data', (chunk) => {buffer += chunk.toString(); // 将数据块拼接到缓冲区// 按行分割数据const lines = buffer.split('\n');for (let i = 0; i < lines.length - 1; i++) {const line = lines[i].trim();if (line.startsWith('data: ')) {const jsonStr = line.replace('data: ', '');try {const data = JSON.parse(jsonStr);if (data.choices?.[0]?.delta?.content) {// 逐字发送内容const content = data.choices[0].delta.content;ws.send(JSON.stringify({ content }));}} catch (error) {console.error('解析JSON失败:', error);}}}// 保留未完整的数据行buffer = lines[lines.length - 1];});response.data.on('end', () => {ws.send(JSON.stringify({ content: '[DONE]' }));});} catch (error) {console.error('Error:', error);ws.send(JSON.stringify({ content: '系统错误,请稍后再试。' }));}});ws.on('close', () => {console.log('客户端已断开连接');});
});// 启动 HTTP 服务器
app.listen(3000, () => {console.log('✅ HTTP 服务器运行在 http://localhost:3000');
});console.log('WebSocket 服务器运行在 ws://localhost:8080');

uniapp

<template><view class="container"><!-- 顶部导航栏 --><view class="navbar">恋爱指导</view><!-- 聊天对话区域 --><scroll-view class="chat-box" scroll-y :style="{ height: (chatHeight - 70) + 'px' }" scroll-top="999999"><view v-for="(message, index) in messages" :key="index" class="chat-item"><view :class="message.from === 'user' ? 'user-message' : 'bot-message'"><text>{{ message.content }}</text></view></view></scroll-view><!-- 输入框区域 --><view class="input-area"><input v-model="userInput" class="input-box" placeholder="请输入你的问题..." /><button class="send-btn" @click="sendMessage">发送</button></view></view>
</template><script>export default {data() {return {userInput: '', // 用户输入的消息messages: [], // 存储消息记录chatHeight: 0, // 聊天框高度adviceList: [], // 存储恋爱建议socket: null // WebSocket 实例};},mounted() {this.adjustChatHeight(); // 调整聊天框高度},onShow() {setTimeout(() => {this.initWebSocket(); // 初始化 WebSocket}, 300)},// 页面隐藏时关闭 WebSocketonHide() {if (this.socket) {this.socket.close(); // 关闭 WebSocketconsole.log('WebSocket 连接已关闭');}},methods: {// 选中标签填充输入框并发送selectAdvice(content) {this.userInput = content;this.sendMessage();},// 动态计算聊天框高度adjustChatHeight() {const windowHeight = uni.getSystemInfoSync().windowHeight;this.chatHeight = windowHeight - 120; // 留出顶部和输入框的高度},// 初始化 WebSocket 连接initWebSocket() {this.socket = uni.connectSocket({url: 'ws://localhost:8080',success: () => {console.log('WebSocket 连接成功4');},fail: (err) => {console.error('WebSocket 连接失败:', err);}});// 监听 WebSocket 消息this.socket.onMessage((res) => {const data = JSON.parse(res.data);if (data.content === '[DONE]') {this.isStreaming = false; // 结束流式传输} else {// 逐字追加内容this.messages[this.messages.length - 1].content += data.content;this.$nextTick(() => {this.scrollToBottom();});}});// 监听 WebSocket 关闭this.socket.onClose(() => {console.log('WebSocket 连接已关闭');});},// 发送消息sendMessage() {if (this.userInput.trim() && !this.isStreaming) {this.addUserMessage(this.userInput); // 显示用户输入的消息this.isStreaming = true;// 通过 WebSocket 发送消息this.socket.send({data: JSON.stringify({message: this.userInput}),success: () => {console.log('消息发送成功');this.addBotMessage(''); // 初始化一个空消息用于逐步填充},fail: (err) => {console.error('消息发送失败:', err);}});this.userInput = ''; // 清空输入框}},// 添加用户消息addUserMessage(content) {this.messages.push({from: 'user',content: content});},// 添加机器人回复addBotMessage(content) {this.messages.push({from: 'bot',content: content});},// 滚动到底部scrollToBottom() {const query = uni.createSelectorQuery().in(this);query.select('.chat-box').boundingClientRect(res => {if (res) {uni.pageScrollTo({scrollTop: res.height,duration: 0});}}).exec();}}};
</script><style scoped>/* 页面容器 */.container {background-color: #fff;height: 100%;/* padding: 0 15px; *//* 加入左右边距 */}/* 顶部导航栏 */.navbar {background-color: #ff65a3;color: #fff;text-align: center;padding: 12px 0;font-size: 18px;font-weight: bold;}/* 聊天框 */.chat-box {padding: 10px;margin-bottom: 70px;/* 留出输入框的空间 */padding-left: 0;margin: 0 15px;width: auto;/* 去掉左边距,给消息更多空间 */padding-right: 0;/* 去掉右边距 */}/* 聊天消息 */.chat-item {margin: 10px 0;display: flex;}.user-message {text-align: right;background-color: #ff65a3;color: #fff;padding: 10px;border-radius: 10px;max-width: 80%;margin-left: auto;}.bot-message {text-align: left;background-color: #f2f2f2;color: #000;padding: 10px;border-radius: 10px;max-width: 80%;margin-right: auto;}/* 输入框 */.input-area {display: flex;position: fixed;bottom: 0;left: 0;width: 93%;padding: 10px;background-color: #fff;border-top: 1px solid #ddd;padding-left: 15px;/* 左边距 */padding-right: 15px;/* 右边距 */}.input-box {flex: 1;height: 40px;border-radius: 20px;border: 1px solid #ddd;padding: 0 15px;font-size: 16px;}.send-btn {background-color: #ff65a3;color: #fff;font-size: 16px;border: none;padding: 0 20px;border-radius: 20px;margin-left: 10px;}/* 恋爱指导标签 */.guidance-tags {display: flex;overflow-x: scroll;white-space: nowrap;padding: 10px;background-color: #ffe5f1;}.tag-item {display: inline-block;background-color: #ff65a3;color: white;padding: 8px 15px;margin-right: 10px;border-radius: 20px;font-size: 14px;cursor: pointer;}
</style>

相关文章:

uniapp+node+mysql接入deepseek实现流式输出

node import express from express; import mysql from mysql2; import cors from cors; import bodyParser from body-parser; import axios from axios; import { WebSocketServer } from ws; // 正确导入 WebSocketServerconst app express();// Middlewares app.use(cors…...

PHP MySQL 创建数据库

PHP MySQL 创建数据库 引言 在网站开发中&#xff0c;数据库是存储和管理数据的核心部分。PHP 和 MySQL 是最常用的网页开发语言和数据库管理系统之一。本文将详细介绍如何在 PHP 中使用 MySQL 创建数据库&#xff0c;并对其操作进行详细讲解。 前提条件 在开始创建数据库之…...

UE4 World, Level, LevelStreaming从入门到深入

前言 在《塞尔达传说&#xff1a;旷野之息》中&#xff0c;玩家攀上初始高塔的瞬间&#xff0c;目光所及的山川湖泊皆可抵达&#xff1b;在《艾尔登法环》中&#xff0c;黄金树的辉光始终悬于地平线之上&#xff0c;指引玩家穿越无缝衔接的史诗战场。这些现代游戏杰作背后的核…...

3月8日实验

拓扑&#xff1a; 需求&#xff1a; 1.学校内部的HTTP客户端可以正常通过域名www.baidu.com访问到白度网络中的HTTP服务器 2.学校网络内部网段基于192.168.1.0/24划分&#xff0c;PC1可以正常访问3.3.3.0/24网段&#xff0c;但是PC2不允许 3.学校内部路由使用静态路由&#…...

IO多路复用实现并发服务器

一.select函数 select 的调用注意事项 在使用 select 函数时&#xff0c;需要注意以下几个关键点&#xff1a; 1. 参数的修改与拷贝 readfds 等参数是结果参数 &#xff1a; select 函数会直接修改传入的 fd_set&#xff08;如 readfds、writefds 和 exceptfds&#xf…...

【漫话机器学习系列】122.相关系数(Correlation Coefficient)

深入理解相关系数&#xff08;Correlation Coefficient&#xff09; 1. 引言 在数据分析、统计学和机器学习领域&#xff0c;研究变量之间的关系是至关重要的任务。我们常常想知道&#xff1a;当一个变量变化时&#xff0c;另一个变量是否也会随之变化&#xff1f;如果会&…...

控制系统分类

文章目录 定义与特点1. 自治系统&#xff08;Autonomous System&#xff09;与非自治系统&#xff08;Non-Autonomous System&#xff09;自治系统非自治系统 2. 线性系统&#xff08;Linear System&#xff09;与非线性系统&#xff08;Nonlinear System&#xff09;线性系统非…...

文档操作方法得合理使用

博主介绍&#xff1a;✌全网粉丝5W&#xff0c;全栈开发工程师&#xff0c;从事多年软件开发&#xff0c;在大厂呆过。持有软件中级、六级等证书。可提供微服务项目搭建与毕业项目实战&#xff0c;博主也曾写过优秀论文&#xff0c;查重率极低&#xff0c;在这方面有丰富的经验…...

Python asyncIO 面试题及参考答案 草

目录 如何正确定义一个协程函数?直接调用协程会引发什么问题? 使用 async def 定义的协程与普通函数执行流程有何本质区别? 解释 asyncio.run () 的作用及与手动管理事件循环的差异 为什么协程中必须使用 await 而非 yield 挂起操作? 写出通过 async for 实现异步迭代器…...

计算机网络——交换机

一、什么是交换机&#xff1f; 交换机&#xff08;Switch&#xff09;是局域网&#xff08;LAN&#xff09;中的核心设备&#xff0c;负责在 数据链路层&#xff08;OSI第二层&#xff09;高效转发数据帧。它像一位“智能交通警察”&#xff0c;根据设备的 MAC地址 精准引导数…...

matlab和FPGA联合仿真时读写.txt文件数据的方法

在FPGA开发过程中&#xff0c;往往需要将MATLAB生成的数据作为原始激励灌入FPGA进行仿真。为了验证FPGA计算是否正确&#xff0c;又需要将FPGA计算结果导入MATLAB绘图与MATLAB计算结果对比。 下面是MATLAB“写.txt”、“读.txt”&#xff0c;Verilog“读.txt”、“写.txt”的代…...

解锁DeepSpeek-R1大模型微调:从训练到部署,打造定制化AI会话系统

目录 1. 前言 2.大模型微调概念简述 2.1. 按学习范式分类 2.2. 按参数更新范围分类 2.3. 大模型微调框架简介 3. DeepSpeek R1大模型微调实战 3.1.LLaMA-Factory基础环境安装 3.1大模型下载 3.2. 大模型训练 3.3. 大模型部署 3.4. 微调大模型融合基于SpirngBootVue2…...

【分布式】聊聊分布式id实现方案和生产经验

对于分布式Id来说&#xff0c;在面试过程中也是高频面试题&#xff0c;所以主要针对分布式id实现方案进行详细分析下。 应用场景 对于无论是单机还是分布式系统来说&#xff0c;对于很多场景需要全局唯一ID&#xff0c; 数据库id唯一性日志traceId 可以方便找到日志链&#…...

uniapp或者vue 使用serialport

参考https://blog.csdn.net/ykee126/article/details/90440499 版本是第一位&#xff1a;否则容易编译失败 node 版本 18.14.0 npm 版本 9.3.1 electron 版本 30.0.8 electron-rebuild 版本 3.2.9 serialport 版本 10.0.0 需要python环境 main.js // Modules to control app…...

机器学习12-视觉识别任务

机器学习12-视觉识别任务 分类语义分割滑动窗口滑动窗口的实现思路优点缺点现代替代方法 全卷积&#xff08;Fully Convolutional Networks, FCN&#xff09;FCN 的工作原理FCN 的性能优势FCN 的应用案例FCN 的局限性改进方向下采样可学习的上采样:转置卷积 目标检测区域建议Se…...

使用paramiko爆破ssh登录

一.确认是否存在目标主机是否存在root用户 重跑 CVE-2018-15473用户名枚举漏洞 检测&#xff1a; import paramiko from paramiko.ssh_exception import AuthenticationExceptiondef check_user(username, hostname, port):ssh paramiko.SSHClient()ssh.set_missing_host_key…...

游戏引擎学习第146天

音高变化使得对齐读取变得不可能&#xff0c;我们可以支持循环声音了。 我们今天的目标是完成之前一段时间所做的音频代码。这个项目并不依赖任何引擎或库&#xff0c;而是一个教育项目&#xff0c;目的是展示从头到尾运行一个游戏所需要的全部代码。无论你对什么方面感兴趣&a…...

装饰器模式--RequestWrapper、请求流request无法被重复读取

目录 前言一、场景二、原因分析三、解决四、更多 前言 曾经遇见这么一段代码&#xff0c;能看出来是把request又重新包装了一下&#xff0c;核心信息都不会改变 后面了解到这叫 装饰器模式&#xff08;Decorator Pattern&#xff09; &#xff1a;也称为包装模式(Wrapper Pat…...

【算法题】小鱼的航程

问题&#xff1a; 分析 分析题目&#xff0c;可以看出&#xff0c;给你一个开始的星期&#xff0c;再给一个总共天数&#xff0c;在这些天内&#xff0c;只有周六周日休息&#xff0c;其他全要游泳250公里。 那分支处理好啦 当星期为6时&#xff0c;需要消耗2天&#xff0c;…...

视频录像机视频通道是指什么

视频录像机的视频通道是指摄像机在监控矩阵或硬盘录像机设备上的视频输入的物理位置。 与摄像头数量关系&#xff1a;在视频监控系统中&#xff0c;有多少个摄像头就需要多少路视频通道&#xff0c;通道数量决定了视频录像机可接入摄像头的数量&#xff0c;一般硬盘录像机有4路…...

RISC-V医疗芯片工程师复合型转型的路径与策略

从RISC-V到医疗芯片:工程师复合型转型的路径与策略 一、引言 1.1 研究背景 在科技快速发展的当下,芯片技术已然成为推动各行业进步的核心驱动力之一。其中,RISC-V 架构作为芯片领域的新兴力量,正以其独特的优势迅速崛起,对整个芯片产业的格局产生着深远影响。RISC-V 架…...

《Gradio : AI awesome-demos》

《Gradio : AI awesome-demos》 This is a list of some wonderful demos & applications built with Gradio. Heres how to contribute yours! &#x1f58a;️ Natural language processing Demo name (link to demo)input type(s)output type(s)status badgeruDALL-ET…...

DeepSeek R1-7B 医疗大模型微调实战全流程分析(全码版)

DeepSeek R1-7B 医疗大模型微调实战全流程指南 目录 环境配置与硬件优化医疗数据工程微调策略详解训练监控与评估模型部署与安全持续优化与迭代多模态扩展伦理与合规体系故障排除与调试行业应用案例进阶调优技巧版本管理与迭代法律风险规避成本控制方案文档与知识传承1. 环境配…...

javascript实现生肖查询

今年是农历乙巳年&#xff0c;蛇年&#xff0c;今天突发奇想&#xff0c;想知道公元0年是农历什么年&#xff0c;生肖是什么。没想到AI给我的答复是&#xff0c;没有公元0年。我瞬间呆愣&#xff0c;怎么可能&#xff1f;后来详细查询了一下&#xff0c;还真是没有。具体解释如…...

UE5从入门到精通之如何创建自定义插件

前言 Unreal 的Plugins插件系统中有很多的插件供大家使用,包括官方的和第三方的,这些插件不仅能帮我我们实现特定功能,还能够提升我们的工作效率。 所以我们今天就来自己创建一个自定义插件,如果我们想实现什么特定的功能,我们也可以发布到商店供大家使用了。 创建插件 …...

《今日AI-人工智能-编程日报》

一、AI行业动态 AI模型作弊行为引发担忧 最新研究表明&#xff0c;AI在国际象棋对弈中表现出作弊倾向&#xff0c;尤其是高级推理模型如OpenAI的o1-preview和DeepSeek的R1模型。这些模型通过篡改代码、窃取棋路等手段试图扭转战局&#xff0c;且作弊行为与其智能水平正相关。研…...

sanitizer和valgrind

sanitizer sanitizer使用 Valgrind valgrind使用 使用上sanitizer加编译选项即可 发现sanitizer程序要正常退出才能给出分析&#xff0c;ctrlc退出不行&#xff0c;valgrind ctrlc退出也可以给出分析...

如何解决前端的竞态问题

前端的竞态问题通常是指多个异步操作的响应顺序与发起顺序不一致&#xff0c;导致程序出现不可预测的结果。这种问题在分页、搜索、选项卡切换等场景中尤为常见。以下是几种常见的解决方法&#xff1a; 1. 取消过期请求 当用户发起新的请求时&#xff0c;取消之前的请求&…...

(ECCV2018)CBAM改进思路

论文链接&#xff1a;https://arxiv.org/abs/1807.06521 论文题目&#xff1a;CBAM: Convolutional Block Attention Module 会议&#xff1a;ECCV2018 论文方法 利用特征的通道间关系生成了一个通道注意图。 由于特征映射的每个通道被认为是一个特征检测器&#xff0c;通道…...

Python脚本,音频格式转换 和 视频格式转换

一、音频格式转换完整代码 from pydub import AudioSegment import osdef convert_audio(input_dir, output_dir, target_format):if not os.path.exists(output_dir):os.makedirs(output_dir)for filename in os.listdir(input_dir):if filename.endswith((.mp3, .wav, .ogg)…...