Skip to content

Commit 2a22b64

Browse files
committed
load and save llm_cache and npc 'memories'
1 parent be9ac20 commit 2a22b64

File tree

6 files changed

+76
-31
lines changed

6 files changed

+76
-31
lines changed

stories/teaparty/world.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@
2626
}
2727
},
2828
"world": {
29-
"creatures": {
29+
"npcs": {
3030
"mad hatter": {
3131
"location": "Living Room",
3232
"name": "Mad hatter",
@@ -120,4 +120,4 @@
120120
},
121121
"items": {}
122122
}
123-
}
123+
}

tale/json_story.py

Lines changed: 14 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,8 @@
1-
import tale
2-
from tale.base import Location, Item, Living
3-
from tale.driver import Driver
41
from tale.llm.llm_ext import DynamicStory
52
from tale.player import Player
6-
from tale.story import StoryBase, StoryConfig
3+
from tale.story import StoryConfig
74
import tale.parse_utils as parse_utils
8-
from tale.zone import Zone
5+
import tale.llm.llm_cache as llm_cache
96

107
class JsonStory(DynamicStory):
118

@@ -30,14 +27,18 @@ def init(self, driver) -> None:
3027
self.add_zone(zone)
3128
for loc in zone.locations.values():
3229
self.add_location(loc, name)
33-
if world['catalogue']['creatures']:
34-
self._catalogue._creatures = world['catalogue']['creatures']
35-
if world['catalogue']['items']:
36-
self._catalogue._items = world['catalogue']['items']
37-
if world['world']['npcs']:
38-
self._world.npcs = parse_utils.load_npcs(world['world']['npcs'].values(), self.locations)
39-
if world['world']['items']:
40-
self._world.items = parse_utils.load_items(world['world']['items'].values(), self.locations)
30+
if world.get('catalogue', None):
31+
if world['catalogue']['creatures']:
32+
self._catalogue._creatures = world['catalogue']['creatures']
33+
if world['catalogue']['items']:
34+
self._catalogue._items = world['catalogue']['items']
35+
if world.get('world', None):
36+
if world['world']['npcs']:
37+
self._world.npcs = parse_utils.load_npcs(world['world']['npcs'].values(), self.locations)
38+
if world['world']['items']:
39+
self._world.items = parse_utils.load_items(world['world']['items'].values(), self.locations)
40+
41+
llm_cache.load(parse_utils.load_json(self.path +'llm_cache.json'))
4142

4243

4344
def welcome(self, player: Player) -> str:

tale/llm/LivingNpc.py

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@ def __init__(self, name: str, gender: str, *,
1717
self.age = age
1818
self.personality = personality
1919
self.occupation = occupation
20-
self.memory_size = 1024
2120
self.known_locations = dict()
2221
self._observed_events = set() # type: set[int] # These are hashed values of action the character has been notified of
2322
self._conversations = set() # type: set[str] # These are hashed values of conversations the character has involved in
@@ -166,4 +165,23 @@ def character_card(self) -> str:
166165
description=self.description,
167166
occupation=self.occupation,
168167
race=self.stats.race,
169-
items=','.join(items))
168+
items=','.join(items))
169+
170+
def dump_memory(self) -> dict:
171+
return dict(
172+
known_locations=self.known_locations,
173+
observed_events=llm_cache.get_events(self._observed_events),
174+
conversations=llm_cache.get_tells(self._conversations),
175+
sentiments=self.sentiments,
176+
action_history=self.action_history,
177+
planned_actions=self.planned_actions,
178+
goal=self.goal)
179+
180+
def load_memory(self, memory: dict):
181+
self.known_locations = memory.get('known_locations', {})
182+
self._observed_events = set([llm_cache.cache_event(event) for event in memory.get('observed_events', [])])
183+
self._conversations = set([llm_cache.cache_tell(tell) for tell in memory.get('conversations', [])])
184+
self.sentiments = memory.get('sentiments', {})
185+
self.action_history = memory.get('action_history', [])
186+
self.planned_actions = memory.get('planned_actions', [])
187+
self.goal = memory.get('goal', None)

tale/llm/llm_cache.py

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,5 @@
11
""" This file stores various caches for LLM related things. """
22

3-
4-
5-
6-
73
event_cache = {}
84
look_cache = {}
95
tell_cache = {}
@@ -53,3 +49,14 @@ def get_tells(tell_hashes: [int]) -> str:
5349
return ", ".join([tell_cache.get(tell_hash, '') for tell_hash in tell_hashes])
5450

5551

52+
def load(cache_file: dict):
53+
""" Loads the caches from disk. """
54+
event_cache = cache_file.get("events", {})
55+
look_cache = cache_file.get("looks", {})
56+
tell_cache = cache_file.get("tells", {})
57+
58+
def save() -> dict:
59+
""" Saves the caches to disk. """
60+
return {"events":event_cache, "looks":look_cache, "tells":tell_cache}
61+
62+

tale/llm/llm_ext.py

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
import json
2-
import typing
32
from tale import parse_utils
43
from tale.base import Item, Living, Location
54
from tale.coord import Coord
65
from tale.story import StoryBase
76

87
from tale.zone import Zone
8+
import tale.llm.llm_cache as llm_cache
99

1010
class DynamicStory(StoryBase):
1111

@@ -111,7 +111,10 @@ def save(self) -> None:
111111
json.dump(story , fp, indent=4)
112112

113113
with open('story_config.json', "w") as fp:
114-
json.dump(parse_utils.save_story_config(self.config) , fp, indent=4)
114+
json.dump(parse_utils.save_story_config(self.config), fp, indent=4)
115+
116+
with open('llm_cache.json', "w") as fp:
117+
json.dump(llm_cache.save(), fp, indent=4)
115118

116119
@property
117120
def get_catalogue(self) -> 'Catalogue':

tale/parse_utils.py

Lines changed: 24 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
from tale.base import Location, Exit, Item, Stats, Weapon, Wearable
55
from tale.coord import Coord
66
from tale.items.basic import Boxlike, Drink, Food, Health, Money, Note
7+
from tale.llm.LivingNpc import LivingNpc
78
from tale.npc_defs import StationaryMob, StationaryNpc
89
from tale.races import UnarmedAttack
910
from tale.story import GameMode, MoneyType, TickMethod, StoryConfig
@@ -13,8 +14,18 @@
1314
import json
1415
import re
1516
import sys
17+
import os
18+
1619

1720
def load_json(file_path: str):
21+
"""
22+
Loads json from supplied file path
23+
Returns dict
24+
Fails silently and returns an empty dict if file doesn't exist
25+
"""
26+
if not os.path.isfile(file_path):
27+
return {}
28+
1829
with open(file_path) as f:
1930
return json.load(f, strict=False)
2031

@@ -125,7 +136,7 @@ def load_npcs(json_npcs: [], locations = {}) -> dict:
125136
Inserts into locations if supplied and has location
126137
"""
127138
npcs = {}
128-
for npc in json_npcs:
139+
for npc in json_npcs: # type dict
129140
npc_type = npc.get('type', 'Mob')
130141
if npc_type == 'ignore':
131142
continue
@@ -160,12 +171,13 @@ def load_npcs(json_npcs: [], locations = {}) -> dict:
160171
new_npc.stats.level = npc.get('level', 1)
161172
if npc.get('stats', None):
162173
new_npc.stats = load_stats(npc['stats'])
163-
# else:
164-
# module = sys.modules['tale.items.basic']
165-
# clazz = getattr(module, npc_type)
166-
# new_npc = clazz(name=npc['name'], gender=npc['gender'], race=npc['race'], title=npc['title'], descr=npc['descr'], short_descr=npc['short_descr'], args=npc)
174+
167175
if locations and npc['location']:
168176
_insert(new_npc, locations, npc['location'])
177+
178+
if npc.get('memory', None):
179+
new_npc.load_memory(npc['memory'])
180+
169181
npcs[name] = new_npc
170182
return npcs
171183

@@ -325,13 +337,11 @@ def trim_response(message: str):
325337

326338
def sanitize_json(result: str):
327339
""" Removes special chars from json string. Some common, and some 'creative' ones. """
328-
# .replace('}}', '}')
329-
# .replace('""', '"')
330340
if result is None:
331341
return ''
332342
result = result.replace('```json', '') #.replace('\\"', '"').replace('"\\n"', '","').replace('\\n', '').replace('}\n{', '},{').replace('}{', '},{').replace('\\r', '').replace('\\t', '').replace('"{', '{').replace('}"', '}').replace('"\\', '"').replace('\\”', '"').replace('" "', '","').replace(':,',':').replace('},]', '}]').replace('},}', '}}')
333343
result = result.split('```')[0]
334-
print('sanitized json: ' + result)
344+
#print('sanitized json: ' + result)
335345
return result
336346

337347
def _convert_name(name: str):
@@ -561,6 +571,9 @@ def save_npcs(creatures: []) -> dict:
561571
stored_npc['aliases'] = list(npc.aliases)
562572
stored_npc['short_descr'] = npc.short_description
563573
stored_npc['descr'] = npc.description
574+
stored_npc['personality'] = npc.personality
575+
stored_npc['occupation'] = npc.occupation
576+
stored_npc['age'] = npc.age
564577

565578
if isinstance(npc, StationaryMob):
566579
stored_npc['type'] = 'Npc'
@@ -575,6 +588,9 @@ def save_npcs(creatures: []) -> dict:
575588
stored_npc['short_descr'] = npc.short_description
576589
stored_npc['level'] = npc.stats.level
577590
stored_npc['stats'] = save_stats(npc.stats)
591+
592+
if isinstance(npc, LivingNpc):
593+
stored_npc['memory'] = npc.dump_memory()
578594

579595
npcs[npc.name] = stored_npc
580596
return npcs

0 commit comments

Comments
 (0)