gateway/modules/features/chatbot/langgraphTools.py
2026-01-30 11:24:24 +01:00

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]