Skip to content

fix: align inspector panel API exposure with what the backend accepts - #14304

Open
tarciorodrigues wants to merge 7 commits into
release-1.11.2from
fix/inspector-api-exposure-parity
Open

fix: align inspector panel API exposure with what the backend accepts#14304
tarciorodrigues wants to merge 7 commits into
release-1.11.2from
fix/inspector-api-exposure-parity

Conversation

@tarciorodrigues

@tarciorodrigues tarciorodrigues commented Jul 28, 2026

Copy link
Copy Markdown
Member

Why

The parameters panel offers an "API" toggle on rows whose exposure never materializes, and in the worst case advertises in the generated snippet fields that the backend silently refuses at run time. Three layers hold three different opinions about "can this field be an API input" and none of them is authoritative: the panel lists rows with no type gate at all (isManageableParameter), the tweaks derivation filters on LANGFLOW_SUPPORTED_TYPES before the exposure predicate ever runs (getNodesWithDefaultValue), and apply_tweaks refuses code fields and protected sinks at run time with a logger.warning the API caller never sees.

The root cause is a borrowed list. LANGFLOW_SUPPORTED_TYPES answers "does this render an inline widget on the node" — that is how its other two consumers use it (RenderInputParameters/utils.ts, reactflowUtils.ts) — and it dates from #1847 (2024). The tweaks derivation reused it as a proxy for "is tweakable". When ModelInput was created the render path was special-cased (if (isModelInput) return true) but the set was never updated, so the derivation silently inherited the gap. LE-1810 introduced a single exposure predicate (isFieldExposable) but did not make it the only word, which is what turned a silent limitation into a visible broken promise.

What

One predicate, expressed by field property — does it carry an editable literal value, and does the backend refuse it — instead of a static allowlist of type names, since the static list is precisely what aged into the bug. Every layer consumes that one predicate.

Class A — the panel promised and the snippet did not deliver. A model field (18 across the component index: Agent, LanguageModelComponent, StructuredOutput, SmartRouter, SQLAgent, ToolCallingAgent, XMLAgent, OpenAPIAgent, OpenAIToolsAgent, EmbeddingModel, GuardrailValidator, Smart Transform, CSVAgent, BatchRunComponent, AMapComponent, AgenerateComponent, AreduceComponent, policies) turned the toggle green, persisted api_editable: true, and never reached the tweaks object. The model type is now tweakable: apply_tweaks has no type allowlist, and ModelInput normalizes the assigned value through its own validator (validate_assignment is on, so _validate_inputs triggers normalize_value), which accepts a plain model name, a list of names, or the dict form.

Class B — the panel offered what cannot mean anything. Handle-only inputs (other, 94 fields — Tools, retrievers) are driven by an edge and carry no literal to send. The derivation always dropped them; now the panel stops offering them.

Class C — the snippet promised and the backend refused in silence. Plain str/bool/code fields that passed every frontend gate and are dropped by apply_tweaks: SQLComponent.database_url and query (protected sinks), PythonREPLComponent.python_code and global_imports, PythonREPLTool.global_imports, Smart Transform.filter_instruction, CSVAgent.allow_dangerous_code (code-execution inputs), PythonFunction.function_code (code-typed — the frontend blocked the field NAME code, never the TYPE). All are show: true, advanced: false today, so they were genuinely offered. The refusal rules are now mirrored in the frontend for UX only — the backend remains the enforcement point, since any caller can post a tweak without going through this UI — and a cross-language parity test fails if the two sides ever diverge.

Uniform treatment, nothing hidden. A field that cannot be tweaked keeps its row and its Add/Remove action (the panel's other job — hiding Tools or database_url from the node must keep working); only the API control is blocked, with the cursor block and the existing tooltip. The non-tweakable case is checked before the off-node one, otherwise the tooltip would tell the user to add the parameter to the node — a way out that does not exist. Fields blocked temporarily (edge-connected, tool mode, off-node) are unchanged from LE-1810: they are tweakable, just not right now, and their reversible state stays visible. No new i18n key, no churn across the 7 locales.

How to validate

Run the numbered script below. The evidence screenshots are numbered 1:1 with these steps — compare step Sn with image Sn. Before captures were taken on release-1.11.1 and after captures on this branch using the SAME saved flow with the same persisted api_editable, so the only variable between the two columns is the code.

  1. S1 Open a flow with an Agent, open Component Parameters, turn the API toggle ON for Language Model, then open Share → API access → check the payload. Before: no tweaks block at all despite the toggle being on. After: "tweaks": {"Agent-XXXXX": {"model": [{"name": "...", "provider": "..."}]}}.
  2. S2 Drop a fresh Agent on the canvas (leave it unconnected) and open its Component Parameters → the Tools row. Before: the API button is live and clickable. After: it is blocked, and Remove on the same row still works.
  3. S3 Drop an SQL Database component and open its Component Parameters → Database URL and SQL Query. Before: both offer a live API toggle. After: both are blocked.
  4. S4 Hover the blocked API control → the tooltip opens with "Disabled fields can't be called via API". (The button is pointer-events-none while disabled, so the hover is carried by the wrapper span — unchanged from LE-1810.)
  5. S5 Regression, visible in the S2 pair: an ordinary literal field (Agent Instructions) still toggles normally, and a field blocked temporarily by an edge still shows the disabled control with its own reason rather than disappearing.

Plus: devtools console shows zero new errors throughout.

Evidence (S1–S4, before | after)

before (release-1.11.1) after (this PR)
01-snippet-S1-before 01-snippet-S1-after
S1 — same flow, same persisted api_editable on the model field: before, the payload jumps from input_value straight to the closing brace with no tweaks block; after, the model tweak is advertised
02-panel-S2-before 02-panel-S2-after
S2 — unconnected Agent: the handle-only Tools input stops offering an exposure the derivation always dropped, while Remove on the same row keeps working
03-panel-S3-before 03-panel-S3-after
S3 — SQL Database: database_url and query are protected sinks that apply_tweaks refuses; the panel no longer advertises them as API inputs
04-panel-S4-after
S4 — the blocked control states its reason in place through the existing tooltip string, so nothing silently disappears from the panel

Tests

  • New api-exposure-rules.test.ts (21 cases) pins the rules that are easy to get wrong on the next change: a code-execution input is refused only on a component that executes code, a protected sink only on the component that owns it (query is refused on SQLComponent and tweakable on NewsSearch), the model type is tweakable, and a handle-only input never is.
  • New test_frontend_mirrors_tweak_refusal_rules in src/lfx/tests/unit/utils/test_flow_validation.py parses the TypeScript mirror and compares it to the Python frozensets, skipping when the frontend package is absent (standalone lfx checkout). Verified to fail on an injected drift.
  • Full frontend suite green: 473 suites / 5482 tests. test_flow_validation 75/75. Biome clean on the diff. tsc unchanged against the base (the 6 pre-existing errors in the touched test file are identical before and after, verified by restoring the base version of the file and recounting).
  • tweaksStore.test.ts fixtures gained the template type that every real template carries — no assertion weakened.

Note

The frontend mirror of the refusal rules is UX only and must never be treated as the enforcement: any caller can post a tweak without going through this UI, and apply_tweaks is what refuses it. The module says so at the top, and the parity test exists so the mirror cannot drift into a lie.

Exposing model is the only change here that widens what reaches the backend — those fields previously could not be advertised through this path at all. It stays under the flow author's control: api_editable defaults to false and the toggle is theirs.

Fields that already carry api_editable: true on a now non-tweakable field are not migrated: per LE-1810 the flag is never mutated, it simply goes inert, and the snippet stops listing it.

Follow-ups deliberately left out: apply_tweaks reports a refused tweak only through a log line, so an API caller cannot tell the override was dropped — making the run response surface that is a behavior change with security framing and deserves its own ticket. Same for renaming LANGFLOW_SUPPORTED_TYPES to what it actually means, and for splitting the catch-all "Disabled fields can't be called via API" tooltip into one message per cause.

Summary by CodeRabbit

  • New Features

    • Improved API field exposure rules to identify which component inputs can be safely adjusted.
    • API exposure controls now disable unsupported, internal, or backend-refused fields with clearer guidance.
    • Added support for language-model selectors and other valid editable input types.
  • Bug Fixes

    • Improved consistency between frontend API controls and backend field restrictions.
  • Tests

    • Added coverage for editable field types, protected inputs, and frontend/backend rule parity.

@coderabbitai

coderabbitai Bot commented Jul 28, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Important

Review skipped

Auto incremental reviews are disabled on this repository.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 7207bb78-4fd2-4cf4-b3c6-ded68da515ef

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

Walkthrough

Changes

API exposure rule alignment

Layer / File(s) Summary
Rule definitions and parity validation
src/frontend/src/modals/apiModal/utils/api-exposure-rules.ts, src/frontend/src/modals/apiModal/utils/__tests__/*, src/lfx/tests/unit/utils/test_flow_validation.py
Adds centralized tweakability and backend-refusal predicates, frontend rule tests, and a parity test against backend constants.
Exposure consumer integration
src/frontend/src/modals/apiModal/utils/get-nodes-with-default-value.ts, src/frontend/src/modals/apiModal/utils/is-field-exposable.ts, src/frontend/src/pages/FlowPage/.../InspectionPanelParameterRow.tsx, src/frontend/src/stores/__tests__/tweaksStore.test.ts
Uses the centralized predicate for default-value discovery, API exposure, inspection controls, tooltips, and updated typed test fixtures.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Suggested labels: test

🚥 Pre-merge checks | ✅ 9
✅ Passed checks (9 passed)
Check name Status Explanation
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Test Coverage For New Implementations ✅ Passed The PR adds substantive tests for the new exposure rules plus a backend parity regression test, and both follow the repo naming conventions.
Test Quality And Coverage ✅ Passed PASS: New helper tests cover allow/refuse/tweakable branches, backend parity is asserted, and existing RTL/pytest suites already cover the UI/store paths and async checks.
Test File Naming And Structure ✅ Passed PASS: Added tests follow repo conventions—frontend Jest unit tests in tests, backend pytest in tests/unit, descriptive names, beforeEach setup, and positive/negative cases.
Excessive Mock Usage Warning ✅ Passed The new tests mostly exercise real predicates directly; the few mocks in tweaksStore and Python unit tests isolate external dependencies and don’t obscure core behavior.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly matches the main change: aligning inspector/API exposure behavior with backend-accepted fields.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/inspector-api-exposure-parity

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.

@github-actions github-actions Bot added the bug Something isn't working label Jul 28, 2026
@github-actions

Copy link
Copy Markdown
Contributor

✅ Test Coverage Advisor

No source changes detected without accompanying tests. Thanks for keeping coverage up! 🎉

Advisory check only — never blocks merge.

@github-actions github-actions Bot added bug Something isn't working and removed bug Something isn't working labels Jul 28, 2026

@Cristhianzl Cristhianzl left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

lgtm

@github-actions github-actions Bot added lgtm This PR has been approved by a maintainer bug Something isn't working and removed bug Something isn't working labels Jul 28, 2026
@tarciorodrigues
tarciorodrigues changed the base branch from release-1.11.1 to release-1.11.2 July 28, 2026 20:48
@github-actions github-actions Bot added bug Something isn't working and removed bug Something isn't working labels Jul 28, 2026
@github-actions

github-actions Bot commented Jul 28, 2026

Copy link
Copy Markdown
Contributor

Build successful! ✅
Deploying docs draft.
Deploy successful! View draft

@codecov

codecov Bot commented Jul 28, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
⚠️ Please upload report for BASE (release-1.11.2@b16e844). Learn more about missing BASE report.

Additional details and impacted files

Impacted file tree graph

@@                Coverage Diff                @@
##             release-1.11.2   #14304   +/-   ##
=================================================
  Coverage                  ?   61.60%           
=================================================
  Files                     ?     2444           
  Lines                     ?   239846           
  Branches                  ?    37307           
=================================================
  Hits                      ?   147768           
  Misses                    ?    90308           
  Partials                  ?     1770           
Flag Coverage Δ
backend 68.90% <ø> (?)
lfx 60.06% <ø> (?)

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

Files with missing lines Coverage Δ
...als/apiModal/utils/get-nodes-with-default-value.ts 100.00% <ø> (ø)
...nd/src/modals/apiModal/utils/is-field-exposable.ts 100.00% <ø> (ø)
...onPanel/components/InspectionPanelParameterRow.tsx 98.34% <ø> (ø)
🚀 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 commented Jul 28, 2026

Copy link
Copy Markdown
Contributor

Frontend Unit Test Coverage Report

Coverage Summary

Lines Statements Branches Functions
Coverage: 47%
47.2% (67300/142574) 70.25% (9470/13480) 45.61% (1546/3389)

Unit Test Results

Tests Skipped Failures Errors Time
5384 0 💤 0 ❌ 0 🔥 19m 30s ⏱️

…al utils

apply_tweaks silently drops tweaks aimed at code fields and protected sinks,
logging a warning the API caller never sees. The parameters panel has no
knowledge of those rules, so it offers an API toggle on fields the backend
will refuse and the generated snippet advertises them.

Add a frontend mirror of CODE_EXECUTION_COMPONENT_TYPES,
CODE_EXECUTION_FIELD_NAMES and PROTECTED_TWEAK_FIELDS_BY_COMPONENT plus an
isBackendRefusedField helper. No consumer yet. The mirror is UX only: the
backend remains the enforcement point.
A mirrored list that drifts silently brings the bug back: the UI would again
offer an API toggle on a field apply_tweaks refuses, or hide one that is
legitimately tweakable. Parse the TypeScript mirror and compare it to the
Python frozensets, skipping when the frontend package is absent (standalone
lfx checkout). Verified to fail on an injected drift.
…able

The tweaks derivation filtered template keys with an inline expression built
on LANGFLOW_SUPPORTED_TYPES, a set that means 'renders an inline widget on
the node' and had been borrowed as if it meant 'is tweakable'. Extract the
gate into isFieldTweakable so the rule has a name and a single home, ready
to be shared with the panel row and the exposure predicate.

Pure refactor: same fields in, same fields out.
A Language Model parameter with the API toggle on never reached the tweaks
object: the derivation gated on LANGFLOW_SUPPORTED_TYPES, which does not list
the model type, so the field was dropped before the exposure predicate ran.
The toggle turned green, api_editable was persisted, and the snippet stayed
silent.

The backend has no such restriction — apply_tweaks refuses only code fields
and protected sinks — and ModelInput normalizes the assigned value through
its validator (validate_assignment is on), so a tweaked model name is
accepted. Treat the model type as tweakable.
The panel listed rows with no type gate at all, so a handle-only input such
as Tools offered a live API toggle while the tweaks derivation dropped it —
the user could turn the toggle green and nothing would ever reach the
snippet.

Make the row and the exposure predicate consume the same isFieldTweakable
gate the derivation uses: one rule, three call sites. A non-tweakable field
now shows the API control blocked, with the cursor block and the tooltip, and
that case is checked before the off-node one so the tooltip does not promise
a way out that does not exist. The row keeps its Add/Remove action.

Test fixtures gained the template type they always have in a real flow.
apply_tweaks refuses a tweak aimed at a code field, at a code-execution
input on a code-execution component, or at a protected sink, and only logs a
warning the caller never sees. The panel offered those fields anyway and the
snippet advertised them, so SQLComponent database_url, PythonREPLComponent
python_code and friends read as configurable inputs that silently do nothing.

Feed the mirrored refusal rules into isFieldTweakable, so the same gate that
drives the derivation and the panel row now covers them: the API control is
blocked and the snippet never lists them. The backend check is untouched and
remains the enforcement point.
Pin the rules that are easy to get wrong on the next change: a code-execution
input is refused only on a component that executes code, a protected sink
only on the component that owns it (query is refused on SQLComponent and
tweakable on NewsSearch), the model type is tweakable and a handle-only input
never is.
@tarciorodrigues

tarciorodrigues commented Jul 28, 2026

Copy link
Copy Markdown
Member Author

Force-pushed to rebuild this branch directly on release-1.11.2.

@tarciorodrigues
tarciorodrigues force-pushed the fix/inspector-api-exposure-parity branch from a240081 to 4835bda Compare July 28, 2026 21:18
@github-actions github-actions Bot added bug Something isn't working and removed bug Something isn't working labels Jul 28, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working lgtm This PR has been approved by a maintainer

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants