Skip to content

Commit

Permalink
feat: support for websocket #21
Browse files Browse the repository at this point in the history
  • Loading branch information
OrenZhang committed Jun 7, 2024
1 parent 5f3ea6f commit a7f3f42
Show file tree
Hide file tree
Showing 7 changed files with 74 additions and 55 deletions.
15 changes: 0 additions & 15 deletions src/api/chat.js
Original file line number Diff line number Diff line change
@@ -1,20 +1,5 @@
import globalContext from '../context';
import http from './index';

export const createChatAPI = (data) => new Promise((resolve, reject) => {
fetch(`${globalContext.backendUrl}/chat/`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(data),
credentials: 'include',
}).then(
(res) => resolve(res),
(err) => reject(err),
);
});

export const preCheckAPI = (data) => new Promise((resolve, reject) => {
http.post('/chat/pre_check/', data).then(
(res) => resolve(res),
Expand Down
104 changes: 64 additions & 40 deletions src/components/ChatInput.vue
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
<script setup>
import {computed, ref, watch} from 'vue';
import {createChatAPI, preCheckAPI} from '../api/chat';
import {computed, onBeforeUnmount, ref, watch} from 'vue';
import {preCheckAPI} from '../api/chat';
import {Message} from '@arco-design/web-vue';
import {Role} from '../constants';
import {useStore} from 'vuex';
import {useI18n} from 'vue-i18n';
import globalContext from '../context';
// props
const props = defineProps({
Expand Down Expand Up @@ -78,6 +79,7 @@ watch(() => model.value, () => {
});
// chat
const lastResponseContent = ref(null);
const doChat = async () => {
// set loading
emits('setChatLoading', true);
Expand All @@ -98,46 +100,68 @@ const doChat = async () => {
if (!key) {
return;
}
createChatAPI({key})
.then((res) => {
// check success
if (!res.ok) {
emits('setChatLoading', false);
Message.error(res.statusText);
return;
}
// push message
emits('addMessage', {role: 'user', content: promptForm.value.content});
// auto scroll
emits('toggleUserBehavior', false);
// init response content
const lastResponseContent = ref({role: Role.Assistant, content: ''});
emits('addMessage', lastResponseContent.value);
// clear input
promptForm.value.content = '';
// decode chunk
const decoder = new TextDecoder('utf-8');
const reader = res.body.getReader();
// eslint-disable-next-line require-jsdoc
function read() {
reader.read().then((chunk) => {
if (!chunk.done) {
const value = decoder.decode(chunk.value);
lastResponseContent.value.content += value;
read();
}
if (chunk.done) {
emits('setChatLoading', false);
}
emits('saveMessage');
});
// add message to display
emits('addMessage', {role: 'user', content: promptForm.value.content});
// auto scroll
emits('toggleUserBehavior', false);
// init response content
lastResponseContent.value = {role: Role.Assistant, content: ''};
emits('addMessage', lastResponseContent.value);
// clear input
promptForm.value.content = '';
// send message
sendMessage(JSON.stringify({key}), true);
};
const onMessage = (event) => {
const data = JSON.parse(event.data);
if (data.is_finished) {
emits('setChatLoading', false);
}
if (data.data) {
lastResponseContent.value.content += data.data;
emits('saveMessage');
}
};
const sendMessage = (message) => {
initWebSocket().then(
() => {
if (webSocket.value && webSocket.value.readyState === WebSocket.OPEN) {
webSocket.value.send(message);
} else {
Message.error(i18n.t('ConnectionClosedPleaseRetry'));
}
read();
}, (err) =>{
emits('setChatLoading', false);
Message.error(err.message);
});
},
);
};
// webSocket
const webSocket = ref(null);
const retryTimes = ref(0);
const maxRetryTimes = ref(1000);
const initWebSocket = () => {
if (webSocket.value && webSocket.value.readyState === WebSocket.OPEN) {
return new Promise();
}
webSocket.value = new WebSocket(`${globalContext.webSocketUrl}/chat/`);
webSocket.value.onmessage = (e) => {
onMessage(e);
};
webSocket.value.onclose = () => {
emits('setChatLoading', false);
};
webSocket.value.onerror = (e) => {
emits('setChatLoading', false);
};
return new Promise((resolve) => {
webSocket.value.onopen = (e) => resolve(e);
});
};
const closeWebSocket = () => {
if (webSocket.value && webSocket.value.readyState === WebSocket.OPEN) {
webSocket.value.close();
}
};
onBeforeUnmount(() => closeWebSocket());
const reGenerate = () => {
if (!props.localMessages.length) {
Expand Down
2 changes: 2 additions & 0 deletions src/components/MessageContent.vue
Original file line number Diff line number Diff line change
Expand Up @@ -49,9 +49,11 @@ const emits = defineEmits(['reGenerate']);
}"
>
<v-md-preview
v-show="message.content"
:text="message.content"
class="v-md-preview"
/>
<icon-loading v-show="!message.content" />
</div>
<a-avatar
v-show="false"
Expand Down
1 change: 1 addition & 0 deletions src/context/index.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
const globalContext = {
siteUrl: process.env.SITE_URL,
backendUrl: process.env.BACKEND_URL,
webSocketUrl: process.env.WEB_SOCKET_URL,
ovincUrl: process.env.OVINC_URL,
ovincWebUrl: process.env.OVINC_WEB_URL,
};
Expand Down
3 changes: 3 additions & 0 deletions src/locale/en-us.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,9 @@ const mEnUS = {
NoModelChoosed: 'No Model Choosed',
VerifyingUserInfo: 'Verifying...',
Logout: 'Logout',
ConnectionClosedPleaseRetry: 'Connection is closed, please try again',
ConnectionClosed: 'Connection is closed',
ConnectionError: 'Connection Error',
};

export default mEnUS;
3 changes: 3 additions & 0 deletions src/locale/zh-cn.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,9 @@ const mZhCN = {
NoModelChoosed: '未选择模型',
VerifyingUserInfo: '登录中',
Logout: '登出',
ConnectionClosedPleaseRetry: '连接异常,请重试',
ConnectionClosed: '连接已关闭',
ConnectionError: '连接异常',
};

export default mZhCN;
1 change: 1 addition & 0 deletions vite.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ export default defineConfig({
'process.env': {
BACKEND_URL: process.env.BACKEND_URL,
SITE_URL: process.env.SITE_URL,
WEB_SOCKET_URL: process.env.WEB_SOCKET_URL,
OVINC_URL: process.env.OVINC_URL,
OVINC_WEB_URL: process.env.OVINC_WEB_URL,
},
Expand Down

0 comments on commit a7f3f42

Please sign in to comment.