前端使用fetch-event-source实现AI对话

安装

npm install --save @microsoft/fetch-event-source

使用

import {fetchEventSource} from '@microsoft/fetch-event-source';

data() {
    return {
      question: '',
      isLoading: false,
      currentIndex: '',
      thinkFlag: false,
      ctrlAbout: null,
      conversationId: null,
      answering: false
    }
},
methods: {
    sendQuestion(question) {
      if (this.isLoading || this.answering) return;
      if (window.EventSource) {
        this.question = '';
        this.currentIndex = this.dataList.length;
        this.isLoading = true;
        this.dataList.push({
          index: this.currentIndex,
          type: 'q',
          code: 200,
          info: question
        });
        this.scrollTop();

        let _that = this;
        let ix = 0;
        const params = {question: question, conversationId: this.conversationId};
        this.ctrlAbout = new AbortController();
        fetchEventSource(process.env.VUE_APP_BASE_API + '/ai/aiQuestion', {
          method: 'POST',
          headers: {
            [tokenName]: 'Bearer ' + getToken(),
            accept: 'application/json;charset=UTF-8,text/plain,*/*',
            'Content-Type': 'application/json;charset=UTF-8', // 文本返回格式
          },
          body: JSON.stringify(params),
          signal: _that.ctrlAbout.signal,
          openWhenHidden: true, // 在浏览器标签页隐藏时保持与服务器的EventSource连接
          onopen(resp) {
            console.log('SSE连接成功...', resp)
            _that.isLoading = false;
            _that.thinkFlag = false;
          },
          onmessage(esm) {
            let esmData = esm.data;
            if (esmData && esmData !== '' && esmData !== 'event: ping') {
              // console.log('esmData', esmData)
              let respData = JSON.parse(esmData.substring(esm.data.indexOf(":") + 1, esmData.length));
              if (respData.event && respData.event === 'message') {
                if (ix < 1) {
                  _that.answering = true;
                }
                if (!_that.answering) return;

                let respAnswer = respData.answer;
                console.log('respAnswer', respAnswer)
                if (respAnswer === '<think>') {
                  _that.thinkFlag = true;
                  respAnswer = '';
                } else if (respAnswer === '</think>') {
                  _that.thinkFlag = false;
                  respAnswer = '';
                }
                let isExist = _that.dataList.some(d => d.type === 'a' && d.index === _that.currentIndex);
                if (isExist) {
                  _that.dataList.forEach(item => {
                    if (item.type === 'a' && item.index === _that.currentIndex) {
                      if (item.info.state === 0) {
                        if (_that.thinkFlag) {
                          item.info.text.think += respAnswer;
                        } else {
                          item.info.text.answer += respAnswer;
                        }
                      } else {
                        if (_that.thinkFlag) {
                          item.info = {
                            state: 0,
                            text: {
                              think: respAnswer,
                              answer: ''
                            }
                          }
                        } else {
                          item.info = {
                            state: 0,
                            text: {
                              think: '',
                              answer: respAnswer
                            }
                          }
                        }
                      }
                    }
                  })
                } else {
                  if (_that.thinkFlag) {
                    _that.dataList.push({
                      index: _that.currentIndex,
                      type: 'a',
                      code: 200,
                      info: {
                        state: 0,
                        showInfo: true,
                        text: {
                          think: respAnswer,
                          answer: ''
                        }
                      }
                    });
                  } else {
                    _that.dataList.push({
                      index: _that.currentIndex,
                      type: 'a',
                      code: 200,
                      info: {
                        state: 0,
                        showInfo: true,
                        text: {
                          think: '',
                          answer: respAnswer
                        }
                      }
                    });
                  }
                }
              } else {
                _that.dataList.forEach(item => {
                  if (item.type === 'a' && item.index === _that.currentIndex && item.info.state === 0) {
                    item.info.state = 1;
                  }
                })
                _that.answering = false;
                _that.ctrlAbout = null;
                _that.getHistorySession();
              }
            }
            _that.scrollTop();
          },
          onclose() {
            this.ctrlAbout = null;
            console.log('SSE已关闭...')
          },
          onerror(e) {
            console.log('SSE出现错误...', e)
            this.stopAnswer();
            this.$message.error('系统错误,请联系管理员进行处理!');
            throw e;
          }
        });
      } else {
        this.$message.warning('您的浏览器不支持SSE!');
      }
    },
    eventSourceClose() {
      if (this.ctrlAbout) {
        this.ctrlAbout.abort();
        this.ctrlAbout = null;
        aiQuestionByClose(this.taskId).then(resp => {
          console.info('SSE已中断...')
        })
      }
    },
    stopAnswer() {
      this.answering = false;
      this.eventSourceClose();
      this.dataList.forEach(item => {
        if (item.type === 'a' && item.index === this.currentIndex && item.info.state === 0) {
          item.info.state = 2;
        }
      });
      this.scrollTop();
      this.getHistorySession();
    },
    scrollTop() {
      this.$nextTick(() => {
        if (this.$refs.kqaWindow.scrollHeight) {
          this.$refs.kqaWindow.scrollTo({
            top: this.$refs.kqaWindow.scrollHeight,
            behavior: 'smooth'
          });
        }
      });
    },
    eventSourceClose() {
      if (this.ctrlAbout) {
        this.ctrlAbout.abort();
        this.ctrlAbout = null;
        aiQuestionByClose(this.taskId).then(resp => {
          console.info('SSE已中断...')
        })
      }
    },
}

参考文档

        https://juejin.cn/post/7518783423273664547

        fetchEventSource使用+源码解析 - 漫思 - 博客园

        https://juejin.cn/post/7411047482652262415

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值