Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
66 changes: 66 additions & 0 deletions docs/docs/Components/bundles-plivo.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
---
title: Plivo
slug: /bundles-plivo
---

import Icon from "@site/src/components/icon";

[Plivo](https://www.plivo.com/?utm_source=github&utm_medium=oss&utm_campaign=langflow-plivo) is a cloud communications platform that provides SMS messaging, voice calling, and phone number lookup APIs.

Your flows can use the **Plivo** bundle components to send SMS messages, place outbound phone calls, and look up phone number details with your Plivo account.

All components authenticate with a Plivo **Auth ID** and **Auth Token**, which you can find in the [Plivo console](https://cx.plivo.com/?utm_source=github&utm_medium=oss&utm_campaign=langflow-plivo).
For more information, see the [Plivo API documentation](https://www.plivo.com/docs/?utm_source=github&utm_medium=oss&utm_campaign=langflow-plivo).

## Plivo Send SMS

The **Plivo Send SMS** component sends an SMS message through the [Plivo Messaging API](https://www.plivo.com/docs/messaging/api/message?utm_source=github&utm_medium=oss&utm_campaign=langflow-plivo).

1. Add the **Plivo Send SMS** component to your flow, and then configure it as follows:

* **Plivo Auth ID** and **Plivo Auth Token**: Enter the credentials for your Plivo account.
* **From Number**: Enter the sender ID. This is a Plivo phone number, short code, or, where permitted, an alphanumeric sender ID.
* **To Number**: Enter the recipient's phone number in E.164 format, such as `+14150000001`.
* **Message**: Enter the text body of the message to send.

2. Connect the component to other components in your flow.
The component can be used as a standalone step in a flow or as a tool for an agent.

To enable **Tool Mode** for this component, change the component's output type from **Output** to **Tool**, and then connect it to the **Tools** port on an **Agent** component.

The component returns Plivo's API response, which includes the message UUID and API request identifier, in Langflow's [`Data` type](/data-types).

## Plivo Make Call

The **Plivo Make Call** component places an outbound phone call through the [Plivo Voice API](https://www.plivo.com/docs/voice/api/call?utm_source=github&utm_medium=oss&utm_campaign=langflow-plivo).

1. Add the **Plivo Make Call** component to your flow, and then configure it as follows:

* **Plivo Auth ID** and **Plivo Auth Token**: Enter the credentials for your Plivo account.
* **From Number**: Enter the caller ID. This is a Plivo phone number in E.164 format.
* **To Number**: Enter the destination phone number in E.164 format, such as `+14150000001`.
* **Answer URL**: Enter a publicly reachable URL that returns Plivo answer XML, such as a `<Speak>` element, when the call is answered. A default public URL is provided, so a basic call needs no additional setup.

2. Connect the component to other components in your flow.
The component can be used as a standalone step in a flow or as a tool for an agent.

To enable **Tool Mode** for this component, change the component's output type from **Output** to **Tool**, and then connect it to the **Tools** port on an **Agent** component.

The component returns Plivo's API response, which includes the call request UUID and API request identifier, in Langflow's [`Data` type](/data-types).

## Plivo Lookup Number

The **Plivo Lookup Number** component looks up carrier and formatting details for a phone number through the [Plivo Lookup API](https://www.plivo.com/docs/lookup/api/number?utm_source=github&utm_medium=oss&utm_campaign=langflow-plivo).

1. Add the **Plivo Lookup Number** component to your flow, and then configure it as follows:

* **Plivo Auth ID** and **Plivo Auth Token**: Enter the credentials for your Plivo account.
* **Phone Number**: Enter the phone number to look up in E.164 format, such as `+14155551234`.
* **Lookup Type**: Choose `carrier` to include the carrier name and line type, or `none` to return country and formatting details only.

2. Connect the component to other components in your flow.
The component can be used as a standalone step in a flow or as a tool for an agent.

To enable **Tool Mode** for this component, change the component's output type from **Output** to **Tool**, and then connect it to the **Tools** port on an **Agent** component.

The component returns Plivo's API response, which includes the number's country, format, and carrier details, in Langflow's [`Data` type](/data-types).
1 change: 1 addition & 0 deletions docs/sidebars.js
Original file line number Diff line number Diff line change
Expand Up @@ -541,6 +541,7 @@ module.exports = {
"Components/bundles-perplexity",
"Components/bundles-pgvector",
"Components/bundles-pinecone",
"Components/bundles-plivo",
"Components/bundles-qdrant",
"Components/bundles-redis",
"Components/bundles-sambanova",
Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -169,7 +169,7 @@ Documentation = "https://docs.langflow.org"

[project.optional-dependencies]
bundles = [
"lfx-bundles[all-no-torch]>=1.1.6,<2.0",
"lfx-bundles[all-no-torch]>=1.1.7,<2.0",
"lfx-arxiv>=0.1.0,<1.0.0",
"lfx-duckduckgo>=0.1.0,<1.0.0",
"lfx-empiriolabs>=0.1.0,<1.0.0",
Expand Down
1 change: 1 addition & 0 deletions scripts/ci/frozen_component_dirs.txt
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@ output_parsers
perplexity
pgvector
pinecone
plivo
processing
prototypes
qdrant
Expand Down
1 change: 1 addition & 0 deletions scripts/ci/release_inventory_contract.json
Original file line number Diff line number Diff line change
Expand Up @@ -276,6 +276,7 @@
"perplexity",
"pgvector",
"pinecone",
"plivo",
"qdrant",
"redis",
"sambanova",
Expand Down
1 change: 1 addition & 0 deletions scripts/migrate/consolidate_bundles.py
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@
PROVIDER_DEPS: dict[str, list[str]] = {
# --- tranche 1: search/tools ---
"tavily": [], # talks to the Tavily API via httpx (an lfx core dep)
"plivo": [], # talks to the Plivo REST API via httpx (an lfx core dep)
# "exa" graduated to the standalone lfx-exa bundle (src/bundles/exa) when
# the component moved off the deprecated metaphor-python SDK onto exa-py.
"wikipedia": ["wikipedia==1.4.0", _LC_COMMUNITY],
Expand Down
5 changes: 4 additions & 1 deletion src/bundles/lfx-bundles/pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[project]
name = "lfx-bundles"
version = "1.1.6"
version = "1.1.7"
description = "Langflow's long-tail provider bundles as a single manifest-less metapackage (the langchain-community model)."
readme = "README.md"
requires-python = ">=3.10,<3.15"
Expand Down Expand Up @@ -76,6 +76,7 @@ openrouter = ["langchain-openai>=1.1.6"]
perplexity = ["langchain-perplexity>=1.0.0,<2.0.0"]
pgvector = ["pgvector>=0.4.2", "langchain-community>=0.4.1,<1.0.0"]
pinecone = ["langchain-pinecone~=0.2.13; python_version < '3.14'"]
plivo = []
qdrant = ["qdrant-client>=1.12.0,<2.0.0", "langchain-qdrant>=1.0.0,<2.0.0"]
redis = ["redis>=7.4.0,<8.0.0", "langchain-community>=0.4.1,<1.0.0"]
sambanova = ["langchain-sambanova~=1.0.0"]
Expand Down Expand Up @@ -144,6 +145,7 @@ all = [
"lfx-bundles[perplexity]",
"lfx-bundles[pgvector]",
"lfx-bundles[pinecone]",
"lfx-bundles[plivo]",
"lfx-bundles[qdrant]",
"lfx-bundles[redis]",
"lfx-bundles[sambanova]",
Expand Down Expand Up @@ -211,6 +213,7 @@ all-no-torch = [
"lfx-bundles[perplexity]",
"lfx-bundles[pgvector]",
"lfx-bundles[pinecone]",
"lfx-bundles[plivo]",
"lfx-bundles[qdrant]",
"lfx-bundles[redis]",
"lfx-bundles[sambanova]",
Expand Down
9 changes: 9 additions & 0 deletions src/bundles/lfx-bundles/src/lfx_bundles/plivo/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
from .lookup_number import PlivoLookupNumberComponent
from .make_call import PlivoMakeCallComponent
from .send_sms import PlivoSendSMSComponent

__all__ = [
"PlivoLookupNumberComponent",
"PlivoMakeCallComponent",
"PlivoSendSMSComponent",
]
90 changes: 90 additions & 0 deletions src/bundles/lfx-bundles/src/lfx_bundles/plivo/lookup_number.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
import json
import os

import httpx
from lfx.custom.custom_component.component import Component
from lfx.io import DropdownInput, MessageTextInput, Output, SecretStrInput
from lfx.log.logger import logger
from lfx.schema.data import Data


class PlivoLookupNumberComponent(Component):
display_name = "Plivo Lookup Number"
description = "Look up carrier and formatting details for a phone number through the Plivo Lookup API."
documentation: str = "https://www.plivo.com/docs/lookup/api/number"
icon = "Plivo"
name = "PlivoLookupNumberComponent"

inputs = [
SecretStrInput(
name="auth_id",
display_name="Plivo Auth ID",
info="Your Plivo Auth ID from the console at cx.plivo.com.",
value=os.getenv("PLIVO_AUTH_ID", ""),
required=True,
),
SecretStrInput(
name="auth_token",
display_name="Plivo Auth Token",
info="Your Plivo Auth Token from the console at cx.plivo.com.",
value=os.getenv("PLIVO_AUTH_TOKEN", ""),
required=True,
),
MessageTextInput(
name="number",
display_name="Phone Number",
info="The phone number to look up, in E.164 format such as +14155551234.",
value="",
required=True,
tool_mode=True,
),
DropdownInput(
name="type",
display_name="Lookup Type",
info=(
"Choose 'carrier' to include carrier name and line type. Choose 'none' for country and formatting only."
),
options=["carrier", "none"],
value="carrier",
),
]

outputs = [
Output(display_name="Output", name="output", method="build_output"),
]

async def build_output(self) -> Data:
try:
auth_id = self.auth_id.strip()
number = self.number.strip()
url = f"https://lookup.plivo.com/v1/Number/{number}"
params = {} if self.type == "none" else {"type": self.type}

await logger.ainfo("Looking up Plivo number: %s", number)

async with httpx.AsyncClient() as client:
response = await client.get(
url,
auth=(auth_id, self.auth_token.strip()),
params=params,
timeout=10.0,
)
response.raise_for_status()
response_data = response.json()
await logger.ainfo("Plivo lookup result: %s", response_data)

except httpx.HTTPStatusError as http_err:
await logger.aexception("HTTP error occurred")
response_data = {"error": f"HTTP error occurred: {http_err}", "response_text": response.text}
except httpx.RequestError as req_err:
await logger.aexception("Request failed")
response_data = {"error": f"Request failed: {req_err}"}
except json.JSONDecodeError as json_err:
await logger.aexception("Response parsing failed")
response_data = {"error": f"Response parsing failed: {json_err}", "raw_response": response.text}
except Exception as e: # noqa: BLE001
await logger.aexception("An unexpected error occurred")
response_data = {"error": f"An unexpected error occurred: {e!s}"}

self.status = response_data
return Data(value=response_data)
116 changes: 116 additions & 0 deletions src/bundles/lfx-bundles/src/lfx_bundles/plivo/make_call.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
import json
import os

import httpx
from lfx.custom.custom_component.component import Component
from lfx.io import DropdownInput, MessageTextInput, Output, SecretStrInput
from lfx.log.logger import logger
from lfx.schema.data import Data


class PlivoMakeCallComponent(Component):
display_name = "Plivo Make Call"
description = "Place an outbound phone call through the Plivo Voice API."
documentation: str = "https://www.plivo.com/docs/voice/api/call"
icon = "Plivo"
name = "PlivoMakeCallComponent"

inputs = [
SecretStrInput(
name="auth_id",
display_name="Plivo Auth ID",
info="Your Plivo Auth ID from the console at cx.plivo.com.",
value=os.getenv("PLIVO_AUTH_ID", ""),
required=True,
),
SecretStrInput(
name="auth_token",
display_name="Plivo Auth Token",
info="Your Plivo Auth Token from the console at cx.plivo.com.",
value=os.getenv("PLIVO_AUTH_TOKEN", ""),
required=True,
),
MessageTextInput(
name="from_number",
display_name="From Number",
info="Caller ID: a Plivo phone number in E.164 format.",
value=os.getenv("PLIVO_SRC", ""),
required=True,
),
MessageTextInput(
name="to_number",
display_name="To Number",
info="The recipient's phone number in E.164 format.",
value="",
required=True,
tool_mode=True,
),
MessageTextInput(
name="answer_url",
display_name="Answer URL",
info=(
"URL Plivo fetches for call-flow XML when the call is answered. A default static URL is "
"provided, so a basic call needs no setup. Override it with your own webhook to customize "
"what the call says."
),
value="https://s3.amazonaws.com/static.plivo.com/answer.xml",
required=False,
),
DropdownInput(
name="answer_method",
display_name="Answer Method",
info=(
"HTTP method Plivo uses to fetch the Answer URL. The built-in default Answer URL is a static "
"file that only supports GET, so keep this on GET unless you point Answer URL at your own "
"webhook that expects POST."
),
options=["GET", "POST"],
value="GET",
required=False,
),
]

outputs = [
Output(display_name="Output", name="output", method="build_output"),
]

async def build_output(self) -> Data:
try:
auth_id = self.auth_id.strip()
payload = {
"from": self.from_number.strip(),
"to": self.to_number.strip(),
"answer_url": self.answer_url.strip(),
"answer_method": (self.answer_method or "GET").strip().upper(),
}

url = f"https://api.plivo.com/v1/Account/{auth_id}/Call/"

await logger.ainfo("Placing Plivo call with payload: %s", payload)

async with httpx.AsyncClient() as client:
response = await client.post(
url,
auth=(auth_id, self.auth_token.strip()),
json=payload,
timeout=10.0,
)
response.raise_for_status()
response_data = response.json()
await logger.ainfo("Plivo call fired: %s", response_data)

except httpx.HTTPStatusError as http_err:
await logger.aexception("HTTP error occurred")
response_data = {"error": f"HTTP error occurred: {http_err}", "response_text": response.text}
except httpx.RequestError as req_err:
await logger.aexception("Request failed")
response_data = {"error": f"Request failed: {req_err}"}
except json.JSONDecodeError as json_err:
await logger.aexception("Response parsing failed")
response_data = {"error": f"Response parsing failed: {json_err}", "raw_response": response.text}
except Exception as e: # noqa: BLE001
await logger.aexception("An unexpected error occurred")
response_data = {"error": f"An unexpected error occurred: {e!s}"}

self.status = response_data
return Data(value=response_data)
Loading
Loading