vue3.0+星火大模型API
先讯飞注册申请到APPID 、APISecret 、APIKey。先安装依赖 pnpm install crypto-js。这里用得是vue+element plus 测试。最终效果,样式有点丑哈,可以根据需求修改哦。
·
先讯飞注册申请到APPID 、APISecret 、APIKey
查看配置
报错返回码查看
这里用得是vue+element plus 测试
先安装依赖 pnpm install crypto-js
<script setup lang="ts">
import CryptoJs from "crypto-js";
import { ref, unref } from "vue";
import { ElMessage } from "element-plus";
// 请填写讯飞申请资料
const GLOBAL_SPARK_AI_APPID = "";
const GLOBAL_SPARK_AI_APISECRET = "";
const GLOBAL_SPARK_AI_APIKEY = "";
/**
* 大模型版本对照表
* 官方文档参考:https://www.xfyun.cn/doc/spark/Web.html#_1-%E6%8E%A5%E5%8F%A3%E8%AF%B4%E6%98%8E
* Spark4.0 Ultra 请求地址: domain:4.0Ultra version:v4.0/chat
* Spark Max 请求地址: domain:generalv3.5 version:v3.5/chat
* Spark Pro-128K 请求地址: domain:pro-128k version:chat/pro-128k
* Spark Pro 请求地址: domain:generalv3 version:v3.1/chat
* Spark V2.0 请求地址: domain:generalv2 version:v2.1/chat
* Spark Lite请求地址: domain:general version:v1.1/chat
*/
const GLOBAL_VERSION = "/v4.0/chat"; //目前用得是Spark4.0 Ultra 或者根据需要看文档改
interface Message {
role: "user" | "assistant";
content: string;
}
const messages = ref<Message[]>([]);
const isLoading = ref(false);
const sMsg = ref("");
const getWebsocketUrl = () => {
let url = "wss://spark-api.xf-yun.com" + GLOBAL_VERSION;
const host = "spark-api.xf-yun.com";
const apiKeyName = "api_key";
const date = new Date().toGMTString();
const algorithm = "hmac-sha256";
const headers = "host date request-line";
let signatureOrigin = `host: ${host}\ndate: ${date}\nGET ${GLOBAL_VERSION} HTTP/1.1`;
let signatureSha = CryptoJs.HmacSHA256(
signatureOrigin,
GLOBAL_SPARK_AI_APISECRET
);
let signature = CryptoJs.enc.Base64.stringify(signatureSha);
let authorizationOrigin = `${apiKeyName}="${GLOBAL_SPARK_AI_APIKEY}", algorithm="${algorithm}", headers="${headers}", signature="${signature}"`;
let authorization = btoa(authorizationOrigin);
return `${url}?authorization=${authorization}&date=${encodeURI(
date
)}&host=${host}`;
};
const sendMsg = () => {
if (unref(isLoading)) {
ElMessage.warning("加载中...");
return;
}
if (!unref(sMsg)) {
ElMessage.warning("请输入内容");
return;
}
// 添加用户消息
messages.value.push({
role: "user",
content: unref(sMsg),
});
const socket = new WebSocket(getWebsocketUrl());
let aiResponse = "";
socket.addEventListener("open", () => {
isLoading.value = true;
const params = {
header: {
app_id: GLOBAL_SPARK_AI_APPID,
uid: "讯飞ai测试",
},
parameter: {
chat: {
domain: "4.0Ultra",
temperature: 0.5,
max_tokens: 1024,
},
},
payload: {
message: {
text: [
...messages.value.map((msg) => ({
role: msg.role,
content: msg.content,
})),
],
},
},
};
socket.send(JSON.stringify(params));
});
socket.addEventListener("message", ({ data }) => {
const response = JSON.parse(data);
aiResponse += response.payload.choices.text[0].content;
// 更新或添加AI响应消息
const lastMessage = messages.value[messages.value.length - 1];
if (lastMessage.role === "assistant") {
lastMessage.content = aiResponse;
} else {
messages.value.push({
role: "assistant",
content: aiResponse,
});
}
});
socket.addEventListener("close", () => {
isLoading.value = false;
sMsg.value = "";
});
socket.addEventListener("error", (error) => {
isLoading.value = false;
ElMessage.error("连接出错,请重试");
console.error("WebSocket error:", error);
});
};
</script>
<template>
<div class="container">
<div class="chat-content">
<div
v-for="(msg, index) in messages"
:key="index"
:class="['msg', msg.role === 'user' ? 'msg-user' : 'msg-ai']"
>
<div class="msg-content" v-html="msg.content"></div>
</div>
<div v-if="isLoading" class="loading-indicator">
<span>AI思考中</span>
<span class="dots">...</span>
</div>
</div>
<div class="ask-content">
<el-input
type="textarea"
v-model="sMsg"
:disabled="isLoading"
placeholder="请输入您的问题"
@keyup.enter.ctrl="sendMsg"
/>
<div class="opt">
<el-button
type="primary"
size="large"
@click="sendMsg"
:loading="isLoading"
>发送</el-button
>
</div>
</div>
</div>
</template>
<style lang="scss" scoped>
.container {
height: 100%;
display: flex;
flex-direction: column;
background: #f2f2f2;
.chat-content {
flex: 1;
overflow-y: auto;
padding: 20px;
.msg {
margin-bottom: 20px;
max-width: 80%;
&.msg-user {
margin-left: auto;
text-align: right;
.msg-content {
background: #007aff;
color: white;
border-radius: 15px 15px 0 15px;
}
}
&.msg-ai {
margin-right: auto;
text-align: left;
.msg-content {
background: white;
border-radius: 15px 15px 15px 0;
}
}
.msg-content {
padding: 12px 16px;
display: inline-block;
word-break: break-word;
}
}
.loading-indicator {
text-align: center;
color: #666;
padding: 10px;
.dots {
display: inline-block;
animation: dotAnimation 1.5s infinite;
}
}
}
.ask-content {
background: white;
border-top: 1px solid #eee;
padding: 16px;
position: relative;
:deep(.el-textarea__inner) {
border: none;
background: #f5f5f5;
padding: 12px;
min-height: 80px !important;
resize: none;
&:focus {
box-shadow: none;
}
}
.opt {
text-align: right;
margin-top: 8px;
}
}
}
@keyframes dotAnimation {
0%,
20% {
content: ".";
}
40% {
content: "..";
}
60% {
content: "...";
}
80%,
100% {
content: "";
}
}
</style>
最终效果,样式有点丑哈,可以根据需求修改哦
更多推荐
所有评论(0)