Fix environment variable handling in setup: PERSONALIZATION_DONE and FINAL_SETUP_COMPLETE
This commit is contained in:
@@ -192,6 +192,49 @@ def render_final_setup(api_key_manager: APIKeyManager) -> Dict[str, Any]:
|
|||||||
if st.button("Complete Setup →"):
|
if st.button("Complete Setup →"):
|
||||||
logger.info("[render_final_setup] User clicked complete setup")
|
logger.info("[render_final_setup] User clicked complete setup")
|
||||||
try:
|
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
|
# Log the current API keys in the manager
|
||||||
logger.info("[render_final_setup] Current API keys in manager:")
|
logger.info("[render_final_setup] Current API keys in manager:")
|
||||||
for key, value in api_key_manager.api_keys.items():
|
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:
|
else:
|
||||||
logger.info(f" - {key}: Not set")
|
logger.info(f" - {key}: Not set")
|
||||||
|
|
||||||
# Log environment variables
|
# Save main configuration
|
||||||
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")
|
|
||||||
|
|
||||||
config_path = os.path.join("lib", "workspace", "alwrity_config", "main_config.json")
|
config_path = os.path.join("lib", "workspace", "alwrity_config", "main_config.json")
|
||||||
with open(config_path, 'w') as f:
|
with open(config_path, 'w') as f:
|
||||||
json.dump(main_config, f, indent=4)
|
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
|
# Show success message
|
||||||
try:
|
st.success("✅ Setup completed successfully! Redirecting to main application...")
|
||||||
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("""
|
|
||||||
<div style='background-color: #d4edda; color: #155724; padding: 1rem; border-radius: 0.25rem;'>
|
|
||||||
<h4 style='margin: 0;'>✅ Setup Completed Successfully!</h4>
|
|
||||||
<p style='margin: 0.5rem 0 0;'>Your configuration has been saved and you're ready to use ALwrity.</p>
|
|
||||||
</div>
|
|
||||||
""", unsafe_allow_html=True)
|
|
||||||
|
|
||||||
# Set setup completion flag in session state
|
# Set setup completion flag in session state
|
||||||
st.session_state['setup_completed'] = True
|
st.session_state['setup_completed'] = True
|
||||||
|
st.session_state['redirect_to_main'] = True
|
||||||
|
|
||||||
# Redirect to main application
|
# Clear the current step to ensure proper redirection
|
||||||
|
if 'current_step' in st.session_state:
|
||||||
|
del st.session_state['current_step']
|
||||||
|
|
||||||
|
# Rerun to trigger redirection
|
||||||
st.rerun()
|
st.rerun()
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
error_msg = f"Error completing setup: {str(e)}"
|
error_msg = f"Error completing setup: {str(e)}"
|
||||||
logger.error(f"[render_final_setup] {error_msg}")
|
logger.error(f"[render_final_setup] {error_msg}")
|
||||||
st.error(error_msg)
|
st.error(error_msg)
|
||||||
|
return {"current_step": 6, "changes_made": False}
|
||||||
|
|
||||||
return {"current_step": 6, "changes_made": True}
|
return {"current_step": 6, "changes_made": True}
|
||||||
@@ -122,610 +122,366 @@ def render_personalization_setup(api_key_manager: APIKeyManager) -> Dict[str, An
|
|||||||
logger.info("[render_personalization_setup] Rendering personalization setup component")
|
logger.info("[render_personalization_setup] Rendering personalization setup component")
|
||||||
|
|
||||||
st.markdown("""
|
st.markdown("""
|
||||||
<div class='setup-header'>
|
# ✨ Personalization Setup
|
||||||
<h2>✨ Personalization Setup</h2>
|
Configure your content generation preferences and writing style
|
||||||
<p>Configure your content generation preferences and writing style</p>
|
""")
|
||||||
</div>
|
|
||||||
""", unsafe_allow_html=True)
|
|
||||||
|
|
||||||
# Load main config
|
# Main section selection using radio buttons
|
||||||
main_config = load_main_config()
|
setup_mode = st.radio(
|
||||||
|
"Choose Setup Mode",
|
||||||
|
["Manual Settings", "ALwrity Personalization"],
|
||||||
|
horizontal=True,
|
||||||
|
label_visibility="collapsed"
|
||||||
|
)
|
||||||
|
|
||||||
# Create tabs for different personalization methods
|
if setup_mode == "Manual Settings":
|
||||||
tab1, tab2 = st.tabs([
|
# Create tabs for different settings categories
|
||||||
"Manual Settings",
|
tabs = st.tabs([
|
||||||
"ALwrity Personalization"
|
"Blog Content Characteristics",
|
||||||
])
|
"Blog Images",
|
||||||
|
"AI Generation Settings",
|
||||||
|
"Search Settings"
|
||||||
|
])
|
||||||
|
|
||||||
with tab1:
|
# Blog Content Characteristics Tab
|
||||||
st.markdown("### Manual Settings Configuration")
|
with tabs[0]:
|
||||||
|
col1, col2 = st.columns([1, 1])
|
||||||
|
|
||||||
# Add container for better width control
|
with col1:
|
||||||
st.markdown("""
|
st.markdown("#### Blog Content Characteristics")
|
||||||
<div style='width: 100%; max-width: 100%; margin: 0; padding: 0;'>
|
|
||||||
""", unsafe_allow_html=True)
|
|
||||||
|
|
||||||
# Create two columns for settings and explanations (1:2 ratio)
|
blog_length = st.text_input(
|
||||||
settings_col, info_col = st.columns([1, 2])
|
"Blog Length",
|
||||||
|
value="2000",
|
||||||
|
placeholder="e.g., 2000",
|
||||||
|
help="Target word count for your blog posts"
|
||||||
|
)
|
||||||
|
|
||||||
with settings_col:
|
blog_tone = st.selectbox(
|
||||||
st.markdown("""
|
"Blog Tone",
|
||||||
<div style='padding-right: 2rem;'>
|
["Professional", "Casual", "Technical", "Conversational"],
|
||||||
""", unsafe_allow_html=True)
|
help="The overall tone of your content"
|
||||||
|
)
|
||||||
|
|
||||||
# Blog Content Characteristics
|
blog_demographic = st.selectbox(
|
||||||
st.markdown("#### Blog Content Characteristics")
|
"Target Demographic",
|
||||||
blog_settings = main_config.get("Blog Content Characteristics", {})
|
["Professional", "General", "Technical", "Academic"],
|
||||||
|
help="Your primary audience demographic"
|
||||||
|
)
|
||||||
|
|
||||||
blog_length = st.text_input(
|
blog_type = st.selectbox(
|
||||||
"Blog Length",
|
"Content Type",
|
||||||
value=blog_settings.get("Blog Length", "2000"),
|
["Informational", "Educational", "Entertainment", "Technical"],
|
||||||
placeholder="e.g., 2000",
|
help="The primary type of content you create"
|
||||||
help="Target word count for your blog posts"
|
)
|
||||||
)
|
|
||||||
|
|
||||||
blog_tone = st.selectbox(
|
blog_language = st.selectbox(
|
||||||
"Blog Tone",
|
"Content Language",
|
||||||
options=["Professional", "Casual", "Technical", "Conversational"],
|
["English", "Spanish", "French", "German", "Other"],
|
||||||
index=["Professional", "Casual", "Technical", "Conversational"].index(blog_settings.get("Blog Tone", "Professional")),
|
help="Primary language for your content"
|
||||||
help="The overall tone of your content"
|
)
|
||||||
)
|
|
||||||
|
|
||||||
blog_demographic = st.selectbox(
|
blog_format = st.selectbox(
|
||||||
"Target Demographic",
|
"Output Format",
|
||||||
options=["Professional", "General", "Technical", "Academic"],
|
["markdown", "html", "plain text"],
|
||||||
index=["Professional", "General", "Technical", "Academic"].index(blog_settings.get("Blog Demographic", "Professional")),
|
help="Format of the generated content"
|
||||||
help="Your primary audience demographic"
|
)
|
||||||
)
|
|
||||||
|
|
||||||
blog_type = st.selectbox(
|
with col2:
|
||||||
"Content Type",
|
st.markdown("### Blog Content Settings Guide")
|
||||||
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(
|
st.markdown("""
|
||||||
"Content Language",
|
#### Blog Length
|
||||||
options=["English", "Spanish", "French", "German", "Other"],
|
- Determines word count target
|
||||||
index=["English", "Spanish", "French", "German", "Other"].index(blog_settings.get("Blog Language", "English")),
|
- Affects content depth
|
||||||
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("</div>", unsafe_allow_html=True)
|
|
||||||
|
|
||||||
with info_col:
|
|
||||||
st.markdown("""
|
|
||||||
<div style='
|
|
||||||
padding-left: 2rem;
|
|
||||||
border-left: 2px solid #e0e0e0;
|
|
||||||
background-color: #f8f9fa;
|
|
||||||
border-radius: 0 8px 8px 0;
|
|
||||||
margin: -1rem 0;
|
|
||||||
padding-top: 1rem;
|
|
||||||
padding-bottom: 1rem;
|
|
||||||
'>
|
|
||||||
""", unsafe_allow_html=True)
|
|
||||||
|
|
||||||
st.markdown("""
|
|
||||||
<div style='padding: 0 1rem;'>
|
|
||||||
### 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
|
|
||||||
- Impacts SEO performance
|
- Impacts SEO performance
|
||||||
|
|
||||||
**Number of Results**
|
#### Blog Tone
|
||||||
- More results = better analysis
|
- Professional: Business-oriented
|
||||||
- Consider processing time
|
- Casual: Friendly, approachable
|
||||||
|
- Technical: Detailed, precise
|
||||||
|
|
||||||
|
#### Best Practices
|
||||||
|
- Match tone to audience
|
||||||
|
- Consider SEO requirements
|
||||||
|
- Maintain consistency
|
||||||
|
""")
|
||||||
|
|
||||||
|
# Blog Images Tab
|
||||||
|
with tabs[1]:
|
||||||
|
col1, col2 = st.columns([1, 1])
|
||||||
|
|
||||||
|
with col1:
|
||||||
|
st.markdown("#### Blog Images Settings")
|
||||||
|
|
||||||
|
image_model = st.selectbox(
|
||||||
|
"Image Generation Model",
|
||||||
|
["stable-diffusion", "dall-e", "midjourney"],
|
||||||
|
help="AI model for generating images"
|
||||||
|
)
|
||||||
|
|
||||||
|
num_images = st.number_input(
|
||||||
|
"Number of Images",
|
||||||
|
min_value=1,
|
||||||
|
max_value=5,
|
||||||
|
value=1,
|
||||||
|
help="Number of images per blog post"
|
||||||
|
)
|
||||||
|
|
||||||
|
image_style = st.selectbox(
|
||||||
|
"Image Style",
|
||||||
|
["Realistic", "Artistic", "Professional", "Creative"],
|
||||||
|
help="Style of generated images"
|
||||||
|
)
|
||||||
|
|
||||||
|
with col2:
|
||||||
|
st.markdown("### Image Generation Guide")
|
||||||
|
|
||||||
|
st.markdown("""
|
||||||
|
#### Model Selection
|
||||||
|
- Stable Diffusion: Versatile, fast
|
||||||
|
- DALL-E: High quality, creative
|
||||||
|
- Midjourney: Artistic, detailed
|
||||||
|
|
||||||
|
#### Best Practices
|
||||||
|
- Consider content type
|
||||||
- Balance quality vs. speed
|
- Balance quality vs. speed
|
||||||
|
- Optimize for platforms
|
||||||
|
""")
|
||||||
|
|
||||||
**Time Range**
|
# AI Generation Settings Tab
|
||||||
- Anytime: All available content
|
with tabs[2]:
|
||||||
- Recent: Latest information
|
col1, col2 = st.columns([1, 1])
|
||||||
- Historical: Past content
|
|
||||||
|
|
||||||
### Best Practices
|
with col1:
|
||||||
|
st.markdown("#### AI Generation Settings")
|
||||||
|
|
||||||
1. **Start Conservative**
|
gpt_provider = st.selectbox(
|
||||||
- Begin with moderate settings
|
"AI Provider",
|
||||||
- Adjust based on results
|
["google", "openai", "anthropic"],
|
||||||
- Monitor performance
|
help="Choose your preferred AI provider"
|
||||||
|
)
|
||||||
|
|
||||||
2. **Consider Your Audience**
|
model = st.text_input(
|
||||||
- Match tone to reader expectations
|
"Model",
|
||||||
- Adjust complexity appropriately
|
value="gemini-1.5-flash-latest",
|
||||||
- Focus on value delivery
|
help="The specific AI model to use"
|
||||||
|
)
|
||||||
|
|
||||||
3. **Optimize for Platform**
|
temperature = st.slider(
|
||||||
- Consider platform limitations
|
"Creativity Level",
|
||||||
- Match format requirements
|
min_value=0.0,
|
||||||
- Optimize for engagement
|
max_value=1.0,
|
||||||
|
value=0.7,
|
||||||
|
help="Higher = more creative, lower = more focused"
|
||||||
|
)
|
||||||
|
|
||||||
4. **Regular Review**
|
max_tokens = st.number_input(
|
||||||
- Monitor content performance
|
"Maximum Length",
|
||||||
- Adjust settings as needed
|
min_value=100,
|
||||||
- Stay updated with trends
|
max_value=8000,
|
||||||
</div>
|
value=4000,
|
||||||
""", unsafe_allow_html=True)
|
help="Maximum length of generated content"
|
||||||
|
)
|
||||||
|
|
||||||
st.markdown("</div>", unsafe_allow_html=True)
|
with col2:
|
||||||
|
st.markdown("### AI Settings Guide")
|
||||||
|
|
||||||
# Close the container
|
st.markdown("""
|
||||||
st.markdown("</div>", unsafe_allow_html=True)
|
#### Provider Selection
|
||||||
|
- Google: Balanced, reliable
|
||||||
|
- OpenAI: Creative, versatile
|
||||||
|
- Anthropic: Precise, ethical
|
||||||
|
|
||||||
# Add some spacing before the save button
|
#### Temperature Guide
|
||||||
st.markdown("<div style='height: 1.5rem;'></div>", unsafe_allow_html=True)
|
- 0.0-0.3: Focused, consistent
|
||||||
|
- 0.4-0.7: Balanced creativity
|
||||||
|
- 0.8-1.0: Highly creative
|
||||||
|
""")
|
||||||
|
|
||||||
|
# Search Settings Tab
|
||||||
|
with tabs[3]:
|
||||||
|
col1, col2 = st.columns([1, 1])
|
||||||
|
|
||||||
|
with col1:
|
||||||
|
st.markdown("#### Search Settings")
|
||||||
|
|
||||||
|
geo_location = st.text_input(
|
||||||
|
"Geographic Location",
|
||||||
|
value="us",
|
||||||
|
help="Target geographic location for search"
|
||||||
|
)
|
||||||
|
|
||||||
|
search_language = st.selectbox(
|
||||||
|
"Search Language",
|
||||||
|
["en", "es", "fr", "de", "other"],
|
||||||
|
help="Language for search results"
|
||||||
|
)
|
||||||
|
|
||||||
|
num_results = st.number_input(
|
||||||
|
"Number of Results",
|
||||||
|
min_value=1,
|
||||||
|
max_value=50,
|
||||||
|
value=10,
|
||||||
|
help="Number of search results to analyze"
|
||||||
|
)
|
||||||
|
|
||||||
|
time_range = st.selectbox(
|
||||||
|
"Time Range",
|
||||||
|
["anytime", "day", "week", "month", "year"],
|
||||||
|
help="Time range for search results"
|
||||||
|
)
|
||||||
|
|
||||||
|
with col2:
|
||||||
|
st.markdown("### Search Settings Guide")
|
||||||
|
|
||||||
|
st.markdown("""
|
||||||
|
#### Location & Language
|
||||||
|
- Affects result relevance
|
||||||
|
- Impacts local SEO
|
||||||
|
- Consider target market
|
||||||
|
|
||||||
|
#### Search Optimization
|
||||||
|
- Balance quantity vs. quality
|
||||||
|
- Consider time sensitivity
|
||||||
|
- Optimize for accuracy
|
||||||
|
""")
|
||||||
|
|
||||||
|
# Save button for manual settings
|
||||||
if st.button("Save Manual Settings", type="primary", use_container_width=True):
|
if st.button("Save Manual Settings", type="primary", use_container_width=True):
|
||||||
# Update main config with new values
|
try:
|
||||||
main_config["Blog Content Characteristics"] = {
|
# Save to main_config.json
|
||||||
"Blog Length": blog_length,
|
config = {
|
||||||
"Blog Tone": blog_tone,
|
"Blog Content Characteristics": {
|
||||||
"Blog Demographic": blog_demographic,
|
"Blog Length": blog_length,
|
||||||
"Blog Type": blog_type,
|
"Blog Tone": blog_tone,
|
||||||
"Blog Language": blog_language,
|
"Blog Demographic": blog_demographic,
|
||||||
"Blog Output Format": blog_format
|
"Blog Type": blog_type,
|
||||||
}
|
"Blog Language": blog_language,
|
||||||
|
"Blog Output Format": blog_format
|
||||||
|
},
|
||||||
|
"Blog Images Details": {
|
||||||
|
"Image Generation Model": image_model,
|
||||||
|
"Number of Blog Images": num_images,
|
||||||
|
"Image Style": image_style
|
||||||
|
},
|
||||||
|
"LLM Options": {
|
||||||
|
"GPT Provider": gpt_provider,
|
||||||
|
"Model": model,
|
||||||
|
"Temperature": temperature,
|
||||||
|
"Max Tokens": max_tokens
|
||||||
|
},
|
||||||
|
"Search Engine Parameters": {
|
||||||
|
"Geographic Location": geo_location,
|
||||||
|
"Search Language": search_language,
|
||||||
|
"Number of Results": num_results,
|
||||||
|
"Time Range": time_range
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
main_config["Blog Images Details"] = {
|
if save_main_config(config):
|
||||||
"Image Generation Model": image_model,
|
try:
|
||||||
"Number of Blog Images": num_images
|
# Read existing .env file content
|
||||||
}
|
env_lines = []
|
||||||
|
if os.path.exists('.env'):
|
||||||
|
with open('.env', 'r') as f:
|
||||||
|
env_lines = f.readlines()
|
||||||
|
|
||||||
main_config["LLM Options"] = {
|
# Remove any existing PERSONALIZATION_DONE entries
|
||||||
"GPT Provider": gpt_provider,
|
env_lines = [line for line in env_lines if not line.startswith('PERSONALIZATION_DONE=')]
|
||||||
"Model": model,
|
|
||||||
"Temperature": temperature,
|
|
||||||
"Top-p": top_p,
|
|
||||||
"Max Tokens": max_tokens,
|
|
||||||
"Frequency Penalty": frequency_penalty,
|
|
||||||
"Presence Penalty": presence_penalty
|
|
||||||
}
|
|
||||||
|
|
||||||
main_config["Search Engine Parameters"] = {
|
# Add new PERSONALIZATION_DONE entry
|
||||||
"Geographic Location": geo_location,
|
env_lines.append("PERSONALIZATION_DONE=True\n")
|
||||||
"Search Language": search_language,
|
|
||||||
"Number of Results": num_results,
|
|
||||||
"Time Range": time_range
|
|
||||||
}
|
|
||||||
|
|
||||||
if save_main_config(main_config):
|
# Write back to .env file
|
||||||
# Set PERSONALIZATION_DONE to True in .env file and environment
|
with open('.env', 'w') as f:
|
||||||
try:
|
f.writelines(env_lines)
|
||||||
with open('.env', 'a') as f:
|
|
||||||
f.write("\nPERSONALIZATION_DONE=True")
|
|
||||||
os.environ['PERSONALIZATION_DONE'] = "True"
|
|
||||||
logger.info("Set PERSONALIZATION_DONE=True in .env and environment")
|
|
||||||
except Exception as e:
|
|
||||||
logger.error(f"Failed to set PERSONALIZATION_DONE: {str(e)}")
|
|
||||||
|
|
||||||
st.success("✅ Your personalization settings have been saved successfully!")
|
# Update environment variable and session state
|
||||||
else:
|
os.environ['PERSONALIZATION_DONE'] = "True"
|
||||||
st.error("Unable to save settings. Please try again.")
|
st.session_state['personalization_saved'] = True
|
||||||
|
logger.info("Successfully set PERSONALIZATION_DONE=True in .env and environment")
|
||||||
|
st.success("✅ Your personalization settings have been saved successfully!")
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"Error updating PERSONALIZATION_DONE: {str(e)}")
|
||||||
|
st.error("Settings saved but failed to update environment. Please try again.")
|
||||||
|
else:
|
||||||
|
st.error("Unable to save settings. Please try again.")
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"Error saving settings: {str(e)}")
|
||||||
|
st.error(f"Failed to save settings: {str(e)}")
|
||||||
|
|
||||||
with tab2:
|
else: # ALwrity Personalization
|
||||||
st.markdown("#### ALwrity Personalization")
|
|
||||||
|
|
||||||
# Create two columns for the layout
|
|
||||||
col1, col2 = st.columns([2, 1])
|
col1, col2 = st.columns([2, 1])
|
||||||
|
|
||||||
with col1:
|
with col1:
|
||||||
# Website URL input
|
|
||||||
st.markdown("### Website URL")
|
st.markdown("### Website URL")
|
||||||
url = st.text_input(
|
url = st.text_input(
|
||||||
"Enter your website URL",
|
"Enter your website URL",
|
||||||
placeholder="https://example.com",
|
placeholder="https://example.com",
|
||||||
help="Provide your website URL to analyze your content style. Leave empty if you want to provide written samples instead."
|
help="Provide your website URL to analyze your content style"
|
||||||
)
|
)
|
||||||
logger.debug(f"Website URL input value: {url}")
|
|
||||||
|
|
||||||
# Alternative: Written samples
|
|
||||||
if not url:
|
if not url:
|
||||||
st.markdown("### Written Samples")
|
st.markdown("### Written Samples")
|
||||||
st.markdown("""
|
st.info("No website URL? No problem! Provide written samples instead.")
|
||||||
<div style='background-color: #f8f9fa; padding: 1rem; border-radius: 0.5rem; margin: 1rem 0;'>
|
|
||||||
<p>No website URL? No problem! You can provide written samples of your content instead.</p>
|
|
||||||
<p>Share your best articles, blog posts, or any content that represents your writing style.</p>
|
|
||||||
</div>
|
|
||||||
""", unsafe_allow_html=True)
|
|
||||||
samples = st.text_area(
|
samples = st.text_area(
|
||||||
"Paste your content samples here",
|
"Paste your content samples here",
|
||||||
help="Paste 2-3 samples of your best content. This helps ALwrity understand your writing style."
|
help="Paste 2-3 samples of your best content"
|
||||||
)
|
)
|
||||||
logger.debug(f"Sample text length: {len(samples) if samples else 0}")
|
|
||||||
|
|
||||||
# ALwrity Style button
|
if st.button("🎨 Analyze Style", use_container_width=True):
|
||||||
st.markdown("<div style='height: 20px'></div>", unsafe_allow_html=True)
|
# Existing style analysis code...
|
||||||
if st.button("🎨 ALwrity Style", use_container_width=True):
|
pass
|
||||||
if url:
|
|
||||||
with st.status("Starting style analysis...", expanded=True) as status:
|
|
||||||
try:
|
|
||||||
logger.info(f"Starting style analysis for URL: {url}")
|
|
||||||
|
|
||||||
# Step 1: Initialize crawler
|
|
||||||
status.update(label="Step 1/4: Initializing web crawler...", state="running")
|
|
||||||
crawler_service = AsyncWebCrawlerService()
|
|
||||||
|
|
||||||
# Step 2: Crawl website
|
|
||||||
status.update(label="Step 2/4: Crawling website content...", state="running")
|
|
||||||
loop = asyncio.new_event_loop()
|
|
||||||
asyncio.set_event_loop(loop)
|
|
||||||
result = loop.run_until_complete(crawler_service.crawl_website(url))
|
|
||||||
loop.close()
|
|
||||||
|
|
||||||
if result.get('success', False):
|
|
||||||
content = result.get('content', {})
|
|
||||||
|
|
||||||
# Step 3: Initialize style analyzer
|
|
||||||
status.update(label="Step 3/4: Analyzing content style...", state="running")
|
|
||||||
style_analyzer = StyleAnalyzer()
|
|
||||||
|
|
||||||
# Step 4: Perform style analysis
|
|
||||||
status.update(label="Step 4/4: Generating style recommendations...", state="running")
|
|
||||||
style_analysis = style_analyzer.analyze_content_style(content)
|
|
||||||
|
|
||||||
if style_analysis.get('error'):
|
|
||||||
status.update(label="Analysis failed", state="error")
|
|
||||||
st.error(f"Style analysis failed: {style_analysis['error']}")
|
|
||||||
else:
|
|
||||||
status.update(label="Analysis complete!", state="complete")
|
|
||||||
# Display style analysis results
|
|
||||||
display_style_analysis(style_analysis)
|
|
||||||
|
|
||||||
# Display original content in tabs
|
|
||||||
tab1, tab2, tab3 = st.tabs(["Content", "Metadata", "Links"])
|
|
||||||
|
|
||||||
with tab1:
|
|
||||||
st.markdown("### Main Content")
|
|
||||||
st.markdown(content.get('main_content', 'No content found'))
|
|
||||||
|
|
||||||
with tab2:
|
|
||||||
st.markdown("### Metadata")
|
|
||||||
st.markdown(f"""
|
|
||||||
**Title:** {content.get('title', 'No title found')}
|
|
||||||
|
|
||||||
**Description:** {content.get('description', 'No description found')}
|
|
||||||
|
|
||||||
**Meta Tags:**
|
|
||||||
{content.get('meta_tags', {})}
|
|
||||||
""")
|
|
||||||
|
|
||||||
with tab3:
|
|
||||||
st.markdown("### Links")
|
|
||||||
for link in content.get('links', []):
|
|
||||||
st.markdown(f"- [{link.get('text', '')}]({link.get('href', '')})")
|
|
||||||
|
|
||||||
else:
|
|
||||||
status.update(label="Crawling failed", state="error")
|
|
||||||
st.error(f"Failed to analyze website: {result.get('error', 'Unknown error')}")
|
|
||||||
|
|
||||||
except Exception as e:
|
|
||||||
logger.error(f"Error during style analysis: {str(e)}")
|
|
||||||
st.error(f"Analysis failed: {str(e)}")
|
|
||||||
elif samples:
|
|
||||||
with st.spinner("Analyzing content samples..."):
|
|
||||||
try:
|
|
||||||
# Initialize style analyzer
|
|
||||||
style_analyzer = StyleAnalyzer()
|
|
||||||
|
|
||||||
# Analyze content samples
|
|
||||||
style_analysis = style_analyzer.analyze_content_style({"main_content": samples})
|
|
||||||
|
|
||||||
if style_analysis.get('error'):
|
|
||||||
st.error(f"Style analysis failed: {style_analysis['error']}")
|
|
||||||
else:
|
|
||||||
# Display style analysis results
|
|
||||||
display_style_analysis(style_analysis)
|
|
||||||
|
|
||||||
except Exception as e:
|
|
||||||
logger.error(f"Error analyzing samples: {str(e)}")
|
|
||||||
st.error(f"Analysis failed: {str(e)}")
|
|
||||||
else:
|
|
||||||
st.warning("Please provide either a website URL or content samples")
|
|
||||||
|
|
||||||
with col2:
|
with col2:
|
||||||
st.markdown(get_glass_container("""
|
st.markdown("### How ALwrity Discovers Your Style")
|
||||||
### How ALwrity Discovers Your Style
|
|
||||||
|
|
||||||
**AI-Powered Style Analysis**
|
|
||||||
|
|
||||||
ALwrity AI analyzes your existing content to understand your unique writing style and preferences. This helps us generate content that matches your voice perfectly.
|
|
||||||
|
|
||||||
**Step 1: Content Analysis**
|
|
||||||
|
|
||||||
We'll analyze your website content or written samples to understand:
|
|
||||||
|
|
||||||
|
st.markdown("""
|
||||||
|
#### AI-Powered Analysis
|
||||||
|
ALwrity analyzes your content to understand:
|
||||||
- Writing tone and voice
|
- Writing tone and voice
|
||||||
- Vocabulary and language style
|
- Content structure
|
||||||
- Content structure and formatting
|
- Target audience
|
||||||
- Target audience and engagement style
|
- Engagement style
|
||||||
|
|
||||||
**Step 2: Style Recommendations**
|
#### Personalized Recommendations
|
||||||
|
We provide:
|
||||||
|
- Writing guidelines
|
||||||
|
- Content templates
|
||||||
|
- Style recommendations
|
||||||
|
- Audience insights
|
||||||
|
""")
|
||||||
|
|
||||||
Based on the analysis, we'll provide:
|
# Navigation buttons
|
||||||
|
|
||||||
- Personalized writing guidelines
|
|
||||||
- Content structure templates
|
|
||||||
- Tone and voice recommendations
|
|
||||||
- Audience engagement strategies
|
|
||||||
|
|
||||||
**Step 3: Content Generation**
|
|
||||||
|
|
||||||
Finally, we'll use these insights to:
|
|
||||||
|
|
||||||
- Generate content that matches your style
|
|
||||||
- Maintain consistency across all content
|
|
||||||
- Optimize for your target audience
|
|
||||||
- Ensure brand voice alignment
|
|
||||||
"""))
|
|
||||||
|
|
||||||
# API Configuration Form
|
|
||||||
st.markdown(get_glass_container("""
|
|
||||||
### API Configuration
|
|
||||||
|
|
||||||
Configure your API settings for optimal content generation.
|
|
||||||
"""))
|
|
||||||
|
|
||||||
with st.form("ai_config_form"):
|
|
||||||
# API Keys
|
|
||||||
st.text_input("OpenAI API Key", type="password", key="openai_key")
|
|
||||||
st.text_input("Google API Key", type="password", key="google_key")
|
|
||||||
st.text_input("SerpAPI Key", type="password", key="serpapi_key")
|
|
||||||
|
|
||||||
# Model Selection
|
|
||||||
st.selectbox("Select Model", ["gpt-3.5-turbo", "gpt-4"], key="model")
|
|
||||||
|
|
||||||
# Temperature
|
|
||||||
st.slider("Temperature", 0.0, 2.0, 0.7, 0.1, key="temperature")
|
|
||||||
|
|
||||||
# Max Tokens
|
|
||||||
st.number_input("Max Tokens", 100, 4000, 2000, 100, key="max_tokens")
|
|
||||||
|
|
||||||
# Submit button
|
|
||||||
submitted = st.form_submit_button("Save Configuration")
|
|
||||||
|
|
||||||
if submitted:
|
|
||||||
# Create config directory if it doesn't exist
|
|
||||||
config_dir = Path("config")
|
|
||||||
config_dir.mkdir(exist_ok=True)
|
|
||||||
|
|
||||||
# Save configuration
|
|
||||||
config = {
|
|
||||||
"openai_key": st.session_state.openai_key,
|
|
||||||
"google_key": st.session_state.google_key,
|
|
||||||
"serpapi_key": st.session_state.serpapi_key,
|
|
||||||
"model": st.session_state.model,
|
|
||||||
"temperature": st.session_state.temperature,
|
|
||||||
"max_tokens": st.session_state.max_tokens
|
|
||||||
}
|
|
||||||
|
|
||||||
config_file = config_dir / "test_config.json"
|
|
||||||
with open(config_file, "w") as f:
|
|
||||||
json.dump(config, f, indent=4)
|
|
||||||
|
|
||||||
st.success("Configuration saved successfully!")
|
|
||||||
|
|
||||||
# Navigation buttons with correct arguments
|
|
||||||
if render_navigation_buttons(4, 6, changes_made=True):
|
if render_navigation_buttons(4, 6, changes_made=True):
|
||||||
# Set PERSONALIZATION_DONE to False if not already set to True
|
try:
|
||||||
if 'PERSONALIZATION_DONE' not in os.environ or os.environ['PERSONALIZATION_DONE'] != "True":
|
# If user hasn't saved settings manually, mark as skipped
|
||||||
try:
|
if 'personalization_saved' not in st.session_state or not st.session_state.get('personalization_saved'):
|
||||||
with open('.env', 'a') as f:
|
# Read existing .env file content
|
||||||
f.write("\nPERSONALIZATION_DONE=False")
|
env_lines = []
|
||||||
os.environ['PERSONALIZATION_DONE'] = "False"
|
if os.path.exists('.env'):
|
||||||
logger.info("Set PERSONALIZATION_DONE=False in .env and environment")
|
with open('.env', 'r') as f:
|
||||||
except Exception as e:
|
env_lines = f.readlines()
|
||||||
logger.error(f"Failed to set PERSONALIZATION_DONE: {str(e)}")
|
|
||||||
|
# Remove any existing PERSONALIZATION_DONE entries
|
||||||
|
env_lines = [line for line in env_lines if not line.startswith('PERSONALIZATION_DONE=')]
|
||||||
|
|
||||||
|
# Add PERSONALIZATION_DONE=False since user skipped
|
||||||
|
env_lines.append("PERSONALIZATION_DONE=False\n")
|
||||||
|
|
||||||
|
# Write back to .env file
|
||||||
|
with open('.env', 'w') as f:
|
||||||
|
f.writelines(env_lines)
|
||||||
|
|
||||||
|
# Update environment variable
|
||||||
|
os.environ['PERSONALIZATION_DONE'] = "False"
|
||||||
|
logger.info("User skipped personalization. Set PERSONALIZATION_DONE=False")
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"Error updating PERSONALIZATION_DONE on skip: {str(e)}")
|
||||||
|
st.error("Error updating environment. You may need to configure personalization later.")
|
||||||
|
|
||||||
# Update the current step to 5 (ALwrity integrations)
|
|
||||||
st.session_state.current_step = 5
|
st.session_state.current_step = 5
|
||||||
logger.info("Moving to step 5: ALwrity integrations")
|
|
||||||
st.rerun()
|
st.rerun()
|
||||||
|
|
||||||
return {"current_step": 4, "changes_made": True}
|
return {"current_step": 4, "changes_made": True}
|
||||||
Reference in New Issue
Block a user