1
0
Fork 0

limit discord channels, ping discord players on their turn, improve action JSON errors
Run Docker Build / build (push) Successful in 17s Details
Run Python Build / build (push) Successful in 26s Details

This commit is contained in:
Sean Sube 2024-06-07 22:22:12 -05:00
parent e1c72c3717
commit aa44632860
Signed by: ssube
GPG Key ID: 3EED7B957D362AF1
4 changed files with 58 additions and 18 deletions

View File

@ -353,6 +353,9 @@ prompts:
{{notes_prompt}} {{events_prompt}} {{notes_prompt}} {{events_prompt}}
What will you do next? Reply with a JSON function call, calling one of the actions. What will you do next? Reply with a JSON function call, calling one of the actions.
You can only perform one action per turn. What is your next action? You can only perform one action per turn. What is your next action?
world_simulate_character_action_error_json: |
Your last reply was not a valid action or the action you tried to use does not exist. Please try again, being
careful to reply with a valid function call in JSON format. The available actions are: {{actions}}.
world_simulate_character_planning: | world_simulate_character_planning: |
You are about to start your turn. Plan your next action carefully. Take notes and schedule events to help keep track of your goals. You are about to start your turn. Plan your next action carefully. Take notes and schedule events to help keep track of your goals.
@ -376,3 +379,6 @@ prompts:
You have no upcoming events. You have no upcoming events.
world_simulate_character_planning_events_item: | world_simulate_character_planning_events_item: |
{{event.name}} in {{turns}} turns {{event.name}} in {{turns}} turns
world_simulate_character_planning_error_json: |
Your last reply was not a valid action or the action you tried to use does not exist. Please try again, being
careful to reply with a valid function call in JSON format. The available actions are: {{actions}}.

View File

@ -45,6 +45,7 @@ client = None
active_tasks = set() active_tasks = set()
event_messages: Dict[int, str | GameEvent] = {} event_messages: Dict[int, str | GameEvent] = {}
event_queue: Queue[GameEvent] = Queue() event_queue: Queue[GameEvent] = Queue()
player_mentions: Dict[str, str] = {}
def remove_tags(text: str) -> str: def remove_tags(text: str) -> str:
@ -80,6 +81,12 @@ class AdventureClient(Client):
if message.author == self.user: if message.author == self.user:
return return
# make sure the message was in a valid channel
active_channels = get_active_channels()
if message.channel not in active_channels:
return
# get message contents
config = get_game_config() config = get_game_config()
author = message.author author = message.author
channel = message.channel channel = message.channel
@ -143,6 +150,7 @@ class AdventureClient(Client):
) )
set_character_agent(character_name, character, player) set_character_agent(character_name, character, player)
set_player(user_name, player) set_player(user_name, player)
player_mentions[user_name] = author.mention
logger.info(f"{user_name} has joined the game as {character.name}!") logger.info(f"{user_name} has joined the game as {character.name}!")
join_event = PlayerEvent("join", character_name, user_name) join_event = PlayerEvent("join", character_name, user_name)
@ -179,6 +187,9 @@ class AdventureClient(Client):
if content.startswith(config.bot.discord.command_prefix + "leave"): if content.startswith(config.bot.discord.command_prefix + "leave"):
remove_player(user_name) remove_player(user_name)
if user_name in player_mentions:
del player_mentions[user_name]
# revert to LLM agent # revert to LLM agent
character, _ = get_character_agent_for_name(player.name) character, _ = get_character_agent_for_name(player.name)
if character and player.fallback_agent: if character and player.fallback_agent:
@ -443,6 +454,9 @@ def embed_from_prompt(event: PromptEvent):
if user: if user:
# TODO: use Discord user.mention to ping the user # TODO: use Discord user.mention to ping the user
if user in player_mentions:
user = player_mentions[user]
prompt_embed.add_field( prompt_embed.add_field(
name="Player", name="Player",
value=user, value=user,

View File

@ -7,10 +7,10 @@ from typing import Callable, Sequence
from packit.agent import Agent from packit.agent import Agent
from packit.conditions import condition_or, condition_threshold from packit.conditions import condition_or, condition_threshold
from packit.errors import ToolError
from packit.loops import loop_retry from packit.loops import loop_retry
from packit.results import function_result from packit.results import function_result
from packit.toolbox import Toolbox from packit.toolbox import Toolbox
from packit.utils import could_be_json
from taleweave.actions.base import ( from taleweave.actions.base import (
action_ask, action_ask,
@ -80,8 +80,10 @@ def world_result_parser(value, agent, **kwargs):
def prompt_character_action( def prompt_character_action(
room, character, agent, action_names, action_toolbox, current_turn room, character, agent, action_toolbox, current_turn
) -> str: ) -> str:
action_names = action_toolbox.list_tools()
# collect data for the prompt # collect data for the prompt
notes_prompt, events_prompt = get_notes_events(character, current_turn) notes_prompt, events_prompt = get_notes_events(character, current_turn)
@ -114,18 +116,21 @@ def prompt_character_action(
except Exception: except Exception:
pass pass
if could_be_json(value): try:
# TODO: only emit valid actions that parse and run correctly, and try to avoid parsing the JSON twice result = world_result_parser(value, **kwargs)
# TODO: try to avoid parsing the JSON twice
event = ActionEvent.from_json(value, room, character) event = ActionEvent.from_json(value, room, character)
else: broadcast(event)
return result
except ToolError:
raise ActionError( raise ActionError(
"Your last reply was not valid JSON. Please try again and reply with a valid function call in JSON format." format_prompt(
"world_simulate_character_action_error_json", actions=action_names
)
) )
broadcast(event)
return world_result_parser(value, **kwargs)
# prompt and act # prompt and act
logger.info("starting turn for character: %s", character.name) logger.info("starting turn for character: %s", character.name)
result = loop_retry( result = loop_retry(
@ -201,9 +206,21 @@ def prompt_character_planning(
event_count = len(character.planner.calendar.events) event_count = len(character.planner.calendar.events)
note_count = len(character.planner.notes) note_count = len(character.planner.notes)
def result_parser(value, **kwargs):
try:
return function_result(value, **kwargs)
except ToolError:
raise ActionError(
format_prompt(
"world_simulate_character_planning_error_json",
actions=planner_toolbox.list_tools(),
)
)
logger.info("starting planning for character: %s", character.name) logger.info("starting planning for character: %s", character.name)
_, condition_end, result_parser = make_keyword_condition( _, condition_end, result_parser = make_keyword_condition(
get_prompt("world_simulate_character_planning_done") get_prompt("world_simulate_character_planning_done"),
result_parser=result_parser,
) )
stop_condition = condition_or( stop_condition = condition_or(
condition_end, partial(condition_threshold, max=max_steps) condition_end, partial(condition_threshold, max=max_steps)
@ -256,7 +273,6 @@ def simulate_world(
*actions, *actions,
] ]
) )
action_names = action_tools.list_tools()
# build a toolbox for the planners # build a toolbox for the planners
planner_toolbox = Toolbox( planner_toolbox = Toolbox(
@ -309,7 +325,7 @@ def simulate_world(
try: try:
result = prompt_character_action( result = prompt_character_action(
room, character, agent, action_names, action_tools, current_turn room, character, agent, action_tools, current_turn
) )
result_event = ResultEvent( result_event = ResultEvent(
result=result, room=room, character=character result=result, room=room, character=character

View File

@ -18,10 +18,14 @@ from .string import and_list, normalize_name
logger = getLogger(__name__) logger = getLogger(__name__)
def make_keyword_condition(end_message: str, keywords=["end", "stop"]): def make_keyword_condition(
end_message: str,
keywords=["end", "stop"],
result_parser=multi_function_or_str_result,
):
set_end, condition_end = make_flag_condition() set_end, condition_end = make_flag_condition()
def result_parser(value, **kwargs): def inner_parser(value, **kwargs):
normalized_value = normalize_name(value) normalized_value = normalize_name(value)
if normalized_value in keywords: if normalized_value in keywords:
logger.debug(f"found keyword, setting stop condition: {normalized_value}") logger.debug(f"found keyword, setting stop condition: {normalized_value}")
@ -51,9 +55,9 @@ def make_keyword_condition(end_message: str, keywords=["end", "stop"]):
set_end() set_end()
return end_message return end_message
return multi_function_or_str_result(value, **kwargs) return result_parser(value, **kwargs)
return set_end, condition_end, result_parser return set_end, condition_end, inner_parser
def summarize_room(room: Room, player: Character) -> str: def summarize_room(room: Room, player: Character) -> str: