OpenAI with ratelimit frontend

วันนี้ Vultureprime ได้ทำ Project เกี่ยวกับ OpenAI เช่น Chatbot  นั้นเอง ในการใช้งาน Chatbot ก็จะมีต้นทุนในส่วนของค่าใช้จ่าย ตัว Model OpenAI  ซึ่งถ้าปล่อยให้ใช้ แล้วมีการ request หลายครั้งจำนวนมาก ส่งผลให้ค่าใช้จ่ายสูง ถ้าได้เห็นบิลคงเป็นลมกันแน่นนอน เพื่อป้องกัน พวกเราเลยได้ทำตัวของ Rate Limit เพื่อจำนวนจำนวนการ Request ในเวลาสั้นจำนวนนึง

ถ้านึกภาพให้เห็น ก็เหมือนกับการที่เราช็อปปิ้งด้วย ที่เรากำลังจับจ่ายใช้สอยแต่ถูกควบคุมด้วยวงเงินที่สามารถใช้จ่ายได้นั้นเอง ซึ่งตัว Rate Limit จะช่วยควบคุมจำนวนที่ user  จะ request เข้ามาใช้ model นั้นเอง

Implement the Chat interface

ขั้นตอนในการสร้างเราก็ได้เลือก Lib หรือ Framwork มาใช้งานดังต่อไปนี้

1.Next.js:

เริ่มโปรเจกต์ใหม่โดยใช้คำสั่ง create-next-app

npx create-next-app my-project
cd my-project

2.React Hook Form:

yarn add react-hook-form @hookform/resolvers zod 

และ เราเลือกใช้ zod ในการจัดการ ทำ validate input form

3.TanStack Query:

ติดตั้ง TanStack Query (React Query) ในโปรเจกต์:

yarn add react-query

ในการ Implement Chat Interface  เราจะทำเป็น 4  section ซึ่งประกอบด้วย

  1. section hero ใน section hero นี้เราก็จะเกริ่นในเรื่องของ API Rate Limits
  2. section plan ใน section plan จะมีให้เลือก plan ในแบบต่างๆ  ถ้าอยากลองเล่นเยอะๆ 😆 ก็ติดต่อเรามาได้เลยนะคะ
  3. section demo chatbot ในส่วนของ chatbot ตัว demo ต้องนำ api ที่ขอ จาก plan มาใส่เพื่อทดลองเล่น
  4. section contact us ในส่วนสุดท้ายใครที่สนใจก็ติดต่อพวกเรามาได้เลย 😎

Connect the frontend with the backend

ซึ่งวันนี้ก็จะขออธิบายในส่วนของการ implement ตัว Chatbot  ซึ่งตัว Chatbot นี้เราทำมาในรูปแบบ Stream การสร้างและใช้ Streamed Text จาก API ใน React คือ รับข้อมูลข้อความจาก API แบบเรียลไทม์และนำข้อมูลนั้นมาแสดงผล ซึ่งส่วนมากเราจะนำมาใช้กับสิ่งที่แสดงข้อมูลที่มีการอัปเดตอยู่ตลอดเวลา เช่น ข้อความแชท, ราคาหุ้นและข้อมูลทางการเงินอื่น ๆ หรือข้อมูลอื่น ๆ ที่ต้องการแสดงผลเรียลไทม์ นั้นเอง

ซึ่งในส่วนของการเชื่อมต่อ API เราจะใช้ fetch โดยมี header เป็น text/event-stream โดยเมื่อเราทำการ fetch เราจะได้ ทำการ read โดยใช้ getReader() ซึ่งเป็น  stream interface ของ ReadableStream  สามารถอ่านเพิ่มเติมได้ที่

ReadableStream: getReader() method - Web APIs | MDN

useEffect(() => {
    const message = { query: watch('query') }
    const getData = async () => {
      try {
        // clear input chat
        setValue('query', '')
        const response = await fetch(
          `${API_BOT}/query?uuid=${localStorage?.session}&message=${message.query}`,
          {
            method: 'GET',
            headers: {
              Accept: 'text/event-stream',
              'x-api-key': localStorage?.apiKey, //api key
            },
          }
        )
        const reader = response.body!.getReader()
        let result = ''
        while (true) {
          const { done, value } = await reader?.read()
          if (done) {
            setStreamText('')
            setAnswer((prevState) => [
              ...prevState,
              {
                id: (prevState.length + 1).toString(),
                role: 'ai',
                message: result,
              },
            ])
            break
          }
          result += new TextDecoder().decode(value)
          setStreamText(
            (prevData) => prevData + new TextDecoder().decode(value)
          )
        }
      } catch (error: any) {
        console.error(error)
        setError('bot', {
          message: error?.response?.data?.message ?? 'Something went wrong',
        })
      }
    }
    if (isSubmitSuccessful) {
      getData()
    }
  }, [submitCount, isSubmitSuccessful, setValue, watch, setError])

ซึ่งหลังจากจัดการได้เเล้ว เราก็มาจัดการนำข้อมูลที่เราได้ไปเชื่อมต่อส่วนต่างๆ ได้เลย 💕

เราก็จะมาดูผลลัพธ์ที่เราได้กัน 😎

Implementing API key generation

หลังจากที่เราสร้าง UI interface ขึ้นมาเเล้วเราก็ต้องทำการ connect กับ ฝั่งของ backed  ที่เป็นตัว  generate API key กัน

ซึ่งตัว API Interface ที่เราได้มาก็มีดังต่อไปนี้

API Plan

GET {endpoint}/plan

Response 200 (OK)

[
  {
    "name": "20RequestPerDay"
  },
  {
    "name": "30RequestPerDay"
  },
  {
    "name": "10RequestPerDay"
  }
]

API Create Key

POST {endpoint}/create_key

Body

{
  "plan_name": "30RequestPerDay",
  "user": "example@email.com"
}

Response 200 (OK)

{
  "value": "7wuOmY7Osz721X1bQUkAP2aGUF5oOVw28EJx7MVS"
}

หลังจาก เราทำในส่วนของ connect api  get key เสร็จเเล้ว ก็จะไปแก้ในส่วนของ Project อื่น โดยการ เพิ่ม header

{
  "x-api-key": "7wuOmY7Osz721X1bQUkAP2aGUF5oOVw28EJx7MVS"
}

Implementing rate limit notification

หลังจากที่ทำการ  input validation เเล้ว เราก็จะมาจัดการ error  ซึ่งก็จะมี error ในส่วนของผู้ใช้ที่ได้ทำการขอ ไปเเล้ว และในส่วนของ User ที่ นำ API Key ไปใช้เเล้ว ใช้จนหมด usage การใช้งานนั้นเอง ซึ่งเราจะได้ Error จากในส่วนของ API

{
  "message": "Limit exceeds"
}

ในการดักเราจะดักจาก Stream โดย เมื่อ ใช้เกินจำนวน API จะส่ง status code 429 มา  เมื่อเราได้ Error status code 429 เราจะทำ set message Error ให้ Bot เพื่อแสดงผลใน Interface

  useEffect(() => {
    const message = { query: watch('query') }
    const getData = async () => {
      try {
        setValue('query', '')

        const response = await fetch(
          `${API_BOT}/query?uuid=${localStorage?.session}&message=${message.query}`,
          {
            method: 'GET',
            headers: {
              Accept: 'text/event-stream',
              'x-api-key': localStorage?.apiKey,
            },
          }
        )
        if (response.status === 200) {
          const reader = response.body!.getReader()
          let result = ''
          while (true) {
            const { done, value } = await reader?.read()
            if (done) {
              setStreamText('')
              setAnswer((prevState) => [
                ...prevState,
                {
                  id: (prevState.length + 1).toString(),
                  role: 'ai',
                  message: result,
                },
              ])
              break
            }
            result += new TextDecoder().decode(value)
            setStreamText(result)
          }
        }
        if (response.status === 429) {
          setError('bot', {
            message: 'Limit Exceeded',
          })
          return
        }
      } catch (error: any) {
        console.error(error)

        setError('bot', {
          message: error?.response?.data?.message ?? 'Something went wrong',
        })
      }
    }
    if (isSubmitSuccessful && localStorage) {
      getData()
    }
  }, [submitCount, isSubmitSuccessful, setValue, watch, setError])

ทิ้งท้ายบทความสำหรับ OpenAI with ratelimit

OpenAI with ratelimit frontend

OpenAI with ratelimit backend

Github Frontend

Github Backend

FAQ Facebook Group

Aa

© 2023, All Rights Reserved, VulturePrime co., ltd.