Tai Truong
fix readme
d202ada
from typing import Any
from langchain.tools import StructuredTool
from langchain_community.utilities.serpapi import SerpAPIWrapper
from langchain_core.tools import ToolException
from loguru import logger
from pydantic import BaseModel, Field
from langflow.base.langchain_utilities.model import LCToolComponent
from langflow.field_typing import Tool
from langflow.inputs import DictInput, IntInput, MultilineInput, SecretStrInput
from langflow.schema import Data
class SerpAPISchema(BaseModel):
"""Schema for SerpAPI search parameters."""
query: str = Field(..., description="The search query")
params: dict[str, Any] | None = Field(
default={
"engine": "google",
"google_domain": "google.com",
"gl": "us",
"hl": "en",
},
description="Additional search parameters",
)
max_results: int = Field(5, description="Maximum number of results to return")
max_snippet_length: int = Field(100, description="Maximum length of each result snippet")
class SerpAPIComponent(LCToolComponent):
display_name = "Serp Search API"
description = "Call Serp Search API with result limiting"
name = "SerpAPI"
icon = "SerpSearch"
inputs = [
SecretStrInput(name="serpapi_api_key", display_name="SerpAPI API Key", required=True),
MultilineInput(
name="input_value",
display_name="Input",
),
DictInput(name="search_params", display_name="Parameters", advanced=True, is_list=True),
IntInput(name="max_results", display_name="Max Results", value=5, advanced=True),
IntInput(name="max_snippet_length", display_name="Max Snippet Length", value=100, advanced=True),
]
def _build_wrapper(self, params: dict[str, Any] | None = None) -> SerpAPIWrapper:
"""Build a SerpAPIWrapper with the provided parameters."""
params = params or {}
if params:
return SerpAPIWrapper(
serpapi_api_key=self.serpapi_api_key,
params=params,
)
return SerpAPIWrapper(serpapi_api_key=self.serpapi_api_key)
def build_tool(self) -> Tool:
wrapper = self._build_wrapper(self.search_params)
def search_func(
query: str, params: dict[str, Any] | None = None, max_results: int = 5, max_snippet_length: int = 100
) -> list[dict[str, Any]]:
try:
local_wrapper = wrapper
if params:
local_wrapper = self._build_wrapper(params)
full_results = local_wrapper.results(query)
organic_results = full_results.get("organic_results", [])[:max_results]
limited_results = []
for result in organic_results:
limited_result = {
"title": result.get("title", "")[:max_snippet_length],
"link": result.get("link", ""),
"snippet": result.get("snippet", "")[:max_snippet_length],
}
limited_results.append(limited_result)
except Exception as e:
error_message = f"Error in SerpAPI search: {e!s}"
logger.debug(error_message)
raise ToolException(error_message) from e
return limited_results
tool = StructuredTool.from_function(
name="serp_search_api",
description="Search for recent results using SerpAPI with result limiting",
func=search_func,
args_schema=SerpAPISchema,
)
self.status = "SerpAPI Tool created"
return tool
def run_model(self) -> list[Data]:
tool = self.build_tool()
try:
results = tool.run(
{
"query": self.input_value,
"params": self.search_params or {},
"max_results": self.max_results,
"max_snippet_length": self.max_snippet_length,
}
)
data_list = [Data(data=result, text=result.get("snippet", "")) for result in results]
except Exception as e: # noqa: BLE001
logger.opt(exception=True).debug("Error running SerpAPI")
self.status = f"Error: {e}"
return [Data(data={"error": str(e)}, text=str(e))]
self.status = data_list # type: ignore[assignment]
return data_list