Files
temp/robot/helper_actor_add_rich_output.py
freemo d430d671a0 fix(cli): add missing Type field, Config, Capabilities, and Tools panels to actor add rich output
Extended `_print_actor()` to render the full spec-required output for
`agents actor add`: the Actor Added panel now includes a Type field, and
three additional panels (Config, Capabilities, Tools) plus a success
status line are rendered when `show_add_panels=True`.

- Add `Type:` field to the Actor Added panel (from `config_blob["type"]`)
- Implement Config panel: Path, Hash, Options count, Nodes count, Edges count
- Implement Capabilities panel: bulleted list (rendered only when non-empty)
- Implement Tools panel: Rich table with Tool, Read-Only, Safe columns
  (rendered only when non-empty; string tool entries default to yes/yes)
- Add `✓ OK Actor added` success status line after all panels
- Pass `config_path` and `show_add_panels=True` from `add()` command
- Add Behave BDD scenarios covering each new panel and the success line
- Add Robot Framework integration test verifying the full rich output

ISSUES CLOSED: #1499
2026-04-03 00:23:58 +00:00

197 lines
6.3 KiB
Python

"""Helper script for actor add rich output panels Robot test."""
from __future__ import annotations
import json
import sys
import tempfile
from pathlib import Path
from typing import Any
from unittest.mock import MagicMock, patch
from typer.testing import CliRunner
from cleveragents.cli.commands.actor import app as actor_app
from cleveragents.domain.models.core.actor import Actor
def _make_actor(
*,
name: str = "local/robot-add-actor",
provider: str = "openai",
model: str = "gpt-4",
config: dict[str, Any] | None = None,
graph_descriptor: dict[str, Any] | None = None,
unsafe: bool = False,
is_default: bool = False,
is_built_in: bool = False,
) -> Actor:
blob = config or {}
return Actor(
id=1,
name=name,
provider=provider,
model=model,
config_blob=blob,
config_hash=Actor.compute_hash(blob),
graph_descriptor=graph_descriptor,
unsafe=unsafe,
is_built_in=is_built_in,
is_default=is_default,
)
def _run_actor_add(
config_data: dict[str, Any],
actor: Actor,
) -> str:
"""Invoke actor add with a temp config file and return the output."""
runner = CliRunner()
with tempfile.NamedTemporaryFile(
delete=False, suffix=".json", mode="w", encoding="utf-8"
) as handle:
json.dump(config_data, handle)
handle.flush()
config_path = Path(handle.name)
try:
with patch("cleveragents.cli.commands.actor._get_services") as mock_svc:
registry = MagicMock()
registry.upsert_actor.return_value = actor
mock_svc.return_value = (MagicMock(), registry)
result = runner.invoke(
actor_app,
["add", "--config", str(config_path)],
)
finally:
config_path.unlink(missing_ok=True)
assert result.exit_code == 0, (
f"actor add failed (exit_code={result.exit_code}):\n{result.output}"
)
return result.output
def test_add_type_field() -> None:
"""Verify actor add rich output includes the Type field."""
config_data: dict[str, Any] = {
"name": "local/robot-add-actor",
"provider": "openai",
"model": "gpt-4",
"type": "graph",
}
actor = _make_actor(config=config_data)
output = _run_actor_add(config_data, actor)
assert "Type:" in output, f"Expected 'Type:' in output:\n{output}"
assert "graph" in output, f"Expected type value 'graph' in output:\n{output}"
print("actor-add-type-field-ok")
def test_add_config_panel() -> None:
"""Verify actor add rich output includes the Config panel with all fields."""
config_data: dict[str, Any] = {
"name": "local/robot-add-actor",
"provider": "openai",
"model": "gpt-4",
"type": "graph",
"options": {"temperature": 0.5, "max_tokens": 256},
}
graph_descriptor = {
"nodes": [{"id": "n1"}, {"id": "n2"}],
"edges": [{"from": "n1", "to": "n2"}],
}
actor = _make_actor(config=config_data, graph_descriptor=graph_descriptor)
output = _run_actor_add(config_data, actor)
for field in ("Config", "Path:", "Hash:", "Options:", "Nodes:", "Edges:"):
assert field in output, f"Expected '{field}' in output:\n{output}"
# Verify counts
assert "2" in output, f"Expected node count '2' in output:\n{output}"
assert "1" in output, f"Expected edge count '1' in output:\n{output}"
print("actor-add-config-panel-ok")
def test_add_capabilities_panel() -> None:
"""Verify actor add rich output includes the Capabilities panel."""
config_data: dict[str, Any] = {
"name": "local/robot-add-actor",
"provider": "openai",
"model": "gpt-4",
"type": "llm",
"capabilities": ["code review", "diff summarization", "lint guidance"],
}
actor = _make_actor(config=config_data)
output = _run_actor_add(config_data, actor)
assert "Capabilities" in output, (
f"Expected 'Capabilities' panel in output:\n{output}"
)
for cap in config_data["capabilities"]:
assert cap in output, f"Expected capability '{cap}' in output:\n{output}"
print("actor-add-capabilities-panel-ok")
def test_add_tools_panel() -> None:
"""Verify actor add rich output includes the Tools panel."""
config_data: dict[str, Any] = {
"name": "local/robot-add-actor",
"provider": "openai",
"model": "gpt-4",
"type": "llm",
"tools": [
{"name": "read_file", "read_only": True, "safe": True},
{"name": "search_files", "read_only": True, "safe": True},
{"name": "git_diff", "read_only": True, "safe": True},
],
}
actor = _make_actor(config=config_data)
output = _run_actor_add(config_data, actor)
assert "Tools" in output, f"Expected 'Tools' panel in output:\n{output}"
assert "Tool" in output, f"Expected 'Tool' column header in output:\n{output}"
assert "Read-Only" in output, f"Expected 'Read-Only' column in output:\n{output}"
assert "Safe" in output, f"Expected 'Safe' column in output:\n{output}"
for tool in config_data["tools"]:
assert tool["name"] in output, (
f"Expected tool '{tool['name']}' in output:\n{output}"
)
print("actor-add-tools-panel-ok")
def test_add_success_line() -> None:
"""Verify actor add rich output ends with the success status line."""
config_data: dict[str, Any] = {
"name": "local/robot-add-actor",
"provider": "openai",
"model": "gpt-4",
"type": "graph",
}
actor = _make_actor(config=config_data)
output = _run_actor_add(config_data, actor)
assert "✓ OK" in output, f"Expected '✓ OK' in output:\n{output}"
assert "Actor added" in output, f"Expected 'Actor added' in output:\n{output}"
print("actor-add-success-line-ok")
def main() -> None:
command = sys.argv[1] if len(sys.argv) > 1 else "add-type-field"
dispatch = {
"add-type-field": test_add_type_field,
"add-config-panel": test_add_config_panel,
"add-capabilities-panel": test_add_capabilities_panel,
"add-tools-panel": test_add_tools_panel,
"add-success-line": test_add_success_line,
}
fn = dispatch.get(command)
if fn is None:
print(f"Unknown command: {command}", file=sys.stderr)
sys.exit(1)
fn()
if __name__ == "__main__":
main()