Skip to content

fix: resolve canvas performance degradation #13460#13759

Open
gmrnlg1971 wants to merge 2 commits into
langflow-ai:mainfrom
gmrnlg1971:fix/canvas-performance-13460
Open

fix: resolve canvas performance degradation #13460#13759
gmrnlg1971 wants to merge 2 commits into
langflow-ai:mainfrom
gmrnlg1971:fix/canvas-performance-13460

Conversation

@gmrnlg1971

@gmrnlg1971 gmrnlg1971 commented Jun 20, 2026

Copy link
Copy Markdown

Summary by CodeRabbit

  • New Features

    • Monitoring endpoints now support pagination controls and configurable sort direction for message queries.
  • Improvements

    • Chat history loading now responds to playground UI state.

@coderabbitai

coderabbitai Bot commented Jun 20, 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

Run ID: 2689fdef-efe9-4638-86b6-84ab6d0cd748

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

Two monitor API endpoints (GET /monitor/messages and GET /monitor/messages/shared) gain order, limit, and offset query parameters supporting configurable sort direction and server-side pagination. The frontend useChatHistory hook gates its useGetMessagesQuery call behind playground visibility state. A debug scratch.mjs file is also present.

Changes

Monitor API Pagination and Frontend Query Gating

Layer / File(s) Summary
Pagination and sort direction for monitor message endpoints
src/backend/base/langflow/api/v1/monitor.py
Both get_messages and get_shared_messages add order (default "desc"), limit (default 30), and offset (default 0) query parameters. SQL ordering switches from always-ascending to DESC/ASC based on order, and OFFSET/LIMIT clauses are applied when the values are truthy.
Gate useGetMessagesQuery on playground visibility
src/frontend/src/components/core/playgroundComponent/chat-view/chat-messages/hooks/use-chat-history.ts
useChatHistory imports usePlaygroundStore and useFlowStore, reads isOpen and playgroundPage, and passes enabled: isPlaygroundOpen || playgroundPage to useGetMessagesQuery, suppressing the fetch when the playground is not visible.

Scratch Debug File

Layer / File(s) Summary
Leftover debug import
scratch.mjs
Imports BASENAME from the frontend config constants module and logs it via console.log.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~10 minutes


Important

Pre-merge checks failed

Please resolve all errors before merging. Addressing warnings is optional.

❌ Failed checks (1 error, 3 warnings)

Check name Status Explanation Resolution
Test Coverage For New Implementations ❌ Error PR includes test files for monitor.py but they don't test the new pagination parameters (order, limit, offset). No tests for use-chat-history hook or scratch.mjs file. Add tests for new pagination functionality in monitor endpoints, add use-chat-history.test.ts for the conditional enable feature, and ensure all new features have corresponding test coverage.
Docstring Coverage ⚠️ Warning Docstring coverage is 50.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Test Quality And Coverage ⚠️ Warning No tests added for the new pagination (limit, offset) and sort (order) parameters in monitor.py endpoints, nor tests for the conditional query enablement in use-chat-history.ts. Existing tests don'... Add tests: (1) Backend pagination tests for limit/offset/order parameters with success/error cases; (2) Frontend test for use-chat-history conditional query enabling based on isOpen/playgroundPage; (3) Validate error handling for invalid...
Test File Naming And Structure ⚠️ Warning No test files were added or modified for the three main PR changes. The frontend hook use-chat-history.ts lacks a test file despite existing test infrastructure in its directory; the backend API... Add test coverage: create use-chat-history.test.ts for the hook, add pagination/order parameter tests to monitor test files, and create tests for scratch.mjs changes following established naming patterns (test_*.py for backend, *.tes...
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title directly addresses the main objective of the pull request—resolving canvas performance degradation—and is reflected in the changes that conditionally gate backend queries and API pagination.
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.
Excessive Mock Usage Warning ✅ Passed No test files were modified in this PR; the check is not applicable. The PR contains only implementation changes to monitor.py, chat-history.ts, and scratch.mjs.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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 Jun 20, 2026

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🧹 Nitpick comments (1)
scratch.mjs (1)

1-2: ⚡ Quick win

Remove the leftover debug scratch file from this PR.

This import + console.log appears to be debugging-only and unrelated to the performance fix, so it should be dropped before merge.

Suggested cleanup
-import { BASENAME } from "./src/frontend/src/customization/config-constants.ts";
-console.log(BASENAME);
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@scratch.mjs` around lines 1 - 2, Delete the scratch.mjs file entirely as it
contains only debugging code with an import of BASENAME and a console.log
statement that are unrelated to the performance fix and should not be included
in the final commit.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/backend/base/langflow/api/v1/monitor.py`:
- Around line 231-232: The pagination logic at multiple locations (including
lines 267-270, 536-537, and 564-567) uses truthy checks on the limit parameter,
which means limit=0 is treated as falsy and skips the pagination guard entirely.
Replace all truthy checks (if limit:) with explicit `is not None` checks to
properly validate the limit parameter. Additionally, add bounds validation to
ensure the limit parameter is greater than 0 before applying it in the query,
preventing zero values from bypassing pagination and causing full-table scans.
This applies to all locations where pagination is applied to query results.
- Around line 261-266: Validate both the order_by and order parameters in the
get_messages function before constructing the ORDER BY clause at line 261. Add
validation to ensure order_by is a valid column name on the MessageTable class,
raising a 400 Bad Request error if it is not, and validate that order is either
"asc" or "desc" (case-insensitive), defaulting to "asc" or raising an error for
invalid values. This prevents the unguarded getattr call from triggering server
errors and ensures the order parameter is explicitly handled rather than
silently defaulting for any non-desc value.

---

Nitpick comments:
In `@scratch.mjs`:
- Around line 1-2: Delete the scratch.mjs file entirely as it contains only
debugging code with an import of BASENAME and a console.log statement that are
unrelated to the performance fix and should not be included in the final commit.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 1dcdef60-fe2d-4104-92ed-2e6a22ea2b0f

📥 Commits

Reviewing files that changed from the base of the PR and between 43d3510 and aab5fa8.

📒 Files selected for processing (3)
  • scratch.mjs
  • src/backend/base/langflow/api/v1/monitor.py
  • src/frontend/src/components/core/playgroundComponent/chat-view/chat-messages/hooks/use-chat-history.ts

Comment on lines +231 to +232
limit: Annotated[int | None, Query()] = 30,
offset: Annotated[int | None, Query()] = 0,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

limit=0 currently disables pagination and can trigger full-table scans.

At Lines 267-270 and 564-567, pagination is applied only on truthy values. Passing limit=0 skips .limit(...) entirely, which defeats the new pagination guard and can cause heavy queries. Use bounds and is not None checks.

Suggested fix
-    limit: Annotated[int | None, Query()] = 30,
-    offset: Annotated[int | None, Query()] = 0,
+    limit: Annotated[int, Query(ge=1, le=200)] = 30,
+    offset: Annotated[int, Query(ge=0)] = 0,
...
-        if offset:
-            stmt = stmt.offset(offset)
-        if limit:
-            stmt = stmt.limit(limit)
+        stmt = stmt.offset(offset).limit(limit)

Also applies to: 267-270, 536-537, 564-567

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/backend/base/langflow/api/v1/monitor.py` around lines 231 - 232, The
pagination logic at multiple locations (including lines 267-270, 536-537, and
564-567) uses truthy checks on the limit parameter, which means limit=0 is
treated as falsy and skips the pagination guard entirely. Replace all truthy
checks (if limit:) with explicit `is not None` checks to properly validate the
limit parameter. Additionally, add bounds validation to ensure the limit
parameter is greater than 0 before applying it in the query, preventing zero
values from bypassing pagination and causing full-table scans. This applies to
all locations where pagination is applied to query results.

Comment on lines +261 to 266
order_col = getattr(MessageTable, order_by)
if order and order.lower() == "desc":
order_col = order_col.desc()
else:
order_col = order_col.asc()
stmt = stmt.order_by(order_col)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Validate order_by and order in get_messages before building the ORDER BY clause.

At Line 261, getattr(MessageTable, order_by) is unguarded. Invalid values can trigger server errors instead of a 4xx client error, and order currently accepts any string (non-desc silently becomes ascending). Mirror the shared-endpoint validation path.

Suggested fix
+        allowed_order_fields = {"timestamp", "sender", "sender_name", "session_id", "text"}
         if order_by:
+            if order_by not in allowed_order_fields:
+                raise HTTPException(status_code=400, detail=f"Invalid order_by field: {order_by}")
+            if order not in {"asc", "desc"}:
+                raise HTTPException(status_code=400, detail=f"Invalid order: {order}")
             order_col = getattr(MessageTable, order_by)
-            if order and order.lower() == "desc":
+            if order == "desc":
                 order_col = order_col.desc()
             else:
                 order_col = order_col.asc()
             stmt = stmt.order_by(order_col)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/backend/base/langflow/api/v1/monitor.py` around lines 261 - 266, Validate
both the order_by and order parameters in the get_messages function before
constructing the ORDER BY clause at line 261. Add validation to ensure order_by
is a valid column name on the MessageTable class, raising a 400 Bad Request
error if it is not, and validate that order is either "asc" or "desc"
(case-insensitive), defaulting to "asc" or raising an error for invalid values.
This prevents the unguarded getattr call from triggering server errors and
ensures the order parameter is explicitly handled rather than silently
defaulting for any non-desc value.

- Add _VALID_ORDER_BY_FIELDS whitelist {'timestamp', 'sender', 'sender_name',
  'session_id', 'flow_id'} and validate order_by before calling getattr(),
  returning HTTP 422 for unknown fields instead of a 500 AttributeError.
- Change 'if limit:' to 'if limit is not None:' so that limit=0 (fetch all)
  is applied correctly rather than silently skipped; add a bounds check that
  rejects negative values with HTTP 422.

Addresses CodeRabbit review comments on PR langflow-ai#13759.
@gmrnlg1971

Copy link
Copy Markdown
Author

✅ CodeRabbit Review Fixes Applied

Thank you for the detailed review! Both flagged issues have been addressed:

1. monitor.py — Whitelist validation for order_by

Previously, getattr(MessageTable, order_by) was called directly on the user-supplied string with no validation, which would raise an AttributeError (surfaced as a 500) for any invalid field name.

Fix: A _VALID_ORDER_BY_FIELDS set {"timestamp", "sender", "sender_name", "session_id", "flow_id"} is now checked before getattr(). Unknown values return HTTP 422 with a descriptive message.

_VALID_ORDER_BY_FIELDS = {"timestamp", "sender", "sender_name", "session_id", "flow_id"}
if order_by:
    if order_by not in _VALID_ORDER_BY_FIELDS:
        raise HTTPException(
            status_code=422,
            detail=f"Invalid order_by field. Must be one of: {sorted(_VALID_ORDER_BY_FIELDS)}",
        )
    order_col = getattr(MessageTable, order_by)
    ...

2. monitor.py — Fix limit=0 silently bypassing pagination

Previously if limit: would evaluate 0 as falsy, so limit=0 (meaning "return all rows") would silently skip the .limit() call and also skip itself — actually the same result, but the intent was wrong and fragile.

Fix: Changed to if limit is not None: with an added negative-value guard:

if limit is not None:
    if limit < 0:
        raise HTTPException(status_code=422, detail="limit must be >= 0")
    stmt = stmt.limit(limit)

This correctly handles limit=0 (fetch all rows via SQLAlchemy .limit(0) semantics) and explicitly rejects negative values.

@github-actions github-actions Bot added bug Something isn't working and removed bug Something isn't working labels Jun 22, 2026
@gmrnlg1971

Copy link
Copy Markdown
Author

Thanks for the review! I've hardened get_messages by adding a strict whitelist validation for the order_by field, returning a 422 if an invalid field is passed. I also fixed the pagination bypass by changing if limit: to if limit is not None: and added bounds checking. Let me know if anything else is needed!

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

Labels

bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant