feat:chatbot weiterimplementiert
This commit is contained in:
parent
b6724a797f
commit
6ded28e21a
4 changed files with 667 additions and 888 deletions
|
|
@ -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
|
|
@ -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
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue