Skip to content

feat: updated component with new Auth mode support.#9756

Merged
edwinjosechittilappilly merged 4 commits into
langflow-ai:mainfrom
ComposioHQ:v3-2-SepUpdatedBaseComponent
Sep 12, 2025
Merged

feat: updated component with new Auth mode support.#9756
edwinjosechittilappilly merged 4 commits into
langflow-ai:mainfrom
ComposioHQ:v3-2-SepUpdatedBaseComponent

Conversation

@Uday-sidagana
Copy link
Copy Markdown
Contributor

@Uday-sidagana Uday-sidagana commented Sep 8, 2025

Update: Composio Base Component

File: /src/backend/base/langflow/base/composio/composio_base.py

  • Updated Composio component to support multiple Auth modes (API-KEY, BEARER, BASIC) and allow custom OAuth2 credentials for components without a default Composio Auth App.
  • Included UI suggestion: action button moved above Auth mode selector.
  • Fixed an error introduced post the new directory structure. Details below.

Error:
Screenshot 2025-09-09 at 03 37 05

Fix:
Changed _get_tools from async to sync, since to_toolkit is synchronous in component.py.

Summary by CodeRabbit

  • New Features

    • Dynamic authentication UI with mode selection (dropdown or pills) and auto-generated fields for OAuth2/API Key.
    • JSON action inputs supported as single multiline/code editors.
  • Improvements

    • Faster, more consistent tool and schema loading with per-instance caching.
    • Auth options auto-refresh after actions are populated.
  • Bug Fixes

    • More reliable connection handling and API key updates, including soft disconnect.
    • Improved validation and error handling to avoid crashes on unexpected schemas.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Sep 8, 2025

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.

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

Walkthrough

Introduces schema-driven, dynamic authentication UI and JSON input handling, adds per-instance toolkit schema caching, refactors tool-loading to synchronous, removes a graph patch, and updates connection/auth lifecycle. Multiple helper methods are added; action population, validation, and error handling are adjusted within ComposioBaseComponent.

Changes

Cohort / File(s) Summary of changes
Schema caching and plain-dict utils
src/lfx/src/lfx/base/composio/composio_base.py
Added per-instance toolkit schema cache (_get_toolkit_schema), plain dict conversion (_to_plain_dict).
Auth mode UI and dynamic fields
src/lfx/src/lfx/base/composio/composio_base.py
Added auth_mode input, dropdown rendering (_render_auth_mode_dropdown), mode extraction (_extract_auth_modes_from_schema), dynamic field rendering (_render_custom_auth_fields, _add_text_field), and clearing helpers (_clear_auth_dynamic_fields, _clear_auth_fields_from_schema, _collect_all_auth_field_names).
Schema inspection helpers
src/lfx/src/lfx/base/composio/composio_base.py
Added _get_schema_field_names, _get_schema_required_entries to parse mode-specific sections.
Action inputs and JSON handling
src/lfx/src/lfx/base/composio/composio_base.py
Updated actions population/validation to treat top-level object/array fields as single multiline/code inputs and skip subfields; added _hide_all_action_fields.
Connection/auth lifecycle
src/lfx/src/lfx/base/composio/composio_base.py
Added _get_connection_auth_info; updated flows to clear connection state on API key changes, soft-disconnect specific connections.
Tool loading refactor
src/lfx/src/lfx/base/composio/composio_base.py
_get_tools changed from async to sync.
Removed graph patch
src/lfx/src/lfx/base/composio/composio_base.py
Removed _patch_graph_clean_null_input_types and its import-time invocation.
Imports and input types
src/lfx/src/lfx/base/composio/composio_base.py
Added json, create_input_schema_from_json_schema; expanded UI inputs: DropdownInput, StrInput, TabInput, MultilineInput, CodeInput.
Error handling/refactoring
src/lfx/src/lfx/base/composio/composio_base.py
Broader try/except guards and reduced mutation of input_types during execute/validation paths.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  actor User
  participant Component as ComposioBaseComponent
  participant Toolkit as Toolkit Schema Cache
  participant UI as Builder UI

  User->>Component: Select toolkit / open config
  Component->>Toolkit: _get_toolkit_schema()
  Toolkit-->>Component: Schema (cached or fetched)
  Component->>Component: _extract_auth_modes_from_schema(schema)
  Component->>UI: _render_auth_mode_dropdown(modes)
  alt User selects managed mode (e.g., OAUTH2)
    Component->>UI: Render managed auth controls (auth link, status)
  else User selects custom mode (e.g., API_KEY)
    Component->>Component: _render_custom_auth_fields(schema, mode)
    Component->>UI: Add dynamic fields (required/optional)
  end
Loading
sequenceDiagram
  autonumber
  participant Component as ComposioBaseComponent
  participant Actions as Actions Builder
  participant Schema as Toolkit Schema

  Component->>Actions: Populate actions
  Component->>Schema: Inspect action input schema
  alt Top-level object/array input
    Component->>Actions: Render single Multiline/Code input
    note right of Actions: Subfields skipped in flattening
  else Primitive inputs
    Component->>Actions: Render individual fields
  end
Loading
sequenceDiagram
  autonumber
  participant Caller as Caller
  participant Component as ComposioBaseComponent
  participant Tools as Tools Registry

  Caller->>Component: Request tools
  Component->>Tools: _get_tools() (sync)
  Tools-->>Component: List[Tool]
  Component-->>Caller: Tools
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Pre-merge checks (1 passed, 2 warnings)

❌ Failed Checks (2 warnings)
Check Name Status Explanation Resolution
Description Check ⚠️ Warning The current description only highlights a few changes such as Auth mode support, UI suggestion, and a sync fix, but omits many substantial updates like schema caching, JSON input handling, helper utilities, and enhanced error handling, providing insufficient context and rationale for reviewers. Please expand the description to summarize all major changes, including toolkit schema caching, dynamic JSON input support, new helper methods, and error-handling improvements, and consider adding a PR template to ensure complete and consistent information in future submissions.
Docstring Coverage ⚠️ Warning Docstring coverage is 64.29% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed Checks (1 passed)
Check Name Status Explanation
Title Check ✅ Passed The title concisely and accurately highlights the primary feature added—support for new Auth modes—without extraneous details, making it clear to reviewers scanning the history.
✨ Finishing touches
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment

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 8, 2025
@autofix-ci
Copy link
Copy Markdown
Contributor

autofix-ci Bot commented Sep 8, 2025

Hi! I'm autofix logoautofix.ci, a bot that automatically fixes trivial issues such as code formatting in pull requests.

I would like to apply some automated changes to this pull request, but it looks like I don't have the necessary permissions to do so. To get this pull request into a mergeable state, please do one of the following two things:

  1. Allow edits by maintainers for your pull request, and then re-trigger CI (for example by pushing a new commit).
  2. Manually fix the issues identified for your pull request (see the GitHub Actions output for details on what I would like to change).

@github-actions github-actions Bot added enhancement New feature or request and removed enhancement New feature or request labels Sep 8, 2025
Copy link
Copy Markdown
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: 8

Caution

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

⚠️ Outside diff range comments (1)
src/lfx/src/lfx/base/composio/composio_base.py (1)

794-804: Don’t overwrite selected OAUTH2 auth_config; fallback only if none found.

This unconditionally assigns the first item, discarding an OAUTH2 match found in the loop.

-            else:
-                auth_config_id = None
-                for auth_config in auth_configs.items:
-                    if auth_config.auth_scheme == "OAUTH2":
-                        auth_config_id = auth_config.id
-
-                auth_config_id = auth_configs.items[0].id
+            else:
+                auth_config_id = None
+                for auth_config in auth_configs.items:
+                    if getattr(auth_config, "auth_scheme", None) == "OAUTH2":
+                        auth_config_id = auth_config.id
+                        break
+                if auth_config_id is None:
+                    auth_config_id = auth_configs.items[0].id
🧹 Nitpick comments (7)
src/lfx/src/lfx/base/composio/composio_base.py (7)

354-376: Normalize file-upload field names to lowercase to match later .lower() checks.

inp.name.lower() in file_upload_fields assumes the set is lowercase. Normalize on insert to avoid mismatches.

-                                if field_schema.get("file_uploadable") is True:
-                                    file_upload_fields.add(clean_field_name)
+                                if field_schema.get("file_uploadable") is True:
+                                    file_upload_fields.add(clean_field_name.lower())
@@
-                                        if isinstance(any_of_item, dict) and any_of_item.get("file_uploadable") is True:
-                                            file_upload_fields.add(clean_field_name)
+                                        if isinstance(any_of_item, dict) and any_of_item.get("file_uploadable") is True:
+                                            file_upload_fields.add(clean_field_name.lower())
@@
-                            file_upload_fields.add("attachment")  # Attachment fields are also file upload fields
+                            file_upload_fields.add("attachment")  # already lowercase
@@
-                    file_upload_fields = file_upload_fields | {"attachment"}
+                    file_upload_fields = file_upload_fields | {"attachment"}

Also applies to: 399-406, 606-609


960-1002: Handle empty/duplicate auth modes gracefully.

If modes is empty, hide the control; also dedupe/normalize to avoid inconsistent casing.

-            if len(modes) <= 1:
+            # Normalize/dedupe
+            modes = sorted({m.upper() for m in modes if isinstance(m, str)})
+            if not modes:
+                auth_mode_cfg["show"] = False
+                return
+            if len(modes) == 1:

713-719: Avoid recomputing all action input schemas on every update.

_get_inputs_for_all_actions calls _validate_schema_inputs for every action, even when only one action is selected. Cache per-action inputs or compute on demand for the selected action to reduce update latency.

Also applies to: 720-730


1958-1969: JSON coercion on execute: use schema of original field when renamed.

For renamed fields (e.g., <app>_user_id), schema_properties.get(field) returns {} and JSON coercion is skipped. Consider mapping back to original names before schema lookup.

Also applies to: 1974-1976


1221-1233: Soft-disconnect UX: consider clearing dynamic auth fields too.

After soft disconnect, also clear/hide any custom auth fields rendered for a mode so the UI resets cleanly.

             build_config["action_button"]["helper_text_metadata"] = {"variant": "destructive"}
-            return build_config
+            try:
+                schema = self._get_toolkit_schema()
+                self._clear_auth_fields_from_schema(build_config, schema)
+            except Exception:  # noqa: BLE001
+                pass
+            return build_config

943-959: Mode normalization: ensure API_KEY/BEARER/BASIC/OAUTH2 casing is consistent.

Upcasing returned modes avoids UI mismatches when comparing with stored values and SDK outputs.

-        return modes
+        return [m.upper() for m in modes]

596-609: Guard file-upload field comparison against case differences.

If not normalizing set items, compare with both raw and lowered names to be robust.

-                        if inp.name.lower() in file_upload_fields or inp.name.lower() == "attachment":
+                        if (inp.name in file_upload_fields)
+                        or (inp.name.lower() in file_upload_fields)
+                        or (inp.name.lower() == "attachment"):
📜 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 a90bece and a129f34.

📒 Files selected for processing (1)
  • src/lfx/src/lfx/base/composio/composio_base.py (25 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
src/lfx/src/lfx/base/composio/composio_base.py (1)
src/backend/base/langflow/api/v1/endpoints.py (1)
  • custom_component (666-684)
🪛 GitHub Check: Ruff Style Check (3.13)
src/lfx/src/lfx/base/composio/composio_base.py

[failure] 23-23: Ruff (F401)
src/lfx/src/lfx/base/composio/composio_base.py:23:5: F401 lfx.inputs.inputs.CodeInput imported but unused


[failure] 132-132: Ruff (BLE001)
src/lfx/src/lfx/base/composio/composio_base.py:132:16: BLE001 Do not catch blind exception: Exception


[failure] 616-616: Ruff (BLE001)
src/lfx/src/lfx/base/composio/composio_base.py:616:24: BLE001 Do not catch blind exception: Exception


[failure] 704-704: Ruff (BLE001)
src/lfx/src/lfx/base/composio/composio_base.py:704:24: BLE001 Do not catch blind exception: Exception


[failure] 874-874: Ruff (BLE001)
src/lfx/src/lfx/base/composio/composio_base.py:874:16: BLE001 Do not catch blind exception: Exception


[failure] 873-873: Ruff (TRY300)
src/lfx/src/lfx/base/composio/composio_base.py:873:13: TRY300 Consider moving this statement to an else block


[failure] 907-907: Ruff (TRY300)
src/lfx/src/lfx/base/composio/composio_base.py:907:13: TRY300 Consider moving this statement to an else block


[failure] 905-905: Ruff (BLE001)
src/lfx/src/lfx/base/composio/composio_base.py:905:24: BLE001 Do not catch blind exception: Exception


[failure] 900-900: Ruff (BLE001)
src/lfx/src/lfx/base/composio/composio_base.py:900:24: BLE001 Do not catch blind exception: Exception

🪛 GitHub Actions: Ruff Style Check
src/lfx/src/lfx/base/composio/composio_base.py

[error] 1-1: Ruff: I001 Import block is un-sorted or un-formatted (I001)

⏰ 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). (1)
  • GitHub Check: Update Starter Projects
🔇 Additional comments (3)
src/lfx/src/lfx/base/composio/composio_base.py (3)

1896-1901: Sync _get_tools matches synchronous to_toolkit usage.

Switch to sync looks correct and aligns with the referenced sync caller. No further changes needed here.


1896-1901: No unguarded awaiters of _get_tools remain; conversion to sync is safe.


10-24: Keep CodeInput importCodeInput is instantiated later in this file to render JSON parent fields (e.g. within the loop at lines 686–688); do not remove it.

Likely an incorrect or invalid review comment.

Comment on lines +10 to +24
from lfx.base.mcp.util import create_input_schema_from_json_schema
from lfx.custom.custom_component.component import Component
from lfx.inputs.inputs import AuthInput, FileInput, InputTypes, MessageTextInput, SecretStrInput, SortableListInput
from lfx.inputs.inputs import (
AuthInput,
FileInput,
InputTypes,
MessageTextInput,
SecretStrInput,
SortableListInput,
DropdownInput,
StrInput,
TabInput,
MultilineInput,
CodeInput,
)
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.

🛠️ Refactor suggestion

Fix import ordering and remove unused CodeInput (ruff I001, F401).

Ruff is failing on unsorted imports and an unused symbol. Sort imports into stdlib/third-party/local groups and drop CodeInput.

-import copy
-import re
-from typing import Any
-import json
+import copy
+import json
+import re
+from typing import Any
@@
-from lfx.inputs.inputs import (
-    AuthInput,
-    FileInput,
-    InputTypes,
-    MessageTextInput,
-    SecretStrInput,
-    SortableListInput,
-    DropdownInput,
-    StrInput,
-    TabInput,
-    MultilineInput,
-    CodeInput,
-)
+from lfx.inputs.inputs import (
+    AuthInput,
+    DropdownInput,
+    FileInput,
+    InputTypes,
+    MessageTextInput,
+    MultilineInput,
+    SecretStrInput,
+    SortableListInput,
+    StrInput,
+    TabInput,
+)
📝 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
from lfx.base.mcp.util import create_input_schema_from_json_schema
from lfx.custom.custom_component.component import Component
from lfx.inputs.inputs import AuthInput, FileInput, InputTypes, MessageTextInput, SecretStrInput, SortableListInput
from lfx.inputs.inputs import (
AuthInput,
FileInput,
InputTypes,
MessageTextInput,
SecretStrInput,
SortableListInput,
DropdownInput,
StrInput,
TabInput,
MultilineInput,
CodeInput,
)
import copy
import json
import re
from typing import Any
from lfx.base.mcp.util import create_input_schema_from_json_schema
from lfx.custom.custom_component.component import Component
from lfx.inputs.inputs import (
AuthInput,
DropdownInput,
FileInput,
InputTypes,
MessageTextInput,
MultilineInput,
SecretStrInput,
SortableListInput,
StrInput,
TabInput,
)
🧰 Tools
🪛 GitHub Check: Ruff Style Check (3.13)

[failure] 23-23: Ruff (F401)
src/lfx/src/lfx/base/composio/composio_base.py:23:5: F401 lfx.inputs.inputs.CodeInput imported but unused

🤖 Prompt for AI Agents
In src/lfx/src/lfx/base/composio/composio_base.py around lines 10 to 24, the
import block is unsorted and includes an unused symbol CodeInput causing ruff
errors (I001, F401); reorder imports into stdlib, third-party, then local groups
and remove CodeInput from the inputs import list, keeping only the actually used
names (AuthInput, FileInput, InputTypes, MessageTextInput, SecretStrInput,
SortableListInput, DropdownInput, StrInput, TabInput, MultilineInput), ensuring
alphabetic ordering within each group.

Comment on lines +127 to +135
# which interferes with logging utilities that probe for '.data'.
df = DataFrame(result)
try:
if "data" in df.columns:
df = df.rename(columns={"data": "_data"})
except Exception:
# If any unexpected structure, return the DataFrame as-is
pass
return df
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.

🛠️ Refactor suggestion

Narrow broad except in as_dataframe (ruff BLE001).

Catching all Exceptions here hides real errors and fails Ruff. Limit to likely issues.

-        try:
+        try:
             if "data" in df.columns:
                 df = df.rename(columns={"data": "_data"})
-        except Exception:
+        except (AttributeError, KeyError, TypeError):
             # If any unexpected structure, return the DataFrame as-is
             pass
📝 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
# which interferes with logging utilities that probe for '.data'.
df = DataFrame(result)
try:
if "data" in df.columns:
df = df.rename(columns={"data": "_data"})
except Exception:
# If any unexpected structure, return the DataFrame as-is
pass
return df
# which interferes with logging utilities that probe for '.data'.
df = DataFrame(result)
try:
if "data" in df.columns:
df = df.rename(columns={"data": "_data"})
except (AttributeError, KeyError, TypeError):
# If any unexpected structure, return the DataFrame as-is
pass
return df
🧰 Tools
🪛 GitHub Check: Ruff Style Check (3.13)

[failure] 132-132: Ruff (BLE001)
src/lfx/src/lfx/base/composio/composio_base.py:132:16: BLE001 Do not catch blind exception: Exception

🤖 Prompt for AI Agents
In src/lfx/src/lfx/base/composio/composio_base.py around lines 127 to 135, the
broad "except Exception" in as_dataframe should be narrowed to only the expected
errors so real bugs aren't swallowed and Ruff BLE001 is satisfied; replace the
bare except with a specific exception tuple (e.g. except (KeyError,
AttributeError, TypeError, ValueError):) that covers likely issues when
checking/renaming columns, or handle pandas-specific errors if applicable, and
leave the existing fallback behavior (pass and return df) intact.

Comment on lines 548 to 551
field_schema_copy = field_schema.copy()
field_schema_copy["description"] = (
f"User ID for {self.app_name.title()}: " + field_schema["description"]
)
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

Avoid KeyError when augmenting user_id description.

field_schema["description"] may be missing. Use .get to prevent a crash.

-                    field_schema_copy["description"] = (
-                        f"User ID for {self.app_name.title()}: " + field_schema["description"]
-                    )
+                    field_schema_copy["description"] = (
+                        f"User ID for {self.app_name.title()}: "
+                        + field_schema.get("description", "")
+                    )
📝 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
field_schema_copy = field_schema.copy()
field_schema_copy["description"] = (
f"User ID for {self.app_name.title()}: " + field_schema["description"]
)
field_schema_copy = field_schema.copy()
field_schema_copy["description"] = (
f"User ID for {self.app_name.title()}: "
field_schema.get("description", "")
)
🤖 Prompt for AI Agents
In src/lfx/src/lfx/base/composio/composio_base.py around lines 548 to 551, the
code assumes field_schema["description"] always exists which can raise a
KeyError; change it to use field_schema.get("description", "") (or a suitable
default string) when constructing the augmented description so missing
descriptions don't crash, e.g. read the original with .get and then prepend the
"User ID for {self.app_name.title()}: " text before assigning to
field_schema_copy["description"].

Comment on lines +610 to +617
# Identify top-level JSON parents (object/array) to render as single CodeInput
top_props_for_json = set()
try:
for top_name, top_schema in parameters_schema.get("properties", {}).items():
if isinstance(top_schema, dict) and top_schema.get("type") in {"object", "array"}:
top_props_for_json.add(top_name)
except Exception:
pass
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.

🛠️ Refactor suggestion

Narrow broad excepts in schema processing (ruff BLE001).

Limit to type/shape errors in these defensive blocks.

-                except Exception:
+                except (AttributeError, TypeError):
                     pass
@@
-                except Exception:
+                except (KeyError, TypeError):
                     pass

Also applies to: 704-705

🧰 Tools
🪛 GitHub Check: Ruff Style Check (3.13)

[failure] 616-616: Ruff (BLE001)
src/lfx/src/lfx/base/composio/composio_base.py:616:24: BLE001 Do not catch blind exception: Exception

🤖 Prompt for AI Agents
In src/lfx/src/lfx/base/composio/composio_base.py around lines 610-617, the
broad "except Exception:" swallowing all errors should be narrowed to only
expected type/shape issues; replace it with a targeted catch such as "except
(AttributeError, TypeError):" (or include KeyError/ValueError if you confirm
those can occur) so only schema shape/access problems are handled, and make the
same change for the similar defensive block at lines 704-705.

Comment on lines +863 to +877
def _get_connection_auth_info(self, connection_id: str) -> tuple[str | None, bool | None]:
"""Return (auth_scheme, is_composio_managed) for a given connection id, if available."""
try:
composio = self._build_wrapper()
connection = composio.connected_accounts.get(nanoid=connection_id)
auth_config = getattr(connection, "auth_config", None)
if auth_config is None and hasattr(connection, "__dict__"):
auth_config = getattr(connection.__dict__, "auth_config", None)
scheme = getattr(auth_config, "auth_scheme", None) if auth_config else None
is_managed = getattr(auth_config, "is_composio_managed", None) if auth_config else None
return scheme, is_managed
except Exception as e:
logger.debug(f"Could not retrieve auth info for connection {connection_id}: {e}")
return None, None

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

Fix dict attribute access and address Ruff TRY300/BLE001 in _get_connection_auth_info.

getattr(connection.__dict__, "auth_config", None) is wrong; __dict__ is a dict. Also restructure try/except to use else and narrow exceptions.

     def _get_connection_auth_info(self, connection_id: str) -> tuple[str | None, bool | None]:
         """Return (auth_scheme, is_composio_managed) for a given connection id, if available."""
-        try:
+        try:
             composio = self._build_wrapper()
             connection = composio.connected_accounts.get(nanoid=connection_id)
             auth_config = getattr(connection, "auth_config", None)
-            if auth_config is None and hasattr(connection, "__dict__"):
-                auth_config = getattr(connection.__dict__, "auth_config", None)
+            if auth_config is None:
+                data = getattr(connection, "__dict__", None)
+                if isinstance(data, dict):
+                    auth_config = data.get("auth_config")
             scheme = getattr(auth_config, "auth_scheme", None) if auth_config else None
             is_managed = getattr(auth_config, "is_composio_managed", None) if auth_config else None
-            return scheme, is_managed
-        except Exception as e:
+        except (ValueError, ConnectionError, AttributeError, KeyError) as e:
             logger.debug(f"Could not retrieve auth info for connection {connection_id}: {e}")
-            return None, None
+            return None, None
+        else:
+            return scheme, is_managed
📝 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 _get_connection_auth_info(self, connection_id: str) -> tuple[str | None, bool | None]:
"""Return (auth_scheme, is_composio_managed) for a given connection id, if available."""
try:
composio = self._build_wrapper()
connection = composio.connected_accounts.get(nanoid=connection_id)
auth_config = getattr(connection, "auth_config", None)
if auth_config is None and hasattr(connection, "__dict__"):
auth_config = getattr(connection.__dict__, "auth_config", None)
scheme = getattr(auth_config, "auth_scheme", None) if auth_config else None
is_managed = getattr(auth_config, "is_composio_managed", None) if auth_config else None
return scheme, is_managed
except Exception as e:
logger.debug(f"Could not retrieve auth info for connection {connection_id}: {e}")
return None, None
def _get_connection_auth_info(self, connection_id: str) -> tuple[str | None, bool | None]:
"""Return (auth_scheme, is_composio_managed) for a given connection id, if available."""
try:
composio = self._build_wrapper()
connection = composio.connected_accounts.get(nanoid=connection_id)
auth_config = getattr(connection, "auth_config", None)
if auth_config is None:
data = getattr(connection, "__dict__", None)
if isinstance(data, dict):
auth_config = data.get("auth_config")
scheme = getattr(auth_config, "auth_scheme", None) if auth_config else None
is_managed = getattr(auth_config, "is_composio_managed", None) if auth_config else None
except (ValueError, ConnectionError, AttributeError, KeyError) as e:
logger.debug(f"Could not retrieve auth info for connection {connection_id}: {e}")
return None, None
else:
return scheme, is_managed
🧰 Tools
🪛 GitHub Check: Ruff Style Check (3.13)

[failure] 874-874: Ruff (BLE001)
src/lfx/src/lfx/base/composio/composio_base.py:874:16: BLE001 Do not catch blind exception: Exception


[failure] 873-873: Ruff (TRY300)
src/lfx/src/lfx/base/composio/composio_base.py:873:13: TRY300 Consider moving this statement to an else block

Comment on lines +890 to +910
def _to_plain_dict(self, obj: Any) -> Any:
"""Recursively convert SDK models/lists to plain Python dicts/lists for safe .get access."""
try:
if isinstance(obj, dict):
return {k: self._to_plain_dict(v) for k, v in obj.items()}
if isinstance(obj, (list, tuple, set)):
return [self._to_plain_dict(v) for v in obj]
if hasattr(obj, "model_dump"):
try:
return self._to_plain_dict(obj.model_dump())
except Exception:
pass
if hasattr(obj, "__dict__") and not isinstance(obj, (str, bytes)):
try:
return self._to_plain_dict({k: v for k, v in obj.__dict__.items() if not k.startswith("_")})
except Exception:
pass
return obj
except Exception:
return obj

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.

🛠️ Refactor suggestion

Refactor _to_plain_dict to avoid broad try and TRY300.

Remove the outer broad try/except; keep targeted inner guards.

-    def _to_plain_dict(self, obj: Any) -> Any:
-        """Recursively convert SDK models/lists to plain Python dicts/lists for safe .get access."""
-        try:
-            if isinstance(obj, dict):
-                return {k: self._to_plain_dict(v) for k, v in obj.items()}
-            if isinstance(obj, (list, tuple, set)):
-                return [self._to_plain_dict(v) for v in obj]
-            if hasattr(obj, "model_dump"):
-                try:
-                    return self._to_plain_dict(obj.model_dump())
-                except Exception:
-                    pass
-            if hasattr(obj, "__dict__") and not isinstance(obj, (str, bytes)):
-                try:
-                    return self._to_plain_dict({k: v for k, v in obj.__dict__.items() if not k.startswith("_")})
-                except Exception:
-                    pass
-            return obj
-        except Exception:
-            return obj
+    def _to_plain_dict(self, obj: Any) -> Any:
+        """Recursively convert SDK models/lists to plain Python dicts/lists for safe .get access."""
+        if isinstance(obj, dict):
+            return {k: self._to_plain_dict(v) for k, v in obj.items()}
+        if isinstance(obj, (list, tuple, set)):
+            return [self._to_plain_dict(v) for v in obj]
+        if hasattr(obj, "model_dump"):
+            try:
+                return self._to_plain_dict(obj.model_dump())
+            except Exception:  # noqa: BLE001
+                pass
+        if hasattr(obj, "__dict__") and not isinstance(obj, (str, bytes)):
+            try:
+                return self._to_plain_dict({k: v for k, v in obj.__dict__.items() if not k.startswith("_")})
+            except Exception:  # noqa: BLE001
+                pass
+        return obj
📝 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 _to_plain_dict(self, obj: Any) -> Any:
"""Recursively convert SDK models/lists to plain Python dicts/lists for safe .get access."""
try:
if isinstance(obj, dict):
return {k: self._to_plain_dict(v) for k, v in obj.items()}
if isinstance(obj, (list, tuple, set)):
return [self._to_plain_dict(v) for v in obj]
if hasattr(obj, "model_dump"):
try:
return self._to_plain_dict(obj.model_dump())
except Exception:
pass
if hasattr(obj, "__dict__") and not isinstance(obj, (str, bytes)):
try:
return self._to_plain_dict({k: v for k, v in obj.__dict__.items() if not k.startswith("_")})
except Exception:
pass
return obj
except Exception:
return obj
def _to_plain_dict(self, obj: Any) -> Any:
"""Recursively convert SDK models/lists to plain Python dicts/lists for safe .get access."""
if isinstance(obj, dict):
return {k: self._to_plain_dict(v) for k, v in obj.items()}
if isinstance(obj, (list, tuple, set)):
return [self._to_plain_dict(v) for v in obj]
if hasattr(obj, "model_dump"):
try:
return self._to_plain_dict(obj.model_dump())
except Exception: # noqa: BLE001
pass
if hasattr(obj, "__dict__") and not isinstance(obj, (str, bytes)):
try:
return self._to_plain_dict({k: v for k, v in obj.__dict__.items() if not k.startswith("_")})
except Exception: # noqa: BLE001
pass
return obj
🧰 Tools
🪛 GitHub Check: Ruff Style Check (3.13)

[failure] 907-907: Ruff (TRY300)
src/lfx/src/lfx/base/composio/composio_base.py:907:13: TRY300 Consider moving this statement to an else block


[failure] 905-905: Ruff (BLE001)
src/lfx/src/lfx/base/composio/composio_base.py:905:24: BLE001 Do not catch blind exception: Exception


[failure] 900-900: Ruff (BLE001)
src/lfx/src/lfx/base/composio/composio_base.py:900:24: BLE001 Do not catch blind exception: Exception

🤖 Prompt for AI Agents
In src/lfx/src/lfx/base/composio/composio_base.py around lines 890 to 910,
remove the outermost try/except that swallows all exceptions and instead rely on
the existing targeted inner guards; keep the isinstance checks for
dict/list/tuple/set, the hasattr(obj, "model_dump") block with its inner
try/except, and the hasattr(obj, "__dict__") block with its inner try/except,
and ensure the final return falls through to returning obj (no broad exception
handling). Adjust indentation accordingly and delete the outer except block so
unexpected errors surface while preserving the inner defensive catches.

Comment on lines +1243 to +1249
build_config.setdefault("auth_link", {})
build_config["auth_link"]["show"] = False
# Reset connection state when switching modes
build_config["auth_link"].pop("connection_id", None)
build_config["auth_link"].pop("auth_config_id", None)
build_config["auth_link"]["value"] = "connect"
build_config["auth_link"]["auth_tooltip"] = "Connect"
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.

🛠️ Refactor suggestion

Auth link should be visible on mode change (comment contradicts code).

Comment says “Always show auth_link,” but show=False hides it. Set to True.

-            build_config.setdefault("auth_link", {})
-            build_config["auth_link"]["show"] = False
+            build_config.setdefault("auth_link", {})
+            build_config["auth_link"]["show"] = True
📝 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
build_config.setdefault("auth_link", {})
build_config["auth_link"]["show"] = False
# Reset connection state when switching modes
build_config["auth_link"].pop("connection_id", None)
build_config["auth_link"].pop("auth_config_id", None)
build_config["auth_link"]["value"] = "connect"
build_config["auth_link"]["auth_tooltip"] = "Connect"
build_config.setdefault("auth_link", {})
build_config["auth_link"]["show"] = True
# Reset connection state when switching modes
build_config["auth_link"].pop("connection_id", None)
build_config["auth_link"].pop("auth_config_id", None)
build_config["auth_link"]["value"] = "connect"
build_config["auth_link"]["auth_tooltip"] = "Connect"
🤖 Prompt for AI Agents
In src/lfx/src/lfx/base/composio/composio_base.py around lines 1243 to 1249, the
code sets build_config["auth_link"]["show"] = False while the comment states
"Always show auth_link"; change the value to True so the auth link is visible on
mode change and update the inline comment to match (or remove the contradictory
comment) and keep the rest of the auth_link population logic unchanged.

Comment on lines +1654 to +1664
build_config.setdefault("auth_link", {})
build_config["auth_link"]["show"] = False
build_config["auth_link"]["display_name"] = ""
try:
schema = self._get_toolkit_schema()
mode = (build_config.get("auth_mode") or {}).get("value")
managed = (schema or {}).get("composio_managed_auth_schemes") or []
if mode and not (isinstance(managed, list) and mode in managed):
if not getattr(self, "_auth_dynamic_fields", set()):
self._render_custom_auth_fields(build_config, schema or {}, mode)
except Exception:
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.

🛠️ Refactor suggestion

Keep auth UI available in Tool Mode.

Block intends to keep auth UI, but hides auth_link. Make it visible.

-            build_config.setdefault("auth_link", {})
-            build_config["auth_link"]["show"] = False
-            build_config["auth_link"]["display_name"] = ""
+            build_config.setdefault("auth_link", {})
+            build_config["auth_link"]["show"] = True
+            # Keep default 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
build_config.setdefault("auth_link", {})
build_config["auth_link"]["show"] = False
build_config["auth_link"]["display_name"] = ""
try:
schema = self._get_toolkit_schema()
mode = (build_config.get("auth_mode") or {}).get("value")
managed = (schema or {}).get("composio_managed_auth_schemes") or []
if mode and not (isinstance(managed, list) and mode in managed):
if not getattr(self, "_auth_dynamic_fields", set()):
self._render_custom_auth_fields(build_config, schema or {}, mode)
except Exception:
build_config.setdefault("auth_link", {})
build_config["auth_link"]["show"] = True
# Keep default display_name
try:
schema = self._get_toolkit_schema()
mode = (build_config.get("auth_mode") or {}).get("value")
managed = (schema or {}).get("composio_managed_auth_schemes") or []
if mode and not (isinstance(managed, list) and mode in managed):
if not getattr(self, "_auth_dynamic_fields", set()):
self._render_custom_auth_fields(build_config, schema or {}, mode)
except Exception:
🤖 Prompt for AI Agents
In src/lfx/src/lfx/base/composio/composio_base.py around lines 1654 to 1664, the
code unconditionally sets build_config["auth_link"]["show"] = False which hides
the auth UI in Tool Mode; update this to keep the auth UI visible by setting
build_config["auth_link"]["show"] = True (or remove the line so existing
visibility is preserved), and ensure display_name remains set as intended; no
other logic changes required.

@github-actions github-actions Bot added enhancement New feature or request and removed enhancement New feature or request labels Sep 8, 2025
Copy link
Copy Markdown
Collaborator

@edwinjosechittilappilly edwinjosechittilappilly left a comment

Choose a reason for hiding this comment

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

LGTM.
Please add test for each of these functions in base.

@github-actions github-actions Bot added enhancement New feature or request and removed enhancement New feature or request labels Sep 12, 2025
@edwinjosechittilappilly edwinjosechittilappilly added this pull request to the merge queue Sep 12, 2025
@github-merge-queue github-merge-queue Bot removed this pull request from the merge queue due to a conflict with the base branch Sep 12, 2025
@edwinjosechittilappilly edwinjosechittilappilly added this pull request to the merge queue Sep 12, 2025
@github-actions github-actions Bot added enhancement New feature or request and removed enhancement New feature or request labels Sep 12, 2025
@sonarqubecloud
Copy link
Copy Markdown

Merged via the queue into langflow-ai:main with commit bd2bb4c Sep 12, 2025
19 checks passed
@edwinjosechittilappilly edwinjosechittilappilly deleted the v3-2-SepUpdatedBaseComponent branch September 12, 2025 19:37
lucaseduoli pushed a commit that referenced this pull request Sep 16, 2025
* feat: updated component with new Auth mode support.

* fix: format

---------

Co-authored-by: Edwin Jose <edwin.jose@datastax.com>
@coderabbitai coderabbitai Bot mentioned this pull request Oct 14, 2025
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.

2 participants