轶哥

📚 Having fun with AI Agent. Always learning.

Vue3对接ChatGPT4接口(打字机效果)

本篇文章将介绍如何在 Vue3 中实现与 ChatGPT4 API 的对接。具体的后端 API 对接方式,还请参考这篇文章:《对接ChatGPT3.5/4的API实现打字机效果》

Vue3代码

<script setup lang="ts">
import { ref } from 'vue'
import Text from './Text.vue'
import { Message } from './GPTComponentType'
import { defineComponent } from 'vue'

defineComponent({
  name: 'GPT'
})

const props = defineProps({
  url: {
    type: String,
    required: true
  },
  auth: {
    type: String,
    required: true
  }
})

const output = ref<string>('')

let abortController: AbortController = new AbortController()
let shouldCancelReadChunk: boolean[] = []

const getGPTAnswer = async (messages: Message[]) => {
  abortController.abort() // 取消当前请求
  abortController = new AbortController() // 创建新的 AbortController

  // 将 shouldCancelReadChunk 的值设置为 true
  shouldCancelReadChunk.forEach((_, idx) => {
    shouldCancelReadChunk[idx] = true
  })
  shouldCancelReadChunk.push(false)
  let index = shouldCancelReadChunk.length - 1

  try {
    let result = ''
    output.value = '请求中...'
    const res = await fetch(props.url, {
      method: "POST",
      body: JSON.stringify({
        auth: props.auth,
        messages
      }),
      headers: {
        "Content-Type": "application/json"
      },
      signal: abortController.signal // 绑定 AbortSignal 到请求
    })
    if (res.body == null) {
      return
    }

    const reader = res.body.getReader()
    const decoder = new TextDecoder("utf-8")
    const readChunk = async (): Promise<void> => {
      // 在循环内检查 shouldCancelReadChunk 的值
      if (shouldCancelReadChunk[index]) {
        // 如果需要取消,则提前返回并停止执行 readChunk
        return
      }
      const { value, done } = await reader.read()

      if (!done) {
        const dataString = decoder.decode(value)
        const lines = dataString.trim().split("data:")
        for (const line of lines) {
          // 在循环内检查 shouldCancelReadChunk 的值
          if (shouldCancelReadChunk[index]) {
            // 如果需要取消,则提前返回并停止执行
            return
          }
          if (line != '') {
            const text = line.replace("data:", "").trim()
            try {
              const data = JSON.parse(text)
              if (data.choices[0].delta.content) {
                result += data.choices[0].delta.content
                output.value = result
              }
              if (data.choices[0].finish_reason === 'length') {
                messages.push({
                  role: 'assistant',
                  content: result
                })
                await getGPTAnswer(messages)
                return
              } else if (data.choices[0].finish_reason === 'stop') {
                output.value = result
                return
              }
            } catch (error: any) {
              if (error && error.code && error.code === 20) {
                console.log('请求被用户终止')
                return
              }
              if (text.trim() === '[DONE]') {
                output.value = result
                return
              }
              console.error(error)
              return
            }
          }
        }
        await readChunk() // 如果没有触发结束标识,则继续读取
      } else {
        console.log('done')
      }
    }
    await readChunk()
  } catch (error: any) {
    if (error.name === 'AbortError') {
      console.log('Request was canceled')
    } else {
      console.error(error)
    }
  }
}

defineExpose({
  getGPTAnswer
})
</script>


<template>
  <div class="gpt">
    <Text :text="output" />
  </div>
</template>

<style>
.gpt {
  width: 100%;
}
</style>

GPTComponentType.ts:

import { ComponentPublicInstance } from 'vue'

export type Message = {
  role: 'user' | 'assistant'
  content: string
}

export type GPTComponentType = ComponentPublicInstance<{
  getGPTAnswer: (messages: Message[]) => Promise<void>
}>

实现原理:

  1. 使用 Vue 3 的 Composition API,我们导入了必要的依赖和定义了组件、props 以及响应式变量。
  2. 为了处理多个请求并能够取消正在进行的请求,我们使用了 AbortController
  3. getGPTAnswer 函数中,通过 POST 请求发送用户消息,并将结果保存在一个可观察的变量 output 中。
  4. 当从服务器接收到数据时,我们将每条消息解析为 JSON 格式,检查是否有输出内容,如果有,则将其添加到 result 变量中。
  5. 如果在解析过程中发生错误,我们会在控制台上显示错误信息。
  6. 为了在请求过程中实现取消功能,我们使用了一个名为 shouldCancelReadChunk 的数组来跟踪每个请求的取消状态。当数组中的值为真时,请求将被取消。

代码处理了重复调用getGPTAnswer方法的情况,当重复调用时会中止上一轮未完成的请求。

渲染Markdown

需要特别注意的是,上述代码并未对Markdown做出渲染,可以在 Text 组件中使用开源库 'markdown-it' 和 'highlight.js' 来实现实时 Markdown 渲染。由于Text.vue的实现相对简单,在此不再赘述。

调用示例

Vue3代码:

<script setup lang="ts">
import { ref } from 'vue'
import GPT from './components/GPT.vue'
import type { GPTComponentType, Message } from './components/GPTComponentType'

// (messages: any[]) => Promise<void>
const gpt = ref<GPTComponentType | null>(null)
const url = 'https://example.com'
const auth = ''
const content = ref('')

const callGPTAnswer = async () => {
  const messages: Message[] = [
    // 您可以根据需要添加您自己的消息列表
    {
      role: 'user',
      content: content.value
    }
  ]
  await gpt.value!.getGPTAnswer(messages)
}
</script>

<template>
  <div class="container">
    <textarea v-model="content" rows="8"></textarea>
    <button @click="callGPTAnswer">获取 GPT 回答</button>
    <GPT ref="gpt" :url="url" :auth="auth" />
  </div>
</template>

通过以上步骤,您可以在 Vue3 项目中实现 ChatGPT4 API 的对接。

打赏
交流区

暂无内容

尚未登陆
发布
  上一篇 (Windows安装rsync命令一键脚本)
下一篇 (强行修改笔记本风扇转速的方法)  

评论回复提醒