node.js + html调用ChatGPTApi实现Ai网站demo(带源码)
文章目录
- 前言
- 一、demo演示
- 二、node.js 使用步骤
- 1.引入库
- 2.引入包
- 前端HTML调用接口和UI
- 所有文件
- 总结
前言
关注博主,学习每天一个小demo 今天是Ai对话网站
又到了每天一个小demo的时候咯,前面我写了多人实时对话demo、和视频转换demo,今天我来使用 node.js + html 调用chatGpt Api实现一个Ai 流式对话小demo,当然现在主流的各种APi调用方式一致,你也可以接入deepseek,或者第三方的接口,效果一样
一、demo演示
下面是一个简单的demo演示,并且是支持流式的
二、node.js 使用步骤
1.引入库
代码如下:
先初始化一个package.js 文件。
npm init -y
我们需要安装 express、cors、openAi、dotenv三方库
{"name": "web","version": "1.0.0","main": "server.js","scripts": {"test": "echo \"Error: no test specified\" && exit 1","start": "node server.js"},"keywords": [],"author": "","license": "ISC","description": "","dependencies": {"axios": "^1.7.9","cors": "^2.8.5","dotenv": "^16.0.3","express": "^4.18.2","openai": "^4.0.0"}
}
2.引入包
创建server.js 引入我们的第三方包 编写接口逻辑
**特别关注:
- baseURL: 这里大家可以替换其他的APi 比如deepseek(好像目前不能充值了),或者一些第三方的API地址。
- apiKey: 这里大家把key可以直接填写上,我这里为了学习知识,新建了一个.env 文件。**
const express = require('express');
const cors = require('cors');
const OpenAI = require('openai');
require('dotenv').config();const app = express();
app.use(express.json());
app.use(cors());// 初始化OpenAI客户端
const openai = new OpenAI({apiKey: process.env.DEEPSEEK_API_KEY, baseURL: "https://api.openai.com/v1" // 使用OpenAI官方API地址 这里可以可以使用别的Api地址// 比如 deepseek 或者其他的第三方的一些// baseURL: "https://api.deepseek.com/v1"
});let conversationHistory = [];app.post('/chat', async (req, res) => {try {const { message } = req.body;// 设置响应头,支持流式输出res.setHeader('Content-Type', 'text/event-stream');res.setHeader('Cache-Control', 'no-cache');res.setHeader('Connection', 'keep-alive');res.setHeader('Access-Control-Allow-Origin', '*'); // 添加CORS支持// 添加用户消息到历史记录conversationHistory.push({ role: "user", content: message });const stream = await openai.chat.completions.create({model: "gpt-3.5-turbo",messages: [{ role: "system", content: "You are a helpful assistant." },...conversationHistory],temperature: 0.7,max_tokens: 1000,stream: true,});let fullResponse = '';// 确保每次写入后立即刷新缓冲区for await (const chunk of stream) {const content = chunk.choices[0]?.delta?.content || '';if (content) {fullResponse += content;const dataToSend = JSON.stringify({ content });console.log('Sending to client:', dataToSend);res.write(`data: ${dataToSend}\n\n`);// 强制刷新缓冲区if (res.flush) {res.flush();}}}// 将完整回复添加到对话历史conversationHistory.push({ role: "assistant", content: fullResponse });if (conversationHistory.length > 10) {conversationHistory = conversationHistory.slice(-10);}res.write('data: [DONE]\n\n');if (res.flush) {res.flush();}res.end();} catch (error) {console.error('Error:', error);res.write(`data: ${JSON.stringify({ error: '服务器错误' })}\n\n`);res.end();}
});const PORT = 3000;
app.listen(PORT, () => {console.log(`服务器运行在端口 ${PORT}`);
});
在根目录执行 node server.js 启动服务
前端HTML调用接口和UI
不墨迹哈,直接把所有代码贴过来 大家好直接研究代码,我就不用一行一行解读了,没啥东西,难处就是对流式数据的一个处理
特别注意, 不要直接点击html打开页面,在vscode里面安装扩展Live Server,然后点击右下角 Go live启动一个微服务。
<!DOCTYPE html>
<html lang="zh">
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>ChatGPT 聊天</title><style>#chat-container {width: 80%;max-width: 800px;margin: 20px auto;padding: 20px;border: 1px solid #ccc;border-radius: 5px;}#chat-messages {height: 400px;overflow-y: auto;margin-bottom: 20px;padding: 10px;border: 1px solid #eee;}.message {margin: 10px 0;padding: 10px;border-radius: 5px;}.user-message {background-color: #e3f2fd;margin-left: 20%;}.bot-message {background-color: #f5f5f5;margin-right: 20%;}#message-form {display: flex;gap: 10px;}#message-input {flex: 1;padding: 8px;}.typing {opacity: 0.5;}.cursor {display: inline-block;width: 2px;height: 15px;background: #000;margin-left: 2px;animation: blink 1s infinite;}@keyframes blink {0%, 100% { opacity: 1; }50% { opacity: 0; }}</style>
</head>
<body><div id="chat-container"><div id="chat-messages"></div><form id="message-form"><input type="text" id="message-input" placeholder="输入消息..." required><button type="submit" id="submit-btn">发送</button></form></div><script>const messageForm = document.getElementById('message-form');const messageInput = document.getElementById('message-input');const chatMessages = document.getElementById('chat-messages');const submitBtn = document.getElementById('submit-btn');messageForm.addEventListener('submit', async (e) => {e.preventDefault();const message = messageInput.value.trim();if (!message) return;// 禁用输入和发送按钮messageInput.disabled = true;submitBtn.disabled = true;// 显示用户消息addMessage(message, 'user');messageInput.value = '';// 创建机器人回复的消息框const botMessageDiv = document.createElement('div');botMessageDiv.className = 'message bot-message typing';chatMessages.appendChild(botMessageDiv);// 添加光标const cursor = document.createElement('span');cursor.className = 'cursor';botMessageDiv.appendChild(cursor);try {const response = await fetch('http://localhost:3000/chat', {method: 'POST',headers: {'Content-Type': 'application/json','Accept': 'text/event-stream'},body: JSON.stringify({ message })});if (!response.ok) {throw new Error(`HTTP error! status: ${response.status}`);}const reader = response.body.getReader();const decoder = new TextDecoder();let botResponse = '';let buffer = '';try {while (true) {const { value, done } = await reader.read();// 如果流结束了,就退出循环if (done) {console.log('Stream complete');break;}// 确保有值才处理if (value) {buffer += decoder.decode(value, { stream: true });const lines = buffer.split('\n');// 保留最后一个不完整的行buffer = lines.pop() || '';for (const line of lines) {if (line.trim() === '') continue;if (!line.startsWith('data: ')) continue;const data = line.slice(6).trim();if (data === '[DONE]') {botMessageDiv.classList.remove('typing');cursor.remove();continue;}try {const parsedData = JSON.parse(data);if (parsedData.content) {botResponse += parsedData.content;botMessageDiv.textContent = botResponse;botMessageDiv.appendChild(cursor);chatMessages.scrollTop = chatMessages.scrollHeight;}} catch (e) {console.error('JSON解析错误:', e, 'Raw data:', data);}}}}// 处理最后可能残留的数据if (buffer.trim()) {const finalText = decoder.decode(); // 完成流的解码if (finalText) {buffer += finalText;const lines = buffer.split('\n');for (const line of lines) {if (line.trim() === '' || !line.startsWith('data: ')) continue;const data = line.slice(6).trim();if (data === '[DONE]') continue;try {const parsedData = JSON.parse(data);if (parsedData.content) {botResponse += parsedData.content;botMessageDiv.textContent = botResponse;}} catch (e) {console.error('最终解析错误:', e, 'Raw data:', data);}}}}} catch (streamError) {console.error('Stream processing error:', streamError);throw streamError;}} catch (error) {console.error('Error:', error);botMessageDiv.textContent = '抱歉,发生错误。';botMessageDiv.classList.remove('typing');cursor.remove();} finally {messageInput.disabled = false;submitBtn.disabled = false;messageInput.focus();}});function addMessage(text, sender) {const messageDiv = document.createElement('div');messageDiv.className = `message ${sender}-message`;messageDiv.textContent = text;chatMessages.appendChild(messageDiv);chatMessages.scrollTop = chatMessages.scrollHeight;}</script>
</body>
</html>
所有文件
- .env 存储了APi-key
- index.html 前端代码
- server.js 后段代码
总结
尽可能的多学习一些知识,或许以后用不到,关注我每天练习一个小demo。
相关文章:

node.js + html调用ChatGPTApi实现Ai网站demo(带源码)
文章目录 前言一、demo演示二、node.js 使用步骤1.引入库2.引入包 前端HTML调用接口和UI所有文件总结 前言 关注博主,学习每天一个小demo 今天是Ai对话网站 又到了每天一个小demo的时候咯,前面我写了多人实时对话demo、和视频转换demo,今天…...

sql语言语法的学习
sql通用语法 sql分类 DDL(操作数据库和表) 操作数据库 操作表_查询 操作表_创建 举例: 操作表_删除 操作表_修改 DML(增删改表中数据) DML添加数据 DML删除数据 DML修改数据 DQL 单表查询 基础查询 条件查询 案例演示: 排序查询 聚合函数 分组查询…...

力扣 最长递增子序列
动态规划,二分查找。 题目 由题,从数组中找一个最长子序列,不难想到,当这个子序列递增子序列的数越接近时是越容易拉长的。从dp上看,当遍历到这个数,会从前面的dp选一个最大的数加上当前数,注意…...

【linux】在 Linux 服务器上部署 DeepSeek-r1:70b 并通过 Windows 远程可视化使用
【linux】在 Linux 服务器上部署 DeepSeek-r1:70b 并通过 Windows 远程可视化使用 【承接商业广告,如需商业合作请+v17740568442】 文章目录 【linux】在 Linux 服务器上部署 DeepSeek-r1:70b 并通过 Windows 远程可视化使用个人配置详情一、安装ollama二、下载deepseek版本…...

visutal studio 2022使用qcustomplot基础教程
编译 下载,2.1.1版支持到Qt6.4 。 拷贝qcustomplot.h和qcustomplot.cpp到项目源目录(Qt project)。 在msvc中将它俩加入项目中。 使用Qt6.8,需要修改两处代码: L6779 # if QT_VERSION > QT_VERSION_CHECK(5, 2, …...

Linux:线程概念、理解、控制
目录 一、认识线程 1.认识线程V1 2.认识线程V2 3.认识线程V3 4.认识线程V4 5.认识线程V5 二、线程控制 1.前言 2.创建线程 3.线程等待 4.线程终止 5.线程分离 三、线程理解 一、认识线程 1.认识线程V1 借用大多数计算机教材的话,线程是进程的一个执行…...

Postman如何流畅使用DeepSeek
上次写了一篇文章是用chatBox调用api的方式使用DeepSeek,但是实际只能请求少数几次就不再能给回响应。这回我干脆用最原生的方法Postman调用接口请求好了。 1. 通过下载安装Postman软件 postman下载(https://pan.quark.cn/s/c8d1c7d526f3),包含7.0和10…...

K8S下载离线安装包所需文件
下载相关文件 官网下载地址集合https://kubernetes.io/zh-cn/releases/download/ 下载相关镜像 官网镜像描述 所有 Kubernetes 容器镜像都被部署到 registry.k8s.io 容器镜像仓库。 容器镜像支持架构registry.k8s.io/kube-apiserver:v1.32.0amd64, arm, arm64, ppc64le, …...
探索Hugging Face:开源AI社区的核心工具与应用实践
引言:AI民主化的先锋 在自然语言处理(NLP)领域,Hugging Face已成为开源社区的代名词。这个成立于2016年的平台,通过提供易用的工具和丰富的预训练模型库,彻底改变了开发者使用和部署AI模型的方式。截至202…...

【操作系统】深入理解Linux物理内存
物理内存的组织结构 我们平时所称的内存也叫随机访问存储器也叫 RAM 。RAM 分为两类: 一类是静态 RAM( SRAM ),这类 SRAM 用于 CPU 高速缓存 L1Cache,L2Cache,L3Cache。其特点是访问速度快,访…...
npm 私服使用介绍
一、导读 本文主要介绍 npm 私服的使用,至于 npm 私服搭建的过程,可以看本人之前的文章《Docker 部署 verdaccio 搭建 npm 私服》 二、前置条件 npm私服地址:http://xxx.xxx.xxx.xxx:port/ 三、本地 npm 源切换 使用nrm,可以方…...

安全筑基,智能赋能:BeeWorks IM引领企业协同新纪元
在数字经济高速发展的今天,企业通讯系统已从单纯的信息传递工具演变为支撑业务创新的核心平台。传统通讯工具在安全性、智能化、协同性等方面的不足,严重制约着企业的数字化转型进程。BeeWorks IM系统以其创新的技术架构和智能化功能,正在重新…...

水务+AI应用探索(一)| FastGPT+DeepSeek 本地部署
在当下的科技浪潮中,AI 无疑是最炙手可热的焦点之一,其强大的能力催生出了丰富多样的应用场景,广泛渗透到各个行业领域。对于水务行业而言,AI 的潜力同样不可估量。为了深入探究 AI 在水务领域的实际应用成效,切实掌握…...

[JVM篇]垃圾回收器
垃圾回收器 Serial Seral Old PartNew CMS(Concurrent Mark Sweep) Parallel Scavenge Parallel Old G1 ZGC...
SQL Server:查看当前连接数和最大连接数
目录标题 **1. 查看当前连接数****使用系统视图****使用动态管理视图** **2. 查看最大连接数****通过配置选项****通过服务器属性** **3. 查看连接数的实时变化****4. 设置最大连接数****5. 查看连接的详细信息****6. 使用 SQL Server Management Studio (SSMS)****7. 使用 SQL…...

DeepSeek应用——与PyCharm的配套使用
目录 一、配置方法 二、使用方法 三、注意事项 1、插件市场无continue插件 2、无结果返回,且在本地模型报错 记录自己学习应用DeepSeek的过程,使用的是自己电脑本地部署的私有化蒸馏模型...... (举一反三,这个不单单是可以用…...

【第15章:量子深度学习与未来趋势—15.3 量子深度学习在图像处理、自然语言处理等领域的应用潜力分析】
一、开篇:为什么我们需要关注这场"量子+AI"的世纪联姻? 各位技术爱好者们,今天我们要聊的这个话题,可能是未来十年最值得押注的技术革命——量子深度学习。这不是简单的"1+1=2"的物理叠加,而是一场可能彻底改写AI发展轨迹的范式转移。 想象这样一个…...

多模态基础模型训练笔记-第一篇InternVL-g
一、TL;DR 将之前所有训练过的大模型的过程都总结和回忆一下,遇到的坑别忘了 二、问题记录 还是注意镜像的选择,选择社区最火的镜像,然后下载好对应的数据,主要显卡的选择,这个时候4090已经带不动了&…...

MyBatis:动态SQL高级标签使用方法指南
一、引言 目前互联网大厂在搭建后端Java服务时,常使用Springboot搭配Mybatis/Mybatis-plus的框架。Mybatis/Mybatis-plus之所以能成为当前国内主流的持久层框架,与其本身的优点有关:支持定制动态 SQL、存储过程及高级映射,简化数…...

使用grafana v11 建立k线(蜡烛图)仪表板
先看实现的结果 沪铜主力合约 2025-02-12 的1分钟k线图 功能介绍: 左上角支持切换主力合约,日期,实现动态加载数据. 项目背景: 我想通过前端展示期货指定品种某1天的1分钟k线,类似tqsdk 的web_gui 生成图形化界面— TianQin Python SDK 3.7.8 文档 项目架构: 后端: fastap…...
React hook之useRef
React useRef 详解 useRef 是 React 提供的一个 Hook,用于在函数组件中创建可变的引用对象。它在 React 开发中有多种重要用途,下面我将全面详细地介绍它的特性和用法。 基本概念 1. 创建 ref const refContainer useRef(initialValue);initialValu…...
IGP(Interior Gateway Protocol,内部网关协议)
IGP(Interior Gateway Protocol,内部网关协议) 是一种用于在一个自治系统(AS)内部传递路由信息的路由协议,主要用于在一个组织或机构的内部网络中决定数据包的最佳路径。与用于自治系统之间通信的 EGP&…...
在四层代理中还原真实客户端ngx_stream_realip_module
一、模块原理与价值 PROXY Protocol 回溯 第三方负载均衡(如 HAProxy、AWS NLB、阿里 SLB)发起上游连接时,将真实客户端 IP/Port 写入 PROXY Protocol v1/v2 头。Stream 层接收到头部后,ngx_stream_realip_module 从中提取原始信息…...

《通信之道——从微积分到 5G》读书总结
第1章 绪 论 1.1 这是一本什么样的书 通信技术,说到底就是数学。 那些最基础、最本质的部分。 1.2 什么是通信 通信 发送方 接收方 承载信息的信号 解调出其中承载的信息 信息在发送方那里被加工成信号(调制) 把信息从信号中抽取出来&am…...
【算法训练营Day07】字符串part1
文章目录 反转字符串反转字符串II替换数字 反转字符串 题目链接:344. 反转字符串 双指针法,两个指针的元素直接调转即可 class Solution {public void reverseString(char[] s) {int head 0;int end s.length - 1;while(head < end) {char temp …...
数据库分批入库
今天在工作中,遇到一个问题,就是分批查询的时候,由于批次过大导致出现了一些问题,一下是问题描述和解决方案: 示例: // 假设已有数据列表 dataList 和 PreparedStatement pstmt int batchSize 1000; // …...

华为云Flexus+DeepSeek征文|DeepSeek-V3/R1 商用服务开通全流程与本地部署搭建
华为云FlexusDeepSeek征文|DeepSeek-V3/R1 商用服务开通全流程与本地部署搭建 前言 如今大模型其性能出色,华为云 ModelArts Studio_MaaS大模型即服务平台华为云内置了大模型,能助力我们轻松驾驭 DeepSeek-V3/R1,本文中将分享如何…...
浅谈不同二分算法的查找情况
二分算法原理比较简单,但是实际的算法模板却有很多,这一切都源于二分查找问题中的复杂情况和二分算法的边界处理,以下是博主对一些二分算法查找的情况分析。 需要说明的是,以下二分算法都是基于有序序列为升序有序的情况…...

AI书签管理工具开发全记录(十九):嵌入资源处理
1.前言 📝 在上一篇文章中,我们完成了书签的导入导出功能。本篇文章我们研究如何处理嵌入资源,方便后续将资源打包到一个可执行文件中。 2.embed介绍 🎯 Go 1.16 引入了革命性的 embed 包,彻底改变了静态资源管理的…...

安宝特方案丨船舶智造的“AR+AI+作业标准化管理解决方案”(装配)
船舶制造装配管理现状:装配工作依赖人工经验,装配工人凭借长期实践积累的操作技巧完成零部件组装。企业通常制定了装配作业指导书,但在实际执行中,工人对指导书的理解和遵循程度参差不齐。 船舶装配过程中的挑战与需求 挑战 (1…...