Pulastya B commited on
Commit
b362856
·
1 Parent(s): 296ce82

Fix progress bar stuck status, improve Assets sidebar visualization/file extraction, better response formatting cleanup

Browse files
FRRONTEEEND/components/ChatInterface.tsx CHANGED
@@ -88,11 +88,22 @@ export const ChatInterface: React.FC<{ onBack: () => void }> = ({ onBack }) => {
88
  const progressData = await progressResponse.json();
89
  const steps = progressData.steps || [];
90
 
91
- // Find the most recent running step
92
  const runningSteps = steps.filter((s: any) => s.status === 'running');
93
  if (runningSteps.length > 0) {
94
  const lastStep = runningSteps[runningSteps.length - 1];
95
  setCurrentStep(lastStep.tool);
 
 
 
 
 
 
 
 
 
 
 
96
  }
97
  }
98
  } catch (err) {
@@ -698,37 +709,85 @@ export const ChatInterface: React.FC<{ onBack: () => void }> = ({ onBack }) => {
698
  {(() => {
699
  const allPlots: Array<{title: string, url: string, type?: string}> = [];
700
  const allReports: Array<{name: string, path: string}> = [];
 
 
701
  const allModels: string[] = [];
702
 
703
  activeSession.messages.forEach(msg => {
704
- if (msg.plots) allPlots.push(...msg.plots);
705
- if (msg.reports) allReports.push(...msg.reports);
706
- // Extract model references from content
707
- if (msg.content.includes('xgboost') || msg.content.includes('model')) {
708
- const modelMatch = msg.content.match(/\b(xgboost|random_forest|catboost|lightgbm)[^\s]*/gi);
709
- if (modelMatch) allModels.push(...modelMatch);
 
 
710
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
711
  });
712
 
713
- const uniqueModels = [...new Set(allModels)];
 
714
 
715
  return (
716
  <>
717
- {/* Models Section */}
718
- {uniqueModels.length > 0 && (
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
719
  <div>
720
  <div className="flex items-center gap-2 mb-3">
721
  <FileText className="w-4 h-4 text-blue-400" />
722
- <h4 className="text-xs font-bold uppercase tracking-wider text-white/60">Models ({uniqueModels.length})</h4>
723
  </div>
724
  <div className="space-y-2">
725
- {uniqueModels.slice(0, 5).map((model, idx) => (
726
  <div
727
  key={idx}
728
  className="p-3 rounded-lg bg-white/5 border border-white/10 hover:bg-white/10 transition-all cursor-pointer group"
729
  >
730
  <div className="flex items-center justify-between">
731
- <span className="text-sm text-white/80 truncate flex-1">{model}</span>
732
  <ChevronRight className="w-4 h-4 text-white/40 group-hover:text-white/80 transition-all" />
733
  </div>
734
  </div>
@@ -737,26 +796,24 @@ export const ChatInterface: React.FC<{ onBack: () => void }> = ({ onBack }) => {
737
  </div>
738
  )}
739
 
740
- {/* Plots Section */}
741
- {allPlots.length > 0 && (
742
  <div>
743
  <div className="flex items-center gap-2 mb-3">
744
- <BarChart3 className="w-4 h-4 text-emerald-400" />
745
- <h4 className="text-xs font-bold uppercase tracking-wider text-white/60">Visualizations ({allPlots.length})</h4>
746
  </div>
747
  <div className="space-y-2">
748
- {allPlots.map((plot, idx) => (
749
- <button
750
  key={idx}
751
- onClick={() => setReportModalUrl(plot.url)}
752
- className="w-full p-3 rounded-lg bg-white/5 border border-white/10 hover:bg-emerald-500/10 hover:border-emerald-500/30 transition-all text-left group"
753
  >
754
  <div className="flex items-center justify-between">
755
- <span className="text-sm text-white/80 truncate flex-1">{plot.title}</span>
756
- <ChevronRight className="w-4 h-4 text-white/40 group-hover:text-emerald-400 transition-all" />
757
  </div>
758
- <span className="text-xs text-white/40 mt-1 block">{plot.type || 'interactive'}</span>
759
- </button>
760
  ))}
761
  </div>
762
  </div>
@@ -766,7 +823,7 @@ export const ChatInterface: React.FC<{ onBack: () => void }> = ({ onBack }) => {
766
  {allReports.length > 0 && (
767
  <div>
768
  <div className="flex items-center gap-2 mb-3">
769
- <FileText className="w-4 h-4 text-purple-400" />
770
  <h4 className="text-xs font-bold uppercase tracking-wider text-white/60">Reports ({allReports.length})</h4>
771
  </div>
772
  <div className="space-y-2">
@@ -774,11 +831,11 @@ export const ChatInterface: React.FC<{ onBack: () => void }> = ({ onBack }) => {
774
  <button
775
  key={idx}
776
  onClick={() => setReportModalUrl(report.path)}
777
- 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"
778
  >
779
  <div className="flex items-center justify-between">
780
  <span className="text-sm text-white/80 truncate flex-1">{report.name}</span>
781
- <ChevronRight className="w-4 h-4 text-white/40 group-hover:text-purple-400 transition-all" />
782
  </div>
783
  </button>
784
  ))}
@@ -787,7 +844,7 @@ export const ChatInterface: React.FC<{ onBack: () => void }> = ({ onBack }) => {
787
  )}
788
 
789
  {/* Empty State */}
790
- {allPlots.length === 0 && allReports.length === 0 && uniqueModels.length === 0 && (
791
  <div className="flex flex-col items-center justify-center h-full text-center p-8">
792
  <Package className="w-12 h-12 text-white/10 mb-3" />
793
  <p className="text-sm text-white/40 mb-1">No assets yet</p>
 
88
  const progressData = await progressResponse.json();
89
  const steps = progressData.steps || [];
90
 
91
+ // Find the most recent running step, or last completed step
92
  const runningSteps = steps.filter((s: any) => s.status === 'running');
93
  if (runningSteps.length > 0) {
94
  const lastStep = runningSteps[runningSteps.length - 1];
95
  setCurrentStep(lastStep.tool);
96
+ } else if (steps.length > 0) {
97
+ // No running steps - check if there are completed steps
98
+ const completedSteps = steps.filter((s: any) => s.status === 'completed');
99
+ if (completedSteps.length > 0) {
100
+ // Only show if very recent (workflow still in progress)
101
+ const lastCompleted = completedSteps[completedSteps.length - 1];
102
+ const timeSinceCompletion = Date.now() - (lastCompleted.timestamp * 1000 || 0);
103
+ if (timeSinceCompletion < 2000) { // Show for 2 seconds after completion
104
+ setCurrentStep('');
105
+ }
106
+ }
107
  }
108
  }
109
  } catch (err) {
 
709
  {(() => {
710
  const allPlots: Array<{title: string, url: string, type?: string}> = [];
711
  const allReports: Array<{name: string, path: string}> = [];
712
+ const allDataFiles: Array<{name: string, path: string}> = [];
713
+ const seenModels = new Set<string>();
714
  const allModels: string[] = [];
715
 
716
  activeSession.messages.forEach(msg => {
717
+ // Collect plots
718
+ if (msg.plots) {
719
+ allPlots.push(...msg.plots.filter(p => p && p.title && p.url));
720
+ }
721
+
722
+ // Collect reports
723
+ if (msg.reports) {
724
+ allReports.push(...msg.reports.filter(r => r && r.name && r.path));
725
  }
726
+
727
+ // Extract data files from content (look for CSV mentions)
728
+ const csvMatch = msg.content.match(/cleaned.*dataset|encoded.*dataset|prepared.*data/gi);
729
+ if (csvMatch && !allDataFiles.some(f => f.name.includes('encoded'))) {
730
+ allDataFiles.push({
731
+ name: 'Cleaned & Encoded Dataset',
732
+ path: '/outputs/data/encoded.csv'
733
+ });
734
+ }
735
+
736
+ // Extract model names - look for baseline models only
737
+ const baselineModels = ['xgboost', 'random_forest', 'catboost', 'lightgbm', 'ridge', 'lasso', 'logistic'];
738
+ baselineModels.forEach(model => {
739
+ if (msg.content.toLowerCase().includes(model) && !seenModels.has(model)) {
740
+ seenModels.add(model);
741
+ allModels.push(model);
742
+ }
743
+ });
744
  });
745
 
746
+ // Remove duplicates from plots
747
+ const uniquePlots = Array.from(new Map(allPlots.map(p => [p.title + p.url, p])).values());
748
 
749
  return (
750
  <>
751
+ {/* Visualizations Section - Show FIRST */}
752
+ {uniquePlots.length > 0 && (
753
+ <div>
754
+ <div className="flex items-center gap-2 mb-3">
755
+ <BarChart3 className="w-4 h-4 text-emerald-400" />
756
+ <h4 className="text-xs font-bold uppercase tracking-wider text-white/60">Visualizations ({uniquePlots.length})</h4>
757
+ </div>
758
+ <div className="space-y-2">
759
+ {uniquePlots.map((plot, idx) => (
760
+ <button
761
+ key={idx}
762
+ onClick={() => setReportModalUrl(plot.url)}
763
+ className="w-full p-3 rounded-lg bg-white/5 border border-white/10 hover:bg-emerald-500/10 hover:border-emerald-500/30 transition-all text-left group"
764
+ >
765
+ <div className="flex items-center justify-between">
766
+ <span className="text-sm text-white/80 truncate flex-1">{plot.title}</span>
767
+ <ChevronRight className="w-4 h-4 text-white/40 group-hover:text-emerald-400 transition-all" />
768
+ </div>
769
+ <span className="text-xs text-white/40 mt-1 block">{plot.type || 'interactive'}</span>
770
+ </button>
771
+ ))}
772
+ </div>
773
+ </div>
774
+ )}
775
+
776
+ {/* Data Files Section */}
777
+ {allDataFiles.length > 0 && (
778
  <div>
779
  <div className="flex items-center gap-2 mb-3">
780
  <FileText className="w-4 h-4 text-blue-400" />
781
+ <h4 className="text-xs font-bold uppercase tracking-wider text-white/60">Data Files ({allDataFiles.length})</h4>
782
  </div>
783
  <div className="space-y-2">
784
+ {allDataFiles.map((file, idx) => (
785
  <div
786
  key={idx}
787
  className="p-3 rounded-lg bg-white/5 border border-white/10 hover:bg-white/10 transition-all cursor-pointer group"
788
  >
789
  <div className="flex items-center justify-between">
790
+ <span className="text-sm text-white/80 truncate flex-1">{file.name}</span>
791
  <ChevronRight className="w-4 h-4 text-white/40 group-hover:text-white/80 transition-all" />
792
  </div>
793
  </div>
 
796
  </div>
797
  )}
798
 
799
+ {/* Models Section */}
800
+ {allModels.length > 0 && (
801
  <div>
802
  <div className="flex items-center gap-2 mb-3">
803
+ <FileText className="w-4 h-4 text-purple-400" />
804
+ <h4 className="text-xs font-bold uppercase tracking-wider text-white/60">Models ({allModels.length})</h4>
805
  </div>
806
  <div className="space-y-2">
807
+ {allModels.map((model, idx) => (
808
+ <div
809
  key={idx}
810
+ className="p-3 rounded-lg bg-white/5 border border-white/10 hover:bg-purple-500/10 hover:border-purple-500/30 transition-all cursor-pointer group"
 
811
  >
812
  <div className="flex items-center justify-between">
813
+ <span className="text-sm text-white/80 truncate flex-1 capitalize">{model.replace(/_/g, ' ')}</span>
814
+ <ChevronRight className="w-4 h-4 text-white/40 group-hover:text-purple-400 transition-all" />
815
  </div>
816
+ </div>
 
817
  ))}
818
  </div>
819
  </div>
 
823
  {allReports.length > 0 && (
824
  <div>
825
  <div className="flex items-center gap-2 mb-3">
826
+ <FileText className="w-4 h-4 text-orange-400" />
827
  <h4 className="text-xs font-bold uppercase tracking-wider text-white/60">Reports ({allReports.length})</h4>
828
  </div>
829
  <div className="space-y-2">
 
831
  <button
832
  key={idx}
833
  onClick={() => setReportModalUrl(report.path)}
834
+ className="w-full p-3 rounded-lg bg-white/5 border border-white/10 hover:bg-orange-500/10 hover:border-orange-500/30 transition-all text-left group"
835
  >
836
  <div className="flex items-center justify-between">
837
  <span className="text-sm text-white/80 truncate flex-1">{report.name}</span>
838
+ <ChevronRight className="w-4 h-4 text-white/40 group-hover:text-orange-400 transition-all" />
839
  </div>
840
  </button>
841
  ))}
 
844
  )}
845
 
846
  {/* Empty State */}
847
+ {uniquePlots.length === 0 && allReports.length === 0 && allDataFiles.length === 0 && allModels.length === 0 && (
848
  <div className="flex flex-col items-center justify-center h-full text-center p-8">
849
  <Package className="w-12 h-12 text-white/10 mb-3" />
850
  <p className="text-sm text-white/40 mb-1">No assets yet</p>
src/orchestrator.py CHANGED
@@ -2305,25 +2305,37 @@ You are a DOER. Complete workflows based on user intent."""
2305
  )
2306
  summary_text = enhanced_summary["text"]
2307
 
2308
- # 🧹 POST-PROCESS: Remove any file paths that slipped through
2309
  import re
2310
- # Remove file path patterns
2311
- summary_text = re.sub(r'\./outputs/[^\s\)]+', '[generated file]', summary_text)
2312
- summary_text = re.sub(r'/outputs/[^\s\)]+', '[generated file]', summary_text)
2313
- summary_text = re.sub(r'outputs/[^\s\)]+', '[generated file]', summary_text)
2314
- # Remove common file path mentions
2315
- summary_text = re.sub(r'saved to:?\s*[^\s]+', 'generated', summary_text, flags=re.IGNORECASE)
2316
- summary_text = re.sub(r'output file:?\s*[^\s]+', 'output generated', summary_text, flags=re.IGNORECASE)
2317
- summary_text = re.sub(r'file path:?\s*[^\s]+', 'file generated', summary_text, flags=re.IGNORECASE)
2318
- # Remove filename patterns in parentheses and backticks
2319
- summary_text = re.sub(r'\([^\)]*\.(csv|pkl|html|png|json)[^\)]*\)', '', summary_text)
2320
- summary_text = re.sub(r'`[^`]*\.(csv|pkl|html|png|json)[^`]*`', '', summary_text)
2321
- # Clean up table separators that mention paths
2322
- summary_text = re.sub(r'\|\s*[^\|]*\.(csv|pkl|html|png)[^\|]*\s*\|', '| [see artifacts] |', summary_text)
 
 
 
 
 
 
 
 
 
 
 
 
 
2323
 
2324
  metrics_data = enhanced_summary.get("metrics", {})
2325
  artifacts_data = enhanced_summary.get("artifacts", {})
2326
- artifacts_data = enhanced_summary.get("artifacts", {})
2327
  plots_data = enhanced_summary.get("plots", [])
2328
  print(f"✅ Enhanced summary generated with {len(plots_data)} plots, {len(metrics_data)} metrics")
2329
  except Exception as e:
 
2305
  )
2306
  summary_text = enhanced_summary["text"]
2307
 
2308
+ # 🧹 POST-PROCESS: Clean file paths and broken formatting
2309
  import re
2310
+ # Remove file path patterns BEFORE they create [generated file]
2311
+ summary_text = re.sub(r'`\./outputs/[^\s`]+`', '', summary_text) # Backtick wrapped paths
2312
+ summary_text = re.sub(r'\./outputs/[^\s\)]+', '', summary_text) # Direct paths
2313
+ summary_text = re.sub(r'/outputs/[^\s\)]+', '', summary_text) # Absolute paths
2314
+ summary_text = re.sub(r'outputs/[^\s\)]+', '', summary_text) # Relative paths
2315
+
2316
+ # Remove broken table rows with empty content
2317
+ summary_text = re.sub(r'\|\s*\|[^\|]*\|', '', summary_text) # Clean malformed tables
2318
+ summary_text = re.sub(r'\|\s*\[\s*[^\]]*\]\([^\)]*\)\s*\|', '', summary_text) # Remove link-only cells
2319
+
2320
+ # Remove leftover [generated file] brackets
2321
+ summary_text = re.sub(r'\s*\[\s*generated file\s*\]\s*', ' ', summary_text)
2322
+ summary_text = re.sub(r'\[\s*see artifacts\s*\]', '', summary_text)
2323
+
2324
+ # Remove broken parentheses and backticks
2325
+ summary_text = re.sub(r'`\[generated[^\]]*\]`', '', summary_text)
2326
+ summary_text = re.sub(r'\(\s*\[\s*generated[^\]]*\]\s*\)', '', summary_text)
2327
+
2328
+ # Clean up "generated file" text patterns
2329
+ summary_text = re.sub(r'\[generated file\]', '', summary_text)
2330
+ summary_text = re.sub(r'saved to:?\s*[^\s]+', '', summary_text, flags=re.IGNORECASE)
2331
+ summary_text = re.sub(r'output file:?\s*[^\n]+', '', summary_text, flags=re.IGNORECASE)
2332
+
2333
+ # Remove excessive whitespace
2334
+ summary_text = re.sub(r'\n\s*\n\s*\n+', '\n\n', summary_text) # Multiple blank lines
2335
+ summary_text = re.sub(r' {2,}', ' ', summary_text) # Multiple spaces
2336
 
2337
  metrics_data = enhanced_summary.get("metrics", {})
2338
  artifacts_data = enhanced_summary.get("artifacts", {})
 
2339
  plots_data = enhanced_summary.get("plots", [])
2340
  print(f"✅ Enhanced summary generated with {len(plots_data)} plots, {len(metrics_data)} metrics")
2341
  except Exception as e: