app.tsx 4.09 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
18
19
20
21
22
      prompt: `A chat between a curious human and an artificial intelligence assistant. The assistant gives helpful, detailed, and polite answers to the human's questions.

      ### Human: Hello, Assistant.
      ### Assistant: Hello. How may I help you today?
      ### Human: ${prompt}`,
      model: 'ggml-model-q4_0',
Jeffrey Morgan's avatar
Jeffrey Morgan committed
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
    }),
  })

  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
39
40
    let decoder = new TextDecoder()
    let str = decoder.decode(value)
41
42
43
44
45
46
47
    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
48
49
        break
      }
50
51

      callback(choice.text)
Jeffrey Morgan's avatar
Jeffrey Morgan committed
52
53
54
55
56
    }
  }

  return
}
Jeffrey Morgan's avatar
Jeffrey Morgan committed
57

Jeffrey Morgan's avatar
Jeffrey Morgan committed
58
export default function () {
Jeffrey Morgan's avatar
Jeffrey Morgan committed
59
60
61
  const [prompt, setPrompt] = useState('')
  const [messages, setMessages] = useState<Message[]>([])

Jeffrey Morgan's avatar
Jeffrey Morgan committed
62
  return (
Jeffrey Morgan's avatar
Jeffrey Morgan committed
63
    <div className='flex min-h-screen flex-1 flex-col justify-between bg-white'>
Jeffrey Morgan's avatar
Jeffrey Morgan committed
64
65
66
67
68
69
70
71
72
      <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}>
73
74
75
76
77
78
79
80
81
82
83
            <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
84
            <div className='flex-1 text-gray-800'>
Jeffrey Morgan's avatar
Jeffrey Morgan committed
85
              {m.content}
86
              {m.sender === 'bot' && <span className='relative -top-[3px] left-1 text-[10px]'></span>}
Jeffrey Morgan's avatar
Jeffrey Morgan committed
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
123
124
125
126
            </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
127
128
129
    </div>
  )
}