261 lines
10 KiB
Python
261 lines
10 KiB
Python
#####################################################
|
|
#
|
|
# Alwrity, AI Long form writer - Writing_with_Prompt_Chaining
|
|
# and generative AI.
|
|
#
|
|
#####################################################
|
|
|
|
import os
|
|
import time #iwish
|
|
import sys
|
|
from pathlib import Path
|
|
from dotenv import load_dotenv
|
|
from google.api_core import retry
|
|
import google.generativeai as genai
|
|
from pprint import pprint
|
|
from textwrap import dedent
|
|
|
|
from loguru import logger
|
|
logger.remove()
|
|
logger.add(sys.stdout,
|
|
colorize=True,
|
|
format="<level>{level}</level>|<green>{file}:{line}:{function}</green>| {message}"
|
|
)
|
|
|
|
from ..utils.read_main_config_params import read_return_config_section
|
|
from ..ai_web_researcher.gpt_online_researcher import do_google_serp_search
|
|
from ..ai_web_researcher.gpt_online_researcher import do_google_serp_search, do_tavily_ai_search
|
|
from ..blog_metadata.get_blog_metadata import blog_metadata
|
|
from ..blog_postprocessing.save_blog_to_file import save_blog_to_file
|
|
|
|
|
|
def generate_with_retry(model, prompt):
|
|
"""
|
|
Generates content from the model with retry handling for errors.
|
|
|
|
Parameters:
|
|
model (GenerativeModel): The generative model to use for content generation.
|
|
prompt (str): The prompt to generate content from.
|
|
|
|
Returns:
|
|
str: The generated content.
|
|
"""
|
|
try:
|
|
# FIXME: Need a progress bar here.
|
|
return model.generate_content(prompt, request_options={'retry':retry.Retry()})
|
|
except Exception as e:
|
|
logger.error(f"Error generating content: {e}")
|
|
return ""
|
|
|
|
|
|
def long_form_generator(content_keywords):
|
|
"""
|
|
Write long form content using prompt chaining and iterative generation.
|
|
Parameters:
|
|
"""
|
|
# Read the main_config to define tone, character, personality of the content to be generated.
|
|
try:
|
|
logger.info(f"Starting to write content on {content_keywords}.")
|
|
# Define persona and writing guidelines
|
|
content_tone, target_audience, content_type, content_language, output_format = read_return_config_section('blog_characteristics')
|
|
except Exception as err:
|
|
logger.error(f"Failed to Read config params from main_config: {err}")
|
|
return
|
|
|
|
writing_guidelines = f'''\
|
|
Writing Guidelines
|
|
|
|
As an expert Content writer and web researcher, demostrate your world class {content_type} content writing skills.
|
|
|
|
Follow the below writing guidelines for writing your content:
|
|
1). You must write in {content_language} language.
|
|
2). Your content must appeal to target audience of {target_audience}.
|
|
3). The tone of your content must be consistent for {content_tone}.
|
|
4). Always ensure orignality and human-like content.
|
|
5). Use simple {content_language} words, to appeal to all readers.
|
|
6). Your content must be well formatted using {output_format} language.
|
|
7). Do not use words like: Unleash, ultimate, Uncover, Discover, Elevate, Revolutionizing, Unveiling, Harnessing, Dive, Delve into, Embrace.
|
|
8). Important: Ensure the total content length is between 5000-7000 words.
|
|
|
|
Remember, your main goal is to write as much as you can. If you get through the content too fast, that is bad.
|
|
Expand, never summarize.
|
|
'''
|
|
|
|
|
|
# Generate prompts
|
|
content_title = f'''\
|
|
As an expert {content_language} content writer, specilizing in SEO writing.
|
|
Your task is to write a blog title following guidelines below:
|
|
|
|
1). Write a blog title for given keywords {content_keywords}.
|
|
2). The title should appeal to audience level of {target_audience}.
|
|
3). Review the given web research result for {content_keywords}. Your title should compete against them.
|
|
4). Do not use words like: Unleash, ultimate, Uncover, Discover, Elevate, Revolutionizing, Unveiling, Harnessing, Dive, Delve into, Embrace.
|
|
|
|
Web research Result:
|
|
|
|
"""{{web_research_result}}"""
|
|
|
|
'''
|
|
|
|
content_outline = f'''\
|
|
As an expert {content_language} content outliner, specilizing in SEO optimised content.
|
|
The title of my content is {{content_title}}. I will provide you with its web research results, as context.
|
|
Your task is write a detailed content outline for the given 'Title', based on the given context.
|
|
|
|
Instructions:
|
|
1). Make sure the outline includes most of the topics from the below given web research results, as context.
|
|
2). The outline should appeal to audience of {target_audience}.
|
|
3). Important: Ensure the total content length is between 5000-7000 words.
|
|
\n
|
|
web research results is:
|
|
|
|
"""{{web_research_result}}"""
|
|
|
|
'''
|
|
|
|
starting_prompt = f'''\
|
|
As an expert {content_language} content writer, specilizing in writing SEO optimised content.
|
|
|
|
Your Content title is:
|
|
|
|
"""{{content_title}}"""
|
|
|
|
The outline of the content is:
|
|
|
|
"""{{content_outline}}"""
|
|
|
|
First, silently review the given content outline and the title. Consider how to start writing your content.
|
|
Start to write the very beginning 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 first bullet of your outline.
|
|
Try to write AT MINIMUM 400 WORDS.
|
|
|
|
"""{writing_guidelines}"""
|
|
'''
|
|
|
|
continuation_prompt = f'''\
|
|
As an expert {content_language} content writer & web researcher, specilizing in writing SEO optimised content.
|
|
|
|
Your Content title is:
|
|
|
|
"""{{content_title}}"""
|
|
|
|
The outline of the content is:
|
|
|
|
"""{{content_outline}}"""
|
|
|
|
Relevant web research results:
|
|
|
|
"""{{web_research_result}}"""
|
|
|
|
============\n
|
|
|
|
You've begun to write the content and continue to do so.
|
|
Here's what you've written so far:
|
|
|
|
"""{{content_text}}"""
|
|
|
|
=====
|
|
|
|
First, take your time and silently review the content outline and what you have written so far.
|
|
Identify what the single next part of your outline you should write.
|
|
|
|
Your task is to continue where you left off and write only the next parts of given outline.
|
|
You are not expected to finish the whole content now.
|
|
Try to write AT MINIMUM 400 WORDS. However, only once the content
|
|
is COMPLETELY finished, write IAMDONE. Remember, do NOT write a whole sections right now.
|
|
Important: Ensure the total content length is between 5000-7000 words.
|
|
\n
|
|
{writing_guidelines}
|
|
'''
|
|
|
|
# Configure generative AI
|
|
load_dotenv(Path('../.env'))
|
|
genai.configure(api_key=os.getenv('GEMINI_API_KEY'))
|
|
# Initialize the generative model
|
|
model = genai.GenerativeModel('gemini-pro')
|
|
model_pro = genai.GenerativeModel('gemini-1.5-flash-latest')
|
|
# Do SERP web research for given keywords to generate title and outline.
|
|
web_research_result, g_titles = do_google_serp_search(content_keywords)
|
|
# Generate prompts
|
|
try:
|
|
content_title = generate_with_retry(model_pro, content_title.format(web_research_result=web_research_result)).text
|
|
logger.info(f"The title of the content is: {content_title}")
|
|
except Exception as err:
|
|
logger.error(f"Content title Generation Error: {err}")
|
|
return
|
|
|
|
try:
|
|
content_outline = generate_with_retry(model_pro,
|
|
content_outline.format(content_title=content_title, web_research_result=web_research_result)).text
|
|
logger.info(f"The content Outline is: {content_outline}\n\n")
|
|
except Exception as err:
|
|
logger.error(f"Failed to generate content outline: {err}")
|
|
|
|
try:
|
|
starting_draft = generate_with_retry(model_pro,
|
|
starting_prompt.format(content_title=content_title, content_outline=content_outline)).text
|
|
except Exception as err:
|
|
logger.error(f"Failed to Generate Starting draft: {err}")
|
|
return
|
|
|
|
try:
|
|
logger.info(f"Starting to write on the outline introduction.")
|
|
draft = starting_draft
|
|
continuation = generate_with_retry(model,
|
|
continuation_prompt.format(content_title=content_title,
|
|
content_outline=content_outline, content_text=draft, web_research_result=web_research_result)).text
|
|
except Exception as err:
|
|
logger.error(f"Failed to write the initial draft: {err}")
|
|
|
|
# Add the continuation to the initial draft, keep building the story until we see 'IAMDONE'
|
|
try:
|
|
draft += '\n\n' + continuation
|
|
except Exception as err:
|
|
logger.error(f"Failed as: {err} and {continuation}")
|
|
try:
|
|
logger.info("Do web research with Tavily to provide context for content creation.")
|
|
# Do Metaphor/Exa AI search.
|
|
table_data = []
|
|
web_research_result, m_titles, t_titles = do_tavily_ai_search(content_keywords)
|
|
for item in web_research_result.get("results"):
|
|
title = item.get("title", "")
|
|
snippet = item.get("content", "")
|
|
table_data.append([title, snippet])
|
|
web_research_result = table_data
|
|
except Exception as err:
|
|
logger.error(f"Failed to do Tavily AI search: {err}")
|
|
return
|
|
|
|
logger.info(f"Writing in progress... Current draft length: {len(draft)} characters")
|
|
while 'IAMDONE' not in continuation:
|
|
try:
|
|
continuation = generate_with_retry(model,
|
|
continuation_prompt.format(content_title=content_title,
|
|
content_outline=content_outline, content_text=draft, web_research_result=web_research_result)).text
|
|
draft += '\n\n' + continuation
|
|
logger.info(f"Writing in progress... Current draft length: {len(draft)} characters")
|
|
except Exception as err:
|
|
logger.error(f"Failed to continually write the Essay: {err}")
|
|
return
|
|
|
|
# Remove 'IAMDONE' and print the final story
|
|
final = draft.replace('IAMDONE', '').strip()
|
|
|
|
blog_title, blog_meta_desc, blog_tags, blog_categories = blog_metadata(final,
|
|
content_keywords, m_titles)
|
|
|
|
generated_image_filepath = None
|
|
# TBD: Save the blog content as a .md file. Markdown or HTML ?
|
|
save_blog_to_file(final, blog_title, blog_meta_desc, blog_tags, blog_categories, generated_image_filepath)
|
|
|
|
blog_frontmatter = f"""
|
|
---
|
|
title: {blog_title}
|
|
categories: [{blog_categories}]
|
|
tags: [{blog_tags}]
|
|
Meta description: {blog_meta_desc.replace(":", "-")}
|
|
---"""
|
|
logger.info(f"\n{blog_frontmatter}{final}\n\n")
|
|
logger.info(f"\n\n ################ Finished writing Blog for : {content_keywords} #################### \n")
|