From 8a5ff392bbc1c08b968bff08150bd5cade10c036 Mon Sep 17 00:00:00 2001 From: Henry Bae <69275685+BaeHenryS@users.noreply.github.com> Date: Sun, 17 Aug 2025 18:00:16 -0700 Subject: [PATCH 1/8] Uniform API Endpoint for the node insertion --- fluidize/adapters/local/graph.py | 15 ++++---- fluidize/managers/graph.py | 15 +++++++- pyproject.toml | 2 +- tests/unit/backends/local/test_graph.py | 23 +++++++++--- tests/unit/managers/test_project_graph.py | 45 +++++++++++++++++------ uv.lock | 8 ++-- 6 files changed, 78 insertions(+), 30 deletions(-) diff --git a/fluidize/adapters/local/graph.py b/fluidize/adapters/local/graph.py index 70ea95f..70a4169 100644 --- a/fluidize/adapters/local/graph.py +++ b/fluidize/adapters/local/graph.py @@ -5,7 +5,7 @@ wrapping the core GraphProcessor with adapter-specific functionality. """ -from typing import Optional +from typing import TYPE_CHECKING, Optional from fluidize.core.modules.graph.processor import GraphProcessor from fluidize.core.types.graph import GraphData, GraphEdge, GraphNode @@ -14,6 +14,9 @@ from fluidize.core.types.project import ProjectSummary from fluidize.core.utils.pathfinder.path_finder import PathFinder +if TYPE_CHECKING: + from fluidize.managers.graph import InsertNodeRequest + class GraphHandler: """ @@ -40,20 +43,18 @@ def get_graph(self, project: ProjectSummary) -> GraphData: processor = GraphProcessor(project) return processor.get_graph() - def insert_node(self, project: ProjectSummary, node: GraphNode, sim_global: bool = True) -> GraphNode: + def insert_node(self, request: "InsertNodeRequest") -> GraphNode: """ Insert a new node into the project graph. Args: - project: The project to add the node to - node: The node to insert - sim_global: Whether to use global simulations (placeholder for future) + request: InsertNodeRequest containing node, project, and sim_global Returns: The inserted node """ - processor = GraphProcessor(project) - return processor.insert_node(node, sim_global) + processor = GraphProcessor(request.project) + return processor.insert_node(request.node, request.sim_global) def insert_node_from_scratch( self, diff --git a/fluidize/managers/graph.py b/fluidize/managers/graph.py index 3842d94..964a092 100644 --- a/fluidize/managers/graph.py +++ b/fluidize/managers/graph.py @@ -4,6 +4,8 @@ from typing import TYPE_CHECKING, Any, Optional +from pydantic import BaseModel + from fluidize.core.types.graph import GraphData, GraphEdge, GraphNode if TYPE_CHECKING: @@ -13,6 +15,15 @@ from fluidize.core.types.project import ProjectSummary +# TODO: the ty +class InsertNodeRequest(BaseModel): + """Uniform request structure for inserting nodes in both local and API modes.""" + + node: GraphNode + project: ProjectSummary + sim_global: bool = True + + class GraphManager: """ Graph manager for a specific project. @@ -68,7 +79,9 @@ def add_node(self, node: GraphNode, sim_global: bool = True) -> "NodeManager": Returns: The added node """ - inserted_node = self.adapter.graph.insert_node(self.project, node, sim_global) + # Create uniform request structure for both local and API modes + request = InsertNodeRequest(node=node, project=self.project, sim_global=sim_global) + inserted_node = self.adapter.graph.insert_node(request) return self.get_node(inserted_node.id) def add_node_from_scratch( diff --git a/pyproject.toml b/pyproject.toml index f0844da..3998446 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -20,7 +20,7 @@ classifiers = [ dependencies = [ "asciitree>=0.3.3", "docker>=7.1.0", - "fluidize-sdk>=0.6.0", + "fluidize-sdk>=0.7.0", "jinja2>=3.1.6", "mlflow>=3.1.4", "networkx>=3.2.1", diff --git a/tests/unit/backends/local/test_graph.py b/tests/unit/backends/local/test_graph.py index a61bbab..df0952f 100644 --- a/tests/unit/backends/local/test_graph.py +++ b/tests/unit/backends/local/test_graph.py @@ -7,6 +7,7 @@ from fluidize.adapters.local.graph import GraphHandler from fluidize.core.types.graph import GraphData from fluidize.core.types.parameters import Parameter +from fluidize.managers.graph import InsertNodeRequest from tests.fixtures.sample_graphs import SampleGraphs from tests.fixtures.sample_projects import SampleProjects @@ -77,7 +78,8 @@ def test_insert_node_success(self, graph_handler, mock_processor, sample_project node = SampleGraphs.sample_nodes()[0] mock_processor.insert_node.return_value = node - result = graph_handler.insert_node(sample_project, node, True) + request = InsertNodeRequest(node=node, project=sample_project, sim_global=True) + result = graph_handler.insert_node(request) assert result == node mock_processor.insert_node.assert_called_once_with(node, True) @@ -87,7 +89,8 @@ def test_insert_node_with_sim_global_false(self, graph_handler, mock_processor, node = SampleGraphs.sample_nodes()[1] mock_processor.insert_node.return_value = node - result = graph_handler.insert_node(sample_project, node, False) + request = InsertNodeRequest(node=node, project=sample_project, sim_global=False) + result = graph_handler.insert_node(request) assert result == node mock_processor.insert_node.assert_called_once_with(node, False) @@ -97,7 +100,8 @@ def test_insert_node_default_sim_global(self, graph_handler, mock_processor, sam node = SampleGraphs.sample_nodes()[0] mock_processor.insert_node.return_value = node - result = graph_handler.insert_node(sample_project, node) + request = InsertNodeRequest(node=node, project=sample_project) # sim_global defaults to True + result = graph_handler.insert_node(request) assert result == node mock_processor.insert_node.assert_called_once_with(node, True) # Default is True @@ -160,7 +164,8 @@ def test_processor_error_propagation_insert_node(self, graph_handler, mock_proce mock_processor.insert_node.side_effect = ValueError("Invalid node data") with pytest.raises(ValueError, match="Invalid node data"): - graph_handler.insert_node(sample_project, node) + request = InsertNodeRequest(node=node, project=sample_project) + graph_handler.insert_node(request) def test_processor_error_propagation_delete_node(self, graph_handler, mock_processor, sample_project): """Test that processor errors are propagated for delete_node.""" @@ -196,7 +201,8 @@ def test_processor_creation_per_operation(self, sample_project): # Perform multiple operations handler.get_graph(sample_project) - handler.insert_node(sample_project, SampleGraphs.sample_nodes()[0]) + request = InsertNodeRequest(node=SampleGraphs.sample_nodes()[0], project=sample_project) + handler.insert_node(request) handler.delete_node(sample_project, "test-id") # Verify processor was created for each operation @@ -245,7 +251,8 @@ def test_all_crud_operations_flow(self, sample_project): # Perform full CRUD cycle graph_data = handler.get_graph(sample_project) - inserted_node = handler.insert_node(sample_project, node) + request = InsertNodeRequest(node=node, project=sample_project) + inserted_node = handler.insert_node(request) updated_node = handler.update_node_position(sample_project, node) handler.delete_node(sample_project, "test-node-id") upserted_edge = handler.upsert_edge(sample_project, edge) @@ -297,6 +304,10 @@ def test_individual_operations(self, sample_project, operation, method_name, arg handler_method = getattr(handler, operation) if operation == "ensure_graph_initialized": handler_method(sample_project) + elif operation == "insert_node": + # insert_node now uses InsertNodeRequest + request = InsertNodeRequest(node=args[0], project=sample_project, sim_global=args[1]) + handler_method(request) else: handler_method(sample_project, *args) diff --git a/tests/unit/managers/test_project_graph.py b/tests/unit/managers/test_project_graph.py index f411022..cf5658a 100644 --- a/tests/unit/managers/test_project_graph.py +++ b/tests/unit/managers/test_project_graph.py @@ -8,7 +8,7 @@ from fluidize.core.types.node import author, nodeMetadata_simulation, nodeProperties_simulation, tag from fluidize.core.types.parameters import Parameter from fluidize.core.types.runs import RunStatus -from fluidize.managers.graph import GraphManager +from fluidize.managers.graph import GraphManager, InsertNodeRequest from fluidize.managers.node import NodeManager from tests.fixtures.sample_graphs import SampleGraphs from tests.fixtures.sample_projects import SampleProjects @@ -97,11 +97,14 @@ def test_add_node_success(self, project_graph, mock_adapter): assert isinstance(result, NodeManager) assert result.node_id == node.id - mock_adapter.graph.insert_node.assert_called_once_with( - project_graph.project, - node, - True, # Default sim_global=True - ) + # Verify that insert_node was called with an InsertNodeRequest + mock_adapter.graph.insert_node.assert_called_once() + call_args = mock_adapter.graph.insert_node.call_args[0] + request = call_args[0] + assert isinstance(request, InsertNodeRequest) + assert request.node == node + assert request.project == project_graph.project + assert request.sim_global is True def test_add_node_with_sim_global_false(self, project_graph, mock_adapter): """Test node addition with sim_global=False.""" @@ -112,7 +115,14 @@ def test_add_node_with_sim_global_false(self, project_graph, mock_adapter): assert isinstance(result, NodeManager) assert result.node_id == node.id - mock_adapter.graph.insert_node.assert_called_once_with(project_graph.project, node, False) + # Verify that insert_node was called with an InsertNodeRequest with sim_global=False + mock_adapter.graph.insert_node.assert_called_once() + call_args = mock_adapter.graph.insert_node.call_args[0] + request = call_args[0] + assert isinstance(request, InsertNodeRequest) + assert request.node == node + assert request.project == project_graph.project + assert request.sim_global is False def test_add_node_from_scratch_success(self, project_graph, mock_adapter): """Test successful node creation from scratch.""" @@ -296,8 +306,13 @@ def test_project_scoping(self, mock_adapter): # Verify each call was made with correct project context calls = mock_adapter.graph.insert_node.call_args_list assert len(calls) == 2 - assert calls[0][0][0] == project1 # First call with project1 - assert calls[1][0][0] == project2 # Second call with project2 + # Check that both calls received InsertNodeRequest with correct projects + request1 = calls[0][0][0] + request2 = calls[1][0][0] + assert isinstance(request1, InsertNodeRequest) + assert isinstance(request2, InsertNodeRequest) + assert request1.project == project1 # First call with project1 + assert request2.project == project2 # Second call with project2 def test_all_methods_delegate_to_adapter(self, project_graph, mock_adapter): """Test that all GraphManager methods properly delegate to adapter.""" @@ -386,9 +401,17 @@ def test_project_context_consistency(self, project_graph, mock_adapter): ] # All calls should include the same project as first argument - for call_list in all_calls: + # (except insert_node which now uses InsertNodeRequest) + for i, call_list in enumerate(all_calls): if call_list: # If method was called - assert call_list[0][0][0] == project + if i == 1: # insert_node call index + # For insert_node, check the project in the InsertNodeRequest + request = call_list[0][0][0] + assert isinstance(request, InsertNodeRequest) + assert request.project == project + else: + # For other calls, project is still the first argument + assert call_list[0][0][0] == project def test_get_parameters_success(self, project_graph, mock_adapter): """Test successful parameter retrieval through ProjectGraph.""" diff --git a/uv.lock b/uv.lock index e1f8dcf..f135c6e 100644 --- a/uv.lock +++ b/uv.lock @@ -759,7 +759,7 @@ dev = [ requires-dist = [ { name = "asciitree", specifier = ">=0.3.3" }, { name = "docker", specifier = ">=7.1.0" }, - { name = "fluidize-sdk", specifier = ">=0.6.0" }, + { name = "fluidize-sdk", specifier = ">=0.7.0" }, { name = "jinja2", specifier = ">=3.1.6" }, { name = "mlflow", specifier = ">=3.1.4" }, { name = "networkx", specifier = ">=3.2.1" }, @@ -786,7 +786,7 @@ dev = [ [[package]] name = "fluidize-sdk" -version = "0.6.0" +version = "0.7.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "anyio" }, @@ -796,9 +796,9 @@ dependencies = [ { name = "sniffio" }, { name = "typing-extensions" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/f0/6d/41a511df1e9f2cfb6526a349162ea7032ef2ef3d4ced52b8ff81a036ca84/fluidize_sdk-0.6.0.tar.gz", hash = "sha256:cb4548e3ebac5c949b177a6f352edc91be5ffd82900dd0267159027ffb895633", size = 119144, upload-time = "2025-08-16T09:45:34.48Z" } +sdist = { url = "https://files.pythonhosted.org/packages/ad/58/af4a33d85bdebfa45cf8994eff983ac23e98a1086cc8dd3cd4b1c4adad7e/fluidize_sdk-0.7.0.tar.gz", hash = "sha256:9f1e2b5c38409790ce36e0e5d85792575a2045c1780ba12a74f1fc7dec87ddb6", size = 119652, upload-time = "2025-08-18T00:45:01.689Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/e9/6e/25a81bd993b6dec0733e7f4f13f199a40e3500222907bfab54fc5e946eea/fluidize_sdk-0.6.0-py3-none-any.whl", hash = "sha256:22db9101b88bbf0adea113057f96ce9436985ce3b9e635fedcdb421fff399ab2", size = 121524, upload-time = "2025-08-16T09:45:32.874Z" }, + { url = "https://files.pythonhosted.org/packages/01/e3/10c44e47f5fe50edb7d91fcdcc3613ec7ed8b34471e888d260f2415f52ae/fluidize_sdk-0.7.0-py3-none-any.whl", hash = "sha256:32744079456e5a5b6cc99e2ce1c48c9f42339f1e72a778383643711e0dcd1faf", size = 122274, upload-time = "2025-08-18T00:45:00.047Z" }, ] [[package]] From ee7d19f88a3b405f6835ab1ab006e7fe3f4946e4 Mon Sep 17 00:00:00 2001 From: Henry Bae <69275685+BaeHenryS@users.noreply.github.com> Date: Sun, 17 Aug 2025 20:00:35 -0700 Subject: [PATCH 2/8] Add Node ProjectSummary Validation Fixed --- fluidize/adapters/local/graph.py | 4 +++- fluidize/managers/graph.py | 13 ++++++++++++- 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/fluidize/adapters/local/graph.py b/fluidize/adapters/local/graph.py index 70a4169..e485451 100644 --- a/fluidize/adapters/local/graph.py +++ b/fluidize/adapters/local/graph.py @@ -15,7 +15,9 @@ from fluidize.core.utils.pathfinder.path_finder import PathFinder if TYPE_CHECKING: - from fluidize.managers.graph import InsertNodeRequest + pass + +from fluidize.managers.graph import InsertNodeRequest class GraphHandler: diff --git a/fluidize/managers/graph.py b/fluidize/managers/graph.py index 964a092..2c5936c 100644 --- a/fluidize/managers/graph.py +++ b/fluidize/managers/graph.py @@ -4,7 +4,7 @@ from typing import TYPE_CHECKING, Any, Optional -from pydantic import BaseModel +from pydantic import BaseModel, field_validator from fluidize.core.types.graph import GraphData, GraphEdge, GraphNode @@ -23,6 +23,17 @@ class InsertNodeRequest(BaseModel): project: ProjectSummary sim_global: bool = True + @field_validator("project") + @classmethod + def validate_project(cls, v: Any) -> ProjectSummary: + """Validate project field, handling both dict and ProjectSummary instances.""" + if isinstance(v, ProjectSummary): + return v + if isinstance(v, dict): + return ProjectSummary.model_validate(v) + msg = "Invalid project type" + raise ValueError(msg) + class GraphManager: """ From 0dc513a515c3a8d3c49bdc090fa7fe9c7c889966 Mon Sep 17 00:00:00 2001 From: Henry Bae <69275685+BaeHenryS@users.noreply.github.com> Date: Sun, 17 Aug 2025 20:09:51 -0700 Subject: [PATCH 3/8] Revert "Add Node ProjectSummary Validation Fixed" This reverts commit ee7d19f88a3b405f6835ab1ab006e7fe3f4946e4. --- fluidize/adapters/local/graph.py | 4 +--- fluidize/managers/graph.py | 13 +------------ 2 files changed, 2 insertions(+), 15 deletions(-) diff --git a/fluidize/adapters/local/graph.py b/fluidize/adapters/local/graph.py index e485451..70a4169 100644 --- a/fluidize/adapters/local/graph.py +++ b/fluidize/adapters/local/graph.py @@ -15,9 +15,7 @@ from fluidize.core.utils.pathfinder.path_finder import PathFinder if TYPE_CHECKING: - pass - -from fluidize.managers.graph import InsertNodeRequest + from fluidize.managers.graph import InsertNodeRequest class GraphHandler: diff --git a/fluidize/managers/graph.py b/fluidize/managers/graph.py index 2c5936c..964a092 100644 --- a/fluidize/managers/graph.py +++ b/fluidize/managers/graph.py @@ -4,7 +4,7 @@ from typing import TYPE_CHECKING, Any, Optional -from pydantic import BaseModel, field_validator +from pydantic import BaseModel from fluidize.core.types.graph import GraphData, GraphEdge, GraphNode @@ -23,17 +23,6 @@ class InsertNodeRequest(BaseModel): project: ProjectSummary sim_global: bool = True - @field_validator("project") - @classmethod - def validate_project(cls, v: Any) -> ProjectSummary: - """Validate project field, handling both dict and ProjectSummary instances.""" - if isinstance(v, ProjectSummary): - return v - if isinstance(v, dict): - return ProjectSummary.model_validate(v) - msg = "Invalid project type" - raise ValueError(msg) - class GraphManager: """ From a1754835b1defe72636ce890539ba145339efa29 Mon Sep 17 00:00:00 2001 From: Henry Bae <69275685+BaeHenryS@users.noreply.github.com> Date: Sun, 17 Aug 2025 20:43:32 -0700 Subject: [PATCH 4/8] Added Project Add Issue --- fluidize/managers/graph.py | 6 ++---- fluidize/managers/registry.py | 1 + 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/fluidize/managers/graph.py b/fluidize/managers/graph.py index 964a092..9c04ecd 100644 --- a/fluidize/managers/graph.py +++ b/fluidize/managers/graph.py @@ -15,7 +15,7 @@ from fluidize.core.types.project import ProjectSummary -# TODO: the ty +# TODO: the schemas should be defined in the schemas folder, which need to be migrated from the api backend. class InsertNodeRequest(BaseModel): """Uniform request structure for inserting nodes in both local and API modes.""" @@ -79,9 +79,7 @@ def add_node(self, node: GraphNode, sim_global: bool = True) -> "NodeManager": Returns: The added node """ - # Create uniform request structure for both local and API modes - request = InsertNodeRequest(node=node, project=self.project, sim_global=sim_global) - inserted_node = self.adapter.graph.insert_node(request) + inserted_node = self.adapter.graph.insert_node(self.project, node, sim_global) return self.get_node(inserted_node.id) def add_node_from_scratch( diff --git a/fluidize/managers/registry.py b/fluidize/managers/registry.py index c4b6094..c59e321 100644 --- a/fluidize/managers/registry.py +++ b/fluidize/managers/registry.py @@ -48,6 +48,7 @@ def create( ) return ProjectManager(self.adapter, project_summary) + # - [ ] ISSUE #1: Project not found error should be put out when invalid project is put with get def get(self, project_id: str) -> ProjectManager: """ Get a project by ID. From acddf4a273ed584788fa133c5a3cce96b81b0db2 Mon Sep 17 00:00:00 2001 From: Henry Bae <69275685+BaeHenryS@users.noreply.github.com> Date: Sun, 17 Aug 2025 21:05:23 -0700 Subject: [PATCH 5/8] Added Issue #2 --- fluidize/managers/graph.py | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/fluidize/managers/graph.py b/fluidize/managers/graph.py index 9c04ecd..d77271b 100644 --- a/fluidize/managers/graph.py +++ b/fluidize/managers/graph.py @@ -79,7 +79,16 @@ def add_node(self, node: GraphNode, sim_global: bool = True) -> "NodeManager": Returns: The added node """ - inserted_node = self.adapter.graph.insert_node(self.project, node, sim_global) + # Create uniform request structure for both local and API modes + # Convert ProjectSummary to dict and back to avoid validation issues with nested model validators + # - [ ] ISSUE #2: Finding a better way to sync types with API and Local Execution + project_dict = self.project.model_dump() if hasattr(self.project, "model_dump") else self.project + request = InsertNodeRequest( + node=node, + project=ProjectSummary(**project_dict) if isinstance(project_dict, dict) else project_dict, + sim_global=sim_global, + ) + inserted_node = self.adapter.graph.insert_node(request) return self.get_node(inserted_node.id) def add_node_from_scratch( From d2d5ee5e775e09f6bc695545f8dcebc5a87bf29a Mon Sep 17 00:00:00 2001 From: Henry Bae <69275685+BaeHenryS@users.noreply.github.com> Date: Sun, 17 Aug 2025 21:06:55 -0700 Subject: [PATCH 6/8] Temporary Fix for incorporating API calls - will need a much more refined method --- fluidize/managers/graph.py | 24 ++++++++++++++---------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/fluidize/managers/graph.py b/fluidize/managers/graph.py index d77271b..24c0250 100644 --- a/fluidize/managers/graph.py +++ b/fluidize/managers/graph.py @@ -79,16 +79,20 @@ def add_node(self, node: GraphNode, sim_global: bool = True) -> "NodeManager": Returns: The added node """ - # Create uniform request structure for both local and API modes - # Convert ProjectSummary to dict and back to avoid validation issues with nested model validators - # - [ ] ISSUE #2: Finding a better way to sync types with API and Local Execution - project_dict = self.project.model_dump() if hasattr(self.project, "model_dump") else self.project - request = InsertNodeRequest( - node=node, - project=ProjectSummary(**project_dict) if isinstance(project_dict, dict) else project_dict, - sim_global=sim_global, - ) - inserted_node = self.adapter.graph.insert_node(request) + # ISSUE #2: + # Temporary adapter detection - check if using SDK/API mode vs local mode + if hasattr(self.adapter, "__class__") and "FluidizeSDK" in str(type(self.adapter)): + # API mode: SDK expects keyword arguments directly + inserted_node = self.adapter.graph.insert_node(node=node, project=self.project, sim_global=sim_global) + else: + # Local mode: expects InsertNodeRequest wrapper + project_dict = self.project.model_dump() if hasattr(self.project, "model_dump") else self.project + request = InsertNodeRequest( + node=node, + project=ProjectSummary(**project_dict) if isinstance(project_dict, dict) else project_dict, + sim_global=sim_global, + ) + inserted_node = self.adapter.graph.insert_node(request) return self.get_node(inserted_node.id) def add_node_from_scratch( From 0a4413506bc7e4bad6a26cf13467437257ede6f6 Mon Sep 17 00:00:00 2001 From: Henry Bae <69275685+BaeHenryS@users.noreply.github.com> Date: Sun, 17 Aug 2025 21:19:52 -0700 Subject: [PATCH 7/8] Graph Add Node Simplified --- fluidize/adapters/local/graph.py | 15 ++++---- fluidize/managers/graph.py | 26 +------------ tests/unit/backends/local/test_graph.py | 24 ++++-------- tests/unit/managers/test_project_graph.py | 45 ++++++++--------------- 4 files changed, 31 insertions(+), 79 deletions(-) diff --git a/fluidize/adapters/local/graph.py b/fluidize/adapters/local/graph.py index 70a4169..ac55ee9 100644 --- a/fluidize/adapters/local/graph.py +++ b/fluidize/adapters/local/graph.py @@ -5,7 +5,7 @@ wrapping the core GraphProcessor with adapter-specific functionality. """ -from typing import TYPE_CHECKING, Optional +from typing import Optional from fluidize.core.modules.graph.processor import GraphProcessor from fluidize.core.types.graph import GraphData, GraphEdge, GraphNode @@ -14,9 +14,6 @@ from fluidize.core.types.project import ProjectSummary from fluidize.core.utils.pathfinder.path_finder import PathFinder -if TYPE_CHECKING: - from fluidize.managers.graph import InsertNodeRequest - class GraphHandler: """ @@ -43,18 +40,20 @@ def get_graph(self, project: ProjectSummary) -> GraphData: processor = GraphProcessor(project) return processor.get_graph() - def insert_node(self, request: "InsertNodeRequest") -> GraphNode: + def insert_node(self, node: GraphNode, project: ProjectSummary, sim_global: bool = True) -> GraphNode: """ Insert a new node into the project graph. Args: - request: InsertNodeRequest containing node, project, and sim_global + node: The node to insert + project: The project to add the node to + sim_global: Whether to use global simulations Returns: The inserted node """ - processor = GraphProcessor(request.project) - return processor.insert_node(request.node, request.sim_global) + processor = GraphProcessor(project) + return processor.insert_node(node, sim_global) def insert_node_from_scratch( self, diff --git a/fluidize/managers/graph.py b/fluidize/managers/graph.py index 24c0250..83fa5d9 100644 --- a/fluidize/managers/graph.py +++ b/fluidize/managers/graph.py @@ -4,8 +4,6 @@ from typing import TYPE_CHECKING, Any, Optional -from pydantic import BaseModel - from fluidize.core.types.graph import GraphData, GraphEdge, GraphNode if TYPE_CHECKING: @@ -15,15 +13,6 @@ from fluidize.core.types.project import ProjectSummary -# TODO: the schemas should be defined in the schemas folder, which need to be migrated from the api backend. -class InsertNodeRequest(BaseModel): - """Uniform request structure for inserting nodes in both local and API modes.""" - - node: GraphNode - project: ProjectSummary - sim_global: bool = True - - class GraphManager: """ Graph manager for a specific project. @@ -79,20 +68,7 @@ def add_node(self, node: GraphNode, sim_global: bool = True) -> "NodeManager": Returns: The added node """ - # ISSUE #2: - # Temporary adapter detection - check if using SDK/API mode vs local mode - if hasattr(self.adapter, "__class__") and "FluidizeSDK" in str(type(self.adapter)): - # API mode: SDK expects keyword arguments directly - inserted_node = self.adapter.graph.insert_node(node=node, project=self.project, sim_global=sim_global) - else: - # Local mode: expects InsertNodeRequest wrapper - project_dict = self.project.model_dump() if hasattr(self.project, "model_dump") else self.project - request = InsertNodeRequest( - node=node, - project=ProjectSummary(**project_dict) if isinstance(project_dict, dict) else project_dict, - sim_global=sim_global, - ) - inserted_node = self.adapter.graph.insert_node(request) + inserted_node = self.adapter.graph.insert_node(node=node, project=self.project, sim_global=sim_global) return self.get_node(inserted_node.id) def add_node_from_scratch( diff --git a/tests/unit/backends/local/test_graph.py b/tests/unit/backends/local/test_graph.py index df0952f..7576670 100644 --- a/tests/unit/backends/local/test_graph.py +++ b/tests/unit/backends/local/test_graph.py @@ -7,7 +7,6 @@ from fluidize.adapters.local.graph import GraphHandler from fluidize.core.types.graph import GraphData from fluidize.core.types.parameters import Parameter -from fluidize.managers.graph import InsertNodeRequest from tests.fixtures.sample_graphs import SampleGraphs from tests.fixtures.sample_projects import SampleProjects @@ -78,8 +77,7 @@ def test_insert_node_success(self, graph_handler, mock_processor, sample_project node = SampleGraphs.sample_nodes()[0] mock_processor.insert_node.return_value = node - request = InsertNodeRequest(node=node, project=sample_project, sim_global=True) - result = graph_handler.insert_node(request) + result = graph_handler.insert_node(node, sample_project, sim_global=True) assert result == node mock_processor.insert_node.assert_called_once_with(node, True) @@ -89,8 +87,7 @@ def test_insert_node_with_sim_global_false(self, graph_handler, mock_processor, node = SampleGraphs.sample_nodes()[1] mock_processor.insert_node.return_value = node - request = InsertNodeRequest(node=node, project=sample_project, sim_global=False) - result = graph_handler.insert_node(request) + result = graph_handler.insert_node(node, sample_project, sim_global=False) assert result == node mock_processor.insert_node.assert_called_once_with(node, False) @@ -100,8 +97,7 @@ def test_insert_node_default_sim_global(self, graph_handler, mock_processor, sam node = SampleGraphs.sample_nodes()[0] mock_processor.insert_node.return_value = node - request = InsertNodeRequest(node=node, project=sample_project) # sim_global defaults to True - result = graph_handler.insert_node(request) + result = graph_handler.insert_node(node, sample_project) # sim_global defaults to True assert result == node mock_processor.insert_node.assert_called_once_with(node, True) # Default is True @@ -164,8 +160,7 @@ def test_processor_error_propagation_insert_node(self, graph_handler, mock_proce mock_processor.insert_node.side_effect = ValueError("Invalid node data") with pytest.raises(ValueError, match="Invalid node data"): - request = InsertNodeRequest(node=node, project=sample_project) - graph_handler.insert_node(request) + graph_handler.insert_node(node, sample_project) def test_processor_error_propagation_delete_node(self, graph_handler, mock_processor, sample_project): """Test that processor errors are propagated for delete_node.""" @@ -201,8 +196,7 @@ def test_processor_creation_per_operation(self, sample_project): # Perform multiple operations handler.get_graph(sample_project) - request = InsertNodeRequest(node=SampleGraphs.sample_nodes()[0], project=sample_project) - handler.insert_node(request) + handler.insert_node(SampleGraphs.sample_nodes()[0], sample_project) handler.delete_node(sample_project, "test-id") # Verify processor was created for each operation @@ -251,8 +245,7 @@ def test_all_crud_operations_flow(self, sample_project): # Perform full CRUD cycle graph_data = handler.get_graph(sample_project) - request = InsertNodeRequest(node=node, project=sample_project) - inserted_node = handler.insert_node(request) + inserted_node = handler.insert_node(node, sample_project) updated_node = handler.update_node_position(sample_project, node) handler.delete_node(sample_project, "test-node-id") upserted_edge = handler.upsert_edge(sample_project, edge) @@ -305,9 +298,8 @@ def test_individual_operations(self, sample_project, operation, method_name, arg if operation == "ensure_graph_initialized": handler_method(sample_project) elif operation == "insert_node": - # insert_node now uses InsertNodeRequest - request = InsertNodeRequest(node=args[0], project=sample_project, sim_global=args[1]) - handler_method(request) + # insert_node uses direct arguments + handler_method(args[0], sample_project, args[1]) else: handler_method(sample_project, *args) diff --git a/tests/unit/managers/test_project_graph.py b/tests/unit/managers/test_project_graph.py index cf5658a..5efcce9 100644 --- a/tests/unit/managers/test_project_graph.py +++ b/tests/unit/managers/test_project_graph.py @@ -8,7 +8,7 @@ from fluidize.core.types.node import author, nodeMetadata_simulation, nodeProperties_simulation, tag from fluidize.core.types.parameters import Parameter from fluidize.core.types.runs import RunStatus -from fluidize.managers.graph import GraphManager, InsertNodeRequest +from fluidize.managers.graph import GraphManager from fluidize.managers.node import NodeManager from tests.fixtures.sample_graphs import SampleGraphs from tests.fixtures.sample_projects import SampleProjects @@ -97,14 +97,10 @@ def test_add_node_success(self, project_graph, mock_adapter): assert isinstance(result, NodeManager) assert result.node_id == node.id - # Verify that insert_node was called with an InsertNodeRequest - mock_adapter.graph.insert_node.assert_called_once() - call_args = mock_adapter.graph.insert_node.call_args[0] - request = call_args[0] - assert isinstance(request, InsertNodeRequest) - assert request.node == node - assert request.project == project_graph.project - assert request.sim_global is True + # Verify that insert_node was called with correct arguments + mock_adapter.graph.insert_node.assert_called_once_with( + node=node, project=project_graph.project, sim_global=True + ) def test_add_node_with_sim_global_false(self, project_graph, mock_adapter): """Test node addition with sim_global=False.""" @@ -115,14 +111,10 @@ def test_add_node_with_sim_global_false(self, project_graph, mock_adapter): assert isinstance(result, NodeManager) assert result.node_id == node.id - # Verify that insert_node was called with an InsertNodeRequest with sim_global=False - mock_adapter.graph.insert_node.assert_called_once() - call_args = mock_adapter.graph.insert_node.call_args[0] - request = call_args[0] - assert isinstance(request, InsertNodeRequest) - assert request.node == node - assert request.project == project_graph.project - assert request.sim_global is False + # Verify that insert_node was called with correct arguments + mock_adapter.graph.insert_node.assert_called_once_with( + node=node, project=project_graph.project, sim_global=False + ) def test_add_node_from_scratch_success(self, project_graph, mock_adapter): """Test successful node creation from scratch.""" @@ -306,13 +298,9 @@ def test_project_scoping(self, mock_adapter): # Verify each call was made with correct project context calls = mock_adapter.graph.insert_node.call_args_list assert len(calls) == 2 - # Check that both calls received InsertNodeRequest with correct projects - request1 = calls[0][0][0] - request2 = calls[1][0][0] - assert isinstance(request1, InsertNodeRequest) - assert isinstance(request2, InsertNodeRequest) - assert request1.project == project1 # First call with project1 - assert request2.project == project2 # Second call with project2 + # Check that both calls received correct keyword arguments + assert calls[0].kwargs["project"] == project1 # First call with project1 + assert calls[1].kwargs["project"] == project2 # Second call with project2 def test_all_methods_delegate_to_adapter(self, project_graph, mock_adapter): """Test that all GraphManager methods properly delegate to adapter.""" @@ -400,15 +388,12 @@ def test_project_context_consistency(self, project_graph, mock_adapter): mock_adapter.graph.delete_edge.call_args_list, ] - # All calls should include the same project as first argument - # (except insert_node which now uses InsertNodeRequest) + # All calls should include the same project (either as first argument or keyword) for i, call_list in enumerate(all_calls): if call_list: # If method was called if i == 1: # insert_node call index - # For insert_node, check the project in the InsertNodeRequest - request = call_list[0][0][0] - assert isinstance(request, InsertNodeRequest) - assert request.project == project + # For insert_node, check the project in keyword arguments + assert call_list[0].kwargs["project"] == project else: # For other calls, project is still the first argument assert call_list[0][0][0] == project From 451fcffd750e57ca5ae4ff965c5f13dffe409e7c Mon Sep 17 00:00:00 2001 From: Henry Bae <69275685+BaeHenryS@users.noreply.github.com> Date: Sun, 17 Aug 2025 23:49:51 -0700 Subject: [PATCH 8/8] Run Flow taking in keyword argument --- fluidize/managers/runs.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fluidize/managers/runs.py b/fluidize/managers/runs.py index 4427f51..2719022 100644 --- a/fluidize/managers/runs.py +++ b/fluidize/managers/runs.py @@ -37,7 +37,7 @@ def run_flow(self, payload: RunFlowPayload) -> dict[str, Any]: Returns: Dictionary with flow_status and run_number """ - return self.adapter.runs.run_flow(self.project, payload) # type: ignore[no-any-return] + return self.adapter.runs.run_flow(project=self.project, payload=payload) # type: ignore[no-any-return] def list_runs(self) -> list[str]: """