8953449dc2
CI / lint (pull_request) Successful in 30s
CI / typecheck (pull_request) Successful in 1m15s
CI / security (pull_request) Successful in 1m18s
CI / quality (pull_request) Successful in 30s
CI / build (pull_request) Successful in 37s
CI / helm (pull_request) Successful in 29s
CI / push-validation (pull_request) Successful in 26s
CI / e2e_tests (pull_request) Successful in 3m58s
CI / integration_tests (pull_request) Successful in 6m49s
CI / unit_tests (pull_request) Successful in 8m39s
CI / docker (pull_request) Successful in 1m23s
CI / coverage (pull_request) Successful in 13m32s
CI / benchmark-regression (push) Failing after 0s
CI / benchmark-publish (push) Failing after 0s
CI / status-check (pull_request) Successful in 1s
CI / lint (push) Successful in 25s
CI / typecheck (push) Successful in 1m1s
CI / quality (push) Successful in 55s
CI / security (push) Successful in 1m9s
CI / build (push) Successful in 24s
CI / helm (push) Successful in 30s
CI / push-validation (push) Successful in 20s
CI / e2e_tests (push) Successful in 5m13s
CI / integration_tests (push) Successful in 7m17s
CI / unit_tests (push) Successful in 8m50s
CI / docker (push) Successful in 1m30s
CI / coverage (push) Successful in 11m57s
CI / status-check (push) Successful in 2s
- schema.py: provider field changed to Optional[str] with model validator
validate_provider_required_for_llm_graph() that requires it only for LLM
and GRAPH actor types; TOOL actors do not require provider
- schema.py: is_v3_yaml() uses version_str == "3" or version_str.startswith("3.")
to avoid false positives from "30" or "300" version strings
- schema.py: tool namespace validation uses strict 2-part split to reject
empty namespace or empty name (e.g. "/tool", "ns/", "a/b/c")
- cli/commands/actor.py: schema_version extraction uses raw_version pattern
(no # type: ignore[assignment]) for clean static typing
- actor/__init__.py: is_v3_yaml removed from __all__ and _LAZY_IMPORTS
since it is a module-private helper, not a public API
- robot/actor_add_v3_schema_validation.robot: YAML fixtures for 'Reject v3
LLM Actor Without Model Field' and 'Reject v3 TOOL Actor Without Tools
Field' now include required provider field (and model for TOOL fixture)
- robot/helper_actor_add_v3_schema_validation.py: except clauses unified to
catch (subprocess.TimeoutExpired, FileNotFoundError) in both add_actor()
and update_actor() functions
- features/actor_add_v3_schema_validation.feature: 'Update a v3 actor with
valid YAML succeeds' scenario now includes 'And the actor should be
validated via ActorConfigSchema'; error assertions tightened to exact
messages (e.g. "Input should be 'llm', 'tool' or 'graph'", "Node ID must
be alphanumeric", "must be namespaced")
- features/steps/actor_add_v3_schema_validation_steps.py: step_run_actor_update
now spies on ActorConfigSchema.model_validate; failure paths assert
isinstance(result.exception, SystemExit)
ISSUES CLOSED: #5869
118 lines
3.6 KiB
Python
118 lines
3.6 KiB
Python
"""Robot Framework helper for actor loader smoke tests.
|
|
|
|
Provides a CLI-style interface for Robot to invoke actor discovery
|
|
and loading. Exit code 0 = success, 1 = failure.
|
|
|
|
Usage:
|
|
python robot/helper_actor_loading.py discover <directory>
|
|
python robot/helper_actor_loading.py discover-examples
|
|
python robot/helper_actor_loading.py discover-hierarchical
|
|
python robot/helper_actor_loading.py discover-invalid <directory>
|
|
python robot/helper_actor_loading.py namespace-default <directory>
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import sys
|
|
import tempfile
|
|
from pathlib 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
|
|
|
|
|
|
def main() -> int:
|
|
if len(sys.argv) < 2:
|
|
print("Usage: helper_actor_loading.py <command> [args...]")
|
|
return 1
|
|
|
|
command = sys.argv[1]
|
|
|
|
if command == "discover":
|
|
directory = sys.argv[2]
|
|
return _discover(Path(directory))
|
|
|
|
if command == "discover-examples":
|
|
project_root = Path(__file__).resolve().parents[1]
|
|
return _discover(project_root / "examples" / "actors")
|
|
|
|
if command == "discover-hierarchical":
|
|
project_root = Path(__file__).resolve().parents[1]
|
|
return _discover_hierarchical(project_root / "examples" / "actors")
|
|
|
|
if command == "discover-invalid":
|
|
directory = sys.argv[2]
|
|
return _discover_invalid(Path(directory))
|
|
|
|
if command == "namespace-default":
|
|
return _namespace_default()
|
|
|
|
print(f"Unknown command: {command}")
|
|
return 1
|
|
|
|
|
|
def _discover(directory: Path) -> int:
|
|
loader = ActorLoader(search_roots=[directory])
|
|
actors = loader.discover()
|
|
for actor in actors:
|
|
print(f"actor-loaded: {actor.name} type={actor.type.value}")
|
|
print(f"actor-count: {len(actors)}")
|
|
return 0
|
|
|
|
|
|
def _discover_hierarchical(examples_dir: Path) -> int:
|
|
"""Load the hierarchical workflow example via ActorLoader and print key stats."""
|
|
loader = ActorLoader(search_roots=[examples_dir])
|
|
actors = loader.discover()
|
|
target = next((a for a in actors if a.name == "workflows/dev-workflow"), None)
|
|
if target is None:
|
|
print("hierarchical-actor-not-found")
|
|
return 1
|
|
print(f"actor-loaded: {target.name}")
|
|
print(f"actor-type: {target.type.value}")
|
|
route = target.route
|
|
if route is not None:
|
|
print(f"node-count: {len(route.nodes)}")
|
|
print(f"edge-count: {len(route.edges)}")
|
|
lsp_configured = bool(getattr(target, "lsp", None))
|
|
print(f"lsp-configured: {lsp_configured}")
|
|
return 0
|
|
|
|
|
|
def _discover_invalid(directory: Path) -> int:
|
|
loader = ActorLoader(search_roots=[directory])
|
|
try:
|
|
loader.discover()
|
|
print("actor-loading-unexpected-success")
|
|
return 1
|
|
except Exception as exc:
|
|
print(f"actor-loading-expected-fail: {exc}")
|
|
return 0
|
|
|
|
|
|
def _namespace_default() -> int:
|
|
tmp = Path(tempfile.mkdtemp(prefix="actor_ns_"))
|
|
yaml_content = (
|
|
"name: bare-name\n"
|
|
"type: llm\n"
|
|
"description: Test namespace defaulting\n"
|
|
'version: "1.0"\n'
|
|
"provider: openai\n"
|
|
"model: gpt-4\n"
|
|
)
|
|
(tmp / "bare.yaml").write_text(yaml_content)
|
|
loader = ActorLoader(search_roots=[tmp])
|
|
actors = loader.discover()
|
|
if len(actors) == 1 and actors[0].name == "local/bare-name":
|
|
print("namespace-default-ok: local/bare-name")
|
|
return 0
|
|
print(f"namespace-default-fail: {[a.name for a in actors]}")
|
|
return 1
|
|
|
|
|
|
if __name__ == "__main__":
|
|
sys.exit(main())
|