diff --git a/.gitignore b/.gitignore index d168362f..74a3cefd 100644 --- a/.gitignore +++ b/.gitignore @@ -21,6 +21,10 @@ __pycache__ *.pywpz *.pywpzp +lib/workspace/alwrity_web_research/* +lib/workspace/alwrity_web_research_cache/* +web_research_report* + .swp .swo .swn diff --git a/alwrity.py b/alwrity.py index 39d8d700..35637eb6 100644 --- a/alwrity.py +++ b/alwrity.py @@ -4,6 +4,7 @@ import json import base64 import logging from datetime import datetime +<<<<<<< HEAD import os import json import base64 @@ -59,13 +60,15 @@ logging.basicConfig( ] ) logger = logging.getLogger(__name__) +======= +>>>>>>> b48a3b1 (Google Search Grounded results, Content Calendar Ideator, Competitor Analysis, and Keyword Researcher) # 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, @@ -73,18 +76,29 @@ 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) @@ -111,18 +125,13 @@ logging.basicConfig( ) logger = logging.getLogger(__name__) -from lib.utils.config_manager import save_config from lib.utils.ui_setup import setup_ui -from lib.utils.alwrity_sidebar import sidebar_configuration from lib.utils.api_key_manager.api_key_manager import APIKeyManager, render from lib.utils.api_key_manager.validation import check_all_api_keys from dotenv import load_dotenv -from lib.utils.content_generators import ai_writers, content_planning_tools, blog_from_keyword, story_input_section, essay_writer, ai_news_writer, ai_finance_ta_writer, write_ai_prod_desc, do_web_research, competitor_analysis -from lib.utils.seo_tools import ai_seo_tools -from lib.utils.ui_setup import setup_ui, setup_tabs -from lib.utils.alwrity_utils import ai_agents_team, ai_social_writer -from lib.utils.file_processor import load_image, read_prompts, write_prompts -from lib.utils.voice_processing import record_voice +from lib.utils.content_generators import blog_from_keyword, story_input_section, essay_writer, ai_news_writer, ai_finance_ta_writer, write_ai_prod_desc, do_web_research, competitor_analysis +from lib.utils.ui_setup import setup_ui, setup_alwrity_ui + def process_folder_for_rag(folder_path): """Placeholder for the process_folder_for_rag function.""" @@ -160,36 +169,110 @@ def main(): # Check API keys and show setup if needed if not check_all_api_keys(api_key_manager): logger.info("API keys not verified") + # Add CSS to hide sidebar during setup + st.markdown(""" + + """, unsafe_allow_html=True) render(api_key_manager) return else: logger.info("All API keys verified") - # Remove the CSS that hides the sidebar + # Remove the CSS that hides the sidebar and ensure it's expanded st.markdown(""" + + """, unsafe_allow_html=True) + + # Set session state to ensure sidebar stays expanded + if 'sidebar_expanded' not in st.session_state: + st.session_state.sidebar_expanded = True + + # Force sidebar state + st.sidebar.markdown(""" + """, unsafe_allow_html=True) - - setup_environment_paths() - sidebar_configuration() - setup_tabs() + + setup_alwrity_ui() def setup_environment_paths(): 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/lib/utils/api_key_manager/components/personalization_setup.py b/lib/utils/api_key_manager/components/personalization_setup.py index f4f773c4..cd61fb15 100644 --- a/lib/utils/api_key_manager/components/personalization_setup.py +++ b/lib/utils/api_key_manager/components/personalization_setup.py @@ -8,11 +8,13 @@ from typing import Dict, Any from ..manager import APIKeyManager from ....web_crawlers.async_web_crawler import AsyncWebCrawlerService from ....personalization.style_analyzer import StyleAnalyzer -from pages.style_utils import ( - get_analysis_section, +from lib.utils.style_utils import ( + get_test_config_styles, get_glass_container, get_info_section, - get_example_box + get_example_box, + get_analysis_section, + get_style_guide_html ) from .base import render_navigation_buttons from .alwrity_integrations import render_alwrity_integrations @@ -618,7 +620,7 @@ def render_personalization_setup(api_key_manager: APIKeyManager) -> Dict[str, An st.warning("Please provide either a website URL or content samples") with col2: - st.markdown(""" + st.markdown(get_glass_container(""" ### How ALwrity Discovers Your Style **AI-Powered Style Analysis** @@ -651,10 +653,15 @@ def render_personalization_setup(api_key_manager: APIKeyManager) -> Dict[str, An - Maintain consistency across all content - Optimize for your target audience - Ensure brand voice alignment - """) + """)) # API Configuration Form - st.markdown("### API Configuration") + 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") diff --git a/lib/utils/ui_setup.py b/lib/utils/ui_setup.py index fb54c9fd..e2b6d623 100644 --- a/lib/utils/ui_setup.py +++ b/lib/utils/ui_setup.py @@ -4,6 +4,7 @@ from lib.utils.file_processor import load_image from lib.utils.content_generators import content_planning_tools, ai_writers from lib.utils.alwrity_utils import ai_social_writer from lib.utils.seo_tools import ai_seo_tools +from lib.utils.settings_page import render_settings_page def setup_ui(): @@ -67,40 +68,73 @@ def setup_ui(): border-radius: 8px; color: white; } + + /* Sidebar navigation styling */ + .sidebar-nav { + padding: 1rem 0; + } + + .nav-button { + width: 100%; + text-align: left; + padding: 0.5rem 1rem; + background: transparent; + border: none; + color: #2c3e50; + font-weight: 500; + cursor: pointer; + transition: all 0.3s ease; + margin: 0.2rem 0; + border-radius: 4px; + } + + .nav-button:hover { + background: rgba(0,0,0,0.05); + padding-left: 1.5rem; + } + + .nav-button.active { + background: #1565C0; + color: white; + } """, unsafe_allow_html=True) - image_base64 = load_image("lib/workspace/alwrity_logo.png") - st.markdown(f""" -
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 diff --git a/requirements.txt b/requirements.txt index 4760f08b..881f53b4 100644 --- a/requirements.txt +++ b/requirements.txt @@ -6,7 +6,7 @@ beautifulsoup4==4.12.2 aiohttp>=3.11.11 openai>=1.3.7 PyPDF2>=3.0.1 -google-generativeai<0.9.0,>=0.8.0 +google-genai==1.9.0 anthropic>=0.18.1 tenacity>=8.2.3 tabulate>=0.9.0