当前项目技术栈:服务端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.实现效果

 内容有误的地方欢迎指正,以免误导他人。

Logo

欢迎加入DeepSeek 技术社区。在这里,你可以找到志同道合的朋友,共同探索AI技术的奥秘。

更多推荐