feat:chatbot weiterimplementiert

This commit is contained in:
Ida Dittrich 2026-01-05 15:12:13 +01:00
parent b6724a797f
commit 6ded28e21a
4 changed files with 667 additions and 888 deletions

View file

@ -39,15 +39,17 @@ class PreprocessorConnector:
logger.info("PreprocessorConnector initialized") logger.info("PreprocessorConnector initialized")
async def executeQuery(self, sql_query: str) -> str: async def executeQuery(self, sql_query: str, return_json: bool = False):
""" """
Execute a SQL query via the preprocessing API. Execute a SQL query via the preprocessing API.
Args: Args:
sql_query: SQL SELECT query to execute sql_query: SQL SELECT query to execute
return_json: If True, returns dict with 'text' and 'data' keys. If False, returns formatted string.
Returns: Returns:
Formatted result string with query results If return_json=False: Formatted result string with query results
If return_json=True: Dict with 'text' (formatted string) and 'data' (raw JSON data list)
Raises: Raises:
ValueError: If query is invalid or contains forbidden keywords ValueError: If query is invalid or contains forbidden keywords
@ -57,16 +59,22 @@ class PreprocessorConnector:
# Validate query # Validate query
validation_error = self._validateQuery(sql_query) validation_error = self._validateQuery(sql_query)
if validation_error: if validation_error:
if return_json:
return {"text": validation_error, "data": []}
return validation_error return validation_error
# Check configuration # Check configuration
if not self.api_key: if not self.api_key:
error_msg = "Error: PP_QUERY_API_KEY not configured" error_msg = "Error: PP_QUERY_API_KEY not configured"
logger.error(error_msg) logger.error(error_msg)
if return_json:
return {"text": error_msg, "data": []}
return error_msg return error_msg
if not self.base_url: if not self.base_url:
error_msg = "Error: PP_QUERY_BASE_URL not configured" error_msg = "Error: PP_QUERY_BASE_URL not configured"
logger.error(error_msg) logger.error(error_msg)
if return_json:
return {"text": error_msg, "data": []}
return error_msg return error_msg
# Make HTTP POST request to preprocessing API # Make HTTP POST request to preprocessing API
@ -86,7 +94,10 @@ class PreprocessorConnector:
# Parse response # Parse response
if not result.get("success"): if not result.get("success"):
error_message = result.get("message", "Unknown error") error_message = result.get("message", "Unknown error")
return f"Query failed: {error_message}" error_text = f"Query failed: {error_message}"
if return_json:
return {"text": error_text, "data": []}
return error_text
# Format results # Format results
data = result.get("data", []) data = result.get("data", [])
@ -97,7 +108,10 @@ class PreprocessorConnector:
# Format results as string # Format results as string
if not display_data: if not display_data:
return f"Query executed successfully. Returned {row_count} rows (no data)." result_text = f"Query executed successfully. Returned {row_count} rows (no data)."
if return_json:
return {"text": result_text, "data": data}
return result_text
# Format each row # Format each row
results = [] results = []
@ -110,6 +124,8 @@ class PreprocessorConnector:
+ "\n".join(results) + "\n".join(results)
) )
if return_json:
return {"text": result_text, "data": data}
return result_text return result_text
except httpx.HTTPStatusError as e: except httpx.HTTPStatusError as e:

File diff suppressed because it is too large Load diff

View file

@ -1004,7 +1004,9 @@ class ChatObjects:
# Create documents in normalized documents table # Create documents in normalized documents table
created_documents = [] created_documents = []
for doc_data in documents_to_create: logger.debug(f"Creating {len(documents_to_create)} document(s) for message {createdMessage['id']}")
for idx, doc_data in enumerate(documents_to_create):
try:
# Normalize to plain dict before assignment # Normalize to plain dict before assignment
if isinstance(doc_data, ChatDocument): if isinstance(doc_data, ChatDocument):
doc_dict = doc_data.model_dump() doc_dict = doc_data.model_dump()
@ -1014,14 +1016,24 @@ class ChatObjects:
# Attempt to coerce to ChatDocument then dump # Attempt to coerce to ChatDocument then dump
try: try:
doc_dict = ChatDocument(**doc_data).model_dump() doc_dict = ChatDocument(**doc_data).model_dump()
except Exception: except Exception as e:
logger.error("Invalid document data type for message creation") logger.error(f"Invalid document data type for message creation (document {idx + 1}/{len(documents_to_create)}): {e}")
continue continue
# Ensure messageId is set
doc_dict["messageId"] = createdMessage["id"] doc_dict["messageId"] = createdMessage["id"]
logger.debug(f"Creating document {idx + 1}/{len(documents_to_create)}: fileName={doc_dict.get('fileName', 'unknown')}, fileId={doc_dict.get('fileId', 'unknown')}, messageId={doc_dict.get('messageId', 'unknown')}")
created_doc = self.createDocument(doc_dict) created_doc = self.createDocument(doc_dict)
if created_doc: if created_doc:
created_documents.append(created_doc) created_documents.append(created_doc)
logger.debug(f"Successfully created document {idx + 1}/{len(documents_to_create)}: {created_doc.fileName} (id: {created_doc.id})")
else:
logger.error(f"Failed to create document {idx + 1}/{len(documents_to_create)}: createDocument returned None for fileName={doc_dict.get('fileName', 'unknown')}")
except Exception as e:
logger.error(f"Error processing document {idx + 1}/{len(documents_to_create)}: {e}", exc_info=True)
logger.info(f"Created {len(created_documents)}/{len(documents_to_create)} document(s) for message {createdMessage['id']}")
# Convert to ChatMessage model # Convert to ChatMessage model
chat_message = ChatMessage( chat_message = ChatMessage(
@ -1256,12 +1268,18 @@ class ChatObjects:
try: try:
# Validate and normalize document data to dict # Validate and normalize document data to dict
document = ChatDocument(**documentData) document = ChatDocument(**documentData)
logger.debug(f"Creating document in database: fileName={document.fileName}, fileId={document.fileId}, messageId={document.messageId}")
created = self.db.recordCreate(ChatDocument, document.model_dump()) created = self.db.recordCreate(ChatDocument, document.model_dump())
if created:
return ChatDocument(**created) created_doc = ChatDocument(**created)
logger.debug(f"Successfully created document in database: {created_doc.fileName} (id: {created_doc.id})")
return created_doc
else:
logger.error(f"Failed to create document in database: recordCreate returned None for fileName={document.fileName}")
return None
except Exception as e: except Exception as e:
logger.error(f"Error creating message document: {str(e)}") logger.error(f"Error creating message document: {str(e)}", exc_info=True)
return None return None

View file

@ -44,12 +44,18 @@ async def process(self, parameters: Dict[str, Any]) -> ActionResult:
# Convert to DocumentReferenceList if needed # Convert to DocumentReferenceList if needed
if documentListParam is None: if documentListParam is None:
documentList = DocumentReferenceList(references=[]) documentList = DocumentReferenceList(references=[])
logger.debug(f"ai.process: documentList is None, using empty DocumentReferenceList")
elif isinstance(documentListParam, DocumentReferenceList): elif isinstance(documentListParam, DocumentReferenceList):
documentList = documentListParam documentList = documentListParam
logger.info(f"ai.process: Received DocumentReferenceList with {len(documentList.references)} references")
for idx, ref in enumerate(documentList.references):
logger.info(f" Reference {idx + 1}: documentId={ref.documentId}, type={type(ref).__name__}")
elif isinstance(documentListParam, str): elif isinstance(documentListParam, str):
documentList = DocumentReferenceList.from_string_list([documentListParam]) documentList = DocumentReferenceList.from_string_list([documentListParam])
logger.info(f"ai.process: Converted string to DocumentReferenceList with {len(documentList.references)} references")
elif isinstance(documentListParam, list): elif isinstance(documentListParam, list):
documentList = DocumentReferenceList.from_string_list(documentListParam) documentList = DocumentReferenceList.from_string_list(documentListParam)
logger.info(f"ai.process: Converted list to DocumentReferenceList with {len(documentList.references)} references")
else: else:
logger.error(f"Invalid documentList type: {type(documentListParam)}") logger.error(f"Invalid documentList type: {type(documentListParam)}")
documentList = DocumentReferenceList(references=[]) documentList = DocumentReferenceList(references=[])
@ -152,9 +158,16 @@ async def process(self, parameters: Dict[str, Any]) -> ActionResult:
) )
else: else:
# Full mode: use unified callAiContent method # Full mode: use unified callAiContent method
# For document generation (xlsx, docx, pdf, etc.), use DATA_GENERATE with document intent
from modules.datamodels.datamodelAi import OperationTypeEnum
# Always use DATA_GENERATE with document intent for ai.process
# This ensures proper document generation pipeline is used
options = AiCallOptions( options = AiCallOptions(
resultFormat=output_format resultFormat=output_format,
operationType=OperationTypeEnum.DATA_GENERATE
) )
generation_intent = "document"
# Update progress - calling AI # Update progress - calling AI
self.services.chat.progressLogUpdate(operationId, 0.6, "Calling AI") self.services.chat.progressLogUpdate(operationId, 0.6, "Calling AI")
@ -171,17 +184,23 @@ async def process(self, parameters: Dict[str, Any]) -> ActionResult:
options=options, options=options,
contentParts=contentParts, # Pre-extracted ContentParts contentParts=contentParts, # Pre-extracted ContentParts
outputFormat=output_format, outputFormat=output_format,
parentOperationId=operationId parentOperationId=operationId,
generationIntent=generation_intent
) )
else: else:
# Pass documentList - callAiContent handles Phases 5A-5E internally # Pass documentList - callAiContent handles Phases 5A-5E internally
# This includes automatic detection of ContentExtracted documents # This includes automatic detection of ContentExtracted documents
logger.info(f"ai.process: Calling callAiContent with {len(documentList.references)} document references")
if documentList.references:
for idx, ref in enumerate(documentList.references):
logger.info(f" Passing reference {idx + 1}: documentId={ref.documentId}")
aiResponse = await self.services.ai.callAiContent( aiResponse = await self.services.ai.callAiContent(
prompt=aiPrompt, prompt=aiPrompt,
options=options, options=options,
documentList=documentList, # callAiContent macht Phasen 5A-5E documentList=documentList, # callAiContent macht Phasen 5A-5E
outputFormat=output_format, outputFormat=output_format,
parentOperationId=operationId parentOperationId=operationId,
generationIntent=generation_intent
) )
# Update progress - processing result # Update progress - processing result