Files
ALwrity/Roadmap TBDs/ONBOARDING_IMPROVEMENTS.md
2025-04-21 21:07:35 +05:30

24 KiB
Raw Blame History

Onboarding Process Improvements

This document outlines a comprehensive plan to improve the user onboarding experience in AI-Writer, focusing on the API key management and initial setup process.

Current Issues

After analyzing the current onboarding process in utils.api_key_manager, several issues were identified:

User Experience Issues

  • Complex Multi-step Process: The onboarding is split across multiple steps without clear indication of progress or purpose
  • Confusing Navigation: Users can get lost between steps with no clear path forward
  • Required vs. Optional: No clear distinction between required and optional API keys
  • No Skip Option: Users must go through all steps even if some are not relevant to them
  • Limited Guidance: Insufficient contextual help for users unfamiliar with API keys

Technical Issues

  • Inconsistent State Management: Wizard state is initialized in multiple places
  • Basic Validation: API keys are only checked for non-emptiness, not actual validity
  • Environment Variable Handling: Not robust across different environments
  • Error Handling: Inconsistent error handling and user feedback
  • No Testing Mechanism: No way to test API keys during setup

UI/Design Issues

  • Inconsistent Styling: Visual inconsistency across different components
  • Poor Mobile Experience: Limited responsiveness for mobile users
  • Visual Hierarchy: Lack of clear visual distinction for important elements
  • Help Text Visibility: Instructions and help text are not prominent enough

Proposed Improvements

1. Redesigned Onboarding Flow

Welcome Screen

┌─────────────────────────────────────────────────────┐
│                                                     │
│  Welcome to AI-Writer! 👋                           │
│                                                     │
│  Let's get you set up in just a few minutes.        │
│                                                     │
│  What would you like to do?                         │
│                                                     │
│  ○ Quick Start (minimal setup)                      │
│  ○ Complete Setup (all features)                    │
│  ○ Import Configuration                             │
│                                                     │
│  [Start Setup]                                      │
│                                                     │
└─────────────────────────────────────────────────────┘

API Key Setup Screen

┌─────────────────────────────────────────────────────┐
│                                                     │
│  Step 1 of 4: Connect AI Models                     │
│  ━━━━━━━━━━○○○○                                     │
│                                                     │
│  Required (choose at least one):                    │
│  ┌─────────────────────────────────────────────┐    │
│  │ ⭐ OpenAI API Key                           │    │
│  │ [••••••••••••••••••]                        │    │
│  │ ✓ Validated successfully!                   │    │
│  └─────────────────────────────────────────────┘    │
│                                                     │
│  ┌─────────────────────────────────────────────┐    │
│  │ ⭐ Google Gemini API Key                    │    │
│  │ [                    ]                      │    │
│  │  Not configured (optional)                │    │
│  └─────────────────────────────────────────────┘    │
│                                                     │
│  Optional:                                          │
│  ┌─────────────────────────────────────────────┐    │
│  │ Anthropic API Key (Coming Soon)             │    │
│  │ [                    ]                      │    │
│  └─────────────────────────────────────────────┘    │
│                                                     │
│  [Skip Optional] [Test Keys] [Continue →]           │
│                                                     │
└─────────────────────────────────────────────────────┘

Progress Summary Screen

┌─────────────────────────────────────────────────────┐
│                                                     │
│  Setup Complete! 🎉                                 │
│                                                     │
│  Here's your configuration:                         │
│                                                     │
│  AI Models:                                         │
│  ✅ OpenAI API - Connected                          │
│  ❌ Google Gemini - Not configured                  │
│                                                     │
│  Research Tools:                                    │
│  ✅ Tavily Search - Connected                       │
│  ❌ Serper API - Not configured                     │
│                                                     │
│  Publishing:                                        │
│  ❌ WordPress - Not configured                      │
│                                                     │
│  You can change these settings anytime from         │
│  the Settings menu.                                 │
│                                                     │
│  [Edit Configuration] [Start Using AI-Writer]       │
│                                                     │
└─────────────────────────────────────────────────────┘

2. Technical Improvements

Unified State Management

# Create a dedicated state manager class
class OnboardingStateManager:
    """Manages the state of the onboarding process."""
    
    def __init__(self):
        """Initialize the onboarding state."""
        if 'onboarding_state' not in st.session_state:
            st.session_state.onboarding_state = {
                'current_step': 1,
                'total_steps': 4,
                'completed_steps': set(),
                'api_keys': {},
                'validated_keys': {},
                'setup_mode': 'quick_start',  # or 'complete'
                'setup_complete': False
            }
    
    def get_state(self):
        """Get the current onboarding state."""
        return st.session_state.onboarding_state
    
    def update_state(self, updates):
        """Update the onboarding state."""
        st.session_state.onboarding_state.update(updates)
    
    def next_step(self):
        """Move to the next step."""
        current = st.session_state.onboarding_state['current_step']
        total = st.session_state.onboarding_state['total_steps']
        
        if current < total:
            st.session_state.onboarding_state['current_step'] += 1
            st.session_state.onboarding_state['completed_steps'].add(current)
    
    def previous_step(self):
        """Move to the previous step."""
        if st.session_state.onboarding_state['current_step'] > 1:
            st.session_state.onboarding_state['current_step'] -= 1
    
    def skip_to_step(self, step):
        """Skip to a specific step."""
        if 1 <= step <= st.session_state.onboarding_state['total_steps']:
            st.session_state.onboarding_state['current_step'] = step
    
    def mark_complete(self):
        """Mark the onboarding as complete."""
        st.session_state.onboarding_state['setup_complete'] = True

Enhanced API Key Validation

async def validate_api_key(service, key):
    """Validate an API key by making a test request."""
    try:
        if service == "openai":
            # Test OpenAI API key with a minimal request
            import openai
            client = openai.OpenAI(api_key=key)
            response = await client.models.list()
            return {"valid": True, "models": [model.id for model in response.data[:5]]}
            
        elif service == "gemini":
            # Test Google Gemini API key
            import google.generativeai as genai
            genai.configure(api_key=key)
            models = genai.list_models()
            return {"valid": True, "models": [model.name for model in models]}
            
        elif service == "tavily":
            # Test Tavily API key
            import requests
            response = requests.get(
                "https://api.tavily.com/health",
                headers={"x-api-key": key}
            )
            if response.status_code == 200:
                return {"valid": True, "status": "healthy"}
            else:
                return {"valid": False, "error": f"Status code: {response.status_code}"}
                
        # Add more services as needed
            
    except Exception as e:
        return {"valid": False, "error": str(e)}

Secure API Key Storage

def save_api_keys(keys_dict):
    """Save API keys securely."""
    try:
        # 1. Save to .env file
        env_path = os.path.join(os.path.dirname(os.path.dirname(__file__)), '.env')
        
        # Read existing .env file
        env_contents = {}
        if os.path.exists(env_path):
            with open(env_path, 'r') as f:
                for line in f:
                    if '=' in line:
                        key, value = line.strip().split('=', 1)
                        env_contents[key] = value
        
        # Update with new keys
        for key_name, key_value in keys_dict.items():
            if key_value:  # Only save non-empty keys
                env_key = f"{key_name.upper()}_API_KEY"
                env_contents[env_key] = key_value
        
        # Write back to .env file
        with open(env_path, 'w') as f:
            for key, value in env_contents.items():
                f.write(f"{key}={value}\n")
        
        # 2. Also store in session state for immediate use
        for key_name, key_value in keys_dict.items():
            if key_value:
                st.session_state[f"{key_name}_api_key"] = key_value
        
        # 3. Set environment variables for current session
        for key_name, key_value in keys_dict.items():
            if key_value:
                os.environ[f"{key_name.upper()}_API_KEY"] = key_value
        
        return True
    except Exception as e:
        logger.error(f"Error saving API keys: {str(e)}")
        return False

3. UI/UX Improvements

Responsive Design

def render_responsive_layout():
    """Render a responsive layout that works on mobile and desktop."""
    # Check viewport width
    st.markdown("""
        <script>
            var width = window.innerWidth;
            if (width < 768) {
                document.documentElement.style.setProperty('--layout', 'mobile');
            } else {
                document.documentElement.style.setProperty('--layout', 'desktop');
            }
        </script>
        
        <style>
            /* Mobile-first styles */
            .container {
                padding: 1rem;
                margin-bottom: 1rem;
            }
            
            /* Desktop adjustments */
            @media (min-width: 768px) {
                .container {
                    padding: 2rem;
                    margin-bottom: 2rem;
                }
            }
        </style>
    """, unsafe_allow_html=True)

Visual Hierarchy for Required vs Optional

def render_api_key_input(label, key_name, required=False, help_text=""):
    """Render an API key input with clear visual hierarchy."""
    
    # Add required indicator if needed
    display_label = f"{label} {'*' if required else '(optional)'}"
    
    # Get existing value from session state or environment
    existing_value = st.session_state.get(f"{key_name}_api_key", "") or os.getenv(f"{key_name.upper()}_API_KEY", "")
    
    # Render the input with appropriate styling
    st.markdown(f"""
        <div class="api-key-input {'required' if required else 'optional'}">
            <label>{display_label}</label>
            <div class="input-help-text">{help_text}</div>
        </div>
    """, unsafe_allow_html=True)
    
    # The actual input field
    value = st.text_input(
        label="",  # Empty because we use custom label above
        value=existing_value,
        type="password",
        key=f"input_{key_name}",
        label_visibility="collapsed"
    )
    
    # Validation status
    if value:
        is_valid = key_name in st.session_state.get("validated_keys", {})
        if is_valid:
            st.success(f"✓ {label} validated successfully")
        else:
            st.info(f"⚠️ {label} not validated yet")
    
    return value

Interactive Help and Tooltips

def render_help_section(service):
    """Render an interactive help section for getting API keys."""
    
    help_content = {
        "openai": {
            "title": "How to get your OpenAI API key",
            "steps": [
                "Go to [OpenAI's website](https://platform.openai.com)",
                "Sign up or log in to your account",
                "Navigate to the API section",
                "Click 'Create new secret key'",
                "Copy the generated key and paste it here"
            ],
            "note": "Keep your API key secure and never share it publicly.",
            "pricing": "$0.002 per 1K tokens for GPT-3.5, $0.06 per 1K tokens for GPT-4",
            "link": "https://platform.openai.com/account/api-keys"
        },
        "gemini": {
            "title": "How to get your Google Gemini API key",
            "steps": [
                "Visit [Google AI Studio](https://makersuite.google.com/app/apikey)",
                "Sign in with your Google account",
                "Click 'Create API key'",
                "Copy the generated key and paste it here"
            ],
            "note": "Make sure to enable the Gemini API in your Google Cloud Console.",
            "pricing": "Free tier available, then $0.0025 per 1K tokens",
            "link": "https://makersuite.google.com/app/apikey"
        }
        # Add more services as needed
    }
    
    if service in help_content:
        content = help_content[service]
        
        with st.expander(f"📋 {content['title']}", expanded=False):
            st.markdown("**Step-by-step guide:**")
            for i, step in enumerate(content["steps"], 1):
                st.markdown(f"{i}. {step}")
            
            st.markdown(f"**Note:** {content['note']}")
            st.markdown(f"**Pricing:** {content['pricing']}")
            st.markdown(f"[Get your API key here]({content['link']})")

4. Implementation Plan

Phase 1: Core Improvements

  1. Create Unified State Manager

    • Implement the OnboardingStateManager class
    • Refactor existing code to use the new state manager
    • Add proper state persistence
  2. Enhance API Key Validation

    • Implement real validation for each service
    • Add visual feedback for validation status
    • Create a "Test All Keys" function
  3. Improve Navigation

    • Redesign step indicator with clear labels
    • Add skip options for optional steps
    • Implement a "Quick Start" mode

Phase 2: UI/UX Enhancements

  1. Redesign Input Components

    • Create clear visual hierarchy
    • Add responsive design for mobile
    • Implement interactive help sections
  2. Create Summary Screens

    • Add welcome screen with setup options
    • Implement completion summary screen
    • Add configuration export/import
  3. Enhance Visual Design

    • Update color scheme for better accessibility
    • Add animations for transitions
    • Implement progress indicators

Phase 3: Advanced Features

  1. Guided Tours

    • Add interactive tutorials
    • Create contextual help popups
    • Implement feature discovery
  2. Smart Defaults

    • Suggest configurations based on user needs
    • Implement templates for common use cases
    • Add recommended settings
  3. Troubleshooting Assistance

    • Add automatic error detection
    • Create guided troubleshooting flows
    • Implement self-healing for common issues

Code Implementation Examples

Welcome Screen Component

def render_welcome_screen():
    """Render the welcome screen for onboarding."""
    st.markdown("""
        <div class="welcome-container">
            <h1>Welcome to AI-Writer! 👋</h1>
            <p class="welcome-subtitle">Let's get you set up in just a few minutes.</p>
        </div>
    """, unsafe_allow_html=True)
    
    # Setup mode selection
    setup_mode = st.radio(
        "What would you like to do?",
        options=["Quick Start (minimal setup)", 
                 "Complete Setup (all features)",
                 "Import Configuration"],
        index=0,
        key="setup_mode_selection"
    )
    
    # Store the selection in state
    if "onboarding_state" in st.session_state:
        st.session_state.onboarding_state["setup_mode"] = setup_mode.split(" ")[0].lower()
    
    # Start button
    if st.button("Start Setup", use_container_width=True, type="primary"):
        if "onboarding_state" in st.session_state:
            st.session_state.onboarding_state["current_step"] = 1
            st.rerun()

API Key Manager Component

def render_api_key_manager():
    """Render the improved API key manager."""
    # Get state manager
    state_manager = OnboardingStateManager()
    state = state_manager.get_state()
    
    # Render step indicator
    render_step_indicator(state["current_step"], state["total_steps"])
    
    # Render appropriate step based on current_step
    if state["current_step"] == 1:
        render_ai_providers_step(state_manager)
    elif state["current_step"] == 2:
        render_research_tools_step(state_manager)
    elif state["current_step"] == 3:
        render_publishing_step(state_manager)
    elif state["current_step"] == 4:
        render_summary_step(state_manager)
    
    # Render navigation buttons
    render_navigation_buttons(state_manager)

Improved AI Providers Step

def render_ai_providers_step(state_manager):
    """Render the improved AI providers setup step."""
    st.markdown("## Step 1: Connect AI Models")
    st.markdown("Configure the AI models you want to use for content generation.")
    
    # Create tabs for required vs optional
    tab1, tab2 = st.tabs(["Required (at least one)", "Optional Models"])
    
    with tab1:
        col1, col2 = st.columns(2)
        
        with col1:
            # OpenAI
            openai_key = render_api_key_input(
                "OpenAI API Key", 
                "openai", 
                required=True,
                help_text="Powers GPT-3.5 and GPT-4 models"
            )
            render_help_section("openai")
        
        with col2:
            # Google Gemini
            gemini_key = render_api_key_input(
                "Google Gemini API Key", 
                "gemini", 
                required=False,
                help_text="Powers Gemini Pro models"
            )
            render_help_section("gemini")
    
    with tab2:
        col1, col2 = st.columns(2)
        
        with col1:
            # Anthropic
            anthropic_key = render_api_key_input(
                "Anthropic API Key", 
                "anthropic", 
                required=False,
                help_text="Powers Claude models"
            )
            render_help_section("anthropic")
        
        with col2:
            # Mistral
            mistral_key = render_api_key_input(
                "Mistral API Key", 
                "mistral", 
                required=False,
                help_text="Powers Mistral models"
            )
            render_help_section("mistral")
    
    # Test keys button
    if st.button("Test API Keys", use_container_width=True):
        with st.spinner("Testing API keys..."):
            # Test each provided key
            results = {}
            if openai_key:
                results["openai"] = asyncio.run(validate_api_key("openai", openai_key))
            if gemini_key:
                results["gemini"] = asyncio.run(validate_api_key("gemini", gemini_key))
            if anthropic_key:
                results["anthropic"] = asyncio.run(validate_api_key("anthropic", anthropic_key))
            if mistral_key:
                results["mistral"] = asyncio.run(validate_api_key("mistral", mistral_key))
            
            # Store validation results
            state_manager.update_state({"validated_keys": results})
            
            # Display results
            for service, result in results.items():
                if result.get("valid", False):
                    st.success(f"✅ {service.title()} API key is valid")
                else:
                    st.error(f"❌ {service.title()} API key is invalid: {result.get('error', 'Unknown error')}")
    
    # Save keys to state
    api_keys = {
        "openai": openai_key,
        "gemini": gemini_key,
        "anthropic": anthropic_key,
        "mistral": mistral_key
    }
    state_manager.update_state({"api_keys": api_keys})
    
    # Check if we have at least one valid key
    has_valid_key = any([
        openai_key and state_manager.get_state().get("validated_keys", {}).get("openai", {}).get("valid", False),
        gemini_key and state_manager.get_state().get("validated_keys", {}).get("gemini", {}).get("valid", False)
    ])
    
    if not has_valid_key and (openai_key or gemini_key):
        st.warning("Please test your API keys before continuing")

Benefits of Improved Onboarding

  1. Increased User Retention

    • Smoother onboarding leads to higher completion rates
    • Clear guidance reduces frustration and abandonment
    • Faster time-to-value improves user satisfaction
  2. Reduced Support Burden

    • Better self-service options decrease support tickets
    • Clearer instructions prevent common setup issues
    • Automated validation catches problems early
  3. Higher Feature Adoption

    • Users understand available features better
    • Guided setup encourages exploration of capabilities
    • Contextual help improves feature discovery
  4. Improved User Experience

    • Consistent design creates a professional impression
    • Responsive layout works across all devices
    • Intuitive navigation reduces cognitive load
  5. Better Data Quality

    • Proper validation ensures working API keys
    • Clear requirements improve data completeness
    • Structured setup leads to better configuration

Implementation Timeline

  • Week 1: Design and prototype core improvements
  • Week 2: Implement unified state management and API validation
  • Week 3: Develop UI components and responsive design
  • Week 4: Create welcome and summary screens
  • Week 5: Add help content and contextual assistance
  • Week 6: Testing, refinement, and documentation

Conclusion

The proposed improvements to the onboarding process will significantly enhance the user experience for new AI-Writer users. By implementing a more intuitive, guided, and responsive setup flow, we can increase user retention, reduce support needs, and help users get value from the platform faster.

These changes represent a comprehensive overhaul of the current system, addressing both technical and user experience issues while maintaining compatibility with the existing codebase.