enhanced voice center
This commit is contained in:
parent
9f46ca3b03
commit
6d393f9cf3
8 changed files with 425 additions and 24 deletions
|
|
@ -403,6 +403,61 @@ class ConnectorGoogleSpeech:
|
||||||
"error": str(e)
|
"error": str(e)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async def detectLanguage(self, text: str) -> Dict:
|
||||||
|
"""
|
||||||
|
Detect the language of text using Google Cloud Translation API.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
text: Text to detect language for
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Dict containing detected language code and confidence
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
if not text.strip():
|
||||||
|
logger.warning("⚠️ Empty text provided for language detection")
|
||||||
|
return {
|
||||||
|
"success": False,
|
||||||
|
"language": "",
|
||||||
|
"error": "Empty text provided"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Use a sample of the text (middle 1000 bytes or full text if smaller)
|
||||||
|
textBytes = text.encode('utf-8')
|
||||||
|
if len(textBytes) > 1000:
|
||||||
|
# Take 1000 bytes from the middle
|
||||||
|
startPos = (len(textBytes) - 1000) // 2
|
||||||
|
textSample = textBytes[startPos:startPos + 1000].decode('utf-8', errors='ignore')
|
||||||
|
else:
|
||||||
|
textSample = text
|
||||||
|
|
||||||
|
logger.info(f"🔍 Detecting language for text sample: '{textSample[:100]}...'")
|
||||||
|
|
||||||
|
# Use translation API with auto-detection (source_language=None)
|
||||||
|
result = self.translate_client.translate(
|
||||||
|
textSample,
|
||||||
|
source_language=None, # Auto-detect
|
||||||
|
target_language='en' # Dummy target, we only need detection
|
||||||
|
)
|
||||||
|
|
||||||
|
detectedLanguage = result.get('detectedSourceLanguage', '')
|
||||||
|
|
||||||
|
logger.info(f"✅ Language detected: {detectedLanguage}")
|
||||||
|
|
||||||
|
return {
|
||||||
|
"success": True,
|
||||||
|
"language": detectedLanguage,
|
||||||
|
"confidence": 1.0 # Google Translation API doesn't provide confidence, assume high
|
||||||
|
}
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"❌ Google Cloud Language Detection error: {e}")
|
||||||
|
return {
|
||||||
|
"success": False,
|
||||||
|
"language": "",
|
||||||
|
"error": str(e)
|
||||||
|
}
|
||||||
|
|
||||||
async def speechToTranslatedText(self, audioContent: bytes,
|
async def speechToTranslatedText(self, audioContent: bytes,
|
||||||
fromLanguage: str = "de-DE",
|
fromLanguage: str = "de-DE",
|
||||||
toLanguage: str = "en") -> Dict:
|
toLanguage: str = "en") -> Dict:
|
||||||
|
|
|
||||||
|
|
@ -99,6 +99,44 @@ class VoiceObjects:
|
||||||
|
|
||||||
# Translation Operations
|
# Translation Operations
|
||||||
|
|
||||||
|
async def detectLanguage(self, text: str) -> Dict[str, Any]:
|
||||||
|
"""
|
||||||
|
Detect the language of text using Google Cloud Translation API.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
text: Text to detect language for
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Dict containing detected language code and confidence
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
logger.info(f"🔍 Language detection request: '{text[:100]}...'")
|
||||||
|
|
||||||
|
if not text.strip():
|
||||||
|
return {
|
||||||
|
"success": False,
|
||||||
|
"language": "",
|
||||||
|
"error": "Empty text provided"
|
||||||
|
}
|
||||||
|
|
||||||
|
connector = self._getGoogleSpeechConnector()
|
||||||
|
result = await connector.detectLanguage(text)
|
||||||
|
|
||||||
|
if result["success"]:
|
||||||
|
logger.info(f"✅ Language detected: {result['language']}")
|
||||||
|
else:
|
||||||
|
logger.warning(f"⚠️ Language detection failed: {result.get('error', 'Unknown error')}")
|
||||||
|
|
||||||
|
return result
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"❌ Language detection error: {e}")
|
||||||
|
return {
|
||||||
|
"success": False,
|
||||||
|
"language": "",
|
||||||
|
"error": str(e)
|
||||||
|
}
|
||||||
|
|
||||||
async def translateText(self, text: str, sourceLanguage: str = "de",
|
async def translateText(self, text: str, sourceLanguage: str = "de",
|
||||||
targetLanguage: str = "en") -> Dict[str, Any]:
|
targetLanguage: str = "en") -> Dict[str, Any]:
|
||||||
"""
|
"""
|
||||||
|
|
|
||||||
|
|
@ -115,6 +115,48 @@ async def speech_to_text(
|
||||||
detail=f"Speech-to-text processing failed: {str(e)}"
|
detail=f"Speech-to-text processing failed: {str(e)}"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@router.post("/detect-language")
|
||||||
|
async def detect_language(
|
||||||
|
text: str = Form(...),
|
||||||
|
currentUser: User = Depends(getCurrentUser)
|
||||||
|
):
|
||||||
|
"""Detect the language of text using Google Cloud Translation API."""
|
||||||
|
try:
|
||||||
|
logger.info(f"🔍 Language detection request: '{text[:100]}...'")
|
||||||
|
|
||||||
|
if not text.strip():
|
||||||
|
raise HTTPException(
|
||||||
|
status_code=400,
|
||||||
|
detail="Empty text provided for language detection"
|
||||||
|
)
|
||||||
|
|
||||||
|
# Get voice interface
|
||||||
|
voiceInterface = _getVoiceInterface(currentUser)
|
||||||
|
|
||||||
|
# Perform language detection
|
||||||
|
result = await voiceInterface.detectLanguage(text)
|
||||||
|
|
||||||
|
if result["success"]:
|
||||||
|
return {
|
||||||
|
"success": True,
|
||||||
|
"language": result["language"],
|
||||||
|
"confidence": result.get("confidence", 1.0)
|
||||||
|
}
|
||||||
|
else:
|
||||||
|
raise HTTPException(
|
||||||
|
status_code=400,
|
||||||
|
detail=f"Language detection failed: {result.get('error', 'Unknown error')}"
|
||||||
|
)
|
||||||
|
|
||||||
|
except HTTPException:
|
||||||
|
raise
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"❌ Language detection error: {e}")
|
||||||
|
raise HTTPException(
|
||||||
|
status_code=500,
|
||||||
|
detail=f"Language detection processing failed: {str(e)}"
|
||||||
|
)
|
||||||
|
|
||||||
@router.post("/translate")
|
@router.post("/translate")
|
||||||
async def translate_text(
|
async def translate_text(
|
||||||
text: str = Form(...),
|
text: str = Form(...),
|
||||||
|
|
|
||||||
|
|
@ -172,11 +172,19 @@ class MethodAi(MethodBase):
|
||||||
if aiResponse.documents and len(aiResponse.documents) > 0:
|
if aiResponse.documents and len(aiResponse.documents) > 0:
|
||||||
action_documents = []
|
action_documents = []
|
||||||
for doc in aiResponse.documents:
|
for doc in aiResponse.documents:
|
||||||
|
validationMetadata = {
|
||||||
|
"actionType": "ai.process",
|
||||||
|
"resultType": normalized_result_type,
|
||||||
|
"outputFormat": output_format,
|
||||||
|
"hasDocuments": True,
|
||||||
|
"documentCount": len(aiResponse.documents)
|
||||||
|
}
|
||||||
action_documents.append(ActionDocument(
|
action_documents.append(ActionDocument(
|
||||||
documentName=doc.documentName,
|
documentName=doc.documentName,
|
||||||
documentData=doc.documentData,
|
documentData=doc.documentData,
|
||||||
mimeType=doc.mimeType or output_mime_type,
|
mimeType=doc.mimeType or output_mime_type,
|
||||||
sourceJson=getattr(doc, 'sourceJson', None) # Preserve source JSON for structure validation
|
sourceJson=getattr(doc, 'sourceJson', None), # Preserve source JSON for structure validation
|
||||||
|
validationMetadata=validationMetadata
|
||||||
))
|
))
|
||||||
|
|
||||||
final_documents = action_documents
|
final_documents = action_documents
|
||||||
|
|
@ -188,10 +196,18 @@ class MethodAi(MethodBase):
|
||||||
extension=extension,
|
extension=extension,
|
||||||
action_name="result"
|
action_name="result"
|
||||||
)
|
)
|
||||||
|
validationMetadata = {
|
||||||
|
"actionType": "ai.process",
|
||||||
|
"resultType": normalized_result_type,
|
||||||
|
"outputFormat": output_format,
|
||||||
|
"hasDocuments": False,
|
||||||
|
"contentType": "text"
|
||||||
|
}
|
||||||
action_document = ActionDocument(
|
action_document = ActionDocument(
|
||||||
documentName=meaningful_name,
|
documentName=meaningful_name,
|
||||||
documentData=aiResponse.content,
|
documentData=aiResponse.content,
|
||||||
mimeType=output_mime_type
|
mimeType=output_mime_type,
|
||||||
|
validationMetadata=validationMetadata
|
||||||
)
|
)
|
||||||
final_documents = [action_document]
|
final_documents = [action_document]
|
||||||
|
|
||||||
|
|
@ -288,10 +304,20 @@ class MethodAi(MethodBase):
|
||||||
)
|
)
|
||||||
|
|
||||||
from modules.datamodels.datamodelChat import ActionDocument
|
from modules.datamodels.datamodelChat import ActionDocument
|
||||||
|
validationMetadata = {
|
||||||
|
"actionType": "ai.webResearch",
|
||||||
|
"prompt": prompt,
|
||||||
|
"urlList": parameters.get("urlList", []),
|
||||||
|
"country": parameters.get("country"),
|
||||||
|
"language": parameters.get("language"),
|
||||||
|
"researchDepth": parameters.get("researchDepth", "general"),
|
||||||
|
"resultFormat": "json"
|
||||||
|
}
|
||||||
actionDocument = ActionDocument(
|
actionDocument = ActionDocument(
|
||||||
documentName=meaningfulName,
|
documentName=meaningfulName,
|
||||||
documentData=result,
|
documentData=result,
|
||||||
mimeType="application/json"
|
mimeType="application/json",
|
||||||
|
validationMetadata=validationMetadata
|
||||||
)
|
)
|
||||||
|
|
||||||
return ActionResult.isSuccess(documents=[actionDocument])
|
return ActionResult.isSuccess(documents=[actionDocument])
|
||||||
|
|
@ -490,11 +516,19 @@ class MethodAi(MethodBase):
|
||||||
rendered_content = self._applyCsvOptions(rendered_content, renderOptions)
|
rendered_content = self._applyCsvOptions(rendered_content, renderOptions)
|
||||||
|
|
||||||
from modules.datamodels.datamodelChat import ActionDocument
|
from modules.datamodels.datamodelChat import ActionDocument
|
||||||
|
validationMetadata = {
|
||||||
|
"actionType": "ai.convert",
|
||||||
|
"inputFormat": normalizedInputFormat,
|
||||||
|
"outputFormat": normalizedOutputFormat,
|
||||||
|
"hasSourceJson": True,
|
||||||
|
"conversionType": "direct_rendering"
|
||||||
|
}
|
||||||
actionDoc = ActionDocument(
|
actionDoc = ActionDocument(
|
||||||
documentName=f"{doc.documentName.rsplit('.', 1)[0] if '.' in doc.documentName else doc.documentName}.{normalizedOutputFormat}",
|
documentName=f"{doc.documentName.rsplit('.', 1)[0] if '.' in doc.documentName else doc.documentName}.{normalizedOutputFormat}",
|
||||||
documentData=rendered_content,
|
documentData=rendered_content,
|
||||||
mimeType=mime_type,
|
mimeType=mime_type,
|
||||||
sourceJson=jsonData # Preserve source JSON for structure validation
|
sourceJson=jsonData, # Preserve source JSON for structure validation
|
||||||
|
validationMetadata=validationMetadata
|
||||||
)
|
)
|
||||||
|
|
||||||
return ActionResult.isSuccess(documents=[actionDoc])
|
return ActionResult.isSuccess(documents=[actionDoc])
|
||||||
|
|
|
||||||
|
|
@ -18,6 +18,19 @@ def action(func):
|
||||||
- success: bool
|
- success: bool
|
||||||
- documents: List[ActionDocument]
|
- documents: List[ActionDocument]
|
||||||
- error: str (if success=False)
|
- error: str (if success=False)
|
||||||
|
|
||||||
|
REQUIRED: All ActionDocument instances MUST include validationMetadata for content validation
|
||||||
|
and refinement. Without validationMetadata, results cannot be approved.
|
||||||
|
|
||||||
|
Example validationMetadata structure:
|
||||||
|
validationMetadata = {
|
||||||
|
"actionType": "moduleName.actionName",
|
||||||
|
"param1": value1,
|
||||||
|
"param2": value2,
|
||||||
|
# ... other relevant parameters for validation
|
||||||
|
}
|
||||||
|
|
||||||
|
See MethodBase._createValidationMetadata() for a helper method to create standard metadata.
|
||||||
"""
|
"""
|
||||||
@wraps(func)
|
@wraps(func)
|
||||||
async def wrapper(self, parameters: Dict[str, Any], *args, **kwargs):
|
async def wrapper(self, parameters: Dict[str, Any], *args, **kwargs):
|
||||||
|
|
@ -26,7 +39,14 @@ def action(func):
|
||||||
return wrapper
|
return wrapper
|
||||||
|
|
||||||
class MethodBase:
|
class MethodBase:
|
||||||
"""Base class for all methods"""
|
"""Base class for all methods
|
||||||
|
|
||||||
|
IMPORTANT: All actions that return ActionDocument instances MUST include validationMetadata.
|
||||||
|
This metadata is required for content validation and refinement. Without it, results cannot
|
||||||
|
be approved by the validation system.
|
||||||
|
|
||||||
|
Use _createValidationMetadata() helper method to create standardized metadata structures.
|
||||||
|
"""
|
||||||
|
|
||||||
def __init__(self, services: Any):
|
def __init__(self, services: Any):
|
||||||
"""Initialize method with services object"""
|
"""Initialize method with services object"""
|
||||||
|
|
@ -168,6 +188,44 @@ class MethodBase:
|
||||||
else:
|
else:
|
||||||
return str(type_annotation)
|
return str(type_annotation)
|
||||||
|
|
||||||
|
def _createValidationMetadata(self, actionName: str, **kwargs) -> Dict[str, Any]:
|
||||||
|
"""
|
||||||
|
Helper method to create standardized validationMetadata for ActionDocument instances.
|
||||||
|
|
||||||
|
This method ensures all actions include the required validationMetadata structure
|
||||||
|
for content validation and refinement. Without metadata, results cannot be approved.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
actionName: Name of the action (e.g., "readEmails", "uploadDocument")
|
||||||
|
**kwargs: Additional action-specific metadata fields
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Dictionary with validationMetadata structure including:
|
||||||
|
- actionType: Full action identifier (moduleName.actionName)
|
||||||
|
- All provided kwargs as additional metadata fields
|
||||||
|
|
||||||
|
Example:
|
||||||
|
validationMetadata = self._createValidationMetadata(
|
||||||
|
"readEmails",
|
||||||
|
connectionReference=connectionReference,
|
||||||
|
folder=folder,
|
||||||
|
limit=limit,
|
||||||
|
emailCount=len(emails)
|
||||||
|
)
|
||||||
|
|
||||||
|
ActionDocument(
|
||||||
|
documentName="emails.json",
|
||||||
|
documentData=json.dumps(data),
|
||||||
|
mimeType="application/json",
|
||||||
|
validationMetadata=validationMetadata # REQUIRED
|
||||||
|
)
|
||||||
|
"""
|
||||||
|
metadata = {
|
||||||
|
"actionType": f"{self.name}.{actionName}"
|
||||||
|
}
|
||||||
|
metadata.update(kwargs)
|
||||||
|
return metadata
|
||||||
|
|
||||||
def _generateMeaningfulFileName(self, base_name: str, extension: str, workflow_context: Dict[str, Any] = None, action_name: str = None) -> str:
|
def _generateMeaningfulFileName(self, base_name: str, extension: str, workflow_context: Dict[str, Any] = None, action_name: str = None) -> str:
|
||||||
"""
|
"""
|
||||||
Generate a meaningful file name with round/task/action information.
|
Generate a meaningful file name with round/task/action information.
|
||||||
|
|
|
||||||
|
|
@ -78,11 +78,19 @@ class MethodContext(MethodBase):
|
||||||
"getDocumentIndex"
|
"getDocumentIndex"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
validationMetadata = {
|
||||||
|
"actionType": "context.getDocumentIndex",
|
||||||
|
"resultType": resultType,
|
||||||
|
"workflowId": getattr(workflow, 'id', 'unknown'),
|
||||||
|
"totalDocuments": indexData.get("totalDocuments", 0) if isinstance(indexData, dict) else 0
|
||||||
|
}
|
||||||
|
|
||||||
# Create ActionDocument
|
# Create ActionDocument
|
||||||
document = ActionDocument(
|
document = ActionDocument(
|
||||||
documentName=filename,
|
documentName=filename,
|
||||||
documentData=indexContent,
|
documentData=indexContent,
|
||||||
mimeType="application/json" if resultType == "json" else "text/plain"
|
mimeType="application/json" if resultType == "json" else "text/plain",
|
||||||
|
validationMetadata=validationMetadata
|
||||||
)
|
)
|
||||||
|
|
||||||
return ActionResult.isSuccess(documents=[document])
|
return ActionResult.isSuccess(documents=[document])
|
||||||
|
|
@ -313,10 +321,18 @@ class MethodContext(MethodBase):
|
||||||
documentName = f"document_{i+1:03d}_extracted_{extracted.id}.json"
|
documentName = f"document_{i+1:03d}_extracted_{extracted.id}.json"
|
||||||
|
|
||||||
# Store ContentExtracted object in ActionDocument.documentData
|
# Store ContentExtracted object in ActionDocument.documentData
|
||||||
|
validationMetadata = {
|
||||||
|
"actionType": "context.extractContent",
|
||||||
|
"documentIndex": i,
|
||||||
|
"extractedId": extracted.id,
|
||||||
|
"partCount": len(extracted.parts) if extracted.parts else 0,
|
||||||
|
"originalFileName": originalDoc.fileName if originalDoc and hasattr(originalDoc, 'fileName') else None
|
||||||
|
}
|
||||||
actionDoc = ActionDocument(
|
actionDoc = ActionDocument(
|
||||||
documentName=documentName,
|
documentName=documentName,
|
||||||
documentData=extracted, # ContentExtracted object
|
documentData=extracted, # ContentExtracted object
|
||||||
mimeType="application/json"
|
mimeType="application/json",
|
||||||
|
validationMetadata=validationMetadata
|
||||||
)
|
)
|
||||||
actionDocuments.append(actionDoc)
|
actionDocuments.append(actionDoc)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -465,11 +465,22 @@ class MethodOutlook(MethodBase):
|
||||||
"timestamp": self.services.utils.timestampGetUtc()
|
"timestamp": self.services.utils.timestampGetUtc()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
validationMetadata = {
|
||||||
|
"actionType": "outlook.readEmails",
|
||||||
|
"connectionReference": connectionReference,
|
||||||
|
"folder": folder,
|
||||||
|
"limit": limit,
|
||||||
|
"filter": filter,
|
||||||
|
"emailCount": email_data.get("count", 0),
|
||||||
|
"outputMimeType": outputMimeType
|
||||||
|
}
|
||||||
|
|
||||||
return ActionResult.isSuccess(
|
return ActionResult.isSuccess(
|
||||||
documents=[ActionDocument(
|
documents=[ActionDocument(
|
||||||
documentName=f"outlook_emails_{self._format_timestamp_for_filename()}.json",
|
documentName=f"outlook_emails_{self._format_timestamp_for_filename()}.json",
|
||||||
documentData=json.dumps(result_data, indent=2),
|
documentData=json.dumps(result_data, indent=2),
|
||||||
mimeType="application/json"
|
mimeType="application/json",
|
||||||
|
validationMetadata=validationMetadata
|
||||||
)]
|
)]
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -695,12 +706,23 @@ class MethodOutlook(MethodBase):
|
||||||
"timestamp": self.services.utils.timestampGetUtc()
|
"timestamp": self.services.utils.timestampGetUtc()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
validationMetadata = {
|
||||||
|
"actionType": "outlook.searchEmails",
|
||||||
|
"connectionReference": connectionReference,
|
||||||
|
"query": query,
|
||||||
|
"folder": folder,
|
||||||
|
"limit": limit,
|
||||||
|
"resultCount": search_result.get("count", 0),
|
||||||
|
"outputMimeType": outputMimeType
|
||||||
|
}
|
||||||
|
|
||||||
return ActionResult(
|
return ActionResult(
|
||||||
success=True,
|
success=True,
|
||||||
documents=[ActionDocument(
|
documents=[ActionDocument(
|
||||||
documentName=f"outlook_email_search_{self._format_timestamp_for_filename()}.json",
|
documentName=f"outlook_email_search_{self._format_timestamp_for_filename()}.json",
|
||||||
documentData=json.dumps(result_data, indent=2),
|
documentData=json.dumps(result_data, indent=2),
|
||||||
mimeType="application/json"
|
mimeType="application/json",
|
||||||
|
validationMetadata=validationMetadata
|
||||||
)]
|
)]
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -818,12 +840,22 @@ class MethodOutlook(MethodBase):
|
||||||
"timestamp": self.services.utils.timestampGetUtc()
|
"timestamp": self.services.utils.timestampGetUtc()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
validationMetadata = {
|
||||||
|
"actionType": "outlook.listDrafts",
|
||||||
|
"connectionReference": connectionReference,
|
||||||
|
"folder": folder,
|
||||||
|
"limit": limit,
|
||||||
|
"draftCount": drafts_result.get("count", 0),
|
||||||
|
"outputMimeType": outputMimeType
|
||||||
|
}
|
||||||
|
|
||||||
return ActionResult(
|
return ActionResult(
|
||||||
success=True,
|
success=True,
|
||||||
documents=[ActionDocument(
|
documents=[ActionDocument(
|
||||||
documentName=f"outlook_drafts_list_{self._format_timestamp_for_filename()}.json",
|
documentName=f"outlook_drafts_list_{self._format_timestamp_for_filename()}.json",
|
||||||
documentData=json.dumps(result_data, indent=2),
|
documentData=json.dumps(result_data, indent=2),
|
||||||
mimeType="application/json"
|
mimeType="application/json",
|
||||||
|
validationMetadata=validationMetadata
|
||||||
)]
|
)]
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -928,12 +960,21 @@ class MethodOutlook(MethodBase):
|
||||||
"timestamp": self.services.utils.timestampGetUtc()
|
"timestamp": self.services.utils.timestampGetUtc()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
validationMetadata = {
|
||||||
|
"actionType": "outlook.findDrafts",
|
||||||
|
"connectionReference": connectionReference,
|
||||||
|
"limit": limit,
|
||||||
|
"totalDrafts": drafts_result.get("totalDrafts", 0),
|
||||||
|
"outputMimeType": outputMimeType
|
||||||
|
}
|
||||||
|
|
||||||
return ActionResult(
|
return ActionResult(
|
||||||
success=True,
|
success=True,
|
||||||
documents=[ActionDocument(
|
documents=[ActionDocument(
|
||||||
documentName=f"outlook_drafts_found_{self._format_timestamp_for_filename()}.json",
|
documentName=f"outlook_drafts_found_{self._format_timestamp_for_filename()}.json",
|
||||||
documentData=json.dumps(result_data, indent=2),
|
documentData=json.dumps(result_data, indent=2),
|
||||||
mimeType="application/json"
|
mimeType="application/json",
|
||||||
|
validationMetadata=validationMetadata
|
||||||
)]
|
)]
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -1069,12 +1110,22 @@ class MethodOutlook(MethodBase):
|
||||||
"timestamp": self.services.utils.timestampGetUtc()
|
"timestamp": self.services.utils.timestampGetUtc()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
validationMetadata = {
|
||||||
|
"actionType": "outlook.checkDraftsFolder",
|
||||||
|
"connectionReference": connectionReference,
|
||||||
|
"limit": limit,
|
||||||
|
"totalDrafts": drafts_result.get("totalDrafts", 0),
|
||||||
|
"draftsFolderId": drafts_result.get("draftsFolderId"),
|
||||||
|
"outputMimeType": outputMimeType
|
||||||
|
}
|
||||||
|
|
||||||
return ActionResult(
|
return ActionResult(
|
||||||
success=True,
|
success=True,
|
||||||
documents=[ActionDocument(
|
documents=[ActionDocument(
|
||||||
documentName=f"outlook_drafts_folder_check_{self._format_timestamp_for_filename()}.json",
|
documentName=f"outlook_drafts_folder_check_{self._format_timestamp_for_filename()}.json",
|
||||||
documentData=json.dumps(result_data, indent=2),
|
documentData=json.dumps(result_data, indent=2),
|
||||||
mimeType="application/json"
|
mimeType="application/json",
|
||||||
|
validationMetadata=validationMetadata
|
||||||
)]
|
)]
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -1624,34 +1675,61 @@ Return JSON:
|
||||||
|
|
||||||
# Determine overall success status
|
# Determine overall success status
|
||||||
if successfulEmails == 0:
|
if successfulEmails == 0:
|
||||||
|
validationMetadata = {
|
||||||
|
"actionType": "outlook.sendDraftEmail",
|
||||||
|
"connectionReference": connectionReference,
|
||||||
|
"totalEmails": totalEmails,
|
||||||
|
"successfulEmails": successfulEmails,
|
||||||
|
"failedEmails": failedEmails,
|
||||||
|
"status": "all_failed"
|
||||||
|
}
|
||||||
return ActionResult.isFailure(
|
return ActionResult.isFailure(
|
||||||
error=f"Failed to send all {totalEmails} email(s)",
|
error=f"Failed to send all {totalEmails} email(s)",
|
||||||
documents=[ActionDocument(
|
documents=[ActionDocument(
|
||||||
documentName=f"sent_mail_confirmation_{self._format_timestamp_for_filename()}.json",
|
documentName=f"sent_mail_confirmation_{self._format_timestamp_for_filename()}.json",
|
||||||
documentData=json.dumps(resultData, indent=2),
|
documentData=json.dumps(resultData, indent=2),
|
||||||
mimeType="application/json"
|
mimeType="application/json",
|
||||||
|
validationMetadata=validationMetadata
|
||||||
)]
|
)]
|
||||||
)
|
)
|
||||||
elif failedEmails > 0:
|
elif failedEmails > 0:
|
||||||
# Partial success
|
# Partial success
|
||||||
logger.warning(f"Sent {successfulEmails} out of {totalEmails} emails. {failedEmails} failed.")
|
logger.warning(f"Sent {successfulEmails} out of {totalEmails} emails. {failedEmails} failed.")
|
||||||
|
validationMetadata = {
|
||||||
|
"actionType": "outlook.sendDraftEmail",
|
||||||
|
"connectionReference": connectionReference,
|
||||||
|
"totalEmails": totalEmails,
|
||||||
|
"successfulEmails": successfulEmails,
|
||||||
|
"failedEmails": failedEmails,
|
||||||
|
"status": "partial_success"
|
||||||
|
}
|
||||||
return ActionResult(
|
return ActionResult(
|
||||||
success=True,
|
success=True,
|
||||||
documents=[ActionDocument(
|
documents=[ActionDocument(
|
||||||
documentName=f"sent_mail_confirmation_{self._format_timestamp_for_filename()}.json",
|
documentName=f"sent_mail_confirmation_{self._format_timestamp_for_filename()}.json",
|
||||||
documentData=json.dumps(resultData, indent=2),
|
documentData=json.dumps(resultData, indent=2),
|
||||||
mimeType="application/json"
|
mimeType="application/json",
|
||||||
|
validationMetadata=validationMetadata
|
||||||
)]
|
)]
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
# All successful
|
# All successful
|
||||||
logger.info(f"Successfully sent all {totalEmails} email(s)")
|
logger.info(f"Successfully sent all {totalEmails} email(s)")
|
||||||
|
validationMetadata = {
|
||||||
|
"actionType": "outlook.sendDraftEmail",
|
||||||
|
"connectionReference": connectionReference,
|
||||||
|
"totalEmails": totalEmails,
|
||||||
|
"successfulEmails": successfulEmails,
|
||||||
|
"failedEmails": failedEmails,
|
||||||
|
"status": "all_successful"
|
||||||
|
}
|
||||||
return ActionResult(
|
return ActionResult(
|
||||||
success=True,
|
success=True,
|
||||||
documents=[ActionDocument(
|
documents=[ActionDocument(
|
||||||
documentName=f"sent_mail_confirmation_{self._format_timestamp_for_filename()}.json",
|
documentName=f"sent_mail_confirmation_{self._format_timestamp_for_filename()}.json",
|
||||||
documentData=json.dumps(resultData, indent=2),
|
documentData=json.dumps(resultData, indent=2),
|
||||||
mimeType="application/json"
|
mimeType="application/json",
|
||||||
|
validationMetadata=validationMetadata
|
||||||
)]
|
)]
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -1693,12 +1771,19 @@ Return JSON:
|
||||||
"status": "ready"
|
"status": "ready"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
validationMetadata = {
|
||||||
|
"actionType": "outlook.checkPermissions",
|
||||||
|
"connectionReference": connectionReference,
|
||||||
|
"permissionsStatus": "ready",
|
||||||
|
"hasPermissions": True
|
||||||
|
}
|
||||||
return ActionResult(
|
return ActionResult(
|
||||||
success=True,
|
success=True,
|
||||||
documents=[ActionDocument(
|
documents=[ActionDocument(
|
||||||
documentName=f"outlook_permissions_check_{self._format_timestamp_for_filename()}.json",
|
documentName=f"outlook_permissions_check_{self._format_timestamp_for_filename()}.json",
|
||||||
documentData=json.dumps(result_data, indent=2),
|
documentData=json.dumps(result_data, indent=2),
|
||||||
mimeType="application/json"
|
mimeType="application/json",
|
||||||
|
validationMetadata=validationMetadata
|
||||||
)]
|
)]
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
|
|
@ -1711,12 +1796,19 @@ Return JSON:
|
||||||
"message": "Please re-authenticate your Microsoft connection to get updated permissions."
|
"message": "Please re-authenticate your Microsoft connection to get updated permissions."
|
||||||
}
|
}
|
||||||
|
|
||||||
|
validationMetadata = {
|
||||||
|
"actionType": "outlook.checkPermissions",
|
||||||
|
"connectionReference": connectionReference,
|
||||||
|
"permissionsStatus": "needs_reauthentication",
|
||||||
|
"hasPermissions": False
|
||||||
|
}
|
||||||
return ActionResult(
|
return ActionResult(
|
||||||
success=False,
|
success=False,
|
||||||
documents=[ActionDocument(
|
documents=[ActionDocument(
|
||||||
documentName=f"outlook_permissions_check_{self._format_timestamp_for_filename()}.json",
|
documentName=f"outlook_permissions_check_{self._format_timestamp_for_filename()}.json",
|
||||||
documentData=json.dumps(result_data, indent=2),
|
documentData=json.dumps(result_data, indent=2),
|
||||||
mimeType="application/json"
|
mimeType="application/json",
|
||||||
|
validationMetadata=validationMetadata
|
||||||
)],
|
)],
|
||||||
error="Connection lacks necessary permissions for Outlook operations"
|
error="Connection lacks necessary permissions for Outlook operations"
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -1072,6 +1072,13 @@ class MethodSharepoint(MethodBase):
|
||||||
outputExtension = ".json" # Default
|
outputExtension = ".json" # Default
|
||||||
outputMimeType = "application/json" # Default
|
outputMimeType = "application/json" # Default
|
||||||
|
|
||||||
|
validationMetadata = {
|
||||||
|
"actionType": "sharepoint.findDocumentPath",
|
||||||
|
"searchQuery": searchQuery,
|
||||||
|
"maxResults": maxResults,
|
||||||
|
"totalResults": len(foundDocuments),
|
||||||
|
"hasResults": len(foundDocuments) > 0
|
||||||
|
}
|
||||||
|
|
||||||
return ActionResult(
|
return ActionResult(
|
||||||
success=True,
|
success=True,
|
||||||
|
|
@ -1079,7 +1086,8 @@ class MethodSharepoint(MethodBase):
|
||||||
ActionDocument(
|
ActionDocument(
|
||||||
documentName=f"sharepoint_find_path_{self._format_timestamp_for_filename()}{outputExtension}",
|
documentName=f"sharepoint_find_path_{self._format_timestamp_for_filename()}{outputExtension}",
|
||||||
documentData=json.dumps(resultData, indent=2),
|
documentData=json.dumps(resultData, indent=2),
|
||||||
mimeType=outputMimeType
|
mimeType=outputMimeType,
|
||||||
|
validationMetadata=validationMetadata
|
||||||
)
|
)
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
|
|
@ -1336,19 +1344,40 @@ class MethodSharepoint(MethodBase):
|
||||||
if fileContent and isinstance(fileContent, bytes):
|
if fileContent and isinstance(fileContent, bytes):
|
||||||
# Encode binary content as Base64 string
|
# Encode binary content as Base64 string
|
||||||
base64Content = base64.b64encode(fileContent).decode('utf-8')
|
base64Content = base64.b64encode(fileContent).decode('utf-8')
|
||||||
|
validationMetadata = {
|
||||||
|
"actionType": "sharepoint.readDocuments",
|
||||||
|
"fileName": fileName,
|
||||||
|
"sharepointFileId": resultItem.get("sharepointFileId"),
|
||||||
|
"siteName": resultItem.get("siteName"),
|
||||||
|
"mimeType": mimeType,
|
||||||
|
"contentType": "binary",
|
||||||
|
"size": len(fileContent),
|
||||||
|
"includeMetadata": includeMetadata
|
||||||
|
}
|
||||||
actionDoc = ActionDocument(
|
actionDoc = ActionDocument(
|
||||||
documentName=fileName,
|
documentName=fileName,
|
||||||
documentData=base64Content, # Base64 string for binary files
|
documentData=base64Content, # Base64 string for binary files
|
||||||
mimeType=mimeType
|
mimeType=mimeType,
|
||||||
|
validationMetadata=validationMetadata
|
||||||
)
|
)
|
||||||
actionDocuments.append(actionDoc)
|
actionDocuments.append(actionDoc)
|
||||||
logger.info(f"Stored binary file {fileName} ({len(fileContent)} bytes) as Base64 in ActionDocument")
|
logger.info(f"Stored binary file {fileName} ({len(fileContent)} bytes) as Base64 in ActionDocument")
|
||||||
elif fileContent:
|
elif fileContent:
|
||||||
# Text content - store directly in documentData
|
# Text content - store directly in documentData
|
||||||
|
validationMetadata = {
|
||||||
|
"actionType": "sharepoint.readDocuments",
|
||||||
|
"fileName": fileName,
|
||||||
|
"sharepointFileId": resultItem.get("sharepointFileId"),
|
||||||
|
"siteName": resultItem.get("siteName"),
|
||||||
|
"mimeType": mimeType,
|
||||||
|
"contentType": "text",
|
||||||
|
"includeMetadata": includeMetadata
|
||||||
|
}
|
||||||
actionDoc = ActionDocument(
|
actionDoc = ActionDocument(
|
||||||
documentName=fileName,
|
documentName=fileName,
|
||||||
documentData=fileContent if isinstance(fileContent, str) else str(fileContent),
|
documentData=fileContent if isinstance(fileContent, str) else str(fileContent),
|
||||||
mimeType=mimeType
|
mimeType=mimeType,
|
||||||
|
validationMetadata=validationMetadata
|
||||||
)
|
)
|
||||||
actionDocuments.append(actionDoc)
|
actionDocuments.append(actionDoc)
|
||||||
else:
|
else:
|
||||||
|
|
@ -1366,10 +1395,20 @@ class MethodSharepoint(MethodBase):
|
||||||
if resultItem.get("metadata"):
|
if resultItem.get("metadata"):
|
||||||
docData["metadata"] = resultItem["metadata"]
|
docData["metadata"] = resultItem["metadata"]
|
||||||
|
|
||||||
|
validationMetadata = {
|
||||||
|
"actionType": "sharepoint.readDocuments",
|
||||||
|
"fileName": fileName,
|
||||||
|
"sharepointFileId": resultItem.get("sharepointFileId"),
|
||||||
|
"siteName": resultItem.get("siteName"),
|
||||||
|
"mimeType": mimeType,
|
||||||
|
"contentType": "metadata_only",
|
||||||
|
"includeMetadata": includeMetadata
|
||||||
|
}
|
||||||
actionDoc = ActionDocument(
|
actionDoc = ActionDocument(
|
||||||
documentName=fileName,
|
documentName=fileName,
|
||||||
documentData=json.dumps(docData, indent=2),
|
documentData=json.dumps(docData, indent=2),
|
||||||
mimeType=mimeType
|
mimeType=mimeType,
|
||||||
|
validationMetadata=validationMetadata
|
||||||
)
|
)
|
||||||
actionDocuments.append(actionDoc)
|
actionDocuments.append(actionDoc)
|
||||||
|
|
||||||
|
|
@ -1583,6 +1622,13 @@ class MethodSharepoint(MethodBase):
|
||||||
outputExtension = ".json" # Default
|
outputExtension = ".json" # Default
|
||||||
outputMimeType = "application/json" # Default
|
outputMimeType = "application/json" # Default
|
||||||
|
|
||||||
|
validationMetadata = {
|
||||||
|
"actionType": "sharepoint.readDocuments",
|
||||||
|
"connectionReference": connectionReference,
|
||||||
|
"documentCount": len(readResults),
|
||||||
|
"includeMetadata": includeMetadata,
|
||||||
|
"sitesSearched": len(sites)
|
||||||
|
}
|
||||||
|
|
||||||
return ActionResult(
|
return ActionResult(
|
||||||
success=True,
|
success=True,
|
||||||
|
|
@ -1590,7 +1636,8 @@ class MethodSharepoint(MethodBase):
|
||||||
ActionDocument(
|
ActionDocument(
|
||||||
documentName=f"sharepoint_documents_{self._format_timestamp_for_filename()}{outputExtension}",
|
documentName=f"sharepoint_documents_{self._format_timestamp_for_filename()}{outputExtension}",
|
||||||
documentData=json.dumps(resultData, indent=2),
|
documentData=json.dumps(resultData, indent=2),
|
||||||
mimeType=outputMimeType
|
mimeType=outputMimeType,
|
||||||
|
validationMetadata=validationMetadata
|
||||||
)
|
)
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
|
|
@ -1998,6 +2045,15 @@ class MethodSharepoint(MethodBase):
|
||||||
outputExtension = ".json" # Default
|
outputExtension = ".json" # Default
|
||||||
outputMimeType = "application/json" # Default
|
outputMimeType = "application/json" # Default
|
||||||
|
|
||||||
|
validationMetadata = {
|
||||||
|
"actionType": "sharepoint.uploadDocument",
|
||||||
|
"connectionReference": connectionReference,
|
||||||
|
"uploadPath": uploadPath,
|
||||||
|
"fileNames": fileNames,
|
||||||
|
"uploadCount": len(uploadResults),
|
||||||
|
"successfulUploads": len([r for r in uploadResults if r.get("uploadStatus") == "success"]),
|
||||||
|
"failedUploads": len([r for r in uploadResults if r.get("uploadStatus") == "failed"])
|
||||||
|
}
|
||||||
|
|
||||||
return ActionResult(
|
return ActionResult(
|
||||||
success=True,
|
success=True,
|
||||||
|
|
@ -2005,7 +2061,8 @@ class MethodSharepoint(MethodBase):
|
||||||
ActionDocument(
|
ActionDocument(
|
||||||
documentName=f"sharepoint_upload_{self._format_timestamp_for_filename()}{outputExtension}",
|
documentName=f"sharepoint_upload_{self._format_timestamp_for_filename()}{outputExtension}",
|
||||||
documentData=json.dumps(resultData, indent=2),
|
documentData=json.dumps(resultData, indent=2),
|
||||||
mimeType=outputMimeType
|
mimeType=outputMimeType,
|
||||||
|
validationMetadata=validationMetadata
|
||||||
)
|
)
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
|
|
@ -2459,6 +2516,14 @@ class MethodSharepoint(MethodBase):
|
||||||
outputExtension = ".json" # Default
|
outputExtension = ".json" # Default
|
||||||
outputMimeType = "application/json" # Default
|
outputMimeType = "application/json" # Default
|
||||||
|
|
||||||
|
validationMetadata = {
|
||||||
|
"actionType": "sharepoint.listDocuments",
|
||||||
|
"pathQuery": listQuery,
|
||||||
|
"includeSubfolders": includeSubfolders,
|
||||||
|
"sitesSearched": len(sites),
|
||||||
|
"folderCount": len(listResults),
|
||||||
|
"totalItems": sum(len(result.get("siteResults", [])) for result in listResults)
|
||||||
|
}
|
||||||
|
|
||||||
return ActionResult(
|
return ActionResult(
|
||||||
success=True,
|
success=True,
|
||||||
|
|
@ -2466,7 +2531,8 @@ class MethodSharepoint(MethodBase):
|
||||||
ActionDocument(
|
ActionDocument(
|
||||||
documentName=f"sharepoint_document_list_{self._format_timestamp_for_filename()}{outputExtension}",
|
documentName=f"sharepoint_document_list_{self._format_timestamp_for_filename()}{outputExtension}",
|
||||||
documentData=json.dumps(resultData, indent=2),
|
documentData=json.dumps(resultData, indent=2),
|
||||||
mimeType=outputMimeType
|
mimeType=outputMimeType,
|
||||||
|
validationMetadata=validationMetadata
|
||||||
)
|
)
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue