Skip to content

feat: add is_active flag to model providers and update provider filtering#8588

Merged
edwinjosechittilappilly merged 10 commits into
mainfrom
fix-QoL-Agent-models
Jun 24, 2025
Merged

feat: add is_active flag to model providers and update provider filtering#8588
edwinjosechittilappilly merged 10 commits into
mainfrom
fix-QoL-Agent-models

Conversation

@edwinjosechittilappilly
Copy link
Copy Markdown
Collaborator

@edwinjosechittilappilly edwinjosechittilappilly commented Jun 17, 2025

Summary by CodeRabbit

  • New Features

    • The list of available model providers is now dynamically filtered to show only active providers in all agent components and dropdown menus.
  • Bug Fixes

    • Removed inactive or unsupported model providers from the selection options in all relevant agent interfaces.
  • Style

    • Updated system prompts in certain agents to provide more specialized and relevant instructions for their use cases.
  • Chores

    • Standardized the way model provider options are managed and displayed across all starter projects for consistency.

@dosubot dosubot Bot added the size:L This PR changes 100-499 lines, ignoring generated files. label Jun 17, 2025
@coderabbitai
Copy link
Copy Markdown
Contributor

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

This change introduces an is_active flag to model provider definitions and updates all relevant code to filter and expose only active providers. All agent components and starter project configurations are updated to use a new MODEL_PROVIDERS list for dropdown options, removing inactive or deprecated providers from user selection.

Changes

Files / Groups Change Summary
src/backend/base/langflow/base/models/model_input_constants.py Added is_active to ModelProvidersDict; filtered provider lists and metadata to only include active entries.
src/backend/base/langflow/components/agents/agent.py Updated dropdown options for model provider selection to use filtered MODEL_PROVIDERS list.
src/backend/base/langflow/initial_setup/starter_projects/*.json Updated embedded agent code to use MODEL_PROVIDERS for dropdowns; removed inactive providers from options; updated imports and metadata references accordingly. Also updated system prompts in some agents.

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant AgentComponent
    participant ModelConstants

    User->>AgentComponent: Open agent configuration UI
    AgentComponent->>ModelConstants: Request MODEL_PROVIDERS
    ModelConstants-->>AgentComponent: Return only active providers
    AgentComponent-->>User: Display filtered provider dropdown options
    User->>AgentComponent: Select provider and configure agent
Loading

Possibly related PRs

  • langflow-ai/langflow#8117: Adds is_active flag and filters providers in model_input_constants.py, updating agent components to use MODEL_PROVIDERS for dropdowns—directly related to this PR's updates to provider filtering and dropdown logic.

Suggested labels

refactor, lgtm

Suggested reviewers

  • mfortman11
  • edwinjosechittilappilly
✨ Finishing Touches
🧪 Generate Unit Tests
  • Create PR with Unit Tests
  • Post Copyable Unit Tests in Comment
  • Commit Unit Tests in branch fix-QoL-Agent-models

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
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Explain this complex logic.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai explain this code block.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and explain its main purpose.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this PR.
  • @coderabbitai auto-generate unit tests to generate unit tests for this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

@coderabbitai coderabbitai Bot changed the title @coderabbitai feat: add is_active flag to model providers and update provider filtering Jun 17, 2025
@github-actions github-actions Bot added the enhancement New feature or request label Jun 17, 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: 2

🔭 Outside diff range comments (9)
src/backend/base/langflow/initial_setup/starter_projects/Travel Planning Agents.json (3)

1934-1960: Critical: Mismatch between options and options_metadata in City Selection Agent
The options list now correctly filters to ["Anthropic", "Google Generative AI", "Groq", "OpenAI", "Custom"], but options_metadata still contains icons for removed providers (Amazon, Azure, NVIDIA, SambaNova). This stale metadata will cause UI inconsistencies.


2699-2726: Critical: Mismatch between options and options_metadata in Local Expert Agent
The dropdown options are pruned to active providers, but the accompanying options_metadata still lists icons for deprecated providers. Please align metadata to match the new options array.


3464-3491: Critical: Mismatch between options and options_metadata in Travel Concierge Agent
Similar to above, the options_metadata retains entries for removed providers. Update it to only include icons for the providers present in options.

src/backend/base/langflow/initial_setup/starter_projects/Instagram Copywriter.json (1)

1600-1610: Use MODEL_PROVIDERS instead of MODELS_METADATA.keys() in Custom branch
In the Custom‐provider branch of update_build_config, options_metadata is built from sorted(MODELS_METADATA.keys()), which can include inactive providers. It should pull metadata only for active ones:

- options_metadata=[MODELS_METADATA[key] for key in sorted(MODELS_METADATA.keys())] + [{"icon": "brain"}],
+ options_metadata=[MODELS_METADATA[key] for key in sorted(MODEL_PROVIDERS)] + [{"icon": "brain"}],
src/backend/base/langflow/initial_setup/starter_projects/Social Media Agent.json (1)

1456-1545: Inconsistent provider metadata generation in custom path

The initial agent_llm dropdown builds its options_metadata from sorted(MODEL_PROVIDERS), but in the Custom branch of update_build_config you’re using sorted(MODELS_METADATA.keys()), which can re-introduce inactive or deprecated providers. To keep only active providers, align the Custom branch to use sorted(MODEL_PROVIDERS) (and their metadata) instead of all keys in MODELS_METADATA.

Also, double-check that every imported constant (MODEL_PROVIDERS_DICT, MODEL_PROVIDERS, MODELS_METADATA, ALL_PROVIDER_FIELDS, MODEL_DYNAMIC_UPDATE_FIELDS) is actually used in this code snippet—remove any unused imports to keep the snippet lean.

src/backend/base/langflow/initial_setup/starter_projects/Invoice Summarizer.json (2)

1282-1314: Mismatch between dropdown options and metadata
In the Agent node’s static template, the "options_metadata" array for agent_llm still includes entries for inactive providers (e.g., Amazon, Azure, NVIDIA, SambaNova), even though they’ve been removed from the "options" list. This will lead to orphaned metadata and UI inconsistencies.
Please trim "options_metadata" so it only contains icons for the providers actually listed under "options" ([*sorted(MODEL_PROVIDERS), "Custom"]).


1470-1510: Custom branch uses wrong metadata source
In the Python update_build_config method, the Custom-dropdown logic builds options_metadata from sorted(MODELS_METADATA.keys()), which pulls in all providers (including inactive ones). It should mirror the active set—i.e. [MODELS_METADATA[key] for key in sorted(MODEL_PROVIDERS)] + [{"icon": "brain"}]—to avoid resurrecting deprecated providers in the UI.

src/backend/base/langflow/initial_setup/starter_projects/Gmail Agent.json (1)

355-362: Fix metadata mismatch in custom LLM dropdown
In the Custom branch of update_build_config, options_metadata currently iterates over sorted(MODELS_METADATA.keys()), which may include inactive providers and doesn’t match the options list. This leads to a length mismatch in the UI dropdown.

Please apply this diff:

-    options_metadata=[MODELS_METADATA[key] for key in sorted(MODELS_METADATA.keys())] + [{"icon": "brain"}],
+    options_metadata=[MODELS_METADATA[key] for key in sorted(MODEL_PROVIDERS)] + [{"icon": "brain"}],
src/backend/base/langflow/initial_setup/starter_projects/Financial Agent.json (1)

2556-2589: Inconsistent options_metadata entries
The JSON options_metadata for "agent_llm" still includes icons for providers removed from the options list (e.g., Amazon, Azure, NVIDIA, SambaNova). This mismatch will lead to orphaned metadata in the frontend.

Please remove metadata entries for inactive providers so the options_metadata array aligns exactly with the options list.

♻️ Duplicate comments (3)
src/backend/base/langflow/initial_setup/starter_projects/Sequential Tasks Agents.json (2)

1160-1220: Duplicate of dynamic provider dropdown update.

See the comment on lines 480–540 for aligning the dropdown options and metadata without re-sorting.


2680-2740: Duplicate of dynamic provider dropdown update.

See the comment on lines 480–540 for aligning the dropdown options and metadata without re-sorting.

src/backend/base/langflow/initial_setup/starter_projects/Financial Agent.json (1)

3388-3427: Duplicate UI mismatch
This second "agent_llm" definition also lists inactive providers in its options_metadata. Apply the same cleanup here to maintain consistency across both agent nodes.

🧹 Nitpick comments (9)
src/backend/base/langflow/base/models/model_input_constants.py (1)

186-205: Consider collapsing duplicated provider metadata into a helper to avoid drift.

Every provider block now sets "is_active": <bool>. Copy-pasting these literal dictionaries makes it easy to forget updating one block (e.g. icon, prefix) when refactoring.

-    MODEL_PROVIDERS_DICT["OpenAI"] = {
-        "fields": openai_fields,
-        ...
-        "icon": OpenAIModelComponent.icon,
-        "is_active": True,
-    }
+    _register_provider(
+        name="OpenAI",
+        fields=openai_fields,
+        inputs=openai_inputs,
+        component_cls=OpenAIModelComponent,
+        active=True,
+    )

A thin helper like _register_provider(**kwargs) will trim ~80 LOC and guarantee consistent keys across providers.

Also applies to: 210-217, 224-230, 236-243, 249-256, 262-269, 274-282

src/backend/base/langflow/components/agents/agent.py (1)

44-49: Dropdown options & metadata stay aligned – edge-case covered, but helper would de-duplicate.

Appending "Custom" and a {"icon": "brain"} placeholder keeps indices in sync – good catch.
To avoid replicating this logic again (see update_build_config), extract a small helper:

def _provider_dropdown():
    options = [*sorted(MODEL_PROVIDERS), "Custom"]
    metadata = [MODELS_METADATA[k] for k in sorted(MODELS_METADATA)] + [{"icon": "brain"}]
    return options, metadata

Makes the intent crystal-clear and prevents accidental divergence.

src/backend/base/langflow/initial_setup/starter_projects/Simple Agent.json (1)

1477-1492: Curated active providers in agent_llm dropdown
The static options list now contains only active providers and omits deprecated ones. Ensure this array remains in sync with MODEL_PROVIDERS in model_input_constants.py to prevent drift between code and starter projects.

src/backend/base/langflow/initial_setup/starter_projects/Sequential Tasks Agents.json (1)

480-540: Preserve provider list order instead of sorting alphabetically.

Sorting MODEL_PROVIDERS may reorder dropdown options in a way that diverges from the intended active providers sequence. To maintain the order defined by the is_active flag in the constant, use MODEL_PROVIDERS directly:

-options=[*sorted(MODEL_PROVIDERS), "Custom"],
-options_metadata=[MODELS_METADATA[key] for key in sorted(MODEL_PROVIDERS)] + [{"icon": "brain"}],
+options=[*MODEL_PROVIDERS, "Custom"],
+options_metadata=[MODELS_METADATA[key] for key in MODEL_PROVIDERS] + [{"icon": "brain"}],
src/backend/base/langflow/initial_setup/starter_projects/Search agent.json (3)

1211-1211: Ensure Custom branch metadata aligns with filtered providers
In the Custom-provider branch, options_metadata is built from sorted(MODELS_METADATA.keys()), which could contain inactive entries. Switch this to sorted(MODEL_PROVIDERS) to stay in sync with the active list.


1211-1211: Group and order imports for readability
The import block intermixes multiple internal modules. For clarity, reorder per PEP8: standard library first, then third-party, then langflow.* internal imports, each group alphabetized.


1211-1211: Cache provider_configs to reduce recomputation
provider_configs is rebuilt on every call to update_build_config. Extract it to a class‐level constant or helper to avoid reallocation on each invocation.

src/backend/base/langflow/initial_setup/starter_projects/Research Agent.json (1)

1125-1290: Filter provider_configs by active MODEL_PROVIDERS

In update_build_config, the comprehension for provider_configs currently iterates over all entries in MODEL_PROVIDERS_DICT, which may include inactive providers. To avoid unnecessary processing and potential drift, consider restricting it to the active set:

-provider_configs: dict[str, tuple[dict, list[dict]]] = {
-    provider: (...)
-    for provider in MODEL_PROVIDERS_DICT
-}
+provider_configs: dict[str, tuple[dict, list[dict]]] = {
+    provider: (...)
+    for provider in MODEL_PROVIDERS
+}
src/backend/base/langflow/initial_setup/starter_projects/Pokédex Agent.json (1)

1899-1905: Typo and Capitalization in Default system_prompt
The new Pokédex prompt contains a few minor errors:

  • “pokédex” should be capitalized (“Pokédex”).
  • Use “Pokémon” (or “pokemon”) rather than “pokemons.”
  • Correct “mispelling” to “misspelling.”

Suggested refinement:

"You are a Pokédex agent. Fetch information about Pokémon using the endpoint:\nhttps://pokeapi.co/api/v2/pokemon/<pokemon_name>\n\nFor example:\nhttps://pokeapi.co/api/v2/pokemon/ditto\nhttps://pokeapi.co/api/v2/pokemon/pikachu\n\nFix user Pokémon name misspellings."
📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 1950116 and 76436f8.

📒 Files selected for processing (18)
  • src/backend/base/langflow/base/models/model_input_constants.py (9 hunks)
  • src/backend/base/langflow/components/agents/agent.py (3 hunks)
  • src/backend/base/langflow/initial_setup/starter_projects/Financial Agent.json (2 hunks)
  • src/backend/base/langflow/initial_setup/starter_projects/Gmail Agent.json (1 hunks)
  • src/backend/base/langflow/initial_setup/starter_projects/Instagram Copywriter.json (1 hunks)
  • src/backend/base/langflow/initial_setup/starter_projects/Invoice Summarizer.json (1 hunks)
  • src/backend/base/langflow/initial_setup/starter_projects/Market Research.json (1 hunks)
  • src/backend/base/langflow/initial_setup/starter_projects/News Aggregator.json (1 hunks)
  • src/backend/base/langflow/initial_setup/starter_projects/Pokédex Agent.json (1 hunks)
  • src/backend/base/langflow/initial_setup/starter_projects/Price Deal Finder.json (1 hunks)
  • src/backend/base/langflow/initial_setup/starter_projects/Research Agent.json (1 hunks)
  • src/backend/base/langflow/initial_setup/starter_projects/SaaS Pricing.json (1 hunks)
  • src/backend/base/langflow/initial_setup/starter_projects/Search agent.json (1 hunks)
  • src/backend/base/langflow/initial_setup/starter_projects/Sequential Tasks Agents.json (3 hunks)
  • src/backend/base/langflow/initial_setup/starter_projects/Simple Agent.json (1 hunks)
  • src/backend/base/langflow/initial_setup/starter_projects/Social Media Agent.json (1 hunks)
  • src/backend/base/langflow/initial_setup/starter_projects/Travel Planning Agents.json (3 hunks)
  • src/backend/base/langflow/initial_setup/starter_projects/Youtube Analysis.json (1 hunks)
🔇 Additional comments (22)
src/backend/base/langflow/base/models/model_input_constants.py (1)

22-22: Good addition – is_active flag formalised in schema.

Explicitly capturing the activation state in the TypedDict prevents silent KeyErrors and documents intent.

src/backend/base/langflow/components/agents/agent.py (1)

223-230: Minor: metadata for “Custom” option differs between constructor & rebuild path.

In the constructor the “Custom” entry re-uses the brain icon; here you build options_metadata from MODELS_METADATA (already filtered) and also append {"icon": "brain"} but without the associated “Custom” label in MODELS_METADATA. Works only because both lists are appended in the same order.

Calling the same helper suggested above will guarantee consistency and avoid subtle off-by-one bugs if either list is re-ordered.

src/backend/base/langflow/initial_setup/starter_projects/Travel Planning Agents.json (2)

2023-2028: Imported MODEL_PROVIDERS & MODELS_METADATA for dynamic configuration
The constants MODEL_PROVIDERS and MODELS_METADATA are correctly added to the import, matching the refactor to global provider lists.


2049-2053: Switched agent_llm dropdown to dynamic MODEL_PROVIDERS
Using options=[*sorted(MODEL_PROVIDERS), "Custom"] with metadata from MODELS_METADATA ensures the code-driven list stays in sync with active providers.

src/backend/base/langflow/initial_setup/starter_projects/Simple Agent.json (3)

1493-1510: Metadata aligned with curated providers
The options_metadata entries match the curated options list and end with the default "brain" icon for custom selections. Confirm the order and count here mirror the options above.


1533-1541: Imports updated for dynamic provider filtering
The code string now imports MODEL_PROVIDERS, MODEL_PROVIDERS_DICT, MODELS_METADATA, MODEL_DYNAMIC_UPDATE_FIELDS, and ALL_PROVIDER_FIELDS from model_input_constants.py, laying the groundwork for active-only provider logic.


1557-1568: Agent inputs now driven by MODEL_PROVIDERS
AgentComponent.inputs leverages [*sorted(MODEL_PROVIDERS), "Custom"] and corresponding metadata comprehension to expose only active model providers at runtime. This centralizes provider configuration appropriately.

src/backend/base/langflow/initial_setup/starter_projects/SaaS Pricing.json (2)

833-841: Import MODEL_PROVIDERS constant
The addition of MODEL_PROVIDERS alongside MODEL_PROVIDERS_DICT ensures we can leverage the centralized, active-only provider list throughout the component. Confirm that every dynamic reference to providers (e.g., in dropdown options or metadata generation) consistently uses MODEL_PROVIDERS.


792-800: Filter out inactive providers in dropdown configuration
The agent_llm.options array has been updated to include only active providers plus "Custom", matching the curated MODEL_PROVIDERS set. This aligns the starter-project defaults with the active provider list—just remember to keep this JSON in sync if you ever adjust MODEL_PROVIDERS.

src/backend/base/langflow/initial_setup/starter_projects/News Aggregator.json (1)

1093-1093: Verify import and usage of MODEL_PROVIDERS
You’ve introduced MODEL_PROVIDERS alongside MODEL_PROVIDERS_DICT to drive the dropdown options. Ensure that both options=[*sorted(MODEL_PROVIDERS), "Custom"] and options_metadata=[MODELS_METADATA[key] for key in sorted(MODEL_PROVIDERS)] + [{"icon": "brain"}] correctly reflect only active providers and that no deprecated entries remain.

src/backend/base/langflow/initial_setup/starter_projects/Instagram Copywriter.json (1)

1496-1503: Import MODEL_PROVIDERS to drive dropdown filtering
Importing MODEL_PROVIDERS alongside MODEL_PROVIDERS_DICT and MODELS_METADATA correctly scopes the dropdown to active providers only, matching the new is_active flag behavior.

src/backend/base/langflow/initial_setup/starter_projects/Search agent.json (1)

1211-1211: Dynamic provider options and metadata usage is correct
Using [*sorted(MODEL_PROVIDERS), "Custom"] for the dropdown and [MODELS_METADATA[key] for key in sorted(MODEL_PROVIDERS)] + [{"icon": "brain"}] for metadata cleanly surfaces only active providers.

src/backend/base/langflow/initial_setup/starter_projects/Gmail Agent.json (1)

266-273: Properly imported MODEL_PROVIDERS
The addition of MODEL_PROVIDERS to the import block allows you to filter the dropdown to active providers only. This aligns with the new is_active flag in your model definitions.

src/backend/base/langflow/initial_setup/starter_projects/Price Deal Finder.json (2)

1727-1740: Verify new imports and constant availability
You’ve added MODEL_PROVIDERS alongside MODEL_PROVIDERS_DICT and MODELS_METADATA (and introduced StructuredTool from langchain_core.tools). Ensure those symbols are actually exported in langflow.base.models.model_input_constants and that langchain_core is a valid dependency in this context. A quick import‐only unit test or REPL check would catch any missing exports or package issues.


1750-1770: Dropdown now driven by curated MODEL_PROVIDERS
Using [*sorted(MODEL_PROVIDERS), "Custom"] for options (and mapping options_metadata only over MODEL_PROVIDERS) properly hides inactive providers and keeps the dropdown in sync with the new flag. This change cleanly aligns the starter project config with the active-only provider list.

src/backend/base/langflow/initial_setup/starter_projects/Research Agent.json (1)

1125-1290: Correct integration of active-only model providers

The AgentComponent code has been updated to import and use MODEL_PROVIDERS for the agent_llm dropdown options and metadata, replacing the previous MODEL_PROVIDERS_DICT.keys() approach. This ensures only active providers are displayed and aligns with the new centralized constants. The related methods (get_llm, _build_llm_model, and update_build_config) have also been refactored to work with the filtered list without altering existing logic or signatures.

src/backend/base/langflow/initial_setup/starter_projects/Financial Agent.json (2)

2238-2246: Update imports for active model providers
You’ve correctly imported MODEL_PROVIDERS and MODELS_METADATA alongside MODEL_PROVIDERS_DICT to enable filtering of active providers.


2260-2274: Use MODEL_PROVIDERS for agent dropdown options
Switching the options list and its options_metadata source to MODEL_PROVIDERS ensures only active providers appear in the agent’s LLM selector. Good use of sorted(MODEL_PROVIDERS) for deterministic ordering.

src/backend/base/langflow/initial_setup/starter_projects/Pokédex Agent.json (3)

1434-1475: Import and Provider Constants Update
The embedded Python now imports MODEL_PROVIDERS and MODELS_METADATA alongside MODEL_PROVIDERS_DICT. This centralizes active providers and their metadata, which is the intended behavior. Confirm that all these constants exist and are correctly exported from model_input_constants.py.


1476-1500: Centralized MODEL_PROVIDERS in agent_llm Input
Switching options to [*sorted(MODEL_PROVIDERS), "Custom"] filters out inactive entries and ensures deterministic ordering. This aligns with the PR objective to expose only active providers. No issues detected here.


1628-1640: Custom‐Provider Dropdown Construction
In the elif field_value == "Custom" block, options is built from MODEL_PROVIDERS, but options_metadata uses sorted(MODELS_METADATA.keys()). Ensure these two sets remain in sync so icons line up with the actual dropdown entries for Custom mode.

src/backend/base/langflow/initial_setup/starter_projects/Youtube Analysis.json (1)

1236-1243: Dynamic import of active provider constants
The new imports (MODEL_PROVIDERS, MODEL_PROVIDERS_DICT, MODELS_METADATA, etc.) cleanly replace hard-coded provider lists and metadata. This aligns with the PR objective of only surfacing active providers.

Comment on lines +286 to +291
# Expose only active providers
MODEL_PROVIDERS = [name for name, prov in MODEL_PROVIDERS_DICT.items() if prov.get("is_active", True)]

ALL_PROVIDER_FIELDS: list[str] = [
field for prov in MODEL_PROVIDERS_DICT.values() if prov.get("is_active", True) for field in prov["fields"]
]
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

ALL_PROVIDER_FIELDS may contain duplicates – use a set to keep list unique.

If two providers expose identically-named fields, the current list-comprehension will duplicate them:

-ALL_PROVIDER_FIELDS: list[str] = [
-    field for prov in MODEL_PROVIDERS_DICT.values() if prov.get("is_active", True) for field in prov["fields"]
-]
+ALL_PROVIDER_FIELDS: list[str] = list(
+    {
+        field
+        for prov in MODEL_PROVIDERS_DICT.values()
+        if prov.get("is_active", True)
+        for field in prov["fields"]
+    }
+)

Guarantees predictable look-ups and slightly cheaper in checks.

📝 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
# Expose only active providers
MODEL_PROVIDERS = [name for name, prov in MODEL_PROVIDERS_DICT.items() if prov.get("is_active", True)]
ALL_PROVIDER_FIELDS: list[str] = [
field for prov in MODEL_PROVIDERS_DICT.values() if prov.get("is_active", True) for field in prov["fields"]
]
# Expose only active providers
MODEL_PROVIDERS = [name for name, prov in MODEL_PROVIDERS_DICT.items() if prov.get("is_active", True)]
ALL_PROVIDER_FIELDS: list[str] = list(
{
field
for prov in MODEL_PROVIDERS_DICT.values()
if prov.get("is_active", True)
for field in prov["fields"]
}
)
🤖 Prompt for AI Agents
In src/backend/base/langflow/base/models/model_input_constants.py around lines
286 to 291, the ALL_PROVIDER_FIELDS list may contain duplicate field names
because it aggregates fields from all active providers without filtering
duplicates. To fix this, convert the list comprehension to build a set instead,
ensuring uniqueness of fields. Then, convert the set back to a list if needed to
maintain the original data type, which will guarantee unique fields and improve
lookup efficiency.

"title_case": false,
"type": "code",
"value": "from langchain_core.tools import StructuredTool\n\nfrom langflow.base.agents.agent import LCToolsAgentComponent\nfrom langflow.base.agents.events import ExceptionWithMessageError\nfrom langflow.base.models.model_input_constants import (\n ALL_PROVIDER_FIELDS,\n MODEL_DYNAMIC_UPDATE_FIELDS,\n MODEL_PROVIDERS_DICT,\n MODELS_METADATA,\n)\nfrom langflow.base.models.model_utils import get_model_name\nfrom langflow.components.helpers.current_date import CurrentDateComponent\nfrom langflow.components.helpers.memory import MemoryComponent\nfrom langflow.components.langchain_utilities.tool_calling import ToolCallingAgentComponent\nfrom langflow.custom.custom_component.component import _get_component_toolkit\nfrom langflow.custom.utils import update_component_build_config\nfrom langflow.field_typing import Tool\nfrom langflow.io import BoolInput, DropdownInput, MultilineInput, Output\nfrom langflow.logging import logger\nfrom langflow.schema.dotdict import dotdict\nfrom langflow.schema.message import Message\n\n\ndef set_advanced_true(component_input):\n component_input.advanced = True\n return component_input\n\n\nclass AgentComponent(ToolCallingAgentComponent):\n display_name: str = \"Agent\"\n description: str = \"Define the agent's instructions, then enter a task to complete using tools.\"\n icon = \"bot\"\n beta = False\n name = \"Agent\"\n\n memory_inputs = [set_advanced_true(component_input) for component_input in MemoryComponent().inputs]\n\n inputs = [\n DropdownInput(\n name=\"agent_llm\",\n display_name=\"Model Provider\",\n info=\"The provider of the language model that the agent will use to generate responses.\",\n options=[*sorted(MODEL_PROVIDERS_DICT.keys()), \"Custom\"],\n value=\"OpenAI\",\n real_time_refresh=True,\n input_types=[],\n options_metadata=[MODELS_METADATA[key] for key in sorted(MODELS_METADATA.keys())] + [{\"icon\": \"brain\"}],\n ),\n *MODEL_PROVIDERS_DICT[\"OpenAI\"][\"inputs\"],\n MultilineInput(\n name=\"system_prompt\",\n display_name=\"Agent Instructions\",\n info=\"System Prompt: Initial instructions and context provided to guide the agent's behavior.\",\n value=\"You are a helpful assistant that can use tools to answer questions and perform tasks.\",\n advanced=False,\n ),\n *LCToolsAgentComponent._base_inputs,\n *memory_inputs,\n BoolInput(\n name=\"add_current_date_tool\",\n display_name=\"Current Date\",\n advanced=True,\n info=\"If true, will add a tool to the agent that returns the current date.\",\n value=True,\n ),\n ]\n outputs = [Output(name=\"response\", display_name=\"Response\", method=\"message_response\")]\n\n async def message_response(self) -> Message:\n try:\n # Get LLM model and validate\n llm_model, display_name = self.get_llm()\n if llm_model is None:\n msg = \"No language model selected. Please choose a model to proceed.\"\n raise ValueError(msg)\n self.model_name = get_model_name(llm_model, display_name=display_name)\n\n # Get memory data\n self.chat_history = await self.get_memory_data()\n\n # Add current date tool if enabled\n if self.add_current_date_tool:\n if not isinstance(self.tools, list): # type: ignore[has-type]\n self.tools = []\n current_date_tool = (await CurrentDateComponent(**self.get_base_args()).to_toolkit()).pop(0)\n if not isinstance(current_date_tool, StructuredTool):\n msg = \"CurrentDateComponent must be converted to a StructuredTool\"\n raise TypeError(msg)\n self.tools.append(current_date_tool)\n # note the tools are not required to run the agent, hence the validation removed.\n\n # Set up and run agent\n self.set(\n llm=llm_model,\n tools=self.tools or [],\n chat_history=self.chat_history,\n input_value=self.input_value,\n system_prompt=self.system_prompt,\n )\n agent = self.create_agent_runnable()\n return await self.run_agent(agent)\n\n except (ValueError, TypeError, KeyError) as e:\n logger.error(f\"{type(e).__name__}: {e!s}\")\n raise\n except ExceptionWithMessageError as e:\n logger.error(f\"ExceptionWithMessageError occurred: {e}\")\n raise\n except Exception as e:\n logger.error(f\"Unexpected error: {e!s}\")\n raise\n\n async def get_memory_data(self):\n memory_kwargs = {\n component_input.name: getattr(self, f\"{component_input.name}\") for component_input in self.memory_inputs\n }\n # filter out empty values\n memory_kwargs = {k: v for k, v in memory_kwargs.items() if v is not None}\n\n return await MemoryComponent(**self.get_base_args()).set(**memory_kwargs).retrieve_messages()\n\n def get_llm(self):\n if not isinstance(self.agent_llm, str):\n return self.agent_llm, None\n\n try:\n provider_info = MODEL_PROVIDERS_DICT.get(self.agent_llm)\n if not provider_info:\n msg = f\"Invalid model provider: {self.agent_llm}\"\n raise ValueError(msg)\n\n component_class = provider_info.get(\"component_class\")\n display_name = component_class.display_name\n inputs = provider_info.get(\"inputs\")\n prefix = provider_info.get(\"prefix\", \"\")\n\n return self._build_llm_model(component_class, inputs, prefix), display_name\n\n except Exception as e:\n logger.error(f\"Error building {self.agent_llm} language model: {e!s}\")\n msg = f\"Failed to initialize language model: {e!s}\"\n raise ValueError(msg) from e\n\n def _build_llm_model(self, component, inputs, prefix=\"\"):\n model_kwargs = {}\n for input_ in inputs:\n if hasattr(self, f\"{prefix}{input_.name}\"):\n model_kwargs[input_.name] = getattr(self, f\"{prefix}{input_.name}\")\n return component.set(**model_kwargs).build_model()\n\n def set_component_params(self, component):\n provider_info = MODEL_PROVIDERS_DICT.get(self.agent_llm)\n if provider_info:\n inputs = provider_info.get(\"inputs\")\n prefix = provider_info.get(\"prefix\")\n model_kwargs = {input_.name: getattr(self, f\"{prefix}{input_.name}\") for input_ in inputs}\n\n return component.set(**model_kwargs)\n return component\n\n def delete_fields(self, build_config: dotdict, fields: dict | list[str]) -> None:\n \"\"\"Delete specified fields from build_config.\"\"\"\n for field in fields:\n build_config.pop(field, None)\n\n def update_input_types(self, build_config: dotdict) -> dotdict:\n \"\"\"Update input types for all fields in build_config.\"\"\"\n for key, value in build_config.items():\n if isinstance(value, dict):\n if value.get(\"input_types\") is None:\n build_config[key][\"input_types\"] = []\n elif hasattr(value, \"input_types\") and value.input_types is None:\n value.input_types = []\n return build_config\n\n async def update_build_config(\n self, build_config: dotdict, field_value: str, field_name: str | None = None\n ) -> dotdict:\n # Iterate over all providers in the MODEL_PROVIDERS_DICT\n # Existing logic for updating build_config\n if field_name in (\"agent_llm\",):\n build_config[\"agent_llm\"][\"value\"] = field_value\n provider_info = MODEL_PROVIDERS_DICT.get(field_value)\n if provider_info:\n component_class = provider_info.get(\"component_class\")\n if component_class and hasattr(component_class, \"update_build_config\"):\n # Call the component class's update_build_config method\n build_config = await update_component_build_config(\n component_class, build_config, field_value, \"model_name\"\n )\n\n provider_configs: dict[str, tuple[dict, list[dict]]] = {\n provider: (\n MODEL_PROVIDERS_DICT[provider][\"fields\"],\n [\n MODEL_PROVIDERS_DICT[other_provider][\"fields\"]\n for other_provider in MODEL_PROVIDERS_DICT\n if other_provider != provider\n ],\n )\n for provider in MODEL_PROVIDERS_DICT\n }\n if field_value in provider_configs:\n fields_to_add, fields_to_delete = provider_configs[field_value]\n\n # Delete fields from other providers\n for fields in fields_to_delete:\n self.delete_fields(build_config, fields)\n\n # Add provider-specific fields\n if field_value == \"OpenAI\" and not any(field in build_config for field in fields_to_add):\n build_config.update(fields_to_add)\n else:\n build_config.update(fields_to_add)\n # Reset input types for agent_llm\n build_config[\"agent_llm\"][\"input_types\"] = []\n elif field_value == \"Custom\":\n # Delete all provider fields\n self.delete_fields(build_config, ALL_PROVIDER_FIELDS)\n # Update with custom component\n custom_component = DropdownInput(\n name=\"agent_llm\",\n display_name=\"Language Model\",\n options=[*sorted(MODEL_PROVIDERS_DICT.keys()), \"Custom\"],\n value=\"Custom\",\n real_time_refresh=True,\n input_types=[\"LanguageModel\"],\n options_metadata=[MODELS_METADATA[key] for key in sorted(MODELS_METADATA.keys())]\n + [{\"icon\": \"brain\"}],\n )\n build_config.update({\"agent_llm\": custom_component.to_dict()})\n # Update input types for all fields\n build_config = self.update_input_types(build_config)\n\n # Validate required keys\n default_keys = [\n \"code\",\n \"_type\",\n \"agent_llm\",\n \"tools\",\n \"input_value\",\n \"add_current_date_tool\",\n \"system_prompt\",\n \"agent_description\",\n \"max_iterations\",\n \"handle_parsing_errors\",\n \"verbose\",\n ]\n missing_keys = [key for key in default_keys if key not in build_config]\n if missing_keys:\n msg = f\"Missing required keys in build_config: {missing_keys}\"\n raise ValueError(msg)\n if (\n isinstance(self.agent_llm, str)\n and self.agent_llm in MODEL_PROVIDERS_DICT\n and field_name in MODEL_DYNAMIC_UPDATE_FIELDS\n ):\n provider_info = MODEL_PROVIDERS_DICT.get(self.agent_llm)\n if provider_info:\n component_class = provider_info.get(\"component_class\")\n component_class = self.set_component_params(component_class)\n prefix = provider_info.get(\"prefix\")\n if component_class and hasattr(component_class, \"update_build_config\"):\n # Call each component class's update_build_config method\n # remove the prefix from the field_name\n if isinstance(field_name, str) and isinstance(prefix, str):\n field_name = field_name.replace(prefix, \"\")\n build_config = await update_component_build_config(\n component_class, build_config, field_value, \"model_name\"\n )\n return dotdict({k: v.to_dict() if hasattr(v, \"to_dict\") else v for k, v in build_config.items()})\n\n async def _get_tools(self) -> list[Tool]:\n component_toolkit = _get_component_toolkit()\n tools_names = self._build_tools_names()\n agent_description = self.get_tool_description()\n # TODO: Agent Description Depreciated Feature to be removed\n description = f\"{agent_description}{tools_names}\"\n tools = component_toolkit(component=self).get_tools(\n tool_name=\"Call_Agent\", tool_description=description, callbacks=self.get_langchain_callbacks()\n )\n if hasattr(self, \"tools_metadata\"):\n tools = component_toolkit(component=self, metadata=self.tools_metadata).update_tools_metadata(tools=tools)\n return tools\n"
"value": "from langchain_core.tools import StructuredTool\n\nfrom langflow.base.agents.agent import LCToolsAgentComponent\nfrom langflow.base.agents.events import ExceptionWithMessageError\nfrom langflow.base.models.model_input_constants import (\n ALL_PROVIDER_FIELDS,\n MODEL_DYNAMIC_UPDATE_FIELDS,\n MODEL_PROVIDERS_DICT,\n MODEL_PROVIDERS,\n MODELS_METADATA,\n)\nfrom langflow.base.models.model_utils import get_model_name\nfrom langflow.components.helpers.current_date import CurrentDateComponent\nfrom langflow.components.helpers.memory import MemoryComponent\nfrom langflow.components.langchain_utilities.tool_calling import ToolCallingAgentComponent\nfrom langflow.custom.custom_component.component import _get_component_toolkit\nfrom langflow.custom.utils import update_component_build_config\nfrom langflow.field_typing import Tool\nfrom langflow.io import BoolInput, DropdownInput, MultilineInput, Output\nfrom langflow.logging import logger\nfrom langflow.schema.dotdict import dotdict\nfrom langflow.schema.message import Message\n\n\ndef set_advanced_true(component_input):\n component_input.advanced = True\n return component_input\n\n\nclass AgentComponent(ToolCallingAgentComponent):\n display_name: str = \"Agent\"\n description: str = \"Define the agent's instructions, then enter a task to complete using tools.\"\n icon = \"bot\"\n beta = False\n name = \"Agent\"\n\n memory_inputs = [set_advanced_true(component_input) for component_input in MemoryComponent().inputs]\n\n inputs = [\n DropdownInput(\n name=\"agent_llm\",\n display_name=\"Model Provider\",\n info=\"The provider of the language model that the agent will use to generate responses.\",\n options=[*sorted(MODEL_PROVIDERS), \"Custom\"],\n value=\"OpenAI\",\n real_time_refresh=True,\n input_types=[],\n options_metadata=[MODELS_METADATA[key] for key in sorted(MODEL_PROVIDERS)] + [{\"icon\": \"brain\"}],\n ),\n *MODEL_PROVIDERS_DICT[\"OpenAI\"][\"inputs\"],\n MultilineInput(\n name=\"system_prompt\",\n display_name=\"Agent Instructions\",\n info=\"System Prompt: Initial instructions and context provided to guide the agent's behavior.\",\n value=\"You are a helpful assistant that can use tools to answer questions and perform tasks.\",\n advanced=False,\n ),\n *LCToolsAgentComponent._base_inputs,\n *memory_inputs,\n BoolInput(\n name=\"add_current_date_tool\",\n display_name=\"Current Date\",\n advanced=True,\n info=\"If true, will add a tool to the agent that returns the current date.\",\n value=True,\n ),\n ]\n outputs = [Output(name=\"response\", display_name=\"Response\", method=\"message_response\")]\n\n async def message_response(self) -> Message:\n try:\n # Get LLM model and validate\n llm_model, display_name = self.get_llm()\n if llm_model is None:\n msg = \"No language model selected. Please choose a model to proceed.\"\n raise ValueError(msg)\n self.model_name = get_model_name(llm_model, display_name=display_name)\n\n # Get memory data\n self.chat_history = await self.get_memory_data()\n\n # Add current date tool if enabled\n if self.add_current_date_tool:\n if not isinstance(self.tools, list): # type: ignore[has-type]\n self.tools = []\n current_date_tool = (await CurrentDateComponent(**self.get_base_args()).to_toolkit()).pop(0)\n if not isinstance(current_date_tool, StructuredTool):\n msg = \"CurrentDateComponent must be converted to a StructuredTool\"\n raise TypeError(msg)\n self.tools.append(current_date_tool)\n # note the tools are not required to run the agent, hence the validation removed.\n\n # Set up and run agent\n self.set(\n llm=llm_model,\n tools=self.tools or [],\n chat_history=self.chat_history,\n input_value=self.input_value,\n system_prompt=self.system_prompt,\n )\n agent = self.create_agent_runnable()\n return await self.run_agent(agent)\n\n except (ValueError, TypeError, KeyError) as e:\n logger.error(f\"{type(e).__name__}: {e!s}\")\n raise\n except ExceptionWithMessageError as e:\n logger.error(f\"ExceptionWithMessageError occurred: {e}\")\n raise\n except Exception as e:\n logger.error(f\"Unexpected error: {e!s}\")\n raise\n\n async def get_memory_data(self):\n memory_kwargs = {\n component_input.name: getattr(self, f\"{component_input.name}\") for component_input in self.memory_inputs\n }\n # filter out empty values\n memory_kwargs = {k: v for k, v in memory_kwargs.items() if v is not None}\n\n return await MemoryComponent(**self.get_base_args()).set(**memory_kwargs).retrieve_messages()\n\n def get_llm(self):\n if not isinstance(self.agent_llm, str):\n return self.agent_llm, None\n\n try:\n provider_info = MODEL_PROVIDERS_DICT.get(self.agent_llm)\n if not provider_info:\n msg = f\"Invalid model provider: {self.agent_llm}\"\n raise ValueError(msg)\n\n component_class = provider_info.get(\"component_class\")\n display_name = component_class.display_name\n inputs = provider_info.get(\"inputs\")\n prefix = provider_info.get(\"prefix\", \"\")\n\n return self._build_llm_model(component_class, inputs, prefix), display_name\n\n except Exception as e:\n logger.error(f\"Error building {self.agent_llm} language model: {e!s}\")\n msg = f\"Failed to initialize language model: {e!s}\"\n raise ValueError(msg) from e\n\n def _build_llm_model(self, component, inputs, prefix=\"\"):\n model_kwargs = {}\n for input_ in inputs:\n if hasattr(self, f\"{prefix}{input_.name}\"):\n model_kwargs[input_.name] = getattr(self, f\"{prefix}{input_.name}\")\n return component.set(**model_kwargs).build_model()\n\n def set_component_params(self, component):\n provider_info = MODEL_PROVIDERS_DICT.get(self.agent_llm)\n if provider_info:\n inputs = provider_info.get(\"inputs\")\n prefix = provider_info.get(\"prefix\")\n model_kwargs = {input_.name: getattr(self, f\"{prefix}{input_.name}\") for input_ in inputs}\n\n return component.set(**model_kwargs)\n return component\n\n def delete_fields(self, build_config: dotdict, fields: dict | list[str]) -> None:\n \"\"\"Delete specified fields from build_config.\"\"\"\n for field in fields:\n build_config.pop(field, None)\n\n def update_input_types(self, build_config: dotdict) -> dotdict:\n \"\"\"Update input types for all fields in build_config.\"\"\"\n for key, value in build_config.items():\n if isinstance(value, dict):\n if value.get(\"input_types\") is None:\n build_config[key][\"input_types\"] = []\n elif hasattr(value, \"input_types\") and value.input_types is None:\n value.input_types = []\n return build_config\n\n async def update_build_config(\n self, build_config: dotdict, field_value: str, field_name: str | None = None\n ) -> dotdict:\n # Iterate over all providers in the MODEL_PROVIDERS_DICT\n # Existing logic for updating build_config\n if field_name in (\"agent_llm\",):\n build_config[\"agent_llm\"][\"value\"] = field_value\n provider_info = MODEL_PROVIDERS_DICT.get(field_value)\n if provider_info:\n component_class = provider_info.get(\"component_class\")\n if component_class and hasattr(component_class, \"update_build_config\"):\n # Call the component class's update_build_config method\n build_config = await update_component_build_config(\n component_class, build_config, field_value, \"model_name\"\n )\n\n provider_configs: dict[str, tuple[dict, list[dict]]] = {\n provider: (\n MODEL_PROVIDERS_DICT[provider][\"fields\"],\n [\n MODEL_PROVIDERS_DICT[other_provider][\"fields\"]\n for other_provider in MODEL_PROVIDERS_DICT\n if other_provider != provider\n ],\n )\n for provider in MODEL_PROVIDERS_DICT\n }\n if field_value in provider_configs:\n fields_to_add, fields_to_delete = provider_configs[field_value]\n\n # Delete fields from other providers\n for fields in fields_to_delete:\n self.delete_fields(build_config, fields)\n\n # Add provider-specific fields\n if field_value == \"OpenAI\" and not any(field in build_config for field in fields_to_add):\n build_config.update(fields_to_add)\n else:\n build_config.update(fields_to_add)\n # Reset input types for agent_llm\n build_config[\"agent_llm\"][\"input_types\"] = []\n elif field_value == \"Custom\":\n # Delete all provider fields\n self.delete_fields(build_config, ALL_PROVIDER_FIELDS)\n # Update with custom component\n custom_component = DropdownInput(\n name=\"agent_llm\",\n display_name=\"Language Model\",\n options=[*sorted(MODEL_PROVIDERS), \"Custom\"],\n value=\"Custom\",\n real_time_refresh=True,\n input_types=[\"LanguageModel\"],\n options_metadata=[MODELS_METADATA[key] for key in sorted(MODELS_METADATA.keys())]\n + [{\"icon\": \"brain\"}],\n )\n build_config.update({\"agent_llm\": custom_component.to_dict()})\n # Update input types for all fields\n build_config = self.update_input_types(build_config)\n\n # Validate required keys\n default_keys = [\n \"code\",\n \"_type\",\n \"agent_llm\",\n \"tools\",\n \"input_value\",\n \"add_current_date_tool\",\n \"system_prompt\",\n \"agent_description\",\n \"max_iterations\",\n \"handle_parsing_errors\",\n \"verbose\",\n ]\n missing_keys = [key for key in default_keys if key not in build_config]\n if missing_keys:\n msg = f\"Missing required keys in build_config: {missing_keys}\"\n raise ValueError(msg)\n if (\n isinstance(self.agent_llm, str)\n and self.agent_llm in MODEL_PROVIDERS_DICT\n and field_name in MODEL_DYNAMIC_UPDATE_FIELDS\n ):\n provider_info = MODEL_PROVIDERS_DICT.get(self.agent_llm)\n if provider_info:\n component_class = provider_info.get(\"component_class\")\n component_class = self.set_component_params(component_class)\n prefix = provider_info.get(\"prefix\")\n if component_class and hasattr(component_class, \"update_build_config\"):\n # Call each component class's update_build_config method\n # remove the prefix from the field_name\n if isinstance(field_name, str) and isinstance(prefix, str):\n field_name = field_name.replace(prefix, \"\")\n build_config = await update_component_build_config(\n component_class, build_config, field_value, \"model_name\"\n )\n return dotdict({k: v.to_dict() if hasattr(v, \"to_dict\") else v for k, v in build_config.items()})\n\n async def _get_tools(self) -> list[Tool]:\n component_toolkit = _get_component_toolkit()\n tools_names = self._build_tools_names()\n agent_description = self.get_tool_description()\n # TODO: Agent Description Depreciated Feature to be removed\n description = f\"{agent_description}{tools_names}\"\n tools = component_toolkit(component=self).get_tools(\n tool_name=\"Call_Agent\", tool_description=description, callbacks=self.get_langchain_callbacks()\n )\n if hasattr(self, \"tools_metadata\"):\n tools = component_toolkit(component=self, metadata=self.tools_metadata).update_tools_metadata(tools=tools)\n return tools\n"
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

Ensure Custom provider metadata aligns with active providers
In the Custom branch you’re constructing options_metadata from sorted(MODELS_METADATA.keys()), which may include deprecated or inactive providers. To stay consistent with the dropdown options (which use MODEL_PROVIDERS), switch to filtering metadata by the same list.

Apply this diff:

-                    options_metadata=[MODELS_METADATA[key] for key in sorted(MODELS_METADATA.keys())]
-                    + [{ "icon": "brain" }],
+                    options_metadata=[MODELS_METADATA[key] for key in sorted(MODEL_PROVIDERS)]
+                    + [{ "icon": "brain" }],
📝 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
"value": "from langchain_core.tools import StructuredTool\n\nfrom langflow.base.agents.agent import LCToolsAgentComponent\nfrom langflow.base.agents.events import ExceptionWithMessageError\nfrom langflow.base.models.model_input_constants import (\n ALL_PROVIDER_FIELDS,\n MODEL_DYNAMIC_UPDATE_FIELDS,\n MODEL_PROVIDERS_DICT,\n MODEL_PROVIDERS,\n MODELS_METADATA,\n)\nfrom langflow.base.models.model_utils import get_model_name\nfrom langflow.components.helpers.current_date import CurrentDateComponent\nfrom langflow.components.helpers.memory import MemoryComponent\nfrom langflow.components.langchain_utilities.tool_calling import ToolCallingAgentComponent\nfrom langflow.custom.custom_component.component import _get_component_toolkit\nfrom langflow.custom.utils import update_component_build_config\nfrom langflow.field_typing import Tool\nfrom langflow.io import BoolInput, DropdownInput, MultilineInput, Output\nfrom langflow.logging import logger\nfrom langflow.schema.dotdict import dotdict\nfrom langflow.schema.message import Message\n\n\ndef set_advanced_true(component_input):\n component_input.advanced = True\n return component_input\n\n\nclass AgentComponent(ToolCallingAgentComponent):\n display_name: str = \"Agent\"\n description: str = \"Define the agent's instructions, then enter a task to complete using tools.\"\n icon = \"bot\"\n beta = False\n name = \"Agent\"\n\n memory_inputs = [set_advanced_true(component_input) for component_input in MemoryComponent().inputs]\n\n inputs = [\n DropdownInput(\n name=\"agent_llm\",\n display_name=\"Model Provider\",\n info=\"The provider of the language model that the agent will use to generate responses.\",\n options=[*sorted(MODEL_PROVIDERS), \"Custom\"],\n value=\"OpenAI\",\n real_time_refresh=True,\n input_types=[],\n options_metadata=[MODELS_METADATA[key] for key in sorted(MODEL_PROVIDERS)] + [{\"icon\": \"brain\"}],\n ),\n *MODEL_PROVIDERS_DICT[\"OpenAI\"][\"inputs\"],\n MultilineInput(\n name=\"system_prompt\",\n display_name=\"Agent Instructions\",\n info=\"System Prompt: Initial instructions and context provided to guide the agent's behavior.\",\n value=\"You are a helpful assistant that can use tools to answer questions and perform tasks.\",\n advanced=False,\n ),\n *LCToolsAgentComponent._base_inputs,\n *memory_inputs,\n BoolInput(\n name=\"add_current_date_tool\",\n display_name=\"Current Date\",\n advanced=True,\n info=\"If true, will add a tool to the agent that returns the current date.\",\n value=True,\n ),\n ]\n outputs = [Output(name=\"response\", display_name=\"Response\", method=\"message_response\")]\n\n async def message_response(self) -> Message:\n try:\n # Get LLM model and validate\n llm_model, display_name = self.get_llm()\n if llm_model is None:\n msg = \"No language model selected. Please choose a model to proceed.\"\n raise ValueError(msg)\n self.model_name = get_model_name(llm_model, display_name=display_name)\n\n # Get memory data\n self.chat_history = await self.get_memory_data()\n\n # Add current date tool if enabled\n if self.add_current_date_tool:\n if not isinstance(self.tools, list): # type: ignore[has-type]\n self.tools = []\n current_date_tool = (await CurrentDateComponent(**self.get_base_args()).to_toolkit()).pop(0)\n if not isinstance(current_date_tool, StructuredTool):\n msg = \"CurrentDateComponent must be converted to a StructuredTool\"\n raise TypeError(msg)\n self.tools.append(current_date_tool)\n # note the tools are not required to run the agent, hence the validation removed.\n\n # Set up and run agent\n self.set(\n llm=llm_model,\n tools=self.tools or [],\n chat_history=self.chat_history,\n input_value=self.input_value,\n system_prompt=self.system_prompt,\n )\n agent = self.create_agent_runnable()\n return await self.run_agent(agent)\n\n except (ValueError, TypeError, KeyError) as e:\n logger.error(f\"{type(e).__name__}: {e!s}\")\n raise\n except ExceptionWithMessageError as e:\n logger.error(f\"ExceptionWithMessageError occurred: {e}\")\n raise\n except Exception as e:\n logger.error(f\"Unexpected error: {e!s}\")\n raise\n\n async def get_memory_data(self):\n memory_kwargs = {\n component_input.name: getattr(self, f\"{component_input.name}\") for component_input in self.memory_inputs\n }\n # filter out empty values\n memory_kwargs = {k: v for k, v in memory_kwargs.items() if v is not None}\n\n return await MemoryComponent(**self.get_base_args()).set(**memory_kwargs).retrieve_messages()\n\n def get_llm(self):\n if not isinstance(self.agent_llm, str):\n return self.agent_llm, None\n\n try:\n provider_info = MODEL_PROVIDERS_DICT.get(self.agent_llm)\n if not provider_info:\n msg = f\"Invalid model provider: {self.agent_llm}\"\n raise ValueError(msg)\n\n component_class = provider_info.get(\"component_class\")\n display_name = component_class.display_name\n inputs = provider_info.get(\"inputs\")\n prefix = provider_info.get(\"prefix\", \"\")\n\n return self._build_llm_model(component_class, inputs, prefix), display_name\n\n except Exception as e:\n logger.error(f\"Error building {self.agent_llm} language model: {e!s}\")\n msg = f\"Failed to initialize language model: {e!s}\"\n raise ValueError(msg) from e\n\n def _build_llm_model(self, component, inputs, prefix=\"\"):\n model_kwargs = {}\n for input_ in inputs:\n if hasattr(self, f\"{prefix}{input_.name}\"):\n model_kwargs[input_.name] = getattr(self, f\"{prefix}{input_.name}\")\n return component.set(**model_kwargs).build_model()\n\n def set_component_params(self, component):\n provider_info = MODEL_PROVIDERS_DICT.get(self.agent_llm)\n if provider_info:\n inputs = provider_info.get(\"inputs\")\n prefix = provider_info.get(\"prefix\")\n model_kwargs = {input_.name: getattr(self, f\"{prefix}{input_.name}\") for input_ in inputs}\n\n return component.set(**model_kwargs)\n return component\n\n def delete_fields(self, build_config: dotdict, fields: dict | list[str]) -> None:\n \"\"\"Delete specified fields from build_config.\"\"\"\n for field in fields:\n build_config.pop(field, None)\n\n def update_input_types(self, build_config: dotdict) -> dotdict:\n \"\"\"Update input types for all fields in build_config.\"\"\"\n for key, value in build_config.items():\n if isinstance(value, dict):\n if value.get(\"input_types\") is None:\n build_config[key][\"input_types\"] = []\n elif hasattr(value, \"input_types\") and value.input_types is None:\n value.input_types = []\n return build_config\n\n async def update_build_config(\n self, build_config: dotdict, field_value: str, field_name: str | None = None\n ) -> dotdict:\n # Iterate over all providers in the MODEL_PROVIDERS_DICT\n # Existing logic for updating build_config\n if field_name in (\"agent_llm\",):\n build_config[\"agent_llm\"][\"value\"] = field_value\n provider_info = MODEL_PROVIDERS_DICT.get(field_value)\n if provider_info:\n component_class = provider_info.get(\"component_class\")\n if component_class and hasattr(component_class, \"update_build_config\"):\n # Call the component class's update_build_config method\n build_config = await update_component_build_config(\n component_class, build_config, field_value, \"model_name\"\n )\n\n provider_configs: dict[str, tuple[dict, list[dict]]] = {\n provider: (\n MODEL_PROVIDERS_DICT[provider][\"fields\"],\n [\n MODEL_PROVIDERS_DICT[other_provider][\"fields\"]\n for other_provider in MODEL_PROVIDERS_DICT\n if other_provider != provider\n ],\n )\n for provider in MODEL_PROVIDERS_DICT\n }\n if field_value in provider_configs:\n fields_to_add, fields_to_delete = provider_configs[field_value]\n\n # Delete fields from other providers\n for fields in fields_to_delete:\n self.delete_fields(build_config, fields)\n\n # Add provider-specific fields\n if field_value == \"OpenAI\" and not any(field in build_config for field in fields_to_add):\n build_config.update(fields_to_add)\n else:\n build_config.update(fields_to_add)\n # Reset input types for agent_llm\n build_config[\"agent_llm\"][\"input_types\"] = []\n elif field_value == \"Custom\":\n # Delete all provider fields\n self.delete_fields(build_config, ALL_PROVIDER_FIELDS)\n # Update with custom component\n custom_component = DropdownInput(\n name=\"agent_llm\",\n display_name=\"Language Model\",\n options=[*sorted(MODEL_PROVIDERS), \"Custom\"],\n value=\"Custom\",\n real_time_refresh=True,\n input_types=[\"LanguageModel\"],\n options_metadata=[MODELS_METADATA[key] for key in sorted(MODELS_METADATA.keys())]\n + [{\"icon\": \"brain\"}],\n )\n build_config.update({\"agent_llm\": custom_component.to_dict()})\n # Update input types for all fields\n build_config = self.update_input_types(build_config)\n\n # Validate required keys\n default_keys = [\n \"code\",\n \"_type\",\n \"agent_llm\",\n \"tools\",\n \"input_value\",\n \"add_current_date_tool\",\n \"system_prompt\",\n \"agent_description\",\n \"max_iterations\",\n \"handle_parsing_errors\",\n \"verbose\",\n ]\n missing_keys = [key for key in default_keys if key not in build_config]\n if missing_keys:\n msg = f\"Missing required keys in build_config: {missing_keys}\"\n raise ValueError(msg)\n if (\n isinstance(self.agent_llm, str)\n and self.agent_llm in MODEL_PROVIDERS_DICT\n and field_name in MODEL_DYNAMIC_UPDATE_FIELDS\n ):\n provider_info = MODEL_PROVIDERS_DICT.get(self.agent_llm)\n if provider_info:\n component_class = provider_info.get(\"component_class\")\n component_class = self.set_component_params(component_class)\n prefix = provider_info.get(\"prefix\")\n if component_class and hasattr(component_class, \"update_build_config\"):\n # Call each component class's update_build_config method\n # remove the prefix from the field_name\n if isinstance(field_name, str) and isinstance(prefix, str):\n field_name = field_name.replace(prefix, \"\")\n build_config = await update_component_build_config(\n component_class, build_config, field_value, \"model_name\"\n )\n return dotdict({k: v.to_dict() if hasattr(v, \"to_dict\") else v for k, v in build_config.items()})\n\n async def _get_tools(self) -> list[Tool]:\n component_toolkit = _get_component_toolkit()\n tools_names = self._build_tools_names()\n agent_description = self.get_tool_description()\n # TODO: Agent Description Depreciated Feature to be removed\n description = f\"{agent_description}{tools_names}\"\n tools = component_toolkit(component=self).get_tools(\n tool_name=\"Call_Agent\", tool_description=description, callbacks=self.get_langchain_callbacks()\n )\n if hasattr(self, \"tools_metadata\"):\n tools = component_toolkit(component=self, metadata=self.tools_metadata).update_tools_metadata(tools=tools)\n return tools\n"
elif field_value == "Custom":
# Delete all provider fields
self.delete_fields(build_config, ALL_PROVIDER_FIELDS)
# Update with custom component
custom_component = DropdownInput(
name="agent_llm",
display_name="Language Model",
options=[*sorted(MODEL_PROVIDERS), "Custom"],
value="Custom",
real_time_refresh=True,
input_types=["LanguageModel"],
options_metadata=[MODELS_METADATA[key] for key in sorted(MODEL_PROVIDERS)]
+ [{"icon": "brain"}],
)
build_config.update({"agent_llm": custom_component.to_dict()})
🤖 Prompt for AI Agents
In src/backend/base/langflow/initial_setup/starter_projects/Market Research.json
at line 1314, the options_metadata for the Custom provider dropdown is built
using sorted(MODELS_METADATA.keys()), which may include deprecated or inactive
providers. To fix this, change the options_metadata construction to filter
MODELS_METADATA by the sorted keys of MODEL_PROVIDERS instead, ensuring
consistency with the dropdown options.

@github-actions github-actions Bot added enhancement New feature or request and removed enhancement New feature or request labels Jun 18, 2025
@github-actions github-actions Bot added enhancement New feature or request and removed enhancement New feature or request labels Jun 18, 2025
Copy link
Copy Markdown
Member

@Cristhianzl Cristhianzl left a comment

Choose a reason for hiding this comment

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

lgtm

@dosubot dosubot Bot added the lgtm This PR has been approved by a maintainer label Jun 18, 2025
@edwinjosechittilappilly
Copy link
Copy Markdown
Collaborator Author

@Cristhianzl Is the CI failing Due to the freeze error?

@github-actions github-actions Bot added enhancement New feature or request and removed enhancement New feature or request labels Jun 21, 2025
Copy link
Copy Markdown
Contributor

@ogabrielluiz ogabrielluiz left a comment

Choose a reason for hiding this comment

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

We should iterate only once to get the actives then all other iterations can be done using the filtered dict.

@dosubot dosubot Bot removed the lgtm This PR has been approved by a maintainer label Jun 21, 2025
@edwinjosechittilappilly
Copy link
Copy Markdown
Collaborator Author

We should iterate only once to get the actives then all other iterations can be done using the filtered dict.

@coderabbitai Looking forward to your suggestions on this comment. Does improvements possible in model_input_constants.py ? Suggestion on how to proceed with the comment. Planning to build ACTIVE_MODEL_PROVIDERS_DICT once and reuse it.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Jun 22, 2025

Tip

For best results, initiate chat on the files or code changes.

🧩 Analysis chain

@edwinjosechittilappilly, excellent optimization opportunity! Looking at the current implementation, I can see multiple filtering operations happening:

# Current approach - multiple iterations:
MODEL_PROVIDERS = [key for key, value in MODEL_PROVIDERS_DICT.items() if value.get("is_active", True)]
ALL_PROVIDER_FIELDS = {k: v for provider_dict in [value for key, value in MODEL_PROVIDERS_DICT.items() if value.get("is_active", True)] for k, v in provider_dict.get("fields", {}).items()}
MODELS_METADATA = {key: value for key, value in MODEL_PROVIDERS_DICT.items() if value.get("is_active", True)}

Recommended optimization:

# Build filtered dict once and reuse it
ACTIVE_MODEL_PROVIDERS_DICT = {
    key: value for key, value in MODEL_PROVIDERS_DICT.items() 
    if value.get("is_active", True)
}

# Derive all other collections from the filtered dict
MODEL_PROVIDERS = list(ACTIVE_MODEL_PROVIDERS_DICT.keys())
ALL_PROVIDER_FIELDS = {
    k: v for provider_dict in ACTIVE_MODEL_PROVIDERS_DICT.values() 
    for k, v in provider_dict.get("fields", {}).items()
}
MODELS_METADATA = ACTIVE_MODEL_PROVIDERS_DICT.copy()

Benefits of this approach:

  1. Single iteration through MODEL_PROVIDERS_DICT instead of 3 separate iterations
  2. Improved maintainability - all active provider logic centralized
  3. Better performance - especially beneficial if the provider dict grows
  4. Cleaner code - the intent is more explicit
  5. Reusability - ACTIVE_MODEL_PROVIDERS_DICT can be imported and used elsewhere

This optimization reduces the time complexity from O(3n) to O(n) for the filtering operations, and makes the code more maintainable by having a single source of truth for active providers.

Would you like me to help plan the implementation of this optimization?


🏁 Script executed:

#!/bin/bash
# Read the model_input_constants.py file to understand the current implementation
cat src/backend/base/langflow/base/models/model_input_constants.py

Length of output: 10589


Perfect! Now I can see the exact optimization opportunity. You're absolutely right - there are 3 separate iterations through MODEL_PROVIDERS_DICT with the same filter condition.

Current Implementation (3 iterations):

# Current - 3 separate iterations with same filter condition
MODEL_PROVIDERS = [name for name, prov in MODEL_PROVIDERS_DICT.items() if prov.get("is_active", True)]

ALL_PROVIDER_FIELDS: list[str] = [
    field for prov in MODEL_PROVIDERS_DICT.values() if prov.get("is_active", True) for field in prov["fields"]
]

MODELS_METADATA = {
    key: {"icon": prov["icon"]} for key, prov in MODEL_PROVIDERS_DICT.items() if prov.get("is_active", True)
}

Optimized Implementation (1 iteration):

# Build filtered dict once - single iteration
ACTIVE_MODEL_PROVIDERS_DICT = {
    name: prov for name, prov in MODEL_PROVIDERS_DICT.items() 
    if prov.get("is_active", True)
}

# Derive all collections from the filtered dict - no additional iterations through original dict
MODEL_PROVIDERS = list(ACTIVE_MODEL_PROVIDERS_DICT.keys())

ALL_PROVIDER_FIELDS: list[str] = [
    field for prov in ACTIVE_MODEL_PROVIDERS_DICT.values() for field in prov["fields"]
]

MODELS_METADATA = {
    key: {"icon": prov["icon"]} for key, prov in ACTIVE_MODEL_PROVIDERS_DICT.items()
}

Benefits:

  1. Performance: Reduces time complexity from O(3n) to O(n)
  2. Single source of truth: ACTIVE_MODEL_PROVIDERS_DICT becomes the canonical filtered collection
  3. Maintainability: If filtering logic changes, only one place to update
  4. Reusability: Other parts of the codebase can import and use ACTIVE_MODEL_PROVIDERS_DICT
  5. Memory efficiency: Active providers are cached, avoiding repeated filtering

Additional Suggestion:

You could also export ACTIVE_MODEL_PROVIDERS_DICT to make it available for other modules:

# At the end of the file, add to __all__ if it exists, or create it:
__all__ = [
    "MODEL_PROVIDERS_DICT",
    "ACTIVE_MODEL_PROVIDERS_DICT",  # New export
    "MODEL_PROVIDERS", 
    "ALL_PROVIDER_FIELDS",
    "MODELS_METADATA",
    "MODEL_DYNAMIC_UPDATE_FIELDS"
]

This optimization is particularly valuable since this module is likely imported by many agent components, so the performance gain will be multiplied across the application.

Would you like me to help plan the code changes for this optimization?

@github-actions github-actions Bot added enhancement New feature or request and removed enhancement New feature or request labels Jun 22, 2025
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Jun 22, 2025

Tip

For best results, initiate chat on the files or code changes.

🧩 Analysis chain

@edwinjosechittilappilly, let me review the current implementation to check if the optimizations have been applied correctly.


🏁 Script executed:

#!/bin/bash
# Read the current content of model_input_constants.py to review the optimizations
cat src/backend/base/langflow/base/models/model_input_constants.py

Length of output: 10703


Perfect! ✅ The optimizations have been correctly implemented. Let me break down what I see:

✅ Optimization Successfully Applied

Before (Multiple Iterations):

# Previous approach - 3 separate iterations
MODEL_PROVIDERS = [name for name, prov in MODEL_PROVIDERS_DICT.items() if prov.get("is_active", True)]
ALL_PROVIDER_FIELDS = [field for prov in MODEL_PROVIDERS_DICT.values() if prov.get("is_active", True) for field in prov["fields"]]
MODELS_METADATA = {key: {"icon": prov["icon"]} for key, prov in MODEL_PROVIDERS_DICT.items() if prov.get("is_active", True)}

After (Single Iteration + Reuse):

# ✅ Build filtered dict once - single iteration
ACTIVE_MODEL_PROVIDERS_DICT: dict[str, ModelProvidersDict] = {
    name: prov for name, prov in MODEL_PROVIDERS_DICT.items() if prov.get("is_active", True)
}

# ✅ Derive all collections from the filtered dict - no additional iterations
MODEL_PROVIDERS: list[str] = list(ACTIVE_MODEL_PROVIDERS_DICT.keys())
ALL_PROVIDER_FIELDS: list[str] = [field for prov in ACTIVE_MODEL_PROVIDERS_DICT.values() for field in prov["fields"]]
MODELS_METADATA = {name: {"icon": prov["icon"]} for name, prov in ACTIVE_MODEL_PROVIDERS_DICT.items()}

✅ Verification Results

  1. Single iteration through MODEL_PROVIDERS_DICT
  2. Type hints added properly ✅ (dict[str, ModelProvidersDict])
  3. All derived collections use the filtered dict
  4. Performance improved from O(3n) to O(n)
  5. Clean section separation with comment

✅ Active Providers Configuration

The is_active flags are properly set:

  • Active: OpenAI, Groq, Anthropic, Google Generative AI
  • Inactive: Azure OpenAI, NVIDIA, Amazon Bedrock, SambaNova

This matches the changes seen in the agent components where the inactive providers were removed from dropdown options.

🎯 Additional Benefits Achieved

  1. Maintainability: Single source of truth for active providers
  2. Reusability: ACTIVE_MODEL_PROVIDERS_DICT can be imported by other modules
  3. Clarity: Clear separation between all providers vs active providers
  4. Performance: Reduced computational overhead, especially beneficial as the provider list grows

The implementation is excellent and follows best practices. The optimization is working exactly as intended! 🚀

@edwinjosechittilappilly
Copy link
Copy Markdown
Collaborator Author

@coderabbitai suggest merge confidence after reviewing the PR

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Jun 22, 2025

Tip

For best results, initiate chat on the files or code changes.

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Comprehensive review of the PR changes

echo "=== 1. CHECKING CONSISTENCY ACROSS AGENT COMPONENTS ==="
# Check if MODEL_PROVIDERS import is consistent across all agent files
echo "Checking MODEL_PROVIDERS import consistency:"
rg "from.*model_input_constants.*import.*MODEL_PROVIDERS" --type json -A 2 -B 2

echo -e "\n=== 2. VERIFYING REMOVED PROVIDERS ARE CONSISTENTLY EXCLUDED ==="
# Check that inactive providers are consistently removed
echo "Checking for any remaining references to inactive providers in dropdowns:"
rg '"Amazon Bedrock"|"Azure OpenAI"|"NVIDIA"|"SambaNova"' src/backend/base/langflow/initial_setup/starter_projects/ -A 1 -B 1

echo -e "\n=== 3. ANALYZING CORE CONSTANTS IMPLEMENTATION ==="
# Verify the optimized constants implementation
echo "Reviewing optimized constants structure:"
rg -A 10 "ACTIVE_MODEL_PROVIDERS_DICT.*=" src/backend/base/langflow/base/models/model_input_constants.py

echo -e "\n=== 4. CHECKING FOR POTENTIAL BREAKING CHANGES ==="
# Look for any direct usage of removed providers in non-starter-project files
echo "Searching for direct provider references in main codebase:"
rg "Amazon Bedrock|Azure OpenAI|NVIDIA|SambaNova" src/backend/base/langflow/components/ -C 2

echo -e "\n=== 5. VERIFYING TYPE CONSISTENCY ==="
# Check if type hints are properly maintained
echo "Checking type annotations consistency:"
rg "ModelProvidersDict|dict\[str.*ModelProvidersDict\]" src/backend/base/langflow/base/models/model_input_constants.py -A 2 -B 2

echo -e "\n=== 6. CHECKING UPDATE_BUILD_CONFIG CONSISTENCY ==="
# Verify update_build_config methods use MODEL_PROVIDERS consistently  
echo "Checking update_build_config implementations:"
rg -A 5 -B 5 "update_build_config.*MODEL_PROVIDERS" src/backend/base/langflow/initial_setup/starter_projects/ | head -20

Length of output: 93660


🏁 Script executed:

#!/bin/bash
# Continue analysis with more targeted scripts

echo "=== CONTINUING ANALYSIS ==="

echo "=== 1. CHECKING FOR LINGERING INACTIVE PROVIDERS ==="
# Check if any inactive providers are still referenced in starter projects
rg -A 2 -B 2 '"Amazon Bedrock"' src/backend/base/langflow/initial_setup/starter_projects/ | head -10
rg -A 2 -B 2 '"Azure OpenAI"' src/backend/base/langflow/initial_setup/starter_projects/ | head -10

echo -e "\n=== 2. VALIDATING CORE CONSTANTS STRUCTURE ==="
# Check the constants file structure  
echo "Checking ACTIVE_MODEL_PROVIDERS_DICT implementation:"
grep -A 5 "ACTIVE_MODEL_PROVIDERS_DICT" src/backend/base/langflow/base/models/model_input_constants.py

echo -e "\n=== 3. CHECKING MAIN AGENT COMPONENT ==="
# Check the main agent component uses MODEL_PROVIDERS correctly
echo "Checking agent.py implementation:"
grep -A 3 -B 3 "MODEL_PROVIDERS" src/backend/base/langflow/components/agents/agent.py

echo -e "\n=== 4. TESTING IMPORT CONSISTENCY ==="
# Verify imports are consistent across files
echo "Import patterns in starter projects:"
rg "from.*model_input_constants.*import" src/backend/base/langflow/initial_setup/starter_projects/ | head -5

echo -e "\n=== 5. CHECKING FOR BREAKING CHANGES ==="
# Look for any hardcoded provider lists that might break
echo "Checking for hardcoded provider references in main components:"
rg -l "Amazon Bedrock|Azure OpenAI|NVIDIA|SambaNova" src/backend/base/langflow/components/ | head -5

Length of output: 72234


@edwinjosechittilappilly Based on my comprehensive analysis of the PR, I can provide a detailed merge confidence assessment:

🎯 MERGE CONFIDENCE: HIGH (85-90%)

Strengths Identified

1. Excellent Implementation Quality

  • ACTIVE_MODEL_PROVIDERS_DICT properly filters active providers
  • ✅ All derived collections correctly use the optimized structure
  • ✅ Performance optimization successfully implemented (O(3n) → O(n))
  • ✅ Proper type hints with dict[str, ModelProvidersDict]

2. Comprehensive Consistency

  • 16 starter projects consistently updated with MODEL_PROVIDERS imports
  • Zero lingering references to inactive providers in starter projects
  • ✅ Main agent.py component properly updated with filtered provider list
  • ✅ All update_build_config methods use MODEL_PROVIDERS consistently

3. Minimal Breaking Change Risk

  • Backward compatible: Inactive providers still exist in MODEL_PROVIDERS_DICT, just filtered
  • Runtime graceful: No hardcoded provider references found in main components
  • Fallback handling: Custom provider option remains available

⚠️ Areas Requiring Attention

1. Testing Implications (Medium Priority)

  • Tests may need updates to reflect the reduced provider set
  • CI might fail if tests expect inactive providers in dropdowns
  • Component initialization tests should be verified

2. Documentation Updates (Low Priority)

  • API documentation may need updating to reflect active vs inactive providers
  • Migration guides for users relying on inactive providers

3. Configuration Management (Low Priority)

  • Consider exporting ACTIVE_MODEL_PROVIDERS_DICT in __all__ for reusability

🔧 Pre-Merge Recommendations

  1. Run comprehensive test suite to identify any test failures
  2. Verify starter project functionality with active providers only
  3. Confirm agent dropdowns show only active providers in UI
  4. Test custom provider workflow remains functional

📊 Risk Assessment

Risk Category Level Mitigation
Breaking Changes Low Backward compatible implementation
Performance Impact Low Improved (optimization achieved)
User Experience Low Cleaner provider selection
Test Coverage Medium May require test updates
Production Stability Low Well-contained changes

🚀 Business Value

  • Performance: Reduced computational overhead in provider filtering
  • Maintainability: Single source of truth for active providers
  • User Experience: Cleaner dropdown with only functional providers
  • Code Quality: Excellent optimization implementation

Final Recommendation: ✅ APPROVE TO MERGE

The implementation is technically sound, performance-optimized, and consistently applied across all components. The risk level is low with high business value. Any potential test failures can be addressed in follow-up commits.

Confidence Level: 87/100 🎯

@dosubot dosubot Bot added the lgtm This PR has been approved by a maintainer label Jun 23, 2025
@github-actions github-actions Bot added enhancement New feature or request and removed enhancement New feature or request labels Jun 23, 2025
@github-actions github-actions Bot added enhancement New feature or request and removed enhancement New feature or request labels Jun 24, 2025
@github-actions github-actions Bot added enhancement New feature or request and removed enhancement New feature or request labels Jun 24, 2025
@github-actions github-actions Bot added enhancement New feature or request and removed enhancement New feature or request labels Jun 24, 2025
@edwinjosechittilappilly edwinjosechittilappilly added this pull request to the merge queue Jun 24, 2025
Merged via the queue into main with commit f4bbfd0 Jun 24, 2025
65 checks passed
@edwinjosechittilappilly edwinjosechittilappilly deleted the fix-QoL-Agent-models branch June 24, 2025 07:09
ogabrielluiz pushed a commit to bkatya2001/langflow that referenced this pull request Jun 24, 2025
…ring (langflow-ai#8588)

* update model constants

* [autofix.ci] apply automated fixes

* update model input constants to reuse the filtered data

* Update test_agent_component.py

* [autofix.ci] apply automated fixes

---------

Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
Khurdhula-Harshavardhan pushed a commit to JigsawStack/langflow that referenced this pull request Jul 1, 2025
…ring (langflow-ai#8588)

* update model constants

* [autofix.ci] apply automated fixes

* update model input constants to reuse the filtered data

* Update test_agent_component.py

* [autofix.ci] apply automated fixes

---------

Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request lgtm This PR has been approved by a maintainer size:L This PR changes 100-499 lines, ignoring generated files.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants