diff --git a/alwrity.py b/alwrity.py index 6430db07..c41b962f 100644 --- a/alwrity.py +++ b/alwrity.py @@ -1,11 +1,16 @@ import streamlit as st +import os +import json +import base64 +import logging +from datetime import datetime # Set page config - must be the first Streamlit command st.set_page_config( page_title="AI Writer - Content Generation Platform", page_icon="✍️", layout="wide", - initial_sidebar_state="collapsed", # Start with collapsed sidebar + initial_sidebar_state="expanded", # Changed from collapsed to expanded menu_items={ 'Get Help': None, 'Report a bug': None, @@ -13,27 +18,32 @@ st.set_page_config( } ) -# Add CSS to hide sidebar during setup -st.markdown(""" +# Load and apply custom CSS +with open('lib/workspace/alwrity_ui_styling.css', 'r') as f: + css = f.read() + +st.markdown(f""" """, unsafe_allow_html=True) -import os -import json -import base64 -import logging -from datetime import datetime - # Configure logging logging.basicConfig( level=logging.DEBUG, diff --git a/lib/utils/alwrity_sidebar.py b/lib/utils/alwrity_sidebar.py deleted file mode 100644 index 8665bead..00000000 --- a/lib/utils/alwrity_sidebar.py +++ /dev/null @@ -1,244 +0,0 @@ -import streamlit as st -import logging - -from .config_manager import save_config - -# Configure logging -logging.basicConfig( - level=logging.DEBUG, - format='%(asctime)s - %(name)s - %(levelname)s - %(message)s', - handlers=[ - logging.StreamHandler(), # Output to console - #logging.FileHandler('alwrity.log') # Output to file - ] -) -logger = logging.getLogger(__name__) - -# Sidebar configuration -def sidebar_configuration(): - """Configure the sidebar with all necessary options.""" - try: - # Configure sidebar styling - st.sidebar.markdown(""" - - """, unsafe_allow_html=True) - - logger.info("Initializing sidebar configuration") - st.sidebar.title("🛠️ Personalization & Settings 🏗️") - - with st.sidebar.expander("**👷 Content Personalization**"): - logger.debug("Setting up content personalization options") - blog_length = st.text_input("**Content Length (words)**", value="2000", - help="Approximate word count for blogs. Note: Actual length may vary based on GPT provider and max token count.") - - blog_tone_options = ["Casual", "Professional", "How-to", "Beginner", "Research", "Programming", "Social Media", "Customize"] - blog_tone = st.selectbox("**Content Tone**", - options=blog_tone_options, - help="Select the desired tone for the blog content.") - logger.debug(f"Selected blog tone: {blog_tone}") - - if blog_tone == "Customize": - custom_tone = st.text_input("Enter the tone of your content", help="Specify the tone of your content.") - if custom_tone: - blog_tone = custom_tone - logger.debug(f"Custom tone set to: {custom_tone}") - else: - logger.warning("Custom tone not specified") - st.warning("Please specify the tone of your content.") - - blog_demographic_options = ["Professional", "Gen-Z", "Tech-savvy", "Student", "Digital Marketing", "Customize"] - - blog_demographic = st.selectbox("**Target Audience**", - options=blog_demographic_options, - help="Select the primary audience for the blog content.") - if blog_demographic == "Customize": - custom_demographic = st.text_input("Enter your target audience", - help="Specify your target audience.", - placeholder="Eg. Domain expert, Content creator, Financial expert etc..") - if custom_demographic: - blog_demographic = custom_demographic - else: - st.warning("Please specify your target audience.") - - blog_type = st.selectbox("**Content Type**", - options=["Informational", "Commercial", "Company", "News", "Finance", "Competitor", "Programming", "Scholar"], - help="Select the category that best describes the blog content.") - - blog_language = st.selectbox("**Content Language**", - options=["English", "Spanish", "German", "Chinese", "Arabic", "Nepali", "Hindi", "Hindustani", "Customize"], - help="Select the language in which the blog will be written.") - if blog_language == "Customize": - custom_lang = st.text_input("Enter the language of your choice", help="Specify the content language.") - if custom_lang: - blog_language = custom_lang - else: - st.warning("Please specify the language of your content.") - - blog_output_format = st.selectbox("**Content Output Format**", - options=["markdown", "HTML", "plaintext"], - help="Select the format for the blog output.") - - with st.sidebar.expander("**🩻 Images Personalization**"): - image_generation_model = st.selectbox("**Image Generation Model**", - options=["stable-diffusion", "dalle2", "dalle3"], - help="Select the model to generate images for the blog.") - number_of_blog_images = st.number_input("**Number of Blog Images**", value=1, help="Specify the number of images to include in the blog.") - - with st.sidebar.expander("**🤖 LLM Personalization**"): - gpt_provider = st.selectbox("**GPT Provider**", - options=["google", "openai", "minstral"], - help="Select the provider for the GPT model.") - model = st.text_input("**Model**", value="gemini-1.5-flash-latest", help="Specify the model version to use from the selected provider.") - temperature = st.slider( - "Temperature", - min_value=0.1, - max_value=1.0, - value=0.7, - step=0.1, - format="%.1f", - help="""Temperature controls the 'creativity' or randomness of the text generated by GPT. - Greater determinism with higher values indicating more randomness.""" - ) - - top_p = st.slider( - "Top-p", - min_value=0.0, - max_value=1.0, - value=0.9, - step=0.1, - format="%.1f", - help="Top-p sampling controls the level of diversity in the generated text." - ) - - # Selectbox for max tokens - max_tokens_options = [500, 1000, 2000, 4000, 16000, 32000, 64000] - max_tokens = st.selectbox( - "Max Tokens", - options=max_tokens_options, - index=max_tokens_options.index(4000), - help="Max tokens determine the maximum length of the output sequence generated by a model." - ) - n = st.number_input("N", - value=1, - min_value=1, - max_value=10, - help="Defines the number of words or characters grouped together in a sequence when analyzing text.") - frequency_penalty = st.slider( - "Frequency Penalty", - min_value=0.0, - max_value=2.0, - value=1.0, - step=0.1, - format="%.1f", - help="Influences word selection during text generation, promoting diversity with higher values." - ) - - presence_penalty = st.slider( - "Presence Penalty", - min_value=0.0, - max_value=2.0, - value=1.0, - step=0.1, - format="%.1f", - help="Encourages the use of diverse words by discouraging repetition." - ) - - with st.sidebar.expander("**🕵️ Search Engine Personalization**"): - geographic_location = st.selectbox("**Geographic Location**", - options=["us", "in", "fr", "cn"], - help="Select the geographic location for tailoring search results.") - search_language = st.selectbox("**Search Language**", - options=["en", "zn-cn", "de", "hi"], - help="Select the language for the search results.") - number_of_results = st.number_input("**Number of Results**", - value=10, - max_value=20, - min_value=1, - help="Specify the number of search results to retrieve.") - time_range = st.selectbox("**Time Range**", - options=["anytime", "past day", "past week", "past month", "past year"], - help="Select the time range for filtering search results.") - include_domains = st.text_input("**Include Domains**", value="", - help="List specific domains to include in search results. Leave blank to include all domains.") - similar_url = st.text_input("**Similar URL**", value="", help="Provide a URL to find similar results. Leave blank if not needed.") - - # Storing collected inputs in a dictionary - config = { - "Blog Content Characteristics": { - "Blog Length": blog_length, - "Blog Tone": blog_tone, - "Blog Demographic": blog_demographic, - "Blog Type": blog_type, - "Blog Language": blog_language, - "Blog Output Format": blog_output_format - }, - "Blog Images Details": { - "Image Generation Model": image_generation_model, - "Number of Blog Images": number_of_blog_images - }, - "LLM Options": { - "GPT Provider": gpt_provider, - "Model": model, - "Temperature": temperature, - "Top-p": top_p, - "Max Tokens": max_tokens, - "N": n, - "Frequency Penalty": frequency_penalty, - "Presence Penalty": presence_penalty - }, - "Search Engine Parameters": { - "Geographic Location": geographic_location, - "Search Language": search_language, - "Number of Results": number_of_results, - "Time Range": time_range, - "Include Domains": include_domains, - "Similar URL": similar_url - } - } - - # Writing the configuration to a file whenever a change is made - save_config(config) - except Exception as e: - logger.error(f"Error configuring sidebar: {str(e)}") - st.error(f"Error configuring sidebar: {str(e)}") \ No newline at end of file diff --git a/pages/ai_research_setup_page.py b/pages/ai_research_setup_page.py deleted file mode 100644 index 5d094cd4..00000000 --- a/pages/ai_research_setup_page.py +++ /dev/null @@ -1,50 +0,0 @@ -"""Page for AI Research Setup redirection.""" - -import streamlit as st -from loguru import logger -import sys -import os - -# Configure logger -logger.remove() # Remove default handler -logger.add( - "logs/ai_research_setup_page.log", - rotation="500 MB", - retention="10 days", - level="DEBUG", - format="{time:YYYY-MM-DD HH:mm:ss} | {level} | {message}", - backtrace=True, - diagnose=True -) -logger.add( - sys.stdout, - level="INFO", - format="{time:YYYY-MM-DD HH:mm:ss} | {level: <8} | {message}" -) - -# Set page config -st.set_page_config( - layout="wide", - initial_sidebar_state="collapsed", - menu_items={ - 'Get Help': None, - 'Report a bug': None, - 'About': None - } -) - -def render_ai_research_setup_page(): - """Render the AI Research Setup page.""" - try: - logger.info("Starting AI Research Setup page") - - # Import and render the AI Research Setup component - from lib.utils.api_key_manager.components.ai_research_setup import render_ai_research_setup - render_ai_research_setup() - - except Exception as e: - logger.error(f"Error in render_ai_research_setup_page: {str(e)}") - st.error(f"An error occurred: {str(e)}") - -if __name__ == "__main__": - render_ai_research_setup_page() \ No newline at end of file diff --git a/pages/personalization_setup.py b/pages/personalization_setup.py deleted file mode 100644 index d35e9930..00000000 --- a/pages/personalization_setup.py +++ /dev/null @@ -1,84 +0,0 @@ -import streamlit as st -import os -import json -from pathlib import Path - -st.set_page_config( - page_title="Personalization Setup", - page_icon="⚙️", - layout="wide" -) - -st.title("Personalization Setup") - -# Initialize session state for active tab if not exists -if 'active_tab' not in st.session_state: - st.session_state.active_tab = "Writing Preferences" - -# Create tabs for different sections -tab1, tab2 = st.tabs(["Writing Preferences", "AI Configuration"]) - -with tab1: - st.write(""" - This section allows you to customize your AI writing experience. - Configure your preferences and settings here. - """) - - # Add your personalization options here - st.subheader("Writing Style Preferences") - tone = st.selectbox( - "Select your preferred writing tone", - ["Professional", "Casual", "Academic", "Creative"] - ) - - st.subheader("Content Preferences") - content_type = st.multiselect( - "Select your preferred content types", - ["Blog Posts", "Articles", "Social Media", "Technical Writing", "Creative Writing"] - ) - - if st.button("Save Preferences"): - st.success("Your preferences have been saved!") - -with tab2: - st.subheader("AI Configuration Settings") - - # Create a form for AI configuration - 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!") \ No newline at end of file diff --git a/pages/style_utils.py b/pages/style_utils.py deleted file mode 100644 index cf0345cc..00000000 --- a/pages/style_utils.py +++ /dev/null @@ -1,352 +0,0 @@ -"""CSS styles and utilities for ALwrity pages.""" - -def get_base_styles() -> str: - """ - Get the base CSS styles for ALwrity. - - Returns: - str: CSS styles as a string - """ - return """ - - """ - -def get_glassmorphic_styles() -> str: - """ - Get the glassmorphic CSS styles for ALwrity. - - Returns: - str: CSS styles as a string - """ - return """ - - """ - -def get_glass_container(content: str) -> str: - """Wrap content in a glass container.""" - return f""" -
- {content} -
- """ - -def get_info_section(content: str) -> str: - """Wrap content in an info section.""" - return f""" -
- {content} -
- """ - -def get_example_box(content: str) -> str: - """Wrap content in an example box.""" - return f""" -
- {content} -
- """ - -def get_analysis_section(title: str, content: str) -> str: - """Create an analysis section with title and content.""" - return f""" -
-

{title}

- {content} -
- """ - -def get_style_guide_html() -> str: - """ - Get the style guide HTML content. - - Returns: - str: HTML content for the style guide section - """ - return """ - ### 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: - - - Writing tone and voice - - Vocabulary and language style - - Content structure and formatting - - Target audience and engagement style - - **Step 2: Style Recommendations** - - Based on the analysis, we'll provide: - - - 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 - """ - -def get_test_config_styles() -> str: - """ - Get all CSS styles for test configuration settings page. - - Returns: - str: Combined CSS styles as a string - """ - return f"{get_base_styles()}{get_glassmorphic_styles()}" \ No newline at end of file diff --git a/pages/test_config_settings.py b/pages/test_config_settings.py deleted file mode 100644 index 1ab9a2c8..00000000 --- a/pages/test_config_settings.py +++ /dev/null @@ -1,310 +0,0 @@ -"""Test configuration settings page for ALwrity.""" - -import streamlit as st -from loguru import logger -import asyncio -from lib.web_crawlers.async_web_crawler import AsyncWebCrawlerService -from pages.style_utils import ( - get_test_config_styles, - get_glass_container, - get_info_section, - get_example_box, - get_analysis_section, - get_style_guide_html -) -import sys -from lib.personalization.style_analyzer import StyleAnalyzer - -# Set page config - must be the first Streamlit command -st.set_page_config( - layout="wide", - initial_sidebar_state="collapsed", - menu_items={ - 'Get Help': None, - 'Report a bug': None, - 'About': None - } -) - -import yaml -from pathlib import Path -import os -from loguru import logger -from lib.utils.read_main_config_params import get_personalization_settings -from lib.web_crawlers.crawl4ai_web_crawler import analyze_style - -# Configure logger -logger.remove() # Remove default handler -logger.add( - "logs/test_config_settings.log", - rotation="500 MB", - retention="10 days", - level="DEBUG", - format="{time:YYYY-MM-DD HH:mm:ss} | {level} | {message}", - backtrace=True, - diagnose=True -) -logger.add( - sys.stdout, - level="INFO", - format="{time:YYYY-MM-DD HH:mm:ss} | {level: <8} | {message}" -) - -# Apply CSS styles -st.markdown(get_test_config_styles(), unsafe_allow_html=True) - -def load_website_url(): - """Load website URL from config file.""" - try: - logger.debug("Loading website URL from config file") - config_path = Path(os.environ["ALWRITY_CONFIG"]) - config = yaml.safe_load(config_path.read_text()) - url = config.get('website', {}).get('url', '') - logger.info(f"Loaded website URL: {url}") - return url - except Exception as e: - logger.error(f"Error loading website URL: {str(e)}", exc_info=True) - return '' - -def display_style_analysis(analysis_results: dict): - """Display the style analysis results in a structured format.""" - try: - # Writing Style Section - st.markdown("### 🎨 Writing Style Analysis") - writing_style = analysis_results.get("writing_style", {}) - writing_style_content = f""" - - """ - st.markdown(get_analysis_section("Writing Style", writing_style_content), unsafe_allow_html=True) - - # Content Characteristics Section - content_chars = analysis_results.get("content_characteristics", {}) - content_chars_content = f""" - - """ - st.markdown(get_analysis_section("Content Characteristics", content_chars_content), unsafe_allow_html=True) - - # Target Audience Section - target_audience = analysis_results.get("target_audience", {}) - target_audience_content = f""" - - """ - st.markdown(get_analysis_section("Target Audience", target_audience_content), unsafe_allow_html=True) - - # Content Type Section - content_type = analysis_results.get("content_type", {}) - content_type_content = f""" - - """ - st.markdown(get_analysis_section("Content Type", content_type_content), unsafe_allow_html=True) - - # Recommended Settings Section - recommended = analysis_results.get("recommended_settings", {}) - recommended_content = f""" - - """ - st.markdown(get_analysis_section("Recommended Settings", recommended_content), unsafe_allow_html=True) - - except Exception as e: - logger.error(f"Error displaying style analysis: {str(e)}") - st.error(f"Error displaying analysis results: {str(e)}") - -def render_test_config_settings(): - """Render the test configuration settings page.""" - try: - logger.info("Starting to render test configuration settings") - - # Add back button at the top - col1, col2 = st.columns([1, 3]) - with col1: - if st.button("← Back to Personalization Setup"): - logger.info("User clicked back to personalization setup") - # Set session state for navigation - st.session_state.current_step = 4 - st.session_state.next_step = "personalization_setup" - # Navigate back to personalization setup - st.switch_page("pages/personalization_setup.py") - - # Title and description - st.title("🎨 Find Your Style with ALwrity") - st.markdown(get_glass_container( - "

Enter a website URL or provide content samples to analyze your writing style and get personalized recommendations.

" - ), unsafe_allow_html=True) - - # Create two columns for the layout - col1, col2 = st.columns([2, 1]) - - with col1: - # Website URL input - st.markdown("### Website URL") - url = st.text_input( - "Enter your website URL", - placeholder="https://example.com", - help="Provide your website URL to analyze your content style. Leave empty if you want to provide written samples instead." - ) - logger.debug(f"Website URL input value: {url}") - - # Alternative: Written samples - if not url: - st.markdown("### Written Samples") - st.markdown(get_info_section(""" -

No website URL? No problem! You can provide written samples of your content instead.

-

Share your best articles, blog posts, or any content that represents your writing style.

- """), unsafe_allow_html=True) - samples = st.text_area( - "Paste your content samples here", - help="Paste 2-3 samples of your best content. This helps ALwrity understand your writing style." - ) - logger.debug(f"Sample text length: {len(samples) if samples else 0}") - - st.markdown('', unsafe_allow_html=True) - - # ALwrity Style button - st.markdown("
", unsafe_allow_html=True) - if st.button("🎨 ALwrity Style", use_container_width=True): - 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: - # TODO: Implement sample text analysis - st.info("Sample text analysis coming soon!") - 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: - st.markdown(""" - ### 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: - - - Writing tone and voice - - Vocabulary and language style - - Content structure and formatting - - Target audience and engagement style - - **Step 2: Style Recommendations** - - Based on the analysis, we'll provide: - - - 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 - """) - - except Exception as e: - logger.error(f"Error in render_test_config_settings: {str(e)}") - st.error(f"An error occurred: {str(e)}") - -if __name__ == "__main__": - logger.info("Starting test config settings page") - render_test_config_settings() - logger.info("Test config settings page rendered successfully") \ No newline at end of file