Spaces:
Runtime error
Runtime error
#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() |