无聊写个 chatgpt 玩玩!这不得试一试 openai 的聊天和绘画功能

chatgpt 最近很火。使用 chatgpt 问一些问题还是很有用的。比如面试题,面试题的答案。简直不要太爽。

不过闲来无事,也使用 openai 提供的api ,写了几个小页面,可以进行聊天,和绘画。

项目放在 github 上了:https://github.com/wtdsn/chat-draw-by-openai

先看看页面吧!

无聊写个 chatgpt 玩玩!这不得试一试 openai 的聊天和绘画功能_第1张图片 无聊写个 chatgpt 玩玩!这不得试一试 openai 的聊天和绘画功能_第2张图片

ok ,话不多说,说说整个过程吧

API 文档

地址 :https://platform.openai.com/docs/api-reference

可以看到,openai 提供了许多的 api 。它甚至提供了 openai 的 npm 包 。并且给了一些使用例子。

当然可以直接使用 openai 提供的 node 模块。可以省去一堆麻烦事。

在node 中怎么使用,看文档就可以了

调用这些 api ,需要一个 apikey 。需要注册 openai 账号。
注册后在 : https://platform.openai.com/account/api-keys 生成 apikey ,并且把它记下来就可以了。

不过这些 api 接口并不是免费的。请求会消耗一些 token ,这些 token 是花钱的。当然新用户有 5美元。能用很久

项目搭建

此项目的搭建呢!我使用了 vite 。不得不说 , vite 确实方便。

项目结构

key.txt 我记录了 apikey 。 github 中没有,丢上去就失效!

无聊写个 chatgpt 玩玩!这不得试一试 openai 的聊天和绘画功能_第3张图片

我写了三个页面

entry.html : 入口页面。输入 apikey ,然后选择去聊天或者去生成图画。

index.html : 聊天页面

draw.html : 生成图画页面

具体的一些 css ,或者如何生成聊天内容。具体就看 js 文件就行了

使用服务端推送

我注意到,chatgpt 回复的内容,是一点一点生成的。但是它可不是故意的。而是这样子用户感知上会比较快。
如果等全部内容都回复完再显示,可能需要等上好几秒。

在 chat 的api中,提供了一个 stream 参数 。那么服务端在回复的时候,是使用服务端推送的,也就是数据是一点一点推送过来的。而不是一次性返回!这样子可以快速的渲染出一些回复的内容,而不需要等待全部生成完再渲染

具体可以了解一下 :EventSource

不过, fetch api 可以处理返回的流数据。倒也不用使用 EventSource 了。

使用例子

FetchH 类 , 对 fetch 进行封装。这里参考了 axios 的一些思路。在请求时,就可以避免重复的配置

class FetchH {
  constructor(url, config = {}) {
    if (typeof url === 'string') {
      config.url = url
    } else if (typeof url === 'object') {
      config = url
    }
    this.config = config
  }

  request(body) {
    // 合并请求参数
    let d = {
      ...this.config.body,
      ...body
    }

    return fetch(this.config.url, {
      method: this.config.method,
      headers: this.config.headers,
      body: JSON.stringify(d)
    })
  }
}

// 返回函数,可直接调用
function createFetchH(url, config) {
  let fh = new FetchH(url, config)
  return (fh.request).bind(fh)
}

// 创建 fetch
fetchH = createFetchH('https://api.openai.com/v1/chat/completions', {
    method: "POST",
    headers: {
      'Content-Type': 'application/json',
      'Authorization': 'Bearer ' + apiKey,
    },
    body: {
      model: "gpt-3.5-turbo",
      temperature: 0.3,
      stream: true,
      max_tokens: 1000
    }
})

sendMsg 发送请求 (有删减)

async function sendMsg() {
  fetchH({ messages: chatStack })
    .then(res => {
      // 获取 reader
      return res.body.getReader()
    })
    .then(reader => {
      // 读取内容
      return getStream(reader)
    })
}

getStream 读取数据

function getStream(reader, textEl) {
  let ans = '', _role = ''
  // 文本转码
  const utf8Decoder = new TextDecoder("utf-8");

  return _getStream()

  // 读取流数据
  function _getStream() {
    return reader.read().then(function (result) {
      // 如果数据已经读取完毕,直接返回
      if (result.done) {
        return {
          role: _role,
          content: ans
        }
      }

      let { role, content } = parseText(utf8Decoder.decode(result.value))
      if (role) {
        _role = role
      }
      if (content) {
        ans += content
      }

      // 逐步添加文字
      requestAnimationFrame(() => {
        textEl.innerHTML = parse(ans)
        scroll()
      })

      // 还有数据,继续读取
      return _getStream();
    })
  }
}

parseText 解析文本 , 获取后的数据是字符串。可能有多个 data , 进行进行分割

function parseText(text) {
  let info = {
    content: ''
  }
  // 默认以 \ndata 进行分割 (可能有问题)
  text.split(/\n(?=data:)/).forEach(v => {
    if (v === 'data: [DONE]\n\n') {
      return ''
    }

    let { role, content } = JSON.parse(v.slice(6)).choices[0].delta
    if (role) {
      info.role = role
    }

    if (content) {
      info.content += content
    }
  })
  return info
}

依赖包

文本的显示,使用了 marked 库。它可以把 markdown 的字符串内容转换成 html 格式的字符串。

还使用了节流 。节流使用了 utils-h 。当然这个包还提供了许多的工具函数。有兴趣可以去了解了解

你可能感兴趣的:(chatgpt,前端,AI作画,gpt-3)