load prompts from globs, provide more information to prompt templates, make pseudo-function parsing more flexible, add enter/exit messages to digest system
This commit is contained in:
parent
573b15befb
commit
8a4ecd2588
|
@ -186,23 +186,7 @@ prompts:
|
||||||
action_check_calendar_each: |
|
action_check_calendar_each: |
|
||||||
{{name}} will happen in {{turns}} turn
|
{{name}} will happen in {{turns}} turn
|
||||||
|
|
||||||
# digest system
|
# default dungeon master
|
||||||
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
|
|
||||||
world_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
|
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
|
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: |
|
world_generate_room_broadcast_characters: |
|
||||||
Generating {{character_count}} characters for room: {{name}}
|
Generating {{character_count}} characters for room: {{name}}
|
||||||
world_generate_room_broadcast_portals: |
|
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: |
|
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}}.
|
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 simulation
|
||||||
world_simulate_character_action: |
|
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 characters: {{visible_characters}}.
|
||||||
The room contains the following items: {{visible_items}}.
|
The room contains the following items: {{visible_items}}.
|
||||||
Your inventory contains the following items: {{character_items}}.
|
Your inventory contains the following items: {{character_items}}.
|
||||||
|
|
|
@ -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]}}.
|
|
@ -1,18 +1,18 @@
|
||||||
prompts:
|
prompts:
|
||||||
action_accept_quest_error_none: No quests are available at the moment.
|
action_accept_quest_error_none: No quests are available at the moment.
|
||||||
action_accept_quest_error_name: |
|
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: |
|
action_accept_quest_error_room: |
|
||||||
{{character}} is not in the room.
|
{{character}} is not in the room.
|
||||||
action_accept_quest_result: |
|
action_accept_quest_result: |
|
||||||
You have started the quest "{{quest_name}}".
|
You have started the quest "{{quest.name}}".
|
||||||
|
|
||||||
action_submit_quest_error_active: |
|
action_submit_quest_error_active: |
|
||||||
You do not have any active quests.
|
You do not have any active quests.
|
||||||
action_submit_quest_error_none: No quests are available at the moment.
|
action_submit_quest_error_none: No quests are available at the moment.
|
||||||
action_submit_quest_error_name: |
|
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: |
|
action_submit_quest_error_room: |
|
||||||
{{character}} is not in the room.
|
{{character}} is not in the room.
|
||||||
action_submit_quest_result: |
|
action_submit_quest_result: |
|
||||||
You have completed the quest "{{quest_name}}".
|
You have completed the quest "{{quest.name}}".
|
|
@ -37,6 +37,7 @@ def action_examine(target: str) -> str:
|
||||||
format_prompt(
|
format_prompt(
|
||||||
"action_examine_broadcast_action",
|
"action_examine_broadcast_action",
|
||||||
action_character=action_character,
|
action_character=action_character,
|
||||||
|
action_room=action_room,
|
||||||
target=target,
|
target=target,
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
@ -49,7 +50,7 @@ def action_examine(target: str) -> str:
|
||||||
action_room=action_room,
|
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)
|
target_character = find_character_in_room(action_room, target)
|
||||||
if target_character:
|
if target_character:
|
||||||
|
@ -118,7 +119,7 @@ def action_move(direction: str) -> str:
|
||||||
format_prompt(
|
format_prompt(
|
||||||
"action_move_error_room",
|
"action_move_error_room",
|
||||||
direction=direction,
|
direction=direction,
|
||||||
destination=portal.destination,
|
portal=portal,
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -74,11 +74,11 @@ def submit_quest(character: str) -> str:
|
||||||
return format_prompt(
|
return format_prompt(
|
||||||
"action_submit_quest_result",
|
"action_submit_quest_result",
|
||||||
character=character,
|
character=character,
|
||||||
quest=active_quest.name,
|
quest=active_quest,
|
||||||
)
|
)
|
||||||
|
|
||||||
return format_prompt(
|
return format_prompt(
|
||||||
"action_submit_quest_error_name",
|
"action_submit_quest_error_name",
|
||||||
character=character,
|
character=character,
|
||||||
quest=active_quest.name,
|
quest=active_quest,
|
||||||
)
|
)
|
||||||
|
|
|
@ -523,7 +523,7 @@ def link_rooms(
|
||||||
format_prompt(
|
format_prompt(
|
||||||
"world_generate_room_broadcast_portals",
|
"world_generate_room_broadcast_portals",
|
||||||
portal_count=num_portals,
|
portal_count=num_portals,
|
||||||
name=room.name,
|
room=room,
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import atexit
|
import atexit
|
||||||
from functools import partial
|
from functools import partial
|
||||||
|
from glob import glob
|
||||||
from logging.config import dictConfig
|
from logging.config import dictConfig
|
||||||
from os import environ, path
|
from os import environ, path
|
||||||
from typing import List
|
from typing import List
|
||||||
|
@ -195,7 +196,11 @@ def get_world_prompt(args) -> WorldPrompt:
|
||||||
|
|
||||||
def load_prompt_library(args) -> None:
|
def load_prompt_library(args) -> None:
|
||||||
if args.prompts:
|
if args.prompts:
|
||||||
|
prompt_files = []
|
||||||
for prompt_file in args.prompts:
|
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:
|
with open(prompt_file, "r") as f:
|
||||||
new_library = PromptLibrary(**load_yaml(f))
|
new_library = PromptLibrary(**load_yaml(f))
|
||||||
logger.info(f"loaded prompt library from {prompt_file}")
|
logger.info(f"loaded prompt library from {prompt_file}")
|
||||||
|
@ -258,6 +263,7 @@ def load_or_generate_world(
|
||||||
llm,
|
llm,
|
||||||
memory_factory=memory_factory,
|
memory_factory=memory_factory,
|
||||||
)
|
)
|
||||||
|
set_dungeon_master(world_builder)
|
||||||
|
|
||||||
if path.exists(world_state_file):
|
if path.exists(world_state_file):
|
||||||
logger.info(f"loading world state from {world_state_file}")
|
logger.info(f"loading world state from {world_state_file}")
|
||||||
|
@ -285,6 +291,8 @@ def load_or_generate_world(
|
||||||
systems,
|
systems,
|
||||||
room_count=args.rooms,
|
room_count=args.rooms,
|
||||||
)
|
)
|
||||||
|
load_or_initialize_system_data(args, systems, world)
|
||||||
|
|
||||||
save_world(world, world_file)
|
save_world(world, world_file)
|
||||||
save_system_data(args, systems)
|
save_system_data(args, systems)
|
||||||
|
|
||||||
|
|
|
@ -10,6 +10,7 @@ from packit.toolbox import Toolbox
|
||||||
|
|
||||||
from taleweave.context import action_context
|
from taleweave.context import action_context
|
||||||
from taleweave.models.event import PromptEvent
|
from taleweave.models.event import PromptEvent
|
||||||
|
from taleweave.utils import try_parse_float, try_parse_int
|
||||||
|
|
||||||
logger = getLogger(__name__)
|
logger = getLogger(__name__)
|
||||||
|
|
||||||
|
@ -122,12 +123,18 @@ class BasePlayer:
|
||||||
def parse_value(value: str) -> str | bool | float | int:
|
def parse_value(value: str) -> str | bool | float | int:
|
||||||
if value.startswith("~"):
|
if value.startswith("~"):
|
||||||
return value[1:]
|
return value[1:]
|
||||||
if value.lower() in ["true", "false"]:
|
|
||||||
return value.lower() == "true"
|
if value.lower() in ["true", "false", "yes", "no", "y", "n"]:
|
||||||
if value.isdecimal():
|
return value.lower() in ["true", "yes", "y"]
|
||||||
return float(value)
|
|
||||||
if value.isnumeric():
|
int_value = try_parse_int(value)
|
||||||
|
if int_value is not None:
|
||||||
return int(value)
|
return int(value)
|
||||||
|
|
||||||
|
float_value = try_parse_float(value)
|
||||||
|
if float_value is not None:
|
||||||
|
return float(value)
|
||||||
|
|
||||||
return value
|
return value
|
||||||
|
|
||||||
params = {
|
params = {
|
||||||
|
|
|
@ -53,7 +53,7 @@ from taleweave.utils.effect import expire_effects
|
||||||
from taleweave.utils.planning import expire_events, get_upcoming_events
|
from taleweave.utils.planning import expire_events, get_upcoming_events
|
||||||
from taleweave.utils.prompt import format_prompt
|
from taleweave.utils.prompt import format_prompt
|
||||||
from taleweave.utils.search import find_containing_room
|
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__)
|
logger = getLogger(__name__)
|
||||||
|
|
||||||
|
@ -136,8 +136,7 @@ def prompt_character_action(
|
||||||
character_items=character_items,
|
character_items=character_items,
|
||||||
attributes=character_attributes,
|
attributes=character_attributes,
|
||||||
directions=room_directions,
|
directions=room_directions,
|
||||||
room_name=room.name,
|
room=room,
|
||||||
room_description=describe_entity(room),
|
|
||||||
visible_characters=room_characters,
|
visible_characters=room_characters,
|
||||||
visible_items=room_items,
|
visible_items=room_items,
|
||||||
notes_prompt=notes_prompt,
|
notes_prompt=notes_prompt,
|
||||||
|
|
|
@ -1,29 +1,87 @@
|
||||||
from logging import getLogger
|
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.context import get_current_world, get_prompt_library, subscribe
|
||||||
from taleweave.game_system import FormatPerspective, GameSystem
|
from taleweave.game_system import FormatPerspective, GameSystem
|
||||||
from taleweave.models.entity import Character, Room, World, WorldEntity
|
from taleweave.models.entity import Character, Room, World, WorldEntity
|
||||||
from taleweave.models.event import ActionEvent, GameEvent
|
from taleweave.models.event import ActionEvent, GameEvent
|
||||||
from taleweave.utils.prompt import format_str
|
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__)
|
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(
|
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]:
|
) -> List[str]:
|
||||||
library = get_prompt_library()
|
library = get_prompt_library()
|
||||||
messages = []
|
messages = []
|
||||||
for event in turn_events:
|
for event in turn_events:
|
||||||
if isinstance(event, ActionEvent):
|
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}"
|
prompt_key = f"digest_{event.action}"
|
||||||
if prompt_key in library.prompts:
|
if prompt_key in library.prompts:
|
||||||
try:
|
try:
|
||||||
template = library.prompts[prompt_key]
|
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)
|
messages.append(message)
|
||||||
except Exception:
|
except Exception:
|
||||||
logger.exception("error formatting digest event: %s", event)
|
logger.exception("error formatting digest event: %s", event)
|
||||||
|
@ -65,10 +123,16 @@ def format_digest(
|
||||||
if not room:
|
if not room:
|
||||||
raise ValueError("Character not found in any 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)
|
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):
|
def initialize_digest(world: World):
|
||||||
for room in world.rooms:
|
for room in world.rooms:
|
||||||
for character in room.characters:
|
for character in room.characters:
|
||||||
|
@ -77,4 +141,11 @@ def initialize_digest(world: World):
|
||||||
|
|
||||||
def init():
|
def init():
|
||||||
subscribe(GameEvent, digest_listener)
|
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,
|
||||||
|
)
|
||||||
|
]
|
||||||
|
|
|
@ -3,6 +3,8 @@ from logging import getLogger
|
||||||
from jinja2 import Environment
|
from jinja2 import Environment
|
||||||
|
|
||||||
from taleweave.context import get_prompt_library
|
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.string import and_list, or_list
|
||||||
from taleweave.utils.world import describe_entity, name_entity
|
from taleweave.utils.world import describe_entity, name_entity
|
||||||
|
|
||||||
|
@ -11,6 +13,7 @@ logger = getLogger(__name__)
|
||||||
jinja_env = Environment()
|
jinja_env = Environment()
|
||||||
jinja_env.filters["describe"] = describe_entity
|
jinja_env.filters["describe"] = describe_entity
|
||||||
jinja_env.filters["name"] = name_entity
|
jinja_env.filters["name"] = name_entity
|
||||||
|
# jinja_env.filters["summary"] = summarize_room
|
||||||
jinja_env.filters["and_list"] = and_list
|
jinja_env.filters["and_list"] = and_list
|
||||||
jinja_env.filters["or_list"] = or_list
|
jinja_env.filters["or_list"] = or_list
|
||||||
|
|
||||||
|
|
|
@ -12,7 +12,11 @@ def describe_character(
|
||||||
perspective: FormatPerspective = FormatPerspective.SECOND_PERSON,
|
perspective: FormatPerspective = FormatPerspective.SECOND_PERSON,
|
||||||
) -> str:
|
) -> str:
|
||||||
attribute_descriptions = format_attributes(character, perspective=perspective)
|
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:
|
if perspective == FormatPerspective.SECOND_PERSON:
|
||||||
character_description = character.backstory
|
character_description = character.backstory
|
||||||
|
|
Loading…
Reference in New Issue