From e8f4dba133376085e7c2338c4d8e3ae3c42e05f2 Mon Sep 17 00:00:00 2001 From: Mendon Kissling <59585235+mendonk@users.noreply.github.com> Date: Tue, 20 May 2025 13:21:35 -0400 Subject: [PATCH] docs: contribute custom components refresh (#7971) * rewrite-to-use-data-component * code-font * cleanup * update-build-behavior * comment-out-for-build * icon-is-str * repeat-deprecation-info * code-review * Apply suggestions from code review Co-authored-by: KimberlyFields <46325568+KimberlyFields@users.noreply.github.com> * add-anchor-link --------- Co-authored-by: Edwin Jose Co-authored-by: KimberlyFields <46325568+KimberlyFields@users.noreply.github.com> --- .../contributing-component-tests.md | 2 +- .../Contributing/contributing-components.md | 178 ++++++++++++++++-- 2 files changed, 166 insertions(+), 14 deletions(-) diff --git a/docs/docs/Contributing/contributing-component-tests.md b/docs/docs/Contributing/contributing-component-tests.md index d3f90e199..eb6b0236f 100644 --- a/docs/docs/Contributing/contributing-component-tests.md +++ b/docs/docs/Contributing/contributing-component-tests.md @@ -23,7 +23,7 @@ For example, if the component being tested is `PromptComponent`, then the test c ## Imports, inheritance, and mandatory methods -To standardize comoponent tests, base test classes have been created and should be imported and inherited by all component test classes. These base classes are located in the file `src/backend/tests/unit/base.py`. +To standardize component tests, base test classes have been created and should be imported and inherited by all component test classes. These base classes are located in the file `src/backend/tests/unit/base.py`. To import the base test classes: diff --git a/docs/docs/Contributing/contributing-components.md b/docs/docs/Contributing/contributing-components.md index 138d52b94..368d7698b 100644 --- a/docs/docs/Contributing/contributing-components.md +++ b/docs/docs/Contributing/contributing-components.md @@ -4,20 +4,172 @@ slug: /contributing-components --- -New components are added as objects of the [CustomComponent](https://github.com/langflow-ai/langflow/blob/dev/src/backend/base/langflow/custom/custom_component/custom_component.py) class. +New components are added as objects of the [Component](https://github.com/langflow-ai/langflow/blob/main/src/backend/base/langflow/custom/custom_component/component.py) class. -Any dependencies are added to the [pyproject.toml](https://github.com/langflow-ai/langflow/blob/main/pyproject.toml#L148) file. +Dependencies are added to the [pyproject.toml](https://github.com/langflow-ai/langflow/blob/main/pyproject.toml#L148) file. -### Contribute an example component to Langflow +## Contribute an example component to Langflow -Anyone can contribute an example component. For example, if you created a new document loader called **MyCustomDocumentLoader**, you can follow these steps to contribute it to Langflow. +Anyone can contribute an example component. For example, to create a new **Data** component called **DataFrame processor**, follow these steps to contribute it to Langflow. -1. Write your loader as an object of the [CustomComponent](https://github.com/langflow-ai/langflow/blob/dev/src/backend/base/langflow/custom/custom_component/custom_component.py) class. You'll create a new class, `MyCustomDocumentLoader`, that will inherit from `CustomComponent` and override the base class's methods. -2. Define optional attributes like `display_name`, `description`, and `documentation` to provide information about your custom component. -3. Implement the `build_config` method to define the configuration options for your custom component. -4. Implement the `build` method to define the logic for taking input parameters specified in the `build_config` method and returning the desired output. -5. Add the code to the [/components/documentloaders](https://github.com/langflow-ai/langflow/tree/dev/src/backend/base/langflow/components) folder. -6. Add the dependency to [/documentloaders/__init__.py](https://github.com/langflow-ai/langflow/blob/dev/src/backend/base/langflow/components/documentloaders/__init__.py) as `from .MyCustomDocumentLoader import MyCustomDocumentLoader`. -7. Add any new dependencies to the [pyproject.toml](https://github.com/langflow-ai/langflow/blob/main/pyproject.toml#L148) file. -8. Submit documentation for your component. For this example, you'd submit documentation to the [loaders page](https://github.com/langflow-ai/langflow/blob/main/docs/docs/Components/components-loaders.md). -9. Submit your changes as a pull request. The Langflow team will have a look, suggest changes, and add your component to Langflow. \ No newline at end of file +1. Create a Python file called `dataframe_processor.py`. +2. Write your processor as an object of the [Component](https://github.com/langflow-ai/langflow/blob/main/src/backend/base/langflow/custom/custom_component/component.py) class. You'll create a new class, `DataFrameProcessor`, that will inherit from `Component` and override the base class's methods. + +```python +from typing import Any, Dict, Optional +import pandas as pd +from langflow.custom import Component + +class DataFrameProcessor(Component): + """A component that processes pandas DataFrames with various operations.""" +``` + +3. Define class attributes to provide information about your custom component: +```python +from typing import Any, Dict, Optional +import pandas as pd +from langflow.custom import Component + +class DataFrameProcessor(Component): + """A component that processes pandas DataFrames with various operations.""" + + display_name: str = "DataFrame Processor" + description: str = "Process and transform pandas DataFrames with various operations like filtering, sorting, and aggregation." + documentation: str = "https://docs.langflow.org/components-dataframe-processor" + icon: str = "DataframeIcon" + priority: int = 100 + name: str = "dataframe_processor" +``` + + * `display_name`: A user-friendly name shown in the UI. + * `description`: A brief description of what your component does. + * `documentation`: A link to detailed documentation. + * `icon`: An emoji or icon identifier for visual representation. + For more information, see [Contributing bundles](/contributing-bundles#add-the-bundle-to-the-frontend-folder). + * `priority`: An optional integer to control display order. Lower numbers appear first. + * `name`: An optional internal identifier that defaults to class name. + +4. Define the component's interface by specifying its inputs, outputs, and the method that will process them. The method name must match the `method` field in your outputs list, as this is how Langflow knows which method to call to generate each output. +This example creates a minimal custom component skeleton. +For more information on creating your custom component, see [Create custom Python components](/components-custom-components). +```python +from typing import Any, Dict, Optional +import pandas as pd +from langflow.custom import Component + +class DataFrameProcessor(Component): + """A component that processes pandas DataFrames with various operations.""" + + display_name: str = "DataFrame Processor" + description: str = "Process and transform pandas DataFrames with various operations like filtering, sorting, and aggregation." + documentation: str = "https://docs.langflow.org/components-dataframe-processor" + icon: str = "DataframeIcon" + priority: int = 100 + name: str = "dataframe_processor" + + # input and output lists + inputs = [] + outputs = [] + + # method + def some_output_method(self): + return ... +``` + +5. Save the `dataframe_processor.py` to the `src > backend > base > langflow > components` directory. +This example adds a **Data** component, so add it to the `/data` directory. + +6. Add the component dependency to `src > backend > base > langflow > components > data > __init__.py` as `from .DataFrameProcessor import DataFrameProcessor`. +You can view the [/data/__init__.py](https://github.com/langflow-ai/langflow/blob/dev/src/backend/base/langflow/components/data/__init__.py) in the Langflow repository. + +7. Add any new dependencies to the [pyproject.toml](https://github.com/langflow-ai/langflow/blob/main/pyproject.toml#L20) file. + +8. Submit documentation for your component. For this example component, you would submit documentation to the [Data components page](https://github.com/langflow-ai/langflow/blob/main/docs/docs/Components/components-data.md). + +9. Submit your changes as a pull request. The Langflow team will review, suggest changes, and add your component to Langflow. + +## Best practices for modifying components + +When creating or updating components, follow these best practices to maintain backward compatibility and ensure a smooth experience for users. + +### Don't rename the class or `name` attribute + +Changing the class name or the `name` attribute breaks the component for all existing users. This happens because the frontend tests the `type` attribute, which is set to the class' name or the `name` attribute. If these names change, the component effectively becomes a new component, and the old component disappears. + +Instead, do the following: +* Change only the display name if the old name is unclear. +* Change only the display name if functionality changes but remains related. +* If a new internal name is necessary, mark the old component as `legacy=true` and create a new component. + +For example: +```python +class MyCustomComponent(BaseComponent): + name = "my_custom_component_internal" + legacy = True +``` + +### Don't remove fields and outputs + +Removing fields or outputs can cause edges to disconnect and change the behavior of components. + +Instead, mark fields as `deprecated` and keep them in the same location. If removal is absolutely necessary, you must define and document a migration plan. Always clearly communicate any changes in the field's information to users. + +### Maintain outdated components as legacy + +When updating components, create them as completely separate entities while maintaining the old component as a legacy version. Always ensure backward compatibility and never remove methods and attributes from base classes, such as `LCModelComponent`. + +### Favor asynchronous methods + +Always favor asynchronous methods and functions in your components. When interacting with files, use `aiofile` and `anyio.Path` for better performance and compatibility. + +### Include tests with your component + +Include tests for your changes using `ComponentTestBase` classes. For more information, see [Contribute component tests](/contributing-component-tests). + +### Documentation + +When documenting changes in pull requests, clearly explain *what* changed, such as display name updates or new fields, *why* it changed, such as improvements or bug fixes, and the *impact* on existing users. + +For example: + +
+Example PR + +```markdown +# Pull request with changes to Notify component + +This pull request updates the Notify component. + +## What changed +- Added new `timeout` field to control how long the component waits for a response. +- Renamed `message` field to `notification_text` for clarity. +- Added support for async operations. +- Deprecated the `retry_count` field in favor of `max_retries`. + +## Why it changed +- `timeout` field addresses user requests for better control over wait times. +- `message` to `notification_text` change makes the field's purpose clearer. +- Async support improves performance in complex flows. +- `retry_count` to `max_retries` aligns with common retry pattern terminology. + +## Impact on users +- New `timeout` field is optional (defaults to 30 seconds). +- Users will see a deprecation warning for `retry_count`. + - Migration: Replace `retry_count` with `max_retries` in existing flows. + - Both fields will work until version 2.0. +- No action needed for async support - it's backward compatible. +``` + +
+ +## Example pull request flow + +1. Create or update a component. +Maintain the class name and `name` attribute if the purpose remains the same. +Otherwise, create a new component and move the old component to `legacy`. +2. Add tests. +Create tests using one of the `ComponentTestBase` classes. +For more information, see [Contribute component tests](/contributing-component-tests). +3. Flag outdated fields and outputs as `deprecated` and keep them in the same location to ensure backward compatibility. +4. Document your changes. +Include migration instructions if breaking changes occur. \ No newline at end of file