| |
|
|
| import streamlit as st |
| import os |
| import sys |
| import shutil |
| import pandas as pd |
| import subprocess |
| import time |
| import uuid |
| import io |
| import json |
| import re |
| from pathlib import Path |
| from datetime import datetime |
|
|
| |
| from config import ( |
| CRAWL_CACHE_DIR, KNOWLEDGE_CACHE_DIR, OUTPUT_DIR, VECTOR_DB_PATH, APP_VERSION, |
| SUPABASE_URL, SUPABASE_KEY, APP_PASSWORD |
| ) |
| from schemas import CHOICE_OPTIONS |
| import db_manager |
|
|
| |
| try: |
| from config import MISTRAL_API_KEY |
| SECRETS_LOADED = bool(MISTRAL_API_KEY) |
| from config import SUPABASE_CONFIGURED |
| except ImportError: |
| SECRETS_LOADED = False |
| SUPABASE_CONFIGURED = False |
|
|
| |
| TEMP_DIR = Path("./temp_streamlit_files") |
| TEMP_DIR.mkdir(exist_ok=True) |
| LOG_DIR = Path("./streamlit_logs") |
| LOG_DIR.mkdir(exist_ok=True) |
|
|
| |
| st.set_page_config(page_title="Crawl4AI Unified System", layout="wide", initial_sidebar_state="auto") |
|
|
| |
| |
| |
|
|
| def check_password(): |
| """Returns `True` if the user had the correct password.""" |
| |
| if not APP_PASSWORD: |
| st.error("⚠️ **CRITICAL ERROR:** 'APP_PASSWORD' secret is not set in Hugging Face Secrets. Access disabled.") |
| return False |
|
|
| if st.session_state.get("password_correct", False): |
| return True |
|
|
| |
| col1, col2, col3 = st.columns([1, 2, 1]) |
| with col2: |
| st.write("") |
| st.write("") |
| with st.container(border=True): |
| st.title("🔒 Restricted Access") |
| st.markdown("Please enter the application password to continue.") |
| pwd_input = st.text_input("Password", type="password", key="password_input") |
| |
| if st.button("Login", type="primary", use_container_width=True): |
| if pwd_input == APP_PASSWORD: |
| st.session_state["password_correct"] = True |
| st.rerun() |
| else: |
| st.error("😕 Password incorrect") |
| |
| return False |
|
|
| |
| if not check_password(): |
| st.stop() |
|
|
| |
| |
| |
|
|
| |
| def initialize_session_state(): |
| """Initializes all necessary session state variables.""" |
| if 'view' not in st.session_state: |
| st.session_state.view = "home" |
| if 'agent_status' not in st.session_state: |
| st.session_state.agent_status = "Idle" |
| if 'active_process' not in st.session_state: |
| st.session_state.active_process = None |
| if 'log_file' not in st.session_state: |
| st.session_state.log_file = None |
| if 'final_log_content' not in st.session_state: |
| st.session_state.final_log_content = "" |
| if 'output_files' not in st.session_state: |
| st.session_state.output_files =[] |
| if 'files_before_run' not in st.session_state: |
| st.session_state.files_before_run = set() |
| if 'db_events' not in st.session_state: |
| st.session_state.db_events =[] |
| if 'db_selected_event' not in st.session_state: |
| st.session_state.db_selected_event = None |
| if 'db_run_outputs' not in st.session_state: |
| st.session_state.db_run_outputs =[] |
| |
| |
| if 'start_crawl4ai_flag' not in st.session_state: |
| st.session_state.start_crawl4ai_flag = False |
| if 'start_calendarcrawl_flag' not in st.session_state: |
| st.session_state.start_calendarcrawl_flag = False |
| if 'start_gemini_flag' not in st.session_state: |
| st.session_state.start_gemini_flag = False |
|
|
| |
| def read_log_file(): |
| if st.session_state.log_file and Path(st.session_state.log_file).exists(): |
| try: |
| with open(st.session_state.log_file, 'r', encoding='utf-8', errors='ignore') as f: |
| return f.read() |
| except Exception: |
| return "Reading log file..." |
| return "Process has not started yet." |
|
|
| def extract_progress(log_content): |
| """Parses the log content to find the current progress (e.g., '1/50').""" |
| match = re.findall(r"\[STARTING MISSION\]\s+(\d+)/(\d+)", log_content) |
| if match: |
| current, total = match[-1] |
| return int(current), int(total) |
| return 0, 0 |
|
|
| def get_files_in_dir(directory): |
| dir_path = Path(directory) |
| if not dir_path.is_dir(): |
| dir_path.mkdir(exist_ok=True) |
| return {str(f) for f in dir_path.glob("*.csv")} |
|
|
| def start_agent_process(command): |
| st.session_state.output_files =[] |
| timestamp = datetime.now().strftime("%Y-%m-%d_%H-%M-%S") |
| st.session_state.log_file = LOG_DIR / f"run_{timestamp}.log" |
| st.session_state.final_log_content = "" |
| st.session_state.files_before_run = get_files_in_dir(OUTPUT_DIR) |
| |
| current_env = os.environ.copy() |
| |
| try: |
| with open(st.session_state.log_file, 'w', encoding='utf-8') as log_f: |
| process = subprocess.Popen( |
| command, |
| stdout=log_f, |
| stderr=subprocess.STDOUT, |
| text=True, |
| encoding='utf-8', |
| errors='replace', |
| env=current_env |
| ) |
| st.session_state.active_process = process |
| st.session_state.agent_status = "Running" |
| except Exception as e: |
| st.error(f"Error during agent setup: {e}") |
|
|
| def change_view(view_name): |
| st.session_state.view = view_name |
|
|
| |
| def process_crawl4ai_start(): |
| if st.session_state.agent_status == "Running": return |
| input_method = st.session_state.get('crawl4ai_input_method', 'File Upload') |
| max_events_str_web = st.session_state.get('max_events_web', "") |
| output_filename = st.session_state.get('crawl4ai_output_filename', "") |
| temp_input_path = TEMP_DIR / "races.json" |
| input_ready = False |
| |
| if input_method == "File Upload": |
| input_file = st.session_state.get('web_agent_uploader') |
| if not input_file: |
| st.error("Please upload an input data file.") |
| return |
| try: |
| file_contents = input_file.getvalue() |
| if input_file.name.lower().endswith('.csv'): |
| df = pd.read_csv(io.BytesIO(file_contents)) |
| df.to_json(temp_input_path, orient='records', indent=4) |
| elif input_file.name.lower().endswith('.xlsx'): |
| df = pd.read_excel(io.BytesIO(file_contents)) |
| df.to_json(temp_input_path, orient='records', indent=4) |
| else: |
| with open(temp_input_path, "wb") as f: f.write(file_contents) |
| input_ready = True |
| except Exception as e: st.error(f"Error preparing input file: {e}") |
| else: |
| manual_event_names = st.session_state.get('manual_event_names', "") |
| event_type = st.session_state.get('manual_event_type', "Run") |
| if not manual_event_names.strip(): |
| st.error("Please enter at least one event name.") |
| return |
| try: |
| event_list =[name.strip() for name in manual_event_names.strip().split('\n') if name.strip()] |
| races_data =[{"Festival": name, "Type": event_type} for name in event_list] |
| with open(temp_input_path, 'w', encoding='utf-8') as f: json.dump(races_data, f, indent=4) |
| input_ready = True |
| except Exception as e: st.error(f"Error preparing manual input: {e}") |
| |
| if input_ready: |
| command =[sys.executable, "-u", "main.py", "--mode", "web_analyst", "--output-dir", OUTPUT_DIR, "--input-file", str(temp_input_path)] |
| if max_events_str_web.isdigit(): command.extend(["--max-events", max_events_str_web]) |
| if output_filename: command.extend(["--output-filename", output_filename]) |
| if st.session_state.get("enable_fallback", False): command.append("--enable-fallback") |
| start_agent_process(command) |
| st.rerun() |
|
|
| def process_calendarcrawl_start(): |
| if st.session_state.agent_status == "Running": return |
| calendar_mode = st.session_state.get('cal_agent_mode', 'Crawl Cycling Calendar') |
| max_events_str_cal = st.session_state.get('max_events_cal', "") |
| output_filename = st.session_state.get('cal_output_filename', "") |
| uploaded_files_cal = st.session_state.get('cal_agent_uploader',[]) |
| |
| if calendar_mode == "Process Default Websites" and not uploaded_files_cal: |
| st.error("Please upload at least one JSON file for this mode.") |
| return |
| |
| temp_input_paths_cal =[] |
| if uploaded_files_cal: |
| for up_file in uploaded_files_cal: |
| path = TEMP_DIR / up_file.name |
| with open(path, "wb") as f: f.write(up_file.getvalue()) |
| temp_input_paths_cal.append(str(path)) |
| |
| mode_flag = "calendar_crawl" if calendar_mode == "Crawl Cycling Calendar" else "default_websites" |
| command =[sys.executable, "-u", "main.py", "--mode", mode_flag, "--output-dir", OUTPUT_DIR] |
| if temp_input_paths_cal: |
| command.append("--input-files") |
| command.extend(temp_input_paths_cal) |
| if max_events_str_cal.isdigit(): command.extend(["--max-events", max_events_str_cal]) |
| if output_filename: command.extend(["--output-filename", output_filename]) |
| if st.session_state.get("enable_fallback", False): command.append("--enable-fallback") |
| start_agent_process(command) |
| st.rerun() |
|
|
| def process_gemini_start(): |
| if st.session_state.agent_status == "Running": return |
| uploaded_images = st.session_state.get('gemini_uploader',[]) |
| output_filename = st.session_state.get('gemini_output_filename', "") |
| if not uploaded_images: |
| st.error("Please upload at least one image.") |
| return |
| |
| run_id = uuid.uuid4() |
| image_input_path = TEMP_DIR / f"gemini_input_{run_id}" |
| image_input_path.mkdir(exist_ok=True) |
| |
| for image in uploaded_images: |
| with open(image_input_path / image.name, "wb") as f: f.write(image.getbuffer()) |
| |
| command =[sys.executable, "-u", "gemini.py", "--output-dir", OUTPUT_DIR, "--input-dir", str(image_input_path)] |
| if output_filename: command.extend(["--output-filename", output_filename]) |
| if st.session_state.get("enable_fallback", False): command.append("--enable-fallback") |
| start_agent_process(command) |
| st.rerun() |
|
|
| |
| def show_home_screen(): |
| st.title("🤖 Welcome to the Crawl4AI Unified Agent System") |
| st.markdown(f"**Version:** `{APP_VERSION}`") |
| st.write("---") |
| |
| if not SECRETS_LOADED: |
| st.error("**Hugging Face Secrets Not Loaded.** Ensure keys like 'MISTRAL_API_KEY' are set in Space Settings.", icon="🔒") |
| elif not SUPABASE_CONFIGURED: |
| st.warning("**Supabase Not Configured.** The application will run in local-only mode.", icon="⚠️") |
|
|
| st.subheader("🔍 Select a Module to Begin") |
| st.write("") |
| |
| row1_col1, row1_col2 = st.columns(2, gap="large") |
| row2_col1, row2_col2 = st.columns(2, gap="large") |
|
|
| with row1_col1: |
| with st.container(border=True): |
| st.header("📘 Web Research Agent") |
| st.markdown("**(Crawl4AI)**") |
| st.write("Provide event names, and this agent will autonomously search trusted sources, validate information, and extract structured data using deep AI analysis.") |
| st.button("Launch Web Agent", on_click=change_view, args=("crawl4ai",), use_container_width=True, type="primary", disabled=not SECRETS_LOADED) |
|
|
| with row1_col2: |
| with st.container(border=True): |
| st.header("📄 Pre-scraped Data Processor") |
| st.markdown("**(CalendarCrawl)**") |
| st.write("Upload pre-scraped JSON files or supply known event calendar URLs. The processor extracts structured event details using the CalendarCrawl engine.") |
| st.button("Launch Data Processor", on_click=change_view, args=("calendarcrawl",), use_container_width=True, type="primary", disabled=not SECRETS_LOADED) |
| |
| with row2_col1: |
| with st.container(border=True): |
| st.header("🖼️ Gemini Image Processor") |
| st.markdown("**(Image Analysis)**") |
| st.write("Upload event posters or banners. This module uses Google Gemini Vision to extract all essential event details with intelligent image analysis.") |
| st.button("Launch Image Processor", on_click=change_view, args=("gemini",), use_container_width=True, type="primary", disabled=not SECRETS_LOADED) |
|
|
| with row2_col2: |
| with st.container(border=True): |
| st.header("🗂️ Database Manager") |
| st.markdown("**(Supabase-backed store)**") |
| st.write("View, manage, and delete persistent knowledge and cached crawl data stored securely in your Supabase project.") |
| st.button("Open Database Manager", on_click=change_view, args=("db_manager",), use_container_width=True, type="primary", disabled=not SUPABASE_CONFIGURED) |
|
|
| def show_agent_ui(): |
| is_running = st.session_state.agent_status == "Running" |
| with st.sidebar: |
| st.header("Configuration Status") |
| |
| if SECRETS_LOADED: |
| st.success("✅ Hugging Face Secrets loaded.") |
| else: |
| st.error("❌ Hugging Face Secrets missing.") |
|
|
| if SUPABASE_CONFIGURED: |
| st.success("☁️ Supabase configured.") |
| else: |
| st.warning("⚠️ Supabase not configured.") |
| |
| st.markdown("---") |
| |
| st.session_state.enable_fallback = st.checkbox( |
| "Enable Schema Fallback", |
| value=st.session_state.get('enable_fallback', False), |
| help="If disabled (default), execution will fail if Database2 context is unavailable or incomplete. Check this to use hardcoded local schemas as a fallback." |
| ) |
|
|
| st.markdown("---") |
| if st.button("🔒 Logout"): |
| st.session_state.password_correct = False |
| st.rerun() |
|
|
| col1, col2 = st.columns([0.45, 0.55], gap="large") |
| with col1: |
| st.button("⬅️ Back to Home", on_click=change_view, args=("home",), disabled=is_running) |
| st.header("Agent Control Panel") |
| |
| if st.session_state.view == "crawl4ai": render_crawl4ai_controls() |
| elif st.session_state.view == "calendarcrawl": render_calendarcrawl_controls() |
| elif st.session_state.view == "gemini": render_gemini_controls() |
| |
| st.header("📂 Results") |
| with st.container(border=True): |
| if st.session_state.agent_status == "Finished": |
| if st.session_state.output_files: |
| st.success("Processing finished!") |
| for file_path in st.session_state.output_files: |
| file = Path(file_path) |
| with open(file, "rb") as fp: |
| st.download_button(label=f"📄 Download {file.name}", data=fp, file_name=file.name, mime="text/csv", key=f"dl_{file.name}", use_container_width=True) |
| else: |
| st.warning("Process finished, but no new output files were generated.") |
| else: |
| st.info("Results from the current run will appear here after completion.") |
| with col2: |
| st.header("Live Status & Logs") |
| |
| progress_container = st.empty() |
|
|
| status = st.session_state.agent_status |
| if status == "Idle": st.info("📊 **Status:** Waiting to start a process.") |
| elif status == "Running": st.warning("⚙️ **Status:** Running... Logs refresh automatically.") |
| elif status == "Finished": st.success("✅ **Status:** Process finished successfully.") |
| elif status == "Terminated": st.error("🛑 **Status:** Process stopped by user.") |
| elif status == "Error": st.error("🔥 **Status:** Process failed with an error. Check logs.") |
| |
| if is_running: |
| if st.button("⏹️ Stop Active Process", use_container_width=True): |
| if st.session_state.active_process: |
| st.session_state.active_process.terminate() |
| time.sleep(1) |
| st.session_state.agent_status = "Terminated" |
| st.session_state.active_process = None |
| st.rerun() |
| |
| log_content = st.session_state.final_log_content if status != "Running" else read_log_file() |
| |
| if status == "Running": |
| curr, total = extract_progress(log_content) |
| if total > 0: |
| progress_container.progress(curr / total, text=f"Processing Event {curr} of {total}") |
| elif curr > 0: |
| progress_container.info(f"Processing Event {curr}...") |
|
|
| st.text_area("Log Output", value=log_content, height=500, disabled=True, key="log_area") |
|
|
| def show_db_manager_ui(): |
| st.button("⬅️ Back to Home", on_click=change_view, args=("home",)) |
| st.title("🗃️ Database & Cache Manager") |
| st.info("Here you can view data saved in Supabase and manage both local and remote caches.") |
| tab1, tab2 = st.tabs(["Knowledge Cache Manager", "Run Outputs Manager"]) |
| |
| with tab1: |
| col1, col2 = st.columns([0.4, 0.6], gap="large") |
| with col1: |
| st.subheader("Cached Events") |
| if st.button("🔄 Refresh Event List"): st.session_state.db_events =[] |
| if not st.session_state.db_events: |
| with st.spinner("Fetching events from Supabase..."): st.session_state.db_events = db_manager.list_all_events() |
| if not st.session_state.db_events: st.warning("No events found in the Supabase knowledge cache.") |
| else: |
| with st.container(height=400): |
| st.radio("Select an event to manage:", options=st.session_state.db_events, key="db_selected_event", label_visibility="collapsed") |
| st.write("---") |
| st.subheader("Global Cache Utilities") |
| with st.container(border=True): |
| if st.button("🗑️ Clear ALL Local Crawl Cache"): |
| try: |
| if Path(CRAWL_CACHE_DIR).exists(): shutil.rmtree(CRAWL_CACHE_DIR) |
| Path(CRAWL_CACHE_DIR).mkdir(exist_ok=True) |
| st.toast("Cleared Local Crawl Cache.", icon="✅") |
| except Exception as e: st.error(f"Error: {e}") |
| if st.button("🗑️ Clear ALL Local Knowledge Cache"): |
| try: |
| if Path(KNOWLEDGE_CACHE_DIR).exists(): shutil.rmtree(KNOWLEDGE_CACHE_DIR) |
| Path(KNOWLEDGE_CACHE_DIR).mkdir(exist_ok=True) |
| st.toast("Cleared Local Knowledge Cache.", icon="✅") |
| except Exception as e: st.error(f"Error: {e}") |
| if st.button("🗑️ Clear ENTIRE Local Vector DB"): |
| try: |
| if Path(VECTOR_DB_PATH).exists(): shutil.rmtree(VECTOR_DB_PATH) |
| Path(VECTOR_DB_PATH).mkdir(exist_ok=True) |
| st.toast("Cleared Local Vector DB.", icon="✅") |
| except Exception as e: st.error(f"Error: {e}") |
| with col2: |
| st.subheader("Event Details & Management") |
| if st.session_state.db_selected_event: |
| with st.container(border=True): |
| with st.spinner(f"Fetching details for '{st.session_state.db_selected_event}'..."): |
| event_data = db_manager.get_event_details(st.session_state.db_selected_event) |
| if event_data: |
| st.subheader("Supabase Knowledge Cache") |
| st.json(event_data, expanded=False) |
| st.write("---") |
| st.subheader("Local Cache Management") |
| if st.button("Clear Local Cache for this Event"): |
| with st.spinner(f"Clearing local cache for '{st.session_state.db_selected_event}'..."): |
| success, message = db_manager.clear_local_event_cache(st.session_state.db_selected_event) |
| if success: st.success(message) |
| else: st.error(message) |
| st.write("---") |
| st.subheader("⚠️ Danger Zone: Supabase Deletion") |
| confirm_delete = st.checkbox(f"I want to permanently delete all data for '{st.session_state.db_selected_event}' from Supabase.", key="delete_confirm") |
| if st.button("🔥 Hard Delete Event from Supabase", type="primary", disabled=not confirm_delete): |
| with st.spinner(f"Performing hard delete for '{st.session_state.db_selected_event}'..."): |
| success, message = db_manager.delete_event_from_supabase(st.session_state.db_selected_event) |
| if success: |
| st.success(message) |
| st.session_state.db_events =[] |
| st.session_state.db_selected_event = None |
| st.rerun() |
| else: |
| st.error(message) |
| else: |
| st.error("Could not retrieve details for the selected event.") |
| else: |
| st.info("Select an event from the list to see its cached data.") |
| with tab2: |
| st.subheader("Stored Run Outputs") |
| if st.button("🔄 Refresh Run Outputs"): st.session_state.db_run_outputs =[] |
| if not st.session_state.db_run_outputs: |
| with st.spinner("Fetching run outputs from Supabase..."): |
| st.session_state.db_run_outputs = db_manager.list_run_outputs() |
| if not st.session_state.db_run_outputs: st.warning("No run outputs found in Supabase.") |
| else: |
| for run in st.session_state.db_run_outputs: |
| with st.container(border=True): |
| st.markdown(f"**File:** `{run['filename']}`") |
| st.markdown(f"**Mode:** `{run['agent_mode']}` | **Events:** `{run['event_count']}` | **Date:** `{datetime.fromisoformat(run['created_at']).strftime('%Y-%m-%d %H:%M')}`") |
| c1, c2 = st.columns(2) |
| with c1: |
| df = pd.DataFrame(run['run_data']) |
| csv_data = df.to_csv(index=False).encode('utf-8') |
| st.download_button(label="📥 Download CSV", data=csv_data, file_name=run['filename'], mime="text/csv", key=f"dl_run_{run['id']}") |
| with c2: |
| if st.button("❌ Delete this Output", key=f"delete_run_{run['id']}", type="primary"): |
| success, message = db_manager.delete_run_output(run['id']) |
| if success: |
| st.toast(f"Deleted {run['filename']}", icon="✅") |
| st.session_state.db_run_outputs =[] |
| st.rerun() |
| else: |
| st.error(message) |
|
|
| def render_crawl4ai_controls(): |
| st.subheader("🌐 Web Research Agent") |
| st.info("This agent takes event names, searches the web, and performs deep analysis.") |
| is_running = st.session_state.agent_status == "Running" |
| with st.container(border=True): |
| st.subheader("1. Input Method") |
| st.radio("Choose how to provide event names:",["File Upload", "Manual Entry"], horizontal=True, label_visibility="collapsed", key='crawl4ai_input_method', disabled=is_running) |
| if st.session_state.crawl4ai_input_method == "File Upload": |
| st.file_uploader("Upload Event List File (JSON/CSV/XLSX)", type=['json', 'csv', 'xlsx'], key="web_agent_uploader", disabled=is_running, label_visibility="collapsed") |
| else: |
| st.subheader("Enter Event Details") |
| st.selectbox("Event Type (for all events below)", options=CHOICE_OPTIONS["type"], index=4, key='manual_event_type', disabled=is_running) |
| st.text_area("Event Names (one per line)", placeholder="Event Name 1\nEvent Name 2", height=150, key='manual_event_names', disabled=is_running) |
| st.text_input("Max events to process (optional)", key="max_events_web", help="Limit the number of events to process from the input.", disabled=is_running) |
| st.text_input("Custom Output Filename (optional)", key="crawl4ai_output_filename", help="e.g., 'My_Marathon_Run_Output'", disabled=is_running) |
| with st.container(border=True): |
| def set_start_flag(): st.session_state.start_crawl4ai_flag = True |
| st.button("▶️ Start Web Research Agent", on_click=set_start_flag, disabled=is_running or not SECRETS_LOADED, use_container_width=True, type="primary") |
|
|
| def render_calendarcrawl_controls(): |
| st.subheader("📄 Pre-scraped Data Processor") |
| st.info("This agent processes data from pre-scraped files or from a specific calendar URL.") |
| is_running = st.session_state.agent_status == "Running" |
| with st.container(border=True): |
| st.subheader("1. Mode") |
| st.selectbox("Select Mode",["Crawl Cycling Calendar", "Process Default Websites"], key='cal_agent_mode', disabled=is_running) |
| st.text_input("Max events to process (optional)", key="max_events_cal", help="Limit the number of events to process.", disabled=is_running) |
| st.text_input("Custom Output Filename (optional)", key="cal_output_filename", help="e.g., 'Cycling_Calendar_Output'", disabled=is_running) |
| with st.container(border=True): |
| st.subheader("2. Inputs") |
| if st.session_state.get('cal_agent_mode') == "Process Default Websites": |
| st.file_uploader("Upload Pre-scraped JSON File(s)", type=['json'], accept_multiple_files=True, key="cal_agent_uploader", disabled=is_running) |
| else: |
| st.success("This mode will automatically crawl `audaxindia.in`.") |
| with st.container(border=True): |
| def set_start_flag(): st.session_state.start_calendarcrawl_flag = True |
| st.button("▶️ Start Pre-scraped Data Processor", on_click=set_start_flag, disabled=is_running or not SECRETS_LOADED, use_container_width=True, type="primary") |
|
|
| def render_gemini_controls(): |
| st.subheader("🖼️ Gemini Image Processor") |
| st.info("This agent analyzes images of event posters to extract key details.") |
| is_running = st.session_state.agent_status == "Running" |
| with st.container(border=True): |
| st.subheader("1. Inputs") |
| st.file_uploader("Upload Image Files", type=['png', 'jpg', 'jpeg'], accept_multiple_files=True, key='gemini_uploader', disabled=is_running) |
| st.text_input("Custom Output Filename (optional)", key="gemini_output_filename", help="e.g., 'Poster_Analysis_Output'", disabled=is_running) |
| with st.container(border=True): |
| def set_start_flag(): st.session_state.start_gemini_flag = True |
| st.button("▶️ Start Gemini Processor", on_click=set_start_flag, disabled=is_running or not SECRETS_LOADED, use_container_width=True, type="primary") |
|
|
|
|
| |
| initialize_session_state() |
|
|
| if st.session_state.view == "home": |
| show_home_screen() |
| elif st.session_state.view == "db_manager": |
| show_db_manager_ui() |
| else: |
| show_agent_ui() |
|
|
| |
| if st.session_state.start_crawl4ai_flag: |
| st.session_state.start_crawl4ai_flag = False |
| process_crawl4ai_start() |
| elif st.session_state.start_calendarcrawl_flag: |
| st.session_state.start_calendarcrawl_flag = False |
| process_calendarcrawl_start() |
| elif st.session_state.start_gemini_flag: |
| st.session_state.start_gemini_flag = False |
| process_gemini_start() |
|
|
| |
| if st.session_state.agent_status == "Running": |
| process = st.session_state.active_process |
| if process and process.poll() is not None: |
| time.sleep(1) |
| st.session_state.final_log_content = read_log_file() |
| return_code = process.poll() |
| st.session_state.agent_status = "Finished" if return_code == 0 else "Error" |
| if return_code == 0: |
| files_after_run = get_files_in_dir(OUTPUT_DIR) |
| st.session_state.output_files = sorted(list(files_after_run - st.session_state.files_before_run)) |
| st.session_state.active_process = None |
| st.rerun() |
| else: |
| time.sleep(5) |
| st.rerun() |