├── .gitignore ├── README.md ├── api_helper ├── ghost_api.py └── serp_api.py ├── app.py ├── llm_keyword_fetcher └── llm_generator.py ├── postgres.py ├── prompt.py ├── prompts ├── content_prompt.py ├── faq_prompt.py ├── feedback_content_prompt.py └── structure_prompt.py ├── requirements.txt ├── seo └── data_for_seo_api.py └── st_frontend ├── frontend.py └── st_helper.py /.gitignore: -------------------------------------------------------------------------------- 1 | .env 2 | /__pycache__ 3 | .DS_Store 4 | /api_helper/__pycache__ 5 | /st_frontend/__pycache__ 6 | /prompts/__pycache__ 7 | /seo/__pycache__ 8 | /llm_keyword_fetcher/__pycache__ -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## BlogIQ - Content Generation using AI (Clone of writesonic.com & copy.ai) 2 | 3 | ### 🚀 Introduction: 4 | 5 | BlogIQ stands as a beacon of innovation in the realm of content creation, providing bloggers with an advanced platform powered by state-of-the-art technology, including Langchain, Langgraph, and OpenAI GPT-4 models. 🌟 Seamlessly integrated with dataforseo.com API, BlogIQ offers a comprehensive suite of features tailored to streamline and optimize the blogging process. 6 | 7 | 🔍 Step 1: Topic and Keyword Selection: 8 | 9 | At its core, BlogIQ simplifies content creation by allowing users to specify their desired topic and primary keyword. Leveraging data from the dataforseo.com API, the app conducts an extensive Google search to curate a selection of relevant URLs. Users retain complete control, with the option to manually select additional URLs or meta keywords, or opt for automated meta keyword generation using OpenAI LLM to ensure adherence to SEO best practices. 📊💡 10 | 11 | 🛠️ Step 2: Title and Structure Generation: 12 | 13 | Powered by Langchain, Langgraph, and OpenAI GPT-4 models, BlogIQ facilitates title and structure generation with unprecedented accuracy. Users are presented with a range of options tailored to their preferences, with the added flexibility of providing input to direct the GPT-4 models during this process, ensuring that the generated content seamlessly aligns with their vision and objectives. 💭✨ 14 | 15 | 📝 Step 3: Content Generation: 16 | 17 | Expanding upon the chosen structure, BlogIQ dynamically generates content enriched with insights gleaned from the selected URLs and meta keywords. Users can further guide the content generation process by providing prompts to the GPT-4 models, fostering personalized and engaging content creation experiences. 📝✨ 18 | 19 | 💬 Step 4: FAQ Generation: 20 | 21 | In the final stage, BlogIQ completes the content creation journey by generating FAQs for the blog. By analyzing the generated content and identifying potential questions, the app automatically generates a set of FAQs, enriching the blog post with valuable additional information and enhancing its engagement potential. 🤔💬 22 | 23 | ![ezgif com-optimize](https://github.com/langchain-tech/BlogIQ/assets/100914015/1b53acb4-bdd9-4bfe-9e81-03dafb11ef68) 24 | 25 | 26 | ## Getting Started 27 | 28 | ### Prerequisites 29 | 30 | Before running the app, make sure you have the following dependencies installed: 31 | 32 | - Python 3.x 33 | - pip (Python package installer) 34 | 35 | ### Installation 36 | 37 | 1. Clone the repository: 38 | 39 | ```bash 40 | git clone https://github.com/langschain/BlogIQ.git 41 | ``` 42 | 43 | 2. Navigate to the project directory: 44 | 45 | ```bash 46 | cd BlogIQ 47 | ``` 48 | 49 | 3. Install the required packages: 50 | 51 | ```bash 52 | pip install -r requirements.txt 53 | ``` 54 | 55 | ### Configuration 56 | 57 | 1. Create a `.env` file in the project root directory. 58 | 59 | 2. Add the following environment variables to the `.env` file: 60 | 61 | ``` 62 | LANGCHAIN_TRACING_V2=your_langchain_tracing_v2_key 63 | LANGCHAIN_PROJECT=your_langchain_project_key 64 | OPENAI_API_KEY=your_openai_api_key 65 | LANGCHAIN_API_KEY=your_langchain_api_key 66 | DATA_FOR_SEO_TOKEN=your_data_for_seo_api_key 67 | ``` 68 | 69 | Replace `your_langchain_tracing_v2_key`, `your_langchain_project_key`, `your_openai_api_key`, `your_langchain_api_key`, and `your_data_for_seo_api_key` with your actual API keys. 70 | 71 | ## Usage 72 | 73 | 1. Run the Streamlit app: 74 | 75 | ```bash 76 | streamlit run app.py 77 | ``` 78 | 79 | 2. Access the app in your web browser at [http://localhost:8501](http://localhost:8501). 80 | 81 | ## Additional Information 82 | 83 | 📚 Explore Technical Blogs Generated by BlogIQ on Langchain Blogs: 84 | 85 | Welcome to the collection of technical blogs generated by BlogIQ on Langchain Blogs, your go-to platform for streamlined content creation powered by advanced AI technology. 🚀 86 | 87 | Where to Find the Technical Blogs: 88 | 89 | Curious to see the technical content generated by BlogIQ in action? You can explore all the technical blogs created using our innovative tool on [Langchain Blogs](https://www.langchain.ca/blog/). From in-depth articles on software development and data science to tutorials on machine learning and programming languages, there's something for every tech enthusiast. 90 | 91 | Why Explore: 92 | 93 | Whether you're a software developer seeking new insights, a data scientist exploring cutting-edge technologies, or a tech enthusiast looking to expand your knowledge, Langchain Blogs is the perfect destination. Each blog post is crafted using BlogIQ's intuitive interface, which harnesses the power of Langchain, Langgraph, and OpenAI GPT-4 models. 94 | 95 | Start Exploring: 96 | 97 | Visit Langchain Blogs today and unlock your creativity! Get ready to dive deep into the world of technology with engaging content that captivates and educates. Happy exploring! 📖✨ 98 | 99 | [Explore Technical Blogs Generated by BlogIQ on Langchain Blogs](https://www.langchain.ca/blog/) 100 | 101 | ## Contributing 102 | 103 | If you'd like to contribute to this project, please fork the repository and submit a pull request. 104 | 105 | ## License 106 | 107 | This project is licensed under the [MIT License](LICENSE). 108 | 109 | ## Acknowledgments 110 | 111 | - Special thanks to Langchain and OpenAI for providing powerful tools for natural language processing. 112 | - The app structure and components are based on the Streamlit framework. 113 | 114 | -------------------------------------------------------------------------------- /api_helper/ghost_api.py: -------------------------------------------------------------------------------- 1 | import requests 2 | 3 | GHOST_API_URL='https://www.langchain.ca/blog/ghost/api/v3/content/posts/' 4 | 5 | # Your Ghost API key 6 | GHOST_API_KEY='65f1ac9109333f517ddaac03:2cb16de1e79fa7d0e140896c14b541b08de4b54507b79ed54ca1e751b62de6a4' 7 | 8 | 9 | 10 | 11 | def post_blog(title, content): 12 | payload = { 13 | 'posts': [{ 14 | 'title': title, 15 | 'html': content, 16 | 'tags': ['tag1', 'tag2'], 17 | 'status': 'draft' 18 | }] 19 | } 20 | print(payload) 21 | response = requests.post(GHOST_API_URL, json=payload, headers={'Authorization': f'Ghost {GHOST_API_KEY}'}, timeout=60) 22 | 23 | # Check response status 24 | if response.status_code == 201: 25 | print('Blog post published successfully!') 26 | else: 27 | print(f'Failed to publish blog post. Status code: {response.status_code}, Error: {response.text}') 28 | -------------------------------------------------------------------------------- /api_helper/serp_api.py: -------------------------------------------------------------------------------- 1 | import os 2 | from serpapi import GoogleSearch 3 | from dotenv import load_dotenv 4 | 5 | load_dotenv() 6 | 7 | SERP_API_KEY = os.getenv("SERP_API_KEY") 8 | 9 | def serp_api_caller(question): 10 | print("---Calling SerpApi---") 11 | params = { 12 | "engine": "google", 13 | "q": question, 14 | "api_key": SERP_API_KEY 15 | } 16 | search = GoogleSearch(params) 17 | results = search.get_dict() 18 | organic_results = results["organic_results"] 19 | return [details['link'] for details in organic_results] -------------------------------------------------------------------------------- /app.py: -------------------------------------------------------------------------------- 1 | import os 2 | import json 3 | import pprint 4 | import secrets 5 | import operator 6 | import streamlit as st 7 | from langchain import hub 8 | from serpapi import GoogleSearch 9 | from dotenv import load_dotenv 10 | from typing import Annotated, Sequence, TypedDict, Dict 11 | from langgraph.graph import END, StateGraph 12 | from langchain.text_splitter import RecursiveCharacterTextSplitter 13 | from langchain_community.document_loaders import WebBaseLoader 14 | from langchain_community.vectorstores import Chroma 15 | from langchain.output_parsers.openai_tools import PydanticToolsParser 16 | from langchain.prompts import PromptTemplate 17 | from langchain.schema import Document 18 | from langchain_community.tools.tavily_search import TavilySearchResults 19 | from langchain_community.vectorstores import Chroma 20 | from langchain_core.messages import BaseMessage, FunctionMessage 21 | from langchain_core.output_parsers import StrOutputParser 22 | from langchain_core.pydantic_v1 import BaseModel, Field 23 | from langchain_core.runnables import RunnablePassthrough 24 | from langchain_core.utils.function_calling import convert_to_openai_tool 25 | from langchain_openai import ChatOpenAI, OpenAIEmbeddings 26 | from postgres import create_record, update_record 27 | from prompt import get_structure_template, get_content_generator_template 28 | 29 | 30 | from langchain_community.chat_models import ChatOllama 31 | from langchain_community.embeddings import OllamaEmbeddings 32 | 33 | ## app imports 34 | from st_frontend.frontend import main 35 | from prompts.content_prompt import content_template 36 | from prompts.structure_prompt import structure_template 37 | from prompts.feedback_content_prompt import feedback_content_template 38 | from prompts.faq_prompt import faq_template 39 | 40 | ### Uncomment import 'pdb' this to use debugger in the app 41 | ### Use this code in between any file or function to stop debugger at any point pdb.set_trace() 42 | import pdb 43 | 44 | ## Used to load .env file 45 | load_dotenv() 46 | 47 | os.environ["LANGCHAIN_TRACING_V2"] = os.getenv("LANGCHAIN_TRACING_V2") 48 | os.environ["LANGCHAIN_PROJECT"] = os.getenv("LANGCHAIN_PROJECT") 49 | os.environ["OPENAI_API_KEY"] = os.getenv("OPENAI_API_KEY") 50 | os.environ["LANGCHAIN_API_KEY"] = os.getenv("LANGCHAIN_API_KEY") 51 | os.environ["TAVILY_API_KEY"] = os.getenv("TAVILY_API_KEY") 52 | 53 | class GraphState(TypedDict): 54 | keys: Dict[str, any] 55 | 56 | def create_collection(collection_name, question, urls): 57 | print("---Got Results---") 58 | docs = [WebBaseLoader(url).load() for url in urls] 59 | docs_list = [item for sublist in docs for item in sublist] 60 | text_splitter = RecursiveCharacterTextSplitter.from_tiktoken_encoder( 61 | chunk_size=1000, chunk_overlap=0 62 | ) 63 | doc_splits = text_splitter.split_documents(docs_list) 64 | print("---CREATING NEW DOCUMENTS---") 65 | vectorstore = Chroma.from_documents( 66 | documents=doc_splits, 67 | collection_name=collection_name, 68 | embedding=OpenAIEmbeddings(), 69 | ) 70 | create_record(collection_name, urls) 71 | print(f"Collection '{collection_name}' created successfully.") 72 | return vectorstore.as_retriever() 73 | 74 | def retrieve_documents(collection_name, question): 75 | print("---RETRIEVING OLD DOCUMENTS---") 76 | embedding_function = OpenAIEmbeddings() 77 | vectorstore = Chroma(collection_name, embedding_function) 78 | return vectorstore.as_retriever() 79 | 80 | 81 | def retrieve(state): 82 | print("---RETRIEVE---") 83 | state_dict = state["keys"] 84 | question = state_dict["question"] 85 | primary_keyword = state_dict["primary_keyword"] 86 | structure_prompt = state_dict["structure_prompt"] 87 | urls = state_dict["selected_urls"] 88 | step_to_execute = state_dict["step_to_execute"] 89 | selected_keywords = state_dict["selected_keywords"] 90 | 91 | 92 | if 'total_headings' in state_dict: 93 | total_headings = state_dict['total_headings'] 94 | else: 95 | total_headings = '' 96 | 97 | if 'current_heading' in state_dict: 98 | current_heading = state_dict['current_heading'] 99 | else: 100 | current_heading = '' 101 | 102 | if 'faq_prompt' in state_dict: 103 | faq_prompt = state_dict['faq_prompt'] 104 | else: 105 | faq_prompt = '' 106 | 107 | if 'blog_prompt' in state_dict: 108 | blog_prompt = state_dict['blog_prompt'] 109 | else: 110 | blog_prompt = '' 111 | 112 | if 'number_of_words_per_heading' in state_dict: 113 | number_of_words_per_heading = state_dict['number_of_words_per_heading'] 114 | else: 115 | number_of_words_per_heading = '' 116 | 117 | if 'blog_content' in state_dict: 118 | blog_content = state_dict['blog_content'] 119 | else: 120 | blog_content = '' 121 | 122 | if 'blog_title' in state_dict: 123 | blog_title = state_dict["blog_title"] 124 | else: 125 | blog_title = '' 126 | 127 | if 'blog' in state_dict: 128 | blog = state_dict["blog"] 129 | else: 130 | blog = '' 131 | 132 | if 'rephrase_context' in state_dict: 133 | rephrase_context = state_dict["rephrase_context"] 134 | else: 135 | rephrase_context = '' 136 | 137 | if 'rephrase' in state_dict: 138 | rephrase = state_dict["rephrase"] 139 | else: 140 | rephrase = '' 141 | 142 | if 'structure' in state_dict: 143 | structure = state_dict["structure"] 144 | else: 145 | structure = "" 146 | 147 | if 'heading' in state_dict: 148 | heading = state_dict["heading"] 149 | else: 150 | heading = "" 151 | 152 | 153 | if 'collection_key' in state_dict: 154 | collection_key = state_dict["collection_key"] 155 | retriever = retrieve_documents(collection_key, heading) 156 | else: 157 | collection_key = secrets.token_hex(12 // 2) 158 | retriever = create_collection(collection_key, question, urls) 159 | 160 | documents = retriever.get_relevant_documents(heading) 161 | 162 | return { "keys": 163 | 164 | { 165 | "documents": documents, 166 | "question": question, 167 | 'primary_keyword': primary_keyword, 168 | "structure_prompt": structure_prompt, 169 | "urls": urls, 170 | "step_to_execute": step_to_execute, 171 | "structure": structure, 172 | "collection_key": collection_key, 173 | "heading": heading, 174 | "rephrase_context": rephrase_context, 175 | "rephrase": rephrase, 176 | "blog": blog, 177 | "blog_title": blog_title, 178 | "selected_keywords": selected_keywords, 179 | "blog_content": blog_content, 180 | "number_of_words_per_heading": number_of_words_per_heading, 181 | "blog_prompt": blog_prompt, 182 | "faq_prompt": faq_prompt, 183 | "total_headings": total_headings, 184 | "current_heading": current_heading 185 | 186 | } 187 | } 188 | 189 | def generate(state): 190 | blog_structure = { 191 | "Blog_Structure_1": 192 | { 193 | "title": "TITLE", 194 | "headings": 195 | [ 196 | "HEADING 1", 197 | "HEADING 2", 198 | "HEADING 3", 199 | "HEADING 4", 200 | "HEADING 5", 201 | "HEADING 6", 202 | "HEADING 7", 203 | "HEADING 8", 204 | "HEADING 9", 205 | "HEADING 10" 206 | ] 207 | }, 208 | "Blog_Structure_2": 209 | { 210 | "title": "TITLE", 211 | "headings": 212 | [ 213 | "HEADING 1", 214 | "HEADING 2", 215 | "HEADING 3", 216 | "HEADING 4", 217 | "HEADING 5", 218 | "HEADING 6", 219 | "HEADING 7", 220 | "HEADING 8", 221 | "HEADING 9", 222 | "HEADING 10" 223 | ] 224 | }, 225 | "Blog_Structure_3": 226 | { 227 | "title": "TITLE", 228 | "headings": 229 | [ 230 | "HEADING 1", 231 | "HEADING 2", 232 | "HEADING 3", 233 | "HEADING 4", 234 | "HEADING 5", 235 | "HEADING 6", 236 | "HEADING 7", 237 | "HEADING 8", 238 | "HEADING 9", 239 | "HEADING 10" 240 | ] 241 | } 242 | } 243 | print("---GENERATE---") 244 | state_dict = state["keys"] 245 | question = state_dict["question"] 246 | documents = state_dict["documents"] 247 | primary_keyword = state_dict["primary_keyword"] 248 | structure_prompt = state_dict["structure_prompt"] 249 | urls = state_dict["urls"] 250 | collection_key = state_dict["collection_key"] 251 | step_to_execute = state_dict["step_to_execute"] 252 | structure = state_dict["structure"] 253 | heading = state_dict["heading"] 254 | rephrase_context = state_dict["rephrase_context"] 255 | rephrase = state_dict["rephrase"] 256 | blog = state_dict["blog"] 257 | blog_title = state_dict["blog_title"] 258 | selected_keywords = state_dict['selected_keywords'] 259 | blog_content = state_dict['blog_content'] 260 | number_of_words_per_heading = state_dict['number_of_words_per_heading'] 261 | blog_prompt = state_dict['blog_prompt'] 262 | faq_prompt = state_dict['faq_prompt'] 263 | total_headings = state_dict['total_headings'] 264 | current_heading = state_dict['current_heading'] 265 | print(state_dict) 266 | 267 | if step_to_execute == "Generate Structure": 268 | heading = '' 269 | template = structure_template() 270 | prompt = PromptTemplate(template=template, input_variables=["documents", "question", "structure_prompt", "primary_keyword", "blog_structure", "selected_keywords"]) 271 | elif rephrase == True: 272 | template = feedback_content_template() 273 | prompt = PromptTemplate(template=template, input_variables=["documents", "structure", "primary_keyword", "refference_links", "rephrase_context", "blog", "structure_prompt"]) 274 | elif step_to_execute == "Generate Blog": 275 | heading = state_dict["heading"] 276 | template = content_template(blog_content) 277 | prompt = PromptTemplate(template=template, input_variables=["documents", "structure", "primary_keyword", "number_of_words_per_heading", "refference_links", "heading", "blog_title", "selected_keywords", "blog_content", "blog_prompt", "total_headings", "current_heading"]) 278 | elif step_to_execute == "Generate Faq's": 279 | template = faq_template() 280 | prompt = PromptTemplate(template=template, input_variables=["documents", "primary_keyword", "selected_keywords", "faq_prompt"]) 281 | 282 | llm = ChatOpenAI(model_name="gpt-4-turbo-preview", temperature=0.7, streaming=True, max_tokens=4096, verbose=True) 283 | # llm = ChatOpenAI(model_name="gpt-3.5-turbo-0125", temperature=0.7, streaming=True, max_tokens=4096, verbose=True) 284 | # llm = ChatOllama(model="llama2:latest") 285 | rag_chain = prompt | llm | StrOutputParser() 286 | 287 | if step_to_execute == "Generate Structure": 288 | 289 | generation = rag_chain.invoke( 290 | { 291 | "documents": documents, 292 | "question": question, 293 | "structure_prompt": structure_prompt, 294 | "primary_keyword": primary_keyword, 295 | "refference_links": urls, 296 | "blog_structure": blog_structure, 297 | "selected_keywords": selected_keywords 298 | } 299 | ) 300 | print("------- Structure Generated -------") 301 | 302 | elif rephrase == True: 303 | generation = rag_chain.invoke( 304 | { 305 | "documents": documents, 306 | "primary_keyword": primary_keyword, 307 | "refference_links": urls, 308 | "structure": structure, 309 | "heading": heading, 310 | "blog": blog, 311 | "blog_title": blog_title, 312 | "rephrase_context": rephrase_context, 313 | "structure_prompt": structure_prompt 314 | } 315 | ) 316 | print("------- Content Rephrased -------") 317 | 318 | elif step_to_execute == "Generate Blog": 319 | generation = rag_chain.invoke( 320 | { 321 | "documents": documents, 322 | "primary_keyword": primary_keyword, 323 | "refference_links": urls, 324 | "structure": structure, 325 | "heading": heading, 326 | "blog": blog, 327 | "blog_title": blog_title, 328 | "selected_keywords": selected_keywords, 329 | "blog_content": blog_content, 330 | "number_of_words_per_heading": number_of_words_per_heading, 331 | "blog_prompt": blog_prompt, 332 | "total_headings": total_headings, 333 | "current_heading": current_heading 334 | } 335 | ) 336 | print("------- Content Generated -------") 337 | 338 | elif step_to_execute == "Generate Faq's": 339 | generation = rag_chain.invoke( 340 | { 341 | "documents": documents, 342 | "primary_keyword": primary_keyword, 343 | "selected_keywords": selected_keywords, 344 | "faq_prompt": faq_prompt, 345 | } 346 | ) 347 | print("------- Faq's Generated -------") 348 | 349 | return { "keys": 350 | 351 | { 352 | "documents": documents, 353 | "question": question, 354 | 'primary_keyword': primary_keyword, 355 | "structure_prompt": structure_prompt, 356 | "urls": urls, 357 | "generation": generation, 358 | "step_to_execute": step_to_execute, 359 | "blog": generation, 360 | "collection_key": collection_key, 361 | "heading": heading 362 | 363 | } 364 | } 365 | 366 | workflow = StateGraph(GraphState) 367 | workflow.add_node("retrieve", retrieve) 368 | workflow.add_node("generate", generate) 369 | workflow.set_entry_point("retrieve") 370 | 371 | workflow.add_edge("retrieve", "generate") 372 | workflow.add_edge("generate", END) 373 | app = workflow.compile() 374 | 375 | if __name__ == "__main__": 376 | main(app) 377 | -------------------------------------------------------------------------------- /llm_keyword_fetcher/llm_generator.py: -------------------------------------------------------------------------------- 1 | import os 2 | from dotenv import load_dotenv 3 | from langchain_openai import ChatOpenAI 4 | from langchain_core.prompts import ChatPromptTemplate 5 | from langchain_core.output_parsers import JsonOutputParser 6 | import json # For parsing JSON output 7 | 8 | ### Uncomment import 'pdb' this to use debugger in the app 9 | ### Use this code in between any file or function to stop debugger at any point pdb.set_trace() 10 | import pdb # Optional debugger 11 | 12 | ## Used to load .env file 13 | load_dotenv() 14 | 15 | os.environ["OPENAI_API_KEY"] = os.getenv("OPENAI_API_KEY") 16 | 17 | # Create output parser and LLM instances 18 | LLM = ChatOpenAI() 19 | 20 | 21 | def call_llm(topic, primary_keywords): 22 | """ 23 | This function calls the LLM to generate SEO-friendly meta keywords 24 | as a Python list for the provided topic and primary keyword. 25 | 26 | Args: 27 | topic (str): Topic of the blog. 28 | primary_keywords (str): Primary keyword for the blog. 29 | 30 | Returns: 31 | list: List of generated SEO-friendly meta keywords. 32 | """ 33 | 34 | prompt = ChatPromptTemplate.from_messages([ 35 | ("system", f"You are a world SEO enhancing engineer. Provide SEO-friendly meta keywords as a JSON object containing a 'keywords' key with a list of keywords, approximately 30-40 in total."), # Modified prompt 36 | ("user", "{input}") 37 | ]) 38 | chain = prompt | LLM | JsonOutputParser() 39 | 40 | response = chain.invoke({"input": f"Here is the topic of blog --> {topic} and primary keyword for blog --> {primary_keywords}"}) 41 | print(response) 42 | 43 | try: 44 | return response["keywords"] 45 | except (KeyError, json.JSONDecodeError) as e: 46 | print(f"Error parsing response: {e}") 47 | return [] -------------------------------------------------------------------------------- /postgres.py: -------------------------------------------------------------------------------- 1 | # CREATE TABLE public.embeddings ( 2 | # collection_key character varying, 3 | # serp_urls character varying[], -- Assuming ARRAY of character varying 4 | # created_at timestamp without time zone, 5 | # updated_at timestamp without time zone 6 | # ); 7 | 8 | import psycopg2 9 | 10 | import os 11 | from dotenv import load_dotenv 12 | load_dotenv() 13 | 14 | dbname = os.getenv("DB_NAME") 15 | user = os.getenv("DB_USER") 16 | password = os.getenv("DB_PASSWORD") 17 | host = os.getenv("DB_HOST") 18 | port = os.getenv("DB_PORT") 19 | 20 | def create_connection(): 21 | try: 22 | connection = psycopg2.connect(dbname=dbname, user=user, password=password, host=host, port=port) 23 | cursor = connection.cursor() 24 | return connection, cursor 25 | except psycopg2.Error as error: 26 | print("Error creating database connection:", error) 27 | return None, None 28 | 29 | def close_connection(connection): 30 | if connection: 31 | connection.close() 32 | print("Connection closed.") 33 | 34 | def create_record(collection_key, serp_urls): 35 | connection, cursor = create_connection() 36 | if connection and cursor: 37 | try: 38 | insert_query = """ 39 | INSERT INTO embeddings (collection_key, serp_urls) 40 | VALUES (%s, %s); 41 | """ 42 | cursor.execute(insert_query, (collection_key, serp_urls)) 43 | connection.commit() 44 | print("Record created successfully.") 45 | except psycopg2.Error as error: 46 | print("Error with database operation:", error) 47 | finally: 48 | close_connection(connection) 49 | 50 | def update_record(collection_key, new_serp_urls): 51 | connection, cursor = create_connection() 52 | 53 | if connection and cursor: 54 | try: 55 | # SQL query to update the serp_urls for a specific record 56 | update_query = """ 57 | UPDATE embeddings 58 | SET serp_urls = %s, updated_at = CURRENT_TIMESTAMP 59 | WHERE collection_key = %s; 60 | """ 61 | cursor.execute(update_query, (new_serp_urls, collection_key)) 62 | connection.commit() 63 | print("Record updated successfully.") 64 | except psycopg2.Error as error: 65 | print("Error with database operation:", error) 66 | finally: 67 | close_connection(connection) 68 | -------------------------------------------------------------------------------- /prompt.py: -------------------------------------------------------------------------------- 1 | def get_structure_template(): 2 | template = """ 3 | Please provide me structure of blog of on {question}, must have a structure, must have good headings and must have 10 headings. 4 | Important Commands --> Need to provide a good title and its must contain the key word --> {primary_keyword} 5 | Additional commands need to follow --> 6 | {structure_prompt} 7 | 8 | Here is the knowledge base for better understanding quality content blog --> {documents} 9 | NOTE --> Provide at least three sets of structure with different variations in heading. 10 | Note to Content Creator: Please ensure that the content you generate is entirely original and not copied from any existing sources. Avoid direct copy-pasting and provide unique insights and perspectives. Plagiarism is strictly prohibited, and we require 100% unique content for the blog. Make sure to use your own words and creativity to deliver high-quality, original content. Thank you for your understanding and adherence to these guidelines. 11 | Very Import Note --> Proivde structure as json 12 | Here is the DEMO structure to follow --> {blog_structure} 13 | """ 14 | return template 15 | 16 | # def get_content_generator_template(): 17 | # template = """ 18 | # You are a world class blog write and your name is Michael. You need to write a write a blog using the first object from the python dictionary. 19 | # Here is the strcuture dictionary object --> {structure} 20 | # Here is the knowledge base for better understanding quality content blog --> {documents} 21 | # Important Command --> You need to write atleast 200 words for each heading. 22 | # """ 23 | # return template 24 | 25 | def get_content_generator_template(): 26 | template = """ 27 | You are a world class writer of blogs and articles and your name is Michael. Will provide you the heading and can make subheadings as per your way of writing. 28 | Here is the heading on that you need to write content --> {heading} 29 | Here is the knowledge base for better understanding quality content blog --> {documents} 30 | Important Command --> You need to write atleast 200 words for the given topic. 31 | """ 32 | return template 33 | 34 | 35 | # def get_template(): 36 | # template = """ 37 | # Provide me Good Blog. 38 | # Here is the question on which you need to write a blog. Question --> {question} 39 | # Note --> Generates content without imposing a maximum token limit. Must contain {blog_words_limit} words. 40 | # As you are one of the best content writers in the world, your name is Joe. Today, we're tasked with writing a blog post, and it's essential that we adhere to the following ruleset: 41 | 42 | # 1) Make sure each paragraph must be 100 to 150 words long. 43 | # 2) The blog should be human-readable and unique. 44 | # 3) A conclusion should be included at the end of the blog. 45 | # 4) The blog should follow a common blogging structure. 46 | # 5) Blog should in multiple paragraphs 47 | # 6) Use meta keywords in blog writing 48 | # 7) Make sure not to copy anything from documents (knowledge base) directly, otherwise it will be plagiarism 49 | 50 | # I'll be provided with documents to review and understand the topics we'll be covering. Additionally, I'll provide meta keywords that we need to incorporate to enhance our ranking on Google search. 51 | 52 | # Here is the documents (knowledge base) that you need to utilise to write blog --> {documents}, primary meta keywords --> {primary_keyword} & here is addtional context ---> {structure_prompt} 53 | # """ 54 | # return template 55 | 56 | # def get_template(): 57 | # template = """ 58 | # Provide a comprehensive blog post on the topic of Natural Language Processing (NLP). 59 | 60 | # Additional Context: 61 | 62 | # {structure_prompt} 63 | 64 | # Meta Keywords: 65 | # {keywords_string} 66 | 67 | # Knowledge Base: 68 | # {documents} 69 | 70 | # Blog Minimum Word Limit and make sure blog contains: 71 | # {blog_words_limit} 72 | # """ 73 | # return template 74 | 75 | 76 | 77 | 78 | -------------------------------------------------------------------------------- /prompts/content_prompt.py: -------------------------------------------------------------------------------- 1 | ### Uncomment import 'pdb' this to use debugger in the app 2 | ### Use this code in between any file or function to stop debugger at any point pdb.set_trace() 3 | import pdb 4 | 5 | def content_template(blog): 6 | """ 7 | This function generates a formatted prompt for the LLM to create blog content. 8 | 9 | Args: 10 | blog (dict): Dictionary containing previously generated content (title, headings, content) 11 | if any. 12 | heading (str): Current heading for which content needs to be generated. 13 | 14 | Returns: 15 | str: The formatted prompt template. 16 | """ 17 | 18 | template = """ 19 | **NOTE: I NEED 100 PERCENT PLAGIARISM-FREE CONTENT. DO NOT DIRECTLY COPY AND PASTE FROM KNOWLEDGE PROVIDED BELOW.** 20 | 21 | {blog_prompt} 22 | 23 | You are a world-class writer of blogs and articles named Michael. You are going to provide headings of blog one by one and you need to write engaging and informative content for the following heading: 24 | 25 | **You need to generate content on total {total_headings} for this blog.** 26 | **Currently you are going to generate content for {current_heading} heading.** 27 | 28 |

{heading}

29 | 30 | **NOTE: Only add `Conclusion` and `Final Thoughts` in last heading of blog if required.** 31 | 32 | **Target Word Count:** {number_of_words_per_heading} 33 | 34 | **NOTE: Please provide content in proper

,

,

,