From 23b3c7f6e0f01a338dbcffdb90d76c7d2df87b8b Mon Sep 17 00:00:00 2001 From: AjaySi Date: Sun, 7 Apr 2024 20:47:49 +0530 Subject: [PATCH] Alwrity - WIP - main_config --- .../gpt_competitor_analysis.py | 31 ++-- .../gpt_online_researcher.py | 23 --- .../gpt_summarize_web_content.py | 31 +--- lib/ai_writers/blog_from_google_serp.py | 41 ++--- lib/ai_writers/combine_blog_and_keywords.py | 31 +--- lib/ai_writers/combine_research_and_blog.py | 46 ++---- lib/ai_writers/keywords_to_blog.py | 7 +- lib/blog_metadata/get_blog_category.py | 24 +-- lib/blog_metadata/get_blog_meta_desc.py | 25 +-- lib/blog_metadata/get_blog_title.py | 29 +--- lib/blog_metadata/get_tags.py | 24 +-- lib/blog_postprocessing/blog_proof_reader.py | 25 +-- lib/blog_postprocessing/humanize_blog.py | 31 ++-- lib/gpt_providers/__init__.py | 0 .../stt_audio_blog.py | 0 lib/gpt_providers/gemini_pro_text.py | 40 ----- .../gemini_image_details.py | 0 .../openai_vision_img_details.py} | 0 .../text_generation/gemini_pro_text.py | 46 ++++++ .../text_generation/main_text_generation.py | 151 ++++++++++++++++++ .../mistral_chat_completion.py | 0 .../{ => text_generation}/openai_text_gen.py | 30 ++-- main_config | 5 +- 23 files changed, 313 insertions(+), 327 deletions(-) delete mode 100644 lib/gpt_providers/__init__.py rename lib/gpt_providers/{ => audio_to_text_generation}/stt_audio_blog.py (100%) delete mode 100644 lib/gpt_providers/gemini_pro_text.py rename lib/gpt_providers/{ => image_to_text_gen}/gemini_image_details.py (100%) rename lib/gpt_providers/{gpt_vision_img_details.py => image_to_text_gen/openai_vision_img_details.py} (100%) create mode 100644 lib/gpt_providers/text_generation/gemini_pro_text.py create mode 100644 lib/gpt_providers/text_generation/main_text_generation.py rename lib/gpt_providers/{ => text_generation}/mistral_chat_completion.py (100%) rename lib/gpt_providers/{ => text_generation}/openai_text_gen.py (77%) diff --git a/lib/ai_web_researcher/gpt_competitor_analysis.py b/lib/ai_web_researcher/gpt_competitor_analysis.py index eb1d6af4..ab847de1 100644 --- a/lib/ai_web_researcher/gpt_competitor_analysis.py +++ b/lib/ai_web_researcher/gpt_competitor_analysis.py @@ -1,8 +1,5 @@ import sys -from ..gpt_providers.openai_text_gen import openai_chatgpt -from ..gpt_providers.gemini_pro_text import gemini_text_response - from loguru import logger logger.remove() logger.add(sys.stdout, @@ -10,14 +7,13 @@ logger.add(sys.stdout, format="{level}|{file}:{line}:{function}| {message}" ) +from ..gpt_providers.text_generation.main_text_generation import llm_text_gen + def summarize_competitor_content(research_content, gpt_providers="openai"): """Combine the given online research and gpt blog content""" - prompt = f""" Web page content: {research_content} """ - - if 'gemini' in gpt_providers: - prompt = f"""You are a helpful assistant writing a research report about a company. I will provide you with company details. + prompt = f"""You are a helpful assistant writing a research report about a company. I will provide you with company details. Summarize the given company details into multiple paragraphs. Be extremely concise, professional, and factual as possible. The first paragraph should be an introduction and summary of the company. @@ -25,17 +21,10 @@ def summarize_competitor_content(research_content, gpt_providers="openai"): The third paragraph should be on their pricing model. Include a conclusion, summarizing your research about the given company details. Company details: '{research_content}'""" - try: - response = gemini_text_response(prompt) - return response - except Exception as err: - logger.error(f"Failed to get response from gemini: {err}") - raise err - elif 'openai' in gpt_providers: - try: - logger.info("Calling OpenAI LLM.") - response = openai_chatgpt(prompt) - return response - except Exception as err: - logger.error(f"failed to get response from Openai: {err}") - raise err + + try: + response = gemini_text_response(prompt) + return response + except Exception as err: + logger.error(f"Failed to get response from LLM: {err}") + raise err diff --git a/lib/ai_web_researcher/gpt_online_researcher.py b/lib/ai_web_researcher/gpt_online_researcher.py index 21bd2277..57e7e71d 100644 --- a/lib/ai_web_researcher/gpt_online_researcher.py +++ b/lib/ai_web_researcher/gpt_online_researcher.py @@ -11,7 +11,6 @@ import sys from typing import List, NamedTuple from datetime import datetime -from ..gpt_providers.gemini_pro_text import gemini_text_response from .tavily_ai_search import get_tavilyai_results from .metaphor_basic_neural_web_search import metaphor_find_similar, metaphor_search_articles from .google_serp_search import google_search @@ -154,25 +153,3 @@ def tavily_extract_information(json_data, keyword): return json_data['follow_up_questions'] else: return f"Invalid keyword: {keyword}" - - -def compete_organic_results(query, report, organic_results): - """ Given a blog content and google search organinc results, create a new blog to compete against them.""" - prompt = f""" As an SEO expert and copywriter, I will provide you with my blog content on topic '{query}', and - Top google search results. - Your task is to rewrite the given blog to make it compete against top position results. - Make sure, the new blog has high probability of ranking highest against given organic search result competitors. - Modify the given blog content following best SEO practises. - Make sure the blog is original, unique and highly readable. - Remember, Maintain and adopt the formatting, structure, style and tone of the provided blog content. - Include relevant emojis in your final blog for visual appeal. Use it sparingly. - Your response should be well-structured, objective, and critically acclaimed blog article based on provided texts. - - Remember, your goal is to create a detailed blog article that will compete against given organic result competitors. - Do not provide explanations, suggestions for your response, reply only with your final response. - Take your time in crafting your content, do not rush to give the response. - Blog Content: '{report}'\n - Organic Search result: '{organic_results}' - """ - report = gemini_text_response(prompt) - return report diff --git a/lib/ai_web_researcher/gpt_summarize_web_content.py b/lib/ai_web_researcher/gpt_summarize_web_content.py index aff9b7ef..e5bccec3 100644 --- a/lib/ai_web_researcher/gpt_summarize_web_content.py +++ b/lib/ai_web_researcher/gpt_summarize_web_content.py @@ -1,38 +1,23 @@ import sys -from ..gpt_providers.openai_text_gen import openai_chatgpt -from ..gpt_providers.gemini_pro_text import gemini_text_response - from loguru import logger logger.remove() logger.add(sys.stdout, colorize=True, format="{level}|{file}:{line}:{function}| {message}" ) +from ..gpt_providers.text_generation.main_text_generation import llm_text_gen def summarize_web_content(page_content, gpt_providers="openai"): """Combine the given online research and gpt blog content""" - prompt = f""" - Web page content: {page_content} - """ - - if 'gemini' in gpt_providers: - prompt = f"""You are a helpful assistant that briefly summarizes the content of a webpage. + prompt = f"""You are a helpful assistant that briefly summarizes the content of a webpage. Summarize the given web page content below. Web page content: '{page_content}'""" - try: - response = gemini_text_response(prompt) - return response - except Exception as err: - logger.error(f"Failed to get response from gemini: {err}") - raise err - elif 'openai' in gpt_providers: - try: - logger.info("Calling OpenAI LLM.") - response = openai_chatgpt(prompt) - return response - except Exception as err: - logger.error(f"failed to get response from Openai: {err}") - raise err + try: + response = llm_text_gen(prompt) + return response + except Exception as err: + logger.error(f"summarize_web_content: Failed to get response from LLM: {err}") + raise err diff --git a/lib/ai_writers/blog_from_google_serp.py b/lib/ai_writers/blog_from_google_serp.py index 895d4551..31eb0f3a 100644 --- a/lib/ai_writers/blog_from_google_serp.py +++ b/lib/ai_writers/blog_from_google_serp.py @@ -1,12 +1,6 @@ import os import sys import json -from pathlib import Path -from dotenv import load_dotenv -load_dotenv(Path('../.env')) - -from ..gpt_providers.openai_text_gen import openai_chatgpt -from ..gpt_providers.gemini_pro_text import gemini_text_response from loguru import logger logger.remove() @@ -15,41 +9,34 @@ logger.add(sys.stdout, format="{level}|{file}:{line}:{function}| {message}" ) +from ..gpt_providers.text_generation.main_text_generation import llm_text_gen + # FIXME: Provide num_blogs, num_faqs as inputs. def write_blog_google_serp(search_keyword, search_results): """Combine the given online research and gpt blog content""" - gpt_providers = os.environ["GPT_PROVIDER"] prompt = f""" As a SEO expert and content writer, I will provide you with my 'web research keywords' and its 'google search result'. - Your task is to write an original, conversational, SEO optimized blog and also 5 FAQs. + Your goal is to create SEO-optimized content and also include 5 FAQs. Follow below guidelines: 1). Your blog content should compete against all blogs from search results. 2). Your FAQ should be based on 'People also ask' and 'Related Queries' from given search result. Always include answers for each FAQ, use your knowledge and confirm with snippets given in search result. 3). Your blog should be highly detailed, unique and written in human-like personality & tone. - 4). Act as subject matter expert for given research keywords and include statistics and facts. - 5). Do not explain, describe your response. - 6). Important: Please read the entire prompt before writing anything, and do not do anything extra. - Follow the prompt exactly as I instructed. + 4). Adopt an engaging, helpful voice, providing actionable and concrete insights, and avoiding buzzwords. + 5). Act as subject matter expert for given research keywords and include statistics and facts. + 6). Do not explain, describe your response. + 7). Your blog should be highly formatted in markdown style and highly readable. + 8). Important: Please read the entire prompt before writing anything. Follow the prompt exactly as I instructed. \n\nWeb Research Keyword: "{search_keyword}" Google search Result: "{search_results}" """ logger.info("Generating blog and FAQs from Google web search results.") - if 'google' in gpt_providers.lower(): - try: - response = gemini_text_response(prompt) - return response - except Exception as err: - logger.error(f"Failed to get response from gemini: {err}") - raise err - elif 'openai' in gpt_providers.lower(): - try: - logger.info("Calling OpenAI LLM.") - response = openai_chatgpt(prompt) - return response - except Exception as err: - logger.error(f"Failed to get response from Openai: {err}") - raise err + try: + response = llm_text_gen(prompt) + return response + except Exception as err: + logger.error(f"Exit: Failed to get response from LLM: {err}") + exit(1) diff --git a/lib/ai_writers/combine_blog_and_keywords.py b/lib/ai_writers/combine_blog_and_keywords.py index d8f86fd7..3542c0fb 100644 --- a/lib/ai_writers/combine_blog_and_keywords.py +++ b/lib/ai_writers/combine_blog_and_keywords.py @@ -1,12 +1,6 @@ import os import sys -from pathlib import Path -from dotenv import load_dotenv -load_dotenv(Path('../.env')) - -from ..gpt_providers.openai_text_gen import openai_chatgpt -from ..gpt_providers.gemini_pro_text import gemini_text_response from loguru import logger logger.remove() @@ -15,10 +9,11 @@ logger.add(sys.stdout, format="{level}|{file}:{line}:{function}| {message}" ) +from ..gpt_providers.text_generation.main_text_generation import llm_text_gen + def blog_with_keywords(blog, keywords): """Combine the given online research and gpt blog content""" - gpt_providers = os.environ["GPT_PROVIDER"] prompt = f""" As an expert digital content writer, specializing in content optimization and SEO. I will provide you with my 'blog content' and 'list of keywords' on the same topic. @@ -28,19 +23,9 @@ def blog_with_keywords(blog, keywords): Blog content: '{blog}' list of keywords: '{keywords}' """ - - if 'google' in gpt_providers.lower(): - try: - response = gemini_text_response(prompt) - return response - except Exception as err: - logger.error(f"Failed to get response from gemini: {err}") - raise err - elif 'openai' in gpt_providers.lower(): - try: - logger.info("Calling OpenAI LLM.") - response = openai_chatgpt(prompt) - return response - except Exception as err: - logger.error(f"failed to get response from Openai: {err}") - raise err + try: + response = llm_text_gen(prompt) + return response + except Exception as err: + logger.error(f"blog_with_keywords: Failed to get response from LLM: {err}") + raise err diff --git a/lib/ai_writers/combine_research_and_blog.py b/lib/ai_writers/combine_research_and_blog.py index 26f5cd33..839044ab 100644 --- a/lib/ai_writers/combine_research_and_blog.py +++ b/lib/ai_writers/combine_research_and_blog.py @@ -1,24 +1,18 @@ import os import sys -from pathlib import Path -from dotenv import load_dotenv -load_dotenv(Path('../.env')) - -from ..gpt_providers.openai_text_gen import openai_chatgpt -from ..gpt_providers.gemini_pro_text import gemini_text_response - from loguru import logger logger.remove() logger.add(sys.stdout, colorize=True, format="{level}|{file}:{line}:{function}| {message}" ) +# Intenral libraries +from ..gpt_providers.text_generation.main_text_generation import llm_text_gen def blog_with_research(report, blog): """Combine the given online research and gpt blog content""" - gpt_providers = os.environ["GPT_PROVIDER"] prompt = f""" You are an expert content editor specializing in SEO content optimization for blogs. I will provide you with a 'research report' and a 'blog content' on the same topic. @@ -29,36 +23,22 @@ def blog_with_research(report, blog): 1. Master the report and blog content: Understand main ideas, key points, and the core message. 2. Sentence Structure: Rephrase while preserving logical flow and conversational tone. 3. Identify Main Keywords: Determine the primary topic and combine the articles on that main topic. - 4. Implement SEO best practises with appropriate keyword density. - 5. Use Creative and Human-like Style: Incorporate contractions, idioms, transitional phrases, + 4. Use Creative and Human-like Style: Incorporate contractions, idioms, transitional phrases, interjections, and colloquialisms. - 6. Blog Structuring: Include an Introduction, subtopics and use bullet points or + 5. Blog Structuring: Include an Introduction, subtopics and use bullet points or numbered lists if appropriate. Important to include FAQs, Conclusion and Referances. - 7. Ensure Uniqueness: Guarantee the article is plagiarism-free. Write in human-like and informative style. - 9. Pass AI Detection Tools: Create content that easily passes AI plagiarism detection tools. - 10. Act as subject matter expert and include statistics and facts in your combined article. - + 6. Ensure Uniqueness: Guarantee the article is plagiarism-free. Write in human-like and informative style. + 7. Act as subject matter expert and include statistics and facts in your combined article. + 8. Do not provide explanations for your response. Important: Please read the entire prompt before writing anything. Follow the prompt exactly as I instructed.\n\n Research report: '{report}' Blog content: '{blog}' """ - if 'google' in gpt_providers.lower(): - try: - response = gemini_text_response(prompt) - return response - except Exception as err: - logger.error(f"Failed to get response from gemini: {err}") - raise err - elif 'openai' in gpt_providers.lower(): - try: - logger.info("Calling OpenAI LLM.") - response = openai_chatgpt(prompt) - return response - except Exception as err: - logger.error(f"failed to get response from Openai: {err}") - raise err - else: - logger.error(f"Unrecognised/Un-Supoorted GPT_PROVIDER: {gpt_providers}\n") - return + try: + response = llm_text_gen(prompt) + return response + except Exception as err: + logger.error(f"blog_with_research: Failed to get response from LLM: {err}") + raise err diff --git a/lib/ai_writers/keywords_to_blog.py b/lib/ai_writers/keywords_to_blog.py index ab38e727..db84ac6a 100644 --- a/lib/ai_writers/keywords_to_blog.py +++ b/lib/ai_writers/keywords_to_blog.py @@ -46,7 +46,7 @@ def write_blog_from_keywords(search_keywords, url=None): except Exception as err: logger.error(f"Failed in Google web research: {err}") # logger.info/check the final blog content. - logger.info(f"######### Blog content Google SERP research: ###########\n\n{blog_markdown_str}\n\n") + logger.info("\n######### Draft1: Finished Blog from Google web search: ###########\n\n") # Do Tavily AI research to augument the above blog. try: @@ -56,15 +56,16 @@ def write_blog_from_keywords(search_keywords, url=None): logger.info(f"######### Blog content after Tavily AI research: ######### \n\n{blog_markdown_str}\n\n") except Exception as err: logger.error(f"Failed to do Tavily AI research: {err}") + logger.info("######### Draft2: Blog content after Tavily AI research: #########\n\n") try: # Do Metaphor/Exa AI search. metaphor_search_result, m_titles = do_metaphor_ai_research(search_keywords) example_blog_titles.append(m_titles) blog_markdown_str = blog_with_research(blog_markdown_str, metaphor_search_result) - logger.info(f"######## Blog content after EXA AI research: ########## \n\n{blog_markdown_str}\n\n") except Exception as err: logger.error(f"Failed to do Metaphor AI search: {err}") + logger.info("######### Draft3: Blog content after Tavily AI research: ######### \n\n") # Do Google trends analysis and combine with latest blog. try: @@ -74,7 +75,7 @@ def write_blog_from_keywords(search_keywords, url=None): except Exception as err: logger.error(f"Failed to do Google Trends Analysis:{err}") logger.info(f"########### Blog Content After Google Trends Analysis:######### \n {blog_markdown_str}\n\n") - + # Combine YOU.com RAG search with the latest blog content. #you_rag_result = get_rag_results(search_keywords) #you_search_result = search_ydc_index(search_keywords) diff --git a/lib/blog_metadata/get_blog_category.py b/lib/blog_metadata/get_blog_category.py index 38137939..c364a1bd 100644 --- a/lib/blog_metadata/get_blog_category.py +++ b/lib/blog_metadata/get_blog_category.py @@ -1,9 +1,6 @@ import sys import os -from pathlib import Path -from dotenv import load_dotenv -load_dotenv(Path('../.env')) from loguru import logger logger.remove() logger.add(sys.stdout, @@ -11,15 +8,13 @@ logger.add(sys.stdout, format="{level}|{file}:{line}:{function}| {message}" ) -from ..gpt_providers.openai_text_gen import openai_chatgpt -from ..gpt_providers.gemini_pro_text import gemini_text_response +from ..gpt_providers.text_generation.main_text_generation import llm_text_gen def get_blog_categories(blog_article): """ Function to generate blog categories for given blog content. """ - gpt_providers = os.environ["GPT_PROVIDER"] prompt = f"""As an expert SEO and content writer, I will provide you with blog content. Suggest only 2 blog categories which are most relevant to provided blog content, by identifying the main topic. Also consider the target audience and the @@ -27,15 +22,8 @@ def get_blog_categories(blog_article): The blog content is: '{blog_article}'" """ logger.info("Generating blog categories for the given blog.") - if 'google' in gpt_providers.lower(): - try: - response = gemini_text_response(prompt) - return response - except Exception as err: - logger.error(f"Failed to get response from gemini: {err}") - elif 'openai' in gpt_providers.lower(): - try: - response = openai_chatgpt(prompt) - return response - except Exception as err: - SystemError(f"Error in generating blog get_blog_categories: {err}") + try: + response = llm_text_gen(prompt) + return response + except Exception as err: + logger.error(f"get_blog_categories:Failed to get response from LLM: {err}") diff --git a/lib/blog_metadata/get_blog_meta_desc.py b/lib/blog_metadata/get_blog_meta_desc.py index a1eb2787..47b6f8f8 100644 --- a/lib/blog_metadata/get_blog_meta_desc.py +++ b/lib/blog_metadata/get_blog_meta_desc.py @@ -1,8 +1,5 @@ import sys import os -from pathlib import Path -from dotenv import load_dotenv -load_dotenv(Path('../.env')) from loguru import logger logger.remove() @@ -11,15 +8,13 @@ logger.add(sys.stdout, format="{level}|{file}:{line}:{function}| {message}" ) -from ..gpt_providers.openai_text_gen import openai_chatgpt -from ..gpt_providers.gemini_pro_text import gemini_text_response +from ..gpt_providers.text_generation.main_text_generation import llm_text_gen def generate_blog_description(blog_content): """ Prompt designed to give SEO optimized blog descripton """ - gpt_providers = os.environ["GPT_PROVIDER"] logger.info("Generating Blog Meta Description for the given blog.") prompt = f"""As an expert SEO and blog writer, Compose a compelling meta description for the given blog content, adhering to SEO best practices. Keep it between 150-160 characters. @@ -27,15 +22,9 @@ def generate_blog_description(blog_content): Respond with only one of your best effort and do not include your explanations. Blog Content: '{blog_content}'""" - if 'google' in gpt_providers.lower(): - try: - response = gemini_text_response(prompt) - return response - except Exception as err: - logger.error("Failed to get response from gemini.") - elif 'openai' in gpt_providers.lower(): - try: - response = openai_chatgpt(prompt) - return response - except Exception as err: - SystemError(f"Error in generating blog summary: {err}") + try: + response = llm_text_gen(prompt) + return response + except Exception as err: + logger.error(f"Failed to get response from LLM:{err}") + raise err diff --git a/lib/blog_metadata/get_blog_title.py b/lib/blog_metadata/get_blog_title.py index 47fe6e78..bfff10c6 100644 --- a/lib/blog_metadata/get_blog_title.py +++ b/lib/blog_metadata/get_blog_title.py @@ -1,13 +1,6 @@ import os import sys -from pathlib import Path -from dotenv import load_dotenv -load_dotenv(Path('../../.env')) - -from ..gpt_providers.openai_text_gen import openai_chatgpt -from ..gpt_providers.gemini_pro_text import gemini_text_response - from loguru import logger logger.remove() logger.add(sys.stdout, @@ -15,13 +8,14 @@ logger.add(sys.stdout, format="{level}|{file}:{line}:{function}| {message}" ) +from ..gpt_providers.text_generation.main_text_generation import llm_text_gen + def generate_blog_title(blog_article, keywords=None, example_titles=None, num_titles=1): """ Given a blog title generate an outline for it """ prompt = '' - gpt_providers = os.environ["GPT_PROVIDER"] logger.info("Generating blog title.") if not keywords and not example_titles: prompt = f"""As a SEO expert, I will provide you with a blog content. @@ -51,16 +45,9 @@ def generate_blog_title(blog_article, keywords=None, example_titles=None, num_ti Negative Keywords: Unvieling, unleash, power of. Dont use such words in your title. Blog Article: '{keywords}' """ - if 'google' in gpt_providers.lower(): - try: - response = gemini_text_response(prompt) - return response - except Exception as err: - logger.error(f"Failed to get response from gemini: {err}") - elif 'openai' in gpt_providers.lower(): - try: - logger.info("Calling OpenAI LLM.") - response = openai_chatgpt(prompt) - return response - except Exception as err: - SystemError(f"Failed to get response from Openai: {err}") + try: + response = llm_text_gen(prompt) + return response + except Exception as err: + logger.error(f"Failed to get response from LLM: {err}") + raise err diff --git a/lib/blog_metadata/get_tags.py b/lib/blog_metadata/get_tags.py index dfbf90c3..63e5b2d4 100644 --- a/lib/blog_metadata/get_tags.py +++ b/lib/blog_metadata/get_tags.py @@ -1,9 +1,6 @@ import sys import os -from pathlib import Path -from dotenv import load_dotenv -load_dotenv(Path('../.env')) from loguru import logger logger.remove() logger.add(sys.stdout, @@ -11,8 +8,7 @@ logger.add(sys.stdout, format="{level}|{file}:{line}:{function}| {message}" ) -from ..gpt_providers.openai_text_gen import openai_chatgpt -from ..gpt_providers.gemini_pro_text import gemini_text_response +from ..gpt_providers.text_generation.main_text_generation import llm_text_gen def get_blog_tags(blog_article): @@ -25,15 +21,9 @@ def get_blog_tags(blog_article): for the given blog content. Only reply with comma separated values. Blog content: {blog_article}.""" logger.info("Generating Blog tags for the given blog post.") - if 'google' in gpt_providers.lower(): - try: - response = gemini_text_response(prompt) - return response - except Exception as err: - logger.error("Failed to get response from gemini.") - elif 'openai' in gpt_providers.lower(): - try: - response = openai_chatgpt(prompt) - return response - except Exception as err: - SystemError(f"Error in generating blog summary: {err}") + try: + response = llm_text_gen(prompt) + return response + except Exception as err: + logger.error(f"Failed to get response from LLM: {err}") + raise err diff --git a/lib/blog_postprocessing/blog_proof_reader.py b/lib/blog_postprocessing/blog_proof_reader.py index 45d175f7..d60bc1dc 100644 --- a/lib/blog_postprocessing/blog_proof_reader.py +++ b/lib/blog_postprocessing/blog_proof_reader.py @@ -1,19 +1,13 @@ import os import sys - -from pathlib import Path -from dotenv import load_dotenv -load_dotenv(Path('../../.env')) import configparser -from ..gpt_providers.gemini_pro_text import gemini_text_response -from ..gpt_providers.openai_text_gen import openai_chatgpt +from ..gpt_providers.text_generation.main_text_generation import llm_text_gen def blog_proof_editor(blog_content): """ Helper for blog proof reading. """ - gpt_provider = os.environ["GPT_PROVIDER"] try: config_path = os.path.abspath(os.path.join(os.path.dirname(__file__), '..', '..', 'main_config')) config = configparser.ConfigParser() @@ -38,15 +32,8 @@ def blog_proof_editor(blog_content): \n\nMy Blog: '{blog_content}'. """ - if 'openai' in gpt_provider.lower(): - try: - response = openai_chatgpt(prompt) - return response - except Exception as err: - SystemError(f"Openai Error Blog Proof Reading: {err}") - elif 'google' in gpt_provider.lower(): - try: - response = gemini_text_response(prompt) - return response - except Exception as err: - SystemError(f"Gemini Error Blog Proof Reading: {err}") + try: + response = llm_text_gen(prompt) + return response + except Exception as err: + logger.error(f"Error Blog Proof Reading: {err}") diff --git a/lib/blog_postprocessing/humanize_blog.py b/lib/blog_postprocessing/humanize_blog.py index 6a4d920e..c00176e0 100644 --- a/lib/blog_postprocessing/humanize_blog.py +++ b/lib/blog_postprocessing/humanize_blog.py @@ -1,17 +1,18 @@ import os import sys -from pathlib import Path -from dotenv import load_dotenv -load_dotenv(Path('../../.env')) +from loguru import logger +logger.remove() +logger.add(sys.stdout, + colorize=True, + format="{level}|{file}:{line}:{function}| {message}" + ) -from ..gpt_providers.gemini_pro_text import gemini_text_response -from ..gpt_providers.openai_text_gen import openai_chatgpt +from ..gpt_providers.text_generation.main_text_generation import llm_text_gen def blog_humanize(blog_content): """ Helper for blog proof reading. """ - gpt_provider = os.environ["GPT_PROVIDER"] prompt = f"""The following is what I will refer to as an 'Exception-list'. Do Not include any of the words or phrases on this list in your future responses to this chat thread. @@ -25,15 +26,9 @@ def blog_humanize(blog_content): \n\nBlog Content: '{blog_content}' """ - if 'openai' in gpt_provider.lower(): - try: - response = openai_chatgpt(prompt) - return response - except Exception as err: - SystemError(f"Openai Error Blog Proof Reading: {err}") - elif 'google' in gpt_provider.lower(): - try: - response = gemini_text_response(prompt) - return response - except Exception as err: - SystemError(f"Gemini Error Blog Proof Reading: {err}") + try: + response = llm_text_gen(prompt) + return response + except Exception as err: + logger.error(f"Openai Error Blog Proof Reading: {err}") + raise err diff --git a/lib/gpt_providers/__init__.py b/lib/gpt_providers/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/lib/gpt_providers/stt_audio_blog.py b/lib/gpt_providers/audio_to_text_generation/stt_audio_blog.py similarity index 100% rename from lib/gpt_providers/stt_audio_blog.py rename to lib/gpt_providers/audio_to_text_generation/stt_audio_blog.py diff --git a/lib/gpt_providers/gemini_pro_text.py b/lib/gpt_providers/gemini_pro_text.py deleted file mode 100644 index 32a9b41f..00000000 --- a/lib/gpt_providers/gemini_pro_text.py +++ /dev/null @@ -1,40 +0,0 @@ -# Using Gemini Pro LLM model -import os -import logging -from pathlib import Path - -import google.generativeai as genai -logging.basicConfig(level=logging.INFO, format='%(asctime)s-%(levelname)s-%(module)s-%(lineno)d-%(message)s') -from dotenv import load_dotenv -load_dotenv(Path('../../.env')) -from .mistral_chat_completion import mistral_text_response - -from tenacity import ( - retry, - stop_after_attempt, - wait_random_exponential, -) # for exponential backoff - - -@retry(wait=wait_random_exponential(min=1, max=60), stop=stop_after_attempt(6)) -def gemini_text_response(prompt): - """ Common functiont to get response from gemini pro Text. """ - genai.configure(api_key=os.getenv('GEMINI_API_KEY')) - - # Set up the model - generation_config = { - "temperature": 1, - "top_p": 1, - "top_k": 1, - "max_output_tokens": 6096, - } - - model = genai.GenerativeModel(model_name="gemini-pro", generation_config=generation_config) - try: - response = model.generate_content(prompt) - except Exception as err: - logger.error(f"Failed to get response from Gemini: {err}. Retrying.") - # Try with minstral. - #response = mistral_text_response(prompt) - #return response - return response.text diff --git a/lib/gpt_providers/gemini_image_details.py b/lib/gpt_providers/image_to_text_gen/gemini_image_details.py similarity index 100% rename from lib/gpt_providers/gemini_image_details.py rename to lib/gpt_providers/image_to_text_gen/gemini_image_details.py diff --git a/lib/gpt_providers/gpt_vision_img_details.py b/lib/gpt_providers/image_to_text_gen/openai_vision_img_details.py similarity index 100% rename from lib/gpt_providers/gpt_vision_img_details.py rename to lib/gpt_providers/image_to_text_gen/openai_vision_img_details.py diff --git a/lib/gpt_providers/text_generation/gemini_pro_text.py b/lib/gpt_providers/text_generation/gemini_pro_text.py new file mode 100644 index 00000000..974db369 --- /dev/null +++ b/lib/gpt_providers/text_generation/gemini_pro_text.py @@ -0,0 +1,46 @@ +# Using Gemini Pro LLM model +import os +import sys +from pathlib import Path + +import google.generativeai as genai +from dotenv import load_dotenv +load_dotenv(Path('../../../.env')) +from loguru import logger +logger.remove() +logger.add(sys.stdout, + colorize=True, + format="{level}|{file}:{line}:{function}| {message}" + ) + +from tenacity import ( + retry, + stop_after_attempt, + wait_random_exponential, +) # for exponential backoff + + +@retry(wait=wait_random_exponential(min=1, max=60), stop=stop_after_attempt(6)) +def gemini_text_response(prompt, temperature, top_p, n, max_tokens): + """ Common functiont to get response from gemini pro Text. """ + try: + genai.configure(api_key=os.getenv('GEMINI_API_KEY')) + except Exception as err: + logger.error(f"Failed to configure Gemini: {err}") + logger.info(f"Temp: {temperature}, MaxTokens: {max_tokens}, TopP: {top_p}, N: {n}") + # Set up the model + generation_config = { + "temperature": temperature, + "top_p": top_p, + "top_k": n, + "max_output_tokens": max_tokens + } + model = genai.GenerativeModel(model_name="gemini-pro", generation_config=generation_config) + try: + response = model.generate_content(prompt, stream=True) + for chunk in response: + print(chunk.text) + return response.text + except Exception as err: + logger.error(response) + logger.error(f"Failed to get response from Gemini: {err}. Retrying.") diff --git a/lib/gpt_providers/text_generation/main_text_generation.py b/lib/gpt_providers/text_generation/main_text_generation.py new file mode 100644 index 00000000..5a2a9508 --- /dev/null +++ b/lib/gpt_providers/text_generation/main_text_generation.py @@ -0,0 +1,151 @@ +import os +import sys +import configparser +from pathlib import Path +from dotenv import load_dotenv +load_dotenv(Path('../.env')) + +from loguru import logger +logger.remove() +logger.add(sys.stdout, + colorize=True, + format="{level}|{file}:{line}:{function}| {message}" + ) + +from .openai_text_gen import openai_chatgpt +from .gemini_pro_text import gemini_text_response + + +def llm_text_gen(prompt): + """ + Generate text using Language Model (LLM) based on the provided prompt. + Args: + prompt (str): The prompt to generate text from. + Returns: + str: Generated text based on the prompt. + """ + try: + config_path = Path(__file__).resolve().parents[3] / "main_config" + gpt_provider, model, temperature, max_tokens, top_p, n, fp = read_llm_parameters(config_path) + + gpt_provider = check_gpt_provider(gpt_provider) + # Check if API key is provided for the given gpt_provider + get_api_key(gpt_provider) + + logger.info(f"Model: {model}, Temp: {temperature}, MaxTokens: {max_tokens}, TopP: {top_p}, N: {n}, FrequencyPenalty: {fp}") + # Perform text generation using the specified LLM parameters and prompt + if 'google' in gpt_provider.lower(): + try: + logger.info("Using Google Gemini Pro text generation model.") + response = gemini_text_response(prompt, temperature, top_p, n, max_tokens) + return response + except Exception as err: + logger.error(f"Failed to get response from gemini: {err}") + raise err + elif 'openai' in gpt_provider.lower(): + try: + logger.info(f"Using OpenAI Model: {model} for text Generation.") + response = openai_chatgpt(prompt, model, temperature, max_tokens, top_p, n, fp) + return response + except Exception as err: + logger.error(f"Failed to get response from Openai: {err}") + raise err + + except Exception as err: + logger.error(f"Failed to read LLM parameters: {err}") + raise + + +def check_gpt_provider(gpt_provider): + """ + Check if the specified GPT provider matches the environment variable GPT_PROVIDER, + assign and export the GPT_PROVIDER value from the config file if missing, + and continue. + + Args: + gpt_provider (str): The specified GPT provider. + + Raises: + ValueError: If both the specified GPT provider and environment variable GPT_PROVIDER are missing. + """ + env_gpt_provider = os.getenv('GPT_PROVIDER') + + if gpt_provider: + os.environ['GPT_PROVIDER'] = gpt_provider + elif env_gpt_provider: + gpt_provider = env_gpt_provider + else: + raise ValueError("Both specified GPT provider and environment variable 'GPT_PROVIDER' are missing.") + + if gpt_provider != env_gpt_provider: + logger.warning(f"Config: '{gpt_provider}' different to environment variable 'GPT_PROVIDER' '{env_gpt_provider}'") + logger.info(f"Using GPT provider: {gpt_provider}") + return gpt_provider + + + +def get_api_key(gpt_provider): + """ + Get the API key for the specified GPT provider. + + Args: + gpt_provider (str): The specified GPT provider. + + Returns: + str: The API key for the specified GPT provider. + + Raises: + ValueError: If no API key is found for the specified GPT provider. + """ + api_key = None + + if gpt_provider.lower() == 'google': + api_key = os.getenv('GEMINI_API_KEY') + elif gpt_provider.lower() == 'openai': + api_key = os.getenv('OPENAI_API_KEY') + + if not api_key: + raise ValueError(f"No API key found for the specified GPT provider: '{gpt_provider}'") + + logger.info(f"Using API key for {gpt_provider}") + return api_key + + + +def read_llm_parameters(config_path: str) -> tuple: + """ + Read Language Model (LLM) parameters from the configuration file. + + Args: + config_path (str): The path to the configuration file. + + Returns: + tuple: A tuple containing the LLM parameters (gpt_provider, model, temperature, max_tokens, top_p, n, frequency_penalty). + + Raises: + FileNotFoundError: If the configuration file is not found. + configparser.Error: If there is an error parsing the configuration file. + """ + try: + config = configparser.ConfigParser() + config.read(config_path) + + gpt_provider = config.get('llm_options', 'gpt_provider') + model = config.get('llm_options', 'model') + temperature = config.getfloat('llm_options', 'temperature') + max_tokens = config.getint('llm_options', 'max_tokens') + top_p = config.getfloat('llm_options', 'top_p') + n = config.getint('llm_options', 'n') + frequency_penalty = config.getfloat('llm_options', 'frequency_penalty') + + return gpt_provider, model, temperature, max_tokens, top_p, n, frequency_penalty + + except FileNotFoundError: + logger.error(f"Configuration file not found: {config_path}") + raise + except configparser.Error as err: + logger.error(f"Error reading LLM parameters from config file: {err}") + raise + except Exception as err: + logger.error(f"An unexpected error occurred: {err}") + raise diff --git a/lib/gpt_providers/mistral_chat_completion.py b/lib/gpt_providers/text_generation/mistral_chat_completion.py similarity index 100% rename from lib/gpt_providers/mistral_chat_completion.py rename to lib/gpt_providers/text_generation/mistral_chat_completion.py diff --git a/lib/gpt_providers/openai_text_gen.py b/lib/gpt_providers/text_generation/openai_text_gen.py similarity index 77% rename from lib/gpt_providers/openai_text_gen.py rename to lib/gpt_providers/text_generation/openai_text_gen.py index 4cf031c4..7ee033e5 100644 --- a/lib/gpt_providers/openai_text_gen.py +++ b/lib/gpt_providers/text_generation/openai_text_gen.py @@ -16,7 +16,7 @@ from tenacity import ( @retry(wait=wait_random_exponential(min=1, max=60), stop=stop_after_attempt(6)) -def openai_chatgpt(prompt): +def openai_chatgpt(prompt, model, temperature, max_tokens, top_p, n, fp): """ Wrapper function for OpenAI's ChatGPT completion. @@ -34,26 +34,16 @@ def openai_chatgpt(prompt): Raises: SystemExit: If an API error, connection error, or rate limit error occurs. """ - try: - config_path = os.path.abspath(os.path.join(os.path.dirname(__file__), '..', '..', 'main_config')) - - config = configparser.ConfigParser() - config.read(config_path) - - model = config.get('llm_options', 'model') - temperature = config.getfloat('llm_options', 'temperature') - max_tokens = config.getint('llm_options', 'max_tokens') - top_p = config.getfloat('llm_options', 'top_p') - n = config.getint('llm_options', 'n') - fp = config.getfloat('llm_options', 'frequency_penalty') - except Exception as err: - logger.error(f"Unable to read Openai parameters from config file:{err}") - # Wait for 10 seconds to comply with rate limits for _ in range(5): time.sleep(1) try: + # Create variables to collect the stream of chunks + collected_chunks = [] + collected_messages = [] + full_reply_content = None + client = openai.OpenAI(api_key=os.getenv('OPENAI_API_KEY')) response = client.chat.completions.create( model=model, @@ -65,17 +55,15 @@ def openai_chatgpt(prompt): frequency_penalty=fp # Additional parameters can be included here ) - # create variables to collect the stream of chunks - collected_chunks = [] - collected_messages = [] - # iterate through the stream of events + + # Iterate through the stream of events for chunk in response: collected_chunks.append(chunk) # save the event response chunk_message = chunk.choices[0].delta.content # extract the message collected_messages.append(chunk_message) # save the message print(chunk.choices[0].delta.content, end = "", flush = True) - # clean None in collected_messages + # Clean None in collected_messages collected_messages = [m for m in collected_messages if m is not None] full_reply_content = ''.join([m for m in collected_messages]) return full_reply_content diff --git a/main_config b/main_config index b4aa1d09..5233b620 100644 --- a/main_config +++ b/main_config @@ -9,7 +9,7 @@ [blog_characteristics] # Length of blogs Or word count. Note: It wont be exact and depends on GPT providers and Max token count. -blog_length = 2000 +blog_length = 3000 # professional, how-to, begginer, research, programming, casual, etc blog_tone = "professional" @@ -55,8 +55,9 @@ num_images = 1 ########################################################### [llm_options] + # Choose one of following: Openai, Google, Minstral -gpt_provider = "google" +gpt_provider = google # Mention which model of the above provider to use. model = gpt-3.5-turbo-0125