Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

How to dynamically update DropDown Input value based on other inputs? #5826

Open
themightywolfie opened this issue Jan 21, 2025 · 5 comments
Labels
question Further information is requested

Comments

@themightywolfie
Copy link

I am trying to create a custom component for a vector store and I would like the component to dynamically show me the collections (as a dropdown) in the vector store based on the other inputs I define like host, port, api key etc. How can it be done?

@dosubot dosubot bot added the question Further information is requested label Jan 21, 2025
Copy link

dosubot bot commented Jan 21, 2025

Hey @themightywolfie! I'm here to help you with any coding bugs, questions, or becoming a contributor. Let's tackle this issue together!

To dynamically update a DropDown input value based on other inputs like host, port, and API key, you can follow a pattern similar to the LangWatchComponent in Langflow. Here's a general approach:

  1. Define Inputs: Create inputs for host, port, API key, and the DropDown for collections.

  2. Fetch Data: Implement a method to fetch the collections from the vector store using the provided host, port, and API key. This can be done using an HTTP request or a client library specific to your vector store.

  3. Update DropDown Options: Once you have the collections, update the DropDown options. You can achieve this by setting the options property of the DropDown component dynamically.

  4. Handle Input Changes: Add event listeners or handlers to the host, port, and API key inputs. When any of these inputs change, trigger the data fetching method to update the DropDown options.

Here's a simplified example of how you might implement this:

def update_collections_dropdown(self, host, port, api_key):
    # Fetch collections from the vector store
    collections = fetch_collections_from_vector_store(host, port, api_key)
    
    # Update the DropDown options
    self.dropdown.options = collections

def on_input_change(self, field_name, field_value):
    if field_name in ["host", "port", "api_key"]:
        # Re-fetch collections when any of these inputs change
        self.update_collections_dropdown(self.host, self.port, self.api_key)

In this example, fetch_collections_from_vector_store is a placeholder for the actual logic to retrieve collections from your vector store. The on_input_change method listens for changes in the relevant inputs and updates the DropDown accordingly. This pattern ensures that the DropDown reflects the current state of the other inputs.

To continue talking to Dosu, mention @dosu.


Help Dosu learn! Give it feedback: Great Response | Irrelevant Answer | Incorrect Sources | Too Verbose | Hallucination | Other

@themightywolfie
Copy link
Author

@dosu Could you please provide the link to the source code of LangWatchComponent? How do I define listeners or add callback functions to the other Inputs? The other inputs are StrInputs

Copy link

dosubot bot commented Jan 21, 2025

The LangWatchComponent is implemented in the Langflow codebase in the file src/backend/base/langflow/components/langwatch/langwatch.py. You can view the source code here [1].

For defining listeners or adding callback functions to StrInput components, you can use the onChange event handler in React. This involves creating a function that handles the input change and updates the state or performs actions based on the new input value. Here's a general approach:

  1. Define the Input Component: Use the Input component with an onChange event handler.
  2. Handle Input Changes: Implement a function that processes the input value and updates the necessary state or triggers other actions.

Here's a simplified example:

<Input
  value={inputValue}
  type="text"
  name="inputName"
  onChange={(event) => handleInputChange(event.target.value)}
/>

function handleInputChange(value) {
  // Update state or perform actions based on the input value
  setInputValue(value);
  // Additional logic to handle the input change
}

In this example, handleInputChange is called whenever the input value changes, allowing you to update the state or perform other actions dynamically.

To continue talking to Dosu, mention @dosu.


Help Dosu learn! Give it feedback: Great Response | Irrelevant Answer | Incorrect Sources | Too Verbose | Hallucination | Other

@themightywolfie
Copy link
Author

themightywolfie commented Jan 21, 2025

@dosu I created a custom component in Langflow for Qdrant Vector Store. Based on your previous comment to implement something similar to the LangWatchComponent, I used the update_build_config method. Here is the code

from langchain.embeddings.base import Embeddings
from langchain_community.vectorstores import Qdrant

from langflow.base.vectorstores.model import LCVectorStoreComponent, check_cached_vector_store
from langflow.helpers.data import docs_to_data
from langflow.io import (
    DataInput,
    DropdownInput,
    HandleInput,
    IntInput,
    MultilineInput,
    SecretStrInput,
    StrInput,
    Output
)
from langflow.schema import Data, Message
from langflow.schema.dotdict import dotdict

class QdrantVectorStoreComponent(LCVectorStoreComponent):
    display_name = "Qdrant"
    description = "Qdrant Vector Store with search capabilities"
    documentation = "https://python.langchain.com/docs/modules/data_connection/vectorstores/integrations/qdrant"
    icon = "Qdrant"

    inputs = [
        # StrInput(name="collection_name", display_name="Collection Name", required=True),
        DropdownInput(
            name="collection_name",
            display_name="Collection",
            options=[""],
            required=True,
            # dynamic=True,
            info="Select a collection",
            refresh_button=True,
            real_time_refresh=True,
        ),
        StrInput(name="host", display_name="Host", value="localhost", advanced=True),
        IntInput(name="port", display_name="Port", value=6333, advanced=True),
        IntInput(name="grpc_port", display_name="gRPC Port", value=6334, advanced=True),
        SecretStrInput(name="api_key", display_name="API Key", advanced=True),
        StrInput(name="prefix", display_name="Prefix", advanced=True),
        IntInput(name="timeout", display_name="Timeout", advanced=True),
        StrInput(name="path", display_name="Path", advanced=True),
        StrInput(name="url", display_name="URL", advanced=True),
        DropdownInput(
            name="distance_func",
            display_name="Distance Function",
            options=["Cosine", "Euclidean", "Dot Product"],
            value="Cosine",
            advanced=True,
        ),
        StrInput(name="content_payload_key", display_name="Content Payload Key", value="page_content", advanced=True),
        StrInput(name="metadata_payload_key", display_name="Metadata Payload Key", value="metadata", advanced=True),
        MultilineInput(name="search_query", display_name="Search Query"),
        DataInput(
            name="ingest_data",
            display_name="Ingest Data",
            is_list=True,
        ),
        HandleInput(name="embedding", display_name="Embedding", input_types=["Embeddings"]),
        IntInput(
            name="number_of_results",
            display_name="Number of Results",
            info="Number of results to return.",
            value=4,
            advanced=True,
        ),
    ]
    
    outputs = [
            Output(name="collections", display_name="collections", method="get_collections"),
        ]

    def update_build_config(self, build_config, field_value, field_name = None):
        # return super().update_build_config(build_config, field_value, field_name)
        from qdrant_client import QdrantClient
        if field_name == "collection_name":
            collections_list = []
            collections = QdrantClient(host=self.host, port=self.port, timeout=30).get_collections()
            for collection in collections:
                for c in list(collection[1]):
                    collections_list.append(c.name)
            # collections = [collection.name for collection in 
            build_config["collection_name"]["options"] = collections_list
            build_config["collection_name"]["value"] = collections_list[0]
        return build_config

For the collection_name dropdown, when I click on the refresh button. I get the error "Variable not found"

Image

Copy link

dosubot bot commented Jan 21, 2025

The error "Variable not found" likely occurs because the update_build_config method is trying to access instance variables like self.host and self.port, which are not defined within the method's scope. To resolve this, you need to ensure that these variables are correctly passed to the method or accessed from the build_config.

Here's how you can modify the update_build_config method to use the values from the build_config:

def update_build_config(self, build_config, field_value, field_name=None):
    from qdrant_client import QdrantClient

    if field_name == "collection_name":
        # Retrieve host and port from the build_config
        host = build_config.get("host", {}).get("value", "localhost")
        port = build_config.get("port", {}).get("value", 6333)

        try:
            # Initialize QdrantClient with the retrieved host and port
            client = QdrantClient(host=host, port=port, timeout=30)
            collections = client.get_collections()

            # Extract collection names
            collections_list = [collection.name for collection in collections]
            
            # Update the build_config with the collection names
            build_config["collection_name"]["options"] = collections_list
            if collections_list:
                build_config["collection_name"]["value"] = collections_list[0]
            else:
                build_config["collection_name"]["value"] = ""
        except Exception as e:
            # Handle exceptions and log errors
            logger.exception(f"Error fetching collections: {e}")
            build_config["collection_name"]["options"] = ["Error fetching collections"]
            build_config["collection_name"]["value"] = ""

    return build_config

This code retrieves the host and port values from the build_config and uses them to initialize the QdrantClient. It also includes error handling to manage any exceptions that occur during the fetching of collections. This should help resolve the "Variable not found" error and ensure that the dropdown is updated correctly when the refresh button is clicked.

To continue talking to Dosu, mention @dosu.


Help Dosu learn! Give it feedback: Great Response | Irrelevant Answer | Incorrect Sources | Too Verbose | Hallucination | Other

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
question Further information is requested
Projects
None yet
Development

No branches or pull requests

1 participant