Skip to content

Commit

Permalink
make search helpers more flexible, split up some prompts, pass world …
Browse files Browse the repository at this point in the history
…to system generators
  • Loading branch information
ssube committed Jun 5, 2024
1 parent cad8e2d commit 5a32bd9
Show file tree
Hide file tree
Showing 18 changed files with 91 additions and 59 deletions.
8 changes: 7 additions & 1 deletion prompts/llama-base.yml
Original file line number Diff line number Diff line change
Expand Up @@ -124,8 +124,10 @@ prompts:
The {{item}} item is not available in your inventory or in the room.
action_use_error_target: |
The {{target}} is not in the room, so you cannot use the {{item}} item on it.
action_use_broadcast: |
action_use_broadcast_effect: |
{{action_character | name}} uses {{item}} on {{target}} and applies the {{effect}} effect.
action_use_broadcast_outcome: |
Using the {{item}} item on {{target}} resulted in: {{outcome}}.
action_use_dm_effect: |
{{action_character | name}} uses {{item}} on {{target}}. {{item}} can apply any of the following effects: {{effect_names}}.
Which effect should be applied? Specify the effect. Do not include the question or any JSON. Only reply with the effect name.
Expand Down Expand Up @@ -177,6 +179,10 @@ prompts:
action_schedule_event_error_name: |
The event must have a name.
action_schedule_event_error_limit: |
You have reached the maximum number of events. Please delete or reschedule some of your existing events before adding more.
action_schedule_event_error_duplicate: |
You already have an event with that name. Please choose a unique name for the event.
action_schedule_event_result: |
You scheduled an event that will happen in {{turns}} turns.
Expand Down
11 changes: 5 additions & 6 deletions taleweave/actions/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -177,22 +177,21 @@ def action_ask(character: str, question: str) -> str:

with action_context() as (action_room, action_character):
# sanity checks
question_character, question_agent = get_character_agent_for_name(character)
if question_character == action_character:
raise ActionError(format_prompt("action_ask_error_self"))

question_character = find_character_in_room(action_room, character)
if not question_character:
raise ActionError(
format_prompt("action_ask_error_target", character=character)
)

if question_character == action_character:
raise ActionError(format_prompt("action_ask_error_self"))

question_agent = get_agent_for_character(question_character)
if not question_agent:
raise ActionError(
format_prompt("action_ask_error_agent", character=character)
)

# TODO: make sure they are in the same room

broadcast(
format_prompt(
"action_ask_broadcast",
Expand Down
14 changes: 11 additions & 3 deletions taleweave/actions/optional.py
Original file line number Diff line number Diff line change
Expand Up @@ -214,7 +214,7 @@ def action_use(item: str, target: str) -> str:

broadcast(
format_prompt(
"action_use_broadcast",
"action_use_broadcast_effect",
action_character=action_character,
effect=effect,
item=item,
Expand All @@ -233,8 +233,16 @@ def action_use(item: str, target: str) -> str:
)
)
broadcast(
f"The action resulted in: {outcome}"
) # TODO: should this be removed or moved to the prompt library?
format_prompt(
"action_use_broadcast_outcome",
action_character=action_character,
action_item=action_item,
effect=effect,
item=item,
target=target,
outcome=outcome,
)
)

# make sure both agents remember the outcome
target_agent = get_agent_for_character(target_character)
Expand Down
13 changes: 10 additions & 3 deletions taleweave/actions/planning.py
Original file line number Diff line number Diff line change
Expand Up @@ -145,15 +145,22 @@ def schedule_event(name: str, turns: int):
turns: The number of turns until the event happens.
"""

# TODO: check for existing events with the same name
# TODO: limit the number of events that can be scheduled

config = get_game_config()
current_turn = get_current_turn()

with action_context() as (_, action_character):
if not name:
raise ActionError(get_prompt("action_schedule_event_error_name"))

if (
len(action_character.planner.calendar.events)
>= config.world.character.event_limit
):
raise ActionError(get_prompt("action_schedule_event_error_limit"))

if name in [event.name for event in action_character.planner.calendar.events]:
raise ActionError(get_prompt("action_schedule_event_error_duplicate"))

event = CalendarEvent(name, turns + current_turn)
action_character.planner.calendar.events.append(event)
return format_prompt("action_schedule_event_result", name=name, turns=turns)
Expand Down
2 changes: 1 addition & 1 deletion taleweave/bot/discord.py
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ async def on_reaction_add(self, reaction, user):
message_id = reaction.message.id
if message_id not in event_messages:
logger.warning(f"message {message_id} not found in event messages")
# TODO: return error message
await reaction.message.add_reaction("❌")
return

event = event_messages[message_id]
Expand Down
4 changes: 3 additions & 1 deletion taleweave/game_system.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,11 @@ def __call__(


class SystemGenerate(Protocol):
def __call__(self, agent: Agent, theme: str, entity: WorldEntity) -> None:
def __call__(self, agent: Agent, world: World, entity: WorldEntity) -> None:
"""
Generate a new world entity based on the given theme and entity.
TODO: should this include the WorldPrompt as a parameter?
"""
...

Expand Down
3 changes: 1 addition & 2 deletions taleweave/generate.py
Original file line number Diff line number Diff line change
Expand Up @@ -92,8 +92,7 @@ def generate_system_attributes(
) -> None:
for system in systems:
if system.generate:
# TODO: pass the whole world
system.generate(agent, world.theme, entity)
system.generate(agent, world, entity)


def generate_room(
Expand Down
6 changes: 3 additions & 3 deletions taleweave/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@
from taleweave.models.config import DEFAULT_CONFIG, Config
from taleweave.models.entity import World, WorldState
from taleweave.models.event import GenerateEvent
from taleweave.models.files import PromptFile, WorldPrompt
from taleweave.models.files import TemplateFile, WorldPrompt
from taleweave.models.prompt import PromptLibrary
from taleweave.plugins import load_plugin
from taleweave.simulate import simulate_world
Expand Down Expand Up @@ -180,8 +180,8 @@ def get_world_prompt(args) -> WorldPrompt:
if args.world_template:
prompt_file, prompt_name = args.world_template.split(":")
with open(prompt_file, "r") as f:
prompts = PromptFile(**load_yaml(f))
for prompt in prompts.prompts:
prompts = TemplateFile(**load_yaml(f))
for prompt in prompts.templates:
if prompt.name == prompt_name:
return prompt

Expand Down
5 changes: 2 additions & 3 deletions taleweave/models/files.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ class WorldPrompt:
flavor: str = ""


# TODO: rename to WorldTemplates
@dataclass
class PromptFile:
prompts: List[WorldPrompt]
class TemplateFile:
templates: List[WorldPrompt]
6 changes: 3 additions & 3 deletions taleweave/render/comfy.py
Original file line number Diff line number Diff line change
Expand Up @@ -274,9 +274,9 @@ def render_loop():
)

if isinstance(event, WorldEntity):
title = event.name # TODO: generate a real title
title = event.name
else:
title = event.type
title = event.type # TODO: generate a real title

broadcast(
RenderEvent(
Expand All @@ -292,7 +292,7 @@ def render_loop():
if isinstance(event, WorldEntity):
logger.info("rendering entity %s", event.name)
prompt = prompt_from_entity(event)
title = event.name # TODO: generate a real title
title = event.name
else:
logger.info("rendering event %s", event.id)
prompt = prompt_from_event(event)
Expand Down
3 changes: 2 additions & 1 deletion taleweave/server/websocket.py
Original file line number Diff line number Diff line change
Expand Up @@ -63,8 +63,9 @@ async def next_turn(event: PromptEvent) -> None:
await websocket.send(
dumps(
{
"id": event.id,
"type": event.type,
"client": id, # TODO: this should be a field in the PromptEvent
"client": id, # TODO: should this be a field in the PromptEvent?
"character": event.character,
"prompt": event.prompt,
"actions": event.actions,
Expand Down
2 changes: 1 addition & 1 deletion taleweave/systems/digest.py
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,7 @@ def format_digest(
return "\n".join(digest)


def generate_digest(agent: Any, theme: str, entity: WorldEntity):
def generate_digest(agent: Any, world: World, entity: WorldEntity):
if isinstance(entity, Character):
if entity.name not in character_buffers:
character_buffers[entity.name] = []
Expand Down
2 changes: 1 addition & 1 deletion taleweave/systems/quest.py
Original file line number Diff line number Diff line change
Expand Up @@ -172,7 +172,7 @@ def initialize_quests(world: World) -> QuestData:
return QuestData(active={}, available={}, completed={})


def generate_quests(agent: Agent, theme: str, entity: WorldEntity) -> None:
def generate_quests(agent: Agent, world: World, entity: WorldEntity) -> None:
"""
Generate new quests for the world.
"""
Expand Down
4 changes: 2 additions & 2 deletions taleweave/systems/weather/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -76,10 +76,10 @@ def generate_room_weather(agent: Agent, theme: str, entity: Room) -> None:
logger.info(f"generated environment for {entity.name}: {environment}")


def generate_weather(agent: Agent, theme: str, entity: WorldEntity) -> None:
def generate_weather(agent: Agent, world: World, entity: WorldEntity) -> None:
if isinstance(entity, Room):
if "environment" not in entity.attributes:
generate_room_weather(agent, theme, entity)
generate_room_weather(agent, world.theme, entity)


def simulate_weather(world: World, turn: int, data: None = None):
Expand Down
8 changes: 6 additions & 2 deletions taleweave/utils/planning.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,9 +31,13 @@ def get_upcoming_events(
"""

calendar = character.planner.calendar
# TODO: sort events by turn
return [
upcoming = [
event
for event in calendar.events
if event.turn - current_turn <= upcoming_turns
]

# sort by turn
upcoming.sort(key=lambda event: event.turn)

return upcoming
1 change: 1 addition & 0 deletions taleweave/utils/prompt.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,5 +26,6 @@ def format_prompt(prompt_key: str, **kwargs) -> str:


def format_str(template_str: str, **kwargs) -> str:
# TODO: cache templates
template = jinja_env.from_string(template_str)
return template.render(**kwargs)
50 changes: 28 additions & 22 deletions taleweave/utils/search.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,13 @@
from .string import normalize_name


def get_entity_name(entity: WorldEntity | str) -> str:
if isinstance(entity, str):
return entity

return normalize_name(entity.name)


def find_room(world: World, room_name: str) -> Room | None:
for room in world.rooms:
if normalize_name(room.name) == normalize_name(room_name):
Expand Down Expand Up @@ -58,61 +65,60 @@ def find_portal_in_room(room: Room, portal_name: str) -> Portal | None:
# TODO: allow item or str
def find_item(
world: World,
item_name: str,
item: Item | str,
include_character_inventory=False,
include_item_inventory=False,
) -> Item | None:
item_name = get_entity_name(item)

for room in world.rooms:
item = find_item_in_room(
result = find_item_in_room(
room, item_name, include_character_inventory, include_item_inventory
)
if item:
return item
if result:
return result

return None


def find_item_in_character(
character: Character, item_name: str, include_item_inventory=False
character: Character, item: Item | str, include_item_inventory=False
) -> Item | None:
return find_item_in_container(character, item_name, include_item_inventory)
return find_item_in_container(character, item, include_item_inventory)


def find_item_in_container(
container: Character | Item, item_name: str, include_item_inventory=False
container: Room | Character | Item, item: Item | str, include_item_inventory=False
) -> Item | None:
item_name = get_entity_name(item)

for item in container.items:
if normalize_name(item.name) == normalize_name(item_name):
return item

if include_item_inventory:
item = find_item_in_container(item, item_name, include_item_inventory)
if item:
return item
result = find_item_in_container(item, item_name, include_item_inventory)
if result:
return result

return None


def find_item_in_room(
room: Room,
item_name: str,
item: Item | str,
include_character_inventory=False,
include_item_inventory=False,
) -> Item | None:
for item in room.items:
if normalize_name(item.name) == normalize_name(item_name):
return item

if include_item_inventory:
item = find_item_in_container(item, item_name, include_item_inventory)
if item:
return item
result = find_item_in_container(room, item, include_item_inventory)
if result:
return result

if include_character_inventory:
for character in room.characters:
item = find_item_in_character(character, item_name, include_item_inventory)
if item:
return item
result = find_item_in_character(character, item, include_item_inventory)
if result:
return result

return None

Expand Down
8 changes: 4 additions & 4 deletions worlds.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
prompts:
templates:
- name: outback-animals
theme: talking animal truckers in the Australian outback
flavor: create a fun and happy world where rough and tumble talking animals drive trucks and run saloons in the outback
Expand All @@ -15,12 +15,12 @@ prompts:
theme: opening scenes from Jurassic Park
flavor: |
follow the script of the film Jurassic Park exactly. do not deviate from the script in any way.
include accurate characters and make sure they will fully utilize all of the actions available to them in this world
include accurate characters and instruct them to utilize all of the actions available to them in this world
- name: star-wars
theme: opening scenes from Star Wars
theme: opening scenes from the 1977 film Star Wars
flavor: |
follow the script of the 1977 film Star Wars exactly. do not deviate from the script in any way.
include accurate characters and make sure they will fully utilize all of the actions available to them in this world
include accurate characters and instruct them to fully utilize all of the actions available to them in this world
- name: cyberpunk-utopia
theme: wealthy cyberpunk utopia with a dark secret
flavor: make a strange and dangerous world where technology is pervasive and scarcity is unheard of - for the upper class, at least
Expand Down

0 comments on commit 5a32bd9

Please sign in to comment.