
使用DeepSeek官网提供api实现本地项目聊天问答功能
当前项目技术栈:服务端nodejs express socket 前端react ant-design。
·
当前项目技术栈:服务端nodejs express socket 前端react ant-design
1.首先根据deepseek官网
服务端安装依赖 npm install openai
2.官网提供的代码
import OpenAI from "openai";
const openai = new OpenAI({
baseURL: 'https://api.deepseek.com',
apiKey: '<DeepSeek API Key>'
});
async function main() {
const completion = await openai.chat.completions.create({
messages: [{ role: "system", content: "You are a helpful assistant." }],
model: "deepseek-chat",
});
console.log(completion.choices[0].message.content);
}
main();
baseURL | 路径(https://api.deepseek.com) |
api_key | 密钥 (你的key) |
messages | 消息多轮对话 ([ {"role": "user", "content": "What's the highest mountain in the world?"}, {"role": "assistant", "content": "The highest mountain in the world is Mount Everest."}, {"role": "user", "content": "What is the second?"} ]) |
model | 模型 (deepseek-chat,deepseek-reasoner) |
stream | 输出方式(非流式输出,流式输出) |
3.为什么我的项目要使用流式输出
刚开始我的项目使用的是非流式输出的方式,服务端获取到deepseek数据后通过接口响应给前端但我发现非流式输出获取deepseek数据获取数据比较慢,所以采用了流式输出的方式获取deepseek数据,但这样就不能继续使用接口请求的方式获取数据了所以我采用了socket来将获取到的deepseek数据响应给前端。
4.结合express+deepseek+socket
express安装依赖npm install socket.io
react安装依赖npm install socket.io-client
express代码
创建socketServer.js文件代码如下:
const OpenAI = require('openai');
const { Server } = require('socket.io');
const JWT = require('../utils/JWT');
const openai = new OpenAI({
baseURL: process.env.OPENAI_BASE_URL,
apiKey: process.env.OPENAI_API_KEY
});
const setupSocketServer = (server) => {
const io = new Server(server, {
cors: {
origin: 'http://localhost:5173', // 允许的来源
methods: ['GET', 'POST'],
credentials: true // 如果需要发送 cookies 或认证信息,设置为 true
}
});
io.use(async(socket, next) => {
const token = socket.handshake.query.token; // 假设客户端通过查询参数传递 token
if (token) {
try {
// 验证 token,这里需要配置你的 secret 和 options
await JWT.verify(token);
next(); // 通过验证,继续处理连接
} catch (err) {
next(new Error('Authentication error')); // 验证失败,拒绝连接
}
} else {
next(new Error('Token is required')); // 没有提供 token,拒绝连接
}
});
io.on('connection', (socket) => {
socket.on('message', async (messages) => {
try {
console.log('Received messages:', messages);
const completion = await openai.chat.completions.create({
messages: messages,
model: "deepseek-chat",
stream: true
});
console.log('Received completion:', completion);
const iterator = completion.iterator();
console.log('Received iterator:', iterator);
for await (const chunk of iterator) {
console.log('Received chunk:', chunk);
socket.emit('message', chunk);
}
} catch (error) {
console.error('Error during OpenAI API call:', error);
socket.emit('error', { message: 'An error occurred while processing your request.', details: error.message });
}
});
socket.on('disconnect', () => {
console.log('user disconnected');
});
});
return io;
};
module.exports = setupSocketServer;
导出io在express www 文件中引用 www文件代码:
#!/usr/bin/env node
/**
* Module dependencies.
*/
var app = require('../app');
var debug = require('debug')('server:server');
var http = require('http');
require('../models/db.config');
/**
* Get port from environment and store in Express.
*/
var port = normalizePort(process.env.PORT || '3000');
app.set('port', port);
/**
* Create HTTP server.
*/
var server = http.createServer(app);
/**
* Setup Socket.IO server
*/
const io = require('../services/socketServer')(server); // 确保路径正确
/**
* Listen on provided port, on all network interfaces.
*/
server.listen(port);
server.on('error', onError);
server.on('listening', onListening);
/**
* Normalize a port into a number, string, or false.
*/
function normalizePort(val) {
var port = parseInt(val, 10);
if (isNaN(port)) {
// named pipe
return val;
}
if (port >= 0) {
// port number
return port;
}
return false;
}
/**
* Event listener for HTTP server "error" event.
*/
function onError(error) {
if (error.syscall !== 'listen') {
throw error;
}
var bind = typeof port === 'string'
? 'Pipe ' + port
: 'Port ' + port;
// handle specific listen errors with friendly messages
switch (error.code) {
case 'EACCES':
console.error(bind + ' requires elevated privileges');
process.exit(1);
break;
case 'EADDRINUSE':
console.error(bind + ' is already in use');
process.exit(1);
break;
default:
throw error;
}
}
/**
* Event listener for HTTP server "listening" event.
*/
function onListening() {
var addr = server.address();
var bind = typeof addr === 'string'
? 'pipe ' + addr
: 'port ' + addr.port;
debug('Listening on ' + bind);
}
5.前端react项目代码
chat.jsx代码:
import { useState, useEffect, } from 'react';
import { Input, Button, List } from 'antd';
import { AliwangwangOutlined, UserOutlined } from '@ant-design/icons';
import io from 'socket.io-client';
import { useSelector } from 'react-redux';
import { getToken } from '../../utils/getToken';
const token = getToken();
const socket = io('ws://localhost:3000', {
withCredentials: true, // 如果需要发送 cookies 或认证信息,设置为 true
query: {token: token}
}); // 确保与后端 WebSocket 服务器地址一致
const Chat = () => {
const [loading, setLoading] = useState(false);
const userInfo = useSelector((state) => state.user.userInfo);
const [error, setError] = useState(null);
const [inputValue, setInputValue] = useState('');
const [messages, setMessages] = useState([
{
role: 'assistant',
content: 'deepseek bot 欢迎您!请问有什么可以帮助您的吗?'
}
]);
useEffect(() => {
socket.on('connect', () => {
console.log('WebSocket connection established');
});
socket.on('connect_error', (err) => {
console.error('WebSocket connection error:', err);
setError('WebSocket connection error');
});
socket.on('message', (chunk) => {
if (chunk.choices && chunk.choices[0] && chunk.choices[0].delta) {
const delta = chunk.choices[0].delta;
if (delta.content) {
setMessages((prevMessages) => {
let lastMessage = prevMessages[prevMessages.length - 1];
if( lastMessage.role === 'system' ){
lastMessage= {role: 'assistant', content: ''}
return [
...prevMessages,
{
...lastMessage,
content: lastMessage.content + delta.content
}
];
}else {
lastMessage = prevMessages[prevMessages.length - 1];
return [
...prevMessages.slice(0, -1),
{
...lastMessage,
content: lastMessage.content + delta.content
}
];
}
});
}
if (chunk.choices[0].finish_reason === 'stop') {
setLoading(false);
}
} else {
console.warn('Received non-message data:', chunk);
}
});
socket.on('error', (err) => {
console.error('WebSocket error:', err);
setError(err.message);
setLoading(false);
});
return () => {
socket.off('connect');
socket.off('connect_error');
socket.off('message');
socket.off('error');
socket.disconnect();
};
}, [socket]);
const handleInputChange = (e) => {
setInputValue(e.target.value);
};
const handleSubmit = () => {
if (!inputValue) return;
// 添加用户消息
const newMessage = { role: 'system', content: inputValue.trim() };
setMessages((prevMessages) => [
...prevMessages,
newMessage
]);
setInputValue('');
setLoading(true);
socket.emit('message', [...messages,newMessage]); // 确保只发送新消息
};
return (
<div style={{ display: 'flex', flexDirection: 'column', height: '100%' }}>
<List style={{ flex: 1, overflow: 'auto' }}
dataSource={messages}
split={false}
renderItem={(item) => (
<List.Item>
<List.Item.Meta
avatar={item.role === 'assistant' ? <AliwangwangOutlined style={{ color: '#4096ff' }} /> : <UserOutlined />}
title={item.role === 'assistant' ? 'DeepSeek Bot' : userInfo.username}
description={item.content}
/>
</List.Item>
)}
/>
{loading && <p>正在处理中...</p>}
{error && <p>{error}</p>}
<div style={{ display: 'flex', alignItems: 'center', marginTop: '16px' }}>
<Input
placeholder="请输入您的问题"
value={inputValue}
onChange={handleInputChange}
style={{ flex: 1, marginRight: '8px' }}
/>
<Button type="primary" onClick={handleSubmit}>
发送
</Button>
</div>
</div>
);
};
export default Chat;
6.实现效果
内容有误的地方欢迎指正,以免误导他人。
更多推荐
所有评论(0)