langflow icon indicating copy to clipboard operation
langflow copied to clipboard

How to create a custom component from backend and deploy and enable for all user

Open yashugupta786 opened this issue 6 months ago • 3 comments

Bug Description

Lets suppose i have api which takes text and output text. This will be a post request in python. I have a function for that .

import requests

def call_custom_api(api_url: str, input_text: str, headers: dict = None, timeout: int = 10) -> str: """ Sends a POST request with plain text to the given API URL and returns the plain text response.

Args:
    api_url (str): The endpoint URL to send the POST request to.
    input_text (str): The text to send in the POST request body.
    headers (dict, optional): Headers to include in the request (e.g., authentication, content-type).
    timeout (int): Timeout for the request in seconds.

Returns:
    str: The plain text response from the API.
"""
try:
    # Set default headers if not provided
    if headers is None:
        headers = {'Content-Type': 'text/plain'}

    response = requests.post(api_url, data=input_text.encode('utf-8'), headers=headers, timeout=timeout)

    response.raise_for_status()  # Raises HTTPError if response code is 4xx or 5xx
    return response.text
except requests.exceptions.RequestException as e:
    print(f"Error calling API: {e}")
    return ""

now i want to add this component in langflow from backend with all description and everything and And when user login to langflow it will be available for all the user. and they can use that component and build some flow on top of it by connecting to other component s. How to achieve it , where to add this code and what changes we need to make and deploy using docker and build the source . and how to show in frontend . I need end to end support for this

Reproduction

Creating custom component from backend and enable for all user

Expected behavior

Creating custom component from backend and enable for all user

Who can help?

No response

Operating System

docker

Langflow Version

latest

Python Version

3.12

Screenshot

No response

Flow File

No response

yashugupta786 avatar May 29 '25 17:05 yashugupta786

To create a custom backend component in Langflow that calls your API, make it available to all users, and deploy it with Docker, follow these steps:


1. Write Your Custom Component

Create a Python file for your component (e.g., custom_api_component.py). Place it in a subfolder (like helpers/) under a custom_components directory. Your component should inherit from CustomComponent and implement a build method. Here’s a template based on your function:

from langflow import CustomComponent
from langflow.schema import Message

import requests

class CustomAPIComponent(CustomComponent):
    display_name = "Custom API"
    description = "Calls a custom API with text input and returns the response."
    icon = "🔗"
    name = "custom_api_component"

    def build_config(self):
        return {
            "api_url": {
                "display_name": "API URL",
                "field_type": "str",
                "required": True,
            },
            "headers": {
                "display_name": "Headers (JSON)",
                "field_type": "str",
                "required": False,
                "info": "Optional JSON string for headers",
            },
            "timeout": {
                "display_name": "Timeout (seconds)",
                "field_type": "int",
                "required": False,
                "value": 10,
            },
        }

    def build(self, api_url: str, input_text: str, headers: str = None, timeout: int = 10) -> Message:
        try:
            hdrs = {'Content-Type': 'text/plain'}
            if headers:
                import json
                hdrs.update(json.loads(headers))
            response = requests.post(api_url, data=input_text.encode('utf-8'), headers=hdrs, timeout=timeout)
            response.raise_for_status()
            return Message(text=response.text)
        except Exception as e:
            return Message(text=f"Error calling API: {e}")

2. Organize the Directory

Your directory structure should look like:

custom_components/
  helpers/
    custom_api_component.py

The subfolder (e.g., helpers) will appear as a category in the Langflow UI sidebar [1].


3. Docker Integration

Option A: Build a Custom Docker Image

  • Add your custom_components folder to your project.
  • Modify your Dockerfile to copy this folder and set the environment variable:
FROM langflowai/langflow:latest
WORKDIR /app
COPY custom_components /app/custom_components
ENV LANGFLOW_COMPONENTS_PATH=/app/custom_components
  • If your component has extra dependencies (like requests), add them to your pyproject.toml and ensure they are installed in the Docker build [2].

Option B: Mount as a Volume

If you want to update components without rebuilding the image, mount the folder:

docker run -d -p 7860:7860 -v /path/to/local/custom_components:/app/custom_components langflowai/langflow:latest

4. Deploy and Verify

  • Build and run your Docker container as above.
  • When Langflow starts, your custom component will appear in the sidebar under the category you chose (e.g., "Helpers").
  • All users will see and be able to use this component in their flows [3].

5. Frontend Display

No extra frontend code is needed if you follow the structure above. Langflow automatically loads and displays custom components in the sidebar for all users. If you want to customize the icon for your category, you can edit the categoryIcons object in src/frontend/src/utils/styleUtils.ts [4].


References:

If you need to support more advanced UI or configuration, you can further customize the build_config method or refer to the contributing guide.

Let me know if you need a more detailed example or run into any issues!

To reply, just mention my name @dosu.


How did I do? Good | Irrelevant | Incorrect | Verbose | Hallucination | Report 🐛 | Other  Join Discord Share on X

dosubot[bot] avatar May 29 '25 17:05 dosubot[bot]

@dosu

this is the test code i have create it is failing

from langflow import CustomComponent from langflow.io import Output, TextInput from langflow.schema import Message import random

class RandomTextAppender(CustomComponent): display_name: str = "Random Text Appender" description: str = "Appends a random phrase to the input text or message." icon: str = "🧩" name: str = "RandomTextAppender"

inputs = [
    TextInput(
        name="input_value",
        display_name="Input Text",
        info="Text or Message to append a random phrase to.",
    )
]

outputs = [
    Output(
        name="output_value",
        display_name="Appended Message",
        method="message_response",
    )
]

def message_response(self) -> Message:
    input_value = self.input_value

    if isinstance(input_value, Message):
        base_text = input_value.get_text()
    else:
        base_text = str(input_value)

    suffix = random.choice([
        " – and that's a fact!",
        " – believe it or not.",
        " – more to come soon.",
        " – how interesting!",
        " – just the beginning!",
    ])
    return Message(text=base_text + suffix)

This is my docker compose

services: langflow-app: build: context: . dockerfile: docker/build_and_push_with_extras.Dockerfile image: langflow-app container_name: langflow-app ports: - "7860:7860" environment: - LANGFLOW_DATABASE_URL=postgresql://langflow:langflow@postgres:5432/langflow - LANGFLOW_SUPERUSER=admin - LANGFLOW_SUPERUSER_PASSWORD=adminpassword - LANGFLOW_SECRET_KEY=23648756921 - LANGFLOW_AUTO_LOGIN=false - LANGFLOW_HOST=0.0.0.0 - LANGFLOW_PORT=7860

  # ✅ Add this line to load custom components
  - LANGFLOW_COMPONENTS_PATH=/app/custom_components

volumes:
  - ./data:/app/data            # For persistent data
  - ./custom_components:/app/custom_components  # ✅ Mount custom components for hot reloads (optional in dev)

depends_on:
  - postgres

postgres: image: postgres:16 container_name: postgres environment: - POSTGRES_DB=langflow - POSTGRES_USER=langflow - POSTGRES_PASSWORD=langflow volumes: - ./pgdata:/var/lib/postgresql/data ports: - "5432:5432"

and this is my build_and_push_with_extras.Dockerfile

syntax=docker/dockerfile:1

Keep this syntax directive! It's used to enable Docker BuildKit

################################

BUILDER-BASE

################################ FROM ghcr.io/astral-sh/uv:python3.12-bookworm-slim AS builder

WORKDIR /app

ENV UV_COMPILE_BYTECODE=1 ENV UV_LINK_MODE=copy

RUN apt-get update
&& apt-get upgrade -y
&& apt-get install --no-install-recommends -y
build-essential
git
npm
gcc
&& apt-get clean
&& rm -rf /var/lib/apt/lists/*

RUN --mount=type=cache,target=/root/.cache/uv
--mount=type=bind,source=uv.lock,target=uv.lock
--mount=type=bind,source=README.md,target=README.md
--mount=type=bind,source=pyproject.toml,target=pyproject.toml
--mount=type=bind,source=src/backend/base/README.md,target=src/backend/base/README.md
--mount=type=bind,source=src/backend/base/uv.lock,target=src/backend/base/uv.lock
--mount=type=bind,source=src/backend/base/pyproject.toml,target=src/backend/base/pyproject.toml
uv sync --frozen --no-install-project --no-editable --extra deploy --extra couchbase --extra cassio --extra local --extra clickhouse-connect --extra nv-ingest --extra postgresql

COPY ./src /app/src

COPY src/frontend /tmp/src/frontend WORKDIR /tmp/src/frontend RUN --mount=type=cache,target=/root/.npm
npm ci
&& NODE_OPTIONS="--max-old-space-size=4096" npm run build
&& cp -r build /app/src/backend/langflow/frontend
&& rm -rf /tmp/src/frontend

WORKDIR /app COPY ./pyproject.toml /app/pyproject.toml COPY ./uv.lock /app/uv.lock COPY ./README.md /app/README.md

👇 Add this to include your custom components

COPY ./custom_components /app/custom_components

RUN --mount=type=cache,target=/root/.cache/uv
uv sync --frozen --no-editable --extra deploy --extra couchbase --extra cassio --extra local --extra clickhouse-connect --extra nv-ingest --extra postgresql

################################

RUNTIME

################################ FROM python:3.12.3-slim AS runtime

RUN apt-get update
&& apt-get upgrade -y
&& apt-get install -y curl git libpq5 gnupg
&& curl -fsSL https://deb.nodesource.com/setup_18.x | bash -
&& apt-get install -y nodejs
&& apt-get clean
&& rm -rf /var/lib/apt/lists/*
&& useradd user -u 1000 -g 0 --no-create-home --home-dir /app/data

COPY --from=builder --chown=1000 /app/.venv /app/.venv

👇 Also copy the custom components and set environment variable

COPY --from=builder --chown=1000 /app/custom_components /app/custom_components ENV LANGFLOW_COMPONENTS_PATH=/app/custom_components

ENV PATH="/app/.venv/bin:$PATH"

LABEL org.opencontainers.image.title=langflow LABEL org.opencontainers.image.authors=['Langflow'] LABEL org.opencontainers.image.licenses=MIT LABEL org.opencontainers.image.url=https://github.com/langflow-ai/langflow LABEL org.opencontainers.image.source=https://github.com/langflow-ai/langflow

USER user WORKDIR /app

ENV LANGFLOW_HOST=0.0.0.0 ENV LANGFLOW_PORT=7860

CMD ["langflow", "run"]

Why it is not working and failing

yashugupta786 avatar May 29 '25 19:05 yashugupta786

Your custom component is likely failing because it does not implement the required build method. In Langflow, every custom component must inherit from CustomComponent and define a build method, which is the entry point for the component's execution and field definitions. The message_response method alone is not sufficient—Langflow expects a build method to exist and match the component's outputs and inputs [docs].

Here’s how you can fix your component:

from langflow import CustomComponent
from langflow.io import Output, TextInput
from langflow.schema import Message
import random

class RandomTextAppender(CustomComponent):
    display_name: str = "Random Text Appender"
    description: str = "Appends a random phrase to the input text or message."
    icon: str = "🧩"
    name: str = "RandomTextAppender"

    inputs = [
        TextInput(
            name="input_value",
            display_name="Input Text",
            info="Text or Message to append a random phrase to.",
        )
    ]

    outputs = [
        Output(
            name="output_value",
            display_name="Appended Message",
            method="build",  # This should match the method name below
        )
    ]

    def build(self, input_value) -> Message:
        if isinstance(input_value, Message):
            base_text = input_value.get_text()
        else:
            base_text = str(input_value)

        suffix = random.choice([
            " – and that's a fact!",
            " – believe it or not.",
            " – more to come soon.",
            " – how interesting!",
            " – just the beginning!",
        ])
        return Message(text=base_text + suffix)

Key points:

  • The output’s method should be "build" to match the method name.
  • The build method must accept the input(s) as arguments.
  • Remove the message_response method unless you want to define multiple outputs.

Docker and directory setup:
Your Docker and volume setup looks correct. Just ensure your custom component is in a subfolder (e.g., custom_components/helpers/random_text_appender.py) and that LANGFLOW_COMPONENTS_PATH points to /app/custom_components [docs].

If it still fails:
Check the langflow.log file or Docker logs for error messages about component loading. Langflow will skip components with errors and log the reason [docs].

If you share the specific error message from the logs, I can help you further debug the issue.

To reply, just mention my name @dosu.


How did I do? Good | Irrelevant | Incorrect | Verbose | Hallucination | Report 🐛 | Other  Join Discord Share on X

dosubot[bot] avatar May 29 '25 19:05 dosubot[bot]

@dosu I am not sure why custom component is not working , am i not building it from source thats why or what . I want to give you my docker file and compose.yml . And let me know what change i should do so that things can work in canvas it can wire and also i want to make changes in frontend . The idea is to build from source and add my components custom to langflow in python

My docker file is as build_and_push_with_extras.Dockerfile

syntax=docker/dockerfile:1

################################

BUILDER-BASE

################################ FROM ghcr.io/astral-sh/uv:python3.12-bookworm-slim AS builder

WORKDIR /app

ENV UV_COMPILE_BYTECODE=1 ENV UV_LINK_MODE=copy

Install system dependencies

RUN apt-get update
&& apt-get upgrade -y
&& apt-get install --no-install-recommends -y
build-essential
git
npm
gcc
&& apt-get clean
&& rm -rf /var/lib/apt/lists/*

Install backend dependencies from lock files

RUN --mount=type=cache,target=/root/.cache/uv
--mount=type=bind,source=uv.lock,target=uv.lock
--mount=type=bind,source=README.md,target=README.md
--mount=type=bind,source=pyproject.toml,target=pyproject.toml
--mount=type=bind,source=src/backend/base/README.md,target=src/backend/base/README.md
--mount=type=bind,source=src/backend/base/uv.lock,target=src/backend/base/uv.lock
--mount=type=bind,source=src/backend/base/pyproject.toml,target=src/backend/base/pyproject.toml
uv sync --frozen --no-install-project --no-editable
--extra deploy --extra couchbase --extra cassio --extra local --extra clickhouse-connect --extra nv-ingest --extra postgresql

Copy everything (source, UI, components)

COPY ./src /app/src

COPY src/frontend /tmp/src/frontend WORKDIR /tmp/src/frontend

Build frontend

RUN --mount=type=cache,target=/root/.npm
npm ci
&& NODE_OPTIONS="--max-old-space-size=4096" npm run build
&& cp -r build /app/src/backend/langflow/frontend
&& rm -rf /tmp/src/frontend

Install backend from your current source

WORKDIR /app

COPY ./pyproject.toml /app/pyproject.toml COPY ./uv.lock /app/uv.lock COPY ./README.md /app/README.md COPY ./custom_components /app/custom_components

RUN --mount=type=cache,target=/root/.cache/uv
uv sync --frozen --no-editable --extra deploy --extra couchbase --extra cassio --extra local --extra clickhouse-connect --extra nv-ingest --extra postgresql

################################

RUNTIME

################################ FROM python:3.12.3-slim AS runtime

RUN apt-get update
&& apt-get upgrade -y
&& apt-get install -y curl git libpq5 gnupg
&& curl -fsSL https://deb.nodesource.com/setup_18.x | bash -
&& apt-get install -y nodejs
&& apt-get clean
&& rm -rf /var/lib/apt/lists/*
&& useradd user -u 1000 -g 0 --no-create-home --home-dir /app/data

COPY --from=builder --chown=1000 /app/.venv /app/.venv COPY --from=builder --chown=1000 /app/custom_components /app/custom_components

ENV LANGFLOW_COMPONENTS_PATH=/app/custom_components

ENV PATH="/app/.venv/bin:$PATH"

LABEL org.opencontainers.image.title=langflow LABEL org.opencontainers.image.authors=['Langflow'] LABEL org.opencontainers.image.licenses=MIT LABEL org.opencontainers.image.url=https://github.com/langflow-ai/langflow LABEL org.opencontainers.image.source=https://github.com/langflow-ai/langflow

USER user WORKDIR /app

ENV LANGFLOW_HOST=0.0.0.0 ENV LANGFLOW_PORT=7860

CMD ["langflow", "run"]

Also Attaching the docker compose yaml , which is in parent directory services: langflow-app: build: context: . dockerfile: docker/build_and_push_with_extras.Dockerfile image: langflow-app container_name: langflow-app ports: - "7860:7860" environment: - LANGFLOW_DATABASE_URL=postgresql://langflow:langflow@postgres:5432/langflow - LANGFLOW_SUPERUSER=admin - LANGFLOW_SUPERUSER_PASSWORD=adminpassword - LANGFLOW_SECRET_KEY=23648756921 - LANGFLOW_AUTO_LOGIN=false - LANGFLOW_HOST=0.0.0.0 - LANGFLOW_PORT=7860 - LANGFLOW_COMPONENTS_PATH=/app/custom_components volumes: - ./data:/app/data - ./custom_components:/app/custom_components depends_on: - postgres

postgres: image: postgres:16 container_name: postgres environment: - POSTGRES_DB=langflow - POSTGRES_USER=langflow - POSTGRES_PASSWORD=langflow volumes: - ./pgdata:/var/lib/postgresql/data ports: - "5432:5432"

Also sharing the custom component directory structure and Code

He is the directory structure

Image

#------------- also sharing the code i have written for custom compnent i just want to wire it on canvas and get output something like that

from langflow import CustomComponent from langflow.inputs import HandleInput from langflow.io import Output from langflow.schema.message import Message import random

class RandomTextAppender(CustomComponent): display_name: str = "Random Text Appender" description: str = "Appends a random phrase to text or message from any upstream component." icon = "🧩" name = "RandomTextAppender"

inputs = [
    HandleInput(
        name="input_value",
        display_name="Text or Message",
        input_types=["Message", "str"],
        required=True,
    )
]

outputs = [
    Output(
        name="output_value",
        display_name="Modified Message",
        method="build_results",
    )
]

async def build_results(self) -> Message:
    print("⚠️ DEBUG: Entering build_results")
    print("⚠️ DEBUG: Component attributes:", dir(self))

    try:
        input_value = self.get_input("input_value")
        print("⚠️ DEBUG: input_value =", input_value)
    except Exception as e:
        print("❌ ERROR getting input_value:", str(e))
        raise ValueError(f"[RandomTextAppender] Failed to get input: {e}")

    # Convert to string if needed
    base_text = input_value.text if isinstance(input_value, Message) else str(input_value)

    # Add random suffix
    suffix = random.choice([
        " – and that's a fact!",
        " – more to come soon.",
        " – believe it or not.",
        " – stay tuned!",
    ])

    final_text = base_text + suffix
    print("✅ DEBUG: Final message:", final_text)

    return Message(text=final_text)

what do you suggest am i not building the docker of lanflow from source , if so give me the complete docker file and docker compose to solve this problem

yashugupta786 avatar May 30 '25 09:05 yashugupta786

@yashugupta786 You're on the right track, as the easiest way is to use LANGFLOW_COMPONENTS_PATH.

This parameter was bugged between versions 1.5.0 and 1.5.0post2, but it's been fixed in main and future releases.

The structure within the folder should follow this configuration: https://docs.langflow.org/components-custom-components#directory-structure-requirements

Empreiteiro avatar Sep 24 '25 20:09 Empreiteiro