Files
cleveragents-core/benchmarks/actor_hierarchy_bench.py
aditya e476d2de0e feat(actor): extend hierarchical actor YAML schema and loader
Extend actor YAML schema to support hierarchical graphs with explicit
node types (agent, tool, conditional, subgraph), per-node LSP bindings
(lsp_binding with server, languages, auto, capabilities), and tool-source
references (skills, mcp_servers, agent_skills).

Add schema validation for namespaced actor references, duplicate node IDs,
edge target existence, and graph reachability — all nodes must be reachable
from entry_node via explicit edges or conditional node routing targets.

Update loader to report YAML parse errors with precise line/column positions
and schema validation errors with dotted field paths and remediation hints
pointing to docs/reference/actor_config.md.

Add docs/reference/actor_config.md as the practical configuration reference
covering hierarchical graph examples, node type table, topology rules, and
common error cases with fix guidance.

Refresh examples/actors/graph_workflow.yaml to replace deprecated actor_path
with actor_ref. Add benchmarks/actor_yaml_bench.py for schema load overhead.

Tests: 95 Behave scenarios, 10 Robot smoke tests (including hierarchical
loader smoke test), security scan clean, coverage 99% (threshold 97%).

ISSUES CLOSED: #157
2026-02-25 10:30:42 +00:00

143 lines
3.6 KiB
Python

"""ASV benchmarks for hierarchical actor YAML parsing performance.
Measures the performance of:
- Parsing actors with per-node LSP bindings
- Parsing actors with tool-source references
- Parsing actors with subgraph actor_ref
- Parsing actors with full hierarchical features combined
"""
from __future__ import annotations
import importlib
import sys
from pathlib import Path
_SRC = str(Path(__file__).resolve().parents[1] / "src")
if _SRC not in sys.path:
sys.path.insert(0, _SRC)
import cleveragents # noqa: E402
importlib.reload(cleveragents)
from cleveragents.actor.schema import ActorConfigSchema # noqa: E402
def _graph_data(nodes: list[dict], *, skills: list[str] | None = None) -> dict:
node_ids = [n["id"] for n in nodes]
edges = []
for i in range(len(node_ids) - 1):
edges.append({"from_node": node_ids[i], "to_node": node_ids[i + 1]})
data: dict = {
"name": "bench/hierarchy",
"type": "graph",
"description": "Benchmark hierarchical actor",
"model": "gpt-4",
"route": {
"nodes": nodes,
"edges": edges,
"entry_node": node_ids[0],
"exit_nodes": [node_ids[-1]],
},
}
if skills:
data["skills"] = skills
return data
_AGENT_NODE = {
"id": "agent",
"type": "agent",
"name": "Agent",
"description": "Agent node",
"config": {"model": "gpt-4"},
}
_LSP_NODE = {
"id": "lsp_agent",
"type": "agent",
"name": "LSP Agent",
"description": "Agent with LSP binding",
"config": {"model": "gpt-4"},
"lsp_binding": {
"server": "local/pyright",
"languages": ["python"],
"capabilities": ["diagnostics", "hover"],
},
}
_TS_NODE = {
"id": "ts_agent",
"type": "agent",
"name": "Tool Source Agent",
"description": "Agent with tool sources",
"config": {},
"tool_sources": [
{"type": "skill", "name": "local/file-ops"},
{"type": "mcp", "name": "local/filesystem"},
{"type": "builtin", "group": "git_operations"},
],
}
_SUBGRAPH_NODE = {
"id": "sub",
"type": "subgraph",
"name": "Sub",
"description": "Subgraph node",
"actor_ref": "local/code-reviewer",
}
class TimeLspBindingParse:
"""Benchmark parsing actors with LSP bindings."""
def setup(self) -> None:
self.data = _graph_data([_LSP_NODE])
def time_parse_lsp_binding(self) -> None:
ActorConfigSchema.model_validate(self.data)
class TimeToolSourcesParse:
"""Benchmark parsing actors with tool-source references."""
def setup(self) -> None:
self.data = _graph_data([_TS_NODE])
def time_parse_tool_sources(self) -> None:
ActorConfigSchema.model_validate(self.data)
class TimeSubgraphRefParse:
"""Benchmark parsing actors with subgraph actor_ref."""
def setup(self) -> None:
self.data = _graph_data(
[_AGENT_NODE, _SUBGRAPH_NODE],
)
def time_parse_subgraph_ref(self) -> None:
ActorConfigSchema.model_validate(self.data)
class TimeFullHierarchyParse:
"""Benchmark parsing a full hierarchical actor with all features."""
def setup(self) -> None:
self.data = _graph_data(
[_LSP_NODE, _TS_NODE, _SUBGRAPH_NODE],
skills=["local/file-ops", "local/git-ops"],
)
self.data["lsp"] = ["local/pyright"]
self.data["lsp_capabilities"] = ["diagnostics"]
def time_parse_full_hierarchy(self) -> None:
ActorConfigSchema.model_validate(self.data)
time_lsp = TimeLspBindingParse()
time_ts = TimeToolSourcesParse()
time_sg = TimeSubgraphRefParse()
time_full = TimeFullHierarchyParse()