feat(validate_dict): expose public validate_dict(config_dict, platform_limits) API #18
Merged
hurui200320
merged 1 commits from 2026-06-05 10:19:26 +00:00
feature/validate-dict-api into master
1 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
149feae15f |
feat(validate_dict): expose public validate_dict(config_dict, platform_limits) API
CI / quality (pull_request) Successful in 41s
CI / lint (pull_request) Successful in 46s
CI / integration_tests (pull_request) Successful in 1m0s
CI / build (pull_request) Successful in 1m2s
CI / typecheck (pull_request) Successful in 1m6s
CI / security (pull_request) Successful in 1m5s
CI / unit_tests (pull_request) Successful in 3m39s
CI / coverage (pull_request) Failing after 3m35s
CI / status-check (pull_request) Failing after 3s
CI / lint (push) Successful in 40s
CI / typecheck (push) Successful in 1m4s
CI / security (push) Successful in 1m4s
CI / quality (push) Successful in 32s
CI / integration_tests (push) Successful in 51s
CI / build (push) Successful in 45s
CI / unit_tests (push) Successful in 3m38s
CI / coverage (push) Failing after 3m34s
CI / status-check (push) Failing after 3s
Implement validate_dict() in cleveractors/validation.py and export it from
cleveractors/__init__.py as the first router-facing API (ADR-2024, Wave 1).
validate_dict(config_dict, platform_limits) -> dict[str, Any]:
- Validates that both 'agents' and 'routes' top-level keys are present.
- Validates each agent's type against the spec-defined vocabulary:
{llm, tool, composite, chain, template_instance}.
- Validates LLM agent 'provider' values against platform_limits['allowed_providers']
when present, or the default allowlist {openai, anthropic, google} when absent.
- Validates route/edge field names: 'from'/'to' are rejected in favour of
'source'/'target' as required by the spec-conformant format (ADR-2025).
- Validates graph node types against the spec vocabulary:
{start, end, agent, function, tool, conditional, subgraph, message_router}.
- Validates stream operator types against the spec vocabulary (all defined types).
- Enforces structural platform limits from platform_limits when present:
max_graph_depth (DFS longest-path check), max_subgraph_depth (recursive
subgraph-reference depth), max_total_nodes (total nodes across all graph routes).
- Returns config_dict unchanged when all checks pass (identity contract).
- Pure static validator: no file I/O, no env-var reads, no app construction.
Review fixes (Cycle 1):
- M3: Added isinstance(node_name, str) guard for graph node keys in
_validate_graph_route, with BDD scenario for non-string node key.
- m1: Added # pragma: no branch to _count_total_nodes defensive guard in
_limits.py (matching the identical guards in _subgraph_children).
- m2: Added # pragma: no branch to dead defensive continue branches in
_validate_graph_route edge validation loop.
- m3: Added dedicated BDD scenario and step for dangling source node
(src not in nodes_raw branch), plus renamed existing scenario from
"source node" to "target node" for accuracy.
- m4: Deleted 4 lines of commented-out InlineYAMLJinja setup code in
features/environment.py.
- n1: Added depth <= 0 ValueError guard in build_subgraph_chain helper.
- n2: Renamed scenario "Edge referencing a non-existent source node" to
"Edge referencing a non-existent target node" (the step tested a target).
- n3: Changed > to >= in both _MAX_DFS_STEPS and _MAX_SUBGRAPH_STEPS guards
to prevent off-by-one allowing one extra iteration beyond documented ceiling.
- n4: Added adjacency target deduplication in _compute_graph_depth to avoid
wasting DFS step budget on parallel edges.
- Spec review: M1 (entry_point optional) and M2 (start/end implicit injection)
were verified against the authoritative spec at docs/specification.md in the
cleveragents-webapp repo (develop branch). The spec does NOT support either
claim; entry_point is required and start/end must be explicitly declared.
No changes needed for M1/M2.
Review fixes (Cycle 2):
- M1: Deduplicated _subgraph_children yields with a seen set to prevent
duplicate subgraph references from consuming DFS steps redundantly and
causing false "too complex" errors.
- M2: Changed adjacency value type from list[str] to set[str] in
_compute_graph_depth for O(1) deduplication, reducing worst case
from O(N^3) to O(N^2) for dense graphs.
- m1: Added BDD scenario for LLM agent without provider field failing
when default "openai" is not in custom allowlist.
- m2: Added positive BDD scenarios for valid node types start, tool,
conditional, message_router, function.
- m3: Added positive BDD scenarios for additional stream operator types
filter, transform, reduce.
- m5: Added depth_cache memoization to _compute_subgraph_depth to avoid
recomputing depths across graph routes sharing subgraph trees.
- m6: Moved cleveractors.validation._limits import from module level into
after_scenario, guarded by hasattr.
- n1: Split validate_dict_routes_steps.py (was 473 lines) into
validate_dict_routes_steps.py (generic route steps) and
validate_dict_graph_route_steps.py (graph-specific steps).
- n2: Updated _MAX_DFS_STEPS module comment to accurately state O(N+E)
complexity and that dense graphs can exhaust the ceiling quickly.
BDD Behave scenarios cover all requirements.
Robot Framework integration tests (robot/validate_dict.robot) cover API usage.
ISSUES CLOSED: #10
|