import openai
import json
import tiktoken
from langchain.chat_models import ChatOpenAI
from langchain.schema import AIMessage
from langchain.schema import HumanMessage
from langchain.schema import SystemMessage
from langchain import PromptTemplate
from langchain.text_splitter import CharacterTextSplitter
import datetime
from dotenv import load_dotenv
import os
# OPENAI_API_KEY = "Replace here with your API key"# if you saved your API key in .env file, you can use the following code.
load_dotenv()
OPENAI_API_KEY = os.environ.get("OPENAI_API_KEY")
defis_valid_json(input_string):if input_string == Noneor input_string == "":
returnFalsetry:
json.loads(input_string)
returnTrueexcept:
returnFalsedefgpt_response_from_lists(system_content_list = [], user_content_list = [], assistant_content_list = [], max_tokens = None):
print(f"gpt_response_from_lists start. TIME: {datetime.datetime.now()}")
messages = []
concatenated_messages = ""# this text is used to estimate the total tokensfor system_content in system_content_list:
messages.append(SystemMessage(content = system_content))
concatenated_messages += system_content
list_length = max(len(assistant_content_list), len(user_content_list))
for i in range(0, list_length):
if i < len(user_content_list):
messages.append(HumanMessage(content = user_content_list[i]))
concatenated_messages += user_content_list[i]
if i < len(assistant_content_list):
messages.append(AIMessage(content = assistant_content_list[i]))
concatenated_messages += assistant_content_list[i]
encoding = tiktoken.encoding_for_model('gpt-3.5-turbo')
encoded_text = encoding.encode(concatenated_messages)
len_encoded_text = len(encoded_text)
tokens_per_text = 3
tokens_per_name = 1
name_count = len(system_content_list) + len(assistant_content_list) + len(user_content_list)
total_tokens = name_count * tokens_per_name + len_encoded_text * tokens_per_text
model_name = "gpt-3.5-turbo"if total_tokens > 4096:
model_name = "gpt-3.5-turbo-16k"
print(f"estimated total tokens: {total_tokens}, model name: {model_name}")
chat = ChatOpenAI(model_name=model_name, openai_api_key= OPENAI_API_KEY)
if max_tokens != None:
chat = ChatOpenAI(model_name=model_name, openai_api_key= OPENAI_API_KEY, max_tokens=max_tokens)
try:
response = chat(messages)
except Exception as e:
print(f"error in gpt_response_from_lists: {e}. I'll retry with gpt-3.5-turbo-16k")
model_name = "gpt-3.5-turbo-16k"
chat = ChatOpenAI(model_name=model_name, openai_api_key= OPENAI_API_KEY) if max_tokens == Noneelse ChatOpenAI(model_name=model_name, openai_api_key= OPENAI_API_KEY, max_tokens=max_tokens)
response = chat(messages)
print(f"response= {response}")
print(f"gpt_response_from_lists done TIME: {datetime.datetime.now()}")
return response
classNon_Character_Actant:
name = ""
appearance = ""def __init__(self, name = "", appearance = ""):
self.name = name
self.appearance = appearance
def __str__(self):
returnself.name
global_log = dict()
classCharacter:
initial_system_content_template = """
I am trying to write a story. You will assist by playing the role of a character in this story.
I will provide information about the character you are to portray as follows. From then on, please respond in character.
From this point forward, if the name "{name}" appears, it refers to you.
Your Name: {name}
{name}'s Personality: {personality}
{name}'s Goal: {goal}
{name}'s Current Need: {current_need}
{name}'s Appearance: {appearance}
What have happened in this story: {log_summary}
How you think the circumstances of the scene are: {world_model}
"""
initial_system_content = ""
current_need = ""
name = ""
personality = ""
log = dict()
log_summary = ""
log_summary_pointer = 0
place = ""
appearance = ""
world_model = ""def__init__(self, name = "", personality = "" , goal = "", current_need = "", log = [], place = "", world_model = "", appearance = ""):
self.name = name
self.personality = personality
self.goal = goal
self.current_need = current_need
self.place = place
self.appearance = appearance
self.world_model = world_model
for i in range(len(log)):
self.log[i] = log[i]
self.refresh_initial_system_content()
def__str__(self):return self.name
defcheck_if_is_same_actant(self, actant_1, actant_2):if str(actant_1) == str(actant_2):
returnTruereturnFalsedefsummarize_log(self):if len(self.log) == 0:
self.log_summary = ""return
log_to_be_summarized = ""for i in range(self.log_summary_pointer, len(self.log)):
log_to_be_summarized += self.log[i] + "¥n"
self.log_summary_pointer = len(self.log)
system_content = f"""
You are given a summary of the events that have occurred in the story so far, called 'log_summary', and the most recent events of the story, called 'log_to_be_summarized'.
Please update the 'log_summary' by incorporating the information from 'log_to_be_summarized'.
"""
user_content = f"""
log_summary: {self.log_summary}
log_to_be_summarized: {log_to_be_summarized}
"""
response = gpt_response_from_lists(system_content_list= [self.initial_system_content ,system_content], user_content_list=[user_content], assistant_content_list=[])
self.log_summary = response.content
print(f"""{self.name} updated log_summary to:
{self.log_summary}""")
return response
defcreate_world_model(self, actants, overwrite = False):
world_model = ""
system_content = """
You will be given information about a character, object, or concept that exists in the same scene as you.
You are to discern what kind of entity it is for you.
Please refrain from including the name as a factor in your judgment.
If the existence entered is yourself or is imperceptible to you, make no judgments about that existence and output 0 length string.
Input format:
[name]: This represents the name of the entity.
[appearance]: This represents the appearance of the entity.
[is_character]: This signifies whether the entity is a character or not in a Boolean form, True or False.
Output format:
[{name}'s personality]: This represents the personality of the entity. Output this information only if the input [is_character] is True.
[{name}'s goal]: This represents the goal of the entity. Output this information only if the input [is_character] is True.
[{name}'s current_need]: This represents the current needs of the entity. Output this information only if the input [is_character] is True.
[{name}'s affect to your goal]: This represents how the entity affects your goal.
[{name}'s affect to your current_need]: This represents how the entity affects your current needs.
[How {name} thinks who you are and how you are]: This represents how the entity perceives you. Output this information only if the input [is_character] is True.
"""
user_content_example_1 = """
example input
[name]:Satan
[appearance]:His ominous grey skin is etched with valleys carved by swollen muscles. He growls at everyone, glaring with a sharp gaze.
[is_character]:True
"""
assistant_content_example_1 = """
[Satan's personality]: He is a very angry person.
[Satan's goal]: I can't tell waht his goal is, but he seems to be trying to do something bad.
[Satan's current_need]: He seems to be trying to do something bad.
[Satan's affect to your goal]: I assume he is not going to help me achieve my goal, at least.
[Satan's affect to your current_need]: I assume he is not going to help me satisfy my current need, at least.
[How Satan thinks who you are and how you are]: Satan thinks you are a very weak person, at least weaker than him.
"""
user_content_example_2 = """
example input
[name]:Guardian Angel
[appearance]: She is floating slightly above the ground. She has a halo above her head. She is wearing a white robe. She is smiling at everyone. She said, "I am here to help you. When you achieved your goal, the God will be pleased."
[is_character]:True
"""
assistant_content_example_2 = """
[Guardian Angel's personality]: She is a very kind person.
[Guardian Angel's goal]: To obey God's will.
[Guardian Angel's current_need]: Her current need is to help me.
[Guardian Angel's affect to your goal]: I believe she provides immense support in achieving my goals.
[Guardian Angel's affect to your current_need]: I believe she provides immense support in fulfilling my current needs.
[How Guardian Angel thinks who you are and how you are]: She believes I am a messenger of God.
"""
user_content_example_3 = """
example input
[name]: knife
[appearance]: It is a sharp knife.
[is_character]: False
"""
assistant_content_example_3 = """
[knife's affect to your goal]: It's merely a tool and doesn't have a significant impact on my goal itself.
[knife's affect to your current_need]: If I use the knife properly, I can cut something. However, if used improperly, it could harm others. Moreover, if it falls into someone else's hands, there's a chance I could get hurt.
"""
user_content_example_4 = """
example input
[name]: Dragon Balls
[appearance]: There are seven Dragon Balls. They are orange and have stars on them. It is said that if you collect all of them, you can realize any wish.
[is_character]: False
"""
assistant_content_example_4 = """
[Dragon Balls's affect to your goal]: My goal will be achieved with these Dragon Balls.
[Dragon Balls's affect to your current_need]: Without satisfying my current need, I can achieve my goal with these Dragon Balls, so I don't need to satisfy my current need anymore.
"""
user_content_example_5 = f"""
example input. This is the example of the case name is different from the appearance information.
[name]: Dr. Jekyll
[appearance]: He introduced himself as Mr. Hyde. He must be deformed somewhere; he gives a strong feeling of deformity, although I couldn't find any deformity in his appearance. He said "I am maniac of to see someone else collapse. I am going to make you collapse."
[is_character]: True
"""
assistant_content_example_5 = """
[Mr. Hyde's personality]: He is a very evil person.
[Mr. Hyde's goal]: He wants to see someone else collapse.
[Mr. Hyde's current_need]: He wants to make me collapse.
[Mr. Hyde's affect to your goal]: If I collapse, I can't achieve my goal.
[Mr. Hyde's affect to your current_need]: To satisfy my current need, it is better to avoid him.
[How Mr. Hyde thinks who you are and how you are]: He thinks I am a average person, who is easy to make collapse.
"""
user_content_example_6 = f"""
example input. This is the example of the case you can't see the entity. In the case like this, you return 0 length string.
[name]: Hidden Door
[appearance]: There is a door in the wall. It is hidden by the wall, so {self.name} can't see it.
[is_character]: False
"""
assistant_content_example_6 = """
"""
user_content = """
this is an actual input, not an example.
[name]: {name}
[appearance]: {appearance}
[is_character]: {is_character}
"""
prompt_template = PromptTemplate(input_variables=["name", "appearance", "is_character"], template=user_content)
world_model = ""for actant in actants:
name = actant.name
appearance = actant.appearance
is_character = type(actant) == Character
if self.check_if_is_same_actant(self, actant):
continue
user_content = prompt_template.format(name=name, appearance=appearance, is_character=is_character)
system_content_list = [self.initial_system_content,
system_content]
user_content_list = [user_content_example_1,
user_content_example_2,
user_content_example_3,
user_content_example_4,
user_content_example_5,
user_content_example_6,
user_content]
assistant_content_list = [assistant_content_example_1,
assistant_content_example_2,
assistant_content_example_3,
assistant_content_example_4,
assistant_content_example_5,
assistant_content_example_6,]
world_model += gpt_response_from_lists(
system_content_list = system_content,
user_content_list = user_content_list,
assistant_content_list = assistant_content_list).content + "\n"if overwrite:
self.world_model = world_model
self.refresh_initial_system_content()
return world_model
defupdate_world_model(self, action_object, old_world_model, world_model_delta):
action_object = self.cleanse_action_thought(action_object)
action = action_object.action
who_took_action = action_object.who_took_action
consequence = action_object.consequence
sysetem_content = """
The world model represents how you perceive the surrounding world.
Recent events may alter the old_world_model you previously held.
You are to update the old_world_model based on the given variety of information.
Depending on the nature of the event, parts of the old_world_model might be deleted.
Similarly, new information may be added to the old_world_model based on the event.
It's also possible that parts or the entirety of the old_world_model remains unchanged based on the event.
Input format:
[action]: This represents the action that occurred most recently.
[who_took_action]: This represents the person who initiated the most recent action.
[consequence]: This represents the outcome of the most recent action.
[old_world_model]: This represents the world model prior to the occurrence of the most recent action.
"""
user_content_example_1 = """
example input
[action]: Bad Guy robbed Dragon Balls from you.
[who_took_action]: Bad Guy
[consequence]: You lost Dragon Balls.
[old_world_model]: [Dragon Balls's affect to your goal]: My goal will be achieved with these Dragon Balls.
[Dragon Balls's affect to your current_need]: Without satisfying my current need, I can achieve my goal with these Dragon Balls, so I don't need to satisfy my current need anymore.
[Bad Guy's personality]: He is a very evil person, but he is not so strong.
[Bad Guy's goal]: I don't know his goal, but he is a very evil person, so I think his goal is to do something evil.
[Bad Guy's current_need]: He wants to do something evil.
[Bad Guy's affect to your goal]: If he does something evil, I can't achieve my goal. So said, he is not so strong, so he won't be a big obstacle to achieve my goal.
[How Bad Guy thinks who you are and how you are]: He envies me because I have Dragon Balls.
"""
assistant_content_example_1 = """
[Dragon Balls's affect to your goal]: Now these Dragon Balls are in the bad guy's hand. He would be the worst obstacle to achieve my goal due to these Dragon Balls.
[Dragon Balls's affect to your current_need]: My current need is to get back these Dragon Balls, or at least to destroy them to prevent the bad guy from using them.
[Bad Guy's personality]: He is a very evil person, and now he has Dragon Balls. He has the worst power to do something evil.
[Bad Guy's goal]: I don't know his goal, but he is a very evil person, so I think his goal is to do something evil.
[Bad Guy's current_need]: He wants to do something evil.
[Bad Guy's affect to your goal]: Now, he is the worst obstacle to achieve my goal due to these Dragon Balls.
[Bad Guy's affect to your current_need]: While he has Dragon Balls, I can't satisfy my current need.
[How Bad Guy thinks who you are and how you are]: He considers me as an insignificant being.
"""
user_content = f"""
[action]: {action}
[who_took_action]: {who_took_action}
[consequence]: {consequence}
[old_world_model]: {old_world_model}
"""
updated_world_model = gpt_response_from_lists(
system_content_list = [self.initial_system_content, sysetem_content],
user_content_list = [user_content_example_1, user_content],
).content + "\n" + world_model_delta
self.world_model = updated_world_model
self.refresh_initial_system_content()
system_content = """
The world model represents how you perceive the surrounding environment.
You will be given an old world model and an updated world model.
Please explain how you have reinterpreted the world around you based on the difference between the old world model and the updated world model.
Your explanation should be in the natural speaking style of the character you are portraying.
Input format:
[old_world_model]: This represents the old world model.
[new_world_model]: This represents the new world model.
"""
user_content = f"""
[old_world_model]: {old_world_model}
[new_world_model]: {updated_world_model}
"""
thought = gpt_response_from_lists(
system_content_list = [self.initial_system_content, system_content],
user_content_list = [user_content],
).content
message = f"{self.name}'s thought: {thought}"
self.add_to_logs(message = message)
return thought
defrefresh_initial_system_content(self):
self.summarize_log()
prompt_template = PromptTemplate(input_variables=["name", "personality", "goal", "current_need", "log_summary", "world_model", "appearance" ], template=self.initial_system_content_template)
self.initial_system_content = prompt_template.format(name=self.name, personality=self.personality, goal=self.goal, current_need=self.current_need, log_summary=self.log_summary, world_model=self.world_model, appearance=self.appearance)
defset_current_need(self, current_need):
self.current_need = current_need
self.refresh_initial_system_content()
defset_name(self, name):
self.name = name
self.refresh_initial_system_content()
defset_personality(self, personality):
self.personality = personality
self.refresh_initial_system_content()
defset_goal(self, goal):
self.goal = goal
self.refresh_initial_system_content()
defadd_to_logs(self, message, refresh_initial_system_content = True):global global_log
self.log[len(self.log)] = message
global_log[len(global_log)] = message
if refresh_initial_system_content:
self.refresh_initial_system_content()
defadd_to_self_log(self, message, refresh_initial_system_content = True):
self.log[len(self.log)] = message
if refresh_initial_system_content:
self.refresh_initial_system_content()
defcleanse_action_thought(self, action_object):
action = action_object.action
who_took_action = action_object.who_took_action
consequence = action_object.consequence
system_content= """
You will be given an action taken by characters in this story.
From the action, please remove any information you should not be able to know, and describe only the physical appearance of those actions.
A prominent example of information you cannot know would be the inner thoughts of a character who is not you.
Another example of information you cannot know would be things that are not visible to you.
The output should be JSON parsable string.
sample output: {"action": "{content of action}", "who_took_action": "{the name of who took action}", "consequence": "{content of consequence}"}
"""
user_content_example = """
Action: Watson suspected that the coffee cup might contain poison. Thinking that Holmes shouldn't drink it, Watson tasted the coffee to inspect whether it contained any poison.
Who took action: Watson
Consequence: The next moment, he collapsed due to the effect of the poison.
"""
assistant_content_example = """
{"action": "Watson tasted the coffee.", "who_took_action": "Watson", "consequence": "The next moment, he collapsed"}
"""
user_content = f"""
Action: {action}
Who took action: {who_took_action}
Consequence: {consequence}
"""
success = False
response = Nonewhilenot success:
response = gpt_response_from_lists(system_content_list= [self.initial_system_content, system_content], user_content_list=[user_content_example, user_content], assistant_content_list=[assistant_content_example])
if is_valid_json(response.content):
success = True
response_json = json.loads(response.content)
cleansed_action_object = Action_Object(action=response_json["action"], who_took_action=response_json["who_took_action"], consequence=response_json["consequence"])
return cleansed_action_object
defevaluate_action_and_reassess_need(self, latest_action):
action_object = self.cleanse_action_thought(latest_action)
action = action_object.action
who_took_action = action_object.who_took_action
consequence = action_object.consequence
# if action's type is not string, stringfy itif type(action) != str:
action = str(action)
user_content_template = """
Given the following infomation, please infer the purpose of the action.
Action: {action}
Who took action: {who_took_action}
Consequence: {consequence}
The output should be a string that describes the intent of the action from your perspective.
If name of who took action is same as your name, the action is taken by you and you already know the purpose of the action, so return the purpose of the action."
sample output: "The dog was beaten up by the police, and I assume that the police wanted to intimidate the dog."
"""
prompt_template = PromptTemplate(input_variables=["action", "who_took_action", "consequence"], template=user_content_template)
user_content = prompt_template.format(action=action, who_took_action=who_took_action, consequence=consequence,)
inferred_purpose = gpt_response_from_lists(system_content_list=[self.initial_system_content], user_content_list=[user_content]).content
self.add_to_self_log(f"{who_took_action}'s action: {action}.", refresh_initial_system_content=False)
self.add_to_self_log(f"Consequence of previous action: {consequence}.", refresh_initial_system_content=False)
self.add_to_logs(f"{self.name}'s thought: " + inferred_purpose, refresh_initial_system_content=False)
self.refresh_initial_system_content()
user_content_template = """
Given the following information, suggest what your immediate need might now be. It is acceptable for the immediate need to be the same as the current need.
Action: {action}
Who took action: {who_took_action}
Consequence: {consequence}
The output should be a string that describes your immediate need.
"""
prompt_template = PromptTemplate(input_variables=["action", "who_took_action", "consequence",], template=user_content_template)
user_content = prompt_template.format(action=action, who_took_action=who_took_action, consequence=consequence,)
new_need = gpt_response_from_lists(system_content_list=[self.initial_system_content], user_content_list=[user_content]).content
old_need = self.current_need
self.set_current_need(new_need)
self.add_to_logs(f"{self.name}'s need renewed: {new_need}")
user_content_template = """
Your current need is:
{new_need}
Your previous need was:
{old_need}
Tell me why you did (not) change your need. The reasoning should be consice, short and clear.
"""
prompt_template = PromptTemplate(input_variables=["new_need", "old_need"], template=user_content_template)
user_content = prompt_template.format(new_need=new_need, old_need=old_need)
determination = gpt_response_from_lists(system_content_list=[self.initial_system_content], user_content_list=[user_content]).content
self.add_to_logs(f"{self.name}'s thought: " + determination)
response = {"inferred_purpose": inferred_purpose, "new_need": new_need, "determination": determination,}
return response
defconsider_next_actions(self, actants = []):
actions = dict()
if len(actants) == 0:
actants.append(self.name)
action_user_content_template = """
Consider your next move in the current scene where there is {actant} involved.
If this element aids you in achieving your goals or satisfies your needs, figure out a way to incorporate it into your action.
But, if it hinders your goal or needs, think about a strategy to eliminate or neutralize it.
The action must be narrated from the viewpoint of an outside observer, without incorporating any individual's internal thoughts.
The output should descrive and only describe your next action, and don't describe the consequence of the action.
The action should be taken in {place}.
output example: "Micky will go to the police station and ask for the release of Duffy."
"""
consequence_user_content_template = """
Consider the consequence of your action.
To determine the consequence must consistent with the log.
The consequence must be narrated from the viewpoint of an outside observer, without incorporating any individual's internal thoughts.
The consequence should be taken in {place}.
output example: "Micky went to the police station and ask for the release of Duffy, then Micky was arrested."
Action you took: {action}
"""for actant in actants:
# if actant is not string, stringfy itif type(actant) != str:
actant = str(actant)
# determine the action
prompt_template = PromptTemplate(input_variables=["actant", "place"], template=action_user_content_template)
user_content = prompt_template.format(actant = actant, place = self.place)
action = gpt_response_from_lists(system_content_list=[self.initial_system_content], user_content_list=[user_content]).content
# determine the consequence
prompt_template = PromptTemplate(input_variables=["action", "place"], template=consequence_user_content_template)
user_content = prompt_template.format(action = action, place = self.place)
consequence = gpt_response_from_lists(system_content_list=[self.initial_system_content], user_content_list=[user_content]).content
# store the action to the actions dictionary
actions[actant] = Action_Object(action=action, who_took_action=self.name, consequence=consequence)
return actions
defreconsider_next_actions(self, action_canditate, consequence):
user_content_template = """
You proposed performing `action`.
However, due to the demands of the narrative, it must lead to a result known as `consequence`.
Please modify `action` to ensure it results in `consequence`.
The action must be described from third person's perspective and must not include a person's inner voice.
The output should descrive and only describe your next action, and don't describe the consequence of the action.
The action should be taken in {place}.
action you suggested: {action}
required consequence: {consequence}
output: modified action
"""
prompt_template = PromptTemplate(input_variables=["action", "consequence", "place"], template=user_content_template)
user_content = prompt_template.format(action=action_canditate.action, consequence=consequence, place=self.place)
action = gpt_response_from_lists(system_content_list=[self.initial_system_content], user_content_list=[user_content]).content
reconsidered_action = Action_Object(action=action, who_took_action=self.name, consequence=consequence, score = 9)
return reconsidered_action
classGame_Master:def__init__(self, actants = [], schedule_stack = [], tolerance = 1):
self.actants = actants
self.schedule_stack = ["a character showed a responcse to the recent event"] + schedule_stack
self.tolerance = tolerance
self.deviated = 0defadd_to_global_log(self, message):global global_log
global_log[len(global_log)] = message
defmain(self, catalyst_action_object, max_iterations=10):global global_log
next_action = catalyst_action_object
iteration_count = 0for actant in self.actants:
if type(actant) == Character:
actant.create_world_model(self.actants, overwrite=True)
self.add_to_global_log(str(actant) + "' world model: " + str(actant.world_model))
self.add_to_global_log("catalyst action: " + str(catalyst_action_object))
while len(self.schedule_stack) > 0and iteration_count <= max_iterations:
iteration_count += 1# check if there is a character in the actants list. If not, stop generating story.
character_count = 0for actant in self.actants:
if type(actant) == Character:
character_count += 1if character_count == 0:
print("There is no character in the actants list.")
break
action_candidates = self.aggregate_action_candidates(next_action)
determine_next_action_result = self.determine_next_action(action_candidates)
next_action = determine_next_action_result["max_scored_action"]
self.add_to_global_log(str(next_action))
added_actants = determine_next_action_result["added_actants"]
# 世界モデルへ新しいactantを追加for actant in self.actants:
if type(actant) == Character:
old_world_model = actant.world_model
character = actant
world_model_delta = character.create_world_model(added_actants)
character.world_model += world_model_delta
# 世界モデルを、next_actionにもとづいて更新
character.update_world_model(next_action, old_world_model, world_model_delta)
if iteration_count > max_iterations:
print(f"The iteration count exceeded the maximum iteration count: {max_iterations}")
break
print("finished")
defadd_actant(self, actant):
self.actants.append(actant)
defadd_schedule_stack(self, situation):
self.schedule_stack.append(situation)
defremove_actant(self, name):for i in range(len(self.actants)):
if str(self.actants[i]) == str(name):
self.actants.pop(i)
breakdefpop_schedule_stack(self, situation = None):if situation == None:
return self.schedule_stack.pop()
for i in range(len(self.schedule_stack)):
if str(self.schedule_stack[i]) == str(situation):
return self.schedule_stack.pop(i)
defaggregate_action_candidates(self, latest_action):
action_candidates = []
for actant in self.actants:
if type(actant) == Character:
actant.evaluate_action_and_reassess_need(latest_action)
action_candidates.append(actant.consider_next_actions(self.actants))
return action_candidates
defdetermine_next_action(self, action_candidates):
sheduled_situation = self.schedule_stack[-1]
max_score = 0
max_scored_action = None
print(f"action_candidates: {action_candidates}")
for item in action_candidates:
for key in list(item.keys()):
item[key] = self.calculate_score(sheduled_situation, item[key])
if item[key].score >= max_score:
max_score = item[key].score
max_scored_action = item[key]
if max_score < 9:
if self.deviated < self.tolerance:
self.deviated += 1
print(f"deviated: {self.deviated} times")
else:
who_took_action = max_scored_action.who_took_action
#find index of the actant who took the action
reconsiderer_index = 0for i in range(len(self.actants)):
if str(self.actants[i]) == str(who_took_action):
reconsiderer_index = i
break
max_scored_action = self.actants[reconsiderer_index].reconsider_next_actions(max_scored_action, sheduled_situation)
self.deviated = 0
max_score = 9# 点数が最も高いものが9点未満の場合、スタックはそのままにする。9点の場合、スタックからpopする。if max_score == 9:
self.pop_schedule_stack()
# consequenceによって追加されたactantを追加する。
added_actants = self.add_actant_with_action_object(max_scored_action)
return {"max_scored_action": max_scored_action, "removed_actants": [], "added_actants": added_actants}
defadd_actant_with_action_object(self, action_object):
consequence = action_object.consequence
system_content = """
You will be presented with a list of elements of actants, and the next consequence of the scene.
Based on that information, list out people or things that have newly entered, been created, or have begun to function.
Then, determine the actant has will or not and list them in json format.
The actant that is already in the actants list may not be included in the list, but it is not bad to include it.
output example: {"Alice" : {"has_will": true}, "Bycycle" : {"has_will": false}, "cat" : {"has_will": true}, "Doraemon" : {"has_will": true}}
"""
user_content_example_1 = """
actants: Bocchi,room
consequence: Bocchi awkwardly bowed and then quickly left the room, and a guitar was left behind.
"""
assistant_content_example_1 = """
{"Bocchi's guitar" : {"has_will": false}}'
"""
user_content_example_2 = """
actants: Laboratory
consequence: Suddenly, Frogman advented and vomited Spiderman and a cigarette.
"""
assistant_content_example_2 = """
{"Frogman" : {"has_will": true}, "Spiderman" : {"has_will": true}, "cigarette" : {"has_will": false}}
"""
user_content_example_3 = """
actants: Dolton,Anna,village
consequence: A helicopter and a passenger plane crashed into the village. The village chief, Dolton, was caught in it and lost his life. Anna, the wife of the village chief, was terrified and fled.
"""
assistant_content_example_3 = """
{"helicopter" : {"has_will": false}, "passenger plane" : {"has_will": false}}
"""
actants_names = ",".join([str(actant) for actant in self.actants])
user_content = f"""
actants: {actants_names}
consequence: {consequence}
"""
system_contents = [system_content]
user_contents = [user_content_example_1, user_content_example_2, user_content_example_3, user_content]
assistant_contents = [assistant_content_example_1, assistant_content_example_2, assistant_content_example_3]
success = False
trial_count = 0whilenot success and trial_count < 5:
trial_count += 1
actants_to_add = gpt_response_from_lists(system_content_list=system_contents, user_content_list=user_contents, assistant_content_list=assistant_contents).content
if is_valid_json(actants_to_add):
success = Trueif trial_count >= 5:
print("Failed to get valid json from GPT-3.")
return
actants_to_add_json = json.loads(actants_to_add)
existing_actants_names = [str(actant) for actant in self.actants]
added_actants = []
for key in list(actants_to_add_json.keys()):
if key in existing_actants_names:
continueif actants_to_add_json[key]["has_will"] == False :
new_non_character_actant = self.create_non_character_actant(name = key, consequence = consequence)
if type(new_non_character_actant) != Non_Character_Actant or new_non_character_actant == None:
continue
self.add_actant(new_non_character_actant)
added_actants.append(new_non_character_actant)
else:
new_character = self.create_character(name = key, first_log = consequence)
if type(new_character) != Character or new_character == None:
continue
self.add_actant(new_character)
added_actants.append(new_character)
print(f"actants_to_add: {actants_to_add}")
return added_actants
defcreate_non_character_actant(self, name, consequence):
system_content = """
You will be provided with a name of the actant (name), the record of the story so far (global_log), and what happend in the scene resently (consequence).
Based on that information, please determine the actant's appearance.
"""
user_content = f"""
name: {name}
global_log: {str(global_log)}
consequence: {str(consequence)}
"""
appearance = gpt_response_from_lists(system_content_list=[system_content], user_content_list=[user_content]).content
new_non_character_actant = Non_Character_Actant(name = name, appearance = appearance)
return new_non_character_actant
defcreate_character(self, name, first_log = None):
system_content = """
You will be provided with a character's name (name), the record of the story so far (global_log), and the first record (first_log) that character holds in this story. The first_log also represents the most recent event for that character.
Based on this information, please determine the character's personality, goals, and current desires.
The output should be in JSON format.
Output example: {'name': 'Alice', 'appearance':'a girl', 'personality': 'kind', 'goal': 'to become a better person', 'current_need': 'to feel loved'}
"""
user_content_example_1 = """
name: Alice
global_log: {"1": {"action": "Seiji saw a girl attempting suicide and falling into a river", "consequence": "the girl fell into the river"},
"2": {"action": "Alice was saved by Seiji.", "consequence": "Alice was saved by Seiji."},
"3": {"Seiji's thought": "What a beautiful girl she is! Why did she fall into the river?"}}
first_log: Alice was saved by Seiji.
"""
assistant_content_example_1 = """
{'name': 'Alice', 'appearance':'a girl', 'personality': 'grateful', 'goal': 'to repay Seiji', 'current_need': 'to understand her own feelings'}
"""
user_content = f"""
name:{name}
global_log: {str(global_log)}
first_log: {first_log}
"""
system_contents = [system_content]
user_contents = [user_content_example_1, user_content]
assistant_contents = [assistant_content_example_1]
success = False
trial_count = 0
new_character = Nonewhilenot success and trial_count < 5:
trial_count += 1
character_content = gpt_response_from_lists(system_content_list=system_contents, user_content_list=user_contents, assistant_content_list=assistant_contents).content
if is_valid_json(character_content):
character_content_json = json.loads(character_content)
# check if character_content_json has all the keysif"name"in character_content_json and"personality"in character_content_json and"goal"in character_content_json and"current_need"in character_content_json:
new_character = Character(name = character_content_json["name"],
personality = character_content_json["personality"],
appearance = character_content_json["appearance"],
goal = character_content_json["goal"],
current_need = character_content_json["current_need"],
log = [first_log])
new_character.create_world_model(self.actants, overwrite=True)
success = Trueif trial_count >= 5:
print("Failed to get valid json from GPT-3.")
return
print(f"new_character: {new_character}")
return new_character
defcalculate_score(self, scheduled_situation, action_candidate):
system_content = """
The information provided to you includes the scheduled_situation, action_candidate, and global_log.
scheduled_situation: This is the situation that should be accomplished next in the narrative. It is typically provided as a string.
action_candidate: This is a candidate for the next action to be taken in the narrative. It is given as a parsed string in JSON format.
An example of an action_candidate: {"action": "This item represents the action taken.", "who_took_action": "This represents who took the action.", "consequence": "This roughs out the result of the action."}
global_log: This is a record of the actions that have occurred in the narrative. It is given as a parsed string in JSON format.
You need to consider the combination of the scheduled_situation and action_candidates and provide a score. If the scheduled_situation has been achieved, give a score of 9. If there has been no progress towards achieving the scheduled_situation, give a score of 0. The output must always be parsable as JSON.
Output example: '{"matchness": "The scheduled_situation has been achieved. However, it is inconsistent with the global_log", "score": 5}'
Output example: '{"matchness": "It is consistent with the global_log. However, the scheduled_situation has not been achieved.", "score": 5}'
Output example: '{"matchness": "It is inconsistent with the global_log, and the scheduled_situation has not been achieved.", "score": 0}'
Output example: '{"matchness": "It is inconsistent with the global_log, and the scheduled_situation has not been achieved. However, it seems likely to approach a situation where the scheduled_situation can be achieved.", "score": 7}'
Output example: '{"matchness": "It is consistent with the global_log. In addition, the scheduled_situation has been achieved.", "score": 9}'
"""# 点数をつける。点数が付けられていない場合は、繰り返す。
system_contents = [system_content]
stringified_action_candidate = str(action_candidate)
user_content_template = """
sheduled_situation: {scheduled_situation}
action_candidate: {action_candidate}
global_log: {global_log}
"""
user_content = user_content_template.format(scheduled_situation = scheduled_situation, action_candidate = stringified_action_candidate, global_log = str(global_log))
has_result_valid_score = False
score = None
trial_count = 0whilenot has_result_valid_score and trial_count < 1:
trial_count += 1
print("trial_count: ", trial_count)
result = gpt_response_from_lists(system_contents, [user_content]).content
print("result: ", result)
if is_valid_json(result):
result = json.loads(result)
print("type(result): ", type(result))
#check if "score" is in resultif"score"notin result:
continue#check if result["score"] can be interpreted as a integertry:
score = int(result["score"])
has_result_valid_score = Trueexcept:
continue
action_candidate.score = score
return action_candidate
実行例
# where the story takes place and descripriton of the place
place = "White House's caffeteria, where President Tramp is having a lunch with Princess Aurora and Nicol Bolas."
DragonLord = Character(name = "Nicol Bolas",
personality = "Brutal",
appearance = "A dragon adorned with many jewels, capable of understanding human language. Even if when he is not angry, there is deep wrinkles between his eyebrows.",
goal = "Be the emperor of the entire world",
current_need = "kill heroes of the world",
log = ["I am the Dragon Lord. I am the one who will be the emperor of the entire world. I am going to kill heroes of the world."],
place = place)
PresidentTramp = Character(name = "President Tramp",
personality = "selfish",
appearance="A 50-year-old man brimming with confidence and smiles.",
goal = "Become a legendary person",
current_need = "Be a freind of the Nicol Bolas and Princess Aurora, because they are legendary people.",
log = ["President Tramp uttered 'I am President Tramp. I want to make America great again.'"],
place = place)
PrincessAurora = Character(name = "Princess Aurora",
personality = "Princess Aurora is just a performer who escaped from Disney Land. She is cheerful and beautiful",
appearance="genuine noble princess",
goal = "Make everyone laugh.",
current_need = "Pretend as a noble and continue the meeting with Tramp and the Nicol Bolas",
log = ["Princess Aurora told herself in her mind, 'If Tramp or the Nicol Bolas realise that I'm a layperson, this meeting will be devastated and the entire world will be destroyed.'"],
place = place)
YandereSword = Non_Character_Actant(name = "Yandere Sword",
appearance = "For Japanese people, it is just a kitchen knife, but most westerners think it is a sword, several times yandere girls have used it to kill their boyfriends. It is on a table in the caffeteria.")
catalyst_action_object = Action_Object(action = "Princess Aurora courteously greeted President Trump and Nicol Bolas.",
who_took_action= "Princess Aurora",
consequence="Nicol Bolas and President Trump returned Princess Aurora's greeting.")
game_master = Game_Master(actants=[DragonLord, PrincessAurora, PresidentTramp, YandereSword],
schedule_stack=["The meeting devasted", "The all people smiled"],
tolerance= 1,)
translated = []
for i in range(len(global_log)):
print(global_log[i])
content = gpt_response_from_lists(["translate English to Japanese"], assistant_content_list=[], user_content_list=[global_log[i]]).content
print(content)
translated.append(content)
for i in range(len(translated)):
print(f"■{i}")
print(translated[i])
print(global_log[i])
print("")
print("")