1
+ 'use client'
2
+
3
+ import React , { useState , useEffect , Suspense } from 'react' ;
4
+ import { Container } from '@/components/Container' ;
5
+ import { useChat } from 'ai/react' ;
6
+ import { clsx } from 'clsx' ;
7
+ import RandomPortrait from '@/components/RandomPortrait' ;
8
+ import SearchForm from '@/components/SearchForm' ;
9
+ import { LoadingAnimation } from '@/components/LoadingAnimation' ;
10
+ import { ContentCard } from '@/components/ContentCard' ;
11
+ import { BlogWithSlug } from '@/types' ;
12
+
13
+ const prepopulatedQuestions = [
14
+ "What is the programming bug?" ,
15
+ "Why do you love Next.js so much?" ,
16
+ "What do you do at Pinecone?" ,
17
+ "How can I become a better developer?" ,
18
+ "What is ggshield and why is it important?" ,
19
+ "How can I use AI to complete side projects more quickly?"
20
+ ] ;
21
+
22
+ export default function ChatPageClientDemo ( ) {
23
+ const [ isLoading , setIsLoading ] = useState ( false ) ;
24
+ const [ articles , setArticles ] = useState < BlogWithSlug [ ] > ( [ ] ) ;
25
+ const [ isClient , setIsClient ] = useState ( false ) ;
26
+
27
+ const { messages, input, setInput, handleSubmit, reload : resetChat } = useChat ( {
28
+ onResponse ( response ) {
29
+ const sourcesHeader = response . headers . get ( 'x-sources' ) ;
30
+ const parsedArticles : BlogWithSlug [ ] = sourcesHeader
31
+ ? ( JSON . parse ( atob ( sourcesHeader as string ) ) as BlogWithSlug [ ] )
32
+ : [ ] ;
33
+ setArticles ( parsedArticles ) ;
34
+ setIsLoading ( false ) ;
35
+ } ,
36
+ headers : { } ,
37
+ onFinish ( ) { } ,
38
+ onError ( ) {
39
+ setIsLoading ( false ) ;
40
+ }
41
+ } ) ;
42
+
43
+ const handleSearch = async ( query : string ) => {
44
+ setInput ( query ) ;
45
+ const customSubmitEvent = {
46
+ preventDefault : ( ) => { } ,
47
+ } as unknown as React . FormEvent < HTMLFormElement > ;
48
+ await handleSubmit ( customSubmitEvent ) ;
49
+ } ;
50
+
51
+ const clearChatState = ( ) => {
52
+ resetChat ( ) ;
53
+ setArticles ( [ ] ) ;
54
+ setInput ( "" ) ;
55
+ const currentMessages = messages . length ;
56
+ setTimeout ( ( ) => {
57
+ if ( messages . length === currentMessages ) {
58
+ resetChat ( ) ;
59
+ }
60
+ } , 100 ) ;
61
+ } ;
62
+
63
+ useEffect ( ( ) => {
64
+ setIsClient ( true ) ;
65
+ } , [ ] ) ;
66
+
67
+ return (
68
+ < Container >
69
+ < div className = "max-w-7xl mx-auto mt-16 sm:mt-32" >
70
+ < div className = "flex flex-col md:flex-row items-start mb-12" >
71
+ < div className = "flex-1 pl-8" >
72
+ < h1 className = "text-4xl font-bold tracking-tight text-zinc-800 dark:text-zinc-100 sm:text-5xl mb-6" >
73
+ Internal RAG Chatbot Demo
74
+ </ h1 >
75
+ < p className = "text-base text-zinc-600 dark:text-zinc-400 mb-6" >
76
+ This is a demonstration of a Retrieval-Augmented Generation (RAG) chatbot. Enter your question below to see how it responds using internal documentation.
77
+ </ p >
78
+ </ div >
79
+ < div className = "mt-6 md:mt-0 w-full md:w-80 h-80" >
80
+ < Suspense fallback = { < div className = "w-full h-full bg-gray-200 rounded-lg pr-8 mr-8" > </ div > } >
81
+ < RandomPortrait width = { 300 } height = { 300 } />
82
+ </ Suspense >
83
+ </ div >
84
+ </ div >
85
+
86
+ { /* Chat interface */ }
87
+ < div className = "mb-8" >
88
+ < SearchForm
89
+ suggestedSearches = { prepopulatedQuestions }
90
+ onSearch = { handleSearch }
91
+ setIsLoading = { setIsLoading }
92
+ buttonText = "Ask question"
93
+ onClearChat = { clearChatState }
94
+ showClearButton = { messages . length > 0 }
95
+ />
96
+ </ div >
97
+
98
+ { isLoading && messages ?. length > 0 && < LoadingAnimation /> }
99
+
100
+ { /* Chat messages and related posts */ }
101
+ < div className = "flex flex-col md:flex-row" >
102
+ < div className = "flex-1 pr-0 md:pr-6 mb-6 md:mb-0" >
103
+ { messages . map ( ( m ) => (
104
+ < div
105
+ key = { m . id }
106
+ className = "mb-4 whitespace-pre-wrap text-lg leading-relaxed"
107
+ >
108
+ < span
109
+ className = { clsx ( 'font-bold' , {
110
+ 'text-blue-700' : m . role === 'user' ,
111
+ 'text-green-700' : m . role !== 'user' ,
112
+ } ) }
113
+ >
114
+ { m . role === 'user'
115
+ ? 'You: '
116
+ : "Chatbot: " }
117
+ </ span >
118
+ { m . content }
119
+ </ div >
120
+ ) ) }
121
+ </ div >
122
+ < div className = "md:w-1/3" >
123
+ { Array . isArray ( articles ) && ( articles . length > 0 ) && (
124
+ < div className = "mt-8" >
125
+ < h3 className = "mb-4 text-xl font-semibold" > Related Posts</ h3 >
126
+ < div className = "space-y-4" >
127
+ { ( articles as BlogWithSlug [ ] ) . map ( ( article ) => (
128
+ < ContentCard key = { article . slug } article = { article } />
129
+ ) ) }
130
+ </ div >
131
+ </ div >
132
+ ) }
133
+ </ div >
134
+ </ div >
135
+ </ div >
136
+ </ Container >
137
+ ) ;
138
+ }
0 commit comments