[Vue3+TS] DeepSeek聊天对接demo

 前言

        本项目主要采用 Vue3 + TypeScript + Vite 构建

        项目主要依赖:Element-Plus + mitt + Scss + base-64 + crypto-js + markdown-it

介绍        

        本项目主要用来学习对接DeepSeek,进行简单的 Web 端页面搭建及基本使用,该 demo 项目具备基本的聊天对话功能,可通过左侧选择不同的助手在右侧窗口进行对话,可供实际开发参考使用。

        本项目是基于原有项目[Vue3+TS]讯飞星火认知大模型搭建demo的基础上进行的新增,调整了文件结构,除了一些样式和微小改动以外,讯飞星火大模型相关的部分代码基本没有变化,可以只专注于DeepSeek相关部分的代码进行查看。

项目链接

        Github仓库地址:GitHub - xkYZI/BigModel-demo

主要代码

1、大模型KEY配置

/**
 * 讯飞星火大模型的Key信息
 */
export const SparkAPPID = "你的SparkAPPID";
export const SparkAPIKey = "你的SparkAPIKey ";
export const SparkAPISecret = "你的SparkAPISecret ";

/**
 * DeepSeek的Key信息
 */
export const DSKey = '你的DeepSeekKey';

2、对接DeepSeek,开启通信

import { AIType, HistoryType } from '@/types';
import OpenAI from 'openai'
import { ChatCompletionMessageParam } from 'openai/resources/chat/completions.mjs';
import { ref } from 'vue';
import { emitter } from '../utils/emitter';
import { decodeUnicode } from "@/utils/markdownUtils"
import { DSKey } from "@/config/authKey"

const historyList = ref([] as HistoryType[]);

const messageList = ref([] as ChatCompletionMessageParam[]);

const resoning_content = ref("");
const content = ref("");

const inputValue = ref("");

const AI = ref({} as AIType);
const isLoading = ref(false);
// const ModelVersion = ref("deepseek-reasoner");
const ModelVersion = ref("deepseek-chat");

const constant = new OpenAI({
    baseURL: 'https://api.deepseek.com',
    apiKey: DSKey,
    dangerouslyAllowBrowser: true
});

//初始化
const init = () => {
    historyList.value = [];
    messageList.value = [];
    messageList.value.push({
        role: "system",
        content: AI.value.roleDefinitionStatement,
    });
}

//根据请求地址和Key发送请求
//流式返回响应
const doSend = async (message: ChatCompletionMessageParam) => {
    messageList.value.push(message);
    historyList.value.push({
        role: "user",
        content: message.content,
        resoning_content: resoning_content.value,
        time: new Date().toLocaleString()
    })
    isLoading.value = true;
    return await constant.chat.completions.create({
        messages: messageList.value,
        model: ModelVersion.value,
        // model: "deepseek-chat",
        stream: true,
    });
}
//重新刷新发送
const doReSend = async () => {
    if (messageList.value[messageList.value.length - 1].role == "user")
        return;
    messageList.value.pop();
    historyList.value.pop();
    isLoading.value = true;
    emitter.emit("ing");
    emitter.emit("scrollToBottom");
    await constant.chat.completions.create({
        messages: messageList.value,
        model: ModelVersion.value,
        stream: true,
    }).then(async (response: any) => {
        resoning_content.value = "";
        content.value = "";
        for await (const chunk of response) {
            emitter.emit("scrollToBottom");
            if (chunk.choices[0].delta.reasoning_content) {
                resoning_content.value += decodeUnicode(chunk.choices[0].delta.reasoning_content);
                emitter.emit("scrollToBottom")
            }
            else if (chunk.choices[0].delta.content) {
                content.value += decodeUnicode(chunk.choices[0].delta.content);
                emitter.emit("scrollToBottom")
            }
        }
        afterDo();
    });
}
const afterDo = () => {
    if (content.value == "") {
        content.value = "当前API接口提供方服务器繁忙,请稍后再试。"
    }
    messageList.value.push({
        role: "assistant",
        content: content.value,
    });
    historyList.value.push({
        role: "assistant",
        content: content.value,
        resoning_content: resoning_content.value,
        time: new Date().toLocaleString()
    });
    content.value = "";
    resoning_content.value = "";
    isLoading.value = false;
    emitter.emit("ing");
}

//处理页面发送操作
const send = () => {
    let msg = {
        role: "user",
        content: inputValue.value,
    } as ChatCompletionMessageParam;
    emitter.emit("ing");
    emitter.emit("scrollToBottom");
    inputValue.value = "";
    doSend(msg).then(async (response: any) => {
        resoning_content.value = "";
        content.value = "";
        console.log(response);
        for await (const chunk of response) {
            emitter.emit("scrollToBottom");
            if (chunk.choices[0].delta.reasoning_content) {
                resoning_content.value += decodeUnicode(chunk.choices[0].delta.reasoning_content);
                emitter.emit("scrollToBottom")
            }
            else if (chunk.choices[0].delta.content) {
                content.value += decodeUnicode(chunk.choices[0].delta.content);
                emitter.emit("scrollToBottom")
            }
        }
        afterDo();
    })
}

function resetHistory() {
    historyList.value = [];
    messageList.value = [];
    ElMessage.success("历史记录已清空");
}


export {
    messageList,
    historyList,
    AI,
    send,
    init,
    inputValue,
    content,
    resoning_content,
    resetHistory,
    isLoading,
    ModelVersion,
    doReSend
}

3、完善markDown文本展示

import MarkdownIt from "markdown-it";
// 解码包含Unicode转义序列的字符串
export function decodeUnicode(str: any) {
    return str.replace(/\\u[\dA-Fa-f]{4}/g, function (match: any) {
        return String.fromCharCode(parseInt(match.substr(2), 16));
    });
}
// script标签中
// 引入代码高亮
import hljs from "highlight.js";
//  这个是高亮的样式,有很多,我选了这个
import "highlight.js/styles/ir-black.css";


export const md = new MarkdownIt({
    highlight: function (str: string, lang: string): string {
        const language = hljs.getLanguage(lang);
        if (language) {
            try {
                return `<div class="hljs" style="position:relative;background-color:rgba(40, 44, 52,.85);
                        border-radius: .375rem;"><small style="background:rgba(0,0,0,.3);
                        position:absolute;top:0;right:0;border-bottom-left-radius: .375rem;
                        padding-left: .5rem;padding-right: .5rem;padding-top: .25rem;padding-bottom: .25rem;
                        font-size: .75rem;text-transform: uppercase;
                        line-height: 1rem;">${lang}</small><div style="padding:0.6rem"><code class="language-html">${hljs.highlight(lang, str, true).value
                    }</code></div></div>`;
            } catch (error) {
                console.error(error);
            }
        }
        // 如果未指定语言或无法识别语言,则使用默认的逃逸 HTML 处理
        return `<div class="hljs" style="position:relative;background-color:rgba(40, 44, 52,.85);
                        border-radius: .375rem;"><small style="background:rgba(0,0,0,.3);
                        position:absolute;top:0;right:0;border-bottom-left-radius: .375rem;
                        padding-left: .5rem;padding-right: .5rem;padding-top: .25rem;padding-bottom: .25rem;
                        font-size: .75rem;text-transform: uppercase;
                        line-height: 1rem;">${lang}</small><div style="padding:0.6rem"><code class="language-html">${md.utils.escapeHtml(str)}</code></div></div>`;
        // return `<div class="hl-code"><div class="hljs"><code>${md.utils.escapeHtml(
        //     str
        // )}</code></div></div>`;
    },
});

4、页面样例

运行方式

1、从GitHub项目链接下载 BigModelDemo v1.0

2、在vscode或其他编译器中打开本项目。

3、新建/打开终端。

4、首次启动 需加载项目所需依赖。

npm install

5、启动项目。

npm run dev

注意

        本项目仅供学习交流使用,不得擅自售卖谋利!

结束

        本人还只是一个编程的小萌新,好多地方做的也不是很好,在毕业设计里加入AI对话也是临时起意边学边写的,可能这边看上个方法就直接去改动尝试了,所以导致讯飞星火大模型相关部分的编码很乱。这次DeepSeek的部分我尽量将代码编写方式调整了一些,但介于项目的前端底子还是原有项目,所以可能还是有些乱,请大家见谅!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值