166 lines
6.2 KiB
Python
166 lines
6.2 KiB
Python
# Copyright (c) 2025 Patrick Motsch
|
|
# All rights reserved.
|
|
"""
|
|
LangGraph-compatible tools for chatbot.
|
|
Wraps connectors and external services as LangGraph tools.
|
|
"""
|
|
|
|
import logging
|
|
import json
|
|
from typing import Optional
|
|
|
|
from langchain_core.tools import tool
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
|
@tool
|
|
def send_streaming_message(message: str) -> str:
|
|
"""Send a streaming message to the user to provide updates during processing.
|
|
|
|
Use this tool to send short status updates to the user while you are working
|
|
on their request. This helps keep the user informed about what you are doing.
|
|
|
|
Args:
|
|
message: A short German message describing what you are currently doing.
|
|
Examples: "Durchsuche Datenbank nach Lampen, LED, Leuchten, und Ähnlichem."
|
|
"Suche im Internet nach Produktinformationen."
|
|
"Analysiere Suchergebnisse."
|
|
|
|
Returns:
|
|
A confirmation that the message was sent.
|
|
"""
|
|
# This tool doesn't actually do anything - it's just for the AI to signal
|
|
# what it's doing to the frontend via the tool call mechanism
|
|
return f"Status-Update gesendet: {message}"
|
|
|
|
|
|
def create_sql_tool(connector_instance):
|
|
"""
|
|
Create a LangGraph-compatible SQL tool using a connector instance.
|
|
|
|
Args:
|
|
connector_instance: PreprocessorConnector or similar connector instance
|
|
|
|
Returns:
|
|
LangChain tool for SQL queries
|
|
"""
|
|
# Store connector in closure
|
|
connector = connector_instance
|
|
|
|
@tool
|
|
async def execute_sql_query(query: str) -> str:
|
|
"""Execute a SQL SELECT query on the database.
|
|
|
|
This tool allows you to query the database to find articles, prices,
|
|
inventory levels, and other information.
|
|
|
|
Args:
|
|
query: A valid SQL SELECT query. Only SELECT queries are allowed.
|
|
Use double quotes for column names with spaces or special characters.
|
|
Example: SELECT "Artikelnummer", "Artikelbezeichnung" FROM Artikel
|
|
WHERE "Artikelbezeichnung" LIKE '%Lampe%' LIMIT 20
|
|
|
|
Returns:
|
|
Query results as formatted string with data rows
|
|
"""
|
|
try:
|
|
logger.info(f"Executing SQL query via connector: {query[:100]}...")
|
|
|
|
# Ensure connector is initialized
|
|
if connector is None:
|
|
return "Error: Database connector not initialized"
|
|
|
|
# Execute query
|
|
result = await connector.executeQuery(query, return_json=True)
|
|
|
|
if isinstance(result, dict):
|
|
# Return formatted text result
|
|
text_result = result.get("text", "Query executed successfully but returned no results.")
|
|
# Also include data count if available
|
|
data = result.get("data", [])
|
|
if data:
|
|
text_result += f"\n\nFound {len(data)} row(s)."
|
|
return text_result
|
|
else:
|
|
# Return string result directly
|
|
return str(result)
|
|
|
|
except Exception as e:
|
|
error_msg = f"Error executing SQL query: {str(e)}"
|
|
logger.error(error_msg, exc_info=True)
|
|
return error_msg
|
|
|
|
# Set tool metadata for better AI understanding
|
|
execute_sql_query.name = "execute_sql_query"
|
|
execute_sql_query.description = """Execute a SQL SELECT query on the database.
|
|
|
|
Use this tool to search for articles, check prices, inventory levels, suppliers, etc.
|
|
Only SELECT queries are allowed. Use double quotes for column names with spaces.
|
|
|
|
Database tables: Artikel, Einkaufspreis_neu, Lagerplatz_Artikel, Lagerplatz
|
|
|
|
Example queries:
|
|
- SELECT "Artikelnummer", "Artikelbezeichnung" FROM Artikel WHERE "Artikelbezeichnung" LIKE '%Lampe%'
|
|
- SELECT a."Artikelnummer", e."EP_CHF" FROM Artikel a LEFT JOIN Einkaufspreis_neu e ON a."I_ID" = e."ARTIKEL"
|
|
- SELECT a."Artikelnummer", l."S_IST_BESTAND" FROM Artikel a LEFT JOIN Lagerplatz_Artikel l ON a."I_ID" = l."R_ARTIKEL"
|
|
"""
|
|
|
|
return execute_sql_query
|
|
|
|
|
|
def create_tavily_tools(tavily_api_key: Optional[str] = None, enable_web_research: bool = True):
|
|
"""
|
|
Create Tavily search tools for web research.
|
|
|
|
Args:
|
|
tavily_api_key: Tavily API key (if None, tools will return error messages)
|
|
enable_web_research: Whether web research is enabled
|
|
|
|
Returns:
|
|
List of Tavily tools (search and extract)
|
|
"""
|
|
tools = []
|
|
|
|
if not enable_web_research or not tavily_api_key:
|
|
# Return dummy tools that explain web research is disabled
|
|
@tool
|
|
def tavily_search_disabled(query: str) -> str:
|
|
"""Web research is disabled for this chatbot instance."""
|
|
return "Web research is not enabled for this chatbot instance."
|
|
|
|
@tool
|
|
def tavily_extract_disabled(urls: str) -> str:
|
|
"""Web research is disabled for this chatbot instance."""
|
|
return "Web research is not enabled for this chatbot instance."
|
|
|
|
return [tavily_search_disabled, tavily_extract_disabled]
|
|
|
|
try:
|
|
from langchain_tavily import TavilySearchResults, TavilyExtract
|
|
|
|
# Create Tavily search tool
|
|
tavily_search = TavilySearchResults(
|
|
tavily_api_key=tavily_api_key,
|
|
max_results=5
|
|
)
|
|
|
|
# Create Tavily extract tool
|
|
tavily_extract = TavilyExtract(tavily_api_key=tavily_api_key)
|
|
|
|
return [tavily_search, tavily_extract]
|
|
|
|
except ImportError:
|
|
logger.warning("langchain_tavily not available, creating dummy tools")
|
|
|
|
@tool
|
|
def tavily_search_fallback(query: str) -> str:
|
|
"""Tavily search tool (not available - langchain_tavily not installed)."""
|
|
return "Tavily search is not available. Please install langchain_tavily package."
|
|
|
|
@tool
|
|
def tavily_extract_fallback(urls: str) -> str:
|
|
"""Tavily extract tool (not available - langchain_tavily not installed)."""
|
|
return "Tavily extract is not available. Please install langchain_tavily package."
|
|
|
|
return [tavily_search_fallback, tavily_extract_fallback]
|