umangchaudhry's picture
Update app.py
85d8d69
#import all packages
import gradio as gr
import pandas as pd
import random
from langchain.llms import OpenAI
from langchain.prompts import PromptTemplate
from langchain.document_loaders import PyPDFLoader
from langchain.text_splitter import CharacterTextSplitter
from langchain.chains import LLMChain
from langchain.output_parsers import CommaSeparatedListOutputParser
import re
import PyPDF2
def generate_summary(recipient, sender, input_letter):
# Load letter
loader = PyPDFLoader(input_letter.name)
input_letter = loader.load_and_split()
# model call
llm = OpenAI(temperature=0.6, openai_api_key="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")
# Insert API key above
summary_prompt_template = PromptTemplate(
template="""Provide a 2-3 sentence summary of what the {sender}
is asking of the {recipient}: {input_letter}
""",
input_variables=["sender", "recipient", "input_letter"],
)
formatted_prompt = summary_prompt_template.format(sender=sender,recipient=recipient,input_letter=input_letter)
summary = llm(formatted_prompt)
return summary
def generate_outcomes(recipient, sender, input_letter): #add any additional parameters here if necessary
# Load letter
loader = PyPDFLoader(input_letter.name)
input_letter = loader.load_and_split()
# model call
llm = OpenAI(temperature=0.8, openai_api_key="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")
# Insert API key above
# die roll template.
llm_die_roll_template = """
I am moderating an educational game in a college course where students roleplay
as historical figures during the French Revolution. Students immerse themselves
in the game, doing things that their characters would feasibly do. The game
does not have to exactly follow history, though. If the players decide to
take reasonable actions that are different than the actual events of the
revolution, then the roleplay's world can diverge from true events. One of
the actions that students can take is to write letters to non-player
characters. When I give you a copy of one of these letters, I would like
you to come up with a list of six possible outcomes.
The most likely outcomes would be a generally favorable response and a generally
unfavorable response. Specifics of the basic responses would differ
based on the content of the letter. There can be some potential outcomes that are a
little more creative, but historical context of the French Revolution must
be kept in mind. For example, perhaps the letter didn't even make it to
the recipient because it was intercepted by political rivals.
Above all, the generated outcomes should be realistic and provide
a good balance between favorable and unfavorable outcomes for the sender of the letter.
For this instance, I would like you to list potential outcomes that are
both creative and specific to the following letter written to {NPC} from a
student playing the role of {student_character}: {student_letter}.
"""
# die roll prompt template.
die_roll_prompt_template = PromptTemplate(
template=llm_die_roll_template,
input_variables=["NPC", "student_character", "student_letter"],
)
# die roll chain.
die_roll_chain = LLMChain(llm=llm, prompt=die_roll_prompt_template)
# outcomes
outcome_string = die_roll_chain.predict(
NPC=recipient, student_character=sender, student_letter=input_letter
)
# Remove the leading introduction
outcome_string = outcome_string.replace(
"Possible outcomes could include:", ""
).strip()
# Split the string into a list of outcomes
outcome_list = re.split("\s*\d+\.\s*", outcome_string)[1:]
df = pd.DataFrame(outcome_list, columns=["Outcomes"])
df['Odds'] = 0
return df
def roll_dice(df: pd.DataFrame):
"""
Roll the dice based on a dataframe of options and their odds.
Args:
df (pd.DataFrame): A dataframe where one column is the options and another column is the odds.
Returns:
str: The selected option after rolling the dice.
"""
# Convert dataframe to dictionary
options = df.set_index('Outcomes')['Odds'].to_dict()
# Convert the odds to integers
options = {k: int(v) for k, v in options.items()}
# Create a list with each option repeated according to its odds
options_list = [option for option, odds in options.items() for _ in range(odds)]
# Select a random option from the list
result = random.choice(options_list)
return result
def generate_letter(input_letter, dice_roll_result, sender_info, receiver_info):
# Load letter
loader = PyPDFLoader(input_letter.name)
input_letter = loader.load_and_split()
llm = OpenAI(max_tokens = 2056, temperature = 0.8, openai_api_key="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")
prompt = PromptTemplate(input_variables = ["original_letter", "sender", "recipient", "outcome"],
template = """As the moderator of an educational game in a college course, I oversee a roleplaying activity set during the French Revolution. Students are fully immersed in the game, assuming the roles of various historical figures from that period. While the game does not strictly adhere to historical events, it allows for reasonable deviations if the players' actions align with their characters' motivations.
One significant aspect of the game involves students writing letters to non-player characters, who respond based on their true historical characteristics. The following letter was written by from a student, playing the character {sender}, and sent to the non-player character {recipient}:
\n{original_letter}\n
I would like you to generate a letter in response from {recipient} to {sender}. {recipient} has decided to do the following: {outcome}. The letter in response should not copy that description word-for-word, but use it as a guide. Avoid specific details unless they are mentioned in the original letter. Most importantly, the generated letter should be as similar as possible to the writing style and tone of {recipient}.""" )
filled_in_prompt = prompt.format(original_letter = input_letter, sender = sender_info, recipient = receiver_info, outcome = dice_roll_result)
response_letter = llm(filled_in_prompt)
return response_letter
def display_original_letter(input_letter):
reader = PyPDF2.PdfReader(input_letter.name)
text = ''
for page_num in range(len(reader.pages)):
page = reader.pages[page_num]
text += page.extract_text()
return text
def display_odds_total(df):
df['Odds'] = df['Odds'].replace('', '0') # Replace empty strings with '0'
sum_odds = df['Odds'].astype(float).sum()
return sum_odds
def export_adjudication(adjudicator, sender, recipient, game_session, outcomes_table, selected_outcome, generated_letter):
# Define the filename
filename = "adjudication.txt"
# Convert the outcomes_table DataFrame to a string
outcomes_table_str = outcomes_table.to_string()
# Create the content by appending all the parameters
content = f"Adjudicator: {adjudicator}\nSender: {sender}\nRecipient: {recipient}\nGame Session: {game_session}\nOutcomes Table:\n{outcomes_table_str}\nSelected Outcome: {selected_outcome}\nGenerated Letter:\n{generated_letter}"
# Write the content to the file
with open(filename, 'w') as file:
file.write(content)
# Return the file path for download
return filename
def export_letter(letter):
# Define the filename
filename = "letter.txt"
# Write the text to the file
with open(filename, 'w') as file:
file.write(letter)
# Return the file path for download
return filename
with gr.Blocks() as demo:
#UI Elements
gr.Markdown("<h1 style='text-align: center;'>Letter Writing Assistant</h1>")
gr.Markdown("""\n ## Instructions \n 1. Upload the student letter in either PDF or docx format.
\n 2. Provide the information about the sender and receiever of the letter.
Please note that this is required even if this information is present in the letter.
\n 3. Click 'Generate Outcomes' to generate a list of possible outcomes based on the information in the letter.
\n 4. Use the resulting table of outcomes and odds to modify the outcomes if necessary and set the odds for
each of the outcomes to control the die roll.
\n 5. Click 'Roll Die' to determine the final outcome.
\n 6. If required, generate a response letter in the tone of the NPC (receiver) by clicking the 'Generate Letter' button.""")
gr.Markdown("\n ## Upload Student Letter")
adjudicator = gr.Textbox(label="Adjudicator")
input_letter = gr.File(file_types=[".docx", ".pdf"])
view_letter_button = gr.Button("View Letter")
view_letter_textbox = gr.Textbox(label="Original Letter")
with gr.Row():
sender = gr.Textbox(label="Sender (Student's Character)")
recipient = gr.Textbox(label="Receiver (NPC)")
game_session = gr.Textbox(label="Game Session")
generate_summary_button = gr.Button("Generate Summary")
summary_textbox = gr.Textbox(label="Generated Summary")
genrate_outcomes_button = gr.Button("Generate Outcomes")
gr.Markdown("\n ## Outcomes and Odds")
outcomes_table = gr.Dataframe(headers=['Outcomes', 'Odds'], col_count=2, interactive = True)
total_odds = gr.Textbox(label = "Odds Total")
with gr.Row():
roll_dice_button = gr.Button("Roll Dice")
dice_roll_result = gr.Textbox(label = "Selected Outcome")
generate_letter_button = gr.Button("Generate Letter")
letter_textbox = gr.Textbox(label = "Generated Letter")
download_letter_button = gr.Button("Download Generated Letter")
download_adjudication_button = gr.Button("Download Adjudication Summary")
#button functionality
outcomes_table.change(fn = display_odds_total, inputs = outcomes_table, outputs = total_odds)
generate_summary_button.click(fn=generate_summary, inputs=[recipient, sender, input_letter], outputs=summary_textbox)
view_letter_button.click(fn=display_original_letter, inputs = [input_letter], outputs = view_letter_textbox)
genrate_outcomes_button.click(fn=generate_outcomes, inputs=[recipient, sender, input_letter], outputs=outcomes_table) #add additional inputs if necessary
roll_dice_button.click(fn=roll_dice, inputs=outcomes_table, outputs=dice_roll_result)
generate_letter_button.click(fn=generate_letter, inputs=[input_letter, dice_roll_result, sender, recipient], outputs=letter_textbox)
download_letter_button.click(fn=export_letter, inputs = [letter_textbox], outputs = gr.File(label="Download Generated Letter"))
download_adjudication_button.click(fn=export_adjudication, inputs = [adjudicator, sender, recipient, game_session, outcomes_table, dice_roll_result, letter_textbox], outputs = gr.File(label="Download Adjudication Summary"))
demo.launch()