-
Notifications
You must be signed in to change notification settings - Fork 45
[ONGOING, DO NOT MERGE] Fixing issues in verify_graph task #290
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -1,3 +1,5 @@ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import asyncio | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||
| from app.models.db.graph_template_model import GraphTemplate, NodeTemplate | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| from app.models.graph_template_validation_status import GraphTemplateValidationStatus | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| from app.models.db.registered_node import RegisteredNode | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -7,26 +9,33 @@ | |||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||
| logger = LogsManager().get_logger() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||
| async def verify_nodes_names(nodes: list[NodeTemplate], errors: list[str]): | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| async def verify_nodes_names(nodes: list[NodeTemplate]) -> list[str]: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| errors = [] | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| for node in nodes: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if node.node_name is None or node.node_name == "": | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| errors.append(f"Node {node.identifier} has no name") | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| errors.append(f"Node {node.identifier} has no name") | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return errors | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+12
to
+17
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 💡 Verification agent 🧩 Analysis chainAdd backward-compatible optional Tests are still calling the old signature with -async def verify_nodes_names(nodes: list[NodeTemplate]) -> list[str]:
- errors = []
+async def verify_nodes_names(nodes: list[NodeTemplate], errors: list[str] | None = None) -> list[str]:
+ errs: list[str] = []
for node in nodes:
- if node.node_name is None or node.node_name == "":
- errors.append(f"Node {node.identifier} has no name")
- return errors
+ if not node.node_name:
+ errs.append(f"Node {node.identifier} has no name")
+ if errors is not None:
+ errors.extend(errs)
+ return errsRun this to confirm all call-sites now work with the optional param (both styles supported): 🏁 Script executed: #!/bin/bash
# Find invocations providing an 'errors' arg (legacy signature)
rg -nP '\bverify_nodes_names\s*\(\s*[^,]+,\s*[^)]+\)'Length of output: 364 Backward Compatibility for The tests in
These calls rely on two behaviors of the original signature:
The proposed diff instead:
This breaks callers expecting: errors = []
result = await verify_nodes_names(nodes, errors)
# result is errors, and errors has been mutatedWith the new code, To restore full backward compatibility:
Suggested replacement: - async def verify_nodes_names(nodes: list[NodeTemplate], errors: list[str] | None = None) -> list[str]:
- errs: list[str] = []
- for node in nodes:
- if not node.node_name:
- errs.append(f"Node {node.identifier} has no name")
- if errors is not None:
- errors.extend(errs)
- return errs
+ async def verify_nodes_names(
+ nodes: list[NodeTemplate],
+ errors: list[str] | None = None,
+ ) -> list[str]:
+ # Use provided list for backward compatibility, or create one if omitted
+ error_list = errors if errors is not None else []
+
+ for node in nodes:
+ if not node.node_name:
+ error_list.append(f"Node {node.identifier} has no name")
+
+ return error_listThis ensures:
Please update the implementation accordingly to fully satisfy existing tests. 🤖 Prompt for AI Agents💡 Verification agent 🧩 Analysis chainResolve pipeline signature mismatches with a temporary compatibility shim Given the CI failures, the optional Run this to find and update tests calling the legacy signatures: I can submit a follow-up patch to update the tests to the new return-based API and then remove the shims. Also applies to: 19-25, 26-36, 37-67, 68-88 🏁 Script executed: #!/bin/bash
# Search legacy-style invocations that pass an explicit 'errors' list
rg -nP '\b(verify_nodes_names|verify_nodes_namespace|verify_node_exists|verify_node_identifiers|verify_secrets)\s*\(\s*[^,]+,\s*[^)]+\)' -C2Length of output: 9126 Add optional The unit tests in state-manager/tests/unit/tasks/test_verify_graph.py still call the legacy signatures—passing in an explicit • Functions to update (signatures currently lack an
• Tests invoking legacy style (examples):
• Example diff for --- a/state-manager/app/tasks/verify_graph.py
+++ b/state-manager/app/tasks/verify_graph.py
@@ -12,7 +12,10 @@
async def verify_nodes_names(nodes: list[NodeTemplate]) -> list[str]:
- errors = []
+async def verify_nodes_names(
+ nodes: list[NodeTemplate],
+ errors: list[str] | None = None, # DEPRECATED: remove after tests migrate
+) -> list[str]:
+ if errors is None:
+ errors = []
for node in nodes:
if node.node_name is None or node.node_name == "":
errors.append(f"Node {node.identifier} has no name")
return errorsApply the same pattern (optional
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||
| async def verify_nodes_namespace(nodes: list[NodeTemplate], graph_namespace: str, errors: list[str]): | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| async def verify_nodes_namespace(nodes: list[NodeTemplate], graph_namespace: str) -> list[str]: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| errors = [] | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| for node in nodes: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if node.namespace != graph_namespace and node.namespace != "exospherehost": | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| errors.append(f"Node {node.identifier} has invalid namespace '{node.namespace}'. Must match graph namespace '{graph_namespace}' or use universal namespace 'exospherehost'") | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return errors | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+19
to
25
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧹 Nitpick (assertive) Namespace check: add optional Mirror the compatibility shim and keep return-based semantics. -async def verify_nodes_namespace(nodes: list[NodeTemplate], graph_namespace: str) -> list[str]:
- errors = []
+async def verify_nodes_namespace(nodes: list[NodeTemplate], graph_namespace: str, errors: list[str] | None = None) -> list[str]:
+ errs: list[str] = []
for node in nodes:
if node.namespace != graph_namespace and node.namespace != "exospherehost":
- errors.append(f"Node {node.identifier} has invalid namespace '{node.namespace}'. Must match graph namespace '{graph_namespace}' or use universal namespace 'exospherehost'")
- return errors
+ errs.append(
+ f"Node {node.identifier} has invalid namespace '{node.namespace}'. "
+ f"Must match graph namespace '{graph_namespace}' or use universal namespace 'exospherehost'"
+ )
+ if errors is not None:
+ errors.extend(errs)
+ return errsOptional clean-up (outside this hunk): define a constant to avoid string duplication. # near the imports
UNIVERSAL_NAMESPACE = "exospherehost"🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||||||||||||||||||
| async def verify_node_exists(nodes: list[NodeTemplate], database_nodes: list[RegisteredNode], errors: list[str]): | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| async def verify_node_exists(nodes: list[NodeTemplate], database_nodes: list[RegisteredNode]) -> list[str]: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| errors = [] | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| template_nodes_set = set([(node.node_name, node.namespace) for node in nodes]) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| database_nodes_set = set([(node.name, node.namespace) for node in database_nodes]) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||
| nodes_not_found = template_nodes_set - database_nodes_set | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||||||||||||||||||||||||||||||||||||||||||||||||||||||
| for node in nodes_not_found: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| errors.append(f"Node {node[0]} in namespace {node[1]} does not exist.") | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return errors | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+26
to
36
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Existence check: add optional Keep the new return style while supporting old callers. -async def verify_node_exists(nodes: list[NodeTemplate], database_nodes: list[RegisteredNode]) -> list[str]:
- errors = []
+async def verify_node_exists(nodes: list[NodeTemplate], database_nodes: list[RegisteredNode], errors: list[str] | None = None) -> list[str]:
+ errs: list[str] = []
@@
- for node in nodes_not_found:
- errors.append(f"Node {node[0]} in namespace {node[1]} does not exist.")
- return errors
+ for node in nodes_not_found:
+ errs.append(f"Node {node[0]} in namespace {node[1]} does not exist.")
+ if errors is not None:
+ errors.extend(errs)
+ return errs📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||||||||||||||||||
| async def verify_node_identifiers(nodes: list[NodeTemplate], errors: list[str]): | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| async def verify_node_identifiers(nodes: list[NodeTemplate]) -> list[str]: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| errors = [] | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| identifier_to_nodes = {} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||
| # First pass: collect all nodes by identifier | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -54,7 +63,10 @@ async def verify_node_identifiers(nodes: list[NodeTemplate], errors: list[str]): | |||||||||||||||||||||||||||||||||||||||||||||||||||||
| if next_node not in valid_identifiers: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| errors.append(f"Node {node.node_name} in namespace {node.namespace} has a next node {next_node} that does not exist in the graph") | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||
| async def verify_secrets(graph_template: GraphTemplate, database_nodes: list[RegisteredNode], errors: list[str]): | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return errors | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||
| async def verify_secrets(graph_template: GraphTemplate, database_nodes: list[RegisteredNode]) -> list[str]: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| errors = [] | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| required_secrets_set = set() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||
| for node in database_nodes: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -71,9 +83,10 @@ async def verify_secrets(graph_template: GraphTemplate, database_nodes: list[Reg | |||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||
| for secret_name in missing_secrets_set: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| errors.append(f"Secret {secret_name} is required but not present in the graph template") | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return errors | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||
| async def get_database_nodes(nodes: list[NodeTemplate], graph_namespace: str): | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| async def get_database_nodes(nodes: list[NodeTemplate], graph_namespace: str) -> list[RegisteredNode]: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| graph_namespace_node_names = [ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| node.node_name for node in nodes if node.namespace == graph_namespace | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ] | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -91,7 +104,8 @@ async def get_database_nodes(nodes: list[NodeTemplate], graph_namespace: str): | |||||||||||||||||||||||||||||||||||||||||||||||||||||
| return graph_namespace_database_nodes + exospherehost_database_nodes | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||
| async def verify_inputs(graph_nodes: list[NodeTemplate], database_nodes: list[RegisteredNode], dependency_graph: dict[str, list[str]], errors: list[str]): | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| async def verify_inputs(graph_nodes: list[NodeTemplate], database_nodes: list[RegisteredNode], dependency_graph: dict[str, list[str]]) -> list[str]: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| errors = [] | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| look_up_table = {} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| for node in graph_nodes: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| look_up_table[node.identifier] = {"graph_node": node} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -146,8 +160,10 @@ async def verify_inputs(graph_nodes: list[NodeTemplate], database_nodes: list[Re | |||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||
| except Exception as e: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| errors.append(f"Error creating input model for node {node.identifier}: {str(e)}") | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return errors | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||
| async def build_dependencies_graph(graph_nodes: list[NodeTemplate]): | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| async def build_dependencies_graph(graph_nodes: list[NodeTemplate]) -> dict[str, set[str]]: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| dependency_graph = {} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| for node in graph_nodes: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| dependency_graph[node.identifier] = set() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -230,21 +246,25 @@ async def verify_unites(graph_nodes: list[NodeTemplate], dependency_graph: dict | |||||||||||||||||||||||||||||||||||||||||||||||||||||
| if node.unites.identifier not in dependency_graph[node.identifier]: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| errors.append(f"Node {node.identifier} depends on {node.unites.identifier} which is not a dependency of {node.identifier}") | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||
| async def verify_graph(graph_template: GraphTemplate): | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| try: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| errors = [] | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| database_nodes = await get_database_nodes(graph_template.nodes, graph_template.namespace) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||
| await verify_nodes_names(graph_template.nodes, errors) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| await verify_nodes_namespace(graph_template.nodes, graph_template.namespace, errors) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| await verify_node_exists(graph_template.nodes, database_nodes, errors) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| await verify_node_identifiers(graph_template.nodes, errors) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| await verify_secrets(graph_template, database_nodes, errors) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| basic_verify_tasks = [ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| verify_nodes_names(graph_template.nodes), | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| verify_nodes_namespace(graph_template.nodes, graph_template.namespace), | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| verify_node_exists(graph_template.nodes, database_nodes), | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| verify_node_identifiers(graph_template.nodes), | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| verify_secrets(graph_template, database_nodes) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ] | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| errors.extend(await asyncio.gather(*basic_verify_tasks)) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The result of You should flatten the list of lists into a single list of error strings.
Suggested change
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+254
to
+262
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Flatten asyncio.gather results instead of extending with a list of lists
Apply this diff: - errors.extend(await asyncio.gather(*basic_verify_tasks))
+ basic_results = await asyncio.gather(*basic_verify_tasks, return_exceptions=False)
+ for errs in basic_results:
+ if errs:
+ errors.extend(errs)📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||||||||||||||||||
| dependency_graph = await verify_topology(graph_template.nodes, errors) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if dependency_graph is not None and len(errors) == 0: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| await verify_inputs(graph_template.nodes, database_nodes, dependency_graph, errors) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| inputs_errors = await verify_inputs(graph_template.nodes, database_nodes, dependency_graph) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| errors.extend(inputs_errors) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||
| await verify_unites(graph_template.nodes, dependency_graph, errors) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
263
to
269
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The refactoring to make verification functions pure is incomplete. The For consistency and to fully realize the benefits of this refactoring, these functions should also be updated to return a list of errors instead of modifying the list in-place. |
||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This line has trailing whitespace, which should be removed to adhere to the PEP 8 style guide.