mirror of https://github.com/aliasrobotics/cai.git
fix agent command
This commit is contained in:
parent
483591f36a
commit
56e6fcf1a4
|
|
@ -280,8 +280,9 @@ def run_cai_cli(starting_agent, context_variables=None, stream=False, max_turns=
|
|||
if commands_handle_command(command, args):
|
||||
continue # Command was handled, continue to next iteration
|
||||
|
||||
# If command wasn't recognized, show error
|
||||
console.print(f"[red]Unknown command: {command}[/red]")
|
||||
# If command wasn't recognized, show error (skip for /shell or /s)
|
||||
if command not in ("/shell", "/s"):
|
||||
console.print(f"[red]Unknown command: {command}[/red]")
|
||||
continue
|
||||
|
||||
# Process the conversation with the agent
|
||||
|
|
|
|||
|
|
@ -155,50 +155,45 @@ class AgentCommand(Command):
|
|||
table = Table(title="Available Agents")
|
||||
table.add_column("#", style="dim")
|
||||
table.add_column("Name", style="cyan")
|
||||
table.add_column("Module", style="magenta")
|
||||
table.add_column("Key", style="magenta")
|
||||
table.add_column("Module", style="green")
|
||||
table.add_column("Description", style="green")
|
||||
table.add_column("Pattern", style="blue")
|
||||
table.add_column("Model", style="yellow")
|
||||
|
||||
# Scan all agents from the agents folder
|
||||
# Retrieve all registered agents
|
||||
agents_to_display = get_available_agents()
|
||||
|
||||
# Display all agents
|
||||
for i, (name, agent) in enumerate(agents_to_display.items(), 1):
|
||||
description = agent.description
|
||||
if not description and hasattr(agent, 'instructions'):
|
||||
if callable(agent.instructions):
|
||||
description = agent.instructions(context_variables={})
|
||||
else:
|
||||
description = agent.instructions
|
||||
# Clean up description - remove newlines and strip spaces
|
||||
for idx, (agent_key, agent) in enumerate(agents_to_display.items(), start=1):
|
||||
# Human-friendly name (falls back to the dict key)
|
||||
display_name = getattr(agent, "name", agent_key)
|
||||
|
||||
# Use provided description, otherwise derive from instructions
|
||||
description = getattr(agent, "description", "") or ""
|
||||
if not description and hasattr(agent, "instructions"):
|
||||
instr = agent.instructions
|
||||
description = instr(context_variables={}) if callable(instr) else instr
|
||||
if isinstance(description, str):
|
||||
description = " ".join(description.split())
|
||||
if len(description) > 50:
|
||||
description = description[:47] + "..."
|
||||
|
||||
# Get the module name for the agent
|
||||
module_name = get_agent_module(name)
|
||||
# Module where this agent lives
|
||||
module_name = get_agent_module(agent_key)
|
||||
|
||||
# Get the pattern if it exists
|
||||
pattern = getattr(agent, 'pattern', '')
|
||||
if pattern:
|
||||
pattern = pattern.capitalize()
|
||||
|
||||
# Handle model display based on agent type
|
||||
model_display = self._get_model_display(name, agent)
|
||||
# Add a row with all collected info
|
||||
table.add_row(
|
||||
str(i),
|
||||
name,
|
||||
str(idx),
|
||||
display_name,
|
||||
agent_key,
|
||||
module_name,
|
||||
description,
|
||||
pattern,
|
||||
model_display
|
||||
description
|
||||
)
|
||||
|
||||
console.print(table)
|
||||
return True
|
||||
|
||||
console.print(table)
|
||||
return True
|
||||
|
||||
def handle_select(self, args: Optional[List[str]] = None) -> bool: # pylint: disable=too-many-branches,line-too-long # noqa: E501
|
||||
"""Handle /agent select command.
|
||||
|
||||
|
|
@ -210,76 +205,41 @@ class AgentCommand(Command):
|
|||
"""
|
||||
if not args:
|
||||
console.print("[red]Error: No agent specified[/red]")
|
||||
console.print("Usage: /agent select <name|number>")
|
||||
console.print("Usage: /agent select <agent_key|number>")
|
||||
return False
|
||||
|
||||
agent_id = args[0]
|
||||
|
||||
# Get the list of available agents
|
||||
agents_to_display = get_available_agents()
|
||||
agent_list = list(agents_to_display.items()) # preserve order for indexing
|
||||
|
||||
# Check if agent_id is a number
|
||||
if agent_id.isdigit():
|
||||
index = int(agent_id)
|
||||
if 1 <= index <= len(agents_to_display):
|
||||
agent_name = list(agents_to_display.keys())[index - 1]
|
||||
if 1 <= index <= len(agent_list):
|
||||
# Get the agent tuple from the list
|
||||
selected_agent_key, selected_agent = agent_list[index - 1]
|
||||
agent_name = getattr(selected_agent, "name", selected_agent_key)
|
||||
agent = selected_agent
|
||||
else:
|
||||
console.print(
|
||||
f"[red]Error: Invalid agent number: {agent_id}[/red]")
|
||||
console.print(f"[red]Error: Invalid agent number: {agent_id}[/red]")
|
||||
return False
|
||||
else:
|
||||
# Treat as agent name
|
||||
agent_name = agent_id
|
||||
if agent_name not in agents_to_display:
|
||||
console.print(f"[red]Error: Unknown agent: {agent_name}[/red]")
|
||||
# Treat as agent key
|
||||
for key, agent_obj in agents_to_display.items():
|
||||
if key == agent_id:
|
||||
agent = agent_obj
|
||||
agent_name = getattr(agent_obj, "name", key)
|
||||
break
|
||||
else:
|
||||
console.print(f"[red]Error: Unknown agent key: {agent_id}[/red]")
|
||||
return False
|
||||
|
||||
# Get the agent
|
||||
agent = agents_to_display[agent_name]
|
||||
|
||||
# Set the agent as the current agent in the REPL
|
||||
# We need to avoid circular imports, so we'll use a different approach
|
||||
# to access the client and current_agent variables
|
||||
|
||||
# Import the module dynamically to avoid circular imports
|
||||
if 'cai.repl.repl' in sys.modules:
|
||||
repl_module = sys.modules['cai.repl.repl']
|
||||
|
||||
# Check if client is initialized
|
||||
if hasattr(repl_module, 'client') and repl_module.client:
|
||||
# Update the active_agent in the client
|
||||
repl_module.client.active_agent = agent
|
||||
|
||||
# Update the global current_agent variable if it exists
|
||||
if hasattr(repl_module, 'current_agent'):
|
||||
repl_module.current_agent = agent
|
||||
|
||||
# Update the global agent variable if it exists
|
||||
if hasattr(repl_module, 'agent'):
|
||||
repl_module.agent = agent
|
||||
|
||||
# Also update the agent variable in the run_demo_loop
|
||||
# function's frame if possible
|
||||
try:
|
||||
for frame_info in inspect.stack():
|
||||
frame = frame_info.frame
|
||||
if ('run_demo_loop' in frame.f_code.co_name and
|
||||
'agent' in frame.f_locals):
|
||||
frame.f_locals['agent'] = agent
|
||||
break
|
||||
except Exception: # pylint: disable=broad-except # nosec
|
||||
# If this fails, we still have the global current_agent as
|
||||
# a fallback
|
||||
pass
|
||||
|
||||
console.print(
|
||||
f"[green]Switched to agent: {agent_name}[/green]")
|
||||
visualize_agent_graph(agent)
|
||||
return True
|
||||
console.print("[red]Error: CAI client not initialized[/red]")
|
||||
return False
|
||||
console.print("[red]Error: REPL module not initialized[/red]")
|
||||
return False
|
||||
os.environ["CAI_MODEL"] = agent_name
|
||||
console.print(
|
||||
f"[green]Switched to agent: {agent_name}[/green]")
|
||||
visualize_agent_graph(agent)
|
||||
return True
|
||||
|
||||
def handle_info(self, args: Optional[List[str]] = None) -> bool:
|
||||
"""Handle /agent info command.
|
||||
|
|
@ -292,57 +252,74 @@ class AgentCommand(Command):
|
|||
"""
|
||||
if not args:
|
||||
console.print("[red]Error: No agent specified[/red]")
|
||||
console.print("Usage: /agent info <name|number>")
|
||||
console.print("Usage: /agent info <agent_key|number>")
|
||||
return False
|
||||
|
||||
agent_id = args[0]
|
||||
|
||||
# Get the list of available agents
|
||||
# Get available agents
|
||||
agents_to_display = get_available_agents()
|
||||
|
||||
# Check if agent_id is a number
|
||||
# Resolve agent_id to an agent key (by index or name)
|
||||
if agent_id.isdigit():
|
||||
index = int(agent_id)
|
||||
if 1 <= index <= len(agents_to_display):
|
||||
agent_name = list(agents_to_display.keys())[index - 1]
|
||||
else:
|
||||
console.print(
|
||||
f"[red]Error: Invalid agent number: {agent_id}[/red]")
|
||||
idx = int(agent_id)
|
||||
if not (1 <= idx <= len(agents_to_display)):
|
||||
console.print(f"[red]Error: Invalid agent number: {agent_id}[/red]")
|
||||
return False
|
||||
agent_key = list(agents_to_display.keys())[idx - 1]
|
||||
else:
|
||||
# Treat as agent name
|
||||
agent_name = agent_id
|
||||
if agent_name not in agents_to_display:
|
||||
console.print(f"[red]Error: Unknown agent: {agent_name}[/red]")
|
||||
agent_key = None
|
||||
for key, ag in agents_to_display.items():
|
||||
if key == agent_id or getattr(ag, "name", "").lower() == agent_id.lower():
|
||||
agent_key = key
|
||||
break
|
||||
if agent_key is None:
|
||||
console.print(f"[red]Error: Unknown agent key: {agent_id}[/red]")
|
||||
return False
|
||||
|
||||
# Get the agent
|
||||
agent = agents_to_display[agent_name]
|
||||
agent = agents_to_display[agent_key]
|
||||
|
||||
# Display agent information
|
||||
# Display agent information
|
||||
instructions = agent.instructions
|
||||
if callable(instructions):
|
||||
instructions = instructions()
|
||||
# Prepare agent properties
|
||||
name = agent.name or agent_key
|
||||
description = getattr(agent, "description", None) or "N/A"
|
||||
clean_description = " ".join(line.strip() for line in description.splitlines())
|
||||
functions = getattr(agent, "functions", [])
|
||||
parallel = getattr(agent, "parallel_tool_calls", False)
|
||||
handoff_desc = getattr(agent, "handoff_description", None) or "N/A"
|
||||
handoffs = getattr(agent, "handoffs", [])
|
||||
tools = getattr(agent, "tools", [])
|
||||
guardrails_in = getattr(agent, "input_guardrails", [])
|
||||
guardrails_out = getattr(agent, "output_guardrails", [])
|
||||
output_type = getattr(agent, "output_type", None) or "N/A"
|
||||
hooks = getattr(agent, "hooks", []) or []
|
||||
|
||||
# Handle model display based on agent type
|
||||
model_display = self._get_model_display_for_info(agent_name, agent)
|
||||
|
||||
# Create a markdown table for agent details
|
||||
# Build markdown content for agent info
|
||||
markdown_content = f"""
|
||||
# Agent: {agent_name}
|
||||
# Agent Info: {name}
|
||||
|
||||
| Property | Value |
|
||||
|----------|-------|
|
||||
| Name | {agent.name} |
|
||||
| Model | {model_display} |
|
||||
| Functions | {len(agent.functions)} |
|
||||
| Parallel Tool Calls | {'Yes' if agent.parallel_tool_calls else 'No'} |
|
||||
| Property | Value |
|
||||
|------------------------|-------------------------------|
|
||||
| Key | {agent_key} |
|
||||
| Name | {name} |
|
||||
| Description | {clean_description} |
|
||||
| Functions | {len(functions)} |
|
||||
| Parallel Tool Calls | {"Yes" if parallel else "No"} |
|
||||
| Handoff Description | {handoff_desc} |
|
||||
| Handoffs | {len(handoffs)} |
|
||||
| Tools | {len(tools)} |
|
||||
| Input Guardrails | {len(guardrails_in)} |
|
||||
| Output Guardrails | {len(guardrails_out)} |
|
||||
| Output Type | {output_type} |
|
||||
| Hooks | {len(hooks)} |
|
||||
|
||||
## Instructions
|
||||
|
||||
{instructions}
|
||||
"""
|
||||
|
||||
"""
|
||||
console.print(Markdown(markdown_content))
|
||||
return True
|
||||
|
||||
|
|
|
|||
|
|
@ -268,87 +268,72 @@ def load_prompt_template(template_path):
|
|||
except Exception as e:
|
||||
raise ValueError(f"Failed to load template '{template_path}': {str(e)}")
|
||||
|
||||
# Start of Selection
|
||||
def visualize_agent_graph(start_agent):
|
||||
"""
|
||||
Visualize agent graph showing all bidirectional connections between agents.
|
||||
Uses Rich library for pretty printing.
|
||||
"""
|
||||
console = Console() # pylint: disable=redefined-outer-name
|
||||
console = Console()
|
||||
if start_agent is None:
|
||||
console.print("[red]No agent provided to visualize.[/red]")
|
||||
return
|
||||
|
||||
tree = Tree(
|
||||
f"🤖 {
|
||||
start_agent.name} (Current Agent)",
|
||||
guide_style="bold blue")
|
||||
tree = Tree(f"🤖 {start_agent.name} (Current Agent)", guide_style="bold blue")
|
||||
|
||||
# Track visited agents and their nodes to handle cross-connections
|
||||
visited = {}
|
||||
visited = set()
|
||||
agent_nodes = {}
|
||||
agent_positions = {} # Track positions in tree
|
||||
position_counter = 0 # Counter for tracking positions
|
||||
agent_positions = {}
|
||||
position_counter = 0
|
||||
|
||||
def add_agent_node(agent, parent=None, is_transfer=False): # pylint: disable=too-many-branches # noqa: E501
|
||||
"""Add agent node and track for cross-connections"""
|
||||
def add_agent_node(agent, parent=None, is_transfer=False):
|
||||
"""Add an agent node and track for cross-connections."""
|
||||
nonlocal position_counter
|
||||
|
||||
if agent is None:
|
||||
return None
|
||||
aid = id(agent)
|
||||
if aid in visited:
|
||||
if is_transfer and parent:
|
||||
original_pos = agent_positions.get(aid)
|
||||
parent.add(f"[cyan]↩ Return to {agent.name} (Agent #{original_pos})[/cyan]")
|
||||
return agent_nodes.get(aid)
|
||||
|
||||
# Create or get existing node for this agent
|
||||
if id(agent) in visited:
|
||||
if is_transfer:
|
||||
# Add reference with position for repeated agents
|
||||
original_pos = agent_positions[id(agent)]
|
||||
parent.add(
|
||||
f"[cyan]↩ Return to {
|
||||
agent.name} (Top Level Agent #{original_pos})[/cyan]")
|
||||
return agent_nodes[id(agent)]
|
||||
|
||||
visited[id(agent)] = True
|
||||
visited.add(aid)
|
||||
position_counter += 1
|
||||
agent_positions[id(agent)] = position_counter
|
||||
agent_positions[aid] = position_counter
|
||||
|
||||
# Create node for current agent
|
||||
if is_transfer:
|
||||
if is_transfer and parent:
|
||||
node = parent
|
||||
elif parent:
|
||||
node = parent.add(f"[green]{agent.name} (#{position_counter})[/green]")
|
||||
else:
|
||||
node = parent.add(
|
||||
f"[green]{agent.name} (#{position_counter})[/green]") if parent else tree # noqa: E501 pylint: disable=line-too-long
|
||||
agent_nodes[id(agent)] = node
|
||||
node = tree
|
||||
agent_nodes[aid] = node
|
||||
|
||||
# Add tools as children
|
||||
# Add tools
|
||||
tools_node = node.add("[yellow]Tools[/yellow]")
|
||||
for fn in getattr(agent, "functions", []):
|
||||
if callable(fn):
|
||||
fn_name = getattr(fn, "__name__", "")
|
||||
if ("handoff" not in fn_name.lower() and
|
||||
not fn_name.startswith("transfer_to")):
|
||||
tools_node.add(f"[blue]{fn_name}[/blue]")
|
||||
for tool in getattr(agent, "tools", []):
|
||||
tool_name = getattr(tool, "name", None) or getattr(tool, "__name__", "")
|
||||
tools_node.add(f"[blue]{tool_name}[/blue]")
|
||||
|
||||
# Add Handoffs section
|
||||
# Add handoffs
|
||||
transfers_node = node.add("[magenta]Handoffs[/magenta]")
|
||||
for handoff_fn in getattr(agent, "handoffs", []):
|
||||
if callable(handoff_fn):
|
||||
try:
|
||||
next_agent = handoff_fn()
|
||||
if next_agent:
|
||||
transfer_node = transfers_node.add(f"🤖 {next_agent.name}")
|
||||
add_agent_node(next_agent, transfer_node, True)
|
||||
except Exception:
|
||||
continue
|
||||
|
||||
# Process handoff functions
|
||||
for fn in getattr(agent, "functions", []): # pylint: disable=too-many-nested-blocks # noqa: E501
|
||||
if callable(fn):
|
||||
fn_name = getattr(fn, "__name__", "")
|
||||
if ("handoff" in fn_name.lower() or
|
||||
fn_name.startswith("transfer_to")):
|
||||
try:
|
||||
next_agent = fn()
|
||||
if next_agent:
|
||||
# Show bidirectional connection
|
||||
transfer = transfers_node.add(
|
||||
f"🤖 {next_agent.name}") # noqa: E501
|
||||
add_agent_node(next_agent, transfer, True)
|
||||
except Exception: # nosec: B112 # pylint: disable=broad-exception-caught # noqa: E501
|
||||
continue
|
||||
return node
|
||||
# Start recursive traversal from root agent
|
||||
|
||||
# Start traversal from the root agent
|
||||
add_agent_node(start_agent)
|
||||
console.print(tree)
|
||||
# End of Selectio
|
||||
|
||||
def fix_litellm_transcription_annotations():
|
||||
"""
|
||||
|
|
|
|||
Loading…
Reference in New Issue