app.tsx 3.82 KB
Newer Older
Jeffrey Morgan's avatar
Jeffrey Morgan committed
1
2
import { useState } from 'react'

Jeffrey Morgan's avatar
Jeffrey Morgan committed
3
const API_URL = 'http://127.0.0.1:5001'
Jeffrey Morgan's avatar
Jeffrey Morgan committed
4
5
6
7
8
9
10

type Message = {
  sender: string
  content: string
}

async function completion(prompt: string, callback: (res: string) => void) {
11
  const result = await fetch(`${API_URL}/generate`, {
Jeffrey Morgan's avatar
Jeffrey Morgan committed
12
    method: 'POST',
13
14
15
    headers: {
      'Content-Type': 'application/json',
    },
Jeffrey Morgan's avatar
Jeffrey Morgan committed
16
    body: JSON.stringify({
17
      prompt: prompt,
18
      model: 'ggml-model-q4_0',
Jeffrey Morgan's avatar
Jeffrey Morgan committed
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
    }),
  })

  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
35
36
    let decoder = new TextDecoder()
    let str = decoder.decode(value)
37
38
39
40
41
42
43
    let re = /}{/g
    str = '[' + str.replace(re, '},{') + ']'
    let messages = JSON.parse(str)

    for (const message of messages) {
      const choice = message.choices[0]
      if (choice.finish_reason === 'stop') {
Jeffrey Morgan's avatar
Jeffrey Morgan committed
44
45
        break
      }
46
47

      callback(choice.text)
Jeffrey Morgan's avatar
Jeffrey Morgan committed
48
49
50
51
52
    }
  }

  return
}
Jeffrey Morgan's avatar
Jeffrey Morgan committed
53

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

Jeffrey Morgan's avatar
Jeffrey Morgan committed
58
  return (
Jeffrey Morgan's avatar
Jeffrey Morgan committed
59
    <div className='flex min-h-screen flex-1 flex-col justify-between bg-white'>
Jeffrey Morgan's avatar
Jeffrey Morgan committed
60
61
62
63
64
65
66
67
68
      <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}>
69
70
71
72
73
74
75
76
77
78
79
            <div className='flex-none pr-1 text-lg'>
              {m.sender === 'human' ? (
                <div className='bg-neutral-200 text-neutral-700 text-sm h-6 w-6 rounded-md flex items-center justify-center mt-px'>
                  H
                </div>
              ) : (
                <div className='bg-blue-600 text-white text-sm h-6 w-6 rounded-md flex items-center justify-center mt-0.5'>
                  L
                </div>
              )}
            </div>
Jeffrey Morgan's avatar
Jeffrey Morgan committed
80
            <div className='flex-1 text-gray-800'>
Jeffrey Morgan's avatar
Jeffrey Morgan committed
81
              {m.content}
82
              {m.sender === 'bot' && <span className='relative -top-[3px] left-1 text-[10px]'></span>}
Jeffrey Morgan's avatar
Jeffrey Morgan committed
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
110
111
112
113
114
115
116
117
118
119
120
121
122
            </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
123
124
125
    </div>
  )
}