mirror of
https://github.com/rowboatlabs/rowboat.git
synced 2026-05-31 19:15:17 +02:00
Merge changes v1
This commit is contained in:
parent
b2fd9bf877
commit
24efe0e887
45 changed files with 2940 additions and 294 deletions
48
apps/rowboat_agents/src/graph/helpers/access.py
Normal file
48
apps/rowboat_agents/src/graph/helpers/access.py
Normal file
|
|
@ -0,0 +1,48 @@
|
|||
from src.utils.common import common_logger
|
||||
logger = common_logger
|
||||
|
||||
def get_external_tools(tool_configs):
|
||||
logger.debug("Getting external tools")
|
||||
tools = [tool["name"] for tool in tool_configs]
|
||||
logger.debug(f"Found {len(tools)} external tools")
|
||||
return tools
|
||||
|
||||
def get_agent_by_name(agent_name, agents):
|
||||
agent = next((a for a in agents if getattr(a, "name", None) == agent_name), None)
|
||||
if not agent:
|
||||
logger.error(f"Agent with name {agent_name} not found")
|
||||
raise ValueError(f"Agent with name {agent_name} not found")
|
||||
return agent
|
||||
|
||||
def get_agent_config_by_name(agent_name, agent_configs):
|
||||
agent_config = next((ac for ac in agent_configs if ac.get("name") == agent_name), None)
|
||||
if not agent_config:
|
||||
logger.error(f"Agent config with name {agent_name} not found")
|
||||
raise ValueError(f"Agent config with name {agent_name} not found")
|
||||
return agent_config
|
||||
|
||||
def pop_agent_config_by_type(agent_configs, agent_type):
|
||||
agent_config = next((ac for ac in agent_configs if ac.get("type") == agent_type), None)
|
||||
if agent_config:
|
||||
agent_configs.remove(agent_config)
|
||||
return agent_config, agent_configs
|
||||
|
||||
def get_agent_by_type(agents, agent_type):
|
||||
return next((a for a in agents if a.type == agent_type), None)
|
||||
|
||||
def get_prompt_by_type(prompt_configs, prompt_type):
|
||||
return next((pc.get("prompt") for pc in prompt_configs if pc.get("type") == prompt_type), None)
|
||||
|
||||
def get_agent_data_by_name(agent_name, agent_data):
|
||||
for data in agent_data:
|
||||
name = data.get("name", "")
|
||||
if name == agent_name:
|
||||
return data
|
||||
|
||||
return None
|
||||
|
||||
def get_tool_config_by_name(tool_configs, tool_name):
|
||||
return next((tc for tc in tool_configs if tc.get("name", "") == tool_name), None)
|
||||
|
||||
def get_tool_config_by_type(tool_configs, tool_type):
|
||||
return next((tc for tc in tool_configs if tc.get("type", "") == tool_type), None)
|
||||
50
apps/rowboat_agents/src/graph/helpers/control.py
Normal file
50
apps/rowboat_agents/src/graph/helpers/control.py
Normal file
|
|
@ -0,0 +1,50 @@
|
|||
from .access import get_agent_config_by_name, get_agent_data_by_name
|
||||
from src.graph.types import ControlType
|
||||
from src.utils.common import common_logger
|
||||
logger = common_logger
|
||||
|
||||
def get_last_agent_name(state, agent_configs, start_agent_name, msg_type, latest_assistant_msg, start_turn_with_start_agent):
|
||||
default_last_agent_name = state.get("last_agent_name", '')
|
||||
last_agent_config = get_agent_config_by_name(default_last_agent_name, agent_configs)
|
||||
specific_agent_data = get_agent_data_by_name(default_last_agent_name, state.get("agent_data", []))
|
||||
|
||||
# Overrides for special cases
|
||||
logger.info("Setting agent control based on last agent and control type")
|
||||
if msg_type == "tool":
|
||||
last_agent_name = default_last_agent_name
|
||||
assert last_agent_name == latest_assistant_msg.get("sender", ''), "Last agent name does not match sender of latest assistant message during tool call handling"
|
||||
|
||||
elif start_turn_with_start_agent:
|
||||
last_agent_name = start_agent_name
|
||||
|
||||
else:
|
||||
control_type = last_agent_config.get("controlType", ControlType.RETAIN.value)
|
||||
if control_type == ControlType.PARENT_AGENT.value:
|
||||
last_agent_name = specific_agent_data.get("most_recent_parent_name", None) if specific_agent_data else None
|
||||
if not last_agent_name:
|
||||
logger.error("Most recent parent is empty, defaulting to same agent instead")
|
||||
last_agent_name = default_last_agent_name
|
||||
elif control_type == ControlType.START_AGENT.value:
|
||||
last_agent_name = start_agent_name
|
||||
else:
|
||||
last_agent_name = default_last_agent_name
|
||||
|
||||
if default_last_agent_name != last_agent_name:
|
||||
logger.info(f"Last agent name changed from {default_last_agent_name} to {last_agent_name} due to control settings")
|
||||
|
||||
return last_agent_name
|
||||
|
||||
|
||||
def get_latest_assistant_msg(messages):
|
||||
# Find the latest message with role assistant
|
||||
for i in range(len(messages)-1, -1, -1):
|
||||
if messages[i].get("role") == "assistant":
|
||||
return messages[i]
|
||||
return None
|
||||
|
||||
def get_latest_non_assistant_messages(messages):
|
||||
# Find all messages after the last assistant message
|
||||
for i in range(len(messages)-1, -1, -1):
|
||||
if messages[i].get("role") == "assistant":
|
||||
return messages[i+1:]
|
||||
return messages
|
||||
39
apps/rowboat_agents/src/graph/helpers/instructions.py
Normal file
39
apps/rowboat_agents/src/graph/helpers/instructions.py
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
from src.graph.instructions import TRANSFER_CHILDREN_INSTRUCTIONS, TRANSFER_PARENT_AWARE_INSTRUCTIONS, RAG_INSTRUCTIONS, ERROR_ESCALATION_AGENT_INSTRUCTIONS, TRANSFER_GIVE_UP_CONTROL_INSTRUCTIONS, SYSTEM_MESSAGE
|
||||
|
||||
def add_transfer_instructions_to_parent_agents(agent, children, transfer_functions):
|
||||
other_agent_name_descriptions_tools = f'\n{'-'*100}\n'.join([f"Name: {agent.name}\nDescription: {agent.description if agent.description else ''}\nTool for transfer: {transfer_functions[agent.name].__name__}" for agent in children.values()])
|
||||
|
||||
prompt = TRANSFER_CHILDREN_INSTRUCTIONS.format(other_agent_name_descriptions_tools=other_agent_name_descriptions_tools)
|
||||
agent.instructions = agent.instructions + f'\n\n{'-'*100}\n\n' + prompt
|
||||
|
||||
return agent
|
||||
|
||||
def add_transfer_instructions_to_child_agents(child, children_aware_of_parent):
|
||||
if children_aware_of_parent:
|
||||
candidate_parents_name_description_tools = f'\n{'-'*100}\n'.join([f"Name: {parent_name}\nTool for transfer: {func.__name__}" for parent_name, func in child.candidate_parent_functions.items()])
|
||||
prompt = TRANSFER_PARENT_AWARE_INSTRUCTIONS.format(candidate_parents_name_description_tools=candidate_parents_name_description_tools)
|
||||
else:
|
||||
candidate_parents_name_description_tools = f'\n{'-'*100}\n'.join(list(set([f"Tool for transfer: {func.__name__}" for _, func in child.candidate_parent_functions.items()])))
|
||||
prompt = TRANSFER_GIVE_UP_CONTROL_INSTRUCTIONS.format(candidate_parents_name_description_tools=candidate_parents_name_description_tools)
|
||||
|
||||
child.instructions = child.instructions + f'\n\n{'-'*100}\n\n' + prompt
|
||||
return child
|
||||
|
||||
def add_rag_instructions_to_agent(agent_config, rag_tool_name):
|
||||
prompt = RAG_INSTRUCTIONS.format(rag_tool_name=rag_tool_name)
|
||||
agent_config["instructions"] = agent_config["instructions"] + f'\n\n{'-'*100}\n\n' + prompt
|
||||
return agent_config
|
||||
|
||||
def add_error_escalation_instructions(agent):
|
||||
prompt = ERROR_ESCALATION_AGENT_INSTRUCTIONS
|
||||
agent.instructions = agent.instructions + f'\n\n{'-'*100}\n\n' + prompt
|
||||
return agent
|
||||
|
||||
def get_universal_system_message(messages):
|
||||
if messages and messages[0].get("role") == "system":
|
||||
return SYSTEM_MESSAGE.format(system_message=messages[0].get("content"))
|
||||
return ""
|
||||
|
||||
def add_universal_system_message_to_agent(agent, universal_sys_msg):
|
||||
agent.instructions = agent.instructions + f'\n\n{'-'*100}\n\n' + universal_sys_msg
|
||||
return agent
|
||||
60
apps/rowboat_agents/src/graph/helpers/state.py
Normal file
60
apps/rowboat_agents/src/graph/helpers/state.py
Normal file
|
|
@ -0,0 +1,60 @@
|
|||
from src.utils.common import common_logger
|
||||
logger = common_logger
|
||||
from .access import get_agent_data_by_name
|
||||
|
||||
def reset_current_turn(messages):
|
||||
# Set all messages' current_turn to False
|
||||
for msg in messages:
|
||||
msg["current_turn"] = False
|
||||
|
||||
# Find most recent user message
|
||||
messages[-1]["current_turn"] = True
|
||||
|
||||
return messages
|
||||
|
||||
def reset_current_turn_agent_history(agent_data, agent_names):
|
||||
for name in agent_names:
|
||||
data = get_agent_data_by_name(name, agent_data)
|
||||
if data:
|
||||
for msg in data["history"]:
|
||||
msg["current_turn"] = False
|
||||
return agent_data
|
||||
|
||||
def add_recent_messages_to_history(recent_messages, last_agent_name, agent_data, messages, parent_has_child_history):
|
||||
last_msg = messages[-1]
|
||||
specific_agent_data = get_agent_data_by_name(last_agent_name, agent_data)
|
||||
if specific_agent_data:
|
||||
specific_agent_data["history"].extend(recent_messages)
|
||||
if parent_has_child_history:
|
||||
current_agent_data = specific_agent_data
|
||||
while current_agent_data.get("most_recent_parent_name"):
|
||||
parent_name = current_agent_data.get("most_recent_parent_name")
|
||||
parent_agent_data = get_agent_data_by_name(parent_name, agent_data)
|
||||
if parent_agent_data:
|
||||
parent_agent_data["history"].extend(recent_messages)
|
||||
current_agent_data = parent_agent_data
|
||||
else:
|
||||
logger.error(f"Parent agent data for {current_agent_data['name']} not found in agent_data")
|
||||
raise ValueError(f"Parent agent data for {current_agent_data['name']} not found in agent_data")
|
||||
else:
|
||||
agent_data.append({
|
||||
"name": last_agent_name,
|
||||
"history": [last_msg]
|
||||
})
|
||||
|
||||
return agent_data
|
||||
|
||||
def construct_state_from_response(response, agents):
|
||||
agent_data = []
|
||||
for agent in agents:
|
||||
agent_data.append({
|
||||
"name": agent.name,
|
||||
"instructions": agent.instructions
|
||||
})
|
||||
|
||||
state = {
|
||||
"last_agent_name": response.agent.name,
|
||||
"agent_data": agent_data
|
||||
}
|
||||
|
||||
return state
|
||||
44
apps/rowboat_agents/src/graph/helpers/transfer.py
Normal file
44
apps/rowboat_agents/src/graph/helpers/transfer.py
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
from src.utils.common import common_logger
|
||||
logger = common_logger
|
||||
|
||||
def create_transfer_function_to_agent(agent):
|
||||
agent_name = agent.name
|
||||
|
||||
fn_spec = {
|
||||
"name": f"transfer_to_{agent_name.lower().replace(' ', '_')}",
|
||||
"description": f"Function to transfer the chat to {agent_name}.",
|
||||
"return_value": agent
|
||||
}
|
||||
|
||||
def generated_function(*args, **kwargs):
|
||||
logger.info(f"Transferring chat to {agent_name}")
|
||||
return fn_spec.get('return_value', None)
|
||||
|
||||
generated_function.__name__ = fn_spec['name']
|
||||
generated_function.__doc__ = fn_spec.get('description', '')
|
||||
|
||||
return generated_function
|
||||
|
||||
def create_transfer_function_to_parent_agent(parent_agent, children_aware_of_parent, transfer_functions):
|
||||
if children_aware_of_parent:
|
||||
name = f"{transfer_functions[parent_agent.name].__name__}_from_child"
|
||||
description = f"Function to transfer the chat to your parent agent: {parent_agent.name}."
|
||||
else:
|
||||
name = "give_up_chat_control"
|
||||
description = "Function to give up control of the chat when you are unable to handle it."
|
||||
|
||||
|
||||
fn_spec = {
|
||||
"name": name,
|
||||
"description": description,
|
||||
"return_value": parent_agent
|
||||
}
|
||||
|
||||
def generated_function(*args, **kwargs):
|
||||
logger.info(f"Transferring chat to parent agent: {parent_agent.name}")
|
||||
return fn_spec.get('return_value', None)
|
||||
|
||||
generated_function.__name__ = fn_spec['name']
|
||||
generated_function.__doc__ = fn_spec.get('description', '')
|
||||
|
||||
return generated_function
|
||||
Loading…
Add table
Add a link
Reference in a new issue