Spaces:
Running
Fix session file persistence and modal title issues
Browse filesCRITICAL FIXES:
1. Session File Persistence (Backend):
- Moved session resolution BEFORE schema extraction
- Follow-up requests now properly retrieve dataset from session
- Fixes 'No such file or directory' error on follow-up requests
- Schema extraction now happens AFTER file_path is resolved
2. Modal Title Display (Frontend):
- Added reportModalTitle state variable
- Modal now shows actual plot/report name instead of hardcoded 'Data Profiling Report'
- Title updates when opening any visualization or report
- Titles: 'Correlation Heatmap', 'EDA Report', etc.
These fixes enable:
- Seamless follow-up requests without re-uploading files
- Proper identification of what visualization is being viewed
- Better UX with accurate modal titles
- FRRONTEEEND/components/ChatInterface.tsx +8 -7
- src/orchestrator.py +26 -24
|
@@ -48,6 +48,7 @@ export const ChatInterface: React.FC<{ onBack: () => void }> = ({ onBack }) => {
|
|
| 48 |
const [currentStep, setCurrentStep] = useState<string>('');
|
| 49 |
const [uploadedFile, setUploadedFile] = useState<File | null>(null);
|
| 50 |
const [reportModalUrl, setReportModalUrl] = useState<string | null>(null);
|
|
|
|
| 51 |
const [showAssets, setShowAssets] = useState(false);
|
| 52 |
const fileInputRef = useRef<HTMLInputElement>(null);
|
| 53 |
const scrollRef = useRef<HTMLDivElement>(null);
|
|
@@ -702,7 +703,7 @@ export const ChatInterface: React.FC<{ onBack: () => void }> = ({ onBack }) => {
|
|
| 702 |
return (
|
| 703 |
<button
|
| 704 |
key={idx}
|
| 705 |
-
onClick={() => setReportModalUrl(`${window.location.origin}${normalizedPath}`)}
|
| 706 |
className="flex items-center gap-2 px-4 py-2 rounded-lg bg-indigo-500/20 hover:bg-indigo-500/30 border border-indigo-500/30 text-indigo-200 text-xs font-medium transition-all group"
|
| 707 |
>
|
| 708 |
<Sparkles className="w-3.5 h-3.5 group-hover:scale-110 transition-transform" />
|
|
@@ -722,7 +723,7 @@ export const ChatInterface: React.FC<{ onBack: () => void }> = ({ onBack }) => {
|
|
| 722 |
{msg.plots.map((plot, idx) => (
|
| 723 |
<button
|
| 724 |
key={idx}
|
| 725 |
-
onClick={() => setReportModalUrl(`${window.location.origin}${plot.url}`)}
|
| 726 |
className="flex items-center gap-2 px-4 py-2 rounded-lg bg-emerald-500/20 hover:bg-emerald-500/30 border border-emerald-500/30 text-emerald-200 text-xs font-medium transition-all group"
|
| 727 |
>
|
| 728 |
<svg className="w-3.5 h-3.5 group-hover:scale-110 transition-transform" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
|
@@ -896,7 +897,7 @@ export const ChatInterface: React.FC<{ onBack: () => void }> = ({ onBack }) => {
|
|
| 896 |
return (
|
| 897 |
<button
|
| 898 |
key={idx}
|
| 899 |
-
onClick={() => setReportModalUrl(plotUrl || plot.url)}
|
| 900 |
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"
|
| 901 |
>
|
| 902 |
<div className="flex items-center justify-between">
|
|
@@ -1007,7 +1008,7 @@ export const ChatInterface: React.FC<{ onBack: () => void }> = ({ onBack }) => {
|
|
| 1007 |
{allReports.map((report, idx) => (
|
| 1008 |
<button
|
| 1009 |
key={idx}
|
| 1010 |
-
onClick={() => setReportModalUrl(report.path)}
|
| 1011 |
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"
|
| 1012 |
>
|
| 1013 |
<div className="flex items-center justify-between">
|
|
@@ -1044,7 +1045,7 @@ export const ChatInterface: React.FC<{ onBack: () => void }> = ({ onBack }) => {
|
|
| 1044 |
animate={{ opacity: 1 }}
|
| 1045 |
exit={{ opacity: 0 }}
|
| 1046 |
className="fixed inset-0 bg-black/80 backdrop-blur-sm z-50 flex items-center justify-center p-4"
|
| 1047 |
-
onClick={() => setReportModalUrl(null)}
|
| 1048 |
>
|
| 1049 |
<motion.div
|
| 1050 |
initial={{ scale: 0.95, opacity: 0 }}
|
|
@@ -1054,9 +1055,9 @@ export const ChatInterface: React.FC<{ onBack: () => void }> = ({ onBack }) => {
|
|
| 1054 |
onClick={(e) => e.stopPropagation()}
|
| 1055 |
>
|
| 1056 |
<div className="flex items-center justify-between p-4 border-b border-white/5">
|
| 1057 |
-
<h3 className="text-lg font-semibold text-white">
|
| 1058 |
<button
|
| 1059 |
-
onClick={() => setReportModalUrl(null)}
|
| 1060 |
className="p-2 rounded-lg hover:bg-white/5 transition-colors"
|
| 1061 |
>
|
| 1062 |
<X className="w-5 h-5" />
|
|
|
|
| 48 |
const [currentStep, setCurrentStep] = useState<string>('');
|
| 49 |
const [uploadedFile, setUploadedFile] = useState<File | null>(null);
|
| 50 |
const [reportModalUrl, setReportModalUrl] = useState<string | null>(null);
|
| 51 |
+
const [reportModalTitle, setReportModalTitle] = useState<string>('Visualization');
|
| 52 |
const [showAssets, setShowAssets] = useState(false);
|
| 53 |
const fileInputRef = useRef<HTMLInputElement>(null);
|
| 54 |
const scrollRef = useRef<HTMLDivElement>(null);
|
|
|
|
| 703 |
return (
|
| 704 |
<button
|
| 705 |
key={idx}
|
| 706 |
+
onClick={() => { setReportModalUrl(`${window.location.origin}${normalizedPath}`); setReportModalTitle(report.name || 'Report'); }}
|
| 707 |
className="flex items-center gap-2 px-4 py-2 rounded-lg bg-indigo-500/20 hover:bg-indigo-500/30 border border-indigo-500/30 text-indigo-200 text-xs font-medium transition-all group"
|
| 708 |
>
|
| 709 |
<Sparkles className="w-3.5 h-3.5 group-hover:scale-110 transition-transform" />
|
|
|
|
| 723 |
{msg.plots.map((plot, idx) => (
|
| 724 |
<button
|
| 725 |
key={idx}
|
| 726 |
+
onClick={() => { setReportModalUrl(`${window.location.origin}${plot.url}`); setReportModalTitle(plot.title || 'Visualization'); }}
|
| 727 |
className="flex items-center gap-2 px-4 py-2 rounded-lg bg-emerald-500/20 hover:bg-emerald-500/30 border border-emerald-500/30 text-emerald-200 text-xs font-medium transition-all group"
|
| 728 |
>
|
| 729 |
<svg className="w-3.5 h-3.5 group-hover:scale-110 transition-transform" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
|
|
|
| 897 |
return (
|
| 898 |
<button
|
| 899 |
key={idx}
|
| 900 |
+
onClick={() => { setReportModalUrl(plotUrl || plot.url); setReportModalTitle(plot.title || 'Visualization'); }}
|
| 901 |
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"
|
| 902 |
>
|
| 903 |
<div className="flex items-center justify-between">
|
|
|
|
| 1008 |
{allReports.map((report, idx) => (
|
| 1009 |
<button
|
| 1010 |
key={idx}
|
| 1011 |
+
onClick={() => { setReportModalUrl(report.path); setReportModalTitle(report.name || 'Report'); }}
|
| 1012 |
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"
|
| 1013 |
>
|
| 1014 |
<div className="flex items-center justify-between">
|
|
|
|
| 1045 |
animate={{ opacity: 1 }}
|
| 1046 |
exit={{ opacity: 0 }}
|
| 1047 |
className="fixed inset-0 bg-black/80 backdrop-blur-sm z-50 flex items-center justify-center p-4"
|
| 1048 |
+
onClick={() => { setReportModalUrl(null); setReportModalTitle('Visualization'); }}
|
| 1049 |
>
|
| 1050 |
<motion.div
|
| 1051 |
initial={{ scale: 0.95, opacity: 0 }}
|
|
|
|
| 1055 |
onClick={(e) => e.stopPropagation()}
|
| 1056 |
>
|
| 1057 |
<div className="flex items-center justify-between p-4 border-b border-white/5">
|
| 1058 |
+
<h3 className="text-lg font-semibold text-white">{reportModalTitle}</h3>
|
| 1059 |
<button
|
| 1060 |
+
onClick={() => { setReportModalUrl(null); setReportModalTitle('Visualization'); }}
|
| 1061 |
className="p-2 rounded-lg hover:bg-white/5 transition-colors"
|
| 1062 |
>
|
| 1063 |
<X className="w-5 h-5" />
|
|
@@ -2094,7 +2094,33 @@ You are a DOER. Complete workflows based on user intent."""
|
|
| 2094 |
"""
|
| 2095 |
start_time = time.time()
|
| 2096 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2097 |
# π LOCAL SCHEMA EXTRACTION (NO LLM) - Extract metadata before any LLM calls
|
|
|
|
| 2098 |
print("π Extracting dataset schema locally (no LLM)...")
|
| 2099 |
schema_info = extract_schema_local(file_path, sample_rows=3)
|
| 2100 |
|
|
@@ -2131,30 +2157,6 @@ You are a DOER. Complete workflows based on user intent."""
|
|
| 2131 |
else:
|
| 2132 |
system_prompt = self._build_system_prompt()
|
| 2133 |
|
| 2134 |
-
# π§ RESOLVE AMBIGUITY USING SESSION MEMORY
|
| 2135 |
-
original_file_path = file_path
|
| 2136 |
-
original_target_col = target_col
|
| 2137 |
-
|
| 2138 |
-
if self.session:
|
| 2139 |
-
# Check if request has ambiguous references
|
| 2140 |
-
resolved_params = self.session.resolve_ambiguity(task_description)
|
| 2141 |
-
|
| 2142 |
-
# Use resolved params if user didn't specify
|
| 2143 |
-
if not file_path or file_path == "":
|
| 2144 |
-
if resolved_params.get("file_path"):
|
| 2145 |
-
file_path = resolved_params["file_path"]
|
| 2146 |
-
print(f"π Using dataset from session: {file_path}")
|
| 2147 |
-
|
| 2148 |
-
if not target_col:
|
| 2149 |
-
if resolved_params.get("target_col"):
|
| 2150 |
-
target_col = resolved_params["target_col"]
|
| 2151 |
-
print(f"π Using target column from session: {target_col}")
|
| 2152 |
-
|
| 2153 |
-
# Show session context if available
|
| 2154 |
-
if self.session.last_dataset or self.session.last_model:
|
| 2155 |
-
context_summary = self.session.get_context_summary()
|
| 2156 |
-
print(f"\n{context_summary}\n")
|
| 2157 |
-
|
| 2158 |
# π― PROACTIVE INTENT DETECTION - Tell LLM which tools to use BEFORE it tries wrong ones
|
| 2159 |
task_lower = task_description.lower()
|
| 2160 |
|
|
|
|
| 2094 |
"""
|
| 2095 |
start_time = time.time()
|
| 2096 |
|
| 2097 |
+
# π§ RESOLVE AMBIGUITY USING SESSION MEMORY (BEFORE SCHEMA EXTRACTION)
|
| 2098 |
+
# This ensures follow-up requests can find the file before we try to extract schema
|
| 2099 |
+
original_file_path = file_path
|
| 2100 |
+
original_target_col = target_col
|
| 2101 |
+
|
| 2102 |
+
if self.session:
|
| 2103 |
+
# Check if request has ambiguous references
|
| 2104 |
+
resolved_params = self.session.resolve_ambiguity(task_description)
|
| 2105 |
+
|
| 2106 |
+
# Use resolved params if user didn't specify
|
| 2107 |
+
if not file_path or file_path == "":
|
| 2108 |
+
if resolved_params.get("file_path"):
|
| 2109 |
+
file_path = resolved_params["file_path"]
|
| 2110 |
+
print(f"π Using dataset from session: {file_path}")
|
| 2111 |
+
|
| 2112 |
+
if not target_col:
|
| 2113 |
+
if resolved_params.get("target_col"):
|
| 2114 |
+
target_col = resolved_params["target_col"]
|
| 2115 |
+
print(f"π Using target column from session: {target_col}")
|
| 2116 |
+
|
| 2117 |
+
# Show session context if available
|
| 2118 |
+
if self.session.last_dataset or self.session.last_model:
|
| 2119 |
+
context_summary = self.session.get_context_summary()
|
| 2120 |
+
print(f"\n{context_summary}\n")
|
| 2121 |
+
|
| 2122 |
# π LOCAL SCHEMA EXTRACTION (NO LLM) - Extract metadata before any LLM calls
|
| 2123 |
+
# Now that file_path is resolved from session if needed
|
| 2124 |
print("π Extracting dataset schema locally (no LLM)...")
|
| 2125 |
schema_info = extract_schema_local(file_path, sample_rows=3)
|
| 2126 |
|
|
|
|
| 2157 |
else:
|
| 2158 |
system_prompt = self._build_system_prompt()
|
| 2159 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 2160 |
# π― PROACTIVE INTENT DETECTION - Tell LLM which tools to use BEFORE it tries wrong ones
|
| 2161 |
task_lower = task_description.lower()
|
| 2162 |
|