fix: include Google as a default partner bundle - #14220
Conversation
|
Important Review skippedAuto incremental reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro Plus Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
WalkthroughGoogle components are graduated into a standalone ChangesGoogle bundle graduation
Estimated code review effort: 4 (Complex) | ~60 minutes Possibly related PRs
Suggested labels: Suggested reviewers: Important Pre-merge checks failedPlease resolve all errors before merging. Addressing warnings is optional. ❌ Failed checks (1 error, 2 warnings)
✅ Passed checks (6 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
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. Comment |
✅ Test Coverage AdvisorNo source changes detected without accompanying tests. Thanks for keeping coverage up! 🎉
|
This comment has been minimized.
This comment has been minimized.
There was a problem hiding this comment.
Actionable comments posted: 6
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
src/bundles/lfx-bundles/pyproject.toml (1)
53-54: 🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick winKeep
Removing this extra means existing
pip install "lfx-bundles[google]"installations no longer installlfx-google; the updatedlfx.components.googleshim will then fail for those users. Retaingoogle = ["lfx-google>=0.1.0,<1.0.0"]as a compatibility alias while keeping it excluded fromallandall-no-torch.Suggested compatibility extra
[project.optional-dependencies] +google = ["lfx-google>=0.1.0,<1.0.0"]This follows from the shim’s new
lfx-googlerequirement and the removed documented installation extra.🤖 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/bundles/lfx-bundles/pyproject.toml` around lines 53 - 54, Restore the deprecated google extra in pyproject.toml with lfx-google>=0.1.0,<1.0.0 as its dependency. Keep google as a compatibility alias, but do not include it in the all or all-no-torch aggregate extras.
🤖 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/bundles/google/src/lfx_google/components/google/gmail.py`:
- Around line 165-192: Update the max_results conversion in the component method
containing the JSON parsing and loader setup to catch invalid numeric input and
raise a friendly ValueError with a descriptive message, consistent with the
existing JSONDecodeError and document-loading error handling; preserve the
default of 100 when max_results is unset.
In `@src/bundles/google/src/lfx_google/components/google/google_drive_search.py`:
- Around line 105-140: Update search_files to catch JSONDecodeError around
json.loads(self.token_string) and raise ValueError("Invalid JSON string"),
matching the sibling loaders. Import RefreshError from google.auth.exceptions
and wrap the Google Drive service setup/search execution in the same error
handling pattern used by those loaders, converting RefreshError and other API
exceptions into the bundle’s consistent ValueError messages.
In `@src/bundles/google/src/lfx_google/components/google/google_generative_ai.py`:
- Around line 111-124: Update the tool-support filtering in the model-selection
method to instantiate ChatGoogleGenerativeAI with the loop variable model, not
self.model_name. Avoid mutating model_ids during iteration by building a
filtered collection or iterating over a copy, and ensure the no-supported-model
case returns an empty result or otherwise follows the existing validation path
without causing a later IndexError-to-ValueError failure in update_build_config.
- Around line 97-110: Update get_models and the related update_build_config
tool-model validation to use a client-scoped Google Gen AI SDK/API initialized
with self.api_key, removing the process-wide genai.configure() call. Pass the
client explicitly through both model listing and validation paths so concurrent
requests using different API keys remain isolated, while preserving the existing
supported-generation filtering, sorting, and fallback behavior.
In `@src/bundles/google/src/lfx_google/components/google/google_oauth_token.py`:
- Around line 40-54: Update validate_scopes so every repeated scope alternative
requires the same comma-and-optional-whitespace separator, preventing
unseparated values such as mail.google.com/ or calendar feeds from being
concatenated and accepted. Preserve validation of single scopes and properly
comma-separated scopes.
- Around line 55-88: Update build_output to stop using the process-wide
token.json and the blocking InstalledAppFlow.run_local_server flow. Use an
isolated, caller-owned credential store keyed to the current flow or request,
and implement OAuth through a redirect/callback exchange with an explicit
timeout and failure handling before returning the credentials in Data.
---
Outside diff comments:
In `@src/bundles/lfx-bundles/pyproject.toml`:
- Around line 53-54: Restore the deprecated google extra in pyproject.toml with
lfx-google>=0.1.0,<1.0.0 as its dependency. Keep google as a compatibility
alias, but do not include it in the all or all-no-torch aggregate extras.
🪄 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 Plus
Run ID: 2206ccff-cdd0-4412-8f20-f3dab7094951
⛔ Files ignored due to path filters (1)
uv.lockis excluded by!**/*.lock
📒 Files selected for processing (31)
docs/docs/Components/bundles-google.mdxdocs/docs/Lfx/extensions-bundle-list.mdxpyproject.tomlscripts/ci/bundle_profile_locks/enterprise-hardened.lock.jsonscripts/ci/bundle_profiles.jsonscripts/ci/release_inventory_contract.jsonscripts/ci/test_release_inventory.pyscripts/migrate/consolidate_bundles.pysrc/backend/base/langflow/locales/en.jsonsrc/backend/tests/unit/components/bundles/google/test_google_bq_sql_executor_component.pysrc/backend/tests/unit/components/bundles/google/test_google_search_api.pysrc/backend/tests/unit/components/bundles/google/test_google_serper_api_core.pysrc/bundles/google/README.mdsrc/bundles/google/pyproject.tomlsrc/bundles/google/src/lfx_google/__init__.pysrc/bundles/google/src/lfx_google/components/__init__.pysrc/bundles/google/src/lfx_google/components/google/__init__.pysrc/bundles/google/src/lfx_google/components/google/gmail.pysrc/bundles/google/src/lfx_google/components/google/google_bq_sql_executor.pysrc/bundles/google/src/lfx_google/components/google/google_drive.pysrc/bundles/google/src/lfx_google/components/google/google_drive_search.pysrc/bundles/google/src/lfx_google/components/google/google_generative_ai.pysrc/bundles/google/src/lfx_google/components/google/google_generative_ai_embeddings.pysrc/bundles/google/src/lfx_google/components/google/google_oauth_token.pysrc/bundles/google/src/lfx_google/components/google/google_search_api_core.pysrc/bundles/google/src/lfx_google/components/google/google_serper_api_core.pysrc/bundles/google/src/lfx_google/extension.jsonsrc/bundles/google/tests/test_google_generative_ai_model.pysrc/bundles/lfx-bundles/README.mdsrc/bundles/lfx-bundles/pyproject.tomlsrc/lfx/src/lfx/components/google/__init__.py
There was a problem hiding this comment.
Caution
Inline review comments failed to post. This is likely due to GitHub's internal server error or limits when posting large numbers of comments. If you are seeing this consistently it is likely a permissions issue. Please check "Moderation" -> "Code review limits" under your organization settings.
Actionable comments posted: 6
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
src/bundles/lfx-bundles/pyproject.toml (1)
53-54: 🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick winKeep
Removing this extra means existing
pip install "lfx-bundles[google]"installations no longer installlfx-google; the updatedlfx.components.googleshim will then fail for those users. Retaingoogle = ["lfx-google>=0.1.0,<1.0.0"]as a compatibility alias while keeping it excluded fromallandall-no-torch.Suggested compatibility extra
[project.optional-dependencies] +google = ["lfx-google>=0.1.0,<1.0.0"]This follows from the shim’s new
lfx-googlerequirement and the removed documented installation extra.🤖 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/bundles/lfx-bundles/pyproject.toml` around lines 53 - 54, Restore the deprecated google extra in pyproject.toml with lfx-google>=0.1.0,<1.0.0 as its dependency. Keep google as a compatibility alias, but do not include it in the all or all-no-torch aggregate extras.
🤖 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/bundles/google/src/lfx_google/components/google/gmail.py`:
- Around line 165-192: Update the max_results conversion in the component method
containing the JSON parsing and loader setup to catch invalid numeric input and
raise a friendly ValueError with a descriptive message, consistent with the
existing JSONDecodeError and document-loading error handling; preserve the
default of 100 when max_results is unset.
In `@src/bundles/google/src/lfx_google/components/google/google_drive_search.py`:
- Around line 105-140: Update search_files to catch JSONDecodeError around
json.loads(self.token_string) and raise ValueError("Invalid JSON string"),
matching the sibling loaders. Import RefreshError from google.auth.exceptions
and wrap the Google Drive service setup/search execution in the same error
handling pattern used by those loaders, converting RefreshError and other API
exceptions into the bundle’s consistent ValueError messages.
In `@src/bundles/google/src/lfx_google/components/google/google_generative_ai.py`:
- Around line 111-124: Update the tool-support filtering in the model-selection
method to instantiate ChatGoogleGenerativeAI with the loop variable model, not
self.model_name. Avoid mutating model_ids during iteration by building a
filtered collection or iterating over a copy, and ensure the no-supported-model
case returns an empty result or otherwise follows the existing validation path
without causing a later IndexError-to-ValueError failure in update_build_config.
- Around line 97-110: Update get_models and the related update_build_config
tool-model validation to use a client-scoped Google Gen AI SDK/API initialized
with self.api_key, removing the process-wide genai.configure() call. Pass the
client explicitly through both model listing and validation paths so concurrent
requests using different API keys remain isolated, while preserving the existing
supported-generation filtering, sorting, and fallback behavior.
In `@src/bundles/google/src/lfx_google/components/google/google_oauth_token.py`:
- Around line 40-54: Update validate_scopes so every repeated scope alternative
requires the same comma-and-optional-whitespace separator, preventing
unseparated values such as mail.google.com/ or calendar feeds from being
concatenated and accepted. Preserve validation of single scopes and properly
comma-separated scopes.
- Around line 55-88: Update build_output to stop using the process-wide
token.json and the blocking InstalledAppFlow.run_local_server flow. Use an
isolated, caller-owned credential store keyed to the current flow or request,
and implement OAuth through a redirect/callback exchange with an explicit
timeout and failure handling before returning the credentials in Data.
---
Outside diff comments:
In `@src/bundles/lfx-bundles/pyproject.toml`:
- Around line 53-54: Restore the deprecated google extra in pyproject.toml with
lfx-google>=0.1.0,<1.0.0 as its dependency. Keep google as a compatibility
alias, but do not include it in the all or all-no-torch aggregate extras.
🪄 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 Plus
Run ID: 2206ccff-cdd0-4412-8f20-f3dab7094951
⛔ Files ignored due to path filters (1)
uv.lockis excluded by!**/*.lock
📒 Files selected for processing (31)
docs/docs/Components/bundles-google.mdxdocs/docs/Lfx/extensions-bundle-list.mdxpyproject.tomlscripts/ci/bundle_profile_locks/enterprise-hardened.lock.jsonscripts/ci/bundle_profiles.jsonscripts/ci/release_inventory_contract.jsonscripts/ci/test_release_inventory.pyscripts/migrate/consolidate_bundles.pysrc/backend/base/langflow/locales/en.jsonsrc/backend/tests/unit/components/bundles/google/test_google_bq_sql_executor_component.pysrc/backend/tests/unit/components/bundles/google/test_google_search_api.pysrc/backend/tests/unit/components/bundles/google/test_google_serper_api_core.pysrc/bundles/google/README.mdsrc/bundles/google/pyproject.tomlsrc/bundles/google/src/lfx_google/__init__.pysrc/bundles/google/src/lfx_google/components/__init__.pysrc/bundles/google/src/lfx_google/components/google/__init__.pysrc/bundles/google/src/lfx_google/components/google/gmail.pysrc/bundles/google/src/lfx_google/components/google/google_bq_sql_executor.pysrc/bundles/google/src/lfx_google/components/google/google_drive.pysrc/bundles/google/src/lfx_google/components/google/google_drive_search.pysrc/bundles/google/src/lfx_google/components/google/google_generative_ai.pysrc/bundles/google/src/lfx_google/components/google/google_generative_ai_embeddings.pysrc/bundles/google/src/lfx_google/components/google/google_oauth_token.pysrc/bundles/google/src/lfx_google/components/google/google_search_api_core.pysrc/bundles/google/src/lfx_google/components/google/google_serper_api_core.pysrc/bundles/google/src/lfx_google/extension.jsonsrc/bundles/google/tests/test_google_generative_ai_model.pysrc/bundles/lfx-bundles/README.mdsrc/bundles/lfx-bundles/pyproject.tomlsrc/lfx/src/lfx/components/google/__init__.py
🛑 Comments failed to post (6)
src/bundles/google/src/lfx_google/components/google/gmail.py (1)
165-192: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
Unguarded
int()conversion produces an unclear error.
int(self.max_results)(Line 167) isn't wrapped in a try/except like the adjacent JSON parsing, Refresh, and generic load-error paths. A non-numericmax_resultsvalue raises a rawValueErrorfromint()instead of the same friendlyValueError(msg)pattern used elsewhere in this method.🐛 Proposed fix
- max_results = int(self.max_results) if self.max_results else 100 + try: + max_results = int(self.max_results) if self.max_results else 100 + except ValueError as e: + msg = f"Invalid max_results value: {self.max_results}" + raise ValueError(msg) from e📝 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.json_string = self.json_string label_ids = self.label_ids.split(",") if self.label_ids else ["INBOX"] try: max_results = int(self.max_results) if self.max_results else 100 except ValueError as e: msg = f"Invalid max_results value: {self.max_results}" raise ValueError(msg) from e # Load the token information from the JSON string try: token_info = json.loads(json_string) except JSONDecodeError as e: msg = "Invalid JSON string" raise ValueError(msg) from e creds = Credentials.from_authorized_user_info(token_info) # Initialize the custom loader with the provided credentials loader = CustomGMailLoader(creds=creds, n=max_results, label_ids=label_ids) try: docs = loader.load() except RefreshError as e: msg = "Authentication error: Unable to refresh authentication token. Please try to reauthenticate." raise ValueError(msg) from e except Exception as e: msg = f"Error loading documents: {e}" raise ValueError(msg) from e # Return the loaded documents self.status = docs return Data(data={"text": docs})🤖 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/bundles/google/src/lfx_google/components/google/gmail.py` around lines 165 - 192, Update the max_results conversion in the component method containing the JSON parsing and loader setup to catch invalid numeric input and raise a friendly ValueError with a descriptive message, consistent with the existing JSONDecodeError and document-loading error handling; preserve the default of 100 when max_results is unset.src/bundles/google/src/lfx_google/components/google/google_drive_search.py (1)
105-140: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
Missing JSON-decode and API-error handling, unlike sibling loaders.
json.loads(self.token_string)(Line 107) isn't wrapped, unlike the identical parsing ingmail.pyandgoogle_drive.py, which catchJSONDecodeErrorand raise a clearValueError("Invalid JSON string"). Similarly,service.files().list(...).execute()has noRefreshError/generic exception handling, unlike the sibling loaders'loader.load()calls. Malformed tokens or API failures here will surface as raw, unfriendly exceptions instead of the consistentValueErrormessages used elsewhere in this bundle.🐛 Proposed fix
def search_files(self) -> dict: # Load the token information from the JSON string - token_info = json.loads(self.token_string) + try: + token_info = json.loads(self.token_string) + except json.JSONDecodeError as e: + msg = "Invalid JSON string" + raise ValueError(msg) from e creds = Credentials.from_authorized_user_info(token_info) # Use the query string from the input (which might have been edited by the user) query = self.query_string or self.generate_query_string() # Initialize the Google Drive API service service = build("drive", "v3", credentials=creds) # Perform the search - results = service.files().list(q=query, pageSize=5, fields="nextPageToken, files(id, name, mimeType)").execute() + try: + results = ( + service.files().list(q=query, pageSize=5, fields="nextPageToken, files(id, name, mimeType)").execute() + ) + except RefreshError as e: + msg = "Authentication error: Unable to refresh authentication token. Please try to reauthenticate." + raise ValueError(msg) from e + except Exception as e: + msg = f"Error searching Google Drive: {e}" + raise ValueError(msg) from e(remember to import
RefreshErrorfromgoogle.auth.exceptions)🤖 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/bundles/google/src/lfx_google/components/google/google_drive_search.py` around lines 105 - 140, Update search_files to catch JSONDecodeError around json.loads(self.token_string) and raise ValueError("Invalid JSON string"), matching the sibling loaders. Import RefreshError from google.auth.exceptions and wrap the Google Drive service setup/search execution in the same error handling pattern used by those loaders, converting RefreshError and other API exceptions into the bundle’s consistent ValueError messages.src/bundles/google/src/lfx_google/components/google/google_generative_ai.py (2)
97-110: 🔒 Security & Privacy | 🟠 Major | 🏗️ Heavy lift
🧩 Analysis chain
🌐 Web query:
Does the google-generativeai Python SDK support listing models with a per-call API key instead of global genai.configure()?💡 Result:
The legacy google-generativeai SDK (which uses genai.configure) does not support passing a per-call API key, as it relies on a global configuration to manage authentication [1][2]. To use different API keys in that SDK, developers typically avoid genai.configure and instead manage authentication via environment variables or low-level client configuration [2]. However, the new, recommended Google Gen AI SDK (google-genai) utilizes a client-based architecture that explicitly supports per-client (and effectively per-instance) API key management [3][4][5]. Instead of global configuration, you instantiate a client with a specific API key, allowing for multiple, independent clients within the same application [3][6][7]: from google import genai # Create a client with a specific API key client = genai.Client(api_key='YOUR_API_KEY') # Use this client to list models or perform other operations models = client.models.list This design resolves the limitations of the legacy SDK by allowing developers to maintain isolated instances with their own credentials [6][7][5]. It is recommended to migrate from google-generativeai to google-genai to benefit from this improved architecture [6][7].
Citations:
- 1: https://theneuralbase.com/gemini-api/learn/beginner/api-key-not-configured-error/
- 2: https://theneuralbase.com/gemini-api/learn/beginner/genai-configure-global-api-key-setup/
- 3: https://googleapis.github.io/python-genai/index.html
- 4: https://github.com/googleapis/python-genai
- 5: https://github.com/googleapis/python-genai/blob/main/google/genai/client.py
- 6: https://ai.google.dev/gemini-api/docs/migrate.md.txt
- 7: https://media.patentllm.org/blog/dev-tool/google-genai-sdk-migration
🏁 Script executed:
#!/bin/bash set -euo pipefail echo "== file location ==" git ls-files | rg 'google_generative_ai.py|pyproject.toml|requirements|Pipfile|poetry|uv.lock|lock' || true echo echo "== target file excerpt ==" fd -a google_generative_ai.py . | while read -r f; do echo "--- $f" wc -l "$f" sed -n '1,180p' "$f" done echo echo "== dependenc references to google generative ==" rg -n "google-(generativeai|genai)|generativeai|genai|Gemini|GenerativeModel" -S . --glob '!*.lock' --glob '!node_modules/**' --glob '!dist/**' || true echo echo "== call sites / class methods ==" rg -n "get_models|update_build_config|api_key|google_generative_ai|Genai|google\.generativeai" -S src || trueRepository: langflow-ai/langflow
Length of output: 50377
🏁 Script executed:
#!/bin/bash set -euo pipefail echo "== inspect package metadata/version constraints if present ==" for f in pyproject.toml requirements.txt requirements*.txt Pipfile poetry.lock uv.lock poetry.lock.backup; do if [ -f "$f" ]; then echo "--- $f" rg -n "google-(genai|generativeai)|generativeai|genai" "$f" || true fi done echo echo "== git diff around modified/deleted dependencies if available ==" git diff --name-only --diff-filter=M || true git diff --stat || trueRepository: langflow-ai/langflow
Length of output: 2754
🏁 Script executed:
#!/bin/bash set -euo pipefail echo "== locate exact google_generative_ai.py ==" git ls-files | grep 'src/bundles/google/src/lfx_google/components/google/google_generative_ai.py$' || true echo echo "== target file ==" f="$(git ls-files | grep 'src/bundles/google/src/lfx_google/components/google/google_generative_ai.py$' || true)" if [ -n "$f" ]; then wc -l "$f" sed -n '1,220p' "$f" fi echo echo "== dependency constraints in pyproject/uv.lock ==" if [ -f uv.lock ]; then rg -n -C 2 "^name = \"google-(genai|generativeai)\"|google-genai|google-generativeai|langchain-google-genai" uv.lock | head -n 120 fi rg -n -C 2 "google-(genai|generativeai)|google_genai|google-generativeai|langchain-google-genai" pyproject.toml pyproject.* uv.lock 2>/dev/null | head -n 160 || true echo echo "== runtime availability and module symbols ==" python3 - <<'PY' import importlib.util for name in ["google.generativeai", "google.genai"]: spec = importlib.util.find_spec(name) print(f"{name}: {spec.origin if spec else 'not installed'}") try: import google.generativeai as genai print("google.generativeai.configure:", getattr(genai, "configure", None)) print("google.generativeai.list_models:", getattr(genai, "list_models", None)) except Exception as e: print("google.generativeai import error:", type(e).__name__, e) try: import google.genai print("google.genai.available:", True) print("google.genai modules:", tuple(dir(google.genai))) except Exception as e: print("google.genai import error:", type(e).__name__, e) PYRepository: langflow-ai/langflow
Length of output: 13123
Use per-client Google Gen AI auth and avoid calling
genai.configure()during UI refresh.
google.generativeai.configure()andlist_models()share the process-wide Gen AI module state, so concurrentget_models()/update_build_config()calls from different Google API keys can execute against the wrong keys. Switch to a client-scoped SDK/API and pass credentials through the client in both model listing and tool-model validation, so refresh/background config refresh cannot leak credentials between flows.🤖 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/bundles/google/src/lfx_google/components/google/google_generative_ai.py` around lines 97 - 110, Update get_models and the related update_build_config tool-model validation to use a client-scoped Google Gen AI SDK/API initialized with self.api_key, removing the process-wide genai.configure() call. Pass the client explicitly through both model listing and validation paths so concurrent requests using different API keys remain isolated, while preserving the existing supported-generation filtering, sorting, and fallback behavior.
111-124: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win
Tool-support filtering is broken: wrong model probed, and list mutated during iteration.
The probe always builds
ChatGoogleGenerativeAI(model=self.model_name, ...)instead of the loop variablemodel, so every candidate is judged by the single currently-selected model's tool support, not its own. Separately,model_ids.remove(model)while iteratingmodel_idsskips the element following each removal. Together,tool_model_enabledfiltering produces incorrect/incomplete results, and an all-removed edge case surfaces later as an opaqueIndexError→ValueErrorinupdate_build_config(Line 139).🐛 Proposed fix
if tool_model_enabled: try: from langchain_google_genai.chat_models import ChatGoogleGenerativeAI except ImportError as e: msg = "langchain_google_genai is not installed." raise ImportError(msg) from e - for model in model_ids: - model_with_tool = ChatGoogleGenerativeAI( - model=self.model_name, - google_api_key=self.api_key, - ) - if not self.supports_tool_calling(model_with_tool): - model_ids.remove(model) + model_ids = [ + model + for model in model_ids + if self.supports_tool_calling( + ChatGoogleGenerativeAI(model=model, google_api_key=self.api_key) + ) + ] return model_ids📝 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.if tool_model_enabled: try: from langchain_google_genai.chat_models import ChatGoogleGenerativeAI except ImportError as e: msg = "langchain_google_genai is not installed." raise ImportError(msg) from e model_ids = [ model for model in model_ids if self.supports_tool_calling( ChatGoogleGenerativeAI(model=model, google_api_key=self.api_key) ) ] return model_ids🤖 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/bundles/google/src/lfx_google/components/google/google_generative_ai.py` around lines 111 - 124, Update the tool-support filtering in the model-selection method to instantiate ChatGoogleGenerativeAI with the loop variable model, not self.model_name. Avoid mutating model_ids during iteration by building a filtered collection or iterating over a copy, and ensure the no-supported-model case returns an empty result or otherwise follows the existing validation path without causing a later IndexError-to-ValueError failure in update_build_config.src/bundles/google/src/lfx_google/components/google/google_oauth_token.py (2)
40-54: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
Scope-validation regex allows missing comma separators.
Only the first alternative in the repeating group carries the
,\s*prefix;mail.google.com/,www.google.com/calendar/feeds, andwww.google.com/m8/feedscan match without a preceding comma, so multiple scopes can be concatenated without a separator and still pass validation.🐛 Proposed fix
def validate_scopes(self, scopes): pattern = ( r"^(https://www\.googleapis\.com/auth/[\w\.\-]+" r"|mail\.google\.com/" r"|www\.google\.com/calendar/feeds" r"|www\.google\.com/m8/feeds)" - r"(,\s*https://www\.googleapis\.com/auth/[\w\.\-]+" - r"|mail\.google\.com/" - r"|www\.google\.com/calendar/feeds" - r"|www\.google\.com/m8/feeds)*$" + r"(,\s*(https://www\.googleapis\.com/auth/[\w\.\-]+" + r"|mail\.google\.com/" + r"|www\.google\.com/calendar/feeds" + r"|www\.google\.com/m8/feeds))*$" )📝 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.def validate_scopes(self, scopes): pattern = ( r"^(https://www\.googleapis\.com/auth/[\w\.\-]+" r"|mail\.google\.com/" r"|www\.google\.com/calendar/feeds" r"|www\.google\.com/m8/feeds)" r"(,\s*(https://www\.googleapis\.com/auth/[\w\.\-]+" r"|mail\.google\.com/" r"|www\.google\.com/calendar/feeds" r"|www\.google\.com/m8/feeds))*$" ) if not re.match(pattern, scopes): error_message = "Invalid scope format." raise ValueError(error_message)🧰 Tools
🪛 ast-grep (0.44.1)
[warning] 50-50: Regex pattern passed to re is built from a non-literal (variable, call, concatenation, or f-string) value. If that value is attacker-controlled it can introduce a malicious pattern with catastrophic backtracking (ReDoS). Use a hardcoded literal pattern, or validate/escape untrusted input with re.escape() and bound the regex complexity before compiling.
Context: re.match(pattern, scopes)
Note: [CWE-1333] Inefficient Regular Expression Complexity.(redos-non-literal-regex-python)
🤖 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/bundles/google/src/lfx_google/components/google/google_oauth_token.py` around lines 40 - 54, Update validate_scopes so every repeated scope alternative requires the same comma-and-optional-whitespace separator, preventing unseparated values such as mail.google.com/ or calendar feeds from being concatenated and accepted. Preserve validation of single scopes and properly comma-separated scopes.
55-88: 🔒 Security & Privacy | 🟠 Major | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash set -euo pipefail echo "== file exists and outline/size ==" fd -a 'google_oauth_token.py' . || true if [ -f src/bundles/google/src/lfx_google/components/google/google_oauth_token.py ]; then wc -l src/bundles/google/src/lfx_google/components/google/google_oauth_token.py ast-grep outline src/bundles/google/src/lfx_google/components/google/google_oauth_token.py || true echo "== relevant lines ==" sed -n '1,140p' src/bundles/google/src/lfx_google/components/google/google_oauth_token.py | cat -n fi echo "== usages/imports/config for google_oauth_token ==" rg -n "google_oauth_token|OAuthToken|token\.json|run_local_server|from_authorized_user_file|to_json" . || true echo "== dependency hints for google oauth library ==" rg -n "google-auth|google-api-python-client|google-auth-oauthlib" -S . -g '!node_modules' || true fd . . -e py | xargs -r rg -n "from google.auth|import google|Credentials|InstalledAppFlow" | head -100Repository: langflow-ai/langflow
Length of output: 50377
🏁 Script executed:
#!/bin/bash set -euo pipefail echo "== google oauth token file ==" cat -n src/bundles/google/src/lfx_google/components/google/google_oauth_token.py echo "== component wiring/usages ==" rg -n "GoogleOAuthToken|google_oauth_token|Google OAuth Token|InstalledAppFlow|run_local_server|token\.json|oauth_credentials" src/bundles/google src -g '!**/Content Aggregator.json' || true echo "== package dependency/source snippets ==" fd . src -e py | xargs -r rg -n "InstalledAppFlow|run_local_server|Credentials\.from_authorized_user|to_json" | head -80 || trueRepository: langflow-ai/langflow
Length of output: 20756
🌐 Web query:
Google Auth Python InstalledAppFlow run_local_server timeout port=0 blocks browser behavior documentation💡 Result:
In the Google Auth library for Python (google-auth-oauthlib), the InstalledAppFlow.run_local_server method is designed to facilitate the OAuth 2.0 flow for installed applications by spinning up a short-lived local web server to capture the authorization code [1][2][3]. Behavior of port=0 When you set port=0 in the run_local_server method, the underlying wsgiref.simple_server library automatically assigns an available ephemeral port [3]. This is a common practice to avoid "Address already in use" errors that can occur when a hardcoded port is already occupied by another process [4][5]. Behavior of Timeout By default, run_local_server blocks indefinitely while waiting for the user to complete the authentication in their browser [6][7][8]. If the user closes the browser or does not complete the flow, the application will remain blocked [8]. Recent updates have introduced a timeout_seconds parameter to address this [9][8]. When this parameter is used, the server will wait for the specified duration for the authorization callback. If no request is received within that time: 1. Historically, this often resulted in an AttributeError: 'NoneType' object has no attribute 'replace' because the code attempted to access the callback URI which was never set [6][9][8]. 2. More recent versions (e.g., following updates like PR
#363in 2026) have been designed to raise a more meaningful exception instead, allowing developers to handle the timeout gracefully [9]. Implementation Recommendations To avoid blocking indefinitely, you should pass the timeout_seconds parameter [8] and wrap the call in a try-except block to handle the potential timeout exception [8]. Example usage: from google_auth_oauthlib.flow import InstalledAppFlow flow = InstalledAppFlow.from_client_secrets_file('client_secrets.json', scopes=[...]) try: # Using port=0 for an ephemeral port and timeout_seconds to prevent indefinite blocking creds = flow.run_local_server(port=0, timeout_seconds=60) except Exception as e: print(f"Authentication timed out or failed: {e}")Citations:
- 1: https://googleapis.dev/python/google-auth-oauthlib/latest/_modules/google_auth_oauthlib/flow.html
- 2: https://github.com/googleapis/google-api-python-client/blob/25fba648ea647b62f2a6edc54ae927c1ed381b45/docs/oauth.md
- 3: https://google-auth-oauthlib.readthedocs.io/en/latest/%5Fmodules/google%5Fauth%5Foauthlib/flow.html
- 4: googleapis/google-auth-library-python-oauthlib#28
- 5: https://github.com/pydata/pydata-google-auth/blob/master/pydata_google_auth/_webserver.py
- 6: googleapis/google-cloud-python#15447
- 7: googleapis/google-cloud-python#15442
- 8: https://stackoverflow.com/questions/64355186/google-python-oauth-detect-web-page-closed-or-timeout
- 9: googleapis/google-cloud-python@adc3ee60
🌐 Web query:
google-auth-oauthlib InstalledAppFlow run_local_server source wsgiref timeout_seconds💡 Result:
In the google-auth-oauthlib library, the InstalledAppFlow.run_local_server method includes a timeout_seconds parameter [1]. This parameter allows you to specify a timeout duration for the local web server created to handle the OAuth 2.0 authorization response [2][3]. The timeout_seconds argument was introduced to address issues where the server would otherwise block indefinitely while waiting for an authorization response [2][3]. When provided, this value is passed to the underlying socket server's timeout mechanism [2]. If no value is provided, it defaults to None, meaning the server will continue to block until a request is received or an error occurs [2]. Key implementation details include: - The parameter is explicitly named timeout_seconds [1]. - It is used to prevent the local server from blocking indefinitely during the OAuth flow [2][3]. - The library uses wsgiref.simple_server.make_server to create the local redirect server [4]. If you are encountering issues with run_local_server blocking or failing to bind to a port, ensure you are using a recent version of the library, as enhancements such as the timeout_seconds parameter and bind_addr (for container environments) have been added to resolve these common behaviors [5][3][6].
Citations:
- 1: https://googleapis.dev/python/google-auth-oauthlib/latest/_modules/google_auth_oauthlib/flow.html
- 2: googleapis/google-auth-library-python-oauthlib#239
- 3: googleapis/google-auth-library-python-oauthlib#245
- 4: https://google-auth-oauthlib.readthedocs.io/en/latest/%5Fmodules/google%5Fauth%5Foauthlib/flow.html
- 5: googleapis/google-auth-library-python-oauthlib#202
- 6: googleapis/google-auth-library-python-oauthlib#87
Avoid using global
token.jsonandrun_local_server()in the backend.
Path("token.json")is a single shared credential file across all flows, so concurrent requests can race or read tokens belonging to another user/token set.flow.run_local_server(port=0)also blocks waiting for the browser callback with no configured timeout, which can hang indefinitely in a server context. Use an isolated, caller-owned store and handle OAuth via a redirect/callback model with explicit timeout/error handling.🤖 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/bundles/google/src/lfx_google/components/google/google_oauth_token.py` around lines 55 - 88, Update build_output to stop using the process-wide token.json and the blocking InstalledAppFlow.run_local_server flow. Use an isolated, caller-owned credential store keyed to the current flow or request, and implement OAuth through a redirect/callback exchange with an explicit timeout and failure handling before returning the credentials in Data.
This comment has been minimized.
This comment has been minimized.
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## release-1.12.0 #14220 +/- ##
==================================================
- Coverage 61.47% 53.61% -7.86%
==================================================
Files 2339 2299 -40
Lines 237145 228084 -9061
Branches 35410 17626 -17784
==================================================
- Hits 145787 122292 -23495
- Misses 89561 103982 +14421
- Partials 1797 1810 +13
Flags with carried forward coverage won't be shown. Click here to find out more. 🚀 New features to boost your workflow:
|
|
@coderabbitai review Addressed the review findings in 0efa677:
The full server-native OAuth redirect UI would require a separate API/frontend design; this commit removes the cross-user state and indefinite-wait risks without widening this release PR. Validation: 110 passed, 8 skipped; Ruff, release-plan/profile checks, lock validation, wheel builds, and pre-commit hooks all pass. |
🧩 Analysis chain🏁 Script executed: #!/bin/bash
set -euo pipefail
echo '== Commit and changed files =='
git show --no-ext-diff --format=fuller --name-status 0efa677d4a
echo
echo '== Targeted change summary =='
git show --no-ext-diff --stat 0efa677d4a
echo
echo '== Relevant modified source/test files =='
git diff-tree --no-commit-id --name-only -r 0efa677d4a |
rg '(^src/bundles/google/|^src/bundles/lfx-bundles/|^src/lfx/src/lfx/components/google/|test_google|pyproject\.toml$)' || true
echo
echo '== Packaging and compatibility diff =='
git show --no-ext-diff --format= -- \
src/bundles/lfx-bundles/pyproject.toml \
src/bundles/lfx-bundles/README.md \
src/bundles/google/pyproject.toml \
pyproject.toml \
src/lfx/src/lfx/components/google/__init__.py 0efa677d4a || true
echo
echo '== OAuth/model-discovery/component diff =='
git show --no-ext-diff --format= 0efa677d4a -- \
src/bundles/google/src/lfx_google/components/google \
src/bundles/google/tests || trueLength of output: 23418 🏁 Script executed: #!/bin/bash
set -euo pipefail
echo '== Commit and changed files =='
git show --no-ext-diff --format=fuller --name-status 0efa677d4a
echo
echo '== Targeted change summary =='
git show --no-ext-diff --stat 0efa677d4a
echo
echo '== Relevant modified source/test files =='
git diff-tree --no-commit-id --name-only -r 0efa677d4a |
rg '(^src/bundles/google/|^src/bundles/lfx-bundles/|^src/lfx/src/lfx/components/google/|test_google|pyproject\.toml$)' || true
echo
echo '== Packaging and compatibility diff =='
git show --no-ext-diff --format= 0efa677d4a -- \
src/bundles/lfx-bundles/pyproject.toml \
src/bundles/lfx-bundles/README.md \
src/bundles/google/pyproject.toml \
pyproject.toml \
src/lfx/src/lfx/components/google/__init__.py || true
echo
echo '== OAuth/model-discovery/component diff =='
git show --no-ext-diff --format= 0efa677d4a -- \
src/bundles/google/src/lfx_google/components/google \
src/bundles/google/tests || trueLength of output: 23418 🏁 Script executed: #!/bin/bash
set -euo pipefail
echo '== Relevant symbol map =='
ast-grep outline src/bundles/google/src/lfx_google/components/google/google_generative_ai.py --items all --type class,function --view expanded
ast-grep outline src/bundles/google/src/lfx_google/components/google/google_oauth_token.py --items all --type class,function --view expanded
ast-grep outline scripts/migrate/consolidate_bundles.py --items all --type function --view expanded
ast-grep outline src/backend/tests/unit/test_lfx_bundles_extras.py --items all --type function --view expanded
echo
echo '== Aggregate-extra generation and compatibility assertions =='
rg -n -C 5 'all-no-torch|optional-dependencies|PROVIDER_DEPS|google|lfx-google' \
scripts/migrate/consolidate_bundles.py \
src/bundles/lfx-bundles/pyproject.toml \
src/backend/tests/unit/test_lfx_bundles_extras.py
echo
echo '== Current implementation and focused regression test =='
sed -n '80,155p' src/bundles/google/src/lfx_google/components/google/google_generative_ai.py
sed -n '1,130p' src/bundles/google/src/lfx_google/components/google/google_oauth_token.py
sed -n '1,245p' src/bundles/google/tests/test_google_component_regressions.py
echo
echo '== Locked google-genai dependency metadata =='
rg -n -A18 -B3 '^name = "google-genai"$' uv.lockLength of output: 44992 🤖 web_search executed:
Length of output: 9324
The Gemini client usage matches the No new issues found in the follow-up changes. 🐇 If you found this review helpful, would you consider giving us a shout-out on X? Thank you for using CodeRabbit! ✅ Action performedReview finished.
|
This comment has been minimized.
This comment has been minimized.
…-google-partner-bundle # Conflicts: # src/lfx/src/lfx/_assets/component_index.json
|
Build successful! ✅ |
Summary
lfx-bundles[google]extra to the standalonelfx-googlepartner bundlelfx-googlein the default Langflow distribution so Gemini always installslangchain-google-genaigooglebundle name and compatibility shim so saved component IDs remain stablelfx-bundlespatch release metadataValidation
lfx-google,lfx-bundles, and defaultlangflowwheels; verified default wheel metadata requireslfx-google>=0.1.0,<1.0.0Jira
Summary by CodeRabbit