1
0
Fork 0

load prompts from globs, provide more information to prompt templates, make pseudo-function parsing more flexible, add enter/exit messages to digest system
Run Docker Build / build (push) Successful in 17s Details
Run Python Build / build (push) Successful in 27s Details

This commit is contained in:
Sean Sube 2024-06-04 21:15:36 -05:00
parent 573b15befb
commit 8a4ecd2588
Signed by: ssube
GPG Key ID: 3EED7B957D362AF1
12 changed files with 145 additions and 44 deletions

View File

@ -186,23 +186,7 @@ prompts:
action_check_calendar_each: |
{{name}} will happen in {{turns}} turn
# digest system
digest_action_move: |
{{event.character | name}} entered the room.
digest_action_take: |
{{event.character | name}} picked up the {{event.parameters[item]}}.
digest_action_give: |
{{event.character | name}} gave the {{event.parameters[item]}} to {{event.parameters[character]}}.
digest_action_drop: |
{{event.character | name}} dropped the {{event.parameters[item]}}.
digest_action_ask: |
{{event.character | name}} asked {{event.parameters[character]}} about something.
digest_action_tell: |
{{event.character | name}} told {{event.parameters[character]}} about something.
digest_action_examine: |
{{event.character | name}} examined the {{event.parameters[target]}}.
# world defaults
# default dungeon master
world_default_dungeon_master: |
You are the dungeon master in charge of creating an engaging fantasy world full of interesting characters who
interact with each other and explore their environment. Be creative and original, creating a world that is
@ -233,7 +217,7 @@ prompts:
world_generate_room_broadcast_characters: |
Generating {{character_count}} characters for room: {{name}}
world_generate_room_broadcast_portals: |
Generating {{portal_count}} portals for room: {{name}}
Generating {{portal_count}} portals for room: {{room | name}}
world_generate_portal_name_outgoing: |
Generate the name of a portal that leads from the {{source_room}} room to the {{dest_room}} room and fits the world theme of {{world_theme}}.
@ -346,7 +330,7 @@ prompts:
# world simulation
world_simulate_character_action: |
You are currently in the {{room_name}} room. {{room_description}}. {{attributes}}.
You are currently in the {{room | name}} room. {{room | describe}}. {{attributes}}.
The room contains the following characters: {{visible_characters}}.
The room contains the following items: {{visible_items}}.
Your inventory contains the following items: {{character_items}}.

24
prompts/llama-digest.yml Normal file
View File

@ -0,0 +1,24 @@
prompts:
# digest system
digest_action_move_other_enter: |
{{event.character | name}} entered the room through the {{source_portal | name}}.
digest_action_move_other_exit: |
{{event.character | name}} left the room, heading through the {{destination_portal | name}}.
digest_action_move_self_enter: |
You entered the room through the {{source_portal | name}}.
digest_action_move_self_exit: |
You left the room, heading through the {{destination_portal | name}}.
digest_action_move: |
{{event.character | name}} entered the room.
digest_action_take: |
{{event.character | name}} picked up the {{event.parameters[item]}}.
digest_action_give: |
{{event.character | name}} gave the {{event.parameters[item]}} to {{event.parameters[character]}}.
digest_action_drop: |
{{event.character | name}} dropped the {{event.parameters[item]}}.
digest_action_ask: |
{{event.character | name}} asked {{event.parameters[character]}} about something.
digest_action_tell: |
{{event.character | name}} told {{event.parameters[character]}} about something.
digest_action_examine: |
{{event.character | name}} examined the {{event.parameters[target]}}.

View File

@ -1,18 +1,18 @@
prompts:
action_accept_quest_error_none: No quests are available at the moment.
action_accept_quest_error_name: |
{{character}} does not have a quest named "{{quest_name}}".
{{character}} does not have a quest named "{{quest.name}}".
action_accept_quest_error_room: |
{{character}} is not in the room.
action_accept_quest_result: |
You have started the quest "{{quest_name}}".
You have started the quest "{{quest.name}}".
action_submit_quest_error_active: |
You do not have any active quests.
action_submit_quest_error_none: No quests are available at the moment.
action_submit_quest_error_name: |
{{character}} does not have a quest named "{{quest_name}}".
{{character}} does not have a quest named "{{quest.name}}".
action_submit_quest_error_room: |
{{character}} is not in the room.
action_submit_quest_result: |
You have completed the quest "{{quest_name}}".
You have completed the quest "{{quest.name}}".

View File

@ -37,6 +37,7 @@ def action_examine(target: str) -> str:
format_prompt(
"action_examine_broadcast_action",
action_character=action_character,
action_room=action_room,
target=target,
)
)
@ -49,7 +50,7 @@ def action_examine(target: str) -> str:
action_room=action_room,
)
)
return format_prompt("action_examine_result_room", action_room=action_room)
return format_prompt("action_examine_result_room", target_room=action_room)
target_character = find_character_in_room(action_room, target)
if target_character:
@ -118,7 +119,7 @@ def action_move(direction: str) -> str:
format_prompt(
"action_move_error_room",
direction=direction,
destination=portal.destination,
portal=portal,
)
)

View File

@ -74,11 +74,11 @@ def submit_quest(character: str) -> str:
return format_prompt(
"action_submit_quest_result",
character=character,
quest=active_quest.name,
quest=active_quest,
)
return format_prompt(
"action_submit_quest_error_name",
character=character,
quest=active_quest.name,
quest=active_quest,
)

View File

@ -523,7 +523,7 @@ def link_rooms(
format_prompt(
"world_generate_room_broadcast_portals",
portal_count=num_portals,
name=room.name,
room=room,
)
)

View File

@ -1,5 +1,6 @@
import atexit
from functools import partial
from glob import glob
from logging.config import dictConfig
from os import environ, path
from typing import List
@ -195,7 +196,11 @@ def get_world_prompt(args) -> WorldPrompt:
def load_prompt_library(args) -> None:
if args.prompts:
prompt_files = []
for prompt_file in args.prompts:
prompt_files.extend(glob(prompt_file, recursive=True))
for prompt_file in prompt_files:
with open(prompt_file, "r") as f:
new_library = PromptLibrary(**load_yaml(f))
logger.info(f"loaded prompt library from {prompt_file}")
@ -258,6 +263,7 @@ def load_or_generate_world(
llm,
memory_factory=memory_factory,
)
set_dungeon_master(world_builder)
if path.exists(world_state_file):
logger.info(f"loading world state from {world_state_file}")
@ -285,6 +291,8 @@ def load_or_generate_world(
systems,
room_count=args.rooms,
)
load_or_initialize_system_data(args, systems, world)
save_world(world, world_file)
save_system_data(args, systems)

View File

@ -10,6 +10,7 @@ from packit.toolbox import Toolbox
from taleweave.context import action_context
from taleweave.models.event import PromptEvent
from taleweave.utils import try_parse_float, try_parse_int
logger = getLogger(__name__)
@ -122,12 +123,18 @@ class BasePlayer:
def parse_value(value: str) -> str | bool | float | int:
if value.startswith("~"):
return value[1:]
if value.lower() in ["true", "false"]:
return value.lower() == "true"
if value.isdecimal():
return float(value)
if value.isnumeric():
if value.lower() in ["true", "false", "yes", "no", "y", "n"]:
return value.lower() in ["true", "yes", "y"]
int_value = try_parse_int(value)
if int_value is not None:
return int(value)
float_value = try_parse_float(value)
if float_value is not None:
return float(value)
return value
params = {

View File

@ -53,7 +53,7 @@ from taleweave.utils.effect import expire_effects
from taleweave.utils.planning import expire_events, get_upcoming_events
from taleweave.utils.prompt import format_prompt
from taleweave.utils.search import find_containing_room
from taleweave.utils.world import describe_entity, format_attributes
from taleweave.utils.world import format_attributes
logger = getLogger(__name__)
@ -136,8 +136,7 @@ def prompt_character_action(
character_items=character_items,
attributes=character_attributes,
directions=room_directions,
room_name=room.name,
room_description=describe_entity(room),
room=room,
visible_characters=room_characters,
visible_items=room_items,
notes_prompt=notes_prompt,

View File

@ -1,29 +1,87 @@
from logging import getLogger
from typing import Dict, List
from typing import Any, Dict, List
from taleweave.context import get_current_world, get_prompt_library, subscribe
from taleweave.game_system import FormatPerspective, GameSystem
from taleweave.models.entity import Character, Room, World, WorldEntity
from taleweave.models.event import ActionEvent, GameEvent
from taleweave.utils.prompt import format_str
from taleweave.utils.search import find_containing_room
from taleweave.utils.search import find_containing_room, find_portal, find_room
logger = getLogger(__name__)
def create_move_digest(
world: World,
active_room: Room,
active_character: Character,
event: ActionEvent,
) -> str:
source_room = event.room
direction = str(event.parameters.get("direction"))
destination_portal = find_portal(world, direction)
if not destination_portal:
raise ValueError(f"Could not find portal for direction {direction}")
destination_room = find_room(world, destination_portal.destination)
if not destination_room:
raise ValueError(
f"Could not find destination room {destination_portal.destination}"
)
# look up the source portal
source_portal = next(
(
portal
for portal in destination_room.portals
if portal.destination == source_room.name
),
None,
)
if not source_portal:
raise ValueError(f"Could not find source portal for {destination_portal.name}")
mode = "self" if (event.character == active_character) else "other"
mood = "enter" if (destination_room == active_room) else "exit"
message = format_str(
f"digest_move_{mode}_{mood}",
destination_portal=destination_portal,
destination_room=destination_room,
direction=direction,
source_portal=source_portal,
source_room=source_room,
)
return message
def create_turn_digest(
active_room: Room, active_character: Character, turn_events: List[GameEvent]
world: World,
active_room: Room,
active_character: Character,
turn_events: List[GameEvent],
) -> List[str]:
library = get_prompt_library()
messages = []
for event in turn_events:
if isinstance(event, ActionEvent):
if event.character == active_character or event.room == active_room:
# special handling for move actions
if event.action == "action_move":
message = create_move_digest(
world, active_room, active_character, event
)
messages.append(message)
elif event.character == active_character or event.room == active_room:
prompt_key = f"digest_{event.action}"
if prompt_key in library.prompts:
try:
template = library.prompts[prompt_key]
message = format_str(template, event=event)
message = format_str(
template,
active_character=active_character,
active_room=active_room,
event=event,
)
messages.append(message)
except Exception:
logger.exception("error formatting digest event: %s", event)
@ -65,10 +123,16 @@ def format_digest(
if not room:
raise ValueError("Character not found in any room")
digest = create_turn_digest(room, entity, buffer)
digest = create_turn_digest(world, room, entity, buffer)
return "\n".join(digest)
def generate_digest(agent: Any, theme: str, entity: WorldEntity):
if isinstance(entity, Character):
if entity.name not in character_buffers:
character_buffers[entity.name] = []
def initialize_digest(world: World):
for room in world.rooms:
for character in room.characters:
@ -77,4 +141,11 @@ def initialize_digest(world: World):
def init():
subscribe(GameEvent, digest_listener)
return [GameSystem("digest", format=format_digest, initialize=initialize_digest)]
return [
GameSystem(
"digest",
format=format_digest,
generate=generate_digest,
initialize=initialize_digest,
)
]

View File

@ -3,6 +3,8 @@ from logging import getLogger
from jinja2 import Environment
from taleweave.context import get_prompt_library
# from taleweave.utils.conversation import summarize_room
from taleweave.utils.string import and_list, or_list
from taleweave.utils.world import describe_entity, name_entity
@ -11,6 +13,7 @@ logger = getLogger(__name__)
jinja_env = Environment()
jinja_env.filters["describe"] = describe_entity
jinja_env.filters["name"] = name_entity
# jinja_env.filters["summary"] = summarize_room
jinja_env.filters["and_list"] = and_list
jinja_env.filters["or_list"] = or_list

View File

@ -12,7 +12,11 @@ def describe_character(
perspective: FormatPerspective = FormatPerspective.SECOND_PERSON,
) -> str:
attribute_descriptions = format_attributes(character, perspective=perspective)
logger.info("describing character: %s, %s", character.name, attribute_descriptions)
logger.info(
"describing character: %s, attributes: '%s'",
character.name,
attribute_descriptions,
)
if perspective == FormatPerspective.SECOND_PERSON:
character_description = character.backstory