接入语音
This commit is contained in:
@@ -1,6 +1,5 @@
|
||||
<template>
|
||||
<meta-human/>
|
||||
<chat class="fill mar-l8" :config="setting" :showSettings="showSettings"
|
||||
<chat class="fill" :config="setting" :showSettings="showSettings"
|
||||
@setting="showSettings=!showSettings"/>
|
||||
<settings v-show="showSettings" v-model="setting"/>
|
||||
</template>
|
||||
@@ -8,12 +7,11 @@
|
||||
<script>
|
||||
import Chat from "./components/chat";
|
||||
import Settings from "./components/settings";
|
||||
import {Alpaca, ChatGLM, ChatGPT} from "./utils/models";
|
||||
import MetaHuman from "./components/metaHuman.vue";
|
||||
import {ChatGLM} from "./utils/models";
|
||||
|
||||
export default {
|
||||
name: 'App',
|
||||
components: {MetaHuman, Settings, Chat},
|
||||
components: {Settings, Chat},
|
||||
data() {
|
||||
return {
|
||||
showSettings: false,
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<template>
|
||||
<section class="chat">
|
||||
<el-row align="middle mar-b16">
|
||||
<el-row align="middle" class="mar-b16">
|
||||
<ai-model :model="config.model"/>
|
||||
<el-row justify="end" class="fill mar-l8 gap-8">
|
||||
<!--聊天操作栏-->
|
||||
@@ -9,7 +9,10 @@
|
||||
</el-row>
|
||||
</el-row>
|
||||
<thinking-bar v-show="loading"/>
|
||||
<chat-content class="fill" :list="chatHistory" ref="chatContent"/>
|
||||
<el-row class="fill">
|
||||
<meta-human :voice="speech"/>
|
||||
<chat-content class="fill" :list="chatHistory" ref="chatContent"/>
|
||||
</el-row>
|
||||
<el-row class="mar-t8" align="middle">
|
||||
<chat-input class="fill mar-r8" v-model="inputText" @enter="handleSend"/>
|
||||
<el-button type="primary" @click="handleSend">发送</el-button>
|
||||
@@ -24,10 +27,11 @@ import ChatInput from "../components/chatInput";
|
||||
import ThinkingBar from "../components/thinkingBar";
|
||||
import AiModel from "../ui/AiModel";
|
||||
import {USER_AVATAR} from "../utils/env";
|
||||
import MetaHuman from "./metaHuman.vue";
|
||||
|
||||
export default {
|
||||
name: "chat",
|
||||
components: {AiModel, ChatContent, ChatInput, ThinkingBar},
|
||||
components: {MetaHuman, AiModel, ChatContent, ChatInput, ThinkingBar},
|
||||
props: {
|
||||
config: {default: () => ({})}
|
||||
},
|
||||
@@ -35,7 +39,8 @@ export default {
|
||||
return {
|
||||
loading: false,
|
||||
inputText: "",
|
||||
chatHistory: []
|
||||
chatHistory: [],
|
||||
speech: ""
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
@@ -65,9 +70,10 @@ export default {
|
||||
}
|
||||
this.chatHistory.push(aiMsg)
|
||||
if (this.config.stream) {
|
||||
ai.chatStream(this.chatHistory).then(reader => ai.streamOutput(reader, this.chatHistory.at(-1))).finally(this.handleAfterSend)
|
||||
ai.chatStream(this.chatHistory).then(reader => ai.streamOutput(reader, this.chatHistory.at(-1), v => this.speech = v)).finally(this.handleAfterSend)
|
||||
} else {
|
||||
ai.chat(this.chatHistory).then(reply => {
|
||||
this.speech = reply
|
||||
const decodeArr = reply.split("")
|
||||
decodeArr.forEach(e => this.chatHistory.at(-1).msg += e)
|
||||
}).finally(this.handleAfterSend)
|
||||
|
||||
@@ -1,28 +1,36 @@
|
||||
<script>
|
||||
import * as PIXI from "pixi.js";
|
||||
import {load} from "../utils/tools.js";
|
||||
import {load, throttle} from "../utils/tools.js";
|
||||
import {Live2DModel} from "pixi-live2d-display";
|
||||
|
||||
window.PIXI = PIXI;
|
||||
|
||||
export default {
|
||||
name: "metaHuman",
|
||||
props: {
|
||||
voice: String
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
ins: null
|
||||
ins: null,
|
||||
speech: null
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
voice(v) {
|
||||
v && this.speak(v)
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
initLive2d() {
|
||||
load(window.Live2D && window.Live2DCubismCore)
|
||||
return load(window.Live2D && window.Live2DCubismCore)
|
||||
.then(() => Live2DModel.from("/live2d/ots14_1203/ots14_1203.model.json"))
|
||||
.then(model => {
|
||||
this.ins.stage.addChild(model)
|
||||
|
||||
model.rotation = Math.PI;
|
||||
model.skew.x = Math.PI;
|
||||
model.x = 400
|
||||
model.y = 200
|
||||
model.x = 380
|
||||
model.y = 100
|
||||
model.scale.set(0.16, 0.16);
|
||||
// interaction
|
||||
model.on('hit', (hitAreas) => {
|
||||
@@ -48,6 +56,17 @@ export default {
|
||||
});
|
||||
model.on("pointerupoutside", () => (model.dragging = false));
|
||||
model.on("pointerup", () => (model.dragging = false));
|
||||
},
|
||||
speak(text) {
|
||||
if (!this.speech) {
|
||||
this.speech = new SpeechSynthesisUtterance()
|
||||
}
|
||||
const synth = speechSynthesis
|
||||
throttle(() => {
|
||||
this.speech.text = text
|
||||
// this.speech.voice = speechSynthesis.getVoices()[13]
|
||||
load(!synth.speaking).then(() => synth.speak(this.speech))
|
||||
})
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
@@ -58,15 +77,14 @@ export default {
|
||||
backgroundAlpha: 0,
|
||||
})
|
||||
this.$el.appendChild(this.ins.view)
|
||||
this.initLive2d()
|
||||
this.initLive2d().then(() => this.speak("大家好,我是AI小助手"))
|
||||
})
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<section class="metaHuman">
|
||||
</section>
|
||||
<section class="metaHuman"/>
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss">
|
||||
|
||||
@@ -198,7 +198,7 @@ export class ChatGLM extends BaseModel {
|
||||
} else return Promise.reject("没有权限或者网络异常,请重新尝试!")
|
||||
}
|
||||
|
||||
streamOutput(reader, chat) {
|
||||
streamOutput(reader, chat, cb) {
|
||||
return reader.read().then(({done, value}) => {
|
||||
if (done) {
|
||||
return;
|
||||
@@ -206,8 +206,13 @@ export class ChatGLM extends BaseModel {
|
||||
const decode = new TextDecoder().decode(value)
|
||||
const dialogue = decode.split("event:").at(-1)
|
||||
const msg = dialogue.split("\n").filter(e => e.startsWith("data:"))?.map(e => e.replace("data:", '')).join("\n")
|
||||
if (msg?.length > 0) chat.msg = msg
|
||||
return this.streamOutput(reader, chat)
|
||||
if (msg?.length > 0) {
|
||||
chat.msg = msg
|
||||
if (typeof cb == "function") {
|
||||
cb(msg)
|
||||
}
|
||||
}
|
||||
return this.streamOutput(reader, chat, cb)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -43,3 +43,9 @@ export const addJs = url => {
|
||||
script.src = url
|
||||
document.body.appendChild(script)
|
||||
}
|
||||
|
||||
export const throttle = (fn, delay = 1000) => {
|
||||
if (typeof fn != "function") return;
|
||||
if (window.throttleTimer) clearTimeout(window.throttleTimer)
|
||||
window.throttleTimer = setTimeout(() => fn.call(), delay)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user