Pulastya B commited on
Commit
e82da77
·
1 Parent(s): c10fec5

Add live tool execution progress, fix malformed responses, enable dataset/model downloads

Browse files
FRRONTEEEND/components/ChatInterface.tsx CHANGED
@@ -55,25 +55,15 @@ export const ChatInterface: React.FC<{ onBack: () => void }> = ({ onBack }) => {
55
 
56
  const activeSession = sessions.find(s => s.id === activeSessionId) || sessions[0];
57
 
58
- // Cleanup progress interval on unmount
59
- useEffect(() => {
60
- return () => {
61
- if (progressIntervalRef.current) {
62
- clearInterval(progressIntervalRef.current);
63
- }
64
- };
65
- }, []);
66
-
67
  useEffect(() => {
68
  if (scrollRef.current) {
69
  scrollRef.current.scrollTop = scrollRef.current.scrollHeight;
70
  }
71
  }, [activeSession.messages, isTyping]);
72
 
73
- // Setup progress polling only when typing is active
74
  useEffect(() => {
75
  if (!isTyping) {
76
- // Stop polling when not typing
77
  if (progressIntervalRef.current) {
78
  clearInterval(progressIntervalRef.current);
79
  progressIntervalRef.current = null;
@@ -94,15 +84,11 @@ export const ChatInterface: React.FC<{ onBack: () => void }> = ({ onBack }) => {
94
 
95
  if (steps.length > 0) {
96
  const latestStep = steps[steps.length - 1];
 
97
  const toolName = latestStep.tool
98
  .replace(/_/g, ' ')
99
- .replace(/generate/gi, 'Generating')
100
- .replace(/train/gi, 'Training')
101
- .replace(/clean/gi, 'Cleaning')
102
- .replace(/create/gi, 'Creating')
103
- .replace(/perform/gi, 'Performing')
104
  .replace(/\b\w/g, (l: string) => l.toUpperCase());
105
- setCurrentStep(toolName);
106
  }
107
  }
108
  } catch (err) {
@@ -110,10 +96,9 @@ export const ChatInterface: React.FC<{ onBack: () => void }> = ({ onBack }) => {
110
  }
111
  };
112
 
113
- // Start polling only when isTyping is true
114
  progressIntervalRef.current = setInterval(pollProgress, 1000);
115
 
116
- // Cleanup on unmount or when isTyping becomes false
117
  return () => {
118
  if (progressIntervalRef.current) {
119
  clearInterval(progressIntervalRef.current);
@@ -285,6 +270,24 @@ export const ChatInterface: React.FC<{ onBack: () => void }> = ({ onBack }) => {
285
  } else {
286
  throw new Error('Invalid response from API');
287
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
288
 
289
  updateSession(activeSessionId, [...newMessages, {
290
  id: (Date.now() + 1).toString(),
@@ -607,24 +610,16 @@ export const ChatInterface: React.FC<{ onBack: () => void }> = ({ onBack }) => {
607
  <Bot className="w-4 h-4 text-indigo-400" />
608
  </div>
609
  <div className="bg-white/[0.03] p-4 rounded-2xl border border-white/5">
610
- {currentStep ? (
611
- <div className="flex items-center gap-3">
612
- <div className="flex gap-1">
613
- <span className="w-1.5 h-1.5 bg-emerald-500 rounded-full animate-bounce [animation-delay:-0.3s]"></span>
614
- <span className="w-1.5 h-1.5 bg-emerald-500 rounded-full animate-bounce [animation-delay:-0.15s]"></span>
615
- <span className="w-1.5 h-1.5 bg-emerald-500 rounded-full animate-bounce"></span>
616
- </div>
617
- <span className="text-sm text-white/60">
618
- 🔧 {currentStep.replace(/_/g, ' ').replace('train', 'Training').replace('clean', 'Cleaning').replace('generate', 'Generating').replace(/\b\w/g, l => l.toUpperCase())}...
619
- </span>
620
- </div>
621
- ) : (
622
  <div className="flex gap-1">
623
- <span className="w-1.5 h-1.5 bg-white/20 rounded-full animate-bounce [animation-delay:-0.3s]"></span>
624
- <span className="w-1.5 h-1.5 bg-white/20 rounded-full animate-bounce [animation-delay:-0.15s]"></span>
625
- <span className="w-1.5 h-1.5 bg-white/20 rounded-full animate-bounce"></span>
626
  </div>
627
- )}
 
 
 
628
  </div>
629
  </div>
630
  )}
@@ -776,18 +771,26 @@ export const ChatInterface: React.FC<{ onBack: () => void }> = ({ onBack }) => {
776
  <h4 className="text-xs font-bold uppercase tracking-wider text-white/60">Data Files ({uniqueDataFiles.length})</h4>
777
  </div>
778
  <div className="space-y-2">
779
- {uniqueDataFiles.map((file, idx) => (
780
- <div
781
- key={idx}
782
- className="p-3 rounded-lg bg-white/5 border border-white/10 hover:bg-blue-500/10 transition-all cursor-pointer group"
783
- >
784
- <div className="flex items-center justify-between">
785
- <span className="text-sm text-white/80 truncate flex-1">{file}</span>
786
- <ChevronRight className="w-4 h-4 text-white/40 group-hover:text-blue-400 transition-all" />
787
- </div>
788
- <span className="text-xs text-white/40 mt-1 block">Dataset</span>
789
- </div>
790
- ))}
 
 
 
 
 
 
 
 
791
  </div>
792
  </div>
793
  )}
@@ -800,17 +803,42 @@ export const ChatInterface: React.FC<{ onBack: () => void }> = ({ onBack }) => {
800
  <h4 className="text-xs font-bold uppercase tracking-wider text-white/60">Models ({uniqueModels.length})</h4>
801
  </div>
802
  <div className="space-y-2">
803
- {uniqueModels.map((model, idx) => (
804
- <div
805
- key={idx}
806
- className="p-3 rounded-lg bg-white/5 border border-white/10 hover:bg-purple-500/10 transition-all cursor-pointer group"
807
- >
808
- <div className="flex items-center justify-between">
809
- <span className="text-sm text-white/80 truncate flex-1">{model}</span>
810
- <ChevronRight className="w-4 h-4 text-white/40 group-hover:text-purple-400 transition-all" />
811
- </div>
812
- </div>
813
- ))}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
814
  </div>
815
  </div>
816
  )}
 
55
 
56
  const activeSession = sessions.find(s => s.id === activeSessionId) || sessions[0];
57
 
 
 
 
 
 
 
 
 
 
58
  useEffect(() => {
59
  if (scrollRef.current) {
60
  scrollRef.current.scrollTop = scrollRef.current.scrollHeight;
61
  }
62
  }, [activeSession.messages, isTyping]);
63
 
64
+ // Poll for progress ONLY when isTyping is true
65
  useEffect(() => {
66
  if (!isTyping) {
 
67
  if (progressIntervalRef.current) {
68
  clearInterval(progressIntervalRef.current);
69
  progressIntervalRef.current = null;
 
84
 
85
  if (steps.length > 0) {
86
  const latestStep = steps[steps.length - 1];
87
+ // Format tool name nicely
88
  const toolName = latestStep.tool
89
  .replace(/_/g, ' ')
 
 
 
 
 
90
  .replace(/\b\w/g, (l: string) => l.toUpperCase());
91
+ setCurrentStep(`Executing: ${toolName}`);
92
  }
93
  }
94
  } catch (err) {
 
96
  }
97
  };
98
 
99
+ // Start polling every 1 second when workflow is active
100
  progressIntervalRef.current = setInterval(pollProgress, 1000);
101
 
 
102
  return () => {
103
  if (progressIntervalRef.current) {
104
  clearInterval(progressIntervalRef.current);
 
270
  } else {
271
  throw new Error('Invalid response from API');
272
  }
273
+
274
+ // Aggressive text cleaning to remove malformed content
275
+ assistantContent = assistantContent
276
+ // Remove broken markdown tables (lines with just | symbols)
277
+ .replace(/^\s*\|\s*\|\s*$/gm, '')
278
+ // Remove confusing phrases
279
+ .replace(/Printed in logs \(see above\)/gi, '')
280
+ .replace(/\(see above\)/gi, '')
281
+ .replace(/see above/gi, '')
282
+ // Remove broken table rows (just dashes and pipes)
283
+ .replace(/^\s*[-|]+\s*$/gm, '')
284
+ // Remove code block markers without content
285
+ .replace(/```\s*```/g, '')
286
+ // Remove empty markdown sections
287
+ .replace(/\n{3,}/g, '\n\n')
288
+ // Clean up broken table syntax
289
+ .replace(/\|\s*\n\s*\|/g, '')
290
+ .trim();
291
 
292
  updateSession(activeSessionId, [...newMessages, {
293
  id: (Date.now() + 1).toString(),
 
610
  <Bot className="w-4 h-4 text-indigo-400" />
611
  </div>
612
  <div className="bg-white/[0.03] p-4 rounded-2xl border border-white/5">
613
+ <div className="flex items-center gap-3">
 
 
 
 
 
 
 
 
 
 
 
614
  <div className="flex gap-1">
615
+ <span className="w-1.5 h-1.5 bg-emerald-500 rounded-full animate-bounce [animation-delay:-0.3s]"></span>
616
+ <span className="w-1.5 h-1.5 bg-emerald-500 rounded-full animate-bounce [animation-delay:-0.15s]"></span>
617
+ <span className="w-1.5 h-1.5 bg-emerald-500 rounded-full animate-bounce"></span>
618
  </div>
619
+ <span className="text-sm text-white/60">
620
+ {currentStep || '🔧 Starting analysis...'}
621
+ </span>
622
+ </div>
623
  </div>
624
  </div>
625
  )}
 
771
  <h4 className="text-xs font-bold uppercase tracking-wider text-white/60">Data Files ({uniqueDataFiles.length})</h4>
772
  </div>
773
  <div className="space-y-2">
774
+ {uniqueDataFiles.map((file, idx) => {
775
+ // Extract filename from path
776
+ const fileName = file.split('/').pop() || file;
777
+ // Create download URL
778
+ const downloadUrl = file.startsWith('/') ? file : `/${file}`;
779
+
780
+ return (
781
+ <button
782
+ key={idx}
783
+ onClick={() => window.open(downloadUrl, '_blank')}
784
+ className="w-full p-3 rounded-lg bg-white/5 border border-white/10 hover:bg-blue-500/10 hover:border-blue-500/30 transition-all text-left group"
785
+ >
786
+ <div className="flex items-center justify-between">
787
+ <span className="text-sm text-white/80 truncate flex-1">{fileName}</span>
788
+ <ChevronRight className="w-4 h-4 text-white/40 group-hover:text-blue-400 transition-all" />
789
+ </div>
790
+ <span className="text-xs text-white/40 mt-1 block">Click to download</span>
791
+ </button>
792
+ );
793
+ })}
794
  </div>
795
  </div>
796
  )}
 
803
  <h4 className="text-xs font-bold uppercase tracking-wider text-white/60">Models ({uniqueModels.length})</h4>
804
  </div>
805
  <div className="space-y-2">
806
+ {uniqueModels.map((model, idx) => {
807
+ // Find the model file path from workflow history
808
+ let modelPath = '';
809
+ activeSession.messages.forEach(msg => {
810
+ if (msg.role === 'assistant' && msg.content.includes(model)) {
811
+ // Try to extract model path (typically in ./outputs/models/)
812
+ const match = msg.content.match(/\.\/outputs\/models\/[^\s)]+\.pkl/);
813
+ if (match) modelPath = match[0].replace('./', '/');
814
+ }
815
+ });
816
+
817
+ // Fallback: construct typical path
818
+ if (!modelPath) {
819
+ modelPath = `/outputs/models/${model.toLowerCase().replace(/\s+/g, '_')}_model.pkl`;
820
+ }
821
+
822
+ return (
823
+ <button
824
+ key={idx}
825
+ onClick={() => {
826
+ // Trigger download
827
+ const link = document.createElement('a');
828
+ link.href = modelPath;
829
+ link.download = `${model.toLowerCase().replace(/\s+/g, '_')}_model.pkl`;
830
+ link.click();
831
+ }}
832
+ className="w-full p-3 rounded-lg bg-white/5 border border-white/10 hover:bg-purple-500/10 hover:border-purple-500/30 transition-all text-left group"
833
+ >
834
+ <div className="flex items-center justify-between">
835
+ <span className="text-sm text-white/80 truncate flex-1">{model}</span>
836
+ <ChevronRight className="w-4 h-4 text-white/40 group-hover:text-purple-400 transition-all" />
837
+ </div>
838
+ <span className="text-xs text-white/40 mt-1 block">Click to download</span>
839
+ </button>
840
+ );
841
+ })}
842
  </div>
843
  </div>
844
  )}