page.tsx 3.41 KB
Newer Older
Jeffrey Morgan's avatar
Jeffrey Morgan committed
1
'use client'
Jeffrey Morgan's avatar
Jeffrey Morgan committed
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
37
38
39
40
41
42
43
44
45
46
47
48
49
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
    }

    const t = Buffer.from(value).toString('utf8')
    if (t.startsWith('data: ')) {
      const message = JSON.parse(t.substring(6))
      callback(message.content)
      if (message.stop) {
        break
      }
    }
  }

  return
}
Jeffrey Morgan's avatar
Jeffrey Morgan committed
50
51

export default function Home() {
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
57
58
59
60
61
62
63
64
65
66
67
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
    <div className='flex min-h-screen flex-1 flex-col justify-between'>
      <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}>
            <div className='flex-none pr-2 text-lg'>{m.sender === 'human' ? '👩' : '🤖'}</div>
            <div className='flex-1 text-gray-900'>
              {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>
  )
}