Skip to content

Commit 9bed64a

Browse files
authored
Update 0.15.2 (#45)
* use health status when generating combat message consider existing neighbor locations when generating location * fix idle action tell * add max_hp to db account * fix test
1 parent 339e179 commit 9bed64a

File tree

14 files changed

+135
-39
lines changed

14 files changed

+135
-39
lines changed

llm_config.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ BASE_PROMPT: "[Story context: {story_context}]; [History: {history}]; [USER_STAR
1414
ACTION_PROMPT: "[Story context: {story_context}]; [History: {history}]; [USER_START] Rewrite the Action, and nothing else, in your own words using the supplied Context and Location. History is what happened before. Use less than {max_words} words. [Text: {input_text}] \n\nEnd of text."
1515
DIALOGUE_PROMPT: '[Story context: {story_context}]; [Location: {location}]; [History: {history}]; The following is a conversation between {character1} and {character2}; {character1}:{character1_description}; character2:{character2_description}; [Chat history: {previous_conversation}]\n\n [{character2}s sentiment towards {character1}: {sentiment}].[USER_START] Write a single response for {character2} in third person pov, using {character2} description.'
1616
ITEM_PROMPT: 'Items:[{items}];Characters:[{character1},{character2}] \n\n [USER_START] Decide if an item was explicitly given, taken, dropped or put somewhere in the following text:[Text:{text}]. Insert your thoughts about whether an item was explicitly given, taken, put somewhere or dropped in "my thoughts", and the results in "item", "from" and "to", or make them empty if . Insert {character1}s sentiment towards {character2} in a single word in "sentiment assessment". Fill in the following JSON template: {{ "thoughts":"", "result": {{ "item":"", "from":"", "to":""}}, {{"sentiment":"sentiment assessment"}} }} End of example. \n\n Write your response in valid JSON'
17-
COMBAT_PROMPT: 'Rewrite the following combat between user {attacker} using {attacker_weapon}, and {victim} using {victim_weapon} in {location}, {location_description} into a vivid description in less than 300 words. [USER_START] Rewrite the following combat result in about 150 words, using the characters weapons . Combat Result: {attacker_msg}'
17+
COMBAT_PROMPT: 'The following is a combat scene between user {attacker} and {victim} in {location}, {location_description} into a vivid description. [USER_START] Rewrite the following combat result in about 150 words, using the characters weapons and their health status: 1.0 is highest, 0.0 is lowest. Combat Result: {attacker_msg}'
1818
PRE_JSON_PROMPT: 'Below is an instruction that describes a task, paired with an input that provides further context. Write a response in valid JSON format that appropriately completes the request.'
1919
CREATE_CHARACTER_PROMPT: '[USER_START] For a {story_type}, create a diverse character with rich personality that can be interacted with using the story context and keywords. Do not mention height. Story context: {story_context}; keywords: {keywords}. Fill in this JSON template and write nothing else: {{"name":"", "description": "50 words", "appearance": "25 words", "personality": "50 words", "money":(int), "level":"", "gender":"m/f/n", "age":(int), "race":""}}'
2020
CREATE_LOCATION_PROMPT: '[Story context: {story_context}]; World info: {world_info}; Zone info: {zone_info}; Exit json example: {{"direction":"", "name":"name of new location", "short_descr":"exit description"}}; Npc or mob example: {{"name":"", "sentiment":"", "race":"", "gender":"m, f, or n", "level":(int), "description":"25 words"}}. Existing connected locations: {exit_locations}. [USER_START] For a {story_type}, describe the following location: {location_name}. {items_prompt} {spawn_prompt} Add a brief description, and one to three additional exits leading to new locations. Fill in this JSON template and do not write anything else: {{"description": "25 words", "exits":[], "items":[], "npcs":[]}}. Write the response in valid JSON.'

tale/accounts.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,7 @@ def _create_database(self) -> None:
101101
level integer NOT NULL,
102102
xp integer NOT NULL,
103103
hp integer NOT NULL,
104+
max_hp integer NOT NULL,
104105
ac integer NOT NULL,
105106
wc integer NOT NULL,
106107
maxhp_dice varchar NULL,

tale/base.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -921,6 +921,7 @@ def __init__(self) -> None:
921921
self.level = 1
922922
self.xp = 0
923923
self.hp = 5
924+
self.max_hp = 5
924925
self.maxhp_dice = ""
925926
self.ac = 0
926927
self.wc = 0
@@ -958,6 +959,7 @@ def set_stats_from_race(self) -> None:
958959
self.weight = r.mass
959960
self.size = r.size
960961
self.hp = r.hp
962+
self.max_hp = r.hp
961963
self.unarmed_attack = Weapon(name=r.unarmed_attack.name)
962964

963965
def get_weapon_skill(self, weapon_type: WeaponType) -> int:
@@ -1373,7 +1375,7 @@ def locate_item(self, name: str, include_inventory: bool=True, include_location:
13731375
return (found, containing_object) if found else (None, None)
13741376

13751377
def start_attack(self, victim: 'Living') -> None:
1376-
"""Starts attacking the given living until death ensues on either side."""
1378+
"""Starts attacking the given living for one round."""
13771379
attacker_name = lang.capital(self.title)
13781380
victim_name = lang.capital(victim.title)
13791381
c = combat.Combat(self, victim)

tale/coord.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,11 @@ def __init__(self, x=0, y=0, z=0):
66
self.y = y
77
self.z = z
88

9+
@classmethod
10+
def from_coord(self, coord: 'Coord'):
11+
return Coord(coord.x, coord.y, coord.z)
12+
13+
914
def distance(self, other) -> float:
1015
""" Returns the manhattan distance between this and another coordinate."""
1116
return abs(self.x - other.x) + abs(self.y - other.y) + abs(self.z - other.z)

tale/driver.py

Lines changed: 16 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -624,11 +624,13 @@ def go_through_exit(self, player: player.Player, direction: str, evoke: bool=Tru
624624

625625
# generate the location if it's not built yet. retry 5 times.
626626
for i in range(5):
627+
neighbor_locations = dynamic_story.neighbors_for_location(xt.target)
627628
new_locations, exits = self.llm_util.build_location(location=xt.target,
628629
exit_location_name=player.location.name,
629630
zone_info=new_zone.get_info(),
630631
world_creatures=dynamic_story.world_creatures,
631-
world_items=dynamic_story.world_items,)
632+
world_items=dynamic_story.world_items,
633+
neighbors=neighbor_locations,)
632634
if new_locations:
633635
break
634636
if not new_locations:
@@ -878,10 +880,16 @@ def prepare_combat_prompt(self,
878880
victim_name += " (as 'You')"
879881
attacker_msg.replace(victim_name, "you")
880882

881-
return self.llm_util.combat_prompt.format(attacker=attacker_name,
882-
victim=victim_name,
883-
attacker_weapon=attacker.wielding.name,
884-
victim_weapon=victim.wielding.name,
885-
attacker_msg=attacker_msg,
886-
location=location_title,
887-
location_description=location_description)
883+
victim_info = {"name": victim_name,
884+
"health": victim.stats.hp / victim.stats.max_hp,
885+
"weapon": victim.wielding.title}
886+
887+
attacker_info = {"name": attacker_name,
888+
"health": attacker.stats.hp / attacker.stats.max_hp,
889+
"weapon": attacker.wielding.title}
890+
891+
return self.llm_util.combat_prompt.format(attacker=attacker_info,
892+
victim=victim_info,
893+
attacker_msg=attacker_msg,
894+
location=location_title,
895+
location_description=location_description)

tale/llm/LivingNpc.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,6 @@ def do_say(self, what_happened: str, actor: Living) -> None:
7272

7373
tell_hash = llm_cache.cache_tell('{actor.title}:{response}'.format(actor=self.title, response=response))
7474
self._conversations.add(tell_hash)
75-
7675
self.tell_others("{response}".format(response=response), evoke=False)
7776
if item_result:
7877
self.handle_item_result(item_result, actor)
@@ -134,7 +133,7 @@ def idle_action(self):
134133
event_history=llm_cache.get_events(self._observed_events),
135134
sentiments=self.sentiments)
136135
if actions:
137-
self.planned_actions.extend(actions)
136+
self.planned_actions.append(actions)
138137
if len(self.planned_actions) > 0:
139138
action = self.planned_actions.pop(0)
140139
self.action_history.append(action)

tale/llm/llm_ext.py

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
import json
2+
from tale import parse_utils
23
from tale.base import Item, Living, Location
4+
from tale.coord import Coord
35
from tale.story import StoryBase
46

57
from tale.zone import Zone
@@ -10,7 +12,8 @@ def __init__(self) -> None:
1012
self._zones = dict() # type: dict[str, Zone]
1113
self._world = dict() # type: dict[str, any]
1214
self._world["creatures"] = dict()
13-
self._world["items"] = dict()
15+
self._world["items"] = dict()
16+
self._world["locations"] = dict() # type: dict[Coord, Location]
1417

1518
def get_zone(self, name: str) -> Zone:
1619
""" Find a zone by name."""
@@ -45,6 +48,7 @@ def add_location(self, location: Location, zone: str = '') -> bool:
4548
""" Add a location to the story.
4649
If zone is specified, add to that zone, otherwise add to first zone.
4750
"""
51+
self._world["locations"][location.world_location] = location
4852
if zone:
4953
return self._zones[zone].add_location(location)
5054
for zone in self._zones:
@@ -68,7 +72,15 @@ def get_npc(self, npc: str) -> Living:
6872

6973
def get_item(self, item: str) -> Item:
7074
return self._world["items"][item]
75+
7176

77+
def neighbors_for_location(self, location: Location) -> dict:
78+
""" Return a dict of neighboring locations for a given location."""
79+
neighbors = dict() # type: dict[str, Location]
80+
for dir in ['north', 'east', 'south', 'west', 'up', 'down']:
81+
neighbors[dir] = self._world["locations"][Coord(location.world_location.add(parse_utils.coordinates_from_direction(dir)))]
82+
return neighbors
83+
7284
@property
7385
def world_creatures(self) -> dict:
7486
return self._world["creatures"]

tale/llm/llm_utils.py

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -120,7 +120,7 @@ def generate_character(self, story_context: str = '', keywords: list = [], story
120120
def get_neighbor_or_generate_zone(self, current_zone: Zone, current_location: Location, target_location: Location) -> Zone:
121121
return self._world_building.get_neighbor_or_generate_zone(current_zone, current_location, target_location, self.__story)
122122

123-
def build_location(self, location: Location, exit_location_name: str, zone_info: dict, world_items: dict = {}, world_creatures: dict = {}):
123+
def build_location(self, location: Location, exit_location_name: str, zone_info: dict, world_items: dict = {}, world_creatures: dict = {}, neighbors: dict = {}):
124124
""" Generate a location based on the current story context"""
125125
return self._world_building.build_location(location,
126126
exit_location_name,
@@ -129,7 +129,8 @@ def build_location(self, location: Location, exit_location_name: str, zone_info:
129129
self.__story.config.context,
130130
self.__story.config.world_info,
131131
world_creatures=world_creatures,
132-
world_items=world_items)
132+
world_items=world_items,
133+
neighbors=neighbors)
133134

134135
def perform_idle_action(self, character_name: str, location: Location, character_card: str = '', sentiments: dict = {}, last_action: str = '', event_history: str = '') -> list:
135136
return self._character.perform_idle_action(character_name, location, self.__story.config.context, character_card, sentiments, last_action, event_history=event_history)
@@ -138,7 +139,13 @@ def perform_travel_action(self, character_name: str, location: Location, locatio
138139
return self._character.perform_travel_action(character_name, location, locations, directions, character_card)
139140

140141
def perform_reaction(self, action: str, character_name: str, acting_character_name: str, location: Location, character_card: str = '', sentiment: str = '', event_history: str = ''):
141-
return self._character.perform_reaction(action, character_name, acting_character_name, location, character_card, sentiment, story_context=self.__story.config.context, event_history=event_history)
142+
return self._character.perform_reaction(action=action,
143+
character_name=character_name,
144+
acting_character_name=acting_character_name,
145+
location=location, character_card=character_card,
146+
sentiment=sentiment,
147+
story_context=self.__story.config.context,
148+
event_history=event_history)
142149

143150
def generate_story_background(self, world_mood: int, world_info: str, story_type: str):
144151
return self._story_building.generate_story_background(world_mood, world_info, story_type)
@@ -180,6 +187,6 @@ def _kobold_generation_prompt(self, request_body: dict) -> dict:
180187
#request_body['banned_tokens'] = ['```']
181188
return request_body
182189

183-
190+
184191

185192

tale/llm/requests/build_location.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ def build_prompt(self, args: dict) -> str:
2424
story_context = args['story_context'] # Type: str
2525

2626
spawn_prompt = ''
27-
spawn_chance = 0.25
27+
spawn_chance = 0.35
2828
spawn = random.random() < spawn_chance
2929
if spawn:
3030
mood = zone_info.get('mood', 0)

tale/llm/world_building.py

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,8 @@ def build_location(self, location: Location,
3838
story_context: str,
3939
world_info: str,
4040
world_items: dict = {},
41-
world_creatures: dict = {}):
41+
world_creatures: dict = {},
42+
neighbors: dict = {}) -> (list, list):
4243

4344
extra_items = generic.generic_items.get(self._check_setting(story_type), {})
4445
if extra_items:
@@ -62,7 +63,7 @@ def build_location(self, location: Location,
6263
result = self.io_util.synchronous_request(request_body, prompt=prompt)
6364
try:
6465
json_result = json.loads(parse_utils.sanitize_json(result))
65-
return self._validate_location(json_result, location, exit_location_name, world_items, world_creatures)
66+
return self._validate_location(json_result, location, exit_location_name, world_items, world_creatures, neighbors)
6667
except json.JSONDecodeError as exc:
6768
print(exc)
6869
return None, None
@@ -73,7 +74,8 @@ def _validate_location(self, json_result: dict,
7374
location_to_build: Location,
7475
exit_location_name: str,
7576
world_items: dict = {},
76-
world_creatures: dict = {}):
77+
world_creatures: dict = {},
78+
neighbors: dict = {}):
7779
"""Validate the location generated by LLM and update the location object."""
7880
try:
7981
# start locations have description already, and we don't want to overwrite it
@@ -88,7 +90,7 @@ def _validate_location(self, json_result: dict,
8890

8991
self._add_npcs(location_to_build, json_result, world_creatures)
9092

91-
new_locations, exits = parse_utils.parse_generated_exits(json_result,
93+
new_locations, exits = parse_utils.parse_generated_exits(json_result.get('exits', []),
9294
exit_location_name,
9395
location_to_build)
9496
location_to_build.built = True

0 commit comments

Comments
 (0)