Onboarding Improvements

This commit is contained in:
ajay.calsoft
2025-04-16 10:32:59 +00:00
parent 036fde9e81
commit 372bf71a0a
3 changed files with 252 additions and 281 deletions

View File

@@ -5,12 +5,12 @@ from loguru import logger
import streamlit as st import streamlit as st
import os import os
import sys import sys
# Assuming api_key_tests are in the parent directory relative to this component # Corrected import: Assuming validation functions are in validation.py in the parent directory
from ..api_key_tests import ( from ..validation import (
test_openai_api_key, test_openai_api_key,
test_gemini_api_key, test_gemini_api_key,
test_anthropic_api_key, # Keep if needed elsewhere, not used in this step currently # test_anthropic_api_key, # Keep commented if not used or add if needed
test_deepseek_api_key, # Keep if needed elsewhere # test_deepseek_api_key, # Keep commented if not used or add if needed
test_mistral_api_key test_mistral_api_key
) )
@@ -23,11 +23,24 @@ def _validate_provider_key(provider_name: str, key_value: str) -> bool:
try: try:
logger.debug(f"Validating key for {provider_name}...") logger.debug(f"Validating key for {provider_name}...")
if provider_name == "openai": if provider_name == "openai":
# Ensure the function exists in validation.py
if callable(getattr(sys.modules[__name__], 'test_openai_api_key', None)):
is_valid = test_openai_api_key(key_value) is_valid = test_openai_api_key(key_value)
else:
logger.error("test_openai_api_key not found in validation module")
is_valid = False # Assume invalid if test func missing
elif provider_name == "gemini": elif provider_name == "gemini":
if callable(getattr(sys.modules[__name__], 'test_gemini_api_key', None)):
is_valid = test_gemini_api_key(key_value) is_valid = test_gemini_api_key(key_value)
else:
logger.error("test_gemini_api_key not found in validation module")
is_valid = False
elif provider_name == "mistral": elif provider_name == "mistral":
if callable(getattr(sys.modules[__name__], 'test_mistral_api_key', None)):
is_valid = test_mistral_api_key(key_value) is_valid = test_mistral_api_key(key_value)
else:
logger.error("test_mistral_api_key not found in validation module")
is_valid = False
else: else:
logger.warning(f"Validation not implemented for provider: {provider_name}") logger.warning(f"Validation not implemented for provider: {provider_name}")
return False # Or True if unknown providers are allowed without validation return False # Or True if unknown providers are allowed without validation
@@ -134,14 +147,15 @@ def render_ai_providers_setup(api_key_manager) -> Dict[str, Any]:
) )
# Feedback Area for OpenAI # Feedback Area for OpenAI
openai_status = st.session_state.get("openai_status", "unsaved") openai_status = st.session_state.get("openai_status", "unsaved")
feedback_placeholder_openai = st.empty()
if openai_status == "saving": if openai_status == "saving":
st.spinner("Validating OpenAI key...") feedback_placeholder_openai.info("Validating OpenAI key...", icon="")
elif openai_status == "valid": elif openai_status == "valid":
st.success("OpenAI key saved and valid!", icon="") feedback_placeholder_openai.success("OpenAI key saved and valid!", icon="")
elif openai_status == "invalid": elif openai_status == "invalid":
st.error("Invalid OpenAI key. Please check and try again.", icon="") feedback_placeholder_openai.error("Invalid OpenAI key. Please check and try again.", icon="")
elif openai_status == "error": elif openai_status == "error":
st.error("Error saving/validating OpenAI key.", icon="⚠️") feedback_placeholder_openai.error("Error saving/validating OpenAI key.", icon="⚠️")
# --- Google Gemini --- # --- Google Gemini ---
st.subheader("Google Gemini (Required)") st.subheader("Google Gemini (Required)")
@@ -157,14 +171,15 @@ def render_ai_providers_setup(api_key_manager) -> Dict[str, Any]:
) )
# Feedback Area for Gemini # Feedback Area for Gemini
gemini_status = st.session_state.get("gemini_status", "unsaved") gemini_status = st.session_state.get("gemini_status", "unsaved")
feedback_placeholder_gemini = st.empty()
if gemini_status == "saving": if gemini_status == "saving":
st.spinner("Validating Gemini key...") feedback_placeholder_gemini.info("Validating Gemini key...", icon="")
elif gemini_status == "valid": elif gemini_status == "valid":
st.success("Gemini key saved and valid!", icon="") feedback_placeholder_gemini.success("Gemini key saved and valid!", icon="")
elif gemini_status == "invalid": elif gemini_status == "invalid":
st.error("Invalid Gemini key. Please check and try again.", icon="") feedback_placeholder_gemini.error("Invalid Gemini key. Please check and try again.", icon="")
elif gemini_status == "error": elif gemini_status == "error":
st.error("Error saving/validating Gemini key.", icon="⚠️") feedback_placeholder_gemini.error("Error saving/validating Gemini key.", icon="⚠️")
# --- Mistral AI (Optional) --- # --- Mistral AI (Optional) ---
st.subheader("Mistral AI (Optional)") st.subheader("Mistral AI (Optional)")
@@ -180,20 +195,19 @@ def render_ai_providers_setup(api_key_manager) -> Dict[str, Any]:
) )
# Feedback Area for Mistral # Feedback Area for Mistral
mistral_status = st.session_state.get("mistral_status", "unsaved") mistral_status = st.session_state.get("mistral_status", "unsaved")
feedback_placeholder_mistral = st.empty()
if mistral_status == "saving": if mistral_status == "saving":
st.spinner("Validating Mistral key...") feedback_placeholder_mistral.info("Validating Mistral key...", icon="")
elif mistral_status == "valid": elif mistral_status == "valid":
st.success("Mistral key saved and valid!", icon="") feedback_placeholder_mistral.success("Mistral key saved and valid!", icon="")
elif mistral_status == "invalid": elif mistral_status == "invalid":
st.error("Invalid Mistral key. Please check and try again.", icon="") feedback_placeholder_mistral.error("Invalid Mistral key. Please check and try again.", icon="")
elif mistral_status == "error": elif mistral_status == "error":
st.error("Error saving/validating Mistral key.", icon="⚠️") feedback_placeholder_mistral.error("Error saving/validating Mistral key.", icon="⚠️")
# --- Final Notes --- # --- Final Notes ---
st.info("Note: At least one AI provider (OpenAI or Google Gemini) must have a valid API key to proceed.") st.info("Note: At least one AI provider (OpenAI or Google Gemini) must have a valid API key to proceed.")
# REMOVED the old saving logic block here
# Return value is not strictly needed if navigation relies on session state status # Return value is not strictly needed if navigation relies on session state status
return {} return {}

View File

@@ -4,12 +4,11 @@ import streamlit as st
from loguru import logger from loguru import logger
from typing import Dict, Any from typing import Dict, Any
from ..manager import APIKeyManager from ..manager import APIKeyManager
# Removed import of render_navigation_buttons as it's handled in base
import os import os
from dotenv import load_dotenv # Keep if api_key_manager uses it from dotenv import load_dotenv # Keep if api_key_manager uses it
import sys import sys
# Import test functions (adjust path if needed) # Corrected import: Assuming validation functions are in validation.py in the parent directory
from ..api_key_tests import ( from ..validation import (
test_serpapi_key, test_serpapi_key,
test_tavily_key, test_tavily_key,
test_metaphor_key, test_metaphor_key,
@@ -28,14 +27,31 @@ def _validate_research_key(provider_name: str, key_value: str) -> bool:
return False return False
try: try:
logger.debug(f"Validating key for {provider_name}...") logger.debug(f"Validating key for {provider_name}...")
# Ensure the function exists in validation.py before calling
if provider_name == "serpapi": if provider_name == "serpapi":
if callable(getattr(sys.modules[__name__], 'test_serpapi_key', None)):
is_valid = test_serpapi_key(key_value) is_valid = test_serpapi_key(key_value)
else:
logger.error("test_serpapi_key not found in validation module")
is_valid = False
elif provider_name == "tavily": elif provider_name == "tavily":
if callable(getattr(sys.modules[__name__], 'test_tavily_key', None)):
is_valid = test_tavily_key(key_value) is_valid = test_tavily_key(key_value)
else:
logger.error("test_tavily_key not found in validation module")
is_valid = False
elif provider_name == "metaphor": elif provider_name == "metaphor":
if callable(getattr(sys.modules[__name__], 'test_metaphor_key', None)):
is_valid = test_metaphor_key(key_value) is_valid = test_metaphor_key(key_value)
else:
logger.error("test_metaphor_key not found in validation module")
is_valid = False
elif provider_name == "firecrawl": elif provider_name == "firecrawl":
if callable(getattr(sys.modules[__name__], 'test_firecrawl_key', None)):
is_valid = test_firecrawl_key(key_value) is_valid = test_firecrawl_key(key_value)
else:
logger.error("test_firecrawl_key not found in validation module")
is_valid = False
else: else:
logger.warning(f"Validation not implemented for research provider: {provider_name}") logger.warning(f"Validation not implemented for research provider: {provider_name}")
return False # Default to False for unknown providers return False # Default to False for unknown providers
@@ -220,6 +236,4 @@ def render_ai_research_setup(api_key_manager: APIKeyManager) -> Dict[str, Any]:
st.info("You can skip this step if you don't need these research tools. Click Continue to proceed.") st.info("You can skip this step if you don't need these research tools. Click Continue to proceed.")
# Removed the old saving logic block here
return {} return {}

View File

@@ -2,265 +2,208 @@
import streamlit as st import streamlit as st
from loguru import logger from loguru import logger
from ...website_analyzer import analyze_website # Removed website_analyzer imports as analysis is separate now
from ...website_analyzer.seo_analyzer import analyze_seo # from ...website_analyzer import analyze_website
# from ...website_analyzer.seo_analyzer import analyze_seo
import asyncio import asyncio
import sys import sys
from typing import Dict, Any from typing import Dict, Any
import requests
import ssl
import socket
from urllib.parse import urlparse
from ..manager import APIKeyManager from ..manager import APIKeyManager
from .base import render_navigation_buttons # Navigation is handled in base.py now
# from .base import render_navigation_buttons
# Configure logger to output to both file and stdout # Configure logger (minimal example)
logger.remove() # Remove default handler logger.add(sys.stderr, level="INFO")
logger.add(
"logs/website_setup.log",
rotation="500 MB",
retention="10 days",
level="DEBUG",
format="{time:YYYY-MM-DD HH:mm:ss} | {level} | {message}"
)
logger.add(
sys.stdout,
level="INFO",
format="<green>{time:YYYY-MM-DD HH:mm:ss}</green> | <level>{level: <8}</level> | <cyan>{message}</cyan>"
)
def render_website_setup(api_key_manager: APIKeyManager) -> Dict[str, Any]: # --- Validation Helpers ---
"""Render the website setup step. def _is_valid_url_format(url: str) -> bool:
"""Checks if the URL has a valid basic format (scheme and netloc)."""
Args:
api_key_manager (APIKeyManager): The API key manager instance
Returns:
Dict[str, Any]: Current state
"""
logger.info("[render_website_setup] Rendering website setup component")
st.markdown("### Step 2: Website Setup")
# Create two columns for input and results
col1, col2 = st.columns([1, 1])
with col1:
st.markdown("#### Enter Website URL")
url = st.text_input("Website URL", placeholder="https://example.com")
logger.debug(f"[render_website_setup] URL input value: {url}")
analyze_type = st.radio(
"Analysis Type",
["Basic Analysis", "Full Analysis with SEO"],
help="Choose between basic website analysis or comprehensive SEO analysis"
)
if st.button("Analyze Website"):
if url:
with st.spinner("Analyzing website..."):
try: try:
logger.info(f"[render_website_setup] Starting website analysis for URL: {url}") result = urlparse(url)
return all([result.scheme in ['http', 'https'], result.netloc])
except ValueError:
return False
# Call the analyze_website function def _check_url_reachability(url: str) -> tuple[bool, str]:
results = analyze_website(url) """Checks if the URL is reachable and returns status code or error."""
try:
response = requests.head(url, allow_redirects=True, timeout=5) # HEAD request is faster
response.raise_for_status() # Raise HTTPError for bad responses (4xx or 5xx)
logger.info(f"URL {url} reachable, status code: {response.status_code}")
return True, f"Reachable (Status: {response.status_code})"
except requests.exceptions.Timeout:
logger.warning(f"URL {url} timed out.")
return False, "Timeout: Server did not respond in time."
except requests.exceptions.RequestException as e:
logger.warning(f"URL {url} not reachable: {e}")
# Provide a more user-friendly message for common errors
if isinstance(e, requests.exceptions.ConnectionError):
return False, "Connection Error: Could not connect to the server."
elif isinstance(e, requests.exceptions.HTTPError):
return False, f"HTTP Error: {e.response.status_code}"
return False, f"Error: {type(e).__name__}"
# If full analysis is selected, add SEO analysis def _check_ssl_certificate(url: str) -> tuple[bool, str]:
if analyze_type == "Full Analysis with SEO": """Checks if the URL has a valid SSL certificate (for https)."""
seo_results = analyze_seo(url) parsed_url = urlparse(url)
if seo_results.success: if parsed_url.scheme != 'https':
results['data']['seo_analysis'] = { return True, "(HTTP URL)" # Not applicable for http
'overall_score': seo_results.overall_score,
'meta_tags': {
'title': seo_results.meta_tags.title,
'description': seo_results.meta_tags.description,
'keywords': seo_results.meta_tags.keywords,
'has_robots': seo_results.meta_tags.has_robots,
'has_sitemap': seo_results.meta_tags.has_sitemap
},
'content': {
'word_count': seo_results.content.word_count,
'readability_score': seo_results.content.readability_score,
'content_quality_score': seo_results.content.content_quality_score,
'headings_structure': seo_results.content.headings_structure,
'keyword_density': seo_results.content.keyword_density
},
'recommendations': [
{
'priority': rec.priority,
'category': rec.category,
'issue': rec.issue,
'recommendation': rec.recommendation,
'impact': rec.impact
}
for rec in seo_results.recommendations
]
}
logger.debug(f"[render_website_setup] Analysis results received: {results.get('success', False)}") hostname = parsed_url.netloc
port = 443
# Store results in session state context = ssl.create_default_context()
st.session_state.website_analysis = results try:
logger.info("[render_website_setup] Results stored in session state") with socket.create_connection((hostname, port), timeout=3) as sock:
with context.wrap_socket(sock, server_hostname=hostname) as ssock:
if not results.get('success', False): cert = ssock.getpeercert()
error_msg = results.get('error', 'Analysis failed') # Basic check: does it exist? More thorough checks (expiry, chain) are possible
logger.error(f"[render_website_setup] Analysis failed: {error_msg}") if cert:
st.error(error_msg) logger.info(f"SSL certificate found for {hostname}")
return True, "Valid SSL Certificate"
else: else:
logger.info("[render_website_setup] Analysis completed successfully") logger.warning(f"No SSL certificate found for {hostname}")
st.success("✅ Website analysis completed successfully!") return False, "No SSL Certificate found"
except ssl.SSLCertVerificationError as e:
logger.warning(f"SSL Verification Error for {hostname}: {e}")
return False, f"SSL Verification Error: {e.verify_message}"
except socket.timeout:
logger.warning(f"SSL check timed out for {hostname}")
return False, "SSL Check Timeout"
except Exception as e: except Exception as e:
error_msg = f"Analysis failed: {str(e)}" logger.error(f"Error checking SSL for {hostname}: {e}", exc_info=True)
logger.error(f"[render_website_setup] {error_msg}") return False, f"SSL Check Error: {type(e).__name__}"
st.error(error_msg)
else:
logger.warning("[render_website_setup] No URL provided")
st.warning("Please enter a valid URL")
with col2:
st.markdown("#### Analysis Results")
# Check if we have analysis results # --- Main Component Logic ---
if 'website_analysis' in st.session_state:
results = st.session_state.website_analysis
if results.get('success', False): def _validate_website_url(url: str) -> tuple[str, str]:
data = results.get('data', {}) """Performs quick validation (format, reachability, basic SSL)."""
analysis = data.get('analysis', {}) if not url:
return "unsaved", ""
# Create tabs for different sections # 1. Format Check
if analyze_type == "Full Analysis with SEO": if not _is_valid_url_format(url):
tab1, tab2, tab3, tab4, tab5 = st.tabs([ logger.warning(f"Invalid URL format: {url}")
"Basic Metrics", return "invalid_format", "Invalid URL format. Please include http:// or https://"
"Content Analysis",
"SEO Analysis",
"Technical SEO",
"Strategy"
])
else:
tab1, tab2, tab3, tab4 = st.tabs([
"Basic Metrics",
"Content Analysis",
"Technical Info",
"Strategy"
])
with tab1: # 2. Reachability Check
st.markdown("##### Basic Metrics") reachable, reach_status = _check_url_reachability(url)
basic_info = analysis.get('basic_info', {}) if not reachable:
st.write(f"Status Code: {basic_info.get('status_code')}") logger.warning(f"URL not reachable: {url} ({reach_status})")
st.write(f"Content Type: {basic_info.get('content_type')}") return "unreachable", reach_status # Return specific error message
st.write(f"Title: {basic_info.get('title')}")
st.write(f"Meta Description: {basic_info.get('meta_description')}")
# SSL Info # 3. Basic SSL Check (only if reachable and HTTPS)
ssl_info = analysis.get('ssl_info', {}) if urlparse(url).scheme == 'https':
if ssl_info.get('has_ssl'): ssl_valid, ssl_status = _check_ssl_certificate(url)
st.success("SSL Certificate is valid") if not ssl_valid:
st.write(f"Expiry: {ssl_info.get('expiry')}") logger.warning(f"SSL check failed for {url} ({ssl_status})")
else: return "ssl_error", ssl_status # Return specific error message
st.error("No valid SSL certificate found")
with tab2: logger.info(f"URL validation successful for: {url}")
st.markdown("##### Content Analysis") return "valid", "URL is valid and reachable."
content_info = analysis.get('content_info', {})
# Content Overview
st.markdown("###### 📊 Content Overview")
col1, col2, col3, col4 = st.columns(4)
with col1:
st.metric("Word Count", content_info.get('word_count', 0))
with col2:
st.metric("Headings", content_info.get('heading_count', 0))
with col3:
st.metric("Images", content_info.get('image_count', 0))
with col4:
st.metric("Links", content_info.get('link_count', 0))
if analyze_type == "Full Analysis with SEO": def _handle_website_url_change(api_key_manager: APIKeyManager):
with tab3: """Save and validate website URL when input changes."""
st.markdown("##### SEO Analysis") url_input_widget_key = "website_url_input"
seo_data = data.get('seo_analysis', {}) status_widget_key = "website_url_status"
# Display SEO Score if url_input_widget_key not in st.session_state:
seo_score = seo_data.get('overall_score', 0) logger.warning(f"Input widget key '{url_input_widget_key}' not found.")
st.markdown(f"### SEO Score: {seo_score}/100") return
st.progress(seo_score / 100)
# Meta Tags Analysis url_value = st.session_state[url_input_widget_key]
st.markdown("#### Meta Tags Analysis") logger.debug(f"Handling website URL change. URL: {url_value}")
meta_analysis = seo_data.get('meta_tags', {})
for key, value in meta_analysis.items():
if isinstance(value, bool):
st.write(f"{'' if value else ''} {key.replace('_', ' ').title()}")
elif isinstance(value, dict):
st.write(f"**{key.replace('_', ' ').title()}:**")
st.write(f"Status: {value.get('status', 'N/A')}")
st.write(f"Value: {value.get('value', 'N/A')}")
if value.get('recommendation'):
st.write(f"Recommendation: {value['recommendation']}")
else:
st.write(f"**{key.replace('_', ' ').title()}:** {value}")
# Content Analysis # Save the URL regardless of validity for now (maybe refine later)
st.markdown("#### AI Content Analysis") # api_key_manager might not be the right place, consider storing directly in session state
content_analysis = seo_data.get('content', {}) # or a dedicated config manager if this isn't an API key.
st.write(f"**Word Count:** {content_analysis.get('word_count', 0)}") # Let's store in session_state for now.
st.write(f"**Readability Score:** {content_analysis.get('readability_score', 0)}/100") st.session_state['configured_website_url'] = url_value
st.write(f"**Content Quality Score:** {content_analysis.get('content_quality_score', 0)}/100") logger.info(f"Saved website URL to session state: {url_value}")
# Recommendations if not url_value:
st.markdown("#### SEO Recommendations") st.session_state[status_widget_key] = ("unsaved", "")
recommendations = seo_data.get('recommendations', []) logger.info("Cleared website URL.")
for rec in recommendations: return
st.write(f"**{rec.get('priority', '').upper()} Priority - {rec.get('category', '')}**")
st.write(f"Issue: {rec.get('issue', '')}")
st.write(f"Recommendation: {rec.get('recommendation', '')}")
st.write(f"Impact: {rec.get('impact', '')}")
st.write("---")
with tab4: st.session_state[status_widget_key] = ("saving", "") # Indicate validation is running
st.markdown("##### Technical SEO")
technical_seo = seo_data.get('technical_analysis', {})
# Mobile Friendliness
st.markdown("#### Mobile Friendliness")
mobile_friendly = technical_seo.get('mobile_friendly', False)
st.write(f"{'' if mobile_friendly else ''} Mobile Friendly")
# Page Speed
st.markdown("#### Page Speed")
speed_metrics = technical_seo.get('speed_metrics', {})
for metric, value in speed_metrics.items():
st.write(f"**{metric.replace('_', ' ').title()}:** {value}")
# Technical Issues
st.markdown("#### Technical Issues")
issues = technical_seo.get('issues', [])
for issue in issues:
st.write(f"{issue}")
with tab4 if analyze_type == "Basic Analysis" else tab5:
st.markdown("##### Strategy Recommendations")
strategy_info = analysis.get('strategy', {})
if strategy_info:
for category, recommendations in strategy_info.items():
st.markdown(f"###### {category.replace('_', ' ').title()}")
for rec in recommendations:
st.write(f"{rec}")
else:
st.info("No strategy recommendations available")
else:
error_msg = results.get('error', 'Analysis failed')
logger.error(f"[render_website_setup] Displaying error: {error_msg}")
st.error(error_msg)
else:
logger.debug("[render_website_setup] No analysis results in session state")
st.info("Enter a URL and click 'Analyze Website' to see results")
# Navigation buttons
if render_navigation_buttons(2, 5, True):
# Move to next step (AI Research Setup)
st.session_state.current_step = 3
st.session_state.next_step = "ai_research_setup"
st.rerun() st.rerun()
return {"current_step": 2, "changes_made": True} try:
validation_status, message = _validate_website_url(url_value)
st.session_state[status_widget_key] = (validation_status, message)
logger.info(f"Website URL validation complete. Status: {validation_status}, Msg: {message}")
except Exception as e:
logger.error(f"Error during website URL validation: {e}", exc_info=True)
st.session_state[status_widget_key] = ("error", "An unexpected error occurred during validation.")
def render_website_setup(api_key_manager: APIKeyManager) -> Dict[str, Any]:
"""Render the website setup step with immediate feedback."""
logger.info("[render_website_setup] Rendering website setup component")
status_key = "website_url_status"
# Initialize status
if status_key not in st.session_state:
st.session_state[status_key] = ("unsaved", "")
# Optionally pre-validate if a URL exists from previous session/config
# pre_existing_url = api_key_manager.get_config("website_url") # Example
# if pre_existing_url:
# st.session_state[status_key] = _validate_website_url(pre_existing_url)
st.markdown("""
<div class='setup-header'>
<h2>Step 2: Website Setup (Optional)</h2>
<p>Enter your primary website URL. This helps Alwrity personalize suggestions and analyze your content.</p>
</div>
""", unsafe_allow_html=True)
# Get current value from session state if available, otherwise empty
current_url = st.session_state.get('configured_website_url', "")
st.text_input(
"Website URL",
value=current_url,
placeholder="https://example.com",
key="website_url_input",
on_change=_handle_website_url_change,
args=(api_key_manager,) # Pass manager if needed by save logic
)
# --- Feedback Area ---
status, message = st.session_state.get(status_key, ("unsaved", ""))
feedback_placeholder = st.empty()
if status == "saving":
feedback_placeholder.info("Validating URL...", icon="")
elif status == "valid":
feedback_placeholder.success(message, icon="")
elif status == "invalid_format":
feedback_placeholder.error(f"Format Error: {message}", icon="")
elif status == "unreachable":
feedback_placeholder.error(f"Reachability Error: {message}", icon="")
elif status == "ssl_error":
feedback_placeholder.warning(f"SSL Warning: {message}", icon="⚠️") # Warning for SSL
elif status == "error":
feedback_placeholder.error(f"Validation Error: {message}", icon="⚠️")
elif status == "unsaved" and current_url: # Show warning if field has text but isn't validated yet
feedback_placeholder.warning("URL not yet validated.", icon="⚠️")
# --- Removed Analysis Section ---
# The detailed website analysis should be a separate feature, not part of the initial setup validation.
st.markdown("--- ---“)
st.markdown(" *The detailed Website Analyzer tool is available separately in the main application.*")
st.info("Entering your website URL is optional. Click Continue to proceed.")
# Return value is not strictly needed if navigation relies on session state status
return {}
# Removed old analysis logic and button handling as it's handled in base.py