saq1b commited on
Commit
81d539b
1 Parent(s): 21c5b44

Create components/AiChat.tsx

Browse files
Files changed (1) hide show
  1. components/AiChat.tsx +133 -0
components/AiChat.tsx ADDED
@@ -0,0 +1,133 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ 'use client'
2
+
3
+ import React, { useState, useEffect, useRef } from 'react'
4
+ import { Button } from "@/components/ui/button"
5
+ import { Input } from "@/components/ui/input"
6
+ import { Textarea } from "@/components/ui/textarea"
7
+ import { Card, CardContent } from "@/components/ui/card"
8
+ import { ChevronDown, ChevronUp, Send } from 'lucide-react'
9
+
10
+ export default function AIChat() {
11
+ const [messages, setMessages] = useState([])
12
+ const [input, setInput] = useState('')
13
+ const [apiKey, setApiKey] = useState('')
14
+ const [thinking, setThinking] = useState('')
15
+ const [isThinkingVisible, setIsThinkingVisible] = useState(false)
16
+ const chatEndRef = useRef(null)
17
+
18
+ useEffect(() => {
19
+ chatEndRef.current?.scrollIntoView({ behavior: 'smooth' })
20
+ }, [messages])
21
+
22
+ const handleSubmit = async (e) => {
23
+ e.preventDefault()
24
+ if (!input.trim()) return
25
+
26
+ const newMessage = { role: 'user', content: input }
27
+ setMessages([...messages, newMessage])
28
+ setInput('')
29
+
30
+ try {
31
+ // Call /think endpoint
32
+ const thinkResponse = await fetch('/api/think', {
33
+ method: 'POST',
34
+ headers: { 'Content-Type': 'application/json' },
35
+ body: JSON.stringify({ messages: [...messages, newMessage], api_key: apiKey }),
36
+ })
37
+
38
+ if (!thinkResponse.ok) throw new Error('Think request failed')
39
+
40
+ const reader = thinkResponse.body.getReader()
41
+ let thinkingContent = ''
42
+
43
+ while (true) {
44
+ const { done, value } = await reader.read()
45
+ if (done) break
46
+ const chunk = new TextDecoder().decode(value)
47
+ thinkingContent += chunk
48
+ setThinking(thinkingContent)
49
+ }
50
+
51
+ // Call /chat endpoint with the thinking result
52
+ const chatResponse = await fetch('/api/chat', {
53
+ method: 'POST',
54
+ headers: { 'Content-Type': 'application/json' },
55
+ body: JSON.stringify({ messages: [...messages, newMessage, { role: 'assistant', content: thinkingContent }], api_key: apiKey }),
56
+ })
57
+
58
+ if (!chatResponse.ok) throw new Error('Chat request failed')
59
+
60
+ const chatReader = chatResponse.body.getReader()
61
+ let chatContent = ''
62
+
63
+ while (true) {
64
+ const { done, value } = await chatReader.read()
65
+ if (done) break
66
+ const chunk = new TextDecoder().decode(value)
67
+ chatContent += chunk
68
+ setMessages(prev => [...prev.slice(0, -1), { role: 'assistant', content: chatContent }])
69
+ }
70
+ } catch (error) {
71
+ console.error('Error:', error)
72
+ setMessages(prev => [...prev, { role: 'assistant', content: 'An error occurred. Please try again.' }])
73
+ }
74
+ }
75
+
76
+ return (
77
+ <div className="min-h-screen bg-gray-900 text-white p-4 flex flex-col">
78
+ <Card className="flex-grow overflow-auto mb-4 bg-gray-800 bg-opacity-50 backdrop-filter backdrop-blur-lg">
79
+ <CardContent className="p-4">
80
+ {messages.map((message, index) => (
81
+ <div key={index} className={`mb-4 ${message.role === 'user' ? 'text-right' : 'text-left'}`}>
82
+ <div className={`inline-block p-2 rounded-lg ${message.role === 'user' ? 'bg-blue-600' : 'bg-gray-700'}`}>
83
+ {message.content}
84
+ </div>
85
+ {message.role === 'assistant' && thinking && (
86
+ <div className="mt-2">
87
+ <Button
88
+ variant="ghost"
89
+ size="sm"
90
+ onClick={() => setIsThinkingVisible(!isThinkingVisible)}
91
+ className="text-xs text-gray-400"
92
+ >
93
+ {isThinkingVisible ? <ChevronUp className="h-4 w-4" /> : <ChevronDown className="h-4 w-4" />}
94
+ Thinking Process
95
+ </Button>
96
+ {isThinkingVisible && (
97
+ <div className="mt-2 text-xs text-gray-400 bg-gray-800 p-2 rounded">
98
+ {thinking.split('\n').map((line, i) => {
99
+ if (line.startsWith('<thinking>') || line.startsWith('<step>')) {
100
+ return <p key={i}>{line.replace(/<\/?thinking>|<\/?step>/g, '')}</p>
101
+ }
102
+ return null
103
+ })}
104
+ </div>
105
+ )}
106
+ </div>
107
+ )}
108
+ </div>
109
+ ))}
110
+ <div ref={chatEndRef} />
111
+ </CardContent>
112
+ </Card>
113
+ <form onSubmit={handleSubmit} className="flex gap-2">
114
+ <Input
115
+ type="text"
116
+ value={apiKey}
117
+ onChange={(e) => setApiKey(e.target.value)}
118
+ placeholder="Enter API Key (optional)"
119
+ className="flex-grow bg-gray-800 text-white"
120
+ />
121
+ <Textarea
122
+ value={input}
123
+ onChange={(e) => setInput(e.target.value)}
124
+ placeholder="Type your message..."
125
+ className="flex-grow bg-gray-800 text-white"
126
+ />
127
+ <Button type="submit" className="bg-blue-600 hover:bg-blue-700">
128
+ <Send className="h-4 w-4" />
129
+ </Button>
130
+ </form>
131
+ </div>
132
+ )
133
+ }