diff --git a/alwrity.py b/alwrity.py index e5f25f02..4fa2fa7d 100644 --- a/alwrity.py +++ b/alwrity.py @@ -1,21 +1,12 @@ -import os -import json -import base64 -from datetime import datetime import streamlit as st - -# Load .env file -def load_environment(): - from dotenv import load_dotenv - load_dotenv() - -from lib.utils.alwrity_utils import ( - blog_from_keyword, ai_agents_team, essay_writer, ai_news_writer, ai_seo_tools, - ai_finance_ta_writer, ai_social_writer, do_web_research, competitor_analysis -) -from lib.ai_writers.ai_story_writer.story_writer import story_input_section -from lib.ai_writers.ai_product_description_writer import write_ai_prod_desc -from lib.content_planning_calender.content_planning_agents_alwrity_crew import ai_agents_content_planner +from lib.utils.environment_utils import load_environment +from lib.utils.config_manager import save_config +from lib.utils.ui_setup import setup_ui, setup_tabs +from lib.utils.api_key_manager import check_api_keys, check_llm_environs +from lib.utils.content_generators import ai_writers, content_planning_tools +from lib.utils.seo_tools import ai_seo_tools +from lib.utils.file_processor import load_image, read_prompts, write_prompts +from lib.utils.voice_processing import record_voice # Placeholder function definitions for missing functions def blog_from_audio(): diff --git a/lib/utils/alwrity_utils.py b/lib/utils/alwrity_utils.py index 0fbc0704..c85adfc1 100644 --- a/lib/utils/alwrity_utils.py +++ b/lib/utils/alwrity_utils.py @@ -1,27 +1,7 @@ -import os import re -import sys import streamlit as st -from streamlit_mic_recorder import speech_to_text -import asyncio import tempfile -from pathlib import Path -import configparser -from datetime import datetime -import uuid -from PIL import Image -import PyPDF2 -import openai -import tiktoken -from docx import Document from loguru import logger -logger.remove() -logger.add(sys.stdout, - colorize=True, - format="{level}|{file}:{line}:{function}| {message}" - ) - - from lib.ai_web_researcher.gpt_online_researcher import gpt_web_researcher from lib.ai_web_researcher.metaphor_basic_neural_web_search import metaphor_find_similar from lib.ai_writers.keywords_to_blog_streamlit import write_blog_from_keywords @@ -38,43 +18,11 @@ from lib.ai_writers.youtube_ai_writer import write_yt_title, write_yt_descriptio from lib.ai_writers.web_url_ai_writer import blog_from_url from lib.ai_writers.image_ai_writer import blog_from_image from lib.ai_writers.ai_essay_writer import ai_essay_generator -from lib.ai_seo_tools.seo_structured_data import ai_structured_data -from lib.ai_seo_tools.content_title_generator import ai_title_generator -from lib.ai_seo_tools.meta_desc_generator import metadesc_generator_main -from lib.ai_seo_tools.image_alt_text_generator import alt_text_gen -from lib.ai_seo_tools.opengraph_generator import og_tag_generator -from lib.ai_seo_tools.optimize_images_for_upload import main_img_optimizer -from lib.ai_seo_tools.google_pagespeed_insights import google_pagespeed_insights -from lib.ai_seo_tools.on_page_seo_analyzer import analyze_onpage_seo -from lib.ai_seo_tools.weburl_seo_checker import url_seo_checker from lib.gpt_providers.text_to_image_generation.main_generate_image_from_prompt import generate_image from lib.content_planning_calender.content_planning_agents_alwrity_crew import ai_agents_content_planner from ..gpt_providers.text_generation.main_text_generation import llm_text_gen -@st.cache_data -def record_voice(language="en"): - # https://github.com/B4PT0R/streamlit-mic-recorder?tab=readme-ov-file#example - state = st.session_state - if "text_received" not in state: - state.text_received = [] - - text = speech_to_text( - start_prompt="🎙️Press & Speak🔊", - stop_prompt="🔇Stop Recording🚨", - language=language, - use_container_width=True, - just_once=False, - ) - if text: - state.text_received.append(text) - result = "" - for text in state.text_received: - result += text - state.text_received = [] - return result if result else None - - def is_youtube_link(text): if text is not None: youtube_regex = re.compile(r'(https?://)?(www\.)?(youtube|youtu|youtube-nocookie)\.(com|be)/(watch\?v=|embed/|v/|.+\?v=)?([^&=%\?]{11})') diff --git a/lib/utils/api_key_manager.py b/lib/utils/api_key_manager.py new file mode 100644 index 00000000..8af8243d --- /dev/null +++ b/lib/utils/api_key_manager.py @@ -0,0 +1,71 @@ +import os +import streamlit as st + +@st.cache_data +def check_api_keys(): + """ + Checks if the required API keys are present in the environment variables. + Prompts the user to enter missing keys and saves them in the .env file. + """ + api_keys = { + "METAPHOR_API_KEY": "https://dashboard.exa.ai/login", + "TAVILY_API_KEY": "https://tavily.com/#api", + "SERPER_API_KEY": "https://serper.dev/signup", + "STABILITY_API_KEY": "https://platform.stability.ai/", + "FIRECRAWL_API_KEY": "https://www.firecrawl.dev/account" + } + + missing_keys = { + key: url for key, url in api_keys.items() if os.getenv(key) is None + } + + if missing_keys: + st.error("🚨 Some API keys are missing! Please provide them below:") + for key, url in missing_keys.items(): + api_key = st.text_input(f"Enter 🔏 {key}: 👉[Get it here]({url})👈") + if api_key: + os.environ[key] = api_key + try: + with open(".env", "a") as env_file: + env_file.write(f"{key}={api_key}\n") + except IOError as e: + st.error(f"Failed to write {key} to .env file: {e}") + st.success(f"✅ {key} added successfully!") + return False + return True + +@st.cache_data +def check_llm_environs(): + """ + Ensures that the LLM provider and corresponding API key are set. + Prompts the user to select a provider and enter the API key if missing. + """ + gpt_provider = os.getenv("GPT_PROVIDER") + supported_providers = { + 'google': "GEMINI_API_KEY", + 'openai': "OPENAI_API_KEY", + 'mistralai': "MISTRAL_API_KEY" + } + + if not gpt_provider or gpt_provider.lower() not in supported_providers: + gpt_provider = st.selectbox( + "Select your LLM Provider", options=list(supported_providers.keys()) + ) + os.environ["GPT_PROVIDER"] = gpt_provider + try: + with open(".env", "a") as env_file: + env_file.write(f"GPT_PROVIDER={gpt_provider}\n") + except IOError as e: + st.error(f"Failed to write GPT_PROVIDER to .env file: {e}") + st.success(f"GPT Provider set to {gpt_provider}") + + api_key_var = supported_providers[gpt_provider.lower()] + if not os.getenv(api_key_var): + api_key = st.text_input(f"Enter {api_key_var}:") + if api_key: + os.environ[api_key_var] = api_key + with open(".env", "a") as env_file: + env_file.write(f"{api_key_var}={api_key}\n") + st.success(f"{api_key_var} added successfully!") + return False + return True diff --git a/lib/utils/config_manager.py b/lib/utils/config_manager.py new file mode 100644 index 00000000..3000e555 --- /dev/null +++ b/lib/utils/config_manager.py @@ -0,0 +1,13 @@ +import json +import os +import streamlit as st + +def save_config(config): + """ + Saves the provided configuration dictionary to a JSON file specified by the environment variable. + """ + try: + with open(os.getenv("ALWRITY_CONFIG"), "w") as config_file: + json.dump(config, config_file, indent=4) + except Exception as e: + st.error(f"An error occurred while saving the configuration: {e}") diff --git a/lib/utils/content_generators.py b/lib/utils/content_generators.py new file mode 100644 index 00000000..566a93be --- /dev/null +++ b/lib/utils/content_generators.py @@ -0,0 +1,67 @@ +import streamlit as st +from lib.utils.alwrity_utils import ( + blog_from_keyword, ai_agents_team, essay_writer, ai_news_writer, ai_seo_tools, + ai_finance_ta_writer, ai_social_writer, do_web_research, competitor_analysis +) +from lib.ai_writers.ai_story_writer.story_writer import story_input_section +from lib.ai_writers.ai_product_description_writer import write_ai_prod_desc +from lib.content_planning_calender.content_planning_agents_alwrity_crew import ai_agents_content_planner + +def ai_writers(): + options = [ + "AI Blog Writer", + "Story Writer", + "Essay writer", + "Write News reports", + "Write Financial TA report", + "AI Product Description Writer", + "AI Copywriter", + "Quit" + ] + choice = st.selectbox("**👇Select a content creation type:**", options, index=0, format_func=lambda x: f"📝 {x}") + + if choice == "AI Blog Writer": + blog_from_keyword() + elif choice == "Write from audio files": + blog_from_audio() + elif choice == "Story Writer": + story_input_section() + elif choice == "Essay writer": + essay_writer() + elif choice == "Write News reports": + ai_news_writer() + elif choice == "Write Financial TA report": + ai_finance_ta_writer() + elif choice == "AI Product Description Writer": + write_ai_prod_desc() + elif choice == "Quit": + st.subheader("Exiting, Getting Lost. But.... I have nowhere to go 🥹🥹") + + +def content_planning_tools(): + st.markdown("""**Alwrity content Ideation & Planning** : Provide few keywords to do comprehensive web research. + Provide few keywords to get Google, Neural, pytrends analysis. Know keywords, blog titles to target. + Generate months long content calendar around given keywords.""") + + options = [ + "Keywords Researcher", + "Competitor Analysis", + "Content Calender Ideator" + ] + choice = st.radio("Select a content planning tool:", options, index=0, format_func=lambda x: f"🔍 {x}") + + if choice == "Keywords Researcher": + do_web_research() + elif choice == "Competitor Analysis": + competitor_analysis() + elif choice == "Content Calender Ideator": + plan_keywords = st.text_input( + "**Enter Your main Keywords to get 2 months content calendar:**", + placeholder="Enter 2-3 main keywords to generate AI content calendar with keyword researched blog titles", + help="The keywords are the ones where you would want to generate 50-60 blogs/articles on." + ) + if st.button("**Ideate Content Calender**"): + if plan_keywords: + ai_agents_content_planner(plan_keywords) + else: + st.error("Come on, really, Enter some keywords to plan on..") diff --git a/lib/utils/environment_utils.py b/lib/utils/environment_utils.py new file mode 100644 index 00000000..23a651e7 --- /dev/null +++ b/lib/utils/environment_utils.py @@ -0,0 +1,5 @@ +import os +from dotenv import load_dotenv + +def load_environment(): + load_dotenv() diff --git a/lib/utils/file_processor.py b/lib/utils/file_processor.py new file mode 100644 index 00000000..5c4737bb --- /dev/null +++ b/lib/utils/file_processor.py @@ -0,0 +1,20 @@ +import os +import base64 +import streamlit as st + +def load_image(image_path): + with open(image_path, "rb") as img_file: + b64_string = base64.b64encode(img_file.read()).decode() + return b64_string + +def read_prompts(file_path="prompt_llm.txt"): + if os.path.exists(file_path): + with open(file_path, "r") as file: + prompts = file.readlines() + return [prompt.strip() for prompt in prompts] + return [] + +def write_prompts(prompts, file_path="prompt_llm.txt"): + with open(file_path, "w") as file: + for prompt in prompts: + file.write(f"{prompt}\n") diff --git a/lib/utils/seo_tools.py b/lib/utils/seo_tools.py new file mode 100644 index 00000000..a2337eac --- /dev/null +++ b/lib/utils/seo_tools.py @@ -0,0 +1,47 @@ +import streamlit as st +from lib.ai_seo_tools.seo_structured_data import ai_structured_data +from lib.ai_seo_tools.content_title_generator import ai_title_generator +from lib.ai_seo_tools.meta_desc_generator import metadesc_generator_main +from lib.ai_seo_tools.image_alt_text_generator import alt_text_gen +from lib.ai_seo_tools.opengraph_generator import og_tag_generator +from lib.ai_seo_tools.optimize_images_for_upload import main_img_optimizer +from lib.ai_seo_tools.google_pagespeed_insights import google_pagespeed_insights +from lib.ai_seo_tools.on_page_seo_analyzer import analyze_onpage_seo +from lib.ai_seo_tools.weburl_seo_checker import url_seo_checker + +def ai_seo_tools(): + """ Collection SEO tools for content creators. """ + options = [ + "Generate Structured Data - Rich Snippet", + "Generate SEO optimized Blog Titles", + "Generate Meta Description for SEO", + "Generate Image Alt Text", + "Generate OpenGraph Tags", + "Optimize/Resize Image", + "Run Google PageSpeed Insights", + "Analyze On Page SEO", + "URL SEO Checker" + ] + + # Using st.radio instead of st.selectbox + choice = st.radio("**👇 Select AI SEO Tool:**", options, index=0, format_func=lambda x: f"📝 {x}") + + # Handle choices based on the selected option + if choice == "Generate Structured Data - Rich Snippet": + ai_structured_data() + elif choice == "Generate Meta Description for SEO": + metadesc_generator_main() + elif choice == "Generate SEO optimized Blog Titles": + ai_title_generator() + elif choice == "Generate Image Alt Text": + alt_text_gen() + elif choice == "Generate OpenGraph Tags": + og_tag_generator() + elif choice == "Optimize/Resize Image": + main_img_optimizer() + elif choice == "Run Google PageSpeed Insights": + google_pagespeed_insights() + elif choice == "Analyze On Page SEO": + analyze_onpage_seo() + elif choice == "URL SEO Checker": + url_seo_checker() diff --git a/lib/utils/ui_setup.py b/lib/utils/ui_setup.py new file mode 100644 index 00000000..f9f51a4c --- /dev/null +++ b/lib/utils/ui_setup.py @@ -0,0 +1,47 @@ +import os +import streamlit as st +from .file_processor import load_image + +def setup_ui(): + """Sets up the Streamlit UI with custom CSS and logo.""" + try: + css_file_path = os.path.join('lib', 'workspace', 'alwrity_ui_styling.css') + with open(css_file_path) as f: + custom_css = f.read() + st.set_page_config(page_title="Alwrity", layout="wide") + st.markdown(f'', unsafe_allow_html=True) + except Exception as err: + st.error(f"Failed in setting up Alwrity Streamlit UI: {err}") + + image_base64 = load_image("lib/workspace/alwrity_logo.png") + st.markdown(f""" +
+ Alwrity Logo + Welcome to Alwrity! +
+ """, unsafe_allow_html=True) + +def setup_tabs(): + """Sets up the main tabs in the Streamlit app.""" + tab1, tab2, tab3, tab4, tab5, tab6 = st.tabs( + ["📅Content Planning", " 📝🤖AI Writers", "🤝🤖Agents Teams", "🛠️🔍AI SEO tools", "📱AI Social Tools", " 💬Ask Alwrity"]) + with tab1: + content_planning_tools() + + with tab2: + ai_writers() + + with tab3: + ai_agents_team() + + with tab4: + ai_seo_tools() + + with tab5: + ai_social_writer() + + with tab6: + st.subheader("Chat with your Data, Chat with any Data.. COMING SOON !") + st.markdown("Create a collection by uploading files (PDF, MD, CSV, etc), or crawl a data source (Websites, more sources coming soon.") + st.markdown("One can ask/chat, summarize and do semantic search over the uploaded data") + # alwrity_chat_docqa() diff --git a/lib/utils/voice_processing.py b/lib/utils/voice_processing.py new file mode 100644 index 00000000..b21ba420 --- /dev/null +++ b/lib/utils/voice_processing.py @@ -0,0 +1,24 @@ +import streamlit as st +from streamlit_mic_recorder import speech_to_text + +@st.cache_data +def record_voice(language="en"): + # https://github.com/B4PT0R/streamlit-mic-recorder?tab=readme-ov-file#example + state = st.session_state + if "text_received" not in state: + state.text_received = [] + + text = speech_to_text( + start_prompt="🎙️Press & Speak🔊", + stop_prompt="🔇Stop Recording🚨", + language=language, + use_container_width=True, + just_once=False, + ) + if text: + state.text_received.append(text) + result = "" + for text in state.text_received: + result += text + state.text_received = [] + return result if result else None