Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update v0.38 #99

Merged
merged 5 commits into from
Nov 23, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions llm_config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ DUNGEON_LOCATION_TEMPLATE: '{"index": (int), "name": "", "description": 25 words
CHARACTER_TEMPLATE: '{"name":"", "description": "50 words", "appearance": "25 words", "personality": "50 words", "money":(int), "level":"", "gender":"m/f/n", "age":(int), "race":"", "occupation":""}'
FOLLOW_TEMPLATE: '{{"response":"yes or no", "reason":"50 words"}}'
ITEM_TYPES: ["Weapon", "Wearable", "Health", "Money", "Trash"]
PRE_PROMPT: 'You are a creative game keeper for an interactive fiction story telling session. You craft detailed worlds and interesting characters with unique and deep personalities for the player to interact with. Always follow the instructions given, never acknowledge the task or speak directly to the user or respond with anything besides the request.'
PRE_PROMPT: 'You are a creative game keeper for an interactive fiction story telling session. You craft detailed worlds and interesting characters with unique and deep personalities for the player to interact with. Always follow the instructions given, never acknowledge the task or speak directly to the user or respond with anything besides the request. Do not invent new characters or locations unless prompted. Use the information provided to create a rich and engaging story. Remember to keep the story consistent with the context provided.'
BASE_PROMPT: '<context>{context}</context>\n[USER_START] Rewrite [{input_text}] in your own words. The information inside the <context> tags should be used to ensure it fits the story. Use about {max_words} words.'
DIALOGUE_PROMPT: '<context>{context}</context>\nThe following is a conversation between {character1} and {character2}; {character2}s sentiment towards {character1}: {sentiment}. Write a single response as {character2} in third person pov, using {character2} description and other information found inside the <context> tags. If {character2} has a quest active, they will discuss it based on its status. Respond in JSON using this template: """{dialogue_template}""". [USER_START]Continue the following conversation as {character2}: {previous_conversation}'
COMBAT_PROMPT: '<context>{context}</context>\nThe following is a combat scene between {attackers} and {defenders} in {location}. [USER_START] Describe the following combat result in about 150 words in vivid language, using the characters weapons and describe their health status without mentioning numbers: 1.0 is highest, 0.0 is dead. <combat result> {input_text}</combat result>'
Expand All @@ -35,14 +35,14 @@ START_LOCATION_PROMPT: '[Story context: {story_context}]; Zone info: {zone_info}
STORY_PLOT_PROMPT: "[USER_START] For an RPG described as {story_type} set in a world described as {world_mood}, {world_info}. Based on the following background: {story_background} write an innovative and engaging plot that the player can become part of. Use less than 400 words."
WORLD_ITEMS: '<context>{context}</context>\n[USER_START] Using the information supplied inside the <context> tags, come up with 7 common items that can be found in the world. Item example: {item_template}, type is one of: {item_types}; Reply with a list of items in JSON format and do not write anything else: {{"items": []}}.'
WORLD_CREATURES: '<context>{context}</context>\n[USER_START] Using the information supplied inside the <context> tags, come up with 5 creatures of various level and sentiment that can be found in the world. Consider the role of the creature in the story and whether it will be friendly or hostile to the player. Creature example: {creature_template}. Reply with a list of creatures in JSON format and do not write anything else: {{"creatures": []}}.'
GOAL_PROMPT: '[Characters:{characters}][Sentiments towards characters: {sentiments}] [Last action: {last_action}] [Location: {location}] [Known locations: {locations}][Acting character: {character}] [Actions available:{actions}] [USER_START] For {character_name}, come up with a goal that plays along with their character description that involves an item, a character or a location in the prompt. Then construct up to three tasks that will lead towards the achievement of said goal. Fill in the following JSON template: {{"goal":"", "tasks":[{"action":"", "what":""}, {"action":"", "what":""}, {"action":"", "what":""}]}}'
GOAL_PROMPT: '[Characters:{characters}][Sentiments towards characters: {sentiments}] [Last action: {last_action}] [Location: {location}] [Known locations: {locations}][Acting character: {character}] [Actions available:{actions}] [USER_START] For {character_name}, describe a goal that goes along with their character description that involves an item, a character or a location in the prompt. Then construct up to three tasks that will lead towards the achievement of said goal. Fill in the following JSON template: {{"goal":"", "tasks":[{"action":"", "what":""}, {"action":"", "what":""}, {"action":"", "what":""}]}}'
JSON_GRAMMAR: "root ::= object\nvalue ::= object | array | string | number | (\"true\" | \"false\" | \"null\") ws\n\nobject ::=\n \"{\" ws (\n string \":\" ws value\n (\",\" ws string \":\" ws value)*\n )? \"}\" ws\n\narray ::=\n \"[\" ws (\n value\n (\",\" ws value)*\n )? \"]\" ws\n\nstring ::=\n \"\\\"\" (\n [^\"\\\\] |\n \"\\\\\" ([\"\\\\/bfnrt] | \"u\" [0-9a-fA-F] [0-9a-fA-F] [0-9a-fA-F] [0-9a-fA-F]) # escapes\n )* \"\\\"\" ws\n\nnumber ::= (\"-\"? ([0-9] | [1-9] [0-9]*)) (\".\" [0-9]+)? ([eE] [-+]? [0-9]+)? ws\n\n# Optional space: by convention, applied in this grammar after literal chars when allowed\nws ::= ([ \\t\\n] ws)?"
PLAYER_ENTER_PROMPT: '<context>{context}</context> Zone info: {zone_info}; Npc example: {npc_template}.\n[USER_START] The player has just re-entered this location: {location_info}. Consider whether any items, npcs or mobs should be spawned. For mobs, only enter the name of race. Fill in this JSON template and do not write anything else: {{"items":[], "npcs":[] "mobs":[]}}.'
QUEST_PROMPT: '<context>{context}</context> Zone info: {zone_info}; Character: {character_card};\n[USER_START] Using the information supplied inside the <context> tags, {character_name} needs someone to perform a task. Based on the following input, come up with a suitable reason for it, using {character_name}s personality and history. Task info: {base_quest}. Fill in this JSON template and do not write anything else: {{"reason":""}} \n\n '
NOTE_QUEST_PROMPT: '<context>{context}</context> Zone info: {zone_info};\n[USER_START]Using the information supplied inside the <context> tags, generate a quest that starts from reading a note. The reader must find and talk to a person. Fill in the following JSON template and write nothing else.: {{"reason": "only what the note says. 50 words.", "type":"talk", "target":"who to talk to", "location":"", "name":"name of quest"}}'
NOTE_LORE_PROMPT: '<context>{context}</context> Zone info: {zone_info};\n[USER_START]FUsing the information supplied inside the <context> tags, decide what is written on a note that has been found. Use the provided story and world information to generate a piece of lore. Use about 50 words.'
ACTION_PROMPT: '<context>{context}</context>\n[USER_START]Act as as {character_name}.\nUsing the information supplied inside the <context> tag, pick an action according to {character_name}s description and mood. If suitable, select something to perform the action on (target). The action should be in the supplied list and should be related to {character_name}s goal and thoughts. Build on events in "History" without repeating them. Respond using JSON in the following format with up to 3 actions: """{action_template}""". Continue the sequence of events: {previous_events}'
REQUEST_FOLLOW_PROMPT: '<context>{context}</context>\n[USER_START]Act as as {character_name}.\nUsing the information supplied inside the <context> tag. {character_name} has received a request to follow {target}. Answer based on {character_name}s description and mood. Reason given by {target}: {target_reason}. Respond using JSON in the following format: {follow_template}'
ACTION_PROMPT: '<context>{context}</context>\n[USER_START]Act as as {character_name}.\nUsing the information supplied inside the <context> tags, pick an action according to {character_name}s description and mood. If suitable, select something to perform the action on (target). The action should be in the supplied list and should be related to {character_name}s goal and thoughts. Build on events in "History" without repeating them. Respond using JSON in the following format with up to 3 actions: """{action_template}""". Continue the sequence of events: {previous_events}'
REQUEST_FOLLOW_PROMPT: '<context>{context}</context>\n[USER_START]Act as {character_name}.\nUsing the information supplied inside the <context> tag. {character_name} has received a request to follow {target}. Answer based on {character_name}s description and mood. Reason given by {target}: {target_reason}. Respond using JSON in the following format: {follow_template}'
DAY_CYCLE_EVENT_PROMPT: '<context>{context}</context>\n[USER_START] Write up to two sentences describing the transition from {from_time} to {to_time} in {location_name}, using the information supplied inside the <context> tags.'
NARRATIVE_EVENT_PROMPT: '<context>{context}</context>\n[USER_START] Write a narrative event that occurs in {location_name} using the information supplied inside the <context> tags. The event should be related to the location and the characters present. Use up to 50 words.'
RANDOM_SPAWN_PROMPT: '<context>{context}</context>\n[USER_START] An npc or a mob has entered {location_name}. Select either and fill in one of the following templates using the information supplied inside the <context> tags. Respond using JSON in the following format: {npc_template}'
Expand Down
3 changes: 2 additions & 1 deletion tale/driver.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
import appdirs
from tale.items.basic import Note

from tale.llm import llm_config
from tale.llm.LivingNpc import LivingNpc
from tale.npc_defs import StationaryNpc
from tale.zone import Zone
Expand Down Expand Up @@ -908,7 +909,7 @@ def prepare_combat_prompt(self,
defender_titles.append(defender_title)


return self.llm_util.combat_prompt.format(attackers=attacker_names,
return llm_config.params['COMBAT_PROMPT'].format(attackers=attacker_names,
defenders=defender_titles,
location=location_title,
input_text=combat_result,
Expand Down
2 changes: 1 addition & 1 deletion tale/llm/LivingNpc.py
Original file line number Diff line number Diff line change
Expand Up @@ -373,7 +373,7 @@ def character_card(self) -> str:
items = []
for i in self.inventory:
items.append(f'"{str(i.name)}"')
return '{{"name":"{name}", "gender":"{gender}","age":{age},"occupation":"{occupation}","personality":"{personality}","appearance":"{description}","items":[{items}], "race":"{race}", "quest":"{quest}", "example_voice":"{example_voice}", "wearing":"{wearing}", "wielding":"{wielding}"}}'.format(
return '{{"name":"{name}", "gender":"{gender}","age":{age},"occupation":"{occupation}","personality":"{personality}","appearance":"{description}","items":[{items}], "race":"{race}", "quest":"{quest}", "goal":"{goal}", "example_voice":"{example_voice}", "wearing":"{wearing}", "wielding":"{wielding}"}}'.format(
name=self.title,
gender=lang.gender_string(self.gender),
age=self.age,
Expand Down
19 changes: 8 additions & 11 deletions tale/llm/contexts/ActionContext.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,3 @@


import json
import random
from tale.base import Location
from tale.llm.contexts.BaseContext import BaseContext
Expand All @@ -20,13 +17,13 @@ def __init__(self, story_context: str, story_type: str, character_name: str, cha

def to_prompt_string(self) -> str:
actions = ', '.join(self.actions)
characters = {}
characters = []
for living in self.location.livings:
if living.visible and living.name != self.character_name.lower():
if living.alive:
characters[living.name] = living.short_description
else:
characters[living.name] = f"{living.short_description} (dead)"
if living.visible and not living.hidden and living.name != self.character_name.lower():
character = f"{living.name}: {living.short_description}"
if not living.alive:
character = character + " (dead)"
characters.append(character)
exits = self.location.exits.keys()
items = [item.name for item in self.location.items if item.visible]
examples = []
Expand All @@ -35,5 +32,5 @@ def to_prompt_string(self) -> str:
if len(exits) > 0:
examples.append(f'{{"goal":"", "thoughts":"I want to go there.", "action":"move", "target":{random.choice(list(exits))}, "text":""}}')
if len(characters) > 0:
examples.append(f'{{"goal":"", "thoughts":"", "action":"say", "target":{random.choice(list(characters.values()))}, "text":""}}')
return f"Story context:{self.story_context}; Story type:{self.story_type}; Available actions: {actions}; Location:{self.location.name}, {self.location.description}; Available exits: {exits}; Self({self.character_name}): {self.character_card}; Present items: {items}; Present characters: {json.dumps(characters)}; History:{self.event_history}; Example actions: {', '.join(examples)};"
examples.append(f'{{"goal":"", "thoughts":"", "action":"say", "target":{random.choice(characters)}, "text":""}}')
return f"Story context:{self.story_context}; Story type:{self.story_type}; Available actions: {actions}; Location: {self.location.name}, {self.location.description}; Available exits: {exits}; Self({self.character_name}): {self.character_card}; Present items: {items}; Present characters: {characters}; History:{self.event_history}; Example actions: {', '.join(examples)};"
16 changes: 6 additions & 10 deletions tale/llm/llm_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,13 +48,9 @@ def __init__(self, io_util: IoUtil = None):
self.default_body = json.loads(backend_config['DEFAULT_BODY'])
self.memory_size = llm_config.params['MEMORY_SIZE']
self.pre_prompt = llm_config.params['PRE_PROMPT'] # type: str
self.evoke_prompt = llm_config.params['BASE_PROMPT'] # type: str
self.combat_prompt = llm_config.params['COMBAT_PROMPT'] # type: str
self.word_limit = llm_config.params['WORD_LIMIT']
self.short_word_limit = llm_config.params['SHORT_WORD_LIMIT']
self.story_background_prompt = llm_config.params['STORY_BACKGROUND_PROMPT'] # type: str
self.day_cycle_event_prompt = llm_config.params['DAY_CYCLE_EVENT_PROMPT'] # type: str
self.narrative_event_prompt = llm_config.params['NARRATIVE_EVENT_PROMPT']

self.__story = None # type: DynamicStory
self.io_util = io_util or IoUtil(config=llm_config.params, backend_config=backend_config)
self.stream = backend_config['STREAM']
Expand All @@ -63,7 +59,7 @@ def __init__(self, io_util: IoUtil = None):
self.__story_context = ''
self.__story_type = ''
self.__world_info = ''
self.action_list = llm_config.params['ACTION_LIST']
#self.action_list = llm_config.params['ACTION_LIST']
json_grammar_key = backend_config['JSON_GRAMMAR_KEY']

#self._look_hashes = dict() # type: dict[int, str] # location hashes for look command. currently never cleared.
Expand Down Expand Up @@ -108,7 +104,7 @@ def evoke(self, message: str, short_len: bool=False, rolling_prompt: str = '', a
history=rolling_prompt if not (skip_history or alt_prompt) else '',
extra_context=extra_context)
prompt = self.pre_prompt
prompt += (alt_prompt or self.evoke_prompt).format(
prompt += (alt_prompt or llm_config.params['BASE_PROMPT']).format(
context = '{context}',
max_words=self.word_limit if not short_len else self.short_word_limit,
input_text=str(trimmed_message))
Expand Down Expand Up @@ -290,7 +286,7 @@ def free_form_action(self, location: Location, character_name: str, character_c
character_card=character_card,
event_history=event_history,
location=location,
actions=self.action_list)
actions=llm_config.params['ACTION_LIST'])
return self._character.free_form_action(action_context)

def request_follow(self, actor: MudObject, character_name: str, character_card: str, event_history: str, location: Location, asker_reason: str):
Expand All @@ -308,7 +304,7 @@ def describe_day_cycle_transition(self, player: PlayerConnection, from_time: str
prompt = self.pre_prompt
location = player.player.location
context = self._get_world_context()
prompt += self.day_cycle_event_prompt.format(
prompt += llm_config.params['DAY_CYCLE_EVENT_PROMPT'].format(
context= '{context}',
location_name=location.name,
from_time=from_time,
Expand All @@ -325,7 +321,7 @@ def describe_day_cycle_transition(self, player: PlayerConnection, from_time: str
def generate_narrative_event(self, location: Location) -> str:
prompt = self.pre_prompt
context = self._get_world_context()
prompt += self.narrative_event_prompt.format(
prompt += llm_config.params['NARRATIVE_EVENT_PROMPT'].format(
context= '{context}',
location_name=location.name)
request_body = deepcopy(self.default_body)
Expand Down
7 changes: 2 additions & 5 deletions tale/llm/quest_building.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,10 @@ def __init__(self, backend: str, io_util: IoUtil, default_body: dict, json_gramm
self.io_util = io_util
self.json_grammar = llm_config.params['JSON_GRAMMAR']
self.json_grammar_key = json_grammar_key # Type: str
self.quest_prompt = llm_config.params['QUEST_PROMPT']
self.note_quest_prompt = llm_config.params['NOTE_QUEST_PROMPT']
self.note_lore_prompt = llm_config.params['NOTE_LORE_PROMPT']

def generate_quest(self, base_quest: dict, character_name: str, location: Location, context: WorldGenerationContext, character_card: str = '', story_type: str = '', world_info: str = '', zone_info: str = '') -> Quest:
prompt = self.pre_prompt
prompt += self.quest_prompt.format(
prompt += llm_config.params['QUEST_PROMPT'].format(
context='{context}',
base_quest=base_quest,
location_name=location.name,
Expand All @@ -36,7 +33,7 @@ def generate_quest(self, base_quest: dict, character_name: str, location: Locati

def generate_note_quest(self, context: WorldGenerationContext, zone_info: str) -> Quest:
prompt = self.pre_prompt
prompt += self.note_quest_prompt.format(
prompt += llm_config.params['NOTE_QUEST_PROMPT'].format(
context='{context}',
zone_info=zone_info)
request_body = deepcopy(self.default_body)
Expand Down
Loading
Loading