├── .env.examle ├── .gitignore ├── LICENSE ├── README.md ├── conda.yml ├── ecrivai ├── __init__.py ├── add_blog.py ├── prompt_templates.py └── write.py ├── notebooks ├── example-gemini.ipynb └── example-openai.ipynb └── setup.py /.env.examle: -------------------------------------------------------------------------------- 1 | OPENAI_API_KEY=your-api-key-here 2 | GOOGLE_API_KEY=your-api-key-here -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Blog content 2 | content 3 | 4 | # Byte-compiled / optimized / DLL files 5 | __pycache__/ 6 | *.py[cod] 7 | *$py.class 8 | 9 | # C extensions 10 | *.so 11 | 12 | # Distribution / packaging 13 | .Python 14 | build/ 15 | develop-eggs/ 16 | dist/ 17 | downloads/ 18 | eggs/ 19 | .eggs/ 20 | lib/ 21 | lib64/ 22 | parts/ 23 | sdist/ 24 | var/ 25 | wheels/ 26 | pip-wheel-metadata/ 27 | share/python-wheels/ 28 | *.egg-info/ 29 | .installed.cfg 30 | *.egg 31 | MANIFEST 32 | 33 | # PyInstaller 34 | # Usually these files are written by a python script from a template 35 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 36 | *.manifest 37 | *.spec 38 | 39 | # Installer logs 40 | pip-log.txt 41 | pip-delete-this-directory.txt 42 | 43 | # Unit test / coverage reports 44 | htmlcov/ 45 | .tox/ 46 | .nox/ 47 | .coverage 48 | .coverage.* 49 | .cache 50 | nosetests.xml 51 | coverage.xml 52 | *.cover 53 | *.py,cover 54 | .hypothesis/ 55 | .pytest_cache/ 56 | 57 | # Translations 58 | *.mo 59 | *.pot 60 | 61 | # Django stuff: 62 | *.log 63 | local_settings.py 64 | db.sqlite3 65 | db.sqlite3-journal 66 | 67 | # Flask stuff: 68 | instance/ 69 | .webassets-cache 70 | 71 | # Scrapy stuff: 72 | .scrapy 73 | 74 | # Sphinx documentation 75 | docs/_build/ 76 | 77 | # PyBuilder 78 | target/ 79 | 80 | # Jupyter Notebook 81 | .ipynb_checkpoints 82 | 83 | # IPython 84 | profile_default/ 85 | ipython_config.py 86 | 87 | # pyenv 88 | .python-version 89 | 90 | # pipenv 91 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. 92 | # However, in case of collaboration, if having platform-specific dependencies or dependencies 93 | # having no cross-platform support, pipenv may install dependencies that don't work, or not 94 | # install all needed dependencies. 95 | #Pipfile.lock 96 | 97 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow 98 | __pypackages__/ 99 | 100 | # Celery stuff 101 | celerybeat-schedule 102 | celerybeat.pid 103 | 104 | # SageMath parsed files 105 | *.sage.py 106 | 107 | # Environments 108 | .env 109 | .venv 110 | env/ 111 | venv/ 112 | ENV/ 113 | env.bak/ 114 | venv.bak/ 115 | 116 | # Spyder project settings 117 | .spyderproject 118 | .spyproject 119 | 120 | # Rope project settings 121 | .ropeproject 122 | 123 | # mkdocs documentation 124 | /site 125 | 126 | # mypy 127 | .mypy_cache/ 128 | .dmypy.json 129 | dmypy.json 130 | 131 | # Pyre type checker 132 | .pyre/ 133 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 Ruan Pretorius 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![blog-link](https://img.shields.io/badge/ecrivai-blog-blue)](https://ruankie.github.io/ecrivai-blog-hugo/) 2 | [![auto-publish](https://github.com/ruankie/ecrivai-blog-hugo/actions/workflows/sheduled-publish.yml/badge.svg)](https://github.com/ruankie/ecrivai-blog-hugo/actions/workflows/sheduled-publish.yml) 3 | [![GitHub stars](https://img.shields.io/github/stars/ruankie/ecrivai)](https://github.com/ruankie/ecrivai/stargazers) 4 | [![GitHub forks](https://img.shields.io/github/forks/ruankie/ecrivai)](https://github.com/ruankie/ecrivai/network) 5 | [![GitHub contributors](https://img.shields.io/github/contributors/ruankie/ecrivai)](https://github.com/ruankie/ecrivai/graphs/contributors) 6 | [![GitHub last commit](https://img.shields.io/github/last-commit/ruankie/ecrivai)](https://github.com/ruankie/ecrivai/commits/main) 7 | 8 | 9 | # 🦜🔗✏️EcrivAI 10 | EcrivAI is a fully automated AI blog writer that uses LangChain and GPT type LLMs for topic selection and content generation. The content is published to [this blog](https://ruankie.github.io/ecrivai-blog-hugo/) 11 | 12 |
Star History 13 | 14 | 15 | 16 | 17 | 18 | Star History Chart 19 | 20 | 21 | 22 |
23 | 24 | ## Usage 25 | ### Prerequisites 26 | 1. 🐍 You will need a working install of [`conda`](https://www.anaconda.com/download#downloads). 27 | 2. 🔑 You will need an API key from OpenAI or Google. You can create one for free here: 28 | - [OpenIA](https://platform.openai.com/account/api-keys) - to use models like GPT4 29 | - [Google](https://ai.google.dev/) - to use models like Gemini 30 | 31 | ### Dev Environment Setup 32 | 1. Set up your API keys in a file called `.env` (see `.env.example` for an example) 33 | 2. Set up and activate conda environment 34 | ```bash 35 | conda env create -f conda.yml 36 | conda activate ecrivai 37 | ``` 38 | 39 | > If you are having trouble setting your environment variables with the `.env` file or you want to manually add them instead of using a `.env` file, you can set your environment variable in your `ecrivai` conda environment like this: 40 | >```shell 41 | > # set api key env var 42 | > conda env config vars set OPENAI_API_KEY="your-api-key-here" 43 | > conda env config vars set GOOGLE_API_KEY="your-api-key-here" 44 | > # re-activate env 45 | > conda activate base 46 | > conda activate ecrivai 47 | >``` 48 | 49 | ### CLI 50 | 51 | > Note: Remember to activate your `ecrivai` conda environment before doing this (see above) 52 | 53 | You can quickly generate a new original blog by running: 54 | 55 | ``` 56 | python ecrivai/add_blog.py 57 | ``` 58 | 59 | This will add a blog to a Markdown file in a directory called `content/`. You can also specify your own output directory by running this instead: 60 | ``` 61 | python ecrivai/add_blog.py --out-dir path/to/dir 62 | ``` 63 | -------------------------------------------------------------------------------- /conda.yml: -------------------------------------------------------------------------------- 1 | name: ecrivai 2 | dependencies: 3 | - python=3.12 4 | - pip 5 | - pip: 6 | - jupyterlab==4.0.9 7 | - langchain-core==0.1.4 8 | - langchain-google-genai==0.0.5 9 | - langchain==0.0.352 10 | - python-dotenv==1.0.0 11 | - black==23.12.1 12 | - pylint==3.0.3 13 | - -e . 14 | -------------------------------------------------------------------------------- /ecrivai/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ruankie/ecrivai/726ba7509126806a5ec59177ceca321103ff16ee/ecrivai/__init__.py -------------------------------------------------------------------------------- /ecrivai/add_blog.py: -------------------------------------------------------------------------------- 1 | import os 2 | import argparse 3 | import logging 4 | from dotenv import load_dotenv 5 | from langchain_google_genai import GoogleGenerativeAI 6 | from langchain.chains import LLMChain, SimpleSequentialChain 7 | from prompt_templates import content_prompt, topic_prompt 8 | from write import to_markdown, md2hugo 9 | 10 | 11 | load_dotenv() 12 | logging.basicConfig(level=logging.INFO, format="%(asctime)s [%(levelname)s] %(message)s") 13 | 14 | def get_blog_chain(): 15 | logging.info("Loading LLM config") 16 | # set up some parameters of the LLM 17 | content_llm_kwargs = { 18 | "temperature": 0.7, 19 | "model": "gemini-pro", 20 | "max_output_tokens": 2048 # ~ 1500 words (max for gemini-pro) 21 | } 22 | 23 | topic_llm_kwargs = { 24 | "temperature": 0.9, 25 | "model": "gemini-pro", 26 | "max_output_tokens": 50 # ~ 38words 27 | } 28 | 29 | # create LLMs with kwargs specified above 30 | content_llm = GoogleGenerativeAI(**content_llm_kwargs) 31 | topic_llm = GoogleGenerativeAI(**topic_llm_kwargs) 32 | 33 | # chain it all together 34 | topic_chain = LLMChain(llm=topic_llm, prompt=topic_prompt) 35 | content_chain = LLMChain(llm=content_llm, prompt=content_prompt) 36 | 37 | chain = SimpleSequentialChain( 38 | chains=[ 39 | topic_chain, 40 | content_chain 41 | ], 42 | verbose=True 43 | ) 44 | 45 | return chain 46 | 47 | if __name__ == "__main__": 48 | logging.info("Parsing CLI args") 49 | parser = argparse.ArgumentParser(description="A create a blog post as a Markdown file with ecrivai") 50 | parser.add_argument("--out-dir", type=str, default="./content", help="The path to the output directory") 51 | args = parser.parse_args() 52 | 53 | chain = get_blog_chain() 54 | logging.info("Generating topic and blog (can take some time)...") 55 | blog_text = chain.run("") 56 | logging.info("Blog content finished") 57 | 58 | out_dir = args.out_dir 59 | logging.info(f"Writing blog to Markdown file at: {out_dir}") 60 | md_file_name = to_markdown(blog_text, out_dir=out_dir) 61 | logging.info(f"Formatting file header for Hugo") 62 | blof_file_path = os.path.join(out_dir, md_file_name) 63 | md2hugo(blof_file_path, blof_file_path) 64 | logging.info(f"Done") 65 | 66 | -------------------------------------------------------------------------------- /ecrivai/prompt_templates.py: -------------------------------------------------------------------------------- 1 | from langchain.prompts import PromptTemplate 2 | 3 | topic_prompt = PromptTemplate( 4 | input_variables=["dummy"], 5 | template="""{dummy}Give me a single, specific topic to write an informative, engaging blog about. 6 | This blog topic must be relevant and appealing to many people so that many readers will want to read about it. 7 | The specific topic can be from a wide range of broader topics like physics, science, engineering, lifestyle, health, learning, teaching, history, technology, cryptocurrency, art, music, sport, business, economics, travel, entertainment, gaming, food, etc. 8 | Only give me the specific topic name after this prompt and nothing else. The topic is:""" 9 | ) 10 | 11 | keyword_prompt = PromptTemplate( 12 | input_variables=["topic"], 13 | template="Give me a list of 5 keywords that for using in blog about {topic}", 14 | ) 15 | 16 | content_prompt = PromptTemplate( 17 | input_variables=["topic"], 18 | template="""Write a blog post about: {topic}. 19 | The blog post should have the following characteristics: 20 | - The style and tone of the blog should be informative. You should write in the first person and use a friendly and engaging voice. 21 | - The length of the blog post should be roughly 1300 words. 22 | - The blog must contain these sections: introduction, body, and conclusion. 23 | - Each section should have a clear and catchy heading that summarizes its main point. 24 | - Use subheadings, bullet points, lists, quotes, or other elements to break up the text and make it easier to read. 25 | - You should explain why the topic is relevant and important for the audience, what problem or challenge it addresses, how it can be solved or improved, what benefits or advantages it offers, and what action or step the reader should take next. 26 | - Use relevant keywords strategically throughout the blog post to optimize it for search engines and attract more readers. You should also avoid keyword stuffing or using irrelevant or misleading keywords that do not match the content of the blog post. 27 | - Use a catchy title, a hook sentence, a clear thesis statement, a compelling story or anecdote, a surprising fact or statistic, a relevant question or challenge, a strong conclusion. 28 | - You should use these components to capture the attention of the reader and convey the main message and purpose of the blog 29 | - The output format of the entire blog post must be in Markdown. All headings, bullet points, links, etc. must use proper Markdown syntax 30 | - Start with the title of the blog as a first level Markdown heading 31 | Please follow these instructions carefully and write a high-quality and original blog post about: {topic}. 32 | Start immediately with the content of the blog post:""" 33 | ) 34 | -------------------------------------------------------------------------------- /ecrivai/write.py: -------------------------------------------------------------------------------- 1 | import os 2 | import re 3 | import datetime 4 | import logging 5 | 6 | 7 | logging.basicConfig(level=logging.INFO, format="%(asctime)s [%(levelname)s] %(message)s") 8 | 9 | def to_markdown(blog_text: str, out_dir: str = "content/") -> str: 10 | """ 11 | Given the blog text, write it to a markdown file in the 12 | output dir. The output file will have the date and time 13 | as a name. 14 | 15 | Args: 16 | blog_text (str): The body of the blog. 17 | out_dir (str): The output directory where the blog will be saved as a Markdown file. 18 | 19 | Returns: 20 | str: Output file name 21 | """ 22 | now = datetime.datetime.now() 23 | now_str = now.strftime("%Y%m%d%H%M") 24 | 25 | out_file_name = f"{now_str}.md" 26 | out_file_path = os.path.join(out_dir, out_file_name) 27 | 28 | if not os.path.exists(out_dir): 29 | logging.info(f"Creating output dir: {out_dir}") 30 | os.makedirs(out_dir) 31 | 32 | logging.info("Writing blog text to Markdown file") 33 | with open(out_file_path, "w") as f: 34 | f.write(blog_text.strip()) 35 | 36 | return out_file_name 37 | 38 | def md2hugo(md_file_path: str, out_file_path: str) -> None: 39 | """ 40 | Opens a Markown file and reformats the title so 41 | that Hugo can interpret it as a new blog post. 42 | 43 | Args: 44 | md_file_path (str): The path to the input Markdown file. 45 | out_file_path (str): The output path to save the formatted Markdown file. 46 | 47 | Returns: 48 | None 49 | """ 50 | logging.info("Extracting title form blog") 51 | 52 | # get original blog content 53 | with open(md_file_path, "r") as f: 54 | blog_text = f.read() 55 | 56 | # extract title (look for 1st level heading then 2nd level heading) 57 | match = re.search(r"^# (.*)$", blog_text, flags=re.MULTILINE) 58 | if match: 59 | blog_title = match.group(1) 60 | logging.info(f"Found blog title (1st level): {blog_title}") 61 | else: 62 | match = re.search(r"^## (.*)$", blog_text, flags=re.MULTILINE) 63 | if match: 64 | blog_title = match.group(1) 65 | logging.info(f"Found blog title (2nd level): {blog_title}") 66 | else: 67 | logging.warning("No blog title found in 1st or 2nd level headings") 68 | blog_title = "Blog Title" 69 | 70 | now = datetime.datetime.now(datetime.timezone.utc) 71 | now_str = now.isoformat(timespec="seconds") 72 | 73 | # new header 74 | header = f""" 75 | --- 76 | title: "{blog_title.replace('"', "'")}" 77 | date: {now_str} 78 | draft: false 79 | --- 80 | 81 | """ 82 | 83 | logging.info("Replacing file header") 84 | text_without_title = blog_text.split("\n")[1:] 85 | text_without_title = "\n".join(text_without_title).strip() 86 | new_blog_text = header + text_without_title 87 | with open(out_file_path, "w") as f: 88 | f.write(new_blog_text) -------------------------------------------------------------------------------- /notebooks/example-gemini.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "code", 5 | "execution_count": 17, 6 | "metadata": {}, 7 | "outputs": [], 8 | "source": [ 9 | "from dotenv import load_dotenv\n", 10 | "from langchain.chains import LLMChain, SimpleSequentialChain\n", 11 | "from ecrivai.prompt_templates import content_prompt, topic_prompt\n", 12 | "from ecrivai.write import to_markdown\n", 13 | "from langchain_google_genai import GoogleGenerativeAI\n" 14 | ] 15 | }, 16 | { 17 | "cell_type": "code", 18 | "execution_count": 2, 19 | "metadata": {}, 20 | "outputs": [ 21 | { 22 | "data": { 23 | "text/plain": [ 24 | "True" 25 | ] 26 | }, 27 | "execution_count": 2, 28 | "metadata": {}, 29 | "output_type": "execute_result" 30 | } 31 | ], 32 | "source": [ 33 | "load_dotenv()" 34 | ] 35 | }, 36 | { 37 | "cell_type": "code", 38 | "execution_count": 4, 39 | "metadata": {}, 40 | "outputs": [], 41 | "source": [ 42 | "# set up some parameters of the LLM\n", 43 | "content_llm_kwargs = {\n", 44 | " \"temperature\": 0.7,\n", 45 | " \"model\": \"gemini-pro\",\n", 46 | " \"max_output_tokens\": 1500 # ~ 1125 words\n", 47 | "}\n", 48 | "\n", 49 | "topic_llm_kwargs = {\n", 50 | " \"temperature\": 0.7,\n", 51 | " \"model\": \"gemini-pro\",\n", 52 | " \"max_output_tokens\": 50 # ~ 38words\n", 53 | "}" 54 | ] 55 | }, 56 | { 57 | "cell_type": "code", 58 | "execution_count": 5, 59 | "metadata": {}, 60 | "outputs": [], 61 | "source": [ 62 | "# create LLMs with kwargs specified above\n", 63 | "content_llm = GoogleGenerativeAI(**content_llm_kwargs)\n", 64 | "topic_llm = GoogleGenerativeAI(**topic_llm_kwargs)" 65 | ] 66 | }, 67 | { 68 | "cell_type": "code", 69 | "execution_count": 13, 70 | "metadata": {}, 71 | "outputs": [], 72 | "source": [ 73 | "# chain it all together\n", 74 | "topic_chain = LLMChain(llm=topic_llm, prompt=topic_prompt)\n", 75 | "content_chain = LLMChain(llm=content_llm, prompt=content_prompt)\n", 76 | "\n", 77 | "chain = SimpleSequentialChain(\n", 78 | " chains=[\n", 79 | " topic_chain,\n", 80 | " content_chain\n", 81 | " ],\n", 82 | " verbose=True\n", 83 | ")" 84 | ] 85 | }, 86 | { 87 | "cell_type": "code", 88 | "execution_count": 14, 89 | "metadata": {}, 90 | "outputs": [ 91 | { 92 | "name": "stdout", 93 | "output_type": "stream", 94 | "text": [ 95 | "\n", 96 | "\n", 97 | "\u001b[1m> Entering new SimpleSequentialChain chain...\u001b[0m\n", 98 | "\u001b[36;1m\u001b[1;3mThe Fascinating History of Artificial Intelligence\u001b[0m\n", 99 | "\u001b[33;1m\u001b[1;3m# The Fascinating History of Artificial Intelligence\n", 100 | "\n", 101 | "## Introduction\n", 102 | "\n", 103 | "Artificial intelligence (AI) has become a ubiquitous part of our lives. From the smartphones in our pockets to the self-driving cars on our roads, AI is revolutionizing the way we live, work, and interact with the world around us. But how did AI come to be? Let's take a journey through the fascinating history of AI, from its early roots to its current state-of-the-art capabilities.\n", 104 | "\n", 105 | "## The Early Days of AI\n", 106 | "\n", 107 | "The concept of artificial intelligence can be traced back to ancient times, with philosophers and scientists pondering the possibility of creating machines that could think and act like humans. However, it wasn't until the mid-20th century that AI began to take shape as a field of study. In 1950, Alan Turing published his seminal paper \"Computing Machinery and Intelligence,\" which proposed the Turing test as a way to measure a machine's ability to exhibit intelligent behavior. This paper sparked a surge of interest in AI research, and the field quickly began to grow.\n", 108 | "\n", 109 | "## The First AI Programs\n", 110 | "\n", 111 | "In the 1950s and 1960s, researchers developed the first AI programs, such as the Logic Theorist and the General Problem Solver. These programs were able to solve simple problems using logical reasoning and search algorithms. However, they were still very limited in their capabilities and could only handle a narrow range of tasks.\n", 112 | "\n", 113 | "## The Rise of Machine Learning\n", 114 | "\n", 115 | "In the 1970s, a new approach to AI emerged known as machine learning. Machine learning algorithms allow computers to learn from data without being explicitly programmed. This led to a significant breakthrough in AI research, as it enabled computers to solve problems that were previously impossible for them to tackle.\n", 116 | "\n", 117 | "## The Modern Era of AI\n", 118 | "\n", 119 | "In the 21st century, AI has experienced a rapid acceleration in development. The availability of massive amounts of data, the increasing computational power of computers, and the development of new algorithms have all contributed to the rise of modern AI. Today, AI is used in a wide range of applications, including image recognition, natural language processing, speech recognition, and robotics.\n", 120 | "\n", 121 | "## The Future of AI\n", 122 | "\n", 123 | "The future of AI is both exciting and uncertain. On the one hand, AI has the potential to solve some of the world's most pressing problems, such as climate change, disease, and poverty. On the other hand, there are also concerns about the potential risks of AI, such as job displacement, autonomous weapons, and the loss of human control.\n", 124 | "\n", 125 | "## Conclusion\n", 126 | "\n", 127 | "The history of AI is a fascinating journey that has taken us from the early dreams of philosophers to the powerful AI systems of today. As AI continues to evolve, it is important to consider the potential benefits and risks of this technology and to work towards a future where AI is used for the betterment of humanity.\u001b[0m\n", 128 | "\n", 129 | "\u001b[1m> Finished chain.\u001b[0m\n" 130 | ] 131 | } 132 | ], 133 | "source": [ 134 | "blog_text = chain.run(\"\")" 135 | ] 136 | }, 137 | { 138 | "cell_type": "code", 139 | "execution_count": 18, 140 | "metadata": {}, 141 | "outputs": [ 142 | { 143 | "name": "stderr", 144 | "output_type": "stream", 145 | "text": [ 146 | "2023-12-26 14:25:30,400 [INFO] Writing blog text to Markdown file\n" 147 | ] 148 | }, 149 | { 150 | "data": { 151 | "text/plain": [ 152 | "'202312261425.md'" 153 | ] 154 | }, 155 | "execution_count": 18, 156 | "metadata": {}, 157 | "output_type": "execute_result" 158 | } 159 | ], 160 | "source": [ 161 | "to_markdown(blog_text, out_dir=\"../content/\")" 162 | ] 163 | } 164 | ], 165 | "metadata": { 166 | "kernelspec": { 167 | "display_name": "langchain", 168 | "language": "python", 169 | "name": "python3" 170 | }, 171 | "language_info": { 172 | "codemirror_mode": { 173 | "name": "ipython", 174 | "version": 3 175 | }, 176 | "file_extension": ".py", 177 | "mimetype": "text/x-python", 178 | "name": "python", 179 | "nbconvert_exporter": "python", 180 | "pygments_lexer": "ipython3", 181 | "version": "3.12.0" 182 | }, 183 | "orig_nbformat": 4 184 | }, 185 | "nbformat": 4, 186 | "nbformat_minor": 2 187 | } 188 | -------------------------------------------------------------------------------- /notebooks/example-openai.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "code", 5 | "execution_count": 16, 6 | "metadata": {}, 7 | "outputs": [], 8 | "source": [ 9 | "from dotenv import load_dotenv\n", 10 | "from langchain.llms import OpenAI\n", 11 | "from langchain.chains import LLMChain, SimpleSequentialChain\n", 12 | "from ecrivai.prompt_templates import content_prompt, topic_prompt\n", 13 | "from ecrivai.write import to_markdown" 14 | ] 15 | }, 16 | { 17 | "cell_type": "code", 18 | "execution_count": 2, 19 | "metadata": {}, 20 | "outputs": [ 21 | { 22 | "data": { 23 | "text/plain": [ 24 | "True" 25 | ] 26 | }, 27 | "execution_count": 2, 28 | "metadata": {}, 29 | "output_type": "execute_result" 30 | } 31 | ], 32 | "source": [ 33 | "load_dotenv()" 34 | ] 35 | }, 36 | { 37 | "cell_type": "code", 38 | "execution_count": 3, 39 | "metadata": {}, 40 | "outputs": [], 41 | "source": [ 42 | "# set up some parameters of the LLM\n", 43 | "content_llm_kwargs = {\n", 44 | " \"temperature\": 0.7,\n", 45 | " \"model_name\": \"text-davinci-003\",\n", 46 | " \"max_tokens\": 1500 # ~ 1125 words\n", 47 | "}\n", 48 | "\n", 49 | "brief_llm_kwargs = {\n", 50 | " \"temperature\": 0.7,\n", 51 | " \"model_name\": \"text-davinci-003\",\n", 52 | " \"max_tokens\": 50 # ~ 38words\n", 53 | "}" 54 | ] 55 | }, 56 | { 57 | "cell_type": "code", 58 | "execution_count": 4, 59 | "metadata": {}, 60 | "outputs": [], 61 | "source": [ 62 | "# create LLMs with kwargs specified above\n", 63 | "content_llm = OpenAI(**content_llm_kwargs)\n", 64 | "brief_llm = OpenAI(**brief_llm_kwargs)" 65 | ] 66 | }, 67 | { 68 | "cell_type": "code", 69 | "execution_count": 5, 70 | "metadata": {}, 71 | "outputs": [], 72 | "source": [ 73 | "# chain it all together\n", 74 | "topic_chain = LLMChain(llm=brief_llm, prompt=topic_prompt)\n", 75 | "content_chain = LLMChain(llm=content_llm, prompt=content_prompt)\n", 76 | "\n", 77 | "chain = SimpleSequentialChain(\n", 78 | " chains=[\n", 79 | " topic_chain,\n", 80 | " content_chain\n", 81 | " ],\n", 82 | " verbose=True\n", 83 | ")" 84 | ] 85 | }, 86 | { 87 | "cell_type": "code", 88 | "execution_count": 6, 89 | "metadata": {}, 90 | "outputs": [ 91 | { 92 | "name": "stdout", 93 | "output_type": "stream", 94 | "text": [ 95 | "\n", 96 | "\n", 97 | "\u001b[1m> Entering new SimpleSequentialChain chain...\u001b[0m\n", 98 | "\u001b[36;1m\u001b[1;3m\n", 99 | "Mental Health Awareness.\u001b[0m\n", 100 | "\u001b[33;1m\u001b[1;3m\n", 101 | "\n", 102 | "## Mental Health Awareness Is Essential For Everyone \n", 103 | "\n", 104 | "Mental health is an important aspect of our lives that is often overlooked or ignored. Mental health awareness is essential for everyone, regardless of age, gender, or background. Mental illness can affect anyone at any time and can have a significant impact on a person’s quality of life. Mental health awareness is key to understanding mental illness, its symptoms, and how to take steps to seek help and support. \n", 105 | "\n", 106 | "### What is Mental Health Awareness?\n", 107 | "\n", 108 | "Mental health awareness is the process of understanding and recognizing the signs and symptoms of mental illness. It is also the process of understanding how to seek help and support from professionals, family members, and friends. Mental health awareness can help reduce the stigma associated with mental illness and empower people to speak up and seek help. Mental health awareness can also help people recognize when they or someone they care about may be experiencing mental health issues. \n", 109 | "\n", 110 | "### Why is Mental Health Awareness Important?\n", 111 | "\n", 112 | "Mental health awareness is essential for everyone because mental illness can affect anyone at any time. Mental illness can have a significant negative impact on a person’s life and can lead to a wide range of physical, emotional, and cognitive health issues. Mental health awareness is important because it can help people recognize the signs and symptoms of mental illness, which can lead to early intervention and treatment. It can also help reduce the stigma associated with mental illness and create a more supportive environment for those who are affected by mental health issues. \n", 113 | "\n", 114 | "### How Can We Increase Mental Health Awareness?\n", 115 | "\n", 116 | "The best way to increase mental health awareness is to educate ourselves and the people around us. We can start by learning about mental health and the different types of mental illness. We can also talk to our family, friends, and colleagues about mental health and teach them how to recognize the signs and symptoms of mental illness. We can also support organizations that work to reduce the stigma associated with mental illness and provide resources to those affected. \n", 117 | "\n", 118 | "### What Action Can We Take?\n", 119 | "\n", 120 | "The best way to take action is to be aware of our own mental health and to seek help if we are experiencing any symptoms of mental illness. If we know someone who is struggling, we should reach out and offer support. We can also spread awareness by sharing information about mental health on social media, participating in mental health awareness events, and donating to organizations that are working to reduce the stigma associated with mental illness. \n", 121 | "\n", 122 | "### Conclusion\n", 123 | "\n", 124 | "Mental health awareness is an essential part of our lives and it is important for everyone to be aware of the signs and symptoms of mental illness. Mental health awareness can help reduce the stigma associated with mental illness and empower people to seek help and support. We can increase mental health awareness by educating ourselves and the people around us, talking openly about mental health, and taking action to support those affected by mental illness. \u001b[0m\n", 125 | "\n", 126 | "\u001b[1m> Finished chain.\u001b[0m\n" 127 | ] 128 | } 129 | ], 130 | "source": [ 131 | "blog_text = chain.run(\"\")" 132 | ] 133 | }, 134 | { 135 | "cell_type": "code", 136 | "execution_count": 18, 137 | "metadata": {}, 138 | "outputs": [], 139 | "source": [ 140 | "to_markdown(blog_text, out_dir=\"../content/\")" 141 | ] 142 | } 143 | ], 144 | "metadata": { 145 | "kernelspec": { 146 | "display_name": "langchain", 147 | "language": "python", 148 | "name": "python3" 149 | }, 150 | "language_info": { 151 | "codemirror_mode": { 152 | "name": "ipython", 153 | "version": 3 154 | }, 155 | "file_extension": ".py", 156 | "mimetype": "text/x-python", 157 | "name": "python", 158 | "nbconvert_exporter": "python", 159 | "pygments_lexer": "ipython3", 160 | "version": "3.9.16" 161 | }, 162 | "orig_nbformat": 4 163 | }, 164 | "nbformat": 4, 165 | "nbformat_minor": 2 166 | } 167 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | from setuptools import find_packages, setup 2 | 3 | setup( 4 | name="ecrivai", 5 | packages=find_packages(), 6 | version='0.0.1' 7 | ) --------------------------------------------------------------------------------