diff --git a/README.md b/README.md index 2c449640..b99e7a13 100644 --- a/README.md +++ b/README.md @@ -20,20 +20,20 @@ If you have ๐Ÿ’ป Laptop + ๐Ÿ›œ Internet + 10 minutes, you will be generating blo --- ### [Getting started for Developers](https://github.com/AJaySi/AI-Writer/wiki/Alwrity--%E2%80%90-Get-started) -` +``` Step1: git clone https://github.com/AJaySi/AI-Writer.git Step2: pip install -r requirements.txt Step3: streamlit run alwrity.py Step4: Visit Alwrity UI in Browser & Start generation AI personalized content. -` +``` --- ### Updating to latest Code: (Existing users) -` +``` 1). Git pull 2). streamlit run alwrity.py 3). pip install -r requirements.txt -` +``` --- **Still stuck, [Open issue here](https://github.com/AJaySi/AI-Writer/issues) & Someone will bail you out. @@ -92,6 +92,7 @@ Use the [main_config](https://github.com/AJaySi/AI-Writer/blob/main/main_config) - [Gemini API](https://gemini.google.com/app): Google powered LLM for natural language processing tasks. - [Ollama](https://ollama.com/) : Local, Privacy focused, LLM provider for research and content generation capabilities. - [CrewAI](https://www.crewai.com/): Collaborative AI agents framework. + - [firecrawl](https://www.firecrawl.dev/): Turn websites into LLM-ready data --- ## Features diff --git a/alwrity.py b/alwrity.py index a3f14aa4..01786543 100644 --- a/alwrity.py +++ b/alwrity.py @@ -33,7 +33,7 @@ def check_api_keys(): missing_keys.append((key, description)) if missing_keys: - st.warning("๐Ÿšจ Some API keys are missing! Please provide them below: ๐Ÿšจ") + st.error("๐Ÿšจ Some API keys are missing! Please provide them below: ๐Ÿšจ") new_keys = [] for key, description in missing_keys: @@ -321,45 +321,43 @@ def main(): if api_keys_valid and llm_environs_valid: # Clear previous messages and display the sidebar configuration sidebar_configuration() - else: - st.error("Error loading Environment variables.") - # Define the tabs - tab1, tab2, tab3, tab4, tab5 = st.tabs( + # Define the tabs + tab1, tab2, tab3, tab4, tab5 = st.tabs( ["AI Writers", "Content Planning", "Agents Content Teams", "Alwrity Brain", "Ask Alwrity"]) - with tab1: - write_blog() + with tab1: + write_blog() - with tab2: - content_planning_tools() + with tab2: + content_planning_tools() - with tab3: - ai_agents_team() + with tab3: + ai_agents_team() - with tab4: - alwrity_brain() + with tab4: + alwrity_brain() - with tab5: - st.info("Chatbot") - 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() + with tab5: + st.info("Chatbot") + 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() - # Sidebar for prompt modification - st.sidebar.title("๐Ÿ“ Modify Prompts") - prompts = read_prompts() + # Sidebar for prompt modification + st.sidebar.title("๐Ÿ“ Modify Prompts") + prompts = read_prompts() - if prompts: - edited_prompts = [] - for i, prompt in enumerate(prompts): - edited_prompt = st.sidebar.text_area(f"Prompt {i+1}", prompt) - edited_prompts.append(edited_prompt) + if prompts: + edited_prompts = [] + for i, prompt in enumerate(prompts): + edited_prompt = st.sidebar.text_area(f"Prompt {i+1}", prompt) + edited_prompts.append(edited_prompt) - if st.sidebar.button("Save Prompts"): - write_prompts(edited_prompts) - st.sidebar.success("Prompts saved successfully!") - else: - st.sidebar.warning("No prompts found in the file.") + if st.sidebar.button("Save Prompts"): + write_prompts(edited_prompts) + st.sidebar.success("Prompts saved successfully!") + else: + st.sidebar.warning("No prompts found in the file.") # Functions for the main options diff --git a/lib/ai_writers/keywords_to_blog_streamlit.py b/lib/ai_writers/keywords_to_blog_streamlit.py index 7816155f..f938c6d5 100644 --- a/lib/ai_writers/keywords_to_blog_streamlit.py +++ b/lib/ai_writers/keywords_to_blog_streamlit.py @@ -1,21 +1,27 @@ import sys import os +import asyncio from textwrap import dedent from pathlib import Path from datetime import datetime import streamlit as st - +from gtts import gTTS +import base64 from dotenv import load_dotenv + +# Load environment variables load_dotenv(Path('../../.env')) +# Logger setup from loguru import logger logger.remove() logger.add(sys.stdout, - colorize=True, - format="{level}|{file}:{line}:{function}| {message}" - ) + colorize=True, + format="{level}|{file}:{line}:{function}| {message}") -from ..ai_web_researcher.gpt_online_researcher import do_google_serp_search,\ - do_tavily_ai_search, do_metaphor_ai_research, do_google_pytrends_analysis +# Import other necessary modules +from ..ai_web_researcher.gpt_online_researcher import ( + do_google_serp_search, do_tavily_ai_search, + do_metaphor_ai_research, do_google_pytrends_analysis) from .blog_from_google_serp import write_blog_google_serp, blog_with_research from ..ai_web_researcher.you_web_reseacher import get_rag_results, search_ydc_index from ..blog_metadata.get_blog_metadata import blog_metadata @@ -23,6 +29,21 @@ from ..blog_postprocessing.save_blog_to_file import save_blog_to_file from ..gpt_providers.text_to_image_generation.main_generate_image_from_prompt import generate_image +# Function to convert text to speech and save as an audio file +def text_to_speech(text, lang='en'): + tts = gTTS(text=text, lang=lang) + tts.save("output.mp3") + return "output.mp3" + + +# Function to get audio file as a downloadable link +def get_audio_file(audio_file): + with open(audio_file, "rb") as file: + data = file.read() + b64_data = base64.b64encode(data).decode() + return f'Download audio file' + + def write_blog_from_keywords(search_keywords, url=None): """ This function will take a blog Topic to first generate sections for it @@ -45,8 +66,8 @@ def write_blog_from_keywords(search_keywords, url=None): status.update(label=f"๐Ÿ›€ Starting Tavily AI research: {search_keywords}") tavily_search_result, t_titles, t_answer = do_tavily_ai_search(search_keywords) - status.update(label=f"๐Ÿ™† Finished Google Search & Tavily AI Search on: {search_keywords}", - state="complete", expanded=False) + status.update(label=f"๐Ÿ™† Finished Google Search & Tavily AI Search on: {search_keywords}", + state="complete", expanded=False) except Exception as err: st.error(f"Failed in web research: {err}") @@ -66,21 +87,21 @@ def write_blog_from_keywords(search_keywords, url=None): # logger.info/check the final blog content. logger.info("######### Draft1: Finished Blog from Google web search: ###########") - + with st.status("Started Writing blog from Tavily Web search..", expanded=True) as status: - # Do Tavily AI research to augument the above blog. + # Do Tavily AI research to augment the above blog. try: - #example_blog_titles.append(t_titles) + # example_blog_titles.append(t_titles) if blog_markdown_str and tavily_search_result: logger.info(f"\n\n######### Blog content after Tavily AI research: ######### \n\n") blog_markdown_str = write_blog_google_serp(search_keywords, tavily_search_result) - status.update(label="Finished Writing Blog From Tavily Results:{blog_markdown_str}", expanded=True) + status.update(label=f"Finished Writing Blog From Tavily Results:{blog_markdown_str}", expanded=True) except Exception as err: logger.error(f"Failed to do Tavily AI research: {err}") status.update(label="๐Ÿ™Ž Generating - Title, Meta Description, Tags, Categories for the content.", expanded=True) try: - blog_title, blog_meta_desc, blog_tags, blog_categories = blog_metadata(blog_markdown_str) + blog_title, blog_meta_desc, blog_tags, blog_categories = asyncio.run(blog_metadata(blog_markdown_str)) except Exception as err: st.error(f"Failed to get blog metadata: {err}") @@ -94,38 +115,21 @@ def write_blog_from_keywords(search_keywords, url=None): except Exception as err: st.warning(f"Failed in Image generation: {err}") - saved_blog_to_file = save_blog_to_file(blog_markdown_str, blog_title, blog_meta_desc, - blog_tags, blog_categories, generated_image_filepath) + saved_blog_to_file = save_blog_to_file(blog_markdown_str, blog_title, blog_meta_desc, + blog_tags, blog_categories, generated_image_filepath) status.update(label=f"Saved the content in this file: {saved_blog_to_file}") logger.info(f"\n\n --------- Finished writing Blog for : {search_keywords} -------------- \n") - - # Render the result on streamlit UI - st.image(generated_image_filepath) - st.markdown(f"{blog_markdown_str}") - status.update(label=f"Finished, Review & Use your Original Content Below: {saved_blog_to_file}", state="complete") - # Display options below the content - col1, col2, col3, col4, col5 = st.columns(5) - if col1.button('Copy'): - pyperclip.copy(blog_markdown_str) - st.success("Text copied to clipboard!") - - if col2.button('Rephrase'): - rephrased_text = rephrase_text(blog_markdown_str) - st.markdown(rephrased_text) - - if col3.button('Change Tone'): - tone = st.selectbox("Select Tone", ["Formal", "Casual", "Professional"]) - if st.button("Apply Tone"): - toned_text = change_tone(blog_markdown_str, tone) - st.markdown(toned_text) - - if col4.button('Make Shorter'): - shorter_text = make_shorter(blog_markdown_str) - st.markdown(shorter_text) - - if col5.button('Translate'): - language = st.selectbox("Select Language", ["Spanish", "French", "German"]) - if st.button("Translate"): - translated_text = translate_text(blog_markdown_str, language) - st.markdown(translated_text) + # Render the result on streamlit UI + if generated_image_filepath: + st.image(generated_image_filepath) + st.markdown(f"{blog_markdown_str}") + status.update(label=f"Finished, Review & Use your Original Content Below: {saved_blog_to_file}", + state="complete") + + # Passing the text and language to the engine, here we have marked slow=False. Which tells + # the module that the converted audio should have a high speed + tts = gTTS(text=blog_markdown_str, lang='en', slow=False) + # Saving the converted audio in a mp3 file + tts.save("delete_me.mp3") + st.audio("delete_me.mp3") diff --git a/lib/ai_writers/long_form_ai_writer.py b/lib/ai_writers/long_form_ai_writer.py index 8666bf5e..36019db4 100644 --- a/lib/ai_writers/long_form_ai_writer.py +++ b/lib/ai_writers/long_form_ai_writer.py @@ -124,15 +124,15 @@ def long_form_generator(content_keywords): # Configure generative AI load_dotenv(Path('../.env')) generation_config = { - "temperature": 0.6, + "temperature": 0.7, "top_p": 1, "max_output_tokens": 8096, } genai.configure(api_key=os.getenv('GEMINI_API_KEY')) # Initialize the generative model - #model = genai.GenerativeModel('gemini-pro', generation_config=generation_config) - model_pro = genai.GenerativeModel('gemini-1.5-flash', generation_config=generation_config) + model = genai.GenerativeModel('gemini-1.5-flash', generation_config=generation_config) + model_pro = genai.GenerativeModel('gemini-pro', generation_config=generation_config) # Do SERP web research for given keywords to generate title and outline. web_research_result, g_titles = do_google_serp_search(content_keywords) @@ -203,14 +203,14 @@ def long_form_generator(content_keywords): logger.info(f"Writing in progress... Current draft length: {len(draft)} characters") status.update(label=f"Writing in progress... Current draft length: {len(draft)} characters") search_terms = f""" - I will provide you with blog outline, your task is to read the outline & return 8 google search keywords. + I will provide you with content outline below, your task is to read the outline & return 8 google search keywords. Your response will be used to do web research for writing on the given outline. Do not explain your response, provide 8 google search sentences encompassing the given content outline. - Provide the search term results as comma separated values.\n\n + Important: Provide the search term results as comma separated values.\n\n Content Outline:\n '{content_outline}' """ - search_words = generate_with_retry(model_pro, search_terms).text + search_words = generate_with_retry(model, search_terms).text status.update(label=f"Search terms from written draft: {search_words}") while 'IAMDONE' not in continuation: @@ -218,6 +218,7 @@ def long_form_generator(content_keywords): str_list = re.split(r',\s*', search_words) # Strip quotes from each element str_list = [s.strip('\'"') for s in str_list] + for search_term in str_list: web_research_result, m_titles, t_titles = do_tavily_ai_search(search_term, max_results=5) try: diff --git a/lib/ai_writers/web_url_ai_writer.py b/lib/ai_writers/web_url_ai_writer.py index 190b2e51..86c5e846 100644 --- a/lib/ai_writers/web_url_ai_writer.py +++ b/lib/ai_writers/web_url_ai_writer.py @@ -17,7 +17,7 @@ logger.add(sys.stdout, ) from ..ai_web_researcher.firecrawl_web_crawler import scrape_url -from ..blog_metadata.get_blog_metadata import blog_metadata +from ..blog_metadata.get_blog_metadata import blog_metadata, run_async from ..blog_postprocessing.save_blog_to_file import save_blog_to_file from ..gpt_providers.text_to_image_generation.main_generate_image_from_prompt import generate_image from ..gpt_providers.text_generation.main_text_generation import llm_text_gen @@ -31,7 +31,11 @@ def blog_from_url(weburl): # Use to store the blog in a string, to save in a *.md file. blog_markdown_str = None tavily_search_result = None - example_blog_titles = [] + # Initializing the variables + blog_title = None + blog_meta_desc = None + blog_tags = None + blog_categories = None logger.info(f"Researching and Writing Blog on: {weburl}") with st.status("Started Writing..", expanded=True) as status: @@ -39,12 +43,12 @@ def blog_from_url(weburl): status.update(label=f"Researching and Writing Blog on: {weburl}") try: scraped_text = scrape_url(weburl) - logger.info(scraped_text) + #logger.info(scraped_text) except Exception as err: st.error(f"Failed to scrape web page from url-{weburl} - Error: {err}") logger.error(f"Failed in web research: {err}") st.stop() - status.update(label="Successfully Scraped/Fetched url: {weburl}", expanded=False, state="complete") + status.update(label=f"Successfully Scraped/Fetched url: {weburl}", expanded=False, state="complete") with st.status(f"Started Writing blog from {weburl}..", expanded=True) as status: # Do Tavily AI research to augument the above blog. @@ -58,7 +62,7 @@ def blog_from_url(weburl): try: status.update(label="๐Ÿ™Ž Generating - Title, Meta Description, Tags, Categories for the content.") - blog_title, blog_meta_desc, blog_tags, blog_categories = blog_metadata(blog_markdown_str) + blog_title, blog_meta_desc, blog_tags, blog_categories = run_async(blog_metadata(blog_markdown_str)) except Exception as err: st.error(f"Failed to get blog metadata: {err}") @@ -71,8 +75,11 @@ def blog_from_url(weburl): saved_blog_to_file = save_blog_to_file(blog_markdown_str, blog_title, blog_meta_desc, blog_tags, blog_categories, generated_image_filepath) status.update(label=f"Saved the content in this file: {saved_blog_to_file}") + logger.info(f"\n\n --------- Finished writing Blog for : {weburl} -------------- \n") - st.image(generated_image_filepath) + if generated_image_filepath: + st.image(generated_image_filepath) + st.markdown(f"{blog_markdown_str}") status.update(label=f"Finished, Review & Use your Original Content Below: {saved_blog_to_file}", state="complete") diff --git a/lib/blog_metadata/get_blog_metadata.py b/lib/blog_metadata/get_blog_metadata.py index fca7d99b..4e0b1e25 100644 --- a/lib/blog_metadata/get_blog_metadata.py +++ b/lib/blog_metadata/get_blog_metadata.py @@ -2,7 +2,7 @@ import sys import streamlit as st from loguru import logger import random -import time +import asyncio logger.remove() logger.add(sys.stdout, @@ -12,7 +12,7 @@ logger.add(sys.stdout, from ..gpt_providers.text_generation.main_text_generation import llm_text_gen -def blog_metadata(blog_article): +async def blog_metadata(blog_article): """ Common function to get blog metadata """ logger.info(f"Generating Content MetaData\n") @@ -20,22 +20,22 @@ def blog_metadata(blog_article): total_steps = 4 # Step 1: Generate blog title - time.sleep(random.uniform(1, 3)) + await asyncio.sleep(random.uniform(1, 3)) blog_title = generate_blog_title(blog_article) progress_bar.progress(1 / total_steps) # Step 2: Generate blog meta description - time.sleep(random.uniform(1, 3)) + await asyncio.sleep(random.uniform(1, 3)) blog_meta_desc = generate_blog_description(blog_article) progress_bar.progress(2 / total_steps) # Step 3: Generate blog tags - time.sleep(random.uniform(1, 3)) + await asyncio.sleep(random.uniform(1, 3)) blog_tags = get_blog_tags(blog_article) progress_bar.progress(3 / total_steps) # Step 4: Generate blog categories - time.sleep(random.uniform(1, 3)) + await asyncio.sleep(random.uniform(1, 3)) blog_categories = get_blog_categories(blog_article) progress_bar.progress(4 / total_steps) @@ -117,3 +117,10 @@ def get_blog_tags(blog_article): logger.error(f"Failed to get response from LLM: {err}") raise err +# Helper function to run the asyncio event loop within Streamlit +def run_async(coro): + loop = asyncio.new_event_loop() + asyncio.set_event_loop(loop) + result = loop.run_until_complete(coro) + loop.close() + return result diff --git a/lib/utils/alwrity_utils.py b/lib/utils/alwrity_utils.py index 70794b4b..016162f5 100644 --- a/lib/utils/alwrity_utils.py +++ b/lib/utils/alwrity_utils.py @@ -2,6 +2,8 @@ import os import re import sys import streamlit as st +from streamlit_mic_recorder import speech_to_text + import tempfile from pathlib import Path import configparser @@ -10,7 +12,6 @@ import uuid from PIL import Image from PyPDF2 import PdfReader from docx import Document - from loguru import logger logger.remove() logger.add(sys.stdout, @@ -19,7 +20,6 @@ logger.add(sys.stdout, ) -from rich import print 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 @@ -41,11 +41,40 @@ from lib.gpt_providers.text_to_image_generation.main_generate_image_from_prompt from lib.content_planning_calender.content_planning_agents_alwrity_crew import ai_agents_planner +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="๐ŸŽ™๏ธRecord๐Ÿ”Š", + 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})') return youtube_regex.match(text) + def is_web_link(text): if text is not None: web_regex = re.compile(r'(https?://)?(www\.)?[-a-zA-Z0-9@:%._\+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b([-a-zA-Z0-9()@:%_\+.~#?&//=]*)') @@ -87,10 +116,11 @@ def process_input(input_text, uploaded_file): return "video_file" return None + def blog_from_keyword(): """ Input blog keywords, research and write a factual blog.""" st.title("Blog Content Writer") - col1, col2 = st.columns([2, 1.5]) + col1, col2, col3 = st.columns([2, 1.5, 0.5]) with col1: user_input = st.text_area('**๐Ÿ‘‡Enter Keywords/Title/YouTube Link/Web URLs**', help='Provide keywords, titles, YouTube links, or web URLs to generate content.', @@ -104,6 +134,10 @@ def blog_from_keyword(): uploaded_file = st.file_uploader("**๐Ÿ‘‡Attach files (Audio, Video, Image, Document)**", type=["txt", "pdf", "docx", "jpg", "jpeg", "png", "mp3", "wav", "mp4", "mkv", "avi"], help='Attach files such as audio, video, images, or documents.') + with col3: + user_input = record_voice() + if user_input: + st.info(user_input) temp_file_path = None if uploaded_file is not None: diff --git a/lib/utils/read_main_config_params.py b/lib/utils/read_main_config_params.py index 3d9eb89b..f196b408 100644 --- a/lib/utils/read_main_config_params.py +++ b/lib/utils/read_main_config_params.py @@ -1,6 +1,14 @@ import os +import sys import json from pathlib import Path +from loguru import logger +logger.remove() +logger.add(sys.stdout, + colorize=True, + format="{level}|{file}:{line}:{function}| {message}" + ) + def read_return_config_section(config_section): """ read_return_config_section diff --git a/lib/workspace/alwrity_ai_writer.png b/lib/workspace/alwrity_ai_writer.png index edb9fb12..c99e1343 100644 Binary files a/lib/workspace/alwrity_ai_writer.png and b/lib/workspace/alwrity_ai_writer.png differ diff --git a/lib/workspace/alwrity_prompts/long_form_ai_writer.prompts b/lib/workspace/alwrity_prompts/long_form_ai_writer.prompts index d7d7f0ae..fc81ea24 100644 --- a/lib/workspace/alwrity_prompts/long_form_ai_writer.prompts +++ b/lib/workspace/alwrity_prompts/long_form_ai_writer.prompts @@ -1,13 +1,27 @@ writing_guidelines: | - As an expert content writer and web researcher, write highly detailed long form, {content_type} content on {content_keywords}. - Follow these writing guidelines: - 1. You must Write in {content_language} language. - 2. Ensure your content appeals to the target audience of {target_audience}. - 3. Maintain a consistent tone of {content_tone} throughout. - 4. Use simple {content_language} words to appeal to all readers. - 5. Format your content using {output_format}. - 6. Avoid words like: Unleash, ultimate, uncover, discover, elevate, revolutionizing, unveiling, harnessing, dive, delve into, embrace. - Remember, your main goal is to write as much as you can. If you get through the story too fast, that is bad. Expand, never summarize. + + Writing Guidelines: + As an expert {content_type} content writer and web researcher on {content_keywords}, follow these writing guidelines: + + Language: Write in the {content_language} language. + Audience: Ensure your content appeals to the target audience of {target_audience}. + Tone: Maintain a consistent tone of {content_tone} throughout. + Simplicity: Use simple {content_language} words to appeal to all readers. + Formatting: Format your content using {output_format}. + Word Choice: Avoid words like: unleash, ultimate, uncover, discover, elevate, revolutionizing, unveiling, harnessing, dive, delve into, embrace. + + Immerse yourself fully in the topic you're exploring. Use vivid descriptions to captivate your readers and bring your subject to life. + Develop your ideas thoroughlyโ€”let their nuances, challenges, and intricacies unfold naturally. + Follow the structure of your outline, but don't feel constrained by it. Allow your blog post to evolve as you write. + + Incorporate rich imagery, sensory details, and evocative language to make your content engaging and relatable. + Introduce elements subtly that can grow into deeper discussions, related topics, or additional insights later in the post. + Keep your readers intrigued by not resolving everything too quickly. + Plant the seeds of subtopics or potential shifts in perspective that can be expanded upon in future posts. + + Remember, your main goal is to provide valuable, in-depth content. If you rush through your topic, it will leave readers wanting more. + Expand your ideas, never summarize. Write as much as you can, ensuring that your content is thorough and comprehensive. + content_title: | @@ -51,7 +65,7 @@ starting_prompt: | First, silently review the content outline and title. Consider how to begin writing your content. Take your time. Start by writing the very beginning of the outline. You are not expected to finish the entire content now. Your writing should be detailed, only scratching the surface of the first bullet of your outline. - Try to write AT MINIMUM 1000 WORDS and MAXIMUM 2000 WORDS. + Try to write AT MINIMUM 500 WORDS. """{{writing_guidelines}}""" @@ -60,30 +74,29 @@ starting_prompt: | continuation_prompt: | As an expert {content_language} content writer and web researcher specializing in SEO-optimized content, continue writing the content for the given title and outline. - The Title of the content is: - """{{content_title}}""" + Title of the Content: + {{content_title}} - The content Outline is: - """{{content_outline}}""" + Content Outline: + {{content_outline}} - Relevant web research results to use: - """{{web_research_result}}""" + Relevant Web Research Results to Use: + {{web_research_result}} - You've begun to immerse yourself in this world, and the words are flowing. - Here's what you've written so far: - """{{content_text}}""" + You've begun to immerse yourself in this subject, and the words are flowing. Here's what you've written so far: + {{content_text}} + ===== - =========== + First, silently review the content outline and what you've written so far. + Take your time to understand the flow and context. + Identify the next section of your outline to write about. + It is important to continue from where you left off. - First, silently review the content outline and what you've written so far. Take your time. - Identify what the single next part of your outline you should write. - Important to Continue from where you left off. + Your task is to continue writing from where you left off and cover the next part of the outline. + You are not expected to finish the entire content now. + Your writing should be detailed enough to thoroughly explore the next part of your outline. + Aim to write at least 500 words. However, only once the entire content is completely finished, write IAMDONE. + Remember, do not write the whole outline sections right now. - Your task is to continue where you left off and write the next part of the outline. - You are not expected to finish the whole content now. Your writing should be - detailed enough that you are only scratching the surface of the next part of - your outline. Try to write AT MINIMUM 1000 WORDS. However, only once the whole outline content - is COMPLETELY finished, write IAMDONE. Remember, do NOT write a whole outline sections right now. - - """{{writing_guidelines}}""" + {writing_guidelines} diff --git a/lib/workspace/keyword_blog.gif b/lib/workspace/keyword_blog.gif index 89946c76..5155603c 100644 Binary files a/lib/workspace/keyword_blog.gif and b/lib/workspace/keyword_blog.gif differ diff --git a/requirements.txt b/requirements.txt index d9dd4877..67848e85 100644 --- a/requirements.txt +++ b/requirements.txt @@ -33,3 +33,5 @@ streamlit yfinance pandas_ta firecrawl-py +gTTS +streamlit-mic-recorder