- Home
- Skills
- Agents & Orchestration
- Building Langflow Components — Safe, Tested, Forever-Named
Works with the AI Agents you already use
Building Langflow Components — Safe, Tested, Forever-Named
Ship Langflow Components safely: permanent names, typed inputs/outputs, AI runtime rules, ComponentTestBase tests.
Free
Building Langflow Components — Safe, Tested, Forever-Named
Example session with this skill installed
In our Langflow checkout we already maintain the acme bundle (src/bundles/acme/, package lfx_acme, has AcmeChatModelComponent). Add a component for Acme's moderation API so flows can check text before sending it to a model.
API (from Acme's docs):
POST https://moderation.acme.example/v1/check
Authorization: Bearer <api key>
Body: { "text": "<string>" }
200: { "flagged": true, "categories": ["harassment"] }
401 bad key · 429 rate limited · 5xx server error
- Read your context and instructions
- Compiled the building langflow components
Summary. This is a niche partner integration, and the acme bundle already exists. So the component goes into that bundle and needs no new bundle or RFC. The class name AcmeModerationComponent and the input and output names below are permanent: please confirm them before this merges. The component calls an AI service, so it has an explicit timeout, maps HTTP failures to clear messages on the node, and never logs the text or the key.
Placement and names (decide once)
File: src/bundles/acme/src/lfx_acme/components/acme/acme_moderation.py. Before creating it, check that no existing Acme component already wraps /v1/check.
- Class:
AcmeModerationComponent, following<Provider><Capability>Component. - Inputs:
api_key,input_value,timeout_seconds. Output:moderation_result.
Icon: "shield-check" (Lucide). If AcmeChatModelComponent uses a custom Acme SVG, use that instead so the bundle stays consistent.
Component
import time
from typing import NoReturn
from langflow.custom import Component
from langflow.io import IntInput, MessageTextInput, Output, SecretStrInput
from langflow.schema import Data
from loguru import logger
MODERATION_URL = "https://moderation.acme.example/v1/check"
STATUS_MESSAGES = {
401: "Acme rejected the API key. Check your secret.",
429: "Acme rate limit reached. Try again later.",
}
def _status_message(status_code: int) -> str:
return STATUS_MESSAGES.get(status_code, f"Acme Moderation failed: HTTP {status_code}")
class AcmeModerationComponent(Component):
display_name = "Acme Moderation"
description = "Check text with Acme's moderation API and return whether it was flagged."
icon = "shield-check"
documentation = "https://moderation.acme.example/docs"
inputs = [
SecretStrInput(
name="api_key",
display_name="API Key",
info="Acme Moderation API key. Stored encrypted.",
required=True,
),
MessageTextInput(name="input_value", display_name="Text", required=True),
IntInput(name="timeout_seconds", display_name="Timeout (s)", value=10, advanced=True),
]
outputs = [
Output(display_name="Moderation Result", name="moderation_result", method="check_text"),
]
def check_text(self) -> Data:
import httpx
start = time.monotonic()
try:
response = httpx.post(
MODERATION_URL,
headers={"Authorization": f"Bearer {self.api_key}"},
json={"text": self.input_value},
timeout=self.timeout_seconds,
)
response.raise_for_status()
except httpx.TimeoutException as exc:
self._fail(f"Acme Moderation timed out after {self.timeout_seconds}s.", "timeout", start, exc)
except httpx.HTTPStatusError as exc:
self._fail(_status_message(exc.response.status_code), "http_error", start, exc)
except httpx.RequestError as exc:
self._fail("Could not reach Acme Moderation.", "unreachable", start, exc)
body = response.json()
logger.info(
"acme.moderation",
outcome="success",
text_chars=len(self.input_value),
duration_ms=int((time.monotonic() - start) * 1000),
)
return Data(data={"flagged": bool(body["flagged"]), "categories": list(body.get("categories", []))})
def _fail(self, message: str, outcome: str, start: float, exc: Exception) -> NoReturn:
logger.warning(
"acme.moderation",
outcome=outcome,
duration_ms=int((time.monotonic() - start) * 1000),
)
self.status = message
raise ValueError(message) from exc
httpx is imported inside the method, so loading the bundle never fails because of it. The logs record the outcome, the text length and the duration. They never record the text, the response or the key. There are no retries: none are implemented by hand, and with no Acme SDK there is nothing to delegate them to. A flow that needs retries can add them around the node.
Registration and changelog
- Add
AcmeModerationComponenttosrc/bundles/acme/src/lfx_acme/components/acme/__init__.py, keeping the alphabetical order. - Add
httpxto the bundle'spyproject.tomlwith a compatible range, unlesslangflow-basealready provides it. - In
src/bundles/acme/BUNDLE_API.md, under## [Unreleased]→### Added:`AcmeModerationComponent`: check text with Acme's moderation API (`/v1/check`).
Tests (src/bundles/acme/tests/unit/components/test_acme_moderation.py)
import os
import httpx
import pytest
from langflow.schema import Data
from lfx_acme.components.acme.acme_moderation import AcmeModerationComponent
from tests.base import ComponentTestBaseWithoutClient
class TestAcmeModerationComponent(ComponentTestBaseWithoutClient):
@pytest.fixture
def component_class(self):
return AcmeModerationComponent
@pytest.fixture
def default_kwargs(self):
return {"api_key": "test-key", "input_value": "Hello there", "timeout_seconds": 10}
@pytest.fixture
def file_names_mapping(self):
return {}
@pytest.mark.api_key_required
def test_should_return_moderation_data_when_called_against_real_api(self, component_class, default_kwargs):
default_kwargs["api_key"] = os.environ["ACME_MODERATION_API_KEY"]
result = component_class(**default_kwargs).check_text()
assert isinstance(result, Data)
assert isinstance(result.data["flagged"], bool)
def test_should_raise_value_error_when_request_times_out(self, component_class, default_kwargs, mocker):
mocker.patch("httpx.post", side_effect=httpx.ReadTimeout("slow"))
component = component_class(**default_kwargs)
with pytest.raises(ValueError, match="timed out after 10s"):
component.check_text()
assert "timed out" in component.status
def test_should_report_rate_limit_when_api_returns_429(self, component_class, default_kwargs, mocker):
request = httpx.Request("POST", "https://moderation.acme.example/v1/check")
mocker.patch("httpx.post", return_value=httpx.Response(429, request=request))
component = component_class(**default_kwargs)
with pytest.raises(ValueError, match="rate limit"):
component.check_text()
The real API test is the main check. Mocks cover only the timeout and the 429, which a sandbox can't reliably reproduce, as Langflow's testing policy recommends. The base class's automatic tests (instantiation, inputs, outputs) run from the three fixtures.
Commands to run (not executed here)
LFX_DEV=acme make backend # the component should appear in the sidebar; drag it onto a flow
uv sync --group dev --package lfx-acme
uv run pytest src/bundles/acme/tests/ -v
make format_backend && uv run git commit
The expected outcome: the two mocked tests pass, and the api_key_required test is skipped unless ACME_MODERATION_API_KEY is set. None of this has been run yet.
Next steps
- Confirm the class, input and output names. After the merge they can't change.
- Run the commands above. Check that the node renders and that a 401 shows "Acme rejected the API key" on the node.
- Learning capture: nothing new beyond SKILL.md and AGENTS.md came up, so no
learnings/entry was written.
Connects securely to your tools. The creator never sees your data.
About this skill
Building Langflow Components is a workflow skill for creating and shipping custom Components for Langflow, the visual builder for LLM flows. A Component is a Python class that appears as a node in the flow editor. Its class name and its input and output names are stored in every saved flow, so a careless rename breaks other people's work without any warning.
Problems it solves
- Renamed classes or inputs that delete a component from every saved flow.
MessageTextInputused for API keys, so they are stored in clear text.- SDK clients created at import time, which crash the whole backend.
- LLM calls without a timeout that hang a flow.
- Raw prompts and keys in logs.
- Niche components placed in
langflow-base. - Bundle changes that fail the
BUNDLE_API.mdchangelog check.
The 12-step workflow. Each step ends with a check you can verify:
- Decide between bundle and base.
- Choose a class name you can keep forever (
<Provider><Capability>Component). - Pick the icon.
- Define
inputsandoutputswith permanent names. - Implement the output methods, importing the SDK inside the method.
- Apply the AI runtime rules (explicit timeout, mapped errors, redaction).
- Hot-reload with
LFX_DEV. - Test with
ComponentTestBaseWith[out]Clientand its three required fixtures. - Prefer real integrations marked
api_key_required. - Register the component.
- Commit with
uv run git commit. - Record a learning.
References
component-anatomy.md: tool, Data, DataFrame and multi-output shapes, plus deprecating without breaking flows.io-reference.md: every input and output type.testing.md: base classes, fixtures, marks, real integrations vs mocks.ai-component-patterns.md: timeouts, retries, error mapping, streaming, token accounting, redaction.icons.md: wiring a custom SVG icon.bundles.md: bundle layout and theBUNDLE_API.mdchangelog check.
When it triggers. "Create a component", "add an OpenAI/Anthropic/Chroma integration", "wrap this LangChain class as a Component".
This is a community skill, not affiliated with or endorsed by the Langflow project. Free and MIT-licensed. Part of the claude-skills-czl collection.
Source on GitHub: this skill · full repository
How to use
Skills on Agensi run inside your AI agent. Copy the prompt, paste it into your agent, and it connects to Agensi and installs this skill for you. If a payment is needed, your agent shows it to you first.
Reviews
No reviews yet
Be one of the first to try it. Every listed skill passes our trust checks below.
Security scanned
Passed our 8-point scan before listing
Fresh listing
Recently published to Agensi
Free forever
No account required to browse
Trust & safety
Security scanned
Verified clean 11 days ago
- Free to download with an account