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

160 lines
5.9 KiB
Python

# Copyright (c) 2025 Patrick Motsch
# All rights reserved.
"""
Utility functions for the chatbot module.
Contains conversation name generation and other utilities.
"""
import logging
import re
from typing import Optional
from modules.datamodels.datamodelAi import AiCallRequest, AiCallOptions, OperationTypeEnum, ProcessingModeEnum
logger = logging.getLogger(__name__)
async def generate_conversation_name(
services,
userPrompt: str,
userLanguage: str = "en"
) -> str:
"""
Generate a short, descriptive conversation name based on user's prompt.
Args:
services: Services instance with AI access
userPrompt: The user's input prompt
userLanguage: User's preferred language (for prompt localization)
Returns:
Short conversation name (max 60 characters)
"""
try:
truncated_prompt = userPrompt[:200] if len(userPrompt) > 200 else userPrompt
name_prompt = f"""Create a professional conversation title in THE SAME LANGUAGE as the user's question.
Question: "{truncated_prompt}"
Rules:
- Title MUST be in the same language as the question (German→German, French→French, English→English)
- Max 60 characters, no punctuation (?, !, .)
- Professional and concise
- Respond ONLY with the title, nothing else"""
await services.ai.ensureAiObjectsInitialized()
nameRequest = AiCallRequest(
prompt=name_prompt,
options=AiCallOptions(
resultFormat="txt",
operationType=OperationTypeEnum.DATA_GENERATE,
processingMode=ProcessingModeEnum.DETAILED,
temperature=0.7
)
)
nameResponse = await services.ai.callAi(nameRequest)
generated_name = nameResponse.content.strip()
# Extract first line and clean up
generated_name = generated_name.split('\n')[0].strip()
generated_name = re.sub(r'^(Title|Titel|Titre|Name|Name:):\s*', '', generated_name, flags=re.IGNORECASE)
generated_name = re.sub(r'^["\']|["\']$', '', generated_name)
generated_name = re.sub(r'[?!.]+$', '', generated_name) # Remove trailing punctuation
# Apply title case
if generated_name:
words = generated_name.split()
capitalized_words = []
for word in words:
if word.isupper() and len(word) > 1:
capitalized_words.append(word) # Keep acronyms
else:
capitalized_words.append(word.capitalize())
generated_name = " ".join(capitalized_words).strip()
# Validate and truncate if needed
if not generated_name or len(generated_name) < 3:
if userLanguage == "de":
generated_name = "Chatbot Konversation"
elif userLanguage == "fr":
generated_name = "Conversation Chatbot"
else:
generated_name = "Chatbot Conversation"
if len(generated_name) > 60:
truncated = generated_name[:57]
last_space = truncated.rfind(' ')
generated_name = truncated[:last_space] + "..." if last_space > 30 else truncated + "..."
logger.info(f"Generated conversation name: '{generated_name}'")
return generated_name
except Exception as e:
logger.error(f"Error generating conversation name: {e}", exc_info=True)
if userLanguage == "de":
return "Chatbot Konversation"
elif userLanguage == "fr":
return "Conversation Chatbot"
else:
return "Chatbot Conversation"
def get_empty_results_retry_instructions(empty_count: int) -> str:
"""
Get retry instructions when empty results are detected.
Args:
empty_count: Number of queries that returned empty results
Returns:
Formatted instructions string
"""
if empty_count == 0:
return ""
return f"""
⚠️ LEERE ERGEBNISSE ERKANNT ⚠️
Es wurden {empty_count} Query(s) ausgeführt, die 0 Zeilen zurückgegeben haben. Versuche alternative Strategien.
⚠️ WICHTIG - MAXIMAL 5 QUERIES FÜR PERFORMANCE ⚠️
Erstelle MAXIMAL 5 alternative SQL-Queries mit komplett anderen Strategien:
1. **Breitere Suche ohne Zertifizierung**: Entferne Zertifizierungsfilter komplett
- Beispiel: Suche nur nach Netzgerät + einphasig + 10A (ohne UL)
- Suche in Artikelbezeichnung, Artikelbeschrieb, Keywords
2. **Erweiterte Suche nach Netzgeräten mit Ampere-Angaben**: Breitere Ampere-Patterns
- Beispiel: (Netzteil OR Netzgerät) AND (10A OR 15A OR 20A OR Ampere)
- Suche auch nach "Ampere" als Begriff, nicht nur Zahlen
3. **Breitere UL-Suche bei Netzgeräten**: Suche UL in allen Feldern
- Beispiel: (UL OR UL-zertifiziert) AND (Netzgerät OR Netzteil OR Power Supply)
- Suche auch in Keywords-Feld
4. **Netzgeräte mit ≥10A ohne weitere Filter**: Minimaler Filter
- Beispiel: (Netzgerät OR Netzteil) AND (10A OR 15A OR 20A)
- Keine Filter auf einphasig oder Zertifizierung
5. **Zertifizierte Netzgeräte allgemein**: Breite Zertifizierungs-Suche
- Beispiel: (UL OR CE OR TÜV OR certified OR zertifiziert) AND (Netzgerät OR Netzteil)
6. **COUNT-Abfrage für Statistik**: Prüfe ob überhaupt Artikel existieren
- SELECT COUNT(*) WHERE (Netzgerät OR Netzteil) AND (10A OR 15A OR 20A)
7. **Spezifische Suche nach einphasigen Netzgeräten**: Ohne Zertifizierung
- Beispiel: (einphasig OR 1-phasig OR single phase) AND (Netzgerät OR Netzteil)
8. **Fallback mit minimalen Filtern**: Nur Hauptkriterien
- Beispiel: Netzgerät AND (10A OR 15A OR 20A) - keine weiteren Filter
WICHTIG:
- Erstelle MAXIMAL 5 Queries mit unterschiedlichen Strategien (für Performance)
- Verwende breitere OR-Bedingungen für alternative Begriffe
- Entferne zu spezifische Filter, die möglicherweise keine Treffer finden
- Suche in Artikelbezeichnung, Artikelbeschrieb UND Keywords-Feld
"""