feat: New Search Bundle (#8146)
* delete data and transfer data to dataframe * [autofix.ci] apply automated fixes * create a new bundle for search * fix type for dataframe * add data_to_dataframe function * [autofix.ci] apply automated fixes * fix test because of files movement * delete message and text * json update * fix search yahoo test * fix run_model output type * fix test errors * fix test errors * fix test error * try fix frontend tests * test fix * [autofix.ci] apply automated fixes * move serp search * fix test * fix test * fix test to pass ruff style check --------- Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com> Co-authored-by: Edwin Jose <edwin.jose@datastax.com> Co-authored-by: Mike Fortman <michael.fortman@datastax.com>
This commit is contained in:
parent
9d5c75bce3
commit
a5ce562299
37 changed files with 25863 additions and 645 deletions
25089
docs/package-lock.json
generated
Normal file
25089
docs/package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load diff
921
docs/yarn.lock
921
docs/yarn.lock
File diff suppressed because it is too large
Load diff
|
|
@ -4,7 +4,7 @@ from collections.abc import Sequence
|
|||
from langflow.custom import Component
|
||||
from langflow.field_typing import Tool
|
||||
from langflow.io import Output
|
||||
from langflow.schema import Data
|
||||
from langflow.schema import Data, DataFrame
|
||||
|
||||
|
||||
class LCToolComponent(Component):
|
||||
|
|
@ -26,7 +26,7 @@ class LCToolComponent(Component):
|
|||
raise ValueError(msg)
|
||||
|
||||
@abstractmethod
|
||||
def run_model(self) -> Data | list[Data]:
|
||||
def run_model(self) -> Data | list[Data] | DataFrame:
|
||||
"""Run model and return the output."""
|
||||
|
||||
@abstractmethod
|
||||
|
|
|
|||
29
src/backend/base/langflow/components/search/__init__.py
Normal file
29
src/backend/base/langflow/components/search/__init__.py
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
from .arxiv import ArXivComponent
|
||||
from .bing_search_api import BingSearchAPIComponent
|
||||
from .duck_duck_go_search_run import DuckDuckGoSearchComponent
|
||||
from .exa_search import ExaSearchToolkit
|
||||
from .glean_search_api import GleanSearchAPISchema
|
||||
from .google_search_api_core import GoogleSearchAPICore
|
||||
from .google_serper_api_core import GoogleSerperAPICore
|
||||
from .search import SearchComponent
|
||||
from .serp import SerpComponent
|
||||
from .wikidata import WikidataComponent
|
||||
from .wikipedia import WikipediaComponent
|
||||
from .wolfram_alpha_api import WolframAlphaAPIComponent
|
||||
from .yahoo import YahooFinanceSchema
|
||||
|
||||
__all__ = [
|
||||
"ArXivComponent",
|
||||
"BingSearchAPIComponent",
|
||||
"DuckDuckGoSearchComponent",
|
||||
"ExaSearchToolkit",
|
||||
"GleanSearchAPISchema",
|
||||
"GoogleSearchAPICore",
|
||||
"GoogleSerperAPICore",
|
||||
"SearchComponent",
|
||||
"SerpComponent",
|
||||
"WikidataComponent",
|
||||
"WikipediaComponent",
|
||||
"WolframAlphaAPIComponent",
|
||||
"YahooFinanceSchema",
|
||||
]
|
||||
|
|
@ -5,6 +5,7 @@ from xml.etree.ElementTree import Element
|
|||
from defusedxml.ElementTree import fromstring
|
||||
|
||||
from langflow.custom import Component
|
||||
from langflow.helpers.data import data_to_dataframe
|
||||
from langflow.io import DropdownInput, IntInput, MessageTextInput, Output
|
||||
from langflow.schema import Data, DataFrame
|
||||
|
||||
|
|
@ -37,8 +38,7 @@ class ArXivComponent(Component):
|
|||
]
|
||||
|
||||
outputs = [
|
||||
Output(display_name="Data", name="data", method="search_papers"),
|
||||
Output(display_name="DataFrame", name="dataframe", method="as_dataframe"),
|
||||
Output(display_name="DataFrame", name="dataframe", method="search_papers_dataframe"),
|
||||
]
|
||||
|
||||
def build_query_url(self) -> str:
|
||||
|
|
@ -105,6 +105,9 @@ class ArXivComponent(Component):
|
|||
cat = element.find("arxiv:primary_category", ns)
|
||||
return cat.get("term") if cat is not None else None
|
||||
|
||||
def run_model(self) -> DataFrame:
|
||||
return self.search_papers_dataframe()
|
||||
|
||||
def search_papers(self) -> list[Data]:
|
||||
"""Search arXiv and return results."""
|
||||
try:
|
||||
|
|
@ -150,13 +153,11 @@ class ArXivComponent(Component):
|
|||
else:
|
||||
return results
|
||||
|
||||
def as_dataframe(self) -> DataFrame:
|
||||
def search_papers_dataframe(self) -> DataFrame:
|
||||
"""Convert the Arxiv search results to a DataFrame.
|
||||
|
||||
Returns:
|
||||
DataFrame: A DataFrame containing the search results.
|
||||
"""
|
||||
data = self.search_papers()
|
||||
if isinstance(data, list):
|
||||
return DataFrame(data=[d.data for d in data])
|
||||
return DataFrame(data=[data.data])
|
||||
return data_to_dataframe(data)
|
||||
|
|
@ -5,8 +5,10 @@ from langchain_community.utilities import BingSearchAPIWrapper
|
|||
|
||||
from langflow.base.langchain_utilities.model import LCToolComponent
|
||||
from langflow.field_typing import Tool
|
||||
from langflow.helpers.data import data_to_dataframe
|
||||
from langflow.inputs import IntInput, MessageTextInput, MultilineInput, SecretStrInput
|
||||
from langflow.schema import Data
|
||||
from langflow.io import Output
|
||||
from langflow.schema import Data, DataFrame
|
||||
|
||||
|
||||
class BingSearchAPIComponent(LCToolComponent):
|
||||
|
|
@ -25,7 +27,15 @@ class BingSearchAPIComponent(LCToolComponent):
|
|||
IntInput(name="k", display_name="Number of results", value=4, required=True),
|
||||
]
|
||||
|
||||
def run_model(self) -> list[Data]:
|
||||
outputs = [
|
||||
Output(display_name="DataFrame", name="dataframe", method="fetch_content_dataframe"),
|
||||
Output(display_name="Tool", name="tool", method="build_tool"),
|
||||
]
|
||||
|
||||
def run_model(self) -> DataFrame:
|
||||
return self.fetch_content_dataframe()
|
||||
|
||||
def fetch_content(self) -> list[Data]:
|
||||
if self.bing_search_url:
|
||||
wrapper = BingSearchAPIWrapper(
|
||||
bing_search_url=self.bing_search_url, bing_subscription_key=self.bing_subscription_key
|
||||
|
|
@ -37,6 +47,10 @@ class BingSearchAPIComponent(LCToolComponent):
|
|||
self.status = data
|
||||
return data
|
||||
|
||||
def fetch_content_dataframe(self) -> DataFrame:
|
||||
data = self.fetch_content()
|
||||
return data_to_dataframe(data)
|
||||
|
||||
def build_tool(self) -> Tool:
|
||||
if self.bing_search_url:
|
||||
wrapper = BingSearchAPIWrapper(
|
||||
|
|
@ -1,10 +1,10 @@
|
|||
from langchain_community.tools import DuckDuckGoSearchRun
|
||||
|
||||
from langflow.custom import Component
|
||||
from langflow.helpers.data import data_to_dataframe
|
||||
from langflow.inputs import IntInput, MessageTextInput
|
||||
from langflow.io import Output
|
||||
from langflow.schema import Data
|
||||
from langflow.schema.message import Message
|
||||
from langflow.schema import Data, DataFrame
|
||||
|
||||
|
||||
class DuckDuckGoSearchComponent(Component):
|
||||
|
|
@ -42,16 +42,15 @@ class DuckDuckGoSearchComponent(Component):
|
|||
]
|
||||
|
||||
outputs = [
|
||||
Output(display_name="Data", name="data", method="fetch_content"),
|
||||
Output(display_name="Text", name="text", method="fetch_content_text"),
|
||||
Output(display_name="DataFrame", name="dataframe", method="fetch_content_dataframe"),
|
||||
]
|
||||
|
||||
def _build_wrapper(self) -> DuckDuckGoSearchRun:
|
||||
"""Build the DuckDuckGo search wrapper."""
|
||||
return DuckDuckGoSearchRun()
|
||||
|
||||
def run_model(self) -> list[Data]:
|
||||
return self.fetch_content()
|
||||
def run_model(self) -> DataFrame:
|
||||
return self.fetch_content_dataframe()
|
||||
|
||||
def fetch_content(self) -> list[Data]:
|
||||
"""Execute the search and return results as Data objects."""
|
||||
|
|
@ -83,9 +82,11 @@ class DuckDuckGoSearchComponent(Component):
|
|||
self.status = data_results
|
||||
return data_results
|
||||
|
||||
def fetch_content_text(self) -> Message:
|
||||
"""Return search results as a single text message."""
|
||||
def fetch_content_dataframe(self) -> DataFrame:
|
||||
"""Convert the search results to a DataFrame.
|
||||
|
||||
Returns:
|
||||
DataFrame: A DataFrame containing the search results.
|
||||
"""
|
||||
data = self.fetch_content()
|
||||
result_string = "\n".join(item.text for item in data)
|
||||
self.status = result_string
|
||||
return Message(text=result_string)
|
||||
return data_to_dataframe(data)
|
||||
|
|
@ -9,6 +9,7 @@ from pydantic.v1 import Field
|
|||
|
||||
from langflow.base.langchain_utilities.model import LCToolComponent
|
||||
from langflow.field_typing import Tool
|
||||
from langflow.helpers.data import data_to_dataframe
|
||||
from langflow.inputs import IntInput, MultilineInput, NestedDictInput, SecretStrInput, StrInput
|
||||
from langflow.io import Output
|
||||
from langflow.schema import Data, DataFrame
|
||||
|
|
@ -104,8 +105,7 @@ class GleanSearchAPIComponent(LCToolComponent):
|
|||
icon: str = "Glean"
|
||||
|
||||
outputs = [
|
||||
Output(display_name="Data", name="data", method="run_model"),
|
||||
Output(display_name="DataFrame", name="dataframe", method="as_dataframe"),
|
||||
Output(display_name="DataFrame", name="dataframe", method="fetch_content_dataframe"),
|
||||
]
|
||||
|
||||
inputs = [
|
||||
|
|
@ -133,7 +133,10 @@ class GleanSearchAPIComponent(LCToolComponent):
|
|||
|
||||
return tool
|
||||
|
||||
def run_model(self) -> list[Data]:
|
||||
def run_model(self) -> DataFrame:
|
||||
return self.fetch_content_dataframe()
|
||||
|
||||
def fetch_content(self) -> list[Data]:
|
||||
tool = self.build_tool()
|
||||
|
||||
results = tool.run(
|
||||
|
|
@ -160,13 +163,11 @@ class GleanSearchAPIComponent(LCToolComponent):
|
|||
glean_access_token=glean_access_token,
|
||||
)
|
||||
|
||||
def as_dataframe(self) -> DataFrame:
|
||||
def fetch_content_dataframe(self) -> DataFrame:
|
||||
"""Convert the Glean search results to a DataFrame.
|
||||
|
||||
Returns:
|
||||
DataFrame: A DataFrame containing the search results.
|
||||
"""
|
||||
data = self.run_model()
|
||||
if isinstance(data, list):
|
||||
return DataFrame(data=[d.data for d in data])
|
||||
return DataFrame(data=[data.data])
|
||||
data = self.fetch_content()
|
||||
return data_to_dataframe(data)
|
||||
|
|
@ -3,10 +3,10 @@ from typing import Any
|
|||
from langchain_community.utilities.searchapi import SearchApiAPIWrapper
|
||||
|
||||
from langflow.custom import Component
|
||||
from langflow.helpers.data import data_to_dataframe
|
||||
from langflow.inputs import DictInput, DropdownInput, IntInput, MultilineInput, SecretStrInput
|
||||
from langflow.io import Output
|
||||
from langflow.schema import Data, DataFrame
|
||||
from langflow.schema.message import Message
|
||||
|
||||
|
||||
class SearchComponent(Component):
|
||||
|
|
@ -29,16 +29,14 @@ class SearchComponent(Component):
|
|||
]
|
||||
|
||||
outputs = [
|
||||
Output(display_name="Data", name="data", method="fetch_content"),
|
||||
Output(display_name="Text", name="text", method="fetch_content_text"),
|
||||
Output(display_name="DataFrame", name="dataframe", method="as_dataframe"),
|
||||
Output(display_name="DataFrame", name="dataframe", method="fetch_content_dataframe"),
|
||||
]
|
||||
|
||||
def _build_wrapper(self):
|
||||
return SearchApiAPIWrapper(engine=self.engine, searchapi_api_key=self.api_key)
|
||||
|
||||
def run_model(self) -> list[Data]:
|
||||
return self.fetch_content()
|
||||
def run_model(self) -> DataFrame:
|
||||
return self.fetch_content_dataframe()
|
||||
|
||||
def fetch_content(self) -> list[Data]:
|
||||
wrapper = self._build_wrapper()
|
||||
|
|
@ -71,19 +69,11 @@ class SearchComponent(Component):
|
|||
self.status = results
|
||||
return results
|
||||
|
||||
def fetch_content_text(self) -> Message:
|
||||
data = self.fetch_content()
|
||||
result_string = ""
|
||||
for item in data:
|
||||
result_string += item.text + "\n"
|
||||
self.status = result_string
|
||||
return Message(text=result_string)
|
||||
|
||||
def as_dataframe(self) -> DataFrame:
|
||||
def fetch_content_dataframe(self) -> DataFrame:
|
||||
"""Convert the search results to a DataFrame.
|
||||
|
||||
Returns:
|
||||
DataFrame: A DataFrame containing the search results.
|
||||
"""
|
||||
data = self.fetch_content()
|
||||
return DataFrame(data)
|
||||
return data_to_dataframe(data)
|
||||
|
|
@ -3,10 +3,9 @@ from httpx import HTTPError
|
|||
from langchain_core.tools import ToolException
|
||||
|
||||
from langflow.custom import Component
|
||||
from langflow.helpers.data import data_to_text
|
||||
from langflow.helpers.data import data_to_dataframe
|
||||
from langflow.io import MultilineInput, Output
|
||||
from langflow.schema import Data
|
||||
from langflow.schema.message import Message
|
||||
from langflow.schema import Data, DataFrame
|
||||
|
||||
|
||||
class WikidataComponent(Component):
|
||||
|
|
@ -25,10 +24,12 @@ class WikidataComponent(Component):
|
|||
]
|
||||
|
||||
outputs = [
|
||||
Output(display_name="Data", name="data", method="fetch_content"),
|
||||
Output(display_name="Message", name="text", method="fetch_content_text"),
|
||||
Output(display_name="DataFrame", name="dataframe", method="fetch_content_dataframe"),
|
||||
]
|
||||
|
||||
def run_model(self) -> DataFrame:
|
||||
return self.fetch_content_dataframe()
|
||||
|
||||
def fetch_content(self) -> list[Data]:
|
||||
try:
|
||||
# Define request parameters for Wikidata API
|
||||
|
|
@ -79,8 +80,6 @@ class WikidataComponent(Component):
|
|||
else:
|
||||
return data
|
||||
|
||||
def fetch_content_text(self) -> Message:
|
||||
def fetch_content_dataframe(self) -> DataFrame:
|
||||
data = self.fetch_content()
|
||||
result_string = data_to_text("{text}", data)
|
||||
self.status = result_string
|
||||
return Message(text=result_string)
|
||||
return data_to_dataframe(data)
|
||||
|
|
@ -1,10 +1,10 @@
|
|||
from langchain_community.utilities.wikipedia import WikipediaAPIWrapper
|
||||
|
||||
from langflow.custom import Component
|
||||
from langflow.helpers.data import data_to_dataframe
|
||||
from langflow.inputs import BoolInput, IntInput, MessageTextInput, MultilineInput
|
||||
from langflow.io import Output
|
||||
from langflow.schema import Data, DataFrame
|
||||
from langflow.schema.message import Message
|
||||
|
||||
|
||||
class WikipediaComponent(Component):
|
||||
|
|
@ -27,24 +27,11 @@ class WikipediaComponent(Component):
|
|||
]
|
||||
|
||||
outputs = [
|
||||
Output(display_name="Data", name="data", method="fetch_content"),
|
||||
Output(display_name="DataFrame", name="dataframe", method="as_dataframe"),
|
||||
Output(display_name="DataFrame", name="dataframe", method="fetch_content_dataframe"),
|
||||
]
|
||||
|
||||
def fetch_content(self) -> list[Data]:
|
||||
wrapper = self._build_wrapper()
|
||||
docs = wrapper.load(self.input_value)
|
||||
data = [Data.from_document(doc) for doc in docs]
|
||||
self.status = data
|
||||
return data
|
||||
|
||||
def fetch_content_text(self) -> Message:
|
||||
data = self.fetch_content()
|
||||
result_string = ""
|
||||
for item in data:
|
||||
result_string += item.text + "\n"
|
||||
self.status = result_string
|
||||
return Message(text=result_string)
|
||||
def run_model(self) -> DataFrame:
|
||||
return self.fetch_content_dataframe()
|
||||
|
||||
def _build_wrapper(self) -> WikipediaAPIWrapper:
|
||||
return WikipediaAPIWrapper(
|
||||
|
|
@ -54,11 +41,13 @@ class WikipediaComponent(Component):
|
|||
doc_content_chars_max=self.doc_content_chars_max,
|
||||
)
|
||||
|
||||
def as_dataframe(self) -> DataFrame:
|
||||
"""Convert the Wikipedia results to a DataFrame.
|
||||
def fetch_content(self) -> list[Data]:
|
||||
wrapper = self._build_wrapper()
|
||||
docs = wrapper.load(self.input_value)
|
||||
data = [Data.from_document(doc) for doc in docs]
|
||||
self.status = data
|
||||
return data
|
||||
|
||||
Returns:
|
||||
DataFrame: A DataFrame containing the Wikipedia results.
|
||||
"""
|
||||
def fetch_content_dataframe(self) -> DataFrame:
|
||||
data = self.fetch_content()
|
||||
return DataFrame(data)
|
||||
return data_to_dataframe(data)
|
||||
|
|
@ -2,6 +2,7 @@ from langchain_community.utilities.wolfram_alpha import WolframAlphaAPIWrapper
|
|||
|
||||
from langflow.base.langchain_utilities.model import LCToolComponent
|
||||
from langflow.field_typing import Tool
|
||||
from langflow.helpers.data import data_to_dataframe
|
||||
from langflow.inputs import MultilineInput, SecretStrInput
|
||||
from langflow.io import Output
|
||||
from langflow.schema import Data, DataFrame
|
||||
|
|
@ -14,8 +15,7 @@ topics, delivering structured responses."""
|
|||
name = "WolframAlphaAPI"
|
||||
|
||||
outputs = [
|
||||
Output(display_name="Data", name="data", method="run_model"),
|
||||
Output(display_name="DataFrame", name="dataframe", method="as_dataframe"),
|
||||
Output(display_name="DataFrame", name="dataframe", method="fetch_content_dataframe"),
|
||||
]
|
||||
|
||||
inputs = [
|
||||
|
|
@ -27,12 +27,8 @@ topics, delivering structured responses."""
|
|||
|
||||
icon = "WolframAlphaAPI"
|
||||
|
||||
def run_model(self) -> list[Data]:
|
||||
wrapper = self._build_wrapper()
|
||||
result_str = wrapper.run(self.input_value)
|
||||
data = [Data(text=result_str)]
|
||||
self.status = data
|
||||
return data
|
||||
def run_model(self) -> DataFrame:
|
||||
return self.fetch_content_dataframe()
|
||||
|
||||
def build_tool(self) -> Tool:
|
||||
wrapper = self._build_wrapper()
|
||||
|
|
@ -41,11 +37,18 @@ topics, delivering structured responses."""
|
|||
def _build_wrapper(self) -> WolframAlphaAPIWrapper:
|
||||
return WolframAlphaAPIWrapper(wolfram_alpha_appid=self.app_id)
|
||||
|
||||
def as_dataframe(self) -> DataFrame:
|
||||
def fetch_content(self) -> list[Data]:
|
||||
wrapper = self._build_wrapper()
|
||||
result_str = wrapper.run(self.input_value)
|
||||
data = [Data(text=result_str)]
|
||||
self.status = data
|
||||
return data
|
||||
|
||||
def fetch_content_dataframe(self) -> DataFrame:
|
||||
"""Convert the Wolfram Alpha results to a DataFrame.
|
||||
|
||||
Returns:
|
||||
DataFrame: A DataFrame containing the query results.
|
||||
"""
|
||||
data = self.run_model()
|
||||
return DataFrame(data)
|
||||
data = self.fetch_content()
|
||||
return data_to_dataframe(data)
|
||||
|
|
@ -8,10 +8,10 @@ from loguru import logger
|
|||
from pydantic import BaseModel, Field
|
||||
|
||||
from langflow.custom import Component
|
||||
from langflow.helpers.data import data_to_dataframe
|
||||
from langflow.inputs import DropdownInput, IntInput, MessageTextInput
|
||||
from langflow.io import Output
|
||||
from langflow.schema import Data, DataFrame
|
||||
from langflow.schema.message import Message
|
||||
|
||||
|
||||
class YahooFinanceMethod(Enum):
|
||||
|
|
@ -77,21 +77,11 @@ to access financial data and market information from Yahoo Finance."""
|
|||
]
|
||||
|
||||
outputs = [
|
||||
Output(display_name="Data", name="data", method="fetch_content"),
|
||||
Output(display_name="Text", name="text", method="fetch_content_text"),
|
||||
Output(display_name="DataFrame", name="dataframe", method="as_dataframe"),
|
||||
Output(display_name="DataFrame", name="dataframe", method="fetch_content_dataframe"),
|
||||
]
|
||||
|
||||
def run_model(self) -> list[Data]:
|
||||
return self.fetch_content()
|
||||
|
||||
def fetch_content_text(self) -> Message:
|
||||
data = self.fetch_content()
|
||||
result_string = ""
|
||||
for item in data:
|
||||
result_string += item.text + "\n"
|
||||
self.status = result_string
|
||||
return Message(text=result_string)
|
||||
def run_model(self) -> DataFrame:
|
||||
return self.fetch_content_dataframe()
|
||||
|
||||
def _fetch_yfinance_data(self, ticker: yf.Ticker, method: YahooFinanceMethod, num_news: int | None) -> str:
|
||||
try:
|
||||
|
|
@ -142,11 +132,6 @@ to access financial data and market information from Yahoo Finance."""
|
|||
|
||||
return data_list
|
||||
|
||||
def as_dataframe(self) -> DataFrame:
|
||||
"""Convert the Yahoo search results to a DataFrame.
|
||||
|
||||
Returns:
|
||||
DataFrame: A DataFrame containing the search results.
|
||||
"""
|
||||
def fetch_content_dataframe(self) -> DataFrame:
|
||||
data = self.fetch_content()
|
||||
return DataFrame(data)
|
||||
return data_to_dataframe(data)
|
||||
|
|
@ -2,69 +2,48 @@ import warnings
|
|||
|
||||
from langchain_core._api.deprecation import LangChainDeprecationWarning
|
||||
|
||||
from .arxiv import ArXivComponent
|
||||
from .bing_search_api import BingSearchAPIComponent
|
||||
from .calculator import CalculatorToolComponent
|
||||
from .calculator_core import CalculatorComponent
|
||||
from .duck_duck_go_search_run import DuckDuckGoSearchComponent
|
||||
from .exa_search import ExaSearchToolkit
|
||||
from .glean_search_api import GleanSearchAPIComponent
|
||||
from .google_search_api import GoogleSearchAPIComponent
|
||||
from .google_search_api_core import GoogleSearchAPICore
|
||||
from .google_serper_api import GoogleSerperAPIComponent
|
||||
from .google_serper_api_core import GoogleSerperAPICore
|
||||
from .mcp_component import MCPToolsComponent
|
||||
from .python_code_structured_tool import PythonCodeStructuredTool
|
||||
from .python_repl import PythonREPLToolComponent
|
||||
from .python_repl_core import PythonREPLComponent
|
||||
from .search import SearchComponent
|
||||
from .search_api import SearchAPIComponent
|
||||
from .searxng import SearXNGToolComponent
|
||||
from .serp import SerpComponent
|
||||
from .serp_api import SerpAPIComponent
|
||||
from .tavily_extract import TavilyExtractComponent
|
||||
from .tavily_search import TavilySearchComponent
|
||||
from .tavily_search_tool import TavilySearchToolComponent
|
||||
from .wikidata import WikidataComponent
|
||||
from .wikidata_api import WikidataAPIComponent
|
||||
from .wikipedia import WikipediaComponent
|
||||
from .wikipedia_api import WikipediaAPIComponent
|
||||
from .wolfram_alpha_api import WolframAlphaAPIComponent
|
||||
from .yahoo import YfinanceComponent
|
||||
from .yahoo_finance import YfinanceToolComponent
|
||||
|
||||
with warnings.catch_warnings():
|
||||
warnings.simplefilter("ignore", LangChainDeprecationWarning)
|
||||
|
||||
__all__ = [
|
||||
"ArXivComponent",
|
||||
"BingSearchAPIComponent",
|
||||
"AstraDBCQLToolComponent",
|
||||
"AstraDBToolComponent",
|
||||
"CalculatorComponent",
|
||||
"CalculatorToolComponent",
|
||||
"DuckDuckGoSearchComponent",
|
||||
"ExaSearchToolkit",
|
||||
"GleanSearchAPIComponent",
|
||||
"GoogleSearchAPIComponent",
|
||||
"GoogleSearchAPICore",
|
||||
"GoogleSerperAPIComponent",
|
||||
"GoogleSerperAPICore",
|
||||
"MCPToolsComponent",
|
||||
"PythonCodeStructuredTool",
|
||||
"PythonREPLComponent",
|
||||
"PythonREPLToolComponent",
|
||||
"SearXNGToolComponent",
|
||||
"SearchAPIComponent",
|
||||
"SearchComponent",
|
||||
"SerpAPIComponent",
|
||||
"SerpComponent",
|
||||
"TavilyExtractComponent",
|
||||
"TavilySearchComponent",
|
||||
"TavilySearchToolComponent",
|
||||
"WikidataAPIComponent",
|
||||
"WikidataComponent",
|
||||
"WikipediaAPIComponent",
|
||||
"WikipediaComponent",
|
||||
"WolframAlphaAPIComponent",
|
||||
"YfinanceComponent",
|
||||
"YfinanceToolComponent",
|
||||
]
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ from collections import defaultdict
|
|||
|
||||
from langchain_core.documents import Document
|
||||
|
||||
from langflow.schema import Data
|
||||
from langflow.schema import Data, DataFrame
|
||||
from langflow.schema.message import Message
|
||||
|
||||
|
||||
|
|
@ -139,3 +139,17 @@ def messages_to_text(template: str, messages: Message | list[Message]) -> str:
|
|||
|
||||
formated_messages = [template.format(data=message.model_dump(), **message.model_dump()) for message in messages_]
|
||||
return "\n".join(formated_messages)
|
||||
|
||||
|
||||
def data_to_dataframe(data: Data | list[Data]) -> DataFrame:
|
||||
"""Converts a Data object or a list of Data objects to a DataFrame.
|
||||
|
||||
Args:
|
||||
data (Data | list[Data]): The Data object or list of Data objects to convert.
|
||||
|
||||
Returns:
|
||||
DataFrame: The converted DataFrame.
|
||||
"""
|
||||
if isinstance(data, Data):
|
||||
return DataFrame([data.data])
|
||||
return DataFrame(data=[d.data for d in data])
|
||||
|
|
|
|||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
|
@ -1462,7 +1462,7 @@
|
|||
"show": true,
|
||||
"title_case": false,
|
||||
"type": "code",
|
||||
"value": "from typing import Any\n\nfrom langchain_community.utilities.searchapi import SearchApiAPIWrapper\n\nfrom langflow.custom import Component\nfrom langflow.inputs import DictInput, DropdownInput, IntInput, MultilineInput, SecretStrInput\nfrom langflow.io import Output\nfrom langflow.schema import Data, DataFrame\nfrom langflow.schema.message import Message\n\n\nclass SearchComponent(Component):\n display_name: str = \"Search API\"\n description: str = \"Call the searchapi.io API with result limiting\"\n documentation: str = \"https://www.searchapi.io/docs/google\"\n icon = \"SearchAPI\"\n\n inputs = [\n DropdownInput(name=\"engine\", display_name=\"Engine\", value=\"google\", options=[\"google\", \"bing\", \"duckduckgo\"]),\n SecretStrInput(name=\"api_key\", display_name=\"SearchAPI API Key\", required=True),\n MultilineInput(\n name=\"input_value\",\n display_name=\"Input\",\n tool_mode=True,\n ),\n DictInput(name=\"search_params\", display_name=\"Search parameters\", advanced=True, is_list=True),\n IntInput(name=\"max_results\", display_name=\"Max Results\", value=5, advanced=True),\n IntInput(name=\"max_snippet_length\", display_name=\"Max Snippet Length\", value=100, advanced=True),\n ]\n\n outputs = [\n Output(display_name=\"Data\", name=\"data\", method=\"fetch_content\"),\n Output(display_name=\"Text\", name=\"text\", method=\"fetch_content_text\"),\n Output(display_name=\"DataFrame\", name=\"dataframe\", method=\"as_dataframe\"),\n ]\n\n def _build_wrapper(self):\n return SearchApiAPIWrapper(engine=self.engine, searchapi_api_key=self.api_key)\n\n def run_model(self) -> list[Data]:\n return self.fetch_content()\n\n def fetch_content(self) -> list[Data]:\n wrapper = self._build_wrapper()\n\n def search_func(\n query: str, params: dict[str, Any] | None = None, max_results: int = 5, max_snippet_length: int = 100\n ) -> list[Data]:\n params = params or {}\n full_results = wrapper.results(query=query, **params)\n organic_results = full_results.get(\"organic_results\", [])[:max_results]\n\n return [\n Data(\n text=result.get(\"snippet\", \"\"),\n data={\n \"title\": result.get(\"title\", \"\")[:max_snippet_length],\n \"link\": result.get(\"link\", \"\"),\n \"snippet\": result.get(\"snippet\", \"\")[:max_snippet_length],\n },\n )\n for result in organic_results\n ]\n\n results = search_func(\n self.input_value,\n self.search_params or {},\n self.max_results,\n self.max_snippet_length,\n )\n self.status = results\n return results\n\n def fetch_content_text(self) -> Message:\n data = self.fetch_content()\n result_string = \"\"\n for item in data:\n result_string += item.text + \"\\n\"\n self.status = result_string\n return Message(text=result_string)\n\n def as_dataframe(self) -> DataFrame:\n \"\"\"Convert the search results to a DataFrame.\n\n Returns:\n DataFrame: A DataFrame containing the search results.\n \"\"\"\n data = self.fetch_content()\n return DataFrame(data)\n"
|
||||
"value": "from typing import Any\n\nfrom langchain_community.utilities.searchapi import SearchApiAPIWrapper\n\nfrom langflow.custom import Component\nfrom langflow.helpers.data import data_to_dataframe\nfrom langflow.inputs import DictInput, DropdownInput, IntInput, MultilineInput, SecretStrInput\nfrom langflow.io import Output\nfrom langflow.schema import Data, DataFrame\n\n\nclass SearchComponent(Component):\n display_name: str = \"Search API\"\n description: str = \"Call the searchapi.io API with result limiting\"\n documentation: str = \"https://www.searchapi.io/docs/google\"\n icon = \"SearchAPI\"\n\n inputs = [\n DropdownInput(name=\"engine\", display_name=\"Engine\", value=\"google\", options=[\"google\", \"bing\", \"duckduckgo\"]),\n SecretStrInput(name=\"api_key\", display_name=\"SearchAPI API Key\", required=True),\n MultilineInput(\n name=\"input_value\",\n display_name=\"Input\",\n tool_mode=True,\n ),\n DictInput(name=\"search_params\", display_name=\"Search parameters\", advanced=True, is_list=True),\n IntInput(name=\"max_results\", display_name=\"Max Results\", value=5, advanced=True),\n IntInput(name=\"max_snippet_length\", display_name=\"Max Snippet Length\", value=100, advanced=True),\n ]\n\n outputs = [\n Output(display_name=\"DataFrame\", name=\"dataframe\", method=\"fetch_content_dataframe\"),\n ]\n\n def _build_wrapper(self):\n return SearchApiAPIWrapper(engine=self.engine, searchapi_api_key=self.api_key)\n\n def run_model(self) -> DataFrame:\n return self.fetch_content_dataframe()\n\n def fetch_content(self) -> list[Data]:\n wrapper = self._build_wrapper()\n\n def search_func(\n query: str, params: dict[str, Any] | None = None, max_results: int = 5, max_snippet_length: int = 100\n ) -> list[Data]:\n params = params or {}\n full_results = wrapper.results(query=query, **params)\n organic_results = full_results.get(\"organic_results\", [])[:max_results]\n\n return [\n Data(\n text=result.get(\"snippet\", \"\"),\n data={\n \"title\": result.get(\"title\", \"\")[:max_snippet_length],\n \"link\": result.get(\"link\", \"\"),\n \"snippet\": result.get(\"snippet\", \"\")[:max_snippet_length],\n },\n )\n for result in organic_results\n ]\n\n results = search_func(\n self.input_value,\n self.search_params or {},\n self.max_results,\n self.max_snippet_length,\n )\n self.status = results\n return results\n\n def fetch_content_dataframe(self) -> DataFrame:\n \"\"\"Convert the search results to a DataFrame.\n\n Returns:\n DataFrame: A DataFrame containing the search results.\n \"\"\"\n data = self.fetch_content()\n return data_to_dataframe(data)\n"
|
||||
},
|
||||
"engine": {
|
||||
"_input_type": "DropdownInput",
|
||||
|
|
|
|||
|
|
@ -50,21 +50,21 @@ class TestNewsSearchComponent(ComponentTestBaseWithoutClient):
|
|||
component = NewsSearchComponent(query="OpenAI")
|
||||
result = component.search_news()
|
||||
assert isinstance(result, DataFrame)
|
||||
df = result
|
||||
assert len(df) == 2
|
||||
assert list(df.columns) == ["title", "link", "published", "summary"]
|
||||
assert df.iloc[0]["title"] == "Test News 1"
|
||||
assert df.iloc[1]["title"] == "Test News 2"
|
||||
news_results_df = result
|
||||
assert len(news_results_df) == 2
|
||||
assert list(news_results_df.columns) == ["title", "link", "published", "summary"]
|
||||
assert news_results_df.iloc[0]["title"] == "Test News 1"
|
||||
assert news_results_df.iloc[1]["title"] == "Test News 2"
|
||||
|
||||
def test_news_search_error(self):
|
||||
with patch("requests.get", side_effect=requests.RequestException("Network error")):
|
||||
component = NewsSearchComponent(query="OpenAI")
|
||||
result = component.search_news()
|
||||
assert isinstance(result, DataFrame)
|
||||
df = result
|
||||
assert len(df) == 1
|
||||
assert df.iloc[0]["title"] == "Error"
|
||||
assert "Network error" in df.iloc[0]["summary"]
|
||||
news_results_df = result
|
||||
assert len(news_results_df) == 1
|
||||
assert news_results_df.iloc[0]["title"] == "Error"
|
||||
assert "Network error" in news_results_df.iloc[0]["summary"]
|
||||
|
||||
def test_empty_news_results(self):
|
||||
# Mock empty RSS feed
|
||||
|
|
@ -83,6 +83,6 @@ class TestNewsSearchComponent(ComponentTestBaseWithoutClient):
|
|||
component = NewsSearchComponent(query="OpenAI")
|
||||
result = component.search_news()
|
||||
assert isinstance(result, DataFrame)
|
||||
df = result
|
||||
assert len(df) == 1
|
||||
assert df.iloc[0]["title"] == "No articles found"
|
||||
news_results_df = result
|
||||
assert len(news_results_df) == 1
|
||||
assert news_results_df.iloc[0]["title"] == "No articles found"
|
||||
|
|
|
|||
0
src/backend/tests/unit/components/search/__init__.py
Normal file
0
src/backend/tests/unit/components/search/__init__.py
Normal file
|
|
@ -8,7 +8,7 @@ from tests.base import ComponentTestBaseWithClient
|
|||
class TestArXivComponent(ComponentTestBaseWithClient):
|
||||
def test_component_versions(self, default_kwargs, file_names_mapping):
|
||||
"""Test component compatibility across versions."""
|
||||
from langflow.components.tools.arxiv import ArXivComponent
|
||||
from langflow.components.search.arxiv import ArXivComponent
|
||||
|
||||
# Test current version
|
||||
component = ArXivComponent(**default_kwargs)
|
||||
|
|
@ -31,7 +31,7 @@ class TestArXivComponent(ComponentTestBaseWithClient):
|
|||
|
||||
@pytest.fixture
|
||||
def component_class(self):
|
||||
from langflow.components.tools.arxiv import ArXivComponent
|
||||
from langflow.components.search.arxiv import ArXivComponent
|
||||
|
||||
return ArXivComponent
|
||||
|
||||
|
|
@ -2,7 +2,7 @@ from unittest.mock import patch
|
|||
|
||||
import pandas as pd
|
||||
import pytest
|
||||
from langflow.components.tools import GoogleSearchAPICore
|
||||
from langflow.components.search import GoogleSearchAPICore
|
||||
from langflow.schema import DataFrame
|
||||
|
||||
from tests.base import ComponentTestBaseWithoutClient
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
from unittest.mock import MagicMock, patch
|
||||
|
||||
import pytest
|
||||
from langflow.components.tools import GoogleSerperAPICore
|
||||
from langflow.components.search import GoogleSerperAPICore
|
||||
from langflow.schema import DataFrame
|
||||
|
||||
|
||||
|
|
@ -3,11 +3,9 @@ from unittest.mock import MagicMock, patch
|
|||
import httpx
|
||||
import pytest
|
||||
from langchain_core.tools import ToolException
|
||||
from langflow.components.tools import WikidataComponent
|
||||
from langflow.components.search import WikidataComponent
|
||||
from langflow.custom import Component
|
||||
from langflow.custom.utils import build_custom_component_template
|
||||
from langflow.schema import Data
|
||||
from langflow.schema.message import Message
|
||||
|
||||
# Import the base test class
|
||||
from tests.base import ComponentTestBaseWithoutClient
|
||||
|
|
@ -102,18 +100,3 @@ class TestWikidataComponent(ComponentTestBaseWithoutClient):
|
|||
|
||||
with pytest.raises(ToolException):
|
||||
component.fetch_content()
|
||||
|
||||
def test_fetch_content_text(self, component_class):
|
||||
component = component_class()
|
||||
component.fetch_content = MagicMock(
|
||||
return_value=[
|
||||
Data(text="First result", data={"label": "Label 1"}),
|
||||
Data(text="Second result", data={"label": "Label 2"}),
|
||||
]
|
||||
)
|
||||
|
||||
result = component.fetch_content_text()
|
||||
|
||||
assert isinstance(result, Message)
|
||||
assert "First result" in result.text
|
||||
assert "Second result" in result.text
|
||||
|
|
@ -1,11 +1,9 @@
|
|||
from unittest.mock import MagicMock
|
||||
|
||||
import pytest
|
||||
from langflow.components.tools import WikipediaComponent
|
||||
from langflow.components.search import WikipediaComponent
|
||||
from langflow.custom import Component
|
||||
from langflow.custom.utils import build_custom_component_template
|
||||
from langflow.schema import Data
|
||||
from langflow.schema.message import Message
|
||||
|
||||
# Import the base test class
|
||||
from tests.base import ComponentTestBaseWithoutClient
|
||||
|
|
@ -83,15 +81,6 @@ class TestWikipediaComponent(ComponentTestBaseWithoutClient):
|
|||
assert len(result) == 1
|
||||
assert result[0].text == "Test content"
|
||||
|
||||
def test_fetch_content_text(self, component_class):
|
||||
component = component_class()
|
||||
component.fetch_content = MagicMock(return_value=[Data(text="First result"), Data(text="Second result")])
|
||||
|
||||
result = component.fetch_content_text()
|
||||
|
||||
assert isinstance(result, Message)
|
||||
assert result.text == "First result\nSecond result\n"
|
||||
|
||||
def test_wikipedia_error_handling(self, component_class):
|
||||
component = component_class()
|
||||
# Mock _build_wrapper to raise exception
|
||||
|
|
@ -2,8 +2,7 @@ from unittest.mock import MagicMock, patch
|
|||
|
||||
import pytest
|
||||
from langchain_core.tools import ToolException
|
||||
from langflow.components.tools import YfinanceComponent
|
||||
from langflow.components.tools.yahoo import YahooFinanceMethod
|
||||
from langflow.components.search.yahoo import YahooFinanceMethod, YfinanceComponent
|
||||
from langflow.custom.utils import build_custom_component_template
|
||||
from langflow.schema import Data
|
||||
|
||||
|
|
@ -38,7 +37,7 @@ class TestYfinanceComponent:
|
|||
for input_name in expected_inputs:
|
||||
assert input_name in input_names
|
||||
|
||||
@patch("langflow.components.tools.yahoo.yf.Ticker")
|
||||
@patch("langflow.components.search.yahoo.yf.Ticker")
|
||||
def test_fetch_info(self, mock_ticker, component_class, default_kwargs):
|
||||
component = component_class(**default_kwargs)
|
||||
|
||||
|
|
@ -53,7 +52,7 @@ class TestYfinanceComponent:
|
|||
assert len(result) == 1
|
||||
assert "Apple Inc." in result[0].text
|
||||
|
||||
@patch("langflow.components.tools.yahoo.yf.Ticker")
|
||||
@patch("langflow.components.search.yahoo.yf.Ticker")
|
||||
def test_fetch_news(self, mock_ticker, component_class):
|
||||
component = component_class(symbol="AAPL", method=YahooFinanceMethod.GET_NEWS, num_news=2)
|
||||
|
||||
|
|
@ -2,7 +2,7 @@ from unittest.mock import MagicMock, patch
|
|||
|
||||
import pytest
|
||||
from langchain_core.tools import ToolException
|
||||
from langflow.components.tools import SerpComponent
|
||||
from langflow.components.search import SerpComponent
|
||||
from langflow.custom import Component
|
||||
from langflow.custom.utils import build_custom_component_template
|
||||
from langflow.schema import Data
|
||||
|
|
@ -34,7 +34,7 @@ def test_serpapi_template():
|
|||
assert input_name in input_names
|
||||
|
||||
|
||||
@patch("langflow.components.tools.serp.SerpAPIWrapper")
|
||||
@patch("langflow.components.search.serp.SerpAPIWrapper")
|
||||
def test_fetch_content(mock_serpapi_wrapper):
|
||||
component = SerpComponent()
|
||||
component.serpapi_api_key = "test-key"
|
||||
|
|
@ -81,7 +81,7 @@ def test_error_handling():
|
|||
component.serpapi_api_key = "test-key"
|
||||
component.input_value = "test query"
|
||||
|
||||
with patch("langflow.components.tools.serp.SerpAPIWrapper") as mock_serpapi:
|
||||
with patch("langflow.components.search.serp.SerpAPIWrapper") as mock_serpapi:
|
||||
mock_instance = MagicMock()
|
||||
mock_serpapi.return_value = mock_instance
|
||||
mock_instance.results.side_effect = Exception("API Error")
|
||||
|
|
|
|||
|
|
@ -165,70 +165,84 @@ async def test_build_flow_polling(client, json_memory_chatbot_no_llm, logged_in_
|
|||
self.max_total_events = 50 # Limit to prevent infinite loops
|
||||
self.max_empty_polls = 10 # Maximum number of empty polls before giving up
|
||||
self.poll_timeout = 3.0 # Timeout for each polling request
|
||||
self._closed = False
|
||||
|
||||
async def aiter_lines(self):
|
||||
if self._closed:
|
||||
return
|
||||
|
||||
try:
|
||||
empty_polls = 0
|
||||
total_events = 0
|
||||
end_event_found = False
|
||||
|
||||
while (
|
||||
empty_polls < self.max_empty_polls and total_events < self.max_total_events and not end_event_found
|
||||
empty_polls < self.max_empty_polls
|
||||
and total_events < self.max_total_events
|
||||
and not end_event_found
|
||||
and not self._closed
|
||||
):
|
||||
# Add Accept header for NDJSON
|
||||
headers = {**self.headers, "Accept": "application/x-ndjson"}
|
||||
|
||||
# Set a timeout for the request
|
||||
response = await asyncio.wait_for(
|
||||
self.client.get(
|
||||
f"api/v1/build/{self.job_id}/events?event_delivery=polling",
|
||||
headers=headers,
|
||||
),
|
||||
timeout=self.poll_timeout,
|
||||
)
|
||||
try:
|
||||
# Set a timeout for the request
|
||||
response = await asyncio.wait_for(
|
||||
self.client.get(
|
||||
f"api/v1/build/{self.job_id}/events?event_delivery=polling",
|
||||
headers=headers,
|
||||
),
|
||||
timeout=self.poll_timeout,
|
||||
)
|
||||
|
||||
assert response.status_code == codes.OK
|
||||
if response.status_code != codes.OK:
|
||||
break
|
||||
|
||||
# Get the NDJSON response as text
|
||||
text = response.text
|
||||
# Get the NDJSON response as text
|
||||
text = response.text
|
||||
|
||||
# Skip if response is empty
|
||||
if not text.strip():
|
||||
empty_polls += 1
|
||||
await asyncio.sleep(0.1)
|
||||
continue
|
||||
|
||||
# Reset empty polls counter since we got data
|
||||
empty_polls = 0
|
||||
|
||||
# Process each line as an individual JSON object
|
||||
line_count = 0
|
||||
for line in text.splitlines():
|
||||
if not line.strip():
|
||||
# Skip if response is empty
|
||||
if not text.strip():
|
||||
empty_polls += 1
|
||||
await asyncio.sleep(0.1)
|
||||
continue
|
||||
|
||||
line_count += 1
|
||||
total_events += 1
|
||||
# Reset empty polls counter since we got data
|
||||
empty_polls = 0
|
||||
|
||||
# Check for end event with multiple possible formats
|
||||
if '"event":"end"' in line or '"event": "end"' in line:
|
||||
end_event_found = True
|
||||
# Process each line as an individual JSON object
|
||||
line_count = 0
|
||||
for line in text.splitlines():
|
||||
if not line.strip():
|
||||
continue
|
||||
|
||||
# Validate it's proper JSON before yielding
|
||||
try:
|
||||
json.loads(line) # Test parse to ensure it's valid JSON
|
||||
yield line
|
||||
except json.JSONDecodeError as e:
|
||||
logger.debug(f"WARNING: Skipping invalid JSON: {line}")
|
||||
logger.debug(f"Error: {e}")
|
||||
# Don't yield invalid JSON, but continue processing other lines
|
||||
line_count += 1
|
||||
total_events += 1
|
||||
|
||||
# If we had no events in this batch, count as empty poll
|
||||
if line_count == 0:
|
||||
# Check for end event with multiple possible formats
|
||||
if '"event":"end"' in line or '"event": "end"' in line:
|
||||
end_event_found = True
|
||||
|
||||
# Validate it's proper JSON before yielding
|
||||
try:
|
||||
json.loads(line) # Test parse to ensure it's valid JSON
|
||||
yield line
|
||||
except json.JSONDecodeError as e:
|
||||
logger.debug(f"WARNING: Skipping invalid JSON: {line}")
|
||||
logger.debug(f"Error: {e}")
|
||||
# Don't yield invalid JSON, but continue processing other lines
|
||||
|
||||
# If we had no events in this batch, count as empty poll
|
||||
if line_count == 0:
|
||||
empty_polls += 1
|
||||
|
||||
# Add a small delay to prevent tight polling
|
||||
await asyncio.sleep(0.1)
|
||||
|
||||
except asyncio.TimeoutError:
|
||||
logger.debug(f"WARNING: Polling request timed out after {self.poll_timeout}s")
|
||||
empty_polls += 1
|
||||
|
||||
# Add a small delay to prevent tight polling
|
||||
await asyncio.sleep(0.1)
|
||||
continue
|
||||
|
||||
# If we hit the limit without finding the end event, log a warning
|
||||
if total_events >= self.max_total_events:
|
||||
|
|
@ -241,10 +255,14 @@ async def test_build_flow_polling(client, json_memory_chatbot_no_llm, logged_in_
|
|||
f"WARNING: Reached maximum empty polls ({self.max_empty_polls}) without finding end event"
|
||||
)
|
||||
|
||||
except asyncio.TimeoutError as e:
|
||||
logger.debug(f"ERROR: Polling request timed out after {self.poll_timeout}s")
|
||||
msg = "Build event polling timed out."
|
||||
raise TimeoutError(msg) from e
|
||||
except Exception as e:
|
||||
logger.debug(f"ERROR: Unexpected error during polling: {e!s}")
|
||||
raise
|
||||
finally:
|
||||
self._closed = True
|
||||
|
||||
def close(self):
|
||||
self._closed = True
|
||||
|
||||
polling_response = PollingResponse(client, job_id, logged_in_headers)
|
||||
|
||||
|
|
|
|||
|
|
@ -271,6 +271,7 @@ export const SIDEBAR_BUNDLES = [
|
|||
name: "homeassistant",
|
||||
icon: "HomeAssistant",
|
||||
},
|
||||
{ display_name: "Search", name: "search", icon: "Search" },
|
||||
];
|
||||
|
||||
export const categoryIcons: Record<string, string> = {
|
||||
|
|
|
|||
|
|
@ -125,7 +125,6 @@ test(
|
|||
await expect(page.getByTestId("dataAPI Request")).toBeVisible();
|
||||
await expect(page.getByTestId("helpersMessage History")).toBeVisible();
|
||||
await expect(page.getByTestId("vectorstoresAstra DB")).toBeVisible();
|
||||
await expect(page.getByTestId("toolsSearch API")).toBeVisible();
|
||||
await expect(page.getByTestId("logicSub Flow [Deprecated]")).toBeVisible();
|
||||
|
||||
await page.getByTestId("sidebar-options-trigger").click();
|
||||
|
|
@ -137,19 +136,16 @@ test(
|
|||
await expect(page.getByTestId("logicSub Flow [Deprecated]")).toBeVisible();
|
||||
|
||||
await expect(page.getByTestId("processingSplit Text")).toBeVisible();
|
||||
await expect(page.getByTestId("toolsSearch API")).toBeVisible();
|
||||
|
||||
await page.getByTestId("icon-X").first().click();
|
||||
|
||||
await expect(page.getByTestId("dataAPI Request")).not.toBeVisible();
|
||||
await expect(page.getByTestId("helpersMessage History")).not.toBeVisible();
|
||||
await expect(page.getByTestId("vectorstoresAstra DB")).not.toBeVisible();
|
||||
await expect(page.getByTestId("toolsSearch API")).not.toBeVisible();
|
||||
await expect(
|
||||
page.getByTestId("logicSub Flow [Deprecated]"),
|
||||
).not.toBeVisible();
|
||||
|
||||
await expect(page.getByTestId("processingSplit Text")).not.toBeVisible();
|
||||
await expect(page.getByTestId("toolsSearch API")).not.toBeVisible();
|
||||
},
|
||||
);
|
||||
|
|
|
|||
|
|
@ -11,12 +11,12 @@ test(
|
|||
await page.getByTestId("sidebar-search-input").click();
|
||||
await page.getByTestId("sidebar-search-input").fill("duck");
|
||||
|
||||
await page.waitForSelector('[data-testid="toolsDuckDuckGo Search"]', {
|
||||
await page.waitForSelector('[data-testid="searchDuckDuckGo Search"]', {
|
||||
timeout: 3000,
|
||||
});
|
||||
|
||||
await page
|
||||
.getByTestId("toolsDuckDuckGo Search")
|
||||
.getByTestId("searchDuckDuckGo Search")
|
||||
.hover()
|
||||
.then(async () => {
|
||||
await page
|
||||
|
|
@ -44,7 +44,7 @@ test(
|
|||
) ?? false;
|
||||
|
||||
await page
|
||||
.getByTestId("output-inspection-data-duckduckgosearchcomponent")
|
||||
.getByTestId("output-inspection-dataframe-duckduckgosearchcomponent")
|
||||
.first()
|
||||
.click();
|
||||
|
||||
|
|
|
|||
|
|
@ -49,14 +49,14 @@ test(
|
|||
|
||||
await page.getByTestId("sidebar-search-input").click();
|
||||
await page.getByTestId("sidebar-search-input").fill("search api");
|
||||
await page.waitForSelector('[data-testid="toolsSearch API"]', {
|
||||
await page.waitForSelector('[data-testid="searchSearch API"]', {
|
||||
timeout: 1000,
|
||||
});
|
||||
|
||||
await zoomOut(page, 3);
|
||||
|
||||
await page
|
||||
.getByTestId("toolsSearch API")
|
||||
.getByTestId("searchSearch API")
|
||||
.dragTo(page.locator('//*[@id="react-flow-id"]'), {
|
||||
targetPosition: { x: 100, y: 100 },
|
||||
});
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue