Skip to content

Conversation

@ogabrielluiz
Copy link
Contributor

@ogabrielluiz ogabrielluiz commented Sep 10, 2025

This will allow users to have more reproducible tweaks.
CleanShot 2025-09-10 at 15 50 16@2x
CleanShot 2025-09-10 at 15 50 38@2x
CleanShot 2025-09-10 at 15 50 55@2x
CleanShot 2025-09-10 at 15 51 17@2x

Summary by CodeRabbit

  • New Features
    • Introduced human‑readable component aliases with inline badges and tooltips across the UI.
    • Automatic aliasing for newly added or pasted components, with smart numbering and migration for existing flows.
    • API code generators (cURL, JavaScript, Python) now use aliases in generated requests.
    • Tweaks now accept node ID, alias, or display name, with clear conflict detection if multiple keys target the same component.
  • Documentation
    • Clarified tweaks schema to include alias/display name keys with examples.
  • Tests
    • Added extensive unit tests for aliasing and tweak processing.
  • Chores
    • Removed a stray console log.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Sep 10, 2025

Walkthrough

Implements component aliasing across frontend and backend: adds alias fields, utilities, migration, UI badges, and code generation using aliases. Updates tweak processing to accept node IDs, aliases, or display names with conflict detection. Introduces dynamic alias assignment during graph dump and comprehensive tests for alias behavior.

Changes

Cohort / File(s) Summary
Frontend UI: alias display in nodes
src/frontend/src/CustomNodes/GenericNode/components/NodeName/index.tsx, src/frontend/src/components/core/codeTabsComponent/components/tweakComponent/index.tsx
Display alias badges next to node names; compute effective alias and numeric suffix; update name blur handler to propagate alias updates.
Frontend add/paste + store migration
src/frontend/src/hooks/use-add-component.ts, src/frontend/src/stores/flowStore.ts
Assign aliases when adding/pasting nodes; migrate existing flows on reset; renumber/assign aliases based on display_name.
Frontend API/code-gen: alias-aware tweaks
src/frontend/src/modals/apiModal/codeTabs/code-tabs.tsx, src/frontend/src/modals/apiModal/utils/get-curl-code.tsx, src/frontend/src/modals/apiModal/utils/get-js-api-code.tsx, src/frontend/src/modals/apiModal/utils/get-python-api-code.tsx
Pass nodes into generators; convert tweaks to alias-keyed maps; keep file detection on original tweaks; replace keys with aliases in payloads and examples.
Frontend types and utilities
src/frontend/src/types/api/index.ts, src/frontend/src/types/flow/index.ts, src/frontend/src/utils/aliasUtils.ts
Add optional alias to APIClassType; add effective-alias helpers; add full alias management utilities: generation, renumbering, migration, display-name change updates, user-defined alias handlers, and tweaks conversion.
Frontend tests
src/frontend/src/utils/__tests__/aliasUtils.test.ts
Unit tests covering alias generation, safety, migration, renumbering, user-defined alias handling, display-name changes, and tweaks conversion.
Minor frontend cleanup
src/frontend/src/pages/FlowPage/components/flowSidebarComponent/index.tsx
Remove debug log.
Backend: component/node alias fields
src/lfx/src/lfx/template/frontend_node/base.py, src/lfx/src/lfx/custom/custom_component/component.py
Add optional alias to FrontendNode; allow to_frontend_node(graph_data) and inject dynamic alias when available.
Backend: graph dump aliasing
src/lfx/src/lfx/graph/graph/base.py
Add dynamic alias assignment during dump; preserve existing aliases; remove unnecessary “#1” when single instance.
Backend: tweak processing
src/lfx/src/lfx/processing/process.py
Resolve tweaks by node ID, alias, or display name; detect conflicts when multiple keys target the same node; apply dict tweaks per-node and batch non-dict tweaks.
Backend schema docs
src/lfx/src/lfx/schema/graph.py
Document alias/display-name tweak keys and add examples.
Backend tests
src/lfx/tests/unit/graph/graph/test_base.py, src/lfx/tests/unit/processing/test_process.py, src/lfx/tests/unit/processing/__init__.py
Tests for graph dump aliasing scenarios and tweak processing with aliases/display names; module docstring.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  actor User
  participant UI as Frontend UI
  participant Store as FlowStore
  participant Utils as aliasUtils
  participant BE as Backend (lfx)

  rect rgba(200,230,255,0.3)
  note over User,UI: Add/Paste Component
  User->>UI: Add component
  UI->>Store: addNode(newNode)
  Store->>Utils: assignAliasToNewComponent(newNode, nodes)
  Utils-->>Store: newNode with alias (if needed)
  Store-->>UI: nodes updated
  end

  rect rgba(220,255,220,0.3)
  note over UI,Store: Rename Display Name
  User->>UI: Edit display_name
  UI->>Store: update node display_name
  UI->>Utils: updateAliasesForDisplayNameChange(nodeId, old, new, nodes)
  Utils-->>UI: nodes with updated/renumbered aliases
  UI->>Store: setNodes(updatedNodes)
  end

  rect rgba(255,245,200,0.4)
  note over UI,BE: Generate API Code (aliases)
  UI->>Store: read nodes + tweaks
  UI->>Utils: convertTweaksToAliases(tweaks, nodes)
  UI-->>User: JS/Python/cURL with alias-keyed tweaks
  User->>BE: Call API with alias/display_name/nodeId keys
  BE->>BE: process_tweaks() resolve keys (id/alias/display_name)
  BE-->>User: Response
  end
Loading
sequenceDiagram
  autonumber
  participant BE as Backend Graph.dump
  participant Comp as Components
  participant Helper as _add_dynamic_aliases

  BE->>Comp: to_frontend_node(graph_data)
  Comp-->>BE: nodes (some with alias)
  BE->>Helper: assign dynamic aliases
  Helper-->>BE: nodes with generated/cleaned aliases
  BE-->>BE: produce GraphDump dict
Loading

Estimated code review effort

🎯 5 (Critical) | ⏱️ ~120 minutes

Possibly related PRs

  • feat: introduce lfx package #9133 — Introduced and relocated lfx backend modules now extended here with alias fields, graph dump logic, and processing; directly touches the same areas.

Suggested labels

enhancement, size:L, lgtm

Suggested reviewers

  • mfortman11
  • lucaseduoli

Tip

👮 Agentic pre-merge checks are now available in preview!

Pro plan users can now enable pre-merge checks in their settings to enforce checklists before merging PRs.

  • Built-in checks – Quickly apply ready-made checks to enforce title conventions, require pull request descriptions that follow templates, validate linked issues for compliance, and more.
  • Custom agentic checks – Define your own rules using CodeRabbit’s advanced agentic capabilities to enforce organization-specific policies and workflows. For example, you can instruct CodeRabbit’s agent to verify that API documentation is updated whenever API schema files are modified in a PR. Note: Upto 5 custom checks are currently allowed during the preview period. Pricing for this feature will be announced in a few weeks.

Please see the documentation for more information.

Example:

reviews:
  pre_merge_checks:
    custom_checks:
      - name: "Undocumented Breaking Changes"
        mode: "warning"
        instructions: |
          Pass/fail criteria: All breaking changes to public APIs, CLI flags, environment variables, configuration keys, database schemas, or HTTP/GraphQL endpoints must be documented in the "Breaking Change" section of the PR description and in CHANGELOG.md. Exclude purely internal or private changes (e.g., code not exported from package entry points or explicitly marked as internal).

Please share your feedback with us on this Discord post.

Pre-merge checks (2 passed, 1 warning)

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 70.45% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Title Check ✅ Passed The title succinctly and accurately captures the primary feature introduced by the pull request—adding alias functionality across both the user interface and the tweaks processing logic—without extraneous detail or file references, making it clear to any reviewer scanning the history what core change was made.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
✨ Finishing Touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch add-component-alias-fix-tweaks

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@github-actions github-actions bot added the enhancement New feature or request label Sep 10, 2025
@sonarqubecloud
Copy link

@github-actions
Copy link
Contributor

Frontend Unit Test Coverage Report

Coverage Summary

Lines Statements Branches Functions
Coverage: 7%
7.12% (1866/26182) 4% (793/19813) 3.99% (225/5627)

Unit Test Results

Tests Skipped Failures Errors Time
715 0 💤 0 ❌ 0 🔥 12.035s ⏱️

@codecov
Copy link

codecov bot commented Sep 10, 2025

Codecov Report

❌ Patch coverage is 66.81223% with 76 lines in your changes missing coverage. Please review.
✅ Project coverage is 21.85%. Comparing base (4144d76) to head (d952ca5).
⚠️ Report is 2 commits behind head on main.

Files with missing lines Patch % Lines
src/frontend/src/utils/aliasUtils.ts 79.60% 10 Missing and 21 partials ⚠️
...tomNodes/GenericNode/components/NodeName/index.tsx 0.00% 13 Missing ⚠️
src/frontend/src/hooks/use-add-component.ts 0.00% 13 Missing ⚠️
src/frontend/src/stores/flowStore.ts 0.00% 6 Missing ⚠️
...eTabsComponent/components/tweakComponent/index.tsx 0.00% 5 Missing ⚠️
...ontend/src/modals/apiModal/utils/get-curl-code.tsx 77.77% 0 Missing and 4 partials ⚠️
src/frontend/src/types/flow/index.ts 50.00% 2 Missing and 2 partials ⚠️
Additional details and impacted files

Impacted file tree graph

@@            Coverage Diff             @@
##             main    #9801      +/-   ##
==========================================
+ Coverage   21.62%   21.85%   +0.23%     
==========================================
  Files        1074     1076       +2     
  Lines       39650    39859     +209     
  Branches     5418     5474      +56     
==========================================
+ Hits         8576     8713     +137     
- Misses      30930    30975      +45     
- Partials      144      171      +27     
Flag Coverage Δ
frontend 6.32% <66.81%> (+0.50%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

Files with missing lines Coverage Δ
...rontend/src/modals/apiModal/codeTabs/code-tabs.tsx 0.00% <ø> (ø)
...tend/src/modals/apiModal/utils/get-js-api-code.tsx 78.68% <100.00%> (+1.49%) ⬆️
.../src/modals/apiModal/utils/get-python-api-code.tsx 78.84% <100.00%> (+1.76%) ⬆️
...FlowPage/components/flowSidebarComponent/index.tsx 0.00% <ø> (ø)
...ontend/src/modals/apiModal/utils/get-curl-code.tsx 60.19% <77.77%> (-0.86%) ⬇️
src/frontend/src/types/flow/index.ts 50.00% <50.00%> (ø)
...eTabsComponent/components/tweakComponent/index.tsx 0.00% <0.00%> (ø)
src/frontend/src/stores/flowStore.ts 0.00% <0.00%> (ø)
...tomNodes/GenericNode/components/NodeName/index.tsx 0.00% <0.00%> (ø)
src/frontend/src/hooks/use-add-component.ts 0.00% <0.00%> (ø)
... and 1 more
🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@github-actions github-actions bot added enhancement New feature or request and removed enhancement New feature or request labels Sep 10, 2025
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 16

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (7)
src/lfx/src/lfx/custom/custom_component/component.py (1)

955-1003: Implement or remove _calculate_dynamic_alias before use
The call self._calculate_dynamic_alias(graph_data) at src/lfx/src/lfx/custom/custom_component/component.py:1002 invokes a method that isn’t defined on this class or any base—this will raise an AttributeError at runtime. Add the missing _calculate_dynamic_alias implementation (or import it) before calling it.

src/lfx/src/lfx/template/frontend_node/base.py (1)

149-171: Add "alias" to reserved attributes to prevent collisions.
Without this, a component could define an input/output named "alias", risking downstream confusion.

Apply:

         attributes = [
             "inputs",
             "outputs",
             "_artifacts",
             "_results",
             "logs",
             "status",
             "vertex",
             "graph",
             "display_name",
             "description",
             "documentation",
             "icon",
+            "alias",
         ]
src/frontend/src/modals/apiModal/utils/get-js-api-code.tsx (1)

53-66: Fix ReferenceError: crypto is undefined when API key is hidden

In the no-file path, crypto.randomUUID() is used regardless of shouldDisplayApiKey, but crypto is only required when shouldDisplayApiKey is true. Always require('crypto') to avoid runtime errors.

-    const authSection = shouldDisplayApiKey
-      ? `const crypto = require('crypto');
-const apiKey = 'YOUR_API_KEY_HERE';
-`
-      : "";
+    const authSection = shouldDisplayApiKey
+      ? `const crypto = require('crypto');
+const apiKey = 'YOUR_API_KEY_HERE';
+`
+      : `const crypto = require('crypto');
+`;
src/frontend/src/modals/apiModal/utils/get-python-api-code.tsx (4)

49-56: Fix NameError: always define headers

When shouldDisplayApiKey is false, headers is undefined but still used in requests. Always set a default empty dict.

Apply:

-  const authSection = shouldDisplayApiKey
-    ? `api_key = 'YOUR_API_KEY_HERE'`
-    : "";
-
-  const headersSection = shouldDisplayApiKey
-    ? `headers = {"x-api-key": api_key}`
-    : "";
+  const authSection = shouldDisplayApiKey
+    ? `api_key = 'YOUR_API_KEY_HERE'`
+    : "";
+
+  const headersSection = `headers = ${
+    shouldDisplayApiKey ? '{"x-api-key": api_key}' : '{}'
+  }`;

44-48: Avoid brittle true/false/null text replacements; use json.loads

String replacements will corrupt string values containing “true/false/null”. Emit JSON and parse in Python.

Apply:

-    const payloadString = JSON.stringify(payloadWithAliases, null, 4)
-      .replace(/true/g, "True")
-      .replace(/false/g, "False")
-      .replace(/null/g, "None");
+    const payloadJson = JSON.stringify(payloadWithAliases, null, 2);

And in the returned Python code:

-import requests
-import os
-import uuid
+import requests
+import os
+import uuid
+import json
@@
-# Request payload configuration
-payload = ${payloadString}
-payload["session_id"] = str(uuid.uuid4())
+# Request payload configuration
+payload = json.loads(r'''${payloadJson}''')
+payload["session_id"] = str(uuid.uuid4())

Also applies to: 57-67


121-129: Python variable paths are quoted as strings in payload (files for ChatInput)

JSON.stringify quotes chat_file_path_X, so the payload sends the literal text, not the uploaded path. Inject via placeholders and replace quotes, and use a string (not array) for ChatInput’s files.

Apply:

-    const originalTweak = tweaks[nodeId];
-    const modifiedTweak = { ...originalTweak };
-    modifiedTweak.files = [`chat_file_path_${index + 1}`];
-    tweakAssignments.push(
-      `    \"${nodeId}\": ${JSON.stringify(modifiedTweak, null, 4)
-        .split("\n")
-        .join("\n    ")}`,
-    );
+    const originalTweak = tweaks[nodeId];
+    const modifiedTweak = { ...originalTweak };
+    const __placeholder = `__pyvar_chat_file_path_${index + 1}__`;
+    // ChatInput expects a string path
+    (modifiedTweak as any).files = __placeholder;
+    const tweakJson = JSON.stringify(modifiedTweak, null, 4).replace(
+      `"${__placeholder}"`,
+      `chat_file_path_${index + 1}`,
+    );
+    tweakAssignments.push(
+      `    "${nodeId}": ${tweakJson.split("\n").join("\n    ")}`,
+    );

143-155: Python variable paths are quoted as strings in payload (File/VideoFile)

Same issue for path/file_path. Use placeholders and unquote them post-stringify.

Apply:

-    const originalTweak = tweaks[nodeId];
-    const modifiedTweak = { ...originalTweak };
-    if ("path" in originalTweak) {
-      modifiedTweak.path = [`file_path_${index + 1}`];
-    } else if ("file_path" in originalTweak) {
-      modifiedTweak.file_path = `file_path_${index + 1}`;
-    }
-    tweakAssignments.push(
-      `    \"${nodeId}\": ${JSON.stringify(modifiedTweak, null, 4)
-        .split("\n")
-        .join("\n    ")}`,
-    );
+    const originalTweak = tweaks[nodeId];
+    const modifiedTweak = { ...originalTweak };
+    const __placeholder = `__pyvar_file_path_${index + 1}__`;
+    if ("path" in originalTweak) {
+      (modifiedTweak as any).path = [__placeholder];
+    } else if ("file_path" in originalTweak) {
+      (modifiedTweak as any).file_path = __placeholder;
+    }
+    const tweakJson = JSON.stringify(modifiedTweak, null, 4).replaceAll(
+      `"${__placeholder}"`,
+      `file_path_${index + 1}`,
+    );
+    tweakAssignments.push(
+      `    "${nodeId}": ${tweakJson.split("\n").join("\n    ")}`,
+    );
🧹 Nitpick comments (22)
src/lfx/src/lfx/schema/graph.py (1)

33-35: Examples look good—consider adding one that shows mixed key types and notes ambiguity.

Optional: add a second example illustrating simultaneous use of a node ID, an alias, and a display name, plus a brief note about how conflicts are handled (error vs precedence). This helps users pattern their tweaks correctly.

src/frontend/src/types/api/index.ts (1)

36-36: Alias field addition looks good; consider a short doc comment.

Optional: add a brief JSDoc describing that alias is a stable, unique identifier (if uniqueness is expected) and when it may be absent.

src/lfx/src/lfx/custom/custom_component/component.py (1)

114-114: Unused private field _alias.

It’s set but not read. Either wire it into alias computation/accessors or remove to avoid confusion.

Apply this diff if you decide to remove it:

-        self._alias: str | None = None
src/lfx/src/lfx/processing/process.py (1)

252-268: Log when a dict-valued tweak key matches no node.

Silent skips are hard to debug.

Apply this diff:

-            if node:
+            if node:
                 node_id = node.get("id")
@@
-                processed_nodes[node_id] = (key, key_type)
-                apply_tweaks(node, value)
+                processed_nodes[node_id] = (key, key_type)
+                apply_tweaks(node, value)
+            else:
+                logger.warning(
+                    f"Tweak key '{key}' did not match any node ID, alias, or display name; skipping."
+                )
src/frontend/src/modals/apiModal/codeTabs/code-tabs.tsx (1)

77-83: Passes nodes into code generators — consider memoizing to avoid heavy recomputation.
getNew* helpers may be expensive; nodes is large and changes often. Wrap codeOptions in useMemo so code strings aren’t regenerated on every minor render.

Apply within this range:

-  const codeOptions = {
-    endpointName: endpointName || "",
-    streaming: streaming,
-    flowId: flowId || "",
-    processedPayload: processedPayload,
-    nodes: nodes,
-  };
+  const codeOptions = useMemo(
+    () => ({
+      endpointName: endpointName || "",
+      streaming,
+      flowId: flowId || "",
+      processedPayload,
+      nodes,
+    }),
+    [endpointName, streaming, flowId, processedPayload, nodes],
+  );

And add the missing import at the top (outside this hunk):

import { useEffect, useMemo, useState } from "react";
src/frontend/src/components/core/codeTabsComponent/components/tweakComponent/index.tsx (1)

7-7: Unify imports from the same module.
Minor cleanup; import type and value from "@/types/flow" together.

-import type { AllNodeType } from "@/types/flow";
-import { getEffectiveAliasFromAnyNode } from "@/types/flow";
+import { getEffectiveAliasFromAnyNode, type AllNodeType } from "@/types/flow";
src/frontend/src/stores/flowStore.ts (1)

218-225: Run migration early — consider defensive cloning to avoid side-effects.
migrateExistingFlow mutates nodes; if flow is reused elsewhere, clone first to keep this function pure.

-    let nodes = flow?.data?.nodes ?? [];
+    let nodes = cloneDeep(flow?.data?.nodes ?? []);
     const edges = flow?.data?.edges ?? [];
src/lfx/tests/unit/processing/test_process.py (2)

93-116: Make the conflict error assertion resilient

Depending on exact backend wording, the regex may be brittle. Consider asserting on ValueError and checking substring containment to avoid false negatives.

Apply:

-        with pytest.raises(ValueError, match="Conflicting tweaks found for the same component"):
-            process_tweaks(graph_data, tweaks)
+        with pytest.raises(ValueError) as exc:
+            process_tweaks(graph_data, tweaks)
+        assert "Conflicting" in str(exc.value)

158-205: Add a test for unknown/ambiguous tweak keys (optional)

Recommend adding a case where a tweak targets a non-existent alias/display_name and one where multiple nodes share a display_name without aliases (should not match), to lock behavior.

I can draft a parametric test covering both scenarios if helpful.

src/frontend/src/hooks/use-add-component.ts (2)

18-24: Stability: avoid re-creating the callback on every nodes change (optional)

Including nodes in the dependency array will recreate the callback frequently. You can read the latest nodes via store.getState() inside the callback and drop nodes from deps.

Example:

-    [store, paste, nodes, setNodes],
+    [store, paste, setNodes],

and replace uses of nodes with store.getState().nodes.

Also applies to: 93-94


73-76: Type string consistency (nit)

Comparing n.type to the literal "genericNode" can drift. Prefer comparing to getNodeRenderType("genericnode") or a shared enum/constant.

-          n.type === "genericNode" && n.data.node.display_name === displayName,
+          n.type === getNodeRenderType("genericnode") &&
+          n.data.node.display_name === displayName,
src/frontend/src/CustomNodes/GenericNode/components/NodeName/index.tsx (1)

40-45: Effect dependency: include selected (nit)

The effect conditionally uses selected; add it to deps to avoid stale reads.

-  }, [editNameDescription]);
+  }, [editNameDescription, selected]);
src/lfx/src/lfx/graph/graph/base.py (1)

235-238: Prevent unintended mutation of raw_graph_data during dump

dump() passes the same dict to the aliasing helper, which mutates it in place. This changes self.raw_graph_data as a side effect of dump(). Prefer aliasing a copy to keep dump side-effect-free.

-        # Calculate dynamic aliases for duplicate components
-        data_dict = self._add_dynamic_aliases(data_dict)
+        # Calculate dynamic aliases for duplicate components (work on a copy to avoid side effects)
+        data_dict = self._add_dynamic_aliases(copy.deepcopy(data_dict))
src/lfx/tests/unit/graph/graph/test_base.py (1)

212-266: Add a test to prevent duplicate numbered aliases when gaps exist

Consider a case like existing "Chat Input#3" and "Chat Input#1" plus one without alias; we should fill "#2" and avoid duplicate "#3". Add a test to lock this in once the helper fills gaps.

+def test_graph_dump_fills_number_gaps_and_avoids_duplicates():
+    from lfx.components.input_output import ChatInput
+    graph = Graph()
+    ids = [graph.add_component(ChatInput()) for _ in range(3)]
+    # Pre-seed aliases with a gap (#2 missing) and an existing #3
+    graph.raw_graph_data = {
+        "nodes": [
+            {"id": ids[0], "data": {"node": {"display_name": "Chat Input", "alias": "Chat Input#1"}}},
+            {"id": ids[1], "data": {"node": {"display_name": "Chat Input", "alias": None}}},
+            {"id": ids[2], "data": {"node": {"display_name": "Chat Input", "alias": "Chat Input#3"}}},
+        ],
+        "edges": [],
+    }
+    data = graph.dump()["data"]["nodes"]
+    aliases = {n["id"]: n["data"]["node"]["alias"] for n in data}
+    assert "Chat Input#2" in aliases.values()
+    # Ensure uniqueness
+    assert len({a for a in aliases.values() if a and a.startswith("Chat Input#")}) == 3
src/frontend/src/types/flow/index.ts (1)

81-97: Place runtime helpers in utils, keep “types” module type-only

These exported functions execute at runtime and are better suited for utils (e.g., src/frontend/src/utils/aliasUtils.ts) to keep the types package strictly for type declarations. If you keep them here, at least mark imports as type-only and ensure APIClassType.alias is typed (string | null).

-// Utility functions for computing effective alias from JSON data
-export function getEffectiveAlias(nodeData: NodeDataType): string {
+// Utility functions for computing effective alias from JSON data
+export function getEffectiveAlias(nodeData: NodeDataType): string {
   return nodeData.node.alias || nodeData.node.display_name;
 }
 
 export function getEffectiveAliasFromNode(node: GenericNodeType): string {
   return node.data.node.alias || node.data.node.display_name;
 }
 
 export function getEffectiveAliasFromAnyNode(node: AllNodeType): string {
   if (node.type === "genericNode") {
     return getEffectiveAliasFromNode(node);
   }
   // For note nodes or other types, fallback to a reasonable display
   return node.data?.node?.display_name || "Unknown";
 }
src/frontend/src/modals/apiModal/utils/get-js-api-code.tsx (2)

3-11: Remove unused imports to satisfy lint rules

getChatInputNodeId, getFileNodeId, and hasChatInputFiles are not used.

 import {
   getAllChatInputNodeIds,
   getAllFileNodeIds,
-  getChatInputNodeId,
-  getFileNodeId,
   getNonFileTypeTweaks,
-  hasChatInputFiles,
   hasFileTweaks,
 } from "./detect-file-tweaks";

18-26: Tighten types for nodes parameter

Use the existing flow types instead of any[]. This improves IDE tooling and catches misuse at compile time.

-import {
+import type {
   getAllChatInputNodeIds,
   getAllFileNodeIds,
   getNonFileTypeTweaks,
   hasFileTweaks,
 } from "./detect-file-tweaks";
+import type { AllNodeType } from "@/types/flow";
...
-  nodes?: any[];
+  nodes?: AllNodeType[];
src/frontend/src/modals/apiModal/utils/get-python-api-code.tsx (2)

25-25: Tighten nodes typing

Prefer precise types for better DX and safety.

Apply:

-  nodes?: any[];
+  nodes?: import("@/types/flow").AllNodeType[];

83-96: Minor consistency and robustness (optional)

  • Use original tweaks for node-id collection (detection + ids) to match cURL path and avoid alias collisions; continue displaying aliases in payload.
  • Consider adding DataStax Authorization header parity (like cURL) when flag is enabled.

I can produce a focused diff if you want parity and detection changes applied consistently.

Also applies to: 173-197

src/frontend/src/modals/apiModal/utils/get-curl-code.tsx (1)

74-78: Guard navigator for non-browser contexts

Prevents ReferenceError in SSR/tests when platform isn’t provided.

Apply:

-  const detectedPlatform =
-    platform ||
-    (/Windows|Win32|Win64|WOW32|WOW64/i.test(navigator.userAgent)
-      ? "powershell"
-      : "unix");
+  const detectedPlatform =
+    platform ||
+    (typeof navigator !== "undefined" &&
+    /Windows|Win32|Win64|WOW32|WOW64/i.test(navigator.userAgent)
+      ? "powershell"
+      : "unix");
src/frontend/src/utils/aliasUtils.ts (2)

16-23: Clarify auto vs. user-defined alias semantics in docs

Current comments say “All aliases are auto-generated” and “#number are protected,” which conflicts with later renumbering. Recommend clarifying: “Auto-generated aliases use ‘DisplayName#n’ and may be renumbered; user-defined aliases are any others and must be preserved.”

- * All aliases are auto-generated, but those with #number are protected
+ * Auto-generated aliases use the pattern `${displayName}#<n>` and may be renumbered
+ * User-defined aliases are any others and must be preserved as-is

380-397: Guard against alias collisions when converting tweaks

Duplicate aliases would overwrite tweak entries. Fallback to node IDs on collision.

   for (const [nodeId, tweakValues] of Object.entries(tweaks)) {
     const node = nodes.find((n) => n.id === nodeId);
     if (node) {
       const alias = getEffectiveAliasFromAnyNode(node);
-      aliasedTweaks[alias] = tweakValues;
+      if (aliasedTweaks[alias] !== undefined) {
+        console.warn(
+          `Duplicate alias "${alias}" detected while converting tweaks; falling back to nodeId ${node.id}.`,
+        );
+        aliasedTweaks[node.id] = tweakValues;
+      } else {
+        aliasedTweaks[alias] = tweakValues;
+      }
     } else {
       // Fallback to original nodeId if node not found
       aliasedTweaks[nodeId] = tweakValues;
     }
   }
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 46e58c9 and d952ca5.

📒 Files selected for processing (21)
  • src/frontend/src/CustomNodes/GenericNode/components/NodeName/index.tsx (5 hunks)
  • src/frontend/src/components/core/codeTabsComponent/components/tweakComponent/index.tsx (2 hunks)
  • src/frontend/src/hooks/use-add-component.ts (2 hunks)
  • src/frontend/src/modals/apiModal/codeTabs/code-tabs.tsx (1 hunks)
  • src/frontend/src/modals/apiModal/utils/get-curl-code.tsx (8 hunks)
  • src/frontend/src/modals/apiModal/utils/get-js-api-code.tsx (3 hunks)
  • src/frontend/src/modals/apiModal/utils/get-python-api-code.tsx (2 hunks)
  • src/frontend/src/pages/FlowPage/components/flowSidebarComponent/index.tsx (0 hunks)
  • src/frontend/src/stores/flowStore.ts (3 hunks)
  • src/frontend/src/types/api/index.ts (1 hunks)
  • src/frontend/src/types/flow/index.ts (1 hunks)
  • src/frontend/src/utils/__tests__/aliasUtils.test.ts (1 hunks)
  • src/frontend/src/utils/aliasUtils.ts (1 hunks)
  • src/lfx/src/lfx/custom/custom_component/component.py (3 hunks)
  • src/lfx/src/lfx/graph/graph/base.py (2 hunks)
  • src/lfx/src/lfx/processing/process.py (2 hunks)
  • src/lfx/src/lfx/schema/graph.py (2 hunks)
  • src/lfx/src/lfx/template/frontend_node/base.py (1 hunks)
  • src/lfx/tests/unit/graph/graph/test_base.py (1 hunks)
  • src/lfx/tests/unit/processing/__init__.py (1 hunks)
  • src/lfx/tests/unit/processing/test_process.py (1 hunks)
💤 Files with no reviewable changes (1)
  • src/frontend/src/pages/FlowPage/components/flowSidebarComponent/index.tsx
🧰 Additional context used
📓 Path-based instructions (10)
src/frontend/src/**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (.cursor/rules/frontend_development.mdc)

src/frontend/src/**/*.{ts,tsx,js,jsx}: All frontend TypeScript and JavaScript code should be located under src/frontend/src/ and organized into components, pages, icons, stores, types, utils, hooks, services, and assets directories as per the specified directory layout.
Use React 18 with TypeScript for all UI components in the frontend.
Format all TypeScript and JavaScript code using the make format_frontend command.
Lint all TypeScript and JavaScript code using the make lint command.

Files:

  • src/frontend/src/types/flow/index.ts
  • src/frontend/src/types/api/index.ts
  • src/frontend/src/components/core/codeTabsComponent/components/tweakComponent/index.tsx
  • src/frontend/src/utils/aliasUtils.ts
  • src/frontend/src/stores/flowStore.ts
  • src/frontend/src/utils/__tests__/aliasUtils.test.ts
  • src/frontend/src/modals/apiModal/utils/get-curl-code.tsx
  • src/frontend/src/modals/apiModal/utils/get-python-api-code.tsx
  • src/frontend/src/modals/apiModal/utils/get-js-api-code.tsx
  • src/frontend/src/hooks/use-add-component.ts
  • src/frontend/src/modals/apiModal/codeTabs/code-tabs.tsx
  • src/frontend/src/CustomNodes/GenericNode/components/NodeName/index.tsx
src/frontend/src/types/**/*.{ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/frontend_development.mdc)

All TypeScript type definitions should be placed in the types directory.

Files:

  • src/frontend/src/types/flow/index.ts
  • src/frontend/src/types/api/index.ts
src/frontend/src/components/**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (.cursor/rules/frontend_development.mdc)

All components should be styled using Tailwind CSS utility classes.

Files:

  • src/frontend/src/components/core/codeTabsComponent/components/tweakComponent/index.tsx
src/frontend/src/@(components|hooks)/**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (.cursor/rules/frontend_development.mdc)

Implement dark mode support in components and hooks where needed.

Files:

  • src/frontend/src/components/core/codeTabsComponent/components/tweakComponent/index.tsx
  • src/frontend/src/hooks/use-add-component.ts
src/frontend/src/utils/**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (.cursor/rules/frontend_development.mdc)

All utility functions should be placed in the utils directory.

Files:

  • src/frontend/src/utils/aliasUtils.ts
  • src/frontend/src/utils/__tests__/aliasUtils.test.ts
src/frontend/src/stores/**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (.cursor/rules/frontend_development.mdc)

Use Zustand for state management in frontend stores.

Files:

  • src/frontend/src/stores/flowStore.ts
src/frontend/src/**/__tests__/**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (.cursor/rules/frontend_development.mdc)

All frontend code should be tested using appropriate component and integration tests.

Files:

  • src/frontend/src/utils/__tests__/aliasUtils.test.ts
src/frontend/**/*.@(test|spec).{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (.cursor/rules/testing.mdc)

src/frontend/**/*.@(test|spec).{ts,tsx,js,jsx}: Frontend test files should be located in 'src/frontend/' and use '.test.{ts,tsx,js,jsx}' or '.spec.{ts,tsx,js,jsx}' extensions.
Test both sync and async code paths in frontend test files.
Mock external dependencies appropriately in frontend test files to isolate unit tests from external services.
Test error handling and edge cases in frontend test files.
Validate input/output behavior and test component initialization and configuration in frontend test files.
Each frontend test should have a clear description or comment explaining its purpose, especially for complex setups or mocks.

Files:

  • src/frontend/src/utils/__tests__/aliasUtils.test.ts
src/frontend/src/@(hooks|services)/**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (.cursor/rules/frontend_development.mdc)

All API calls must implement proper error handling, such as using try/catch and setting error state.

Files:

  • src/frontend/src/hooks/use-add-component.ts
src/frontend/src/hooks/**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (.cursor/rules/frontend_development.mdc)

All custom React hooks should be placed in the hooks directory.

Files:

  • src/frontend/src/hooks/use-add-component.ts
🧠 Learnings (6)
📚 Learning: 2025-06-23T12:46:42.048Z
Learnt from: CR
PR: langflow-ai/langflow#0
File: .cursor/rules/frontend_development.mdc:0-0
Timestamp: 2025-06-23T12:46:42.048Z
Learning: Custom React Flow node types should be implemented as memoized components, using Handle components for connection points and supporting optional icons and labels.

Applied to files:

  • src/frontend/src/components/core/codeTabsComponent/components/tweakComponent/index.tsx
  • src/frontend/src/stores/flowStore.ts
  • src/frontend/src/hooks/use-add-component.ts
  • src/frontend/src/CustomNodes/GenericNode/components/NodeName/index.tsx
📚 Learning: 2025-07-18T18:27:12.609Z
Learnt from: CR
PR: langflow-ai/langflow#0
File: .cursor/rules/frontend_development.mdc:0-0
Timestamp: 2025-07-18T18:27:12.609Z
Learning: Applies to src/frontend/src/components/**/@(FlowGraph|nodes)/**/*.{ts,tsx,js,jsx} : Use React Flow for flow graph visualization components.

Applied to files:

  • src/frontend/src/stores/flowStore.ts
  • src/frontend/src/hooks/use-add-component.ts
📚 Learning: 2025-06-23T12:46:42.048Z
Learnt from: CR
PR: langflow-ai/langflow#0
File: .cursor/rules/frontend_development.mdc:0-0
Timestamp: 2025-06-23T12:46:42.048Z
Learning: React Flow should be used for flow graph visualization, with nodes and edges passed as props, and changes handled via onNodesChange and onEdgesChange callbacks.

Applied to files:

  • src/frontend/src/stores/flowStore.ts
📚 Learning: 2025-07-21T14:16:14.125Z
Learnt from: CR
PR: langflow-ai/langflow#0
File: .cursor/rules/testing.mdc:0-0
Timestamp: 2025-07-21T14:16:14.125Z
Learning: Applies to src/frontend/**/*.@(test|spec).{ts,tsx,js,jsx} : Mock external dependencies appropriately in frontend test files to isolate unit tests from external services.

Applied to files:

  • src/frontend/src/utils/__tests__/aliasUtils.test.ts
📚 Learning: 2025-07-21T14:16:14.125Z
Learnt from: CR
PR: langflow-ai/langflow#0
File: .cursor/rules/testing.mdc:0-0
Timestamp: 2025-07-21T14:16:14.125Z
Learning: Applies to src/frontend/**/*.@(test|spec).{ts,tsx,js,jsx} : Each frontend test should have a clear description or comment explaining its purpose, especially for complex setups or mocks.

Applied to files:

  • src/frontend/src/utils/__tests__/aliasUtils.test.ts
📚 Learning: 2025-07-11T22:12:46.255Z
Learnt from: namastex888
PR: langflow-ai/langflow#9018
File: src/frontend/src/modals/apiModal/codeTabs/code-tabs.tsx:244-244
Timestamp: 2025-07-11T22:12:46.255Z
Learning: In src/frontend/src/modals/apiModal/codeTabs/code-tabs.tsx, the inconsistent showLineNumbers setting between Step 1 (false) and Step 2 (true) in the API modal is intentional to prevent breaking the modal height. Step 1 uses showLineNumbers={false} to save vertical space while Step 2 uses showLineNumbers={true} for better readability of longer code.

Applied to files:

  • src/frontend/src/modals/apiModal/codeTabs/code-tabs.tsx
🧬 Code graph analysis (10)
src/frontend/src/components/core/codeTabsComponent/components/tweakComponent/index.tsx (1)
src/frontend/src/types/flow/index.ts (1)
  • getEffectiveAliasFromAnyNode (90-96)
src/frontend/src/utils/aliasUtils.ts (1)
src/frontend/src/types/flow/index.ts (3)
  • GenericNodeType (39-39)
  • AllNodeType (42-42)
  • getEffectiveAliasFromAnyNode (90-96)
src/frontend/src/stores/flowStore.ts (1)
src/frontend/src/utils/aliasUtils.ts (3)
  • needsMigration (326-368)
  • migrateExistingFlow (240-321)
  • assignAliasToNewComponent (117-141)
src/lfx/tests/unit/graph/graph/test_base.py (1)
src/lfx/src/lfx/graph/graph/base.py (2)
  • Graph (57-2281)
  • dump (224-251)
src/frontend/src/utils/__tests__/aliasUtils.test.ts (2)
src/frontend/src/types/flow/index.ts (2)
  • GenericNodeType (39-39)
  • AllNodeType (42-42)
src/frontend/src/utils/aliasUtils.ts (11)
  • hasNumberSuffix (20-23)
  • isSafeToModifyAlias (29-36)
  • generateAutoAlias (41-71)
  • assignAliasToNewComponent (117-141)
  • migrateExistingFlow (240-321)
  • needsMigration (326-368)
  • renumberAutoGeneratedAliases (76-112)
  • setUserDefinedAlias (183-202)
  • clearUserDefinedAlias (207-234)
  • updateAliasesForDisplayNameChange (146-178)
  • convertTweaksToAliases (380-397)
src/frontend/src/modals/apiModal/utils/get-curl-code.tsx (3)
src/frontend/src/utils/aliasUtils.ts (1)
  • convertTweaksToAliases (380-397)
src/frontend/src/modals/apiModal/utils/detect-file-tweaks.ts (4)
  • hasFileTweaks (2-18)
  • getAllChatInputNodeIds (61-72)
  • getAllFileNodeIds (75-92)
  • getNonFileTypeTweaks (95-117)
src/frontend/src/types/flow/index.ts (1)
  • getEffectiveAliasFromAnyNode (90-96)
src/frontend/src/modals/apiModal/utils/get-python-api-code.tsx (3)
src/frontend/src/customization/utils/custom-get-host-protocol.ts (1)
  • customGetHostProtocol (1-6)
src/frontend/src/utils/aliasUtils.ts (1)
  • convertTweaksToAliases (380-397)
src/frontend/src/modals/apiModal/utils/detect-file-tweaks.ts (1)
  • hasFileTweaks (2-18)
src/frontend/src/modals/apiModal/utils/get-js-api-code.tsx (2)
src/frontend/src/utils/aliasUtils.ts (1)
  • convertTweaksToAliases (380-397)
src/frontend/src/modals/apiModal/utils/detect-file-tweaks.ts (1)
  • hasFileTweaks (2-18)
src/frontend/src/hooks/use-add-component.ts (1)
src/frontend/src/utils/aliasUtils.ts (1)
  • assignAliasToNewComponent (117-141)
src/frontend/src/CustomNodes/GenericNode/components/NodeName/index.tsx (2)
src/frontend/src/types/flow/index.ts (1)
  • getEffectiveAliasFromAnyNode (90-96)
src/frontend/src/utils/aliasUtils.ts (1)
  • updateAliasesForDisplayNameChange (146-178)
🪛 ast-grep (0.38.6)
src/frontend/src/utils/aliasUtils.ts

[warning] 86-88: Regular expression constructed from variable input detected. This can lead to Regular Expression Denial of Service (ReDoS) attacks if the variable contains malicious patterns. Use libraries like 'recheck' to validate regex safety or use static patterns.
Context: new RegExp(
^${removedDisplayName.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}#\\d+$,
)
Note: [CWE-1333] Inefficient Regular Expression Complexity [REFERENCES]
- https://owasp.org/www-community/attacks/Regular_expression_Denial_of_Service_-_ReDoS
- https://cwe.mitre.org/data/definitions/1333.html

(regexp-from-variable)


[warning] 269-271: Regular expression constructed from variable input detected. This can lead to Regular Expression Denial of Service (ReDoS) attacks if the variable contains malicious patterns. Use libraries like 'recheck' to validate regex safety or use static patterns.
Context: new RegExp(
^${displayName.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}#(\\d+)$,
)
Note: [CWE-1333] Inefficient Regular Expression Complexity [REFERENCES]
- https://owasp.org/www-community/attacks/Regular_expression_Denial_of_Service_-_ReDoS
- https://cwe.mitre.org/data/definitions/1333.html

(regexp-from-variable)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: Run Frontend Tests / Determine Test Suites and Shard Distribution
  • GitHub Check: Test Starter Templates
🔇 Additional comments (24)
src/lfx/src/lfx/schema/graph.py (1)

22-23: Clarify resolution order and ambiguity handling for keys (IDs vs aliases vs display names).
Please verify the actual resolution behavior and update the docstring to specify:

  • handling of duplicate display names
  • precedence among node ID > alias > display name
  • case sensitivity in alias/display-name matching
src/lfx/src/lfx/processing/process.py (2)

201-207: Docstring update aligns with alias/display-name support.

Clear and matches behavior.


255-266: Conflict detection is excellent.

Good immediate error with clear message when the same node is targeted by multiple keys.

src/lfx/tests/unit/processing/__init__.py (1)

1-1: LGTM — module docstring added.
Clear and harmless addition.

src/lfx/src/lfx/template/frontend_node/base.py (1)

110-115: Confirm serialization behavior for alias.
to_dict uses exclude_none=True (alias omitted if None) but model_serializer path returns all fields as-is. Ensure callers don’t rely on alias always being present.

src/frontend/src/components/core/codeTabsComponent/components/tweakComponent/index.tsx (1)

39-47: Nice UX touch with the alias badge.
Concise, non-intrusive, and consistent with the rest of the UI.

src/frontend/src/stores/flowStore.ts (2)

20-24: Alias utilities wired correctly.
Imports are minimal and scoped; good separation of concerns.


492-496: Alias assignment on paste looks correct; verifies 2nd-of-type case.
This covers promoting the first-of-type to #1 when a sibling is pasted. Please ensure tests include pasting multiple identical components in one operation.

src/lfx/tests/unit/processing/test_process.py (1)

11-38: Good coverage of user-defined alias path

Test validates alias_is_user_defined precedence and correct tweak application. Looks solid.

src/frontend/src/CustomNodes/GenericNode/components/NodeName/index.tsx (2)

32-37: Alias badge derivation looks good

Clean derivation via getEffectiveAliasFromAnyNode and safe parsing of the numeric suffix.


124-130: Nice UX touch

Alias tooltip/badge is compact and informative; respects theming tokens.

src/lfx/tests/unit/graph/graph/test_base.py (3)

117-166: Solid coverage for duplicate vs single-component aliasing

These tests validate baseline behaviors (duplicates get numbered aliases; singles stay unaliased). LGTM.


168-210: Good preservation checks

Verifies both custom aliases and existing numbered aliases are preserved through dump(). Looks correct.


268-326: Great edge-case tests

Removing unnecessary "#1" for singles and preserving custom single aliases both look good.

src/frontend/src/modals/apiModal/utils/get-js-api-code.tsx (3)

36-46: Good: alias conversion and file-detection separation

Using originalTweaks for hasFileTweaks avoids false negatives after alias conversion. Nice.


210-317: End-to-end flow code generation looks consistent

The multipart steps and final execute payload incorporate aliased tweaks correctly. Assuming the verification above passes, this is in good shape.


81-99: Verify helpers inspect tweak values, not keys
getAllChatInputNodeIds and getAllFileNodeIds must determine file inputs by examining properties on each tweak object (e.g. files, path, file_path), rather than assuming the tweak key is a node ID post-alias conversion. Confirm their implementations reflect this.

src/frontend/src/modals/apiModal/utils/get-curl-code.tsx (1)

80-90: Aliased payload flow looks good

Using original tweaks for detection and aliased tweaks for display/payload is consistent and user-friendly.

Also applies to: 138-147, 219-226

src/frontend/src/utils/__tests__/aliasUtils.test.ts (4)

45-118: Solid coverage of detection and auto-aliasing paths

Clear cases for suffix detection and auto-increment/gap-fill look good.


157-199: Great migration and uniqueness checks

Migration preserves user-defined aliases and ensures stable numbering; uniqueness assertions are valuable.

Also applies to: 430-497


319-347: Good negative-path testing with console.warn spy

Captures protected-alias scenarios properly.


384-418: Tweaks-to-alias conversion tests validate the new codegen dependencies

Fallback behavior and empty-tweaks handling are well covered.

src/frontend/src/utils/aliasUtils.ts (2)

326-368: Minor: skip building alias arrays for non-generic nodes

Guard is already present; nothing to change here.


399-406: LGTM on helper

Filtering by display_name is correct and type-safe with the type guard.

Comment on lines +30 to +34
// Get alias info for badge display
const effectiveAlias = getEffectiveAliasFromAnyNode(node);
const aliasNumber = effectiveAlias?.match(/#(\d+)$/)?.[1];
const displayName = node.data.node?.display_name || "";

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Derive badge number from the actual alias field to avoid false positives.
Using effectiveAlias risks matching trailing numbers in display_name for non-generic nodes. Read from node.data.node.alias instead.

-  const effectiveAlias = getEffectiveAliasFromAnyNode(node);
-  const aliasNumber = effectiveAlias?.match(/#(\d+)$/)?.[1];
-  const displayName = node.data.node?.display_name || "";
+  const effectiveAlias = getEffectiveAliasFromAnyNode(node);
+  const rawAlias =
+    node.type === "genericNode" ? node.data.node?.alias ?? "" : "";
+  const aliasNumber = rawAlias.match(/#(\d+)$/)?.[1];
+  const displayName = node.data.node?.display_name || "";
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
// Get alias info for badge display
const effectiveAlias = getEffectiveAliasFromAnyNode(node);
const aliasNumber = effectiveAlias?.match(/#(\d+)$/)?.[1];
const displayName = node.data.node?.display_name || "";
// Get alias info for badge display
const effectiveAlias = getEffectiveAliasFromAnyNode(node);
const rawAlias =
node.type === "genericNode" ? node.data.node?.alias ?? "" : "";
const aliasNumber = rawAlias.match(/#(\d+)$/)?.[1];
const displayName = node.data.node?.display_name || "";
🤖 Prompt for AI Agents
In
src/frontend/src/components/core/codeTabsComponent/components/tweakComponent/index.tsx
around lines 30 to 34, the badge number is currently derived from effectiveAlias
which can accidentally match numbers in display_name; instead read the alias
directly from node.data.node?.alias, safely extract the badge number with
alias?.match(/#(\d+)$/)?.[1] (handle undefined/null), and keep displayName as
node.data.node?.display_name || ""; replace the effectiveAlias usage with this
direct alias extraction to avoid false positives.

Comment on lines +67 to +77
// Update aliases when display name changes
if (oldDisplayName && newDisplayName !== oldDisplayName) {
const allNodes = useFlowStore.getState().nodes;
const updatedNodes = updateAliasesForDisplayNameChange(
nodeId,
oldDisplayName,
newDisplayName,
allNodes,
);
useFlowStore.getState().setNodes(updatedNodes);
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Verification agent

🧩 Analysis chain

Verify alias update produces a new nodes array

If updateAliasesForDisplayNameChange mutates and returns the same array reference, some subscribers may not re-render. Ensure it returns a new array instance.

Run:

Expected: It returns a new array (e.g., return [...allNodes]). If it returns allNodes directly, consider cloning before setNodes.


🏁 Script executed:

#!/bin/bash
# Inspect implementation to confirm immutable return
rg -nC2 'export function updateAliasesForDisplayNameChange' src/frontend/src/utils/aliasUtils.ts
rg -n 'return\s+\[\.{3}allNodes\]|\ballNodes\.slice\(\)|return\s+allNodes;' src/frontend/src/utils/aliasUtils.ts

Length of output: 262


Ensure updateAliasesForDisplayNameChange always returns a new array instance
In src/frontend/src/utils/aliasUtils.ts the function currently does:

  • Early-return at line 153: return allNodes;
  • Final return at line 177: return allNodes;

Both return the original array reference, which can prevent subscribers from re-rendering. Change these to return a clone (e.g. return [...allNodes]) or otherwise produce a new array before calling setNodes.

🤖 Prompt for AI Agents
In src/frontend/src/CustomNodes/GenericNode/components/NodeName/index.tsx around
lines 67 to 77, the call site uses updateAliasesForDisplayNameChange but that
utility sometimes returns the original allNodes array reference (early return at
line 153 and final return at line 177 in src/frontend/src/utils/aliasUtils.ts),
which prevents subscribers from detecting changes; update the utility so that
every return produces a new array instance (for example clone before returning
with [...allNodes] or map/other immutable update) so setNodes receives a new
reference and triggers re-renders.

Comment on lines +68 to +90
// Generate alias for the new component and update existing ones if needed
assignAliasToNewComponent(newNode, nodes);

// Check if we need to update existing nodes (when adding second component of same type)
const displayName = newNode.data.node.display_name;
const sameTypeNodes = nodes.filter(
(n) =>
n.type === "genericNode" && n.data.node.display_name === displayName,
);

if (sameTypeNodes.length === 1 && !sameTypeNodes[0].data.node.alias) {
// This is the second component - need to assign aliases to both
const firstNode = sameTypeNodes[0];
if (firstNode.type === "genericNode") {
firstNode.data.node.alias = `${displayName}#1`;
}

// Update the first node in the store
setNodes((currentNodes) =>
currentNodes.map((n) => (n.id === firstNode.id ? firstNode : n)),
);
}

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Fix: store mutation via utility is not persisted; reorder logic to ensure setNodes runs

assignAliasToNewComponent mutates the existing nodes array by reference. Because the zustand store isn’t updated unless setNodes is called, your subsequent condition can be skipped (alias already set in-memory), leading to no store update and missing re-render. Compute the “second-of-its-type” condition before calling the utility, then always persist the first node’s alias immutably when needed.

Apply:

-      // Generate alias for the new component and update existing ones if needed
-      assignAliasToNewComponent(newNode, nodes);
-
-      // Check if we need to update existing nodes (when adding second component of same type)
-      const displayName = newNode.data.node.display_name;
-      const sameTypeNodes = nodes.filter(
-        (n) =>
-          n.type === "genericNode" && n.data.node.display_name === displayName,
-      );
-
-      if (sameTypeNodes.length === 1 && !sameTypeNodes[0].data.node.alias) {
-        // This is the second component - need to assign aliases to both
-        const firstNode = sameTypeNodes[0];
-        if (firstNode.type === "genericNode") {
-          firstNode.data.node.alias = `${displayName}#1`;
-        }
-
-        // Update the first node in the store
-        setNodes((currentNodes) =>
-          currentNodes.map((n) => (n.id === firstNode.id ? firstNode : n)),
-        );
-      }
+      // Precompute if we are adding the second component of this display_name with no existing alias
+      const displayName = newNode.data.node.display_name;
+      const sameTypeNodesBefore = nodes.filter(
+        (n) => n.type === "genericNode" && n.data.node.display_name === displayName,
+      );
+      const needsPromoteFirst =
+        sameTypeNodesBefore.length === 1 &&
+        !sameTypeNodesBefore[0].data.node.alias;
+
+      // Assign alias to the new node (the utility will also compute target aliases)
+      assignAliasToNewComponent(newNode, nodes);
+
+      // Persist alias on the first existing node immutably so subscribers re-render
+      if (needsPromoteFirst) {
+        const firstId = sameTypeNodesBefore[0].id;
+        setNodes((currentNodes) =>
+          currentNodes.map((n) =>
+            n.id === firstId && n.type === "genericNode"
+              ? {
+                  ...n,
+                  data: {
+                    ...n.data,
+                    node: {
+                      ...n.data.node,
+                      alias: `${displayName}#1`,
+                    },
+                  },
+                }
+              : n,
+          ),
+        );
+      }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
// Generate alias for the new component and update existing ones if needed
assignAliasToNewComponent(newNode, nodes);
// Check if we need to update existing nodes (when adding second component of same type)
const displayName = newNode.data.node.display_name;
const sameTypeNodes = nodes.filter(
(n) =>
n.type === "genericNode" && n.data.node.display_name === displayName,
);
if (sameTypeNodes.length === 1 && !sameTypeNodes[0].data.node.alias) {
// This is the second component - need to assign aliases to both
const firstNode = sameTypeNodes[0];
if (firstNode.type === "genericNode") {
firstNode.data.node.alias = `${displayName}#1`;
}
// Update the first node in the store
setNodes((currentNodes) =>
currentNodes.map((n) => (n.id === firstNode.id ? firstNode : n)),
);
}
// Precompute if we are adding the second component of this display_name with no existing alias
const displayName = newNode.data.node.display_name;
const sameTypeNodesBefore = nodes.filter(
(n) => n.type === "genericNode" && n.data.node.display_name === displayName,
);
const needsPromoteFirst =
sameTypeNodesBefore.length === 1 &&
!sameTypeNodesBefore[0].data.node.alias;
// Assign alias to the new node (the utility will also compute target aliases)
assignAliasToNewComponent(newNode, nodes);
// Persist alias on the first existing node immutably so subscribers re-render
if (needsPromoteFirst) {
const firstId = sameTypeNodesBefore[0].id;
setNodes((currentNodes) =>
currentNodes.map((n) =>
n.id === firstId && n.type === "genericNode"
? {
...n,
data: {
...n.data,
node: {
...n.data.node,
alias: `${displayName}#1`,
},
},
}
: n,
),
);
}

Comment on lines +166 to +169
// Get alias for this node
const node = nodes?.find((n) => n.id === nodeId);
const alias = node ? getEffectiveAliasFromAnyNode(node) : nodeId;

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Missing import for getEffectiveAliasFromAnyNode causes build error

Function is used but not imported.

Apply:

 import { GetCodeType } from "@/types/tweaks";
+import { getEffectiveAliasFromAnyNode } from "@/types/flow";

Also applies to: 199-202

🤖 Prompt for AI Agents
In src/frontend/src/modals/apiModal/utils/get-curl-code.tsx around lines 166-169
(and also at 199-202) the function getEffectiveAliasFromAnyNode is used but not
imported; add a named import for getEffectiveAliasFromAnyNode from the module
that exports it at the top of the file (the same module where other node-alias
helper functions are exported), then rebuild to confirm the missing-import error
is resolved.

Comment on lines +29 to +36
export function isSafeToModifyAlias(node: GenericNodeType): boolean {
const alias = node.data.node.alias;

if (!alias) return true; // No alias, safe to add one

// Can only modify if it doesn't have a number suffix
return !hasNumberSuffix(alias);
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Fix inverted mutability check (breaks migration and single-node cleanup)

Currently, numbered aliases are treated as “protected” from modification, blocking renumbering and migration. We must allow modifying auto-generated (“DisplayName#n”) aliases while preserving user-defined ones.

 export function isSafeToModifyAlias(node: GenericNodeType): boolean {
-  const alias = node.data.node.alias;
-
-  if (!alias) return true; // No alias, safe to add one
-
-  // Can only modify if it doesn't have a number suffix
-  return !hasNumberSuffix(alias);
+  const alias = node.data.node.alias;
+  const displayName = node.data.node.display_name;
+  // Safe to modify if there's no alias or if it's auto-generated for this display name.
+  return !alias || isAutoGeneratedAliasForDisplayName(alias, displayName);
 }

Note: Add helper isAutoGeneratedAliasForDisplayName (see separate diff below).

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
In src/frontend/src/utils/aliasUtils.ts around lines 29 to 36, the current check
treats numbered aliases as immutable; instead, allow modification when the alias
is an auto-generated "DisplayName#n". Change the logic so that if there's no
alias return true, otherwise return true if
isAutoGeneratedAliasForDisplayName(alias, node.data.node.displayName) is true,
otherwise fall back to the existing !hasNumberSuffix(alias) check; add and call
the helper isAutoGeneratedAliasForDisplayName as described in the review.

Comment on lines +370 to +375
// Helper functions
function extractNumber(alias: string | undefined): number {
if (!alias) return 0;
const match = alias.match(/^.+#(\d+)$/);
return match ? parseInt(match[1]) : 0;
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Introduce helper: avoid dynamic regex and encode intent

Centralize auto-generated pattern detection and avoid constructing regex from variable input.

 // Helper functions
-function extractNumber(alias: string | undefined): number {
+function isAutoGeneratedAliasForDisplayName(alias: string, displayName: string): boolean {
+  const prefix = `${displayName}#`;
+  return alias.startsWith(prefix) && /^\d+$/.test(alias.slice(prefix.length));
+}
+
+function extractNumber(alias: string | undefined): number {
   if (!alias) return 0;
   const match = alias.match(/^.+#(\d+)$/);
   return match ? parseInt(match[1]) : 0;
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
// Helper functions
function extractNumber(alias: string | undefined): number {
if (!alias) return 0;
const match = alias.match(/^.+#(\d+)$/);
return match ? parseInt(match[1]) : 0;
}
// Helper functions
function isAutoGeneratedAliasForDisplayName(alias: string, displayName: string): boolean {
const prefix = `${displayName}#`;
return alias.startsWith(prefix) && /^\d+$/.test(alias.slice(prefix.length));
}
function extractNumber(alias: string | undefined): number {
if (!alias) return 0;
const match = alias.match(/^.+#(\d+)$/);
return match ? parseInt(match[1]) : 0;
}

Comment on lines +253 to +291
def _add_dynamic_aliases(self, data_dict: dict) -> dict:
"""Calculate and assign dynamic aliases to duplicate components in graph data."""
nodes = data_dict.get("nodes", [])

# Group nodes by display_name to identify duplicates
display_name_groups: dict[str, list] = {}

for node in nodes:
node_info = node.get("data", {}).get("node", {})
display_name = node_info.get("display_name")

if display_name:
if display_name not in display_name_groups:
display_name_groups[display_name] = []
display_name_groups[display_name].append(node)

# Assign aliases to groups with multiple components
for display_name, group_nodes in display_name_groups.items():
if len(group_nodes) > 1:
# Multiple components - assign numbered aliases
for index, node in enumerate(group_nodes):
node_info = node.get("data", {}).get("node", {})
if node_info:
# Only assign if no existing alias (preserve all existing aliases)
current_alias = node_info.get("alias")

if not current_alias:
expected_alias = f"{display_name}#{index + 1}"
node_info["alias"] = expected_alias
else:
# Single component - ensure no alias
node_info = group_nodes[0].get("data", {}).get("node", {})
if node_info and node_info.get("alias", "").endswith("#1"):
# Remove #1 suffix if it's the only component of its type
alias = node_info.get("alias", "")
if alias == f"{display_name}#1":
node_info["alias"] = None

return data_dict
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Ensure alias uniqueness and fill gaps when some numbered aliases already exist

Current logic may assign duplicate numbered aliases when some items in a group already have valid numbered aliases (e.g., an existing "Chat Input#3" plus new assignments can collide). Track used numbers per display_name and assign the next available number to guarantee uniqueness and stable, gap-filling behavior.

-        # Assign aliases to groups with multiple components
-        for display_name, group_nodes in display_name_groups.items():
-            if len(group_nodes) > 1:
-                # Multiple components - assign numbered aliases
-                for index, node in enumerate(group_nodes):
-                    node_info = node.get("data", {}).get("node", {})
-                    if node_info:
-                        # Only assign if no existing alias (preserve all existing aliases)
-                        current_alias = node_info.get("alias")
-
-                        if not current_alias:
-                            expected_alias = f"{display_name}#{index + 1}"
-                            node_info["alias"] = expected_alias
-            else:
-                # Single component - ensure no alias
-                node_info = group_nodes[0].get("data", {}).get("node", {})
-                if node_info and node_info.get("alias", "").endswith("#1"):
-                    # Remove #1 suffix if it's the only component of its type
-                    alias = node_info.get("alias", "")
-                    if alias == f"{display_name}#1":
-                        node_info["alias"] = None
+        # Assign aliases to groups with multiple components
+        for display_name, group_nodes in display_name_groups.items():
+            if len(group_nodes) > 1:
+                # Collect already used numbers from valid "<DisplayName>#<n>" aliases
+                used_numbers: set[int] = set()
+                for node in group_nodes:
+                    alias = node.get("data", {}).get("node", {}).get("alias")
+                    if isinstance(alias, str) and alias.startswith(f"{display_name}#"):
+                        try:
+                            used_numbers.add(int(alias.split("#")[-1]))
+                        except ValueError:
+                            # ignore non-numeric suffixes (custom aliases)
+                            pass
+
+                # Assign the smallest available numbers to items without an alias
+                next_n = 1
+                for node in group_nodes:
+                    node_info = node.get("data", {}).get("node", {})
+                    if not node_info or node_info.get("alias"):
+                        continue
+                    while next_n in used_numbers:
+                        next_n += 1
+                    node_info["alias"] = f"{display_name}#{next_n}"
+                    used_numbers.add(next_n)
+                    next_n += 1
+            else:
+                # Single component - remove trailing "#1" only if exactly "<DisplayName>#1"
+                node_info = group_nodes[0].get("data", {}).get("node", {})
+                alias = node_info.get("alias") if node_info else None
+                if isinstance(alias, str) and alias == f"{display_name}#1":
+                    node_info["alias"] = None
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
def _add_dynamic_aliases(self, data_dict: dict) -> dict:
"""Calculate and assign dynamic aliases to duplicate components in graph data."""
nodes = data_dict.get("nodes", [])
# Group nodes by display_name to identify duplicates
display_name_groups: dict[str, list] = {}
for node in nodes:
node_info = node.get("data", {}).get("node", {})
display_name = node_info.get("display_name")
if display_name:
if display_name not in display_name_groups:
display_name_groups[display_name] = []
display_name_groups[display_name].append(node)
# Assign aliases to groups with multiple components
for display_name, group_nodes in display_name_groups.items():
if len(group_nodes) > 1:
# Multiple components - assign numbered aliases
for index, node in enumerate(group_nodes):
node_info = node.get("data", {}).get("node", {})
if node_info:
# Only assign if no existing alias (preserve all existing aliases)
current_alias = node_info.get("alias")
if not current_alias:
expected_alias = f"{display_name}#{index + 1}"
node_info["alias"] = expected_alias
else:
# Single component - ensure no alias
node_info = group_nodes[0].get("data", {}).get("node", {})
if node_info and node_info.get("alias", "").endswith("#1"):
# Remove #1 suffix if it's the only component of its type
alias = node_info.get("alias", "")
if alias == f"{display_name}#1":
node_info["alias"] = None
return data_dict
def _add_dynamic_aliases(self, data_dict: dict) -> dict:
"""Calculate and assign dynamic aliases to duplicate components in graph data."""
nodes = data_dict.get("nodes", [])
# Group nodes by display_name to identify duplicates
display_name_groups: dict[str, list] = {}
for node in nodes:
node_info = node.get("data", {}).get("node", {})
display_name = node_info.get("display_name")
if display_name:
if display_name not in display_name_groups:
display_name_groups[display_name] = []
display_name_groups[display_name].append(node)
# Assign aliases to groups with multiple components
for display_name, group_nodes in display_name_groups.items():
if len(group_nodes) > 1:
# Collect already used numbers from valid "<DisplayName>#<n>" aliases
used_numbers: set[int] = set()
for node in group_nodes:
alias = node.get("data", {}).get("node", {}).get("alias")
if isinstance(alias, str) and alias.startswith(f"{display_name}#"):
try:
used_numbers.add(int(alias.split("#")[-1]))
except ValueError:
# ignore non-numeric suffixes (custom aliases)
pass
# Assign the smallest available numbers to items without an alias
next_n = 1
for node in group_nodes:
node_info = node.get("data", {}).get("node", {})
if not node_info or node_info.get("alias"):
continue
while next_n in used_numbers:
next_n += 1
node_info["alias"] = f"{display_name}#{next_n}"
used_numbers.add(next_n)
next_n += 1
else:
# Single component - remove trailing "#1" only if exactly "<DisplayName>#1"
node_info = group_nodes[0].get("data", {}).get("node", {})
alias = node_info.get("alias") if node_info else None
if isinstance(alias, str) and alias == f"{display_name}#1":
node_info["alias"] = None
return data_dict
🤖 Prompt for AI Agents
In src/lfx/src/lfx/graph/graph/base.py around lines 253 to 291, the alias
assignment can produce duplicate numbered aliases when some nodes already have
numbered aliases; update the logic to first collect existing numbered aliases
per display_name (parse node_info.get("alias") with a regex like r"^(.*)#(\d+)$"
to capture the number), build a set of used integers for that display_name, and
when assigning aliases iterate from 1 upward to pick the smallest unused
positive integer (so you fill gaps and avoid collisions) rather than using the
enumeration index; preserve any existing non-empty alias values, update only
nodes without alias, and keep the single-component logic (remove the "#1" suffix
only when the alias exactly equals f"{display_name}#1").

Comment on lines +213 to 235
# Create all resolution mappings in a single pass
nodes_map = {}
alias_map = {}
display_name_map = {}

for node in nodes:
node_id = node.get("id")
node_info = node.get("data", {}).get("node", {})

# Map node ID
if node_id:
nodes_map[node_id] = node

# Map aliases (both user-defined and auto-generated)
alias = node_info.get("alias")
if alias:
alias_map[alias] = node

# Map display_name only if no alias is set (for single components)
display_name = node_info.get("display_name")
if display_name and not alias:
display_name_map[display_name] = node

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Detect and fail on duplicate aliases; make display-name mapping unambiguous.

Currently last-write-wins can silently target the wrong node when aliases or display names collide. Enforce alias uniqueness and treat duplicate display names as ambiguous at resolution time.

Apply this diff:

-    nodes_map = {}
-    alias_map = {}
-    display_name_map = {}
+    nodes_map: dict[str, dict] = {}
+    alias_map: dict[str, dict] = {}
+    display_name_map: dict[str, list[dict]] = {}
@@
-        if alias:
-            alias_map[alias] = node
+        if alias:
+            if alias in alias_map and alias_map[alias].get("id") != node_id:
+                raise ValueError(
+                    f"Duplicate alias '{alias}' found for node IDs "
+                    f"{alias_map[alias].get('id')} and {node_id}. "
+                    "Aliases must be unique. Please adjust aliases."
+                )
+            alias_map[alias] = node
@@
-        if display_name and not alias:
-            display_name_map[display_name] = node
+        if display_name and not alias:
+            display_name_map.setdefault(display_name, []).append(node)
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
# Create all resolution mappings in a single pass
nodes_map = {}
alias_map = {}
display_name_map = {}
for node in nodes:
node_id = node.get("id")
node_info = node.get("data", {}).get("node", {})
# Map node ID
if node_id:
nodes_map[node_id] = node
# Map aliases (both user-defined and auto-generated)
alias = node_info.get("alias")
if alias:
alias_map[alias] = node
# Map display_name only if no alias is set (for single components)
display_name = node_info.get("display_name")
if display_name and not alias:
display_name_map[display_name] = node
# Create all resolution mappings in a single pass
nodes_map: dict[str, dict] = {}
alias_map: dict[str, dict] = {}
display_name_map: dict[str, list[dict]] = {}
for node in nodes:
node_id = node.get("id")
node_info = node.get("data", {}).get("node", {})
# Map node ID
if node_id:
nodes_map[node_id] = node
# Map aliases (both user-defined and auto-generated)
alias = node_info.get("alias")
if alias:
if alias in alias_map and alias_map[alias].get("id") != node_id:
raise ValueError(
f"Duplicate alias '{alias}' found for node IDs "
f"{alias_map[alias].get('id')} and {node_id}. "
"Aliases must be unique. Please adjust aliases."
)
alias_map[alias] = node
# Map display_name only if no alias is set (for single components)
display_name = node_info.get("display_name")
if display_name and not alias:
display_name_map.setdefault(display_name, []).append(node)
🤖 Prompt for AI Agents
In src/lfx/src/lfx/processing/process.py around lines 213 to 235, update the
single-pass mapping to detect duplicate aliases and to make display_name
mappings unambiguous: during the loop, if an alias is already present in
alias_map, record the duplicate (or raise a clear error) and fail the processing
so duplicate aliases cannot silently overwrite each other; for display_name, if
a display_name is already present, mark it as ambiguous by removing/unsetting
that key (or storing a sentinel) so it is not treated as a unique mapping later;
keep node_id mapping as-is and ensure the function returns/raises a clear error
listing duplicate aliases (and optionally duplicate display_names) so callers
can surface the failure.

Comment on lines 241 to +251
if isinstance(value, dict):
if (node := nodes_map.get(key)) or (node := nodes_display_name_map.get(key)):
node = None
key_type = ""

if node := nodes_map.get(key):
key_type = "node ID"
elif node := alias_map.get(key):
key_type = "alias"
elif node := display_name_map.get(key):
key_type = "display name"

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Handle ambiguous display names at resolution time.

Avoid silently picking one when multiple nodes share the same display name (and no alias).

Apply this diff:

-            if node := nodes_map.get(key):
+            if node := nodes_map.get(key):
                 key_type = "node ID"
-            elif node := alias_map.get(key):
+            elif node := alias_map.get(key):
                 key_type = "alias"
-            elif node := display_name_map.get(key):
-                key_type = "display name"
+            elif candidates := display_name_map.get(key):
+                if len(candidates) > 1:
+                    ids = ", ".join(n.get("id") or "<unknown>" for n in candidates)
+                    raise ValueError(
+                        f"Ambiguous display name '{key}' refers to multiple components (IDs: {ids}). "
+                        "Set unique aliases or use node IDs."
+                    )
+                node = candidates[0]
+                key_type = "display name"
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if isinstance(value, dict):
if (node := nodes_map.get(key)) or (node := nodes_display_name_map.get(key)):
node = None
key_type = ""
if node := nodes_map.get(key):
key_type = "node ID"
elif node := alias_map.get(key):
key_type = "alias"
elif node := display_name_map.get(key):
key_type = "display name"
if isinstance(value, dict):
node = None
key_type = ""
if node := nodes_map.get(key):
key_type = "node ID"
elif node := alias_map.get(key):
key_type = "alias"
elif candidates := display_name_map.get(key):
if len(candidates) > 1:
ids = ", ".join(n.get("id") or "<unknown>" for n in candidates)
raise ValueError(
f"Ambiguous display name '{key}' refers to multiple components (IDs: {ids}). "
"Set unique aliases or use node IDs."
)
node = candidates[0]
key_type = "display name"
🤖 Prompt for AI Agents
In src/lfx/src/lfx/processing/process.py around lines 241 to 251, the resolution
logic currently silently picks a node when a key matches multiple nodes via
display_name_map; change it to detect ambiguity and fail-fast: when
display_name_map.get(key) returns more than one candidate, raise a clear error
(or return a resolution failure) indicating the display name is ambiguous and
require an alias or unique identifier; otherwise, continue to select the single
matching node. Also avoid using the walrus assignment pattern for clarity and
ensure key_type is set appropriately ("node ID", "alias", or "display name")
only after confirming a single unambiguous match.

Comment on lines +31 to +32
alias: str | None = None
"""Alias of the frontend node for user-friendly referencing."""
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Good addition of alias field — reserve the name in input/output validation.
Alias becomes part of the public surface; prevent template fields from colliding with it.

🤖 Prompt for AI Agents
In src/lfx/src/lfx/template/frontend_node/base.py around lines 31-32, the new
public field "alias" must be reserved so template input/output fields cannot
collide with it; update the template validation logic to reject any input or
output field named "alias" (exact match) by adding a validation check that
raises a clear validation error when a template defines a field with that name,
include the check in both input and output validation paths, and add/adjust unit
tests and error message text accordingly.

@Empreiteiro
Copy link
Collaborator

This idea is great! It will be very helpful when building the input schema.

@philnash
Copy link
Member

I like this as an idea, though I have some questions/issues.

It looks like you can edit aliases, which is good. But I feel like the #N scheme adding on to it is confusing with the tag style display.

I also noticed a bug, when you edit the alias of one component where you already have multiple, then drag a new one to the canvas, the numbers all update, which breaks the consistency.

Monosnap.screencast.2025-09-12.09-36-14.mp4

Also, when you're in the Input Schema, Component#1 and Component#2 don't differentiate between what those components are for, it would be better to encourage users to rename their components so that they can recognise them wherever these aliases appear.

Does this need to include the tagged number system? Could you make the alias the same as the component ID when you first drag it to the canvas, and then encourage the behaviour of renaming components, say by focusing on the name editing field once the component is dropped? This would be good for self documenting purposes and would make the components more recognisable when editing in the Input Schema.

Copy link
Collaborator

@dkaushik94 dkaushik94 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Probably a non-trivial thing to solve.

graph_dict["endpoint_name"] = str(endpoint_name)
return graph_dict

def _add_dynamic_aliases(self, data_dict: dict) -> dict:
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consider the following:
User has the following payload

"tweaks": {
      "chat_input.input": {"text": "Hello"},
      "opensearch#1.input": {"url":"https://www.google1.com"}
      "opensearch#2.input": {"url":"https://www.google1.com"}
      "opensearch#3.input": {"url":"https://www.google2.com"}
}

and is using this in a client to run their workflow.
Another user, ends up deleting the opensearch#2 and adds back the same component again (maybe it was a mistake, or some other reason), the new component will be opensearch#4 but this will be a breaking change for clients because they want to provide the input for the component. This will require the client end to accommodate this component identifier change in their code. We should maybe think about how we can deterministically evaluate component aliases. Fairly important for our Developer APIs and the SDK we are building.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants