-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathindex.tsx
197 lines (182 loc) · 6.19 KB
/
index.tsx
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
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
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
'use client';
import React, { useEffect, useState, useRef, useCallback } from 'react';
import {
ResizablePanelGroup,
ResizablePanel,
ResizableHandle,
} from '@/components/ui/resizable';
import { GET_CHAT_HISTORY } from '@/graphql/request';
import { useQuery } from '@apollo/client';
import { toast } from 'sonner';
import { EventEnum } from '@/const/EventEnum';
import ChatContent from '@/components/chat/chat-panel';
import { useModels } from '@/hooks/useModels';
import { useChatList } from '@/hooks/useChatList';
import { useChatStream } from '@/hooks/useChatStream';
import { CodeEngine } from './code-engine/code-engine';
import { useProjectStatusMonitor } from '@/hooks/useProjectStatusMonitor';
import { useAuthContext } from '@/providers/AuthProvider';
export default function Chat() {
// Initialize state, refs, and custom hooks
const { isAuthorized } = useAuthContext();
const urlParams = new URLSearchParams(window.location.search);
const [chatId, setChatId] = useState('');
const [messages, setMessages] = useState([]);
const [thinkingProcess, setThinkingProcess] = useState([]);
const [input, setInput] = useState('');
const formRef = useRef<HTMLFormElement>(null);
const { models } = useModels();
const [selectedModel, setSelectedModel] = useState(models[0] || 'gpt-4o');
const { refetchChats } = useChatList();
const [isTPUpdating, setIsTPUpdating] = useState(false);
// Project status monitoring for the current chat
const { isReady, projectId, projectName, error } =
useProjectStatusMonitor(chatId);
// Apollo query to fetch chat history
useQuery(GET_CHAT_HISTORY, {
variables: { chatId },
skip: !isAuthorized || !chatId,
onCompleted: (data) => {
if (data?.getChatHistory) {
const processedMessages = data.getChatHistory.map((msg) => {
try {
const content = JSON.parse(msg.content);
return {
id: msg.id,
role: msg.role,
content: content.final_response,
createdAt: msg.createdAt,
thinking_process: content.thinking_process,
};
} catch (e) {
return msg;
}
});
setMessages(processedMessages);
const tpMessages = processedMessages
.filter((msg) => msg.thinking_process)
.map((msg) => ({
id: msg.id,
role: msg.role,
content: msg.thinking_process,
createdAt: msg.createdAt,
}));
setThinkingProcess(tpMessages);
}
},
onError: () => {
toast.error('Failed to load chat history');
},
});
// Custom hook for handling chat streaming
const { loadingSubmit, handleSubmit, handleInputChange, stop } =
useChatStream({
chatId,
input,
setInput,
setMessages,
setThinkingProcess,
selectedModel,
setIsTPUpdating,
});
// Callback to clear the chat ID
const cleanChatId = () => setChatId('');
// Callback to update chat ID based on URL parameters and refresh the chat list
const updateChatId = useCallback(() => {
const params = new URLSearchParams(window.location.search);
setChatId(params.get('id') || '');
refetchChats();
}, [refetchChats]);
// Callback to switch to the settings view
const updateSetting = () => setChatId(EventEnum.SETTING);
const updateProject = () => setChatId(EventEnum.NEW_PROJECT);
// Effect to initialize chat ID and refresh the chat list based on URL parameters
useEffect(() => {
const newChatId = urlParams.get('id') || '';
if (newChatId !== chatId) {
setChatId(newChatId);
refetchChats();
}
}, [urlParams, chatId, refetchChats]);
// Effect to add and remove global event listeners
useEffect(() => {
window.addEventListener(EventEnum.CHAT, updateChatId);
window.addEventListener(EventEnum.NEW_CHAT, cleanChatId);
window.addEventListener(EventEnum.SETTING, updateSetting);
window.addEventListener(EventEnum.NEW_PROJECT, updateProject);
return () => {
window.removeEventListener(EventEnum.CHAT, updateChatId);
window.removeEventListener(EventEnum.NEW_PROJECT, updateProject);
window.removeEventListener(EventEnum.NEW_CHAT, cleanChatId);
window.removeEventListener(EventEnum.SETTING, updateSetting);
};
}, [updateChatId]);
// Render the main layout
return chatId ? (
<ResizablePanelGroup
direction="horizontal"
className="h-full w-full p-2"
key="with-chat"
>
<ResizablePanel
defaultSize={40}
minSize={20}
maxSize={70}
className="h-full"
>
<div className="h-full overflow-hidden">
<ChatContent
chatId={chatId}
setSelectedModel={setSelectedModel}
messages={messages}
thinkingProcess={thinkingProcess}
input={input}
handleInputChange={handleInputChange}
handleSubmit={handleSubmit}
loadingSubmit={loadingSubmit}
stop={stop}
formRef={formRef}
setInput={setInput}
setMessages={setMessages}
setThinkingProcess={setThinkingProcess}
isTPUpdating={isTPUpdating}
/>
</div>
</ResizablePanel>
<ResizableHandle withHandle className="bg-border/20 w-[3px]" />
<ResizablePanel
defaultSize={60}
minSize={30}
maxSize={80}
className="h-full"
>
<div className="h-full overflow-auto">
<CodeEngine
chatId={chatId}
isProjectReady={isReady}
projectId={projectId}
/>
</div>
</ResizablePanel>
</ResizablePanelGroup>
) : (
<div className="h-full w-full" key="without-chat">
<ChatContent
chatId={chatId}
setSelectedModel={setSelectedModel}
messages={messages}
thinkingProcess={thinkingProcess}
input={input}
handleInputChange={handleInputChange}
handleSubmit={handleSubmit}
loadingSubmit={loadingSubmit}
stop={stop}
formRef={formRef}
setInput={setInput}
setMessages={setMessages}
setThinkingProcess={setThinkingProcess}
isTPUpdating={isTPUpdating}
/>
</div>
);
}