app.tsx 3.43 KB
Newer Older
Jeffrey Morgan's avatar
Jeffrey Morgan committed
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
import { useState } from 'react'

const API_URL = 'http://127.0.0.1:8080'

type Message = {
  sender: string
  content: string
}

async function completion(prompt: string, callback: (res: string) => void) {
  const result = await fetch(`${API_URL}/completion`, {
    method: 'POST',
    body: JSON.stringify({
      prompt: prompt,
      temperature: 0.2,
      top_k: 40,
      top_p: 0.9,
      n_predict: 256,
      stop: ['\n### Human:'], // stop completion after generating this
      stream: true,
    }),
  })

  if (!result.ok || !result.body) {
    return
  }

  let reader = result.body.getReader()

  while (true) {
    const { done, value } = await reader.read()

    if (done) {
      break
    }

Jeffrey Morgan's avatar
Jeffrey Morgan committed
37
38
39
40
    let decoder = new TextDecoder()
    let str = decoder.decode(value)
    if (str.startsWith('data: ')) {
      const message = JSON.parse(str.substring(6))
Jeffrey Morgan's avatar
Jeffrey Morgan committed
41
42
43
44
45
46
47
48
49
      callback(message.content)
      if (message.stop) {
        break
      }
    }
  }

  return
}
Jeffrey Morgan's avatar
Jeffrey Morgan committed
50

Jeffrey Morgan's avatar
Jeffrey Morgan committed
51
export default function () {
Jeffrey Morgan's avatar
Jeffrey Morgan committed
52
53
54
  const [prompt, setPrompt] = useState('')
  const [messages, setMessages] = useState<Message[]>([])

Jeffrey Morgan's avatar
Jeffrey Morgan committed
55
  return (
Jeffrey Morgan's avatar
Jeffrey Morgan committed
56
    <div className='flex min-h-screen flex-1 flex-col justify-between bg-white'>
Jeffrey Morgan's avatar
Jeffrey Morgan committed
57
58
59
60
61
62
63
64
65
      <header className='drag sticky top-0 z-50 flex w-full flex-row items-center border-b border-black/5 bg-gray-50/75 p-3 backdrop-blur-md'>
        <div className='mx-auto w-full max-w-xl leading-none'>
          <h1 className='text-sm font-medium'>LLaMa</h1>
          <h2 className='text-xs text-black/50'>Meta Platforms, Inc.</h2>
        </div>
      </header>
      <section className='mx-auto mb-10 w-full max-w-xl flex-1 break-words'>
        {messages.map((m, i) => (
          <div className='my-4 flex gap-4' key={i}>
Jeffrey Morgan's avatar
Jeffrey Morgan committed
66
67
            <div className='flex-none pr-1 text-lg'>{m.sender === 'human' ? '👩' : '🤖'}</div>
            <div className='flex-1 text-gray-800'>
Jeffrey Morgan's avatar
Jeffrey Morgan committed
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
              {m.content}
              {m.sender === 'bot' && <span className='relative -top-[3px] left-1 text-[10px] text-blue-600'></span>}
            </div>
          </div>
        ))}
      </section>
      <div className='sticky bottom-0 bg-gradient-to-b from-transparent to-white'>
        <textarea
          autoFocus
          rows={1}
          value={prompt}
          placeholder='Send a message...'
          onChange={e => setPrompt(e.target.value)}
          className='mx-auto my-4 block w-full max-w-xl resize-none rounded-xl border border-gray-200 px-5 py-3.5 text-[15px] shadow-lg shadow-black/5 focus:outline-none'
          onKeyDownCapture={async e => {
            if (e.key === 'Enter' && !e.shiftKey) {
              e.preventDefault() // Prevents the newline character from being inserted
              // Perform your desired action here, such as submitting the form or handling the entered text

              await setMessages(messages => {
                return [...messages, { sender: 'human', content: prompt }]
              })

              const index = messages.length + 1
              completion(prompt, res => {
                setMessages(messages => {
                  let message = messages[index]
                  if (!message) {
                    message = { sender: 'bot', content: '' }
                  }

                  message.content = message.content + res

                  return [...messages.slice(0, index), message]
                })
              })

              setPrompt('')
            }
          }}
        ></textarea>
      </div>
Jeffrey Morgan's avatar
Jeffrey Morgan committed
110
111
112
    </div>
  )
}