"""Robot Framework helper for actor YAML examples validation. Provides CLI-style interface for Robot to validate actor examples, check patterns, and verify documentation completeness. Usage: python robot/helper_actor_examples.py validate python robot/helper_actor_examples.py validate-invalid python robot/helper_actor_examples.py count-examples python robot/helper_actor_examples.py check-pattern python robot/helper_actor_examples.py check-docs python robot/helper_actor_examples.py check-doc-patterns python robot/helper_actor_examples.py validate-all """ from __future__ import annotations import sys from pathlib import Path # Ensure the src directory is on the import path. _SRC = str(Path(__file__).resolve().parents[1] / "src") if _SRC not in sys.path: sys.path.insert(0, _SRC) from cleveragents.actor.loader import ActorLoader # noqa: E402 from cleveragents.actor.schema import ActorConfigSchema, ActorType # noqa: E402 def validate_actor(yaml_path: str) -> int: """Validate actor YAML and print details.""" try: config = ActorConfigSchema.from_yaml_file(yaml_path) print("actor-ok") print(f"name:{config.name}") print(f"type:{config.type.value}") if config.tools: print("has-tools") if config.skills: print("has-skills") if config.route: print("has-route") if config.env_vars: print("has-env-vars") if config.memory: print("has-memory") if config.context_view: print(f"context-view:{config.context_view.value}") return 0 except Exception as exc: print(f"actor-fail: {exc}") return 1 def validate_invalid_actor(yaml_path: str) -> int: """Expect validation to fail.""" try: ActorConfigSchema.from_yaml_file(yaml_path) print("unexpected-success") return 1 except Exception as exc: print("expected-fail") error_msg = str(exc).lower() if "namespace" in error_msg or "name" in error_msg: print("namespace") if "model" in error_msg: print("model") if "tool" in error_msg: print("tool") if "route" in error_msg: print("route") return 0 def count_examples(directory: str) -> int: """Count YAML example files.""" try: examples_dir = Path(directory) yaml_files = list(examples_dir.glob("*.yaml")) print(f"example-count:{len(yaml_files)}") return 0 except Exception as exc: print(f"count-fail: {exc}") return 1 def check_pattern(yaml_path: str, pattern: str) -> int: """Check if actor follows a specific pattern.""" try: config = ActorConfigSchema.from_yaml_file(yaml_path) if pattern == "strategist": # Strategist pattern: large context, read-only tools, memory enabled if config.memory and config.memory.enabled: print("has-memory") if config.context_view: print(f"context-view:{config.context_view.value}") print(f"pattern-ok:{pattern}") elif pattern == "tool-only": # Tool-only pattern: no model, has tools if config.type == ActorType.TOOL: print("pattern-ok:tool-only") if not config.model: print("no-model") elif pattern == "graph": # Graph pattern: has route with entry/exit nodes if config.route: print("pattern-ok:graph") if config.route.entry_node: print("has-entry-node") if config.route.exit_nodes: print("has-exit-nodes") elif pattern == "hierarchical": # Hierarchical pattern: graph with subgraph nodes if config.route: print("pattern-ok:hierarchical") # Check for subgraph nodes from cleveragents.actor.schema import NodeType subgraph_nodes = [ node for node in config.route.nodes if node.type == NodeType.SUBGRAPH ] if subgraph_nodes: print("has-subgraph") return 0 except Exception as exc: print(f"pattern-check-fail: {exc}") return 1 def check_docs(doc_file: str) -> int: """Check if documentation references all example files.""" try: doc_path = Path(doc_file) doc_content = doc_path.read_text() example_files = [ "simple_llm.yaml", "llm_with_tools.yaml", "estimator.yaml", "tool_collection.yaml", "simple_graph.yaml", "graph_workflow.yaml", ] for filename in example_files: if filename in doc_content: print(f"doc-references:{filename}") return 0 except Exception as exc: print(f"doc-check-fail: {exc}") return 1 def check_doc_patterns(doc_file: str) -> int: """Check if documentation includes all required patterns.""" try: doc_path = Path(doc_file) doc_content = doc_path.read_text().lower() patterns = [ "strategist", "executor", "reviewer", "tool-only", "validation", "hierarchical", ] for pattern in patterns: if pattern in doc_content: print(f"has-pattern:{pattern}") return 0 except Exception as exc: print(f"doc-pattern-check-fail: {exc}") return 1 def validate_all_examples(directory: str) -> int: """Validate all actor examples in a directory via ActorLoader business logic. Uses ActorLoader (which applies ActorConfigSchema validation) to discover and validate every YAML file in the given directory. Prints one ``actor-imported:`` line per successfully loaded actor and a final ``all-examples-ok:`` summary line. Returns 1 on any failure. """ try: examples_dir = Path(directory) loader = ActorLoader(search_roots=[examples_dir]) actors = loader.discover() for actor in actors: print(f"actor-imported:{actor.name}") print(f"all-examples-ok:{len(actors)}") return 0 except Exception as exc: print(f"validate-all-fail: {exc}") return 1 def main() -> int: """Entry point called by Robot Framework ``Run Process``.""" if len(sys.argv) < 2: print( "Usage: helper_actor_examples.py [args...]\n" "Commands:\n" " validate \n" " validate-invalid \n" " count-examples \n" " check-pattern \n" " check-docs \n" " check-doc-patterns \n" " validate-all " ) return 1 command = sys.argv[1] if command == "validate": if len(sys.argv) < 3: print("Usage: validate ") return 1 return validate_actor(sys.argv[2]) if command == "validate-invalid": if len(sys.argv) < 3: print("Usage: validate-invalid ") return 1 return validate_invalid_actor(sys.argv[2]) if command == "count-examples": if len(sys.argv) < 3: print("Usage: count-examples ") return 1 return count_examples(sys.argv[2]) if command == "check-pattern": if len(sys.argv) < 4: print("Usage: check-pattern ") return 1 return check_pattern(sys.argv[2], sys.argv[3]) if command == "check-docs": if len(sys.argv) < 3: print("Usage: check-docs ") return 1 return check_docs(sys.argv[2]) if command == "check-doc-patterns": if len(sys.argv) < 3: print("Usage: check-doc-patterns ") return 1 return check_doc_patterns(sys.argv[2]) if command == "validate-all": if len(sys.argv) < 3: print("Usage: validate-all ") return 1 return validate_all_examples(sys.argv[2]) print(f"Unknown command: {command}") return 1 if __name__ == "__main__": sys.exit(main())