lfx-nightly 0.1.12.dev27__py3-none-any.whl → 0.1.12.dev28__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- lfx/base/agents/agent.py +19 -11
- lfx/base/agents/utils.py +18 -0
- lfx/base/data/base_file.py +28 -19
- lfx/components/data/__init__.py +0 -6
- lfx/components/data/file.py +1 -1
- lfx/components/data/mock_data.py +5 -8
- lfx/components/data/save_file.py +625 -0
- lfx/components/data/web_search.py +225 -11
- lfx/components/docling/docling_remote.py +4 -1
- lfx/components/input_output/chat.py +8 -1
- lfx/components/nvidia/nvidia.py +1 -4
- lfx/components/processing/__init__.py +3 -3
- lfx/components/processing/dataframe_to_toolset.py +259 -0
- lfx/components/processing/lambda_filter.py +3 -3
- lfx/schema/image.py +72 -19
- lfx/schema/message.py +7 -2
- lfx/services/settings/base.py +7 -0
- lfx/utils/util.py +135 -0
- {lfx_nightly-0.1.12.dev27.dist-info → lfx_nightly-0.1.12.dev28.dist-info}/METADATA +1 -1
- {lfx_nightly-0.1.12.dev27.dist-info → lfx_nightly-0.1.12.dev28.dist-info}/RECORD +22 -23
- lfx/components/data/news_search.py +0 -164
- lfx/components/data/rss.py +0 -69
- lfx/components/processing/save_file.py +0 -225
- {lfx_nightly-0.1.12.dev27.dist-info → lfx_nightly-0.1.12.dev28.dist-info}/WHEEL +0 -0
- {lfx_nightly-0.1.12.dev27.dist-info → lfx_nightly-0.1.12.dev28.dist-info}/entry_points.txt +0 -0
lfx/components/data/rss.py
DELETED
|
@@ -1,69 +0,0 @@
|
|
|
1
|
-
import pandas as pd
|
|
2
|
-
import requests
|
|
3
|
-
from bs4 import BeautifulSoup
|
|
4
|
-
|
|
5
|
-
from lfx.custom import Component
|
|
6
|
-
from lfx.io import IntInput, MessageTextInput, Output
|
|
7
|
-
from lfx.log.logger import logger
|
|
8
|
-
from lfx.schema import DataFrame
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
class RSSReaderComponent(Component):
|
|
12
|
-
display_name = "RSS Reader"
|
|
13
|
-
description = "Fetches and parses an RSS feed."
|
|
14
|
-
documentation: str = "https://docs.langflow.org/components-data#rss-reader"
|
|
15
|
-
icon = "rss"
|
|
16
|
-
name = "RSSReaderSimple"
|
|
17
|
-
|
|
18
|
-
inputs = [
|
|
19
|
-
MessageTextInput(
|
|
20
|
-
name="rss_url",
|
|
21
|
-
display_name="RSS Feed URL",
|
|
22
|
-
info="URL of the RSS feed to parse.",
|
|
23
|
-
tool_mode=True,
|
|
24
|
-
required=True,
|
|
25
|
-
),
|
|
26
|
-
IntInput(
|
|
27
|
-
name="timeout",
|
|
28
|
-
display_name="Timeout",
|
|
29
|
-
info="Timeout for the RSS feed request.",
|
|
30
|
-
value=5,
|
|
31
|
-
advanced=True,
|
|
32
|
-
),
|
|
33
|
-
]
|
|
34
|
-
|
|
35
|
-
outputs = [Output(name="articles", display_name="Articles", method="read_rss")]
|
|
36
|
-
|
|
37
|
-
def read_rss(self) -> DataFrame:
|
|
38
|
-
try:
|
|
39
|
-
response = requests.get(self.rss_url, timeout=self.timeout)
|
|
40
|
-
response.raise_for_status()
|
|
41
|
-
if not response.content.strip():
|
|
42
|
-
msg = "Empty response received"
|
|
43
|
-
raise ValueError(msg)
|
|
44
|
-
# Check if the response is valid XML
|
|
45
|
-
try:
|
|
46
|
-
BeautifulSoup(response.content, "xml")
|
|
47
|
-
except Exception as e:
|
|
48
|
-
msg = f"Invalid XML response: {e}"
|
|
49
|
-
raise ValueError(msg) from e
|
|
50
|
-
soup = BeautifulSoup(response.content, "xml")
|
|
51
|
-
items = soup.find_all("item")
|
|
52
|
-
except (requests.RequestException, ValueError) as e:
|
|
53
|
-
self.status = f"Failed to fetch RSS: {e}"
|
|
54
|
-
return DataFrame(pd.DataFrame([{"title": "Error", "link": "", "published": "", "summary": str(e)}]))
|
|
55
|
-
|
|
56
|
-
articles = [
|
|
57
|
-
{
|
|
58
|
-
"title": item.title.text if item.title else "",
|
|
59
|
-
"link": item.link.text if item.link else "",
|
|
60
|
-
"published": item.pubDate.text if item.pubDate else "",
|
|
61
|
-
"summary": item.description.text if item.description else "",
|
|
62
|
-
}
|
|
63
|
-
for item in items
|
|
64
|
-
]
|
|
65
|
-
|
|
66
|
-
# Ensure the DataFrame has the correct columns even if empty
|
|
67
|
-
df_articles = pd.DataFrame(articles, columns=["title", "link", "published", "summary"])
|
|
68
|
-
logger.info(f"Fetched {len(df_articles)} articles.")
|
|
69
|
-
return DataFrame(df_articles)
|
|
@@ -1,225 +0,0 @@
|
|
|
1
|
-
import json
|
|
2
|
-
from collections.abc import AsyncIterator, Iterator
|
|
3
|
-
from pathlib import Path
|
|
4
|
-
|
|
5
|
-
import orjson
|
|
6
|
-
import pandas as pd
|
|
7
|
-
from fastapi import UploadFile
|
|
8
|
-
from fastapi.encoders import jsonable_encoder
|
|
9
|
-
|
|
10
|
-
from lfx.custom import Component
|
|
11
|
-
from lfx.io import DropdownInput, HandleInput, StrInput
|
|
12
|
-
from lfx.schema import Data, DataFrame, Message
|
|
13
|
-
from lfx.services.deps import get_settings_service, get_storage_service
|
|
14
|
-
from lfx.template.field.base import Output
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
class SaveToFileComponent(Component):
|
|
18
|
-
display_name = "Save File"
|
|
19
|
-
description = "Save data to a local file in the selected format."
|
|
20
|
-
documentation: str = "https://docs.langflow.org/components-processing#save-file"
|
|
21
|
-
icon = "save"
|
|
22
|
-
name = "SaveToFile"
|
|
23
|
-
|
|
24
|
-
# File format options for different types
|
|
25
|
-
DATA_FORMAT_CHOICES = ["csv", "excel", "json", "markdown"]
|
|
26
|
-
MESSAGE_FORMAT_CHOICES = ["txt", "json", "markdown"]
|
|
27
|
-
|
|
28
|
-
inputs = [
|
|
29
|
-
HandleInput(
|
|
30
|
-
name="input",
|
|
31
|
-
display_name="Input",
|
|
32
|
-
info="The input to save.",
|
|
33
|
-
dynamic=True,
|
|
34
|
-
input_types=["Data", "DataFrame", "Message"],
|
|
35
|
-
required=True,
|
|
36
|
-
),
|
|
37
|
-
StrInput(
|
|
38
|
-
name="file_name",
|
|
39
|
-
display_name="File Name",
|
|
40
|
-
info="Name file will be saved as (without extension).",
|
|
41
|
-
required=True,
|
|
42
|
-
),
|
|
43
|
-
DropdownInput(
|
|
44
|
-
name="file_format",
|
|
45
|
-
display_name="File Format",
|
|
46
|
-
options=list(dict.fromkeys(DATA_FORMAT_CHOICES + MESSAGE_FORMAT_CHOICES)),
|
|
47
|
-
info="Select the file format to save the input. If not provided, the default format will be used.",
|
|
48
|
-
value="",
|
|
49
|
-
advanced=True,
|
|
50
|
-
),
|
|
51
|
-
]
|
|
52
|
-
|
|
53
|
-
outputs = [Output(display_name="File Path", name="message", method="save_to_file")]
|
|
54
|
-
|
|
55
|
-
async def save_to_file(self) -> Message:
|
|
56
|
-
"""Save the input to a file and upload it, returning a confirmation message."""
|
|
57
|
-
# Validate inputs
|
|
58
|
-
if not self.file_name:
|
|
59
|
-
msg = "File name must be provided."
|
|
60
|
-
raise ValueError(msg)
|
|
61
|
-
if not self._get_input_type():
|
|
62
|
-
msg = "Input type is not set."
|
|
63
|
-
raise ValueError(msg)
|
|
64
|
-
|
|
65
|
-
# Validate file format based on input type
|
|
66
|
-
file_format = self.file_format or self._get_default_format()
|
|
67
|
-
allowed_formats = (
|
|
68
|
-
self.MESSAGE_FORMAT_CHOICES if self._get_input_type() == "Message" else self.DATA_FORMAT_CHOICES
|
|
69
|
-
)
|
|
70
|
-
if file_format not in allowed_formats:
|
|
71
|
-
msg = f"Invalid file format '{file_format}' for {self._get_input_type()}. Allowed: {allowed_formats}"
|
|
72
|
-
raise ValueError(msg)
|
|
73
|
-
|
|
74
|
-
# Prepare file path
|
|
75
|
-
file_path = Path(self.file_name).expanduser()
|
|
76
|
-
if not file_path.parent.exists():
|
|
77
|
-
file_path.parent.mkdir(parents=True, exist_ok=True)
|
|
78
|
-
file_path = self._adjust_file_path_with_format(file_path, file_format)
|
|
79
|
-
|
|
80
|
-
# Save the input to file based on type
|
|
81
|
-
if self._get_input_type() == "DataFrame":
|
|
82
|
-
confirmation = self._save_dataframe(self.input, file_path, file_format)
|
|
83
|
-
elif self._get_input_type() == "Data":
|
|
84
|
-
confirmation = self._save_data(self.input, file_path, file_format)
|
|
85
|
-
elif self._get_input_type() == "Message":
|
|
86
|
-
confirmation = await self._save_message(self.input, file_path, file_format)
|
|
87
|
-
else:
|
|
88
|
-
msg = f"Unsupported input type: {self._get_input_type()}"
|
|
89
|
-
raise ValueError(msg)
|
|
90
|
-
|
|
91
|
-
# Upload the saved file
|
|
92
|
-
await self._upload_file(file_path)
|
|
93
|
-
|
|
94
|
-
# Return the final file path and confirmation message
|
|
95
|
-
final_path = Path.cwd() / file_path if not file_path.is_absolute() else file_path
|
|
96
|
-
|
|
97
|
-
return Message(text=f"{confirmation} at {final_path}")
|
|
98
|
-
|
|
99
|
-
def _get_input_type(self) -> str:
|
|
100
|
-
"""Determine the input type based on the provided input."""
|
|
101
|
-
# Use exact type checking (type() is) instead of isinstance() to avoid inheritance issues.
|
|
102
|
-
# Since Message inherits from Data, isinstance(message, Data) would return True for Message objects,
|
|
103
|
-
# causing Message inputs to be incorrectly identified as Data type.
|
|
104
|
-
if type(self.input) is DataFrame:
|
|
105
|
-
return "DataFrame"
|
|
106
|
-
if type(self.input) is Message:
|
|
107
|
-
return "Message"
|
|
108
|
-
if type(self.input) is Data:
|
|
109
|
-
return "Data"
|
|
110
|
-
msg = f"Unsupported input type: {type(self.input)}"
|
|
111
|
-
raise ValueError(msg)
|
|
112
|
-
|
|
113
|
-
def _get_default_format(self) -> str:
|
|
114
|
-
"""Return the default file format based on input type."""
|
|
115
|
-
if self._get_input_type() == "DataFrame":
|
|
116
|
-
return "csv"
|
|
117
|
-
if self._get_input_type() == "Data":
|
|
118
|
-
return "json"
|
|
119
|
-
if self._get_input_type() == "Message":
|
|
120
|
-
return "json"
|
|
121
|
-
return "json" # Fallback
|
|
122
|
-
|
|
123
|
-
def _adjust_file_path_with_format(self, path: Path, fmt: str) -> Path:
|
|
124
|
-
"""Adjust the file path to include the correct extension."""
|
|
125
|
-
file_extension = path.suffix.lower().lstrip(".")
|
|
126
|
-
if fmt == "excel":
|
|
127
|
-
return Path(f"{path}.xlsx").expanduser() if file_extension not in ["xlsx", "xls"] else path
|
|
128
|
-
return Path(f"{path}.{fmt}").expanduser() if file_extension != fmt else path
|
|
129
|
-
|
|
130
|
-
async def _upload_file(self, file_path: Path) -> None:
|
|
131
|
-
"""Upload the saved file using the upload_user_file service."""
|
|
132
|
-
try:
|
|
133
|
-
from langflow.api.v2.files import upload_user_file
|
|
134
|
-
from langflow.services.database.models.user.crud import get_user_by_id
|
|
135
|
-
except ImportError as e:
|
|
136
|
-
msg = (
|
|
137
|
-
"Langflow file upload functionality is not available. "
|
|
138
|
-
"This feature requires the full Langflow installation. "
|
|
139
|
-
)
|
|
140
|
-
raise ImportError(msg) from e
|
|
141
|
-
|
|
142
|
-
if not file_path.exists():
|
|
143
|
-
msg = f"File not found: {file_path}"
|
|
144
|
-
raise FileNotFoundError(msg)
|
|
145
|
-
|
|
146
|
-
with file_path.open("rb") as f:
|
|
147
|
-
try:
|
|
148
|
-
from langflow.services.database.models.user.crud import get_user_by_id
|
|
149
|
-
from langflow.services.deps import session_scope
|
|
150
|
-
except ImportError as e:
|
|
151
|
-
msg = (
|
|
152
|
-
"Langflow MCP server functionality is not available. "
|
|
153
|
-
"This feature requires the full Langflow installation."
|
|
154
|
-
)
|
|
155
|
-
raise ImportError(msg) from e
|
|
156
|
-
async with session_scope() as db:
|
|
157
|
-
if not self.user_id:
|
|
158
|
-
msg = "User ID is required for file saving."
|
|
159
|
-
raise ValueError(msg)
|
|
160
|
-
current_user = await get_user_by_id(db, self.user_id)
|
|
161
|
-
|
|
162
|
-
await upload_user_file(
|
|
163
|
-
file=UploadFile(filename=file_path.name, file=f, size=file_path.stat().st_size),
|
|
164
|
-
session=db,
|
|
165
|
-
current_user=current_user,
|
|
166
|
-
storage_service=get_storage_service(),
|
|
167
|
-
settings_service=get_settings_service(),
|
|
168
|
-
)
|
|
169
|
-
|
|
170
|
-
def _save_dataframe(self, dataframe: DataFrame, path: Path, fmt: str) -> str:
|
|
171
|
-
"""Save a DataFrame to the specified file format."""
|
|
172
|
-
if fmt == "csv":
|
|
173
|
-
dataframe.to_csv(path, index=False)
|
|
174
|
-
elif fmt == "excel":
|
|
175
|
-
dataframe.to_excel(path, index=False, engine="openpyxl")
|
|
176
|
-
elif fmt == "json":
|
|
177
|
-
dataframe.to_json(path, orient="records", indent=2)
|
|
178
|
-
elif fmt == "markdown":
|
|
179
|
-
path.write_text(dataframe.to_markdown(index=False), encoding="utf-8")
|
|
180
|
-
else:
|
|
181
|
-
msg = f"Unsupported DataFrame format: {fmt}"
|
|
182
|
-
raise ValueError(msg)
|
|
183
|
-
return f"DataFrame saved successfully as '{path}'"
|
|
184
|
-
|
|
185
|
-
def _save_data(self, data: Data, path: Path, fmt: str) -> str:
|
|
186
|
-
"""Save a Data object to the specified file format."""
|
|
187
|
-
if fmt == "csv":
|
|
188
|
-
pd.DataFrame(data.data).to_csv(path, index=False)
|
|
189
|
-
elif fmt == "excel":
|
|
190
|
-
pd.DataFrame(data.data).to_excel(path, index=False, engine="openpyxl")
|
|
191
|
-
elif fmt == "json":
|
|
192
|
-
path.write_text(
|
|
193
|
-
orjson.dumps(jsonable_encoder(data.data), option=orjson.OPT_INDENT_2).decode("utf-8"), encoding="utf-8"
|
|
194
|
-
)
|
|
195
|
-
elif fmt == "markdown":
|
|
196
|
-
path.write_text(pd.DataFrame(data.data).to_markdown(index=False), encoding="utf-8")
|
|
197
|
-
else:
|
|
198
|
-
msg = f"Unsupported Data format: {fmt}"
|
|
199
|
-
raise ValueError(msg)
|
|
200
|
-
return f"Data saved successfully as '{path}'"
|
|
201
|
-
|
|
202
|
-
async def _save_message(self, message: Message, path: Path, fmt: str) -> str:
|
|
203
|
-
"""Save a Message to the specified file format, handling async iterators."""
|
|
204
|
-
content = ""
|
|
205
|
-
if message.text is None:
|
|
206
|
-
content = ""
|
|
207
|
-
elif isinstance(message.text, AsyncIterator):
|
|
208
|
-
async for item in message.text:
|
|
209
|
-
content += str(item) + " "
|
|
210
|
-
content = content.strip()
|
|
211
|
-
elif isinstance(message.text, Iterator):
|
|
212
|
-
content = " ".join(str(item) for item in message.text)
|
|
213
|
-
else:
|
|
214
|
-
content = str(message.text)
|
|
215
|
-
|
|
216
|
-
if fmt == "txt":
|
|
217
|
-
path.write_text(content, encoding="utf-8")
|
|
218
|
-
elif fmt == "json":
|
|
219
|
-
path.write_text(json.dumps({"message": content}, indent=2), encoding="utf-8")
|
|
220
|
-
elif fmt == "markdown":
|
|
221
|
-
path.write_text(f"**Message:**\n\n{content}", encoding="utf-8")
|
|
222
|
-
else:
|
|
223
|
-
msg = f"Unsupported Message format: {fmt}"
|
|
224
|
-
raise ValueError(msg)
|
|
225
|
-
return f"Message saved successfully as '{path}'"
|
|
File without changes
|
|
File without changes
|