diff --git a/.gitignore b/.gitignore
index b6b32d43..39a52973 100644
--- a/.gitignore
+++ b/.gitignore
@@ -21,6 +21,7 @@ __pycache__
*.pywpz
*.pywpzp
+
lib/workspace/alwrity_web_research/*
lib/workspace/alwrity_web_research_cache/*
web_research_report*
diff --git a/alwrity.py b/alwrity.py
index 9650cd27..0cb6f5a6 100644
--- a/alwrity.py
+++ b/alwrity.py
@@ -201,4 +201,4 @@ def setup_environment_paths():
if __name__ == "__main__":
- main()
+ main()
\ No newline at end of file
diff --git a/lib/utils/alwrity_sidebar.py b/lib/utils/alwrity_sidebar.py
new file mode 100644
index 00000000..8665bead
--- /dev/null
+++ b/lib/utils/alwrity_sidebar.py
@@ -0,0 +1,244 @@
+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
new file mode 100644
index 00000000..5d094cd4
--- /dev/null
+++ b/pages/ai_research_setup_page.py
@@ -0,0 +1,50 @@
+"""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="
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 881f53b4..e2dbc0e7 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-genai==1.9.0 +google-genai>=1.9.0 anthropic>=0.18.1 tenacity>=8.2.3 tabulate>=0.9.0