Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,10 @@ import {
CustomParameterLabel,
getCustomParameterTitle,
} from "@/customization/components/custom-parameter";
import { LANGFLOW_AGENTIC_EXPERIENCE } from "@/customization/feature-flags";
import {
ENABLE_INSPECTION_PANEL,
LANGFLOW_AGENTIC_EXPERIENCE,
} from "@/customization/feature-flags";
import { useIsAutoLogin } from "@/hooks/use-is-auto-login";
import useAuthStore from "@/stores/authStore";
import { cn } from "@/utils/utils";
Expand Down Expand Up @@ -226,27 +229,28 @@ export default function NodeInputField({
/>
</div>

{data.node?.template[name] !== undefined && (
<CustomParameterComponent
handleOnNewValue={handleOnNewValue}
name={name}
nodeId={data.id}
inputId={id}
templateData={data.node?.template[name]!}
templateValue={data.node?.template[name].value ?? ""}
editNode={false}
handleNodeClass={handleNodeClass}
nodeClass={data.node!}
placeholder={
isToolMode
? DEFAULT_TOOLSET_PLACEHOLDER
: data.node?.template[name].placeholder
}
isToolMode={isToolMode}
nodeInformationMetadata={nodeInformationMetadata}
proxy={proxy}
/>
)}
{(!ENABLE_INSPECTION_PANEL || !optionalHandle) &&
data.node?.template[name] !== undefined && (
<CustomParameterComponent
handleOnNewValue={handleOnNewValue}
name={name}
nodeId={data.id}
inputId={id}
templateData={data.node?.template[name]!}
templateValue={data.node?.template[name].value ?? ""}
editNode={false}
handleNodeClass={handleNodeClass}
nodeClass={data.node!}
placeholder={
isToolMode
? DEFAULT_TOOLSET_PLACEHOLDER
: data.node?.template[name].placeholder
}
isToolMode={isToolMode}
nodeInformationMetadata={nodeInformationMetadata}
proxy={proxy}
/>
)}
</div>
</div>
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { sortToolModeFields } from "@/CustomNodes/helpers/sort-tool-mode-field";
import getFieldTitle from "@/CustomNodes/utils/get-field-title";
import { scapedJSONStringfy } from "@/utils/reactflowUtils";
import NodeInputField from "../NodeInputField";
import { ENABLE_INSPECTION_PANEL } from "@/customization/feature-flags";

const RenderInputParameters = ({
data,
Expand All @@ -31,11 +32,31 @@ const RenderInputParameters = ({
const shownTemplateFields = useMemo(() => {
return templateFields.filter((templateField) => {
const template = data.node?.template[templateField];
return (
template?.show &&
!template?.advanced &&
!(template?.tool_mode && isToolMode)
);

if (!ENABLE_INSPECTION_PANEL) {
return (
template?.show &&
!template?.advanced &&
!(template?.tool_mode && isToolMode)
);
}

// Basic visibility check
if (
!template?.show ||
template?.advanced ||
(template?.tool_mode && isToolMode)
) {
return false;
}

// Only show fields that have handles (input_types)
const hasHandle = template.input_types && template.input_types.length > 0;
if (!hasHandle) {
return false;
}

return true;
});
}, [templateFields, data.node?.template, isToolMode]);

Expand Down
1 change: 1 addition & 0 deletions src/frontend/src/customization/feature-flags.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ export const ENABLE_IMAGE_ON_PLAYGROUND = false;
export const ENABLE_MCP = true;
export const ENABLE_MCP_NOTICE = false;
export const ENABLE_KNOWLEDGE_BASES = false;
export const ENABLE_INSPECTION_PANEL = true;

export const ENABLE_MCP_COMPOSER =
import.meta.env.LANGFLOW_MCP_COMPOSER_ENABLED === "true";
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,191 @@
import { useMemo } from "react";
import { AssistantButton } from "@/components/common/assistant";
import IconComponent from "@/components/common/genericIconComponent";
import ShadTooltip from "@/components/common/shadTooltipComponent";
import {
CustomParameterComponent,
CustomParameterLabel,
getCustomParameterTitle,
} from "@/customization/components/custom-parameter";
import { LANGFLOW_AGENTIC_EXPERIENCE } from "@/customization/feature-flags";
import { useIsAutoLogin } from "@/hooks/use-is-auto-login";
import useAuthStore from "@/stores/authStore";
import useFlowStore from "@/stores/flowStore";
import { useTypesStore } from "@/stores/typesStore";
import type { NodeInputFieldComponentType } from "@/types/components";
import { cn } from "@/utils/utils";
import {
DEFAULT_TOOLSET_PLACEHOLDER,
FLEX_VIEW_TYPES,
ICON_STROKE_WIDTH,
} from "@/constants/constants";
import useHandleNodeClass from "@/CustomNodes/hooks/use-handle-node-class";
import { usePostTemplateValue } from "@/controllers/API/queries/nodes/use-post-template-value";
import useFetchDataOnMount from "@/CustomNodes/hooks/use-fetch-data-on-mount";
import useHandleOnNewValue from "@/CustomNodes/hooks/use-handle-new-value";
import NodeInputInfo from "@/CustomNodes/GenericNode/components/NodeInputInfo";

export default function InspectionPanelField({
id,
data,
title,
name = "",
required = false,
info = "",
showNode,
isToolMode = false,
proxy,
}: Omit<
NodeInputFieldComponentType,
| "colors"
| "tooltipTitle"
| "type"
| "optionalHandle"
| "colorName"
| "lastInput"
>) {
const isAuthenticated = useAuthStore((state) => state.isAuthenticated);
const isAutoLogin = useIsAutoLogin();
const shouldDisplayApiKey = isAuthenticated && !isAutoLogin;

const { currentFlowId, currentFlowName } = useFlowStore((state) => ({
currentFlowId: state.currentFlow?.id,
currentFlowName: state.currentFlow?.name,
}));

const myData = useTypesStore((state) => state.data);
const postTemplateValue = usePostTemplateValue({
node: data.node!,
nodeId: data.id,
parameterId: name,
});

const { handleNodeClass } = useHandleNodeClass(data.id);

const { handleOnNewValue } = useHandleOnNewValue({
node: data.node!,
nodeId: data.id,
name,
});

const nodeInformationMetadata = useMemo(() => {
return {
flowId: currentFlowId ?? "",
nodeType: data?.type?.toLowerCase() ?? "",
flowName: currentFlowName ?? "",
isAuth: shouldDisplayApiKey!,
variableName: name,
};
}, [data?.node?.id, shouldDisplayApiKey, name]);

Comment on lines +71 to +80
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 | 🟠 Major

Fix useMemo deps to avoid stale nodeInformationMetadata.

nodeInformationMetadata uses currentFlowId, currentFlowName, and data.type, but they’re not in the dependency list, so metadata can silently go stale.

Proposed fix
   const nodeInformationMetadata = useMemo(() => {
     return {
       flowId: currentFlowId ?? "",
       nodeType: data?.type?.toLowerCase() ?? "",
       flowName: currentFlowName ?? "",
       isAuth: shouldDisplayApiKey!,
       variableName: name,
     };
-  }, [data?.node?.id, shouldDisplayApiKey, name]);
+  }, [currentFlowId, currentFlowName, data?.type, shouldDisplayApiKey, name]);
🤖 Prompt for AI Agents
In
@src/frontend/src/pages/FlowPage/components/InspectionPanel/components/InspectionPanelField.tsx
around lines 71 - 80, The useMemo for nodeInformationMetadata is missing
dependencies and can produce stale values; update the dependency array of the
useMemo that defines nodeInformationMetadata to include currentFlowId,
currentFlowName, and data?.type in addition to the existing shouldDisplayApiKey
and name (and optionally keep data?.node?.id if you still need it) so the memo
recalculates when currentFlowId, currentFlowName, or data.type change.

useFetchDataOnMount(
data.node!,
data.id,
handleNodeClass,
name,
postTemplateValue,
);

const template = data.node?.template[name];
const type = template?.type;
const isFlexView = FLEX_VIEW_TYPES.includes(type ?? "");

return (
<div
className={cn(
"relative flex min-h-10 w-full flex-wrap items-center justify-between px-3 py-1.5",
isToolMode && "bg-primary/10",
(name === "code" && type === "code") || (name.includes("code") && proxy)
? "hidden"
: "",
)}
Comment on lines +94 to +101
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 | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

# First, let's read the file to understand the full context
cat -n "src/frontend/src/pages/FlowPage/components/InspectionPanel/components/InspectionPanelField.tsx" | head -120 | tail -40

Repository: langflow-ai/langflow

Length of output: 1500


🏁 Script executed:

# Get more context around lines 94-101
wc -l "src/frontend/src/pages/FlowPage/components/InspectionPanel/components/InspectionPanelField.tsx"

Repository: langflow-ai/langflow

Length of output: 162


🏁 Script executed:

# Read the entire file to understand the component structure
cat -n "src/frontend/src/pages/FlowPage/components/InspectionPanel/components/InspectionPanelField.tsx"

Repository: langflow-ai/langflow

Length of output: 7409


🏁 Script executed:

# Search for where this component is used and what name values are passed
rg "InspectionPanelField" --type tsx -A 3 -B 1 | head -100

Repository: langflow-ai/langflow

Length of output: 91


🏁 Script executed:

# Look for field names that might contain "code"
rg "name.*code|code.*name" --type tsx -i | head -50

Repository: langflow-ai/langflow

Length of output: 91


🏁 Script executed:

# Search for InspectionPanelField usage with name parameters
rg "InspectionPanelField" -A 5 -B 2 | head -150

Repository: langflow-ai/langflow

Length of output: 5862


🏁 Script executed:

# Search for field definitions that might contain "code" in their names
rg "name.*=.*code|code.*name" --type ts --type tsx -i -A 2 -B 2 | head -100

Repository: langflow-ai/langflow

Length of output: 91


🏁 Script executed:

# Look for template fields that might be passed as name prop
rg "template\[.*code" --type ts --type tsx -A 2 -B 2 | head -50

Repository: langflow-ai/langflow

Length of output: 91


🏁 Script executed:

# Search for what proxy fields might be and their naming patterns
rg "proxy" --type tsx -B 3 -A 3 "InspectionPanel" | head -80

Repository: langflow-ai/langflow

Length of output: 91


🏁 Script executed:

# Find all usages of name.includes pattern in codebase
rg 'name\.includes.*code' --type ts --type tsx

Repository: langflow-ai/langflow

Length of output: 91


🏁 Script executed:

# Look for InspectionPanelFields.tsx to see how fields are iterated
cat -n "src/frontend/src/pages/FlowPage/components/InspectionPanel/components/InspectionPanelFields.tsx"

Repository: langflow-ai/langflow

Length of output: 2970


🏁 Script executed:

# Search for name.includes pattern anywhere in the codebase
rg 'name\.includes' -A 2 -B 2 | head -80

Repository: langflow-ai/langflow

Length of output: 5103


🏁 Script executed:

# Look for proxy-related field names in templates
rg '"code"' src/frontend --max-count 50 | grep -i name

Repository: langflow-ai/langflow

Length of output: 4997


🏁 Script executed:

# Search for field definitions/templates that contain "code" in various files
rg '"[^"]*code[^"]*":\s*{' -A 3 src/frontend --max-count 30

Repository: langflow-ai/langflow

Length of output: 169


🏁 Script executed:

# Look for proxy field definitions with "code" in their names
rg 'proxy.*true' src/backend -A 2 -B 2 | grep -i code | head -50

Repository: langflow-ai/langflow

Length of output: 50376


🏁 Script executed:

# Check NodeInputField to see if it has similar logic
cat -n "src/frontend/src/CustomNodes/GenericNode/components/NodeInputField/index.tsx" | grep -A 10 -B 10 'name.includes.*code'

Repository: langflow-ai/langflow

Length of output: 772


🏁 Script executed:

# Search for proxy field definitions with any field names
rg '"proxy".*"field"' -A 2 -B 2 | head -80

Repository: langflow-ai/langflow

Length of output: 50377


🏁 Script executed:

# Look for any fields that might be code-related with proxy
rg 'includes.*code|encode|decode' src/frontend --type ts -A 3 -B 3 | head -100

Repository: langflow-ai/langflow

Length of output: 6939


🏁 Script executed:

# Check the actual backend to understand what fields exist
find src/backend -name "*.py" -type f | head -20 | xargs rg 'proxy.*=.*True' -A 2 -B 2 | head -80

Repository: langflow-ai/langflow

Length of output: 46


🏁 Script executed:

# Search for actual proxy field patterns and names more carefully
rg '"field"\s*:\s*"[^"]*code' src/backend src/frontend -A 1 -B 1 | head -50

Repository: langflow-ai/langflow

Length of output: 1330


🏁 Script executed:

# Check if there are any Python definitions of fields with "code" in name and proxy=True
find src/backend -name "*.py" -type f -exec grep -l "proxy.*True" {} \; | head -5 | xargs grep -B 5 -A 5 'name.*=.*' | grep -i code

Repository: langflow-ai/langflow

Length of output: 46


🏁 Script executed:

# Let's verify the filtering is indeed redundant by checking if any fields pass both filters
python3 << 'EOF'
# Simulate the logic from InspectionPanelFields
test_fields = [
    ("code", {"show": True, "type": "code"}),
    ("model_kwargs", {"show": True, "type": "code", "proxy": {"id": "x", "field": "y"}}),
    ("my_code_field", {"show": True, "type": "str", "proxy": {"id": "x", "field": "z"}}),
    ("code_handler", {"show": True, "type": "str", "proxy": {"id": "x", "field": "w"}}),
    ("llm", {"show": True, "type": "BaseLanguageModel", "proxy": {"id": "x", "field": "llm"}}),
]

print("Fields that would be filtered (redundantly hidden in InspectionPanelField):")
for field_name, template in test_fields:
    if (field_name == "code" and template.get("type") == "code") or (field_name.includes("code") and template.get("proxy")):
        print(f"  - {field_name}: includes('code')={field_name.includes('code')}, proxy={bool(template.get('proxy'))}")
EOF

Repository: langflow-ai/langflow

Length of output: 150


🏁 Script executed:

# Better approach - check actual field data from real flows
rg -A 15 '"template"\s*:' src/backend/tests/data/grouped_chat.json 2>/dev/null | grep -E '"name"|"type"|"proxy"' | head -40

Repository: langflow-ai/langflow

Length of output: 20337


🏁 Script executed:

# Check if fields filtered in InspectionPanelFields ever reach InspectionPanelField
cat -n "src/frontend/src/pages/FlowPage/components/InspectionPanel/components/InspectionPanelFields.tsx" | head -50

Repository: langflow-ai/langflow

Length of output: 1946


Tighten the field name matching to avoid unintentionally hiding unrelated fields.

The condition name.includes("code") is overly broad and can hide valid fields with names like "encode", "decode", or any field containing "code" as a substring. Since proxy fields with "field": "code" do exist in the codebase, a more precise predicate is warranted.

Suggested fix
-        (name === "code" && type === "code") || (name.includes("code") && proxy)
+        (name === "code" && type === "code") ||
+        (proxy && /(^|_)code($|_)/.test(name))
           ? "hidden"
           : "",
🤖 Prompt for AI Agents
In
@src/frontend/src/pages/FlowPage/components/InspectionPanel/components/InspectionPanelField.tsx
around lines 94 - 101, The name matching is too broad: replace the substring
check `name.includes("code")` with a precise check so unrelated fields like
"encode" aren't hidden; update the condition in InspectionPanelField.tsx so the
hidden branch only triggers for an explicit code field (e.g., keep `(name ===
"code" && type === "code")` and change the proxy case to check the proxy's
declared field like `proxy?.field === "code"` or `proxy && proxy.field ===
"code"`) so only actual proxy fields that target "code" hide the element.

>
<div
className={cn(
"flex w-full flex-col gap-2",
isFlexView ? "flex-row" : "flex-col",
)}
>
<div className="flex w-full items-center justify-between text-sm">
<div className="flex w-full items-center truncate">
{proxy ? (
<ShadTooltip content={<span>{proxy.id}</span>}>
<span>
{getCustomParameterTitle({
title,
nodeId: data.id,
isFlexView,
required,
})}
</span>
</ShadTooltip>
) : (
<div className="flex gap-2">
<span className="text-sm font-medium">
{getCustomParameterTitle({
title,
nodeId: data.id,
isFlexView,
required,
})}
</span>
</div>
)}
<div>
{info !== "" && (
<ShadTooltip content={<NodeInputInfo info={info} />}>
<div className="cursor-help">
<IconComponent
name="Info"
strokeWidth={ICON_STROKE_WIDTH}
className="relative ml-1 h-3 w-3 text-placeholder"
/>
</div>
</ShadTooltip>
)}
</div>
{LANGFLOW_AGENTIC_EXPERIENCE &&
data.node?.template[name]?.ai_enabled && (
<AssistantButton
compData={id}
handleOnNewValue={handleOnNewValue}
inputValue={data.node?.template[name]?.value}
type="field"
/>
)}
</div>
<CustomParameterLabel
name={name}
nodeId={data.id}
templateValue={data.node?.template[name]}
nodeClass={data.node!}
/>
</div>

{data.node?.template[name] !== undefined && (
<CustomParameterComponent
handleOnNewValue={handleOnNewValue}
name={name}
nodeId={data.id}
inputId={id}
templateData={data.node?.template[name]!}
templateValue={data.node?.template[name].value ?? ""}
editNode={false}
handleNodeClass={handleNodeClass}
nodeClass={data.node!}
placeholder={
isToolMode
? DEFAULT_TOOLSET_PLACEHOLDER
: data.node?.template[name].placeholder
}
isToolMode={isToolMode}
nodeInformationMetadata={nodeInformationMetadata}
proxy={proxy}
/>
)}
</div>
</div>
);
}

// Made with Bob
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
import { useMemo } from "react";
import { sortToolModeFields } from "@/CustomNodes/helpers/sort-tool-mode-field";
import getFieldTitle from "@/CustomNodes/utils/get-field-title";
import type { NodeDataType } from "@/types/flow";
import { LANGFLOW_SUPPORTED_TYPES } from "@/constants/constants";
import InspectionPanelField from "./InspectionPanelField";

interface InspectionPanelFieldsProps {
data: NodeDataType;
}

export default function InspectionPanelFields({
data,
}: InspectionPanelFieldsProps) {
// Get all fields in one list - show ALL fields in Inspection Panel
const allFields = useMemo(() => {
return Object.keys(data.node?.template || {})
.filter((templateField) => {
const template = data.node?.template[templateField];

// Filter out fields that shouldn't be shown
if (
templateField.charAt(0) === "_" ||
!template?.show ||
(templateField === "code" && template.type === "code") ||
(templateField.includes("code") && template.proxy)
) {
return false;
}

return true;
})
.sort((a, b) =>
sortToolModeFields(
a,
b,
data.node!.template,
data.node?.field_order ?? [],
false,
),
);
}, [data.node?.template, data.node?.field_order]);

if (allFields.length === 0) {
return (
<div className="flex items-center justify-center p-8 text-sm text-muted-foreground">
No fields available
</div>
);
}

return (
<div className="space-y-2 p-2">
{allFields.map((templateField: string) => {
const template = data.node?.template[templateField];

return (
<InspectionPanelField
key={`${data.id}-${templateField}`}
data={data}
title={getFieldTitle(data.node?.template!, templateField)}
info={template.info!}
name={templateField}
required={template.required}
id={{
inputTypes: template.input_types,
type: template.type,
id: data.id,
fieldName: templateField,
}}
proxy={template.proxy}
showNode={true}
isToolMode={false}
/>
);
})}
</div>
);
}

// Made with Bob
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 | 🟡 Minor

Remove the stray “Made with Bob” comment before merging.

🤖 Prompt for AI Agents
In
@src/frontend/src/pages/FlowPage/components/InspectionPanel/components/InspectionPanelFields.tsx
at line 81, Remove the stray comment line containing "Made with Bob" from the
InspectionPanelFields.tsx file: locate the comment in the InspectionPanelFields
component (or nearby top-level file header) and delete that line so no stray
comment remains; then run the project's lint/format step to ensure no trailing
whitespace or formatting issues.

Loading
Loading