diff --git a/lib/utils/api_key_manager/components/final_setup.py b/lib/utils/api_key_manager/components/final_setup.py
index 6f7ea881..eba0256c 100644
--- a/lib/utils/api_key_manager/components/final_setup.py
+++ b/lib/utils/api_key_manager/components/final_setup.py
@@ -192,6 +192,49 @@ def render_final_setup(api_key_manager: APIKeyManager) -> Dict[str, Any]:
if st.button("Complete Setup →"):
logger.info("[render_final_setup] User clicked complete setup")
try:
+ # First set FINAL_SETUP_COMPLETE to True
+ try:
+ # Read existing .env content
+ env_lines = []
+ if os.path.exists('.env'):
+ with open('.env', 'r') as f:
+ env_lines = f.readlines()
+
+ # Remove any existing FINAL_SETUP_COMPLETE entries
+ env_lines = [line for line in env_lines if not line.startswith('FINAL_SETUP_COMPLETE=')]
+
+ # Add the new FINAL_SETUP_COMPLETE entry
+ env_lines.append("FINAL_SETUP_COMPLETE=True\n")
+
+ # Write back to .env file
+ with open('.env', 'w') as f:
+ f.writelines(env_lines)
+
+ # Set environment variable
+ os.environ['FINAL_SETUP_COMPLETE'] = "True"
+ logger.info("[render_final_setup] Set FINAL_SETUP_COMPLETE=True")
+ except Exception as e:
+ logger.error(f"[render_final_setup] Error setting FINAL_SETUP_COMPLETE: {str(e)}")
+ st.error("Error updating setup status. Please try again.")
+ return {"current_step": 6, "changes_made": False}
+
+ # Now validate all steps
+ validation_result = check_all_api_keys(api_key_manager)
+ if not validation_result:
+ # If validation fails, revert FINAL_SETUP_COMPLETE
+ try:
+ env_lines = [line for line in env_lines if not line.startswith('FINAL_SETUP_COMPLETE=')]
+ env_lines.append("FINAL_SETUP_COMPLETE=False\n")
+ with open('.env', 'w') as f:
+ f.writelines(env_lines)
+ os.environ['FINAL_SETUP_COMPLETE'] = "False"
+ except Exception:
+ pass # Ignore reversion errors
+
+ st.error("Setup validation failed. Please ensure all required steps are completed.")
+ logger.error("[render_final_setup] Validation failed")
+ return {"current_step": 6, "changes_made": False}
+
# Log the current API keys in the manager
logger.info("[render_final_setup] Current API keys in manager:")
for key, value in api_key_manager.api_keys.items():
@@ -200,46 +243,30 @@ def render_final_setup(api_key_manager: APIKeyManager) -> Dict[str, Any]:
else:
logger.info(f" - {key}: Not set")
- # Log environment variables
- logger.info("[render_final_setup] Checking environment variables:")
- for key in os.environ.keys():
- if any(provider in key for provider in ['API_KEY', 'SERPAPI', 'TAVILY', 'METAPHOR', 'FIRECRAWL']):
- value = os.getenv(key)
- if value:
- logger.info(f" - {key}: {'*' * 8}{value[-4:]}")
- else:
- logger.error(f" - {key}: Not set")
-
+ # Save main configuration
config_path = os.path.join("lib", "workspace", "alwrity_config", "main_config.json")
with open(config_path, 'w') as f:
json.dump(main_config, f, indent=4)
+ logger.info("[render_final_setup] Saved main configuration")
- # Set FINAL_SETUP_COMPLETE to True in .env file and environment
- try:
- with open('.env', 'a') as f:
- f.write("\nFINAL_SETUP_COMPLETE=True")
- os.environ['FINAL_SETUP_COMPLETE'] = "True"
- logger.info("Set FINAL_SETUP_COMPLETE=True in .env and environment")
- except Exception as e:
- logger.error(f"Failed to set FINAL_SETUP_COMPLETE: {str(e)}")
-
- # Show success message with HTML formatting
- st.markdown("""
-
- """, unsafe_allow_html=True)
-
- # Create two columns for settings and explanations (1:2 ratio)
- settings_col, info_col = st.columns([1, 2])
-
- with settings_col:
- st.markdown("""
-
- """, unsafe_allow_html=True)
+ # Blog Content Characteristics Tab
+ with tabs[0]:
+ col1, col2 = st.columns([1, 1])
- # Blog Content Characteristics
- st.markdown("#### Blog Content Characteristics")
- blog_settings = main_config.get("Blog Content Characteristics", {})
+ with col1:
+ st.markdown("#### Blog Content Characteristics")
+
+ blog_length = st.text_input(
+ "Blog Length",
+ value="2000",
+ placeholder="e.g., 2000",
+ help="Target word count for your blog posts"
+ )
+
+ blog_tone = st.selectbox(
+ "Blog Tone",
+ ["Professional", "Casual", "Technical", "Conversational"],
+ help="The overall tone of your content"
+ )
+
+ blog_demographic = st.selectbox(
+ "Target Demographic",
+ ["Professional", "General", "Technical", "Academic"],
+ help="Your primary audience demographic"
+ )
+
+ blog_type = st.selectbox(
+ "Content Type",
+ ["Informational", "Educational", "Entertainment", "Technical"],
+ help="The primary type of content you create"
+ )
+
+ blog_language = st.selectbox(
+ "Content Language",
+ ["English", "Spanish", "French", "German", "Other"],
+ help="Primary language for your content"
+ )
+
+ blog_format = st.selectbox(
+ "Output Format",
+ ["markdown", "html", "plain text"],
+ help="Format of the generated content"
+ )
- blog_length = st.text_input(
- "Blog Length",
- value=blog_settings.get("Blog Length", "2000"),
- placeholder="e.g., 2000",
- help="Target word count for your blog posts"
- )
-
- blog_tone = st.selectbox(
- "Blog Tone",
- options=["Professional", "Casual", "Technical", "Conversational"],
- index=["Professional", "Casual", "Technical", "Conversational"].index(blog_settings.get("Blog Tone", "Professional")),
- help="The overall tone of your content"
- )
-
- blog_demographic = st.selectbox(
- "Target Demographic",
- options=["Professional", "General", "Technical", "Academic"],
- index=["Professional", "General", "Technical", "Academic"].index(blog_settings.get("Blog Demographic", "Professional")),
- help="Your primary audience demographic"
- )
-
- blog_type = st.selectbox(
- "Content Type",
- options=["Informational", "Educational", "Entertainment", "Technical"],
- index=["Informational", "Educational", "Entertainment", "Technical"].index(blog_settings.get("Blog Type", "Informational")),
- help="The primary type of content you create"
- )
-
- blog_language = st.selectbox(
- "Content Language",
- options=["English", "Spanish", "French", "German", "Other"],
- index=["English", "Spanish", "French", "German", "Other"].index(blog_settings.get("Blog Language", "English")),
- help="Primary language for your content"
- )
-
- blog_format = st.selectbox(
- "Output Format",
- options=["markdown", "html", "plain text"],
- index=["markdown", "html", "plain text"].index(blog_settings.get("Blog Output Format", "markdown")),
- help="Format of the generated content"
- )
-
- # Blog Images Details
- st.markdown("#### Blog Images")
- image_settings = main_config.get("Blog Images Details", {})
-
- image_model = st.selectbox(
- "Image Generation Model",
- options=["stable-diffusion", "dall-e", "midjourney"],
- index=["stable-diffusion", "dall-e", "midjourney"].index(image_settings.get("Image Generation Model", "stable-diffusion")),
- help="AI model for generating images"
- )
-
- num_images = st.number_input(
- "Number of Images",
- min_value=1,
- max_value=5,
- value=image_settings.get("Number of Blog Images", 1),
- help="Number of images to generate per blog post"
- )
-
- # LLM Options
- st.markdown("#### AI Generation Settings")
- llm_settings = main_config.get("LLM Options", {})
-
- gpt_provider = st.selectbox(
- "AI Provider",
- options=["google", "openai", "anthropic"],
- index=["google", "openai", "anthropic"].index(llm_settings.get("GPT Provider", "google")),
- help="Choose your preferred AI provider"
- )
-
- model = st.text_input(
- "Model",
- value=llm_settings.get("Model", "gemini-1.5-flash-latest"),
- placeholder="e.g., gemini-1.5-flash-latest",
- help="The specific AI model to use"
- )
-
- temperature = st.slider(
- "Creativity Level",
- min_value=0.0,
- max_value=1.0,
- value=float(llm_settings.get("Temperature", 0.7)),
- help="Higher values = more creative, lower values = more focused"
- )
-
- top_p = st.slider(
- "Output Diversity",
- min_value=0.0,
- max_value=1.0,
- value=float(llm_settings.get("Top-p", 0.9)),
- help="Controls diversity of generated content"
- )
-
- max_tokens = st.number_input(
- "Maximum Length",
- min_value=100,
- max_value=8000,
- value=int(llm_settings.get("Max Tokens", 4000)),
- help="Maximum length of generated content"
- )
-
- frequency_penalty = st.slider(
- "Frequency Penalty",
- min_value=-2.0,
- max_value=2.0,
- value=float(llm_settings.get("Frequency Penalty", 1.0)),
- help="Reduces repetition of the same words"
- )
-
- presence_penalty = st.slider(
- "Presence Penalty",
- min_value=-2.0,
- max_value=2.0,
- value=float(llm_settings.get("Presence Penalty", 1.0)),
- help="Encourages discussion of new topics"
- )
-
- # Search Engine Parameters
- st.markdown("#### Search Settings")
- search_settings = main_config.get("Search Engine Parameters", {})
-
- geo_location = st.text_input(
- "Geographic Location",
- value=search_settings.get("Geographic Location", "us"),
- placeholder="e.g., us, uk, ca",
- help="Target geographic location for search results"
- )
-
- search_language = st.selectbox(
- "Search Language",
- options=["en", "es", "fr", "de", "other"],
- index=["en", "es", "fr", "de", "other"].index(search_settings.get("Search Language", "en")),
- help="Language for search results"
- )
-
- num_results = st.number_input(
- "Number of Results",
- min_value=1,
- max_value=50,
- value=search_settings.get("Number of Results", 10),
- help="Number of search results to analyze"
- )
-
- time_range = st.selectbox(
- "Time Range",
- options=["anytime", "day", "week", "month", "year"],
- index=["anytime", "day", "week", "month", "year"].index(search_settings.get("Time Range", "anytime")),
- help="Time range for search results"
- )
-
- st.markdown("
", unsafe_allow_html=True)
-
- with info_col:
- st.markdown("""
-
- """, unsafe_allow_html=True)
-
- st.markdown("""
-
- ### Understanding Your Settings
-
- #### Blog Content Settings
-
- **Blog Length**
- - Determines the target word count for your posts
- - Affects content depth and detail level
- - Impacts reader engagement and SEO performance
- - Recommended: 1500-2500 words for comprehensive coverage
-
- **Blog Tone**
- - Professional: Formal, business-oriented, authoritative
- - Casual: Friendly, conversational, approachable
- - Technical: Detailed, precise, industry-specific
- - Conversational: Engaging, relatable, personal
-
- **Target Demographic**
- - Professional: Business audience, decision-makers
- - General: Broad readership, general public
- - Technical: Specialized audience, industry experts
- - Academic: Research-focused, scholarly readers
-
- **Content Type**
- - Informational: Facts, insights, and analysis
- - Educational: Teaching, tutorials, how-to guides
- - Entertainment: Engaging, fun, light content
- - Technical: Detailed analysis, specifications
-
- **Content Language**
- - Select your primary content language
- - Affects grammar, idioms, and cultural context
- - Impacts SEO and audience reach
-
- **Output Format**
- - Markdown: Best for most platforms
- - HTML: For web publishing
- - Plain Text: For simple content
-
- #### Image Generation Settings
-
- **Image Generation Model**
- - Stable Diffusion: Best for general content
- - DALL-E: Great for creative concepts
- - Midjourney: Excellent for artistic content
-
- **Number of Images**
- - Consider your content type and platform
- - More images = better engagement but higher cost
- - Recommended: 1-2 images per post
-
- #### AI Generation Settings
-
- **AI Provider**
- - Google: Balanced, reliable, cost-effective
- - OpenAI: Creative, nuanced, versatile
- - Anthropic: Precise, ethical, focused
-
- **Model Selection**
- - Latest models offer best performance
- - Specialized models for specific needs
- - Consider cost vs. quality trade-offs
-
- **Creativity Level (Temperature)**
- - 0.0: Focused, consistent, predictable
- - 0.5: Balanced creativity and coherence
- - 1.0: Maximum creativity, more varied
-
- **Output Diversity (Top-p)**
- - Controls variety in word choices
- - Higher values = more diverse vocabulary
- - Lower values = more focused terminology
-
- **Maximum Length**
- - Affects content completeness
- - Consider platform limits
- - Balance detail vs. readability
-
- **Frequency & Presence Penalties**
- - Reduce repetition of words
- - Encourage topic diversity
- - Fine-tune content variety
-
- #### Search Settings
-
- **Geographic Location**
- - Target specific regions
- - Affects local SEO
- - Influences content relevance
-
- **Search Language**
- - Match your content language
- - Affects result relevance
+ with col2:
+ st.markdown("### Blog Content Settings Guide")
+
+ st.markdown("""
+ #### Blog Length
+ - Determines word count target
+ - Affects content depth
- Impacts SEO performance
- **Number of Results**
- - More results = better analysis
- - Consider processing time
- - Balance quality vs. speed
+ #### Blog Tone
+ - Professional: Business-oriented
+ - Casual: Friendly, approachable
+ - Technical: Detailed, precise
- **Time Range**
- - Anytime: All available content
- - Recent: Latest information
- - Historical: Past content
-
- ### Best Practices
-
- 1. **Start Conservative**
- - Begin with moderate settings
- - Adjust based on results
- - Monitor performance
-
- 2. **Consider Your Audience**
- - Match tone to reader expectations
- - Adjust complexity appropriately
- - Focus on value delivery
-
- 3. **Optimize for Platform**
- - Consider platform limitations
- - Match format requirements
- - Optimize for engagement
-
- 4. **Regular Review**
- - Monitor content performance
- - Adjust settings as needed
- - Stay updated with trends
-
- """, unsafe_allow_html=True)
-
- st.markdown("
", unsafe_allow_html=True)
+ #### Best Practices
+ - Match tone to audience
+ - Consider SEO requirements
+ - Maintain consistency
+ """)
- # Close the container
- st.markdown("
", unsafe_allow_html=True)
-
- # Add some spacing before the save button
- st.markdown("