page.tsx 3.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112
  1. 'use client'
  2. import { useState } from 'react'
  3. const API_URL = 'http://127.0.0.1:8080'
  4. type Message = {
  5. sender: string
  6. content: string
  7. }
  8. async function completion(prompt: string, callback: (res: string) => void) {
  9. const result = await fetch(`${API_URL}/completion`, {
  10. method: 'POST',
  11. body: JSON.stringify({
  12. prompt: prompt,
  13. temperature: 0.2,
  14. top_k: 40,
  15. top_p: 0.9,
  16. n_predict: 256,
  17. stop: ['\n### Human:'], // stop completion after generating this
  18. stream: true,
  19. }),
  20. })
  21. if (!result.ok || !result.body) {
  22. return
  23. }
  24. let reader = result.body.getReader()
  25. while (true) {
  26. const { done, value } = await reader.read()
  27. if (done) {
  28. break
  29. }
  30. const t = Buffer.from(value).toString('utf8')
  31. if (t.startsWith('data: ')) {
  32. const message = JSON.parse(t.substring(6))
  33. callback(message.content)
  34. if (message.stop) {
  35. break
  36. }
  37. }
  38. }
  39. return
  40. }
  41. export default function Home() {
  42. const [prompt, setPrompt] = useState('')
  43. const [messages, setMessages] = useState<Message[]>([])
  44. return (
  45. <div className='flex min-h-screen flex-1 flex-col justify-between'>
  46. <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'>
  47. <div className='mx-auto w-full max-w-xl leading-none'>
  48. <h1 className='text-sm font-medium'>LLaMa</h1>
  49. <h2 className='text-xs text-black/50'>Meta Platforms, Inc.</h2>
  50. </div>
  51. </header>
  52. <section className='mx-auto mb-10 w-full max-w-xl flex-1 break-words'>
  53. {messages.map((m, i) => (
  54. <div className='my-4 flex gap-4' key={i}>
  55. <div className='flex-none pr-2 text-lg'>{m.sender === 'human' ? '👩' : '🤖'}</div>
  56. <div className='flex-1 text-gray-900'>
  57. {m.content}
  58. {m.sender === 'bot' && <span className='relative -top-[3px] left-1 text-[10px] text-blue-600'>⬤</span>}
  59. </div>
  60. </div>
  61. ))}
  62. </section>
  63. <div className='sticky bottom-0 bg-gradient-to-b from-transparent to-white'>
  64. <textarea
  65. autoFocus
  66. rows={1}
  67. value={prompt}
  68. placeholder='Send a message...'
  69. onChange={e => setPrompt(e.target.value)}
  70. 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'
  71. onKeyDownCapture={async e => {
  72. if (e.key === 'Enter' && !e.shiftKey) {
  73. e.preventDefault() // Prevents the newline character from being inserted
  74. // Perform your desired action here, such as submitting the form or handling the entered text
  75. await setMessages(messages => {
  76. return [...messages, { sender: 'human', content: prompt }]
  77. })
  78. const index = messages.length + 1
  79. completion(prompt, res => {
  80. setMessages(messages => {
  81. let message = messages[index]
  82. if (!message) {
  83. message = { sender: 'bot', content: '' }
  84. }
  85. message.content = message.content + res
  86. return [...messages.slice(0, index), message]
  87. })
  88. })
  89. setPrompt('')
  90. }
  91. }}
  92. ></textarea>
  93. </div>
  94. </div>
  95. )
  96. }