langflow icon indicating copy to clipboard operation
langflow copied to clipboard

Issue with custom component

Open yashugupta786 opened this issue 6 months ago • 3 comments

Bug Description

I have created a custom component under just to test

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:
    input_value = await self.get_input_value("input_value")

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

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

    return Message(text=base_text + suffix)

When i am connecting it is giving error as

Image

Error building Component Random Text Appender:

'RandomTextAppender' object has no attribute 'get_input_value'

Traceback (most recent call last): File "/app/.venv/lib/python3.12/site-packages/langflow/graph/vertex/base.py", line 632, in _build_results result = await initialize.loading.get_instance_results( ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/app/.venv/lib/python3.12/site-packages/langflow/interface/initialize/loading.py", line 68, in get_instance_results return await build_component(params=custom_params, custom_component=custom_component) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/app/.venv/lib/python3.12/site-packages/langflow/interface/initialize/loading.py", line 145, in build_component build_results, artifacts = await custom_component.build_results() ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "", line 80, in build_results AttributeError: 'RandomTextAppender' object has no attribute 'get_input_value'

why these error how to fix. I have build from source with docker

Reproduction

Image

Expected behavior

Image

Who can help?

No response

Operating System

docker

Langflow Version

latest main repo

Python Version

3.12

Screenshot

No response

Flow File

No response

yashugupta786 avatar May 29 '25 19:05 yashugupta786

The error occurs because the method get_input_value does not exist on Langflow's CustomComponent class. In Langflow, custom components do not use get_input_value to access their inputs. Instead, input values are automatically assigned as attributes to your component instance, using the input name you defined.

To fix your RandomTextAppender, you should access the input directly as self.input_value inside your build method (or build_results if that's your output method). Here’s how you can update your component:

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:
        input_value = self.input_value  # <-- Access input directly

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

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

        return Message(text=base_text + suffix)

You do not need to implement or call get_input_value; just use self.input_value as shown above. This approach matches how Langflow assigns input values to component instances, as described in the custom component documentation.

If you encounter further issues, make sure your custom component is placed in the correct directory (such as /app/custom_components/helpers/) and that the LANGFLOW_COMPONENTS_PATH environment variable is set appropriately in your Docker setup. Also, ensure there are no name overlaps between your inputs and outputs, and that your output method returns the correct type (in this case, a Message object) [source].

Let me know if you need more help!

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

again i am getting this error Error building Component Random Text Appender:

'RandomTextAppender' object has no attribute 'input_value'

Traceback (most recent call last): File "/app/.venv/lib/python3.12/site-packages/langflow/graph/vertex/base.py", line 632, in _build_results result = await initialize.loading.get_instance_results( ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/app/.venv/lib/python3.12/site-packages/langflow/interface/initialize/loading.py", line 68, in get_instance_results return await build_component(params=custom_params, custom_component=custom_component) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "/app/.venv/lib/python3.12/site-packages/langflow/interface/initialize/loading.py", line 145, in build_component build_results, artifacts = await custom_component.build_results() ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "", line 79, in build_results AttributeError: 'RandomTextAppender' object has no attribute 'input_value'. Did you mean: 'repr_value'?

The code i have used is

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:
    input_value = self.input_value  # ✅ Direct assignment from Langflow

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

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

    return Message(text=base_text + suffix)

and this is the flow

Image

please help i am struggling

yashugupta786 avatar May 29 '25 19:05 yashugupta786

Your code and approach are correct according to the Langflow documentation: inputs defined in the class-level inputs list should be accessible as self.input_value in your method. However, if you are still getting 'RandomTextAppender' object has no attribute 'input_value', this usually means the input is not being set on the instance—often due to a loading or wiring issue in the custom component setup.

Here’s how to resolve and debug this:

  1. Access input using get_input:
    Langflow’s custom component base class provides a get_input method that safely retrieves the input value from the internal dictionary. Replace your input access line with:

    input_value = self.get_input("input_value")
    

    This method will raise a clear error if the input is missing and is the recommended way to access inputs in situations where direct attribute access fails [source].

  2. Directory structure and environment variable:
    Make sure your custom component file is inside a category subfolder (e.g., /app/custom_components/helpers/random_text_appender.py) and not directly in /app/custom_components. The environment variable LANGFLOW_COMPONENTS_PATH must be set to /app/custom_components in your Docker setup. Langflow will not load components placed directly in the base directory—they must be in a subfolder, which determines their category in the UI [source].

  3. Restart Langflow after changes:
    After adjusting the directory structure or environment variable, restart your Langflow Docker container to ensure changes take effect [source].

  4. Debug attributes:
    To debug, you can temporarily add this to your build_results method:

    print("Component attributes:", dir(self))
    

    This will show you what attributes are present on your component instance at runtime.

Example fix:

async def build_results(self) -> Message:
    input_value = self.get_input("input_value")
    base_text = input_value.text if isinstance(input_value, Message) else str(input_value)
    suffix = random.choice([
        " – and that's a fact!",
        " – more to come soon.",
        " – believe it or not.",
        " – stay tuned!",
    ])
    return Message(text=base_text + suffix)

If you follow these steps and still encounter issues, double-check for typos in the input name and ensure there are no name overlaps or reserved attribute names in your component [source].

Let me know if this resolves your issue or if you see a different error!

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 10:05 yashugupta786

@yashugupta786 Here is the code for your component adjusted to Langlow standards (I created the component compatible with 1.6 with lfx inpiort, if you need to use it in an earlier version, change lfx to langflow):

import json
import random
from typing import Optional

from lfx.custom.custom_component.component import Component
from lfx.io import HandleInput, Output
from lfx.schema.message import Message


class RandomTextAppender(Component):
    display_name = "Random Text Appender"
    description = "Extracts inner text from a Message payload (if present) and appends a random phrase."
    icon = "🧩"
    name = "RandomTextAppender"

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

    outputs = [
        Output(display_name="Message Output", name="message_output", method="get_message_output")
    ]

    def _extract_text_from_possible_json(self, s: str, max_depth: int = 3) -> str:
        if not isinstance(s, str) or not s:
            return "" if s is None else str(s)

        text = s.strip()

        start_idx = text.find("{")
        if start_idx == -1:
            return text  

        depth = 0
        end_idx: Optional[int] = None
        for i in range(start_idx, len(text)):
            ch = text[i]
            if ch == "{":
                depth += 1
            elif ch == "}":
                depth -= 1
                if depth == 0:
                    end_idx = i
                    break

        if end_idx is None:
            return text  

        json_sub = text[start_idx : end_idx + 1]

        try:
            parsed = json.loads(json_sub)
        except Exception:
            return text  

        nested = None
        if isinstance(parsed, dict):
            nested = parsed.get("text", None)
            if nested is None:
                nested = parsed.get("content", None)

        if isinstance(nested, str):
            nested_str = nested.strip()
            if max_depth > 0 and (nested_str.startswith("{") or '"text"' in nested_str):
                return self._extract_text_from_possible_json(nested_str, max_depth - 1)
            return nested
        elif nested is not None:
            return str(nested)

        return text

    def get_message_output(self) -> Message:
        input_value = getattr(self, "input_value", None)

        if input_value is None:
            return Message(text="No input provided")

        if isinstance(input_value, Message):
            candidate = getattr(input_value, "text", None) or getattr(input_value, "content", None) or ""
        elif isinstance(input_value, str):
            candidate = input_value
        else:
            candidate = str(input_value)

        extracted = self._extract_text_from_possible_json(candidate)

        base_text = extracted if extracted else candidate

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

        final_text = base_text + suffix

        return Message(text=final_text)

If you need more examples, I have a repository with dozens of components: https://github.com/Empreiteiro/langflow-factory

You can also count on the GPT component maker: https://chatgpt.com/g/g-67ae2e4396a48191a0e292d370ddd3d0-component-maker-agent

And whenever needed, our custom components documentation: https://docs.langflow.org/components-custom-components

Empreiteiro avatar Sep 25 '25 17:09 Empreiteiro