Girish Jeswani commited on
Commit
d8a51fd
·
1 Parent(s): 2619dcc

add orchestrator ui

Browse files
phd-advisor-frontend/src/App.js CHANGED
@@ -12,6 +12,13 @@ function App() {
12
 
13
  const navigateToHome = () => {
14
  setCurrentView('home');
 
 
 
 
 
 
 
15
  };
16
 
17
  return (
 
12
 
13
  const navigateToHome = () => {
14
  setCurrentView('home');
15
+ // Reset session when going home
16
+ fetch('http://localhost:8000/reset-session', {
17
+ method: 'POST',
18
+ headers: {
19
+ 'Content-Type': 'application/json',
20
+ }
21
+ }).catch(console.error);
22
  };
23
 
24
  return (
phd-advisor-frontend/src/pages/ChatPage.js CHANGED
@@ -9,6 +9,7 @@ const ChatPage = ({ onNavigateToHome }) => {
9
  const [messages, setMessages] = useState([]);
10
  const [isLoading, setIsLoading] = useState(false);
11
  const [thinkingAdvisors, setThinkingAdvisors] = useState([]);
 
12
  const messagesEndRef = useRef(null);
13
 
14
  const scrollToBottom = () => {
@@ -20,17 +21,16 @@ const ChatPage = ({ onNavigateToHome }) => {
20
  }, [messages, thinkingAdvisors]);
21
 
22
  const handleSendMessage = async (inputMessage) => {
 
23
  const userMessage = {
24
  type: 'user',
25
  content: inputMessage,
26
  timestamp: new Date()
27
  };
28
-
29
  setMessages(prev => [...prev, userMessage]);
30
- setIsLoading(true);
31
 
32
- // Show thinking indicators for all advisors
33
- setThinkingAdvisors(['methodist', 'theorist', 'pragmatist']);
34
 
35
  try {
36
  const response = await fetch('http://localhost:8000/chat', {
@@ -39,8 +39,7 @@ const ChatPage = ({ onNavigateToHome }) => {
39
  'Content-Type': 'application/json',
40
  },
41
  body: JSON.stringify({
42
- user_input: inputMessage,
43
- active_personas: ['methodist', 'theorist', 'pragmatist']
44
  }),
45
  });
46
 
@@ -49,34 +48,64 @@ const ChatPage = ({ onNavigateToHome }) => {
49
  }
50
 
51
  const data = await response.json();
52
-
53
- // Clear thinking indicators
54
  setThinkingAdvisors([]);
55
 
56
- // Add advisor responses with staggered timing for better UX
57
- data.forEach((advisorResponse, index) => {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
58
  setTimeout(() => {
59
- // Map the API persona names to our advisor IDs
60
- let advisorId = 'methodist'; // default fallback
61
-
62
- if (advisorResponse.persona.toLowerCase().includes('methodist')) {
63
- advisorId = 'methodist';
64
- } else if (advisorResponse.persona.toLowerCase().includes('theorist')) {
65
- advisorId = 'theorist';
66
- } else if (advisorResponse.persona.toLowerCase().includes('pragmatist')) {
67
- advisorId = 'pragmatist';
68
- }
69
 
70
- const message = {
71
- type: 'advisor',
72
- advisorId,
73
- content: advisorResponse.response,
74
- timestamp: new Date()
75
- };
76
-
77
- setMessages(prev => [...prev, message]);
78
- }, index * 800); // Stagger responses by 800ms
79
- });
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
80
 
81
  } catch (error) {
82
  console.error('Error sending message:', error);
@@ -105,7 +134,12 @@ const ChatPage = ({ onNavigateToHome }) => {
105
  </button>
106
  <div>
107
  <h1 className="chat-title">Advisory Panel Chat</h1>
108
- <p className="chat-subtitle">Consulting with your three advisors</p>
 
 
 
 
 
109
  </div>
110
  </div>
111
  <div className="advisor-indicators">
@@ -142,12 +176,56 @@ const ChatPage = ({ onNavigateToHome }) => {
142
  </div>
143
  )}
144
 
145
- {messages.map((message, index) => (
146
- <MessageBubble key={index} message={message} />
147
- ))}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
148
 
149
  {/* Thinking Indicators */}
150
- {thinkingAdvisors.map(advisorId => (
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
151
  <ThinkingIndicator key={advisorId} advisorId={advisorId} />
152
  ))}
153
 
 
9
  const [messages, setMessages] = useState([]);
10
  const [isLoading, setIsLoading] = useState(false);
11
  const [thinkingAdvisors, setThinkingAdvisors] = useState([]);
12
+ const [collectedInfo, setCollectedInfo] = useState({});
13
  const messagesEndRef = useRef(null);
14
 
15
  const scrollToBottom = () => {
 
21
  }, [messages, thinkingAdvisors]);
22
 
23
  const handleSendMessage = async (inputMessage) => {
24
+ // Add user message immediately
25
  const userMessage = {
26
  type: 'user',
27
  content: inputMessage,
28
  timestamp: new Date()
29
  };
 
30
  setMessages(prev => [...prev, userMessage]);
 
31
 
32
+ setIsLoading(true);
33
+ setThinkingAdvisors(['system']); // Show thinking indicator for orchestrator/system
34
 
35
  try {
36
  const response = await fetch('http://localhost:8000/chat', {
 
39
  'Content-Type': 'application/json',
40
  },
41
  body: JSON.stringify({
42
+ user_input: inputMessage
 
43
  }),
44
  });
45
 
 
48
  }
49
 
50
  const data = await response.json();
 
 
51
  setThinkingAdvisors([]);
52
 
53
+ // Update collected info if provided
54
+ if (data.collected_info) {
55
+ setCollectedInfo(data.collected_info);
56
+ }
57
+
58
+ if (data.type === 'orchestrator_question') {
59
+ // Orchestrator is asking for clarification
60
+ const orchestratorMessage = {
61
+ type: 'orchestrator',
62
+ content: data.responses[0].response,
63
+ timestamp: new Date()
64
+ };
65
+ setMessages(prev => [...prev, orchestratorMessage]);
66
+
67
+ } else if (data.type === 'advisor_responses') {
68
+ // Show thinking indicators for advisors before their responses
69
+ setThinkingAdvisors(['methodist', 'theorist', 'pragmatist']);
70
+
71
+ // Add a small delay then show advisor responses
72
  setTimeout(() => {
73
+ setThinkingAdvisors([]);
 
 
 
 
 
 
 
 
 
74
 
75
+ // Add advisor responses with staggered timing
76
+ data.responses.forEach((advisorResponse, index) => {
77
+ setTimeout(() => {
78
+ let advisorId = 'methodist';
79
+
80
+ if (advisorResponse.persona.toLowerCase().includes('methodist')) {
81
+ advisorId = 'methodist';
82
+ } else if (advisorResponse.persona.toLowerCase().includes('theorist')) {
83
+ advisorId = 'theorist';
84
+ } else if (advisorResponse.persona.toLowerCase().includes('pragmatist')) {
85
+ advisorId = 'pragmatist';
86
+ }
87
+
88
+ const message = {
89
+ type: 'advisor',
90
+ advisorId,
91
+ content: advisorResponse.response,
92
+ timestamp: new Date()
93
+ };
94
+
95
+ setMessages(prev => [...prev, message]);
96
+ }, index * 800);
97
+ });
98
+ }, 1000); // Small delay to show advisor thinking
99
+
100
+ } else if (data.type === 'error') {
101
+ // Handle error response
102
+ const errorMessage = {
103
+ type: 'error',
104
+ content: data.responses[0].response,
105
+ timestamp: new Date()
106
+ };
107
+ setMessages(prev => [...prev, errorMessage]);
108
+ }
109
 
110
  } catch (error) {
111
  console.error('Error sending message:', error);
 
134
  </button>
135
  <div>
136
  <h1 className="chat-title">Advisory Panel Chat</h1>
137
+ <p className="chat-subtitle">
138
+ {Object.keys(collectedInfo).length > 0
139
+ ? `Context: ${Object.entries(collectedInfo).map(([k,v]) => `${k}: ${v}`).join(', ')}`
140
+ : 'Consulting with your three advisors'
141
+ }
142
+ </p>
143
  </div>
144
  </div>
145
  <div className="advisor-indicators">
 
176
  </div>
177
  )}
178
 
179
+ {messages.map((message, index) => {
180
+ if (message.type === 'orchestrator') {
181
+ return (
182
+ <div key={index} className="advisor-message-container">
183
+ <div className="advisor-avatar" style={{ backgroundColor: '#F3F4F6' }}>
184
+ <MessageCircle style={{ color: '#6B7280' }} />
185
+ </div>
186
+ <div className="advisor-message-bubble">
187
+ <div className="advisor-message-header">
188
+ <h4 className="advisor-message-name" style={{ color: '#6B7280' }}>
189
+ PhD Assistant
190
+ </h4>
191
+ <span className="message-time">
192
+ {message.timestamp.toLocaleTimeString([], {
193
+ hour: '2-digit',
194
+ minute: '2-digit'
195
+ })}
196
+ </span>
197
+ </div>
198
+ <p className="advisor-message-text">{message.content}</p>
199
+ </div>
200
+ </div>
201
+ );
202
+ }
203
+ return <MessageBubble key={index} message={message} />;
204
+ })}
205
 
206
  {/* Thinking Indicators */}
207
+ {thinkingAdvisors.includes('system') && (
208
+ <div className="thinking-container">
209
+ <div className="advisor-avatar" style={{ backgroundColor: '#F3F4F6' }}>
210
+ <MessageCircle style={{ color: '#6B7280' }} />
211
+ </div>
212
+ <div className="thinking-bubble">
213
+ <div className="thinking-header">
214
+ <h4 className="advisor-name" style={{ color: '#6B7280' }}>
215
+ Processing...
216
+ </h4>
217
+ </div>
218
+ <div className="thinking-dots">
219
+ <div className="thinking-dot" style={{ backgroundColor: '#6B7280', animationDelay: '0ms' }}></div>
220
+ <div className="thinking-dot" style={{ backgroundColor: '#6B7280', animationDelay: '150ms' }}></div>
221
+ <div className="thinking-dot" style={{ backgroundColor: '#6B7280', animationDelay: '300ms' }}></div>
222
+ </div>
223
+ <p className="thinking-text">analyzing your question...</p>
224
+ </div>
225
+ </div>
226
+ )}
227
+
228
+ {thinkingAdvisors.filter(id => id !== 'system').map(advisorId => (
229
  <ThinkingIndicator key={advisorId} advisorId={advisorId} />
230
  ))}
231
 
phd-advisor-frontend/src/styles/components.css CHANGED
@@ -231,6 +231,234 @@
231
  line-height: 1.6;
232
  }
233
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
234
  /* Chat Page Styles */
235
  .chat-page {
236
  min-height: 100vh;
 
231
  line-height: 1.6;
232
  }
233
 
234
+ /* Clarification Flow Styles */
235
+ .clarification-flow {
236
+ background-color: #FFFFFF;
237
+ border-radius: 16px;
238
+ box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1);
239
+ padding: 24px;
240
+ margin: 24px auto;
241
+ max-width: 600px;
242
+ border: 1px solid #E5E7EB;
243
+ }
244
+
245
+ .clarification-header {
246
+ text-align: center;
247
+ margin-bottom: 24px;
248
+ padding-bottom: 16px;
249
+ border-bottom: 1px solid #F3F4F6;
250
+ }
251
+
252
+ .clarification-icon {
253
+ width: 48px;
254
+ height: 48px;
255
+ background-color: #EFF6FF;
256
+ border-radius: 12px;
257
+ display: flex;
258
+ align-items: center;
259
+ justify-content: center;
260
+ margin: 0 auto 16px auto;
261
+ }
262
+
263
+ .clarification-icon svg {
264
+ width: 24px;
265
+ height: 24px;
266
+ }
267
+
268
+ .clarification-title {
269
+ font-size: 20px;
270
+ font-weight: 600;
271
+ color: #111827;
272
+ margin-bottom: 8px;
273
+ }
274
+
275
+ .clarification-subtitle {
276
+ font-size: 14px;
277
+ color: #6B7280;
278
+ margin: 0;
279
+ }
280
+
281
+ .clarification-conversation {
282
+ max-height: 300px;
283
+ overflow-y: auto;
284
+ margin-bottom: 16px;
285
+ }
286
+
287
+ .clarification-message {
288
+ margin-bottom: 12px;
289
+ }
290
+
291
+ .user-clarification-message {
292
+ background: linear-gradient(135deg, #6366F1, #8B5CF6);
293
+ color: #FFFFFF;
294
+ border-radius: 12px;
295
+ padding: 10px 16px;
296
+ margin-left: auto;
297
+ max-width: 80%;
298
+ width: fit-content;
299
+ }
300
+
301
+ .user-clarification-message p {
302
+ margin: 0;
303
+ font-size: 14px;
304
+ }
305
+
306
+ .system-clarification-message {
307
+ display: flex;
308
+ align-items: flex-start;
309
+ gap: 8px;
310
+ background-color: #F9FAFB;
311
+ border-radius: 12px;
312
+ padding: 12px 16px;
313
+ max-width: 90%;
314
+ }
315
+
316
+ .system-clarification-message p {
317
+ margin: 0;
318
+ font-size: 14px;
319
+ color: #374151;
320
+ line-height: 1.5;
321
+ }
322
+
323
+ .current-question {
324
+ margin-bottom: 16px;
325
+ padding: 16px;
326
+ background-color: #EFF6FF;
327
+ border-radius: 12px;
328
+ border-left: 4px solid #6366F1;
329
+ }
330
+
331
+ .collected-info {
332
+ background-color: #F0FDF4;
333
+ border-radius: 12px;
334
+ padding: 16px;
335
+ margin-bottom: 16px;
336
+ border-left: 4px solid #10B981;
337
+ }
338
+
339
+ .collected-info h4 {
340
+ margin: 0 0 12px 0;
341
+ font-size: 14px;
342
+ font-weight: 600;
343
+ color: #065F46;
344
+ }
345
+
346
+ .info-tags {
347
+ display: flex;
348
+ flex-wrap: wrap;
349
+ gap: 8px;
350
+ }
351
+
352
+ .info-tag {
353
+ display: flex;
354
+ align-items: center;
355
+ gap: 4px;
356
+ background-color: #DCFCE7;
357
+ color: #166534;
358
+ padding: 4px 8px;
359
+ border-radius: 6px;
360
+ font-size: 12px;
361
+ font-weight: 500;
362
+ }
363
+
364
+ .info-tag svg {
365
+ width: 14px;
366
+ height: 14px;
367
+ }
368
+
369
+ .clarification-input-area {
370
+ border-top: 1px solid #F3F4F6;
371
+ padding-top: 16px;
372
+ }
373
+
374
+ .clarification-input-container {
375
+ display: flex;
376
+ flex-direction: column;
377
+ gap: 12px;
378
+ }
379
+
380
+ .clarification-input {
381
+ width: 100%;
382
+ resize: none;
383
+ border: 1px solid #D1D5DB;
384
+ border-radius: 8px;
385
+ padding: 12px;
386
+ font-size: 14px;
387
+ font-family: inherit;
388
+ outline: none;
389
+ transition: border-color 0.2s ease;
390
+ }
391
+
392
+ .clarification-input:focus {
393
+ border-color: #6366F1;
394
+ box-shadow: 0 0 0 3px rgba(99, 102, 241, 0.1);
395
+ }
396
+
397
+ .clarification-buttons {
398
+ display: flex;
399
+ gap: 8px;
400
+ flex-wrap: wrap;
401
+ }
402
+
403
+ .clarification-submit-btn {
404
+ background: linear-gradient(135deg, #6366F1, #8B5CF6);
405
+ color: #FFFFFF;
406
+ padding: 8px 16px;
407
+ border-radius: 8px;
408
+ border: none;
409
+ font-weight: 500;
410
+ cursor: pointer;
411
+ transition: all 0.2s ease;
412
+ display: flex;
413
+ align-items: center;
414
+ gap: 6px;
415
+ font-size: 14px;
416
+ }
417
+
418
+ .clarification-submit-btn:hover:not(:disabled) {
419
+ transform: translateY(-1px);
420
+ box-shadow: 0 4px 12px rgba(99, 102, 241, 0.3);
421
+ }
422
+
423
+ .clarification-submit-btn:disabled {
424
+ opacity: 0.5;
425
+ cursor: not-allowed;
426
+ transform: none;
427
+ }
428
+
429
+ .clarification-skip-btn {
430
+ background-color: #F3F4F6;
431
+ color: #6B7280;
432
+ padding: 8px 16px;
433
+ border-radius: 8px;
434
+ border: none;
435
+ font-weight: 500;
436
+ cursor: pointer;
437
+ transition: all 0.2s ease;
438
+ font-size: 14px;
439
+ }
440
+
441
+ .clarification-skip-btn:hover {
442
+ background-color: #E5E7EB;
443
+ color: #374151;
444
+ }
445
+
446
+ .clarification-cancel-btn {
447
+ background-color: transparent;
448
+ color: #DC2626;
449
+ padding: 8px 16px;
450
+ border-radius: 8px;
451
+ border: 1px solid #FCA5A5;
452
+ font-weight: 500;
453
+ cursor: pointer;
454
+ transition: all 0.2s ease;
455
+ font-size: 14px;
456
+ }
457
+
458
+ .clarification-cancel-btn:hover {
459
+ background-color: #FEF2F2;
460
+ }
461
+
462
  /* Chat Page Styles */
463
  .chat-page {
464
  min-height: 100vh;