limit discord channels, ping discord players on their turn, improve action JSON errors
This commit is contained in:
parent
e1c72c3717
commit
aa44632860
|
@ -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}}.
|
|
@ -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,
|
||||||
|
|
|
@ -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,17 +116,20 @@ 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)
|
||||||
event = ActionEvent.from_json(value, room, character)
|
|
||||||
else:
|
|
||||||
raise ActionError(
|
|
||||||
"Your last reply was not valid JSON. Please try again and reply with a valid function call in JSON format."
|
|
||||||
)
|
|
||||||
|
|
||||||
|
# TODO: try to avoid parsing the JSON twice
|
||||||
|
event = ActionEvent.from_json(value, room, character)
|
||||||
broadcast(event)
|
broadcast(event)
|
||||||
|
|
||||||
return world_result_parser(value, **kwargs)
|
return result
|
||||||
|
except ToolError:
|
||||||
|
raise ActionError(
|
||||||
|
format_prompt(
|
||||||
|
"world_simulate_character_action_error_json", actions=action_names
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
# prompt and act
|
# prompt and act
|
||||||
logger.info("starting turn for character: %s", character.name)
|
logger.info("starting turn for character: %s", character.name)
|
||||||
|
@ -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
|
||||||
|
|
|
@ -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:
|
||||||
|
|
Loading…
Reference in New Issue