Generate a main character
Generate a Title
Generate a Plot
Generate the Chapters
Generate the Chapters’ plots
Generate the plot’s events
Write the book
Publish into a document
I can now write a book in 20 minutes on any subject! Here are two books written using this software:
Here is another one where I modified the prompts and left the LLM to add the characters it wanted:
Below is the code used in the video!
Installing the packages:
pip install python-dotenv langchain openai pypdf python-docx
The app.py file:
from dotenv import load_dotenv
load_dotenv()
from characters import MainCharacterChain
from structure import get_structure
from events import get_events
from writing import write_book
from publishing import DocWriter
subject = 'Machine War'
author='Ernest Hemingway'
genre='horror'
main_character_chain = MainCharacterChain()
profile = main_character_chain.run('Profile.pdf')
doc_writer = DocWriter()
title, plot, chapter_dict = get_structure(
subject,
genre,
author,
profile
)
summaries_dict, event_dict = get_events(
subject,
genre,
author,
profile,
title,
plot,
chapter_dict
)
book = write_book(
genre,
author,
title,
profile,
plot,
summaries_dict,
event_dict
)
doc_writer.write_doc(
book,
chapter_dict,
title
)
The utils.py file:
from langchain.chains import LLMChain
from langchain.chat_models import ChatOpenAI
class BaseStructureChain:
PROMPT = ''
def __init__(self) -> None:
self.llm = ChatOpenAI()
self.chain = LLMChain.from_string(
llm=self.llm,
template=self.PROMPT,
)
self.chain.verbose = True
class BaseEventChain:
PROMPT = ''
def __init__(self) -> None:
self.llm = ChatOpenAI(model_name='gpt-3.5-turbo-16k')
self.chain = LLMChain.from_string(
llm=self.llm,
template=self.PROMPT,
)
self.chain.verbose = True
The characters.py file:
import os
from langchain.document_loaders import PyPDFLoader
from langchain.chat_models import ChatOpenAI
from langchain.chains import LLMChain
class MainCharacterChain:
PROMPT = """
You are provided with the resume of a person.
Describe the person's profile in a few sentences and include that person's name.
Resume: {text}
Profile:"""
def __init__(self) -> None:
self.llm = ChatOpenAI()
self.chain = LLMChain.from_string(
llm=self.llm,
template=self.PROMPT
)
self.chain.verbose = True
def load_resume(self, file_name):
folder = './docs'
file_path = os.path.join(folder, file_name)
loader = PyPDFLoader(file_path)
docs = loader.load_and_split()
return docs
def run(self, file_name):
docs = self.load_resume(file_name)
resume = '\n\n'.join([doc.page_content for doc in docs])
return self.chain.run(resume)
The structure.py file:
from utils import BaseStructureChain, ChatOpenAI
class TitleChain(BaseStructureChain):
PROMPT = """
Your job is to generate the title for a novel about the following subject and main character.
Return a title and only a title!
The title should be consistent with the genre of the novel.
The title should be consistent with the style of the author.
Subject: {subject}
Genre: {genre}
Author: {author}
Main character's profile: {profile}
Title:"""
def run(self, subject, genre, author, profile):
return self.chain.predict(
subject=subject,
genre=genre,
author=author,
profile=profile
)
class PlotChain(BaseStructureChain):
PROMPT = """
Your job is to generate the plot for a novel. Return a plot and only a plot!
Describe the full plot of the story and don't hesitate to create new characters to make it compelling.
You are provided the following subject, title and main character's profile.
Make sure that the main character is at the center of the story
The plot should be consistent with the genre of the novel.
The plot should be consistent with the style of the author.
Consider the following attributes to write an exciting story:
{features}
subject: {subject}
Genre: {genre}
Author: {author}
Title: {title}
Main character's profile: {profile}
Plot:"""
HELPER_PROMPT = """
Generate a list of attributes that characterized an exciting story.
List of attributes:"""
def run(self, subject, genre, author, profile, title):
features = ChatOpenAI().predict(self.HELPER_PROMPT)
plot = self.chain.predict(
features=features,
subject=subject,
genre=genre,
author=author,
profile=profile,
title=title
)
return plot
class ChaptersChain(BaseStructureChain):
PROMPT = """
Your job is to generate a list of chapters.
ONLY the list and nothing more!
You are provided with a title, a plot and a main character for a novel.
Generate a list of chapters describing the plot of that novel.
Make sure the chapters are consistent with the plot.
The chapters should be consistent with the genre of the novel.
The chapters should be consistent with the style of the author.
Follow this template:
Prologue: [description of prologue]
Chapter 1: [description of chapter 1]
...
Epilogue: [description of epilogue]
Make sure the chapter is followed by the character `:` and its description. For example: `Chapter 1: [description of chapter 1]`
subject: {subject}
Genre: {genre}
Author: {author}
Title: {title}
Main character's profile: {profile}
Plot: {plot}
Return the chapter list and only the chapter list
Chapters list:"""
def run(self, subject, genre, author, profile, title, plot):
response = self.chain.predict(
subject=subject,
genre=genre,
author=author,
profile=profile,
title=title,
plot=plot
)
return self.parse(response)
def parse(self, response):
chapter_list = response.strip().split('\n')
chapter_list = [chapter for chapter in chapter_list if ':' in chapter]
chapter_dict = dict([
chapter.strip().split(':')
for chapter in chapter_list
])
return chapter_dict
def get_structure(subject, genre, author, profile):
title_chain = TitleChain()
plot_chain = PlotChain()
chapters_chain = ChaptersChain()
title = title_chain.run(
subject,
genre,
author,
profile
)
plot = plot_chain.run(
subject,
genre,
author,
profile,
title
)
chapter_dict = chapters_chain.run(
subject,
genre,
author,
profile,
title,
plot
)
return title, plot, chapter_dict
The events.py file:
from utils import BaseEventChain, ChatOpenAI
class ChapterPlotChain(BaseEventChain):
HELPER_PROMPT = """
Generate a list of attributes that characterized an exciting story.
List of attributes:"""
PROMPT = """
You are a writer and your job is to generate the plot for one and only one chapter of a novel.
You are provided with the title, the main plot of the novel and the main character.
Additionally, you are provided with the plots of the previous chapters and the outline of the novel.
Make sure to generate a plot that describe accurately the story of the chapter.
Each chapter should have its own arc, but should be consistent with the other chapters and the overall story.
The summary should be consistent with the genre of the novel.
The summary should be consistent with the style of the author.
Consider the following attributes to write an exciting story:
{features}
subject: {subject}
Genre: {genre}
Author: {author}
Title: {title}
Main character's profile: {profile}
Novel's Plot: {plot}
Outline:
{outline}
Chapter Plots:
{summaries}
Return the plot and only the plot
Plot of {chapter}:"""
def run(self, subject, genre, author, profile, title,
plot, summaries_dict, chapter_dict, chapter):
features = ChatOpenAI().predict(self.HELPER_PROMPT)
outline = '\n'.join([
'{} - {}'.format(chapter, description)
for chapter, description in chapter_dict.items()
])
summaries = '\n\n'.join([
'Plot of {}: {}'.format(chapter, summary)
for chapter, summary in summaries_dict.items()
])
return self.chain.predict(
subject=subject,
genre=genre,
author=author,
profile=profile,
title=title,
plot=plot,
features=features,
outline=outline,
summaries=summaries,
chapter=chapter
)
class EventsChain(BaseEventChain):
PROMPT = """
You are a writer and your job is to come up with a detailled list of events happens in the current chapter of a novel.
Those events describes the plot of that chapter and the actions of the different characters in chronological order.
You are provided with the title, the main plot of the novel, the main character, and a summary of that chapter.
Additionally, you are provided with the list of the events that were outlined in the previous chapters.
The event list should be consistent with the genre of the novel.
The event list should be consistent with the style of the author.
The each element of that list should be returned on different lines. Follow this template:
Event 1
Event 2
...
Final event
subject: {subject}
Genre: {genre}
Author: {author}
Title: {title}
Main character's profile: {profile}
Novel's Plot: {plot}
Events you outlined for previous chapters: {previous_events}
Summary of the current chapter:
{summary}
Return the events and only the events!
Event list for that chapter:"""
def run(self, subject, genre, author, profile,
title, plot, summary, event_dict):
previous_events = ''
for chapter, events in event_dict.items():
previous_events += '\n' + chapter
for event in events:
previous_events += '\n' + event
response = self.chain.predict(
subject=subject,
genre=genre,
author=author,
profile=profile,
title=title,
plot=plot,
summary=summary,
previous_events=previous_events,
)
return self.parse(response)
def parse(self, response):
event_list = response.strip().split('\n')
event_list = [
event.strip() for event in event_list if event.strip()
]
return event_list
def get_events(subject, genre, author, profile, title, plot, chapter_dict):
chapter_plot_chain = ChapterPlotChain()
events_chain = EventsChain()
summaries_dict = {}
event_dict = {}
for chapter, _ in chapter_dict.items():
summaries_dict[chapter] = chapter_plot_chain.run(
subject=subject,
genre=genre,
author=author,
profile=profile,
title=title,
plot=plot,
summaries_dict=summaries_dict,
chapter_dict=chapter_dict,
chapter=chapter
)
event_dict[chapter] = events_chain.run(
subject=subject,
genre=genre,
author=author,
profile=profile,
title=title,
plot=plot,
summary=summaries_dict[chapter],
event_dict=event_dict
)
return summaries_dict, event_dict
The writing.py file:
from utils import BaseEventChain
class WriterChain(BaseEventChain):
PROMPT = """
You are a novel writer. The novel is described by a list of events.
You have already written the novel up to the last event.
Your job is to generate the paragraphs of the novel about the new event.
You are provided with a the title, the novel's plot, a description of the main character and a plot of the current chapter.
Make sure the paragraphs are consistent with the plot of the chapter.
Additionally you are provided with the list of events you have already written about.
The paragraphs should be consistent with the genre of the novel.
The paragraphs should be consistent with the style of the author.
Genre: {genre}
Author: {author}
Title: {title}
Main character's profile: {profile}
Novel's Plot: {plot}
Previous events:
{previous_events}
Current Chapter summary: {summary}
Previous paragraphs:
{previous_paragraphs}
New event you need to write about now:
{current_event}
Paragraphs of the novel describing that event:"""
def run(self, genre, author, title, profile, plot,
previous_events, summary, previous_paragraphs, current_event):
previous_events = '\n'.join(previous_events)
return self.chain.predict(
genre=genre,
author=author,
title=title,
profile=profile,
plot=plot,
previous_events=previous_events,
summary=summary,
previous_paragraphs=previous_paragraphs,
current_event=current_event
)
def write_book(genre, author, title, profile, plot, summaries_dict, event_dict):
writer_chain = WriterChain()
previous_events = []
book = {}
paragraphs = ''
for chapter, event_list in event_dict.items():
book[chapter] = []
for event in event_list:
paragraphs = writer_chain.run(
genre=genre,
author=author,
title=title,
profile=profile,
plot=plot,
previous_events=previous_events,
summary=summaries_dict[chapter],
previous_paragraphs=paragraphs,
current_event=event
)
previous_events.append(event)
book[chapter].append(paragraphs)
return book
The publishing.py file:
import docx
class DocWriter:
def __init__(self) -> None:
self.doc = docx.Document()
def write_doc(self, book, chapter_dict, title):
self.doc.add_heading(title, 0)
for chapter, paragraphs_list in book.items():
description = chapter_dict[chapter]
chapter_name = '{}: {}'.format(
chapter.strip(), description.strip()
)
self.doc.add_heading(chapter_name, 1)
text = '\n\n'.join(paragraphs_list)
self.doc.add_paragraph(text)
self.doc.save('./docs/book.docx')
Share this post