核工业西南物理研究院知识库AI客户端
xiangpei
2025-04-16 6abf7642caa66239f3f3e75454811f95aaf50a26
src/components/AiChat.vue
@@ -95,16 +95,21 @@
        top_k: 3,
        score_threshold: 2,
        history: [
        ],
        stream: true,
        model: "Qwen25-32B-Instruct",
        temperature: 0.7,
        max_tokens: 64,
        temperature: 1.15,
        max_tokens: 512,
        prompt_name: "default",
        return_direct: false
      },
      msgIndex: null,
      msgRole: null
      msgRole: null,
      chunkRef: null, // ai回答临时数据
      haveThinkStart: false, // ai回答响应数据是否有think标签
      haveThinkEnd: false, // ai回答响应数据是否有think标签
      controller: null, // ai回答请求控制器,可以根据业务中断请求
    };
  },
  methods: {
@@ -156,77 +161,89 @@
      return roleNames[role];
    },
    // 发送消息
    async sendMessage() {
      if (this.inputMessage.trim() === '') return;
      // 添加用户消息
      this.messages.push({
        role: 'user',
        content: this.inputMessage,
      });
      this.messages.push({
        role: 'assistant',
        content: ''
      })
      this.sendMsgForm.query = this.inputMessage
      // 清空输入框
      // 初始化状态
      this.chunkRef = '';
      this.haveThinkStart = false;
      this.haveThinkEnd = false;
      this.controller = new AbortController();
      // 添加用户消息和占位助理消息
      this.messages.push(
          { role: 'user', content: this.inputMessage },
          { role: 'assistant', content: '' }
      );
      this.sendMsgForm.query = this.inputMessage;
      this.inputMessage = '';
      const assistantIndex = this.messages.length - 1;
      try {
        const response = await fetch('/api/chat/kb_chat', {
          method: 'POST',
          headers: {
            'Content-Type': 'application/json',
            "Accept": 'text/event-stream',
            'Cache-Control': 'no-cache, no-transform'
          },
          signal: this.controller.signal,
          body: JSON.stringify(this.sendMsgForm)
        });
      const response = await fetch('/api/chat/kb_chat', {
        method: 'POST',
        headers: {
          'Content-Type': 'application/json',
        },
        body: JSON.stringify(this.sendMsgForm)
      });
      const reader = response.body.getReader();
      const decoder = new TextDecoder();
        if (!response.ok) throw new Error(`HTTP ${response.status}`);
      // eslint-disable-next-line no-constant-condition
      while (true) {
        const { done, value } = await reader.read();
        if (done) break;
        console.log("结束循环")
        const chunk = decoder.decode(value, { stream: false });
        const dataList = chunk.split('\r\n\r\n')
        console.log("获取到了流式响应数据", dataList)
        if (dataList && dataList.length > 0) {
          for (const data of dataList) {
            if (data.startsWith("data: ")) {
              const jsonStr = data.slice(6).trim();
              if (jsonStr) {
                const json = JSON.parse(jsonStr);
                if (json.docs && json.docs.length > 0) {
                  console.log("1", json)
                  json.docs.forEach(str => {
                    this.messages[this.messages.length - 1].content += str
                    // 强制Vue更新视图
                    this.$forceUpdate();
                  })
                } else if (json.choices && json.choices.length > 0) {
                  console.log("2", json)
                  json.choices.forEach(choice => {
                    this.messages[this.messages.length - 1].content += choice.delta.content
                    // 强制Vue更新视图
                    this.$forceUpdate();
                  })
        const reader = response.body.getReader();
        const decoder = new TextDecoder();
        let jsonBuffer = '';
        // eslint-disable-next-line no-constant-condition
        while (true) {
          const { done, value } = await reader.read();
          if (done) {
            this.sendMsgForm.history.push({ role: 'user', content: this.inputMessage })
            this.sendMsgForm.history.push({ role: 'assistant', content: this.messages[assistantIndex].content })
            break;
          }
          const chunk = decoder.decode(value, { stream: true });
          const lines = chunk.split('\n');
          for (const line of lines) {
            if (!line.startsWith('data:')) continue;
            const rawData = line.slice(5).trim();
            if (rawData === '[DONE]') continue;
            try {
              jsonBuffer += rawData;
              const data = JSON.parse(jsonBuffer);
              jsonBuffer = '';
              let content = '';
              if (data.docs) {
                for (let i = 0; i < data.docs.length; i++) {
                  content += data.docs[i]
                }
              } else {
                content = data?.choices?.[0]?.delta?.content || '';
              }
              if (!content) continue;
              // 实时更新
              this.messages[assistantIndex].content += content;
              this.messages[assistantIndex].content = this.messages[assistantIndex].content.replace(/<\/?think>/g, '');
              await this.$nextTick();
            } catch {
              // JSON不完整,等待下次数据
              continue;
            }
          }
        }
      } catch (error) {
        if (error.name !== 'AbortError') {
          console.error('Request failed:', error);
          this.messages[assistantIndex].content = '请求异常';
        }
      }
      // sendKbMsg(this.sendMsgForm).then(res => {
      //   console.log(res, "拿到回复了!!")
      //   this.sendMsgForm.history.push({
      //     role: 'assistant',
      //     content: JSON.parse(res.data).choices[0].message.content,
      //   });
      // })
    },
    }
  },
};