1
1
'use client' ;
2
2
3
- import { ChatLayout } from " @/components/chat/chat-layout" ;
4
- import { getSelectedModel } from " @/lib/model-helper" ;
5
- import React , { useEffect , useRef , useState } from " react" ;
6
- import { toast } from " sonner" ;
7
- import useChatStore from " ../hooks/useChatStore" ;
8
- import { Message } from " @/components/types" ;
3
+ import { ChatLayout } from ' @/components/chat/chat-layout' ;
4
+ import { getSelectedModel } from ' @/lib/model-helper' ;
5
+ import React , { useEffect , useRef , useState } from ' react' ;
6
+ import { toast } from ' sonner' ;
7
+ import useChatStore from ' ../hooks/useChatStore' ;
8
+ import { Message } from ' @/components/types' ;
9
9
10
10
interface ChatStreamResponse {
11
11
chatStream : {
@@ -31,18 +31,17 @@ interface Attachment {
31
31
export default function Page ( { params } : { params : { id : string } } ) {
32
32
// 状态管理
33
33
const [ messages , setMessages ] = useState < Message [ ] > ( [ ] ) ;
34
- const [ input , setInput ] = useState ( "" ) ;
34
+ const [ input , setInput ] = useState ( '' ) ;
35
35
const [ isLoading , setIsLoading ] = useState ( false ) ;
36
36
const [ error , setError ] = useState < Error | null > ( null ) ;
37
- const [ selectedModel , setSelectedModel ] = useState < string > (
38
- getSelectedModel ( )
39
- ) ;
37
+ const [ selectedModel , setSelectedModel ] =
38
+ useState < string > ( getSelectedModel ( ) ) ;
40
39
const [ loadingSubmit , setLoadingSubmit ] = useState ( false ) ;
41
40
const formRef = useRef < HTMLFormElement > ( null ) ;
42
41
const ws = useRef < WebSocket | null > ( null ) ;
43
42
const base64Images = useChatStore ( ( state ) => state . base64Images ) ;
44
43
const setBase64Images = useChatStore ( ( state ) => state . setBase64Images ) ;
45
- const [ currentAssistantMessage , setCurrentAssistantMessage ] = useState ( "" ) ;
44
+ const [ currentAssistantMessage , setCurrentAssistantMessage ] = useState ( '' ) ;
46
45
47
46
// 初始化 WebSocket 连接
48
47
useEffect ( ( ) => {
@@ -65,35 +64,35 @@ export default function Page({ params }: { params: { id: string } }) {
65
64
useEffect ( ( ) => {
66
65
if ( ! isLoading && ! error && messages . length > 0 ) {
67
66
localStorage . setItem ( `chat_${ params . id } ` , JSON . stringify ( messages ) ) ;
68
- window . dispatchEvent ( new Event ( " storage" ) ) ;
67
+ window . dispatchEvent ( new Event ( ' storage' ) ) ;
69
68
}
70
69
} , [ messages , isLoading , error , params . id ] ) ;
71
70
72
71
const initWebSocket = ( ) => {
73
- ws . current = new WebSocket ( " ws://localhost:8080/graphql" ) ;
72
+ ws . current = new WebSocket ( ' ws://localhost:8080/graphql' ) ;
74
73
75
74
ws . current . onopen = ( ) => {
76
- console . log ( " WebSocket connected" ) ;
75
+ console . log ( ' WebSocket connected' ) ;
77
76
} ;
78
77
79
78
ws . current . onerror = ( error ) => {
80
- console . error ( " WebSocket error:" , error ) ;
81
- toast . error ( " Connection error. Retrying..." ) ;
79
+ console . error ( ' WebSocket error:' , error ) ;
80
+ toast . error ( ' Connection error. Retrying...' ) ;
82
81
setTimeout ( initWebSocket , 3000 ) ;
83
82
} ;
84
83
85
84
ws . current . onclose = ( ) => {
86
- console . log ( " WebSocket closed" ) ;
85
+ console . log ( ' WebSocket closed' ) ;
87
86
setTimeout ( initWebSocket , 3000 ) ;
88
87
} ;
89
88
} ;
90
89
91
90
const loadChatHistory = async ( chatId : string ) => {
92
91
try {
93
- const response = await fetch ( " http://localhost:8080/graphql" , {
94
- method : " POST" ,
92
+ const response = await fetch ( ' http://localhost:8080/graphql' , {
93
+ method : ' POST' ,
95
94
headers : {
96
- " Content-Type" : " application/json" ,
95
+ ' Content-Type' : ' application/json' ,
97
96
} ,
98
97
body : JSON . stringify ( {
99
98
query : `
@@ -120,7 +119,7 @@ export default function Page({ params }: { params: { id: string } }) {
120
119
const savedMessages = data . data . getChatHistory || [ ] ;
121
120
setMessages ( savedMessages ) ;
122
121
} catch ( error ) {
123
- console . error ( " Error loading chat history:" , error ) ;
122
+ console . error ( ' Error loading chat history:' , error ) ;
124
123
// 尝试从本地存储加载
125
124
const localMessages = localStorage . getItem ( `chat_${ chatId } ` ) ;
126
125
if ( localMessages ) {
@@ -137,7 +136,7 @@ export default function Page({ params }: { params: { id: string } }) {
137
136
if ( ws . current ?. readyState === WebSocket . OPEN ) {
138
137
ws . current . send (
139
138
JSON . stringify ( {
140
- type : " stop" ,
139
+ type : ' stop' ,
141
140
id : params . id ,
142
141
} )
143
142
) ;
@@ -152,7 +151,7 @@ export default function Page({ params }: { params: { id: string } }) {
152
151
ws . current . readyState !== WebSocket . OPEN
153
152
) {
154
153
if ( ! ws . current || ws . current . readyState !== WebSocket . OPEN ) {
155
- toast . error ( " Connection lost. Reconnecting..." ) ;
154
+ toast . error ( ' Connection lost. Reconnecting...' ) ;
156
155
initWebSocket ( ) ;
157
156
}
158
157
return ;
@@ -162,25 +161,25 @@ export default function Page({ params }: { params: { id: string } }) {
162
161
163
162
const newMessage : Message = {
164
163
id : params . id ,
165
- role : " user" ,
164
+ role : ' user' ,
166
165
content : input ,
167
166
createdAt : new Date ( ) . toISOString ( ) ,
168
167
} ;
169
168
170
169
setMessages ( ( prev ) => [ ...prev , newMessage ] ) ;
171
- setInput ( "" ) ;
172
- setCurrentAssistantMessage ( "" ) ;
170
+ setInput ( '' ) ;
171
+ setCurrentAssistantMessage ( '' ) ;
173
172
174
173
const attachments = base64Images
175
174
? base64Images . map ( ( image ) => ( {
176
- contentType : " image/base64" ,
175
+ contentType : ' image/base64' ,
177
176
url : image ,
178
177
} ) )
179
178
: [ ] ;
180
179
181
180
// 发送 GraphQL subscription 请求
182
181
const subscriptionMsg = {
183
- type : " start" ,
182
+ type : ' start' ,
184
183
id : Date . now ( ) . toString ( ) ,
185
184
payload : {
186
185
query : `
@@ -215,15 +214,15 @@ export default function Page({ params }: { params: { id: string } }) {
215
214
ws . current . onmessage = ( event ) => {
216
215
const response = JSON . parse ( event . data ) ;
217
216
218
- if ( response . type === " data" && response . payload . data ) {
217
+ if ( response . type === ' data' && response . payload . data ) {
219
218
const chunk = response . payload . data . chatStream ;
220
219
const content = chunk . choices [ 0 ] ?. delta ?. content ;
221
220
222
221
if ( content ) {
223
222
setCurrentAssistantMessage ( ( prev ) => prev + content ) ;
224
223
setMessages ( ( prev ) => {
225
224
const lastMsg = prev [ prev . length - 1 ] ;
226
- if ( lastMsg ?. role === " assistant" ) {
225
+ if ( lastMsg ?. role === ' assistant' ) {
227
226
return [
228
227
...prev . slice ( 0 , - 1 ) ,
229
228
{
@@ -236,7 +235,7 @@ export default function Page({ params }: { params: { id: string } }) {
236
235
...prev ,
237
236
{
238
237
id : chunk . id ,
239
- role : " assistant" ,
238
+ role : ' assistant' ,
240
239
content,
241
240
createdAt : new Date ( chunk . created * 1000 ) . toISOString ( ) ,
242
241
} ,
@@ -245,22 +244,22 @@ export default function Page({ params }: { params: { id: string } }) {
245
244
} ) ;
246
245
}
247
246
248
- if ( chunk . choices [ 0 ] ?. finish_reason === " stop" ) {
247
+ if ( chunk . choices [ 0 ] ?. finish_reason === ' stop' ) {
249
248
setLoadingSubmit ( false ) ;
250
- setCurrentAssistantMessage ( "" ) ;
249
+ setCurrentAssistantMessage ( '' ) ;
251
250
252
251
// 保存消息
253
252
localStorage . setItem ( `chat_${ params . id } ` , JSON . stringify ( messages ) ) ;
254
- window . dispatchEvent ( new Event ( " storage" ) ) ;
253
+ window . dispatchEvent ( new Event ( ' storage' ) ) ;
255
254
}
256
255
}
257
256
} ;
258
257
259
258
ws . current . send ( JSON . stringify ( subscriptionMsg ) ) ;
260
259
setBase64Images ( null ) ;
261
260
} catch ( error ) {
262
- console . error ( " Error:" , error ) ;
263
- toast . error ( " Failed to send message" ) ;
261
+ console . error ( ' Error:' , error ) ;
262
+ toast . error ( ' Failed to send message' ) ;
264
263
setLoadingSubmit ( false ) ;
265
264
}
266
265
} ;
0 commit comments