|
@@ -1,16 +1,17 @@
|
|
<template>
|
|
<template>
|
|
<view-full-screen :is-full-screen="true" class="right">
|
|
<view-full-screen :is-full-screen="true" class="right">
|
|
<div :class="bodyClass">
|
|
<div :class="bodyClass">
|
|
- <div ref="scrollView" :style="{ 'overflow-y': 'scroll', 'height': 'calc(100% - ' + (52 + safeAreaInsets.bottom) + 'px)' }">
|
|
|
|
|
|
+ <div ref="scrollView" class="chatBubbleContainer" :style="{ 'height': 'calc(100% - ' + (52 + safeAreaInsets.bottom) + 'px)' }">
|
|
|
|
+ <CCDTitleCell></CCDTitleCell>
|
|
<template v-for="cell in model">
|
|
<template v-for="cell in model">
|
|
- <CCDChatCell :cell-type="cell.type" :message="cell.content" @shutdown="doShutdown"></CCDChatCell>
|
|
|
|
|
|
+ <CCDChatCell :cell-type="cell.type" :message="cell.content"></CCDChatCell>
|
|
</template>
|
|
</template>
|
|
<div style="height: 30px"></div>
|
|
<div style="height: 30px"></div>
|
|
</div>
|
|
</div>
|
|
<div class="bottomAskBar" style="height: 50px; padding-right: 4px">
|
|
<div class="bottomAskBar" style="height: 50px; padding-right: 4px">
|
|
<div style="height: 50px; width: calc(100% - 50px);">
|
|
<div style="height: 50px; width: calc(100% - 50px);">
|
|
<input v-if="allowInput" ref="inputAsk" @keyup.enter="doPostMessage" type="text" placeholder="输入问题..." value=""/>
|
|
<input v-if="allowInput" ref="inputAsk" @keyup.enter="doPostMessage" type="text" placeholder="输入问题..." value=""/>
|
|
- <input v-else value=" 请等待AI回复" style="text-align: center" disabled/>
|
|
|
|
|
|
+ <input v-else value="AI正在打字" style="text-align: center" disabled/>
|
|
</div>
|
|
</div>
|
|
<button v-if="allowInput" @click="doPostMessage" :style="{
|
|
<button v-if="allowInput" @click="doPostMessage" :style="{
|
|
height: '50px',
|
|
height: '50px',
|
|
@@ -22,7 +23,19 @@
|
|
background: 'transparent',
|
|
background: 'transparent',
|
|
border: 'none'
|
|
border: 'none'
|
|
}">
|
|
}">
|
|
- <img src="../assets/run.png" style="width: calc(100% - 1px); height: calc(100% - 3px)">
|
|
|
|
|
|
+ <img src="../assets/run.png" style="width: calc(100% - 1px); height: calc(100% - 3px); object-fit: scale-down">
|
|
|
|
+ </button>
|
|
|
|
+ <button v-else @click="doShutdownManually" :style="{
|
|
|
|
+ height: '50px',
|
|
|
|
+ width: '50px',
|
|
|
|
+ right: '0px',
|
|
|
|
+ top: 'calc(100% - ' + (50 + safeAreaInsets.bottom) + 'px)',
|
|
|
|
+ position: 'absolute',
|
|
|
|
+ float: 'right',
|
|
|
|
+ background: 'transparent',
|
|
|
|
+ border: 'none'
|
|
|
|
+ }">
|
|
|
|
+ <img src="../assets/pause.png" style="width: calc(100% - 1px); height: calc(100% - 3px); object-fit: scale-down">
|
|
</button>
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
@@ -63,28 +76,23 @@ input {
|
|
<script>
|
|
<script>
|
|
import CCDChatCell from "@/components/CCDChatCell";
|
|
import CCDChatCell from "@/components/CCDChatCell";
|
|
import { fetchEventSource } from '@microsoft/fetch-event-source';
|
|
import { fetchEventSource } from '@microsoft/fetch-event-source';
|
|
-let websocketAPIURL = "wss://chat.forgetive.net/free_chat"
|
|
|
|
|
|
+import IntentManager from "@/aigc/IntentManager";
|
|
|
|
+import CCDTitleCell from "@/components/CCDTitleCell";
|
|
|
|
+
|
|
let sseAPIURL = "https://notebook.forgetive.net/freeai/llm"
|
|
let sseAPIURL = "https://notebook.forgetive.net/freeai/llm"
|
|
|
|
|
|
export default {
|
|
export default {
|
|
name: 'CCDChatSystem',
|
|
name: 'CCDChatSystem',
|
|
- components: {CCDChatCell},
|
|
|
|
|
|
+ components: {CCDTitleCell, CCDChatCell},
|
|
data() {
|
|
data() {
|
|
return {
|
|
return {
|
|
allowInput: true,
|
|
allowInput: true,
|
|
model: [
|
|
model: [
|
|
{
|
|
{
|
|
- "type" : "title"
|
|
|
|
- },
|
|
|
|
- {
|
|
|
|
- "type" : "tip",
|
|
|
|
- "content" : "您好,我是 C Code Develop & C Notebook AI助手,可以帮您解决代码问题。从编程技巧到历史人文,我无所不知。快来跟我聊天吧~"
|
|
|
|
|
|
+ type: "answer",
|
|
|
|
+ content: "您好,我是 C Code Develop & C Notebook AI助手,可以帮您解决代码问题。从编程技巧到历史人文,我无所不知。快来跟我聊天吧~"
|
|
}
|
|
}
|
|
],
|
|
],
|
|
- intents: [
|
|
|
|
- "如果用户询问他有多少积分,请回答'*INTENT_CREDITS'",
|
|
|
|
- "如果用户询问与他账号信息有关的内容,请回答'*INTENT_ACCOUNT'"
|
|
|
|
- ],
|
|
|
|
sseInstance: null,
|
|
sseInstance: null,
|
|
bodyClass: "bodyBackground_xcwk",
|
|
bodyClass: "bodyBackground_xcwk",
|
|
safeAreaInsets: {
|
|
safeAreaInsets: {
|
|
@@ -208,13 +216,15 @@ export default {
|
|
self.inAppPendingCard = messageCard
|
|
self.inAppPendingCard = messageCard
|
|
|
|
|
|
let messageModel = []
|
|
let messageModel = []
|
|
- for (let index in self.intents) {
|
|
|
|
- let intent = self.intents[index];
|
|
|
|
- messageModel.push({
|
|
|
|
- "role" : "system",
|
|
|
|
- "content" : intent
|
|
|
|
- })
|
|
|
|
- }
|
|
|
|
|
|
+
|
|
|
|
+ let intentPrompt = IntentManager.generateIntentPrompts()
|
|
|
|
+ messageModel.push({
|
|
|
|
+ "role" : "system",
|
|
|
|
+ "content" : "不要帮用户生成代码。" + intentPrompt
|
|
|
|
+ })
|
|
|
|
+
|
|
|
|
+ console.log(intentPrompt)
|
|
|
|
+
|
|
for (let index in self.model) {
|
|
for (let index in self.model) {
|
|
let model = self.model[index]
|
|
let model = self.model[index]
|
|
if (model.type == "ask") {
|
|
if (model.type == "ask") {
|
|
@@ -232,10 +242,10 @@ export default {
|
|
}
|
|
}
|
|
|
|
|
|
self.insertMessageBlock(messageCard)
|
|
self.insertMessageBlock(messageCard)
|
|
- self.insertMessageBlock({
|
|
|
|
- "type" : "answerTyping",
|
|
|
|
- "content" : ""
|
|
|
|
- })
|
|
|
|
|
|
+ // self.insertMessageBlock({
|
|
|
|
+ // "type" : "answerTyping",
|
|
|
|
+ // "content" : ""
|
|
|
|
+ // })
|
|
|
|
|
|
xcwk.std.sseRequest({
|
|
xcwk.std.sseRequest({
|
|
"path" : "/freeai/llm",
|
|
"path" : "/freeai/llm",
|
|
@@ -245,6 +255,7 @@ export default {
|
|
}
|
|
}
|
|
}, function (params) {
|
|
}, function (params) {
|
|
self.inAppSSEHandle = params.sseId
|
|
self.inAppSSEHandle = params.sseId
|
|
|
|
+ IntentManager.llmResponseBegin()
|
|
})
|
|
})
|
|
},
|
|
},
|
|
doInAppSSE_ReceiveMessage(message) {
|
|
doInAppSSE_ReceiveMessage(message) {
|
|
@@ -258,8 +269,11 @@ export default {
|
|
case 0x1: { // data
|
|
case 0x1: { // data
|
|
let partModel = JSON.parse(message.data)
|
|
let partModel = JSON.parse(message.data)
|
|
if (partModel["v"] != null) {
|
|
if (partModel["v"] != null) {
|
|
- messageCard.content += partModel["v"]
|
|
|
|
- self.scrollViewScrollToBottom()
|
|
|
|
|
|
+ let processedData = IntentManager.llmResponseToken(partModel["v"])
|
|
|
|
+ if (processedData) {
|
|
|
|
+ messageCard.content += data
|
|
|
|
+ self.scrollViewScrollToBottom()
|
|
|
|
+ }
|
|
}
|
|
}
|
|
break
|
|
break
|
|
}
|
|
}
|
|
@@ -278,18 +292,20 @@ export default {
|
|
self.abortController = null
|
|
self.abortController = null
|
|
self.inAppPendingCard = null
|
|
self.inAppPendingCard = null
|
|
self.inAppSSEHandle = null
|
|
self.inAppSSEHandle = null
|
|
- for (let id = 0; id < self.model.length; id++) {
|
|
|
|
- if (self.model[id].type == "answerTyping") {
|
|
|
|
- self.model.splice(id, 1)
|
|
|
|
- id--;
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
|
|
+ IntentManager.llmResponseEnd()
|
|
|
|
+ // for (let id = 0; id < self.model.length; id++) {
|
|
|
|
+ // if (self.model[id].type == "answerTyping") {
|
|
|
|
+ // self.model.splice(id, 1)
|
|
|
|
+ // id--;
|
|
|
|
+ // }
|
|
|
|
+ // }
|
|
break
|
|
break
|
|
}
|
|
}
|
|
}
|
|
}
|
|
},
|
|
},
|
|
doAsyncSSE(message) {
|
|
doAsyncSSE(message) {
|
|
let self = this
|
|
let self = this
|
|
|
|
+ IntentManager.llmResponseBegin()
|
|
|
|
|
|
self.allowInput = false
|
|
self.allowInput = false
|
|
let messageCard = {
|
|
let messageCard = {
|
|
@@ -298,6 +314,12 @@ export default {
|
|
}
|
|
}
|
|
|
|
|
|
let messageModel = []
|
|
let messageModel = []
|
|
|
|
+ let intentPrompt = IntentManager.generateIntentPrompts()
|
|
|
|
+ messageModel.push({
|
|
|
|
+ "role" : "system",
|
|
|
|
+ "content" : "不要帮用户生成代码。" + intentPrompt
|
|
|
|
+ })
|
|
|
|
+
|
|
for (let index in self.model) {
|
|
for (let index in self.model) {
|
|
let model = self.model[index]
|
|
let model = self.model[index]
|
|
if (model.type == "ask") {
|
|
if (model.type == "ask") {
|
|
@@ -315,10 +337,10 @@ export default {
|
|
}
|
|
}
|
|
|
|
|
|
self.insertMessageBlock(messageCard)
|
|
self.insertMessageBlock(messageCard)
|
|
- self.insertMessageBlock({
|
|
|
|
- "type" : "answerTyping",
|
|
|
|
- "content" : ""
|
|
|
|
- })
|
|
|
|
|
|
+ // self.insertMessageBlock({
|
|
|
|
+ // "type" : "answerTyping",
|
|
|
|
+ // "content" : ""
|
|
|
|
+ // })
|
|
|
|
|
|
self.abortController = new AbortController()
|
|
self.abortController = new AbortController()
|
|
let eventSource = fetchEventSource(sseAPIURL, {
|
|
let eventSource = fetchEventSource(sseAPIURL, {
|
|
@@ -335,7 +357,7 @@ export default {
|
|
onmessage(event) {
|
|
onmessage(event) {
|
|
let partModel = JSON.parse(event.data)
|
|
let partModel = JSON.parse(event.data)
|
|
if (partModel["v"] != null) {
|
|
if (partModel["v"] != null) {
|
|
- messageCard.content += partModel["v"]
|
|
|
|
|
|
+ messageCard.content += IntentManager.llmResponseToken(partModel["v"])
|
|
self.scrollViewScrollToBottom()
|
|
self.scrollViewScrollToBottom()
|
|
}
|
|
}
|
|
},
|
|
},
|
|
@@ -343,26 +365,34 @@ export default {
|
|
if (messageCard.content.length < 5) {
|
|
if (messageCard.content.length < 5) {
|
|
messageCard.content = "回答出了一点问题"
|
|
messageCard.content = "回答出了一点问题"
|
|
}
|
|
}
|
|
- self.sseInstance = null
|
|
|
|
self.allowInput = true
|
|
self.allowInput = true
|
|
self.doShutdown()
|
|
self.doShutdown()
|
|
throw err
|
|
throw err
|
|
},
|
|
},
|
|
onclose() {
|
|
onclose() {
|
|
- self.sseInstance = null
|
|
|
|
self.allowInput = true
|
|
self.allowInput = true
|
|
self.abortController = null
|
|
self.abortController = null
|
|
- for (let id = 0; id < self.model.length; id++) {
|
|
|
|
- if (self.model[id].type == "answerTyping") {
|
|
|
|
- self.model.splice(id, 1)
|
|
|
|
- id--;
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
|
|
+ self.sseInstance = null
|
|
|
|
+ IntentManager.llmResponseEnd()
|
|
|
|
+ // for (let id = 0; id < self.model.length; id++) {
|
|
|
|
+ // if (self.model[id].type == "answerTyping") {
|
|
|
|
+ // self.model.splice(id, 1)
|
|
|
|
+ // id--;
|
|
|
|
+ // }
|
|
|
|
+ // }
|
|
}
|
|
}
|
|
})
|
|
})
|
|
self.sseInstance = eventSource
|
|
self.sseInstance = eventSource
|
|
},
|
|
},
|
|
|
|
+ doShutdownManually() {
|
|
|
|
+ this.doShutdown()
|
|
|
|
+ let lastBubble = this.model[this.model.length - 1]
|
|
|
|
+ if (lastBubble.type == "answer") {
|
|
|
|
+ lastBubble.content += "\n\n**已停止回答**"
|
|
|
|
+ }
|
|
|
|
+ },
|
|
doShutdown() {
|
|
doShutdown() {
|
|
|
|
+ IntentManager.llmResponseEnd()
|
|
let self = this
|
|
let self = this
|
|
if (self.socketObject != null) {
|
|
if (self.socketObject != null) {
|
|
self.socketObject.close(1000)
|
|
self.socketObject.close(1000)
|
|
@@ -372,12 +402,12 @@ export default {
|
|
self.sseInstance = null
|
|
self.sseInstance = null
|
|
self.allowInput = true
|
|
self.allowInput = true
|
|
self.abortController = null
|
|
self.abortController = null
|
|
- for (let id = 0; id < self.model.length; id++) {
|
|
|
|
- if (self.model[id].type == "answerTyping") {
|
|
|
|
- self.model.splice(id, 1)
|
|
|
|
- id--;
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
|
|
+ // for (let id = 0; id < self.model.length; id++) {
|
|
|
|
+ // if (self.model[id].type == "answerTyping") {
|
|
|
|
+ // self.model.splice(id, 1)
|
|
|
|
+ // id--;
|
|
|
|
+ // }
|
|
|
|
+ // }
|
|
}
|
|
}
|
|
if (self.inAppSSEHandle != null) {
|
|
if (self.inAppSSEHandle != null) {
|
|
xcwk.std.cancelSSERequest({
|
|
xcwk.std.cancelSSERequest({
|
|
@@ -388,12 +418,12 @@ export default {
|
|
self.allowInput = true
|
|
self.allowInput = true
|
|
self.abortController = null
|
|
self.abortController = null
|
|
self.inAppPendingCard = null
|
|
self.inAppPendingCard = null
|
|
- for (let id = 0; id < self.model.length; id++) {
|
|
|
|
- if (self.model[id].type == "answerTyping") {
|
|
|
|
- self.model.splice(id, 1)
|
|
|
|
- id--;
|
|
|
|
- }
|
|
|
|
- }
|
|
|
|
|
|
+ // for (let id = 0; id < self.model.length; id++) {
|
|
|
|
+ // if (self.model[id].type == "answerTyping") {
|
|
|
|
+ // self.model.splice(id, 1)
|
|
|
|
+ // id--;
|
|
|
|
+ // }
|
|
|
|
+ // }
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
@@ -401,5 +431,14 @@ export default {
|
|
</script>
|
|
</script>
|
|
|
|
|
|
<style scoped>
|
|
<style scoped>
|
|
-
|
|
|
|
|
|
+.chatBubbleContainer {
|
|
|
|
+ display: block;
|
|
|
|
+ overflow: hidden;
|
|
|
|
+ overflow-y: scroll;
|
|
|
|
+ scrollbar-width: none;
|
|
|
|
+ -ms-overflow-style: none;
|
|
|
|
+}
|
|
|
|
+::-webkit-scrollbar {
|
|
|
|
+ display: none;
|
|
|
|
+}
|
|
</style>
|
|
</style>
|