Spaces:
Running
Running
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 +86 -29
- src/orchestrator.py +27 -15
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 |
-
|
| 705 |
-
if (msg.
|
| 706 |
-
|
| 707 |
-
|
| 708 |
-
|
| 709 |
-
|
|
|
|
|
|
|
| 710 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 711 |
});
|
| 712 |
|
| 713 |
-
|
|
|
|
| 714 |
|
| 715 |
return (
|
| 716 |
<>
|
| 717 |
-
{/*
|
| 718 |
-
{
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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">
|
| 723 |
</div>
|
| 724 |
<div className="space-y-2">
|
| 725 |
-
{
|
| 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">{
|
| 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 |
-
{/*
|
| 741 |
-
{
|
| 742 |
<div>
|
| 743 |
<div className="flex items-center gap-2 mb-3">
|
| 744 |
-
<
|
| 745 |
-
<h4 className="text-xs font-bold uppercase tracking-wider text-white/60">
|
| 746 |
</div>
|
| 747 |
<div className="space-y-2">
|
| 748 |
-
{
|
| 749 |
-
<
|
| 750 |
key={idx}
|
| 751 |
-
|
| 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">{
|
| 756 |
-
<ChevronRight className="w-4 h-4 text-white/40 group-hover:text-
|
| 757 |
</div>
|
| 758 |
-
|
| 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-
|
| 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-
|
| 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-
|
| 782 |
</div>
|
| 783 |
</button>
|
| 784 |
))}
|
|
@@ -787,7 +844,7 @@ export const ChatInterface: React.FC<{ onBack: () => void }> = ({ onBack }) => {
|
|
| 787 |
)}
|
| 788 |
|
| 789 |
{/* Empty State */}
|
| 790 |
-
{
|
| 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:
|
| 2309 |
import re
|
| 2310 |
-
# Remove file path patterns
|
| 2311 |
-
summary_text = re.sub(r'
|
| 2312 |
-
summary_text = re.sub(r'
|
| 2313 |
-
summary_text = re.sub(r'outputs/[^\s\)]+', '
|
| 2314 |
-
|
| 2315 |
-
|
| 2316 |
-
|
| 2317 |
-
summary_text = re.sub(r'
|
| 2318 |
-
|
| 2319 |
-
|
| 2320 |
-
|
| 2321 |
-
|
| 2322 |
-
summary_text = re.sub(r'
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 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:
|