diff --git a/lib/utils/api_key_manager/components/ai_providers.py b/lib/utils/api_key_manager/components/ai_providers.py index 1bc3d92e..441ef2f5 100644 --- a/lib/utils/api_key_manager/components/ai_providers.py +++ b/lib/utils/api_key_manager/components/ai_providers.py @@ -1,32 +1,245 @@ -"""AI providers setup component - Wrapper for the actual setup UI.""" +"""AI providers setup component.""" import streamlit as st from loguru import logger from typing import Dict, Any from ..manager import APIKeyManager -from .ai_providers_setup import render_ai_providers_setup # Import the refactored setup UI +from .base import render_navigation_buttons, render_step_indicator, render_tab_style +from ..wizard_state import next_step, update_progress +from datetime import datetime + +def validate_api_key(key: str) -> bool: + """Validate if an API key is properly formatted.""" + if not key: + return False + # Basic validation - check if key is not empty and has minimum length + return len(key.strip()) > 0 def render_ai_providers(api_key_manager: APIKeyManager) -> Dict[str, Any]: - """Renders the AI providers setup step by calling the dedicated setup function.""" - logger.debug("[render_ai_providers] Calling render_ai_providers_setup") + """Render the AI providers setup step.""" + logger.info("[render_ai_providers] Starting AI providers setup") try: - # The actual UI, saving, validation, and feedback are now handled within render_ai_providers_setup - # This function acts primarily as a placeholder in the step sequence if needed, - # or can be bypassed entirely if the main wizard calls render_ai_providers_setup directly. + # Initialize wizard state if not already initialized + if 'wizard_state' not in st.session_state: + st.session_state.wizard_state = { + 'current_step': 1, + 'total_steps': 6, + 'progress': 0, + 'completed_steps': set(), + 'last_updated': datetime.now() + } + logger.info("[render_ai_providers] Initialized wizard state") - # Store the manager instance if needed by other potential logic (unlikely now) - if 'api_key_manager' not in st.session_state: - st.session_state['api_key_manager'] = api_key_manager - - # Call the function that now contains all the rendering and logic for this step - component_state = render_ai_providers_setup(api_key_manager) + # Store API key manager in session state for update_progress + st.session_state['api_key_manager'] = api_key_manager + + # Main content + st.markdown(""" +
+

🤖 AI Providers Setup

+

Configure your AI service providers for content generation

+
+ """, unsafe_allow_html=True) + + # Create tabs for different AI providers + tabs = st.tabs(["Primary Providers", "Additional Providers"]) + + # Track if any changes were made + changes_made = False + has_valid_key = False + validation_message = "" + + with tabs[0]: + st.markdown("### Primary AI Providers") + st.markdown("Configure the main AI providers for content creation") + + # Create a grid layout for AI provider cards + col1, col2 = st.columns(2) + + with col1: + # OpenAI Card + with st.container(): + st.markdown(""" +
+
+
🤖
+
OpenAI
+
+
+

Power your content with GPT-4 and GPT-3.5 models

+
+ """, unsafe_allow_html=True) + + openai_key = st.text_input( + "OpenAI API Key", + type="password", + key="openai_key", + help="Enter your OpenAI API key" + ) + + if openai_key: + if validate_api_key(openai_key): + st.markdown(""" +
+ ✓ API key configured +
+ """, unsafe_allow_html=True) + else: + st.markdown(""" +
+ ⚠️ Invalid API key format +
+ """, unsafe_allow_html=True) + + with st.expander("📋 How to get your OpenAI API key", expanded=False): + st.markdown(""" + **Step-by-step guide:** + 1. Go to [OpenAI's website](https://platform.openai.com) + 2. Sign up or log in to your account + 3. Navigate to the API section + 4. Click "Create new secret key" + 5. Copy the generated key and paste it here + + **Note:** Keep your API key secure and never share it publicly. + """) + + st.markdown("
", unsafe_allow_html=True) + + with col2: + # Google Card + with st.container(): + st.markdown(""" +
+
+
🔍
+
Google Gemini
+
+
+

Leverage Google's powerful Gemini models

+
+ """, unsafe_allow_html=True) + + google_key = st.text_input( + "Google API Key", + type="password", + key="google_key", + help="Enter your Google API key" + ) + + if google_key: + if validate_api_key(google_key): + st.markdown(""" +
+ ✓ API key configured +
+ """, unsafe_allow_html=True) + else: + st.markdown(""" +
+ ⚠️ Invalid API key format +
+ """, unsafe_allow_html=True) + + with st.expander("📋 How to get your Google API key", expanded=False): + st.markdown(""" + **Step-by-step guide:** + 1. Visit [Google AI Studio](https://makersuite.google.com/app/apikey) + 2. Sign in with your Google account + 3. Click "Create API key" + 4. Copy the generated key and paste it here + + **Note:** Make sure to enable the Gemini API in your Google Cloud Console. + """) + + st.markdown("
", unsafe_allow_html=True) + + with tabs[1]: + st.markdown("### Additional AI Providers") + st.markdown("Configure additional AI providers for enhanced capabilities") + + # Create a grid layout for additional provider cards + col1, col2 = st.columns(2) + + with col1: + # Anthropic Card (Coming Soon) + with st.container(): + st.markdown(""" +
+
+
🧠
+
Anthropic Coming Soon
+
+
+

Access Claude for advanced content generation

+
+
+ """, unsafe_allow_html=True) + st.info("Anthropic integration will be available in the next update") + + with col2: + # Mistral Card (Coming Soon) + with st.container(): + st.markdown(""" +
+
+
+
Mistral Coming Soon
+
+
+

Use Mistral's efficient language models

+
+
+ """, unsafe_allow_html=True) + st.info("Mistral integration will be available in the next update") + + # Track changes and validate keys + if any([openai_key, google_key]): + changes_made = True + # Check if at least one valid API key is provided + if validate_api_key(openai_key) or validate_api_key(google_key): + has_valid_key = True + validation_message = "✅ At least one AI provider configured successfully" + else: + validation_message = "⚠️ Please provide at least one valid API key" + else: + validation_message = "⚠️ Please configure at least one AI provider to continue" + + # Display validation message + if validation_message: + if "✅" in validation_message: + st.success(validation_message) + else: + st.warning(validation_message) + + # Navigation buttons + if render_navigation_buttons(1, 6, changes_made): + if has_valid_key: + # Store the API keys in a separate session state key + st.session_state['api_keys'] = { + 'openai': openai_key if validate_api_key(openai_key) else None, + 'google': google_key if validate_api_key(google_key) else None + } + + # Save API keys to .env file + if validate_api_key(openai_key): + api_key_manager.save_api_key("openai", openai_key) + logger.info("[render_ai_providers] OpenAI API key saved to .env file") + + if validate_api_key(google_key): + api_key_manager.save_api_key("gemini", google_key) + logger.info("[render_ai_providers] Google Gemini API key saved to .env file") + + # Update progress and move to next step + st.session_state['current_step'] = 2 # Set the next step explicitly + update_progress() + st.rerun() # Rerun to apply the changes + else: + st.error("Please configure at least one valid AI provider to continue") + + return {"current_step": 1, "changes_made": changes_made} - # Return the state from the setup function, although it might not be used directly - return component_state - except Exception as e: - error_msg = f"Error calling AI providers setup: {str(e)}" - logger.error(f"[render_ai_providers] {error_msg}", exc_info=True) - st.error("An error occurred while setting up AI providers.") - # Ensure consistency in error return format if expected by the caller - return {"error": error_msg} + error_msg = f"Error in AI providers setup: {str(e)}" + logger.error(f"[render_ai_providers] {error_msg}") + st.error(error_msg) + return {"current_step": 1, "error": error_msg} \ No newline at end of file