revised validation logic

This commit is contained in:
ValueOn AG 2025-11-02 23:31:10 +01:00
parent f4b08a0bb6
commit 69fa367584
9 changed files with 333 additions and 34 deletions

View file

@ -0,0 +1,285 @@
# Pydantic Class Enhancement Proposal
## Format Tracking & Validation Alignment
**Date:** 2025-11-02
**Purpose:** Align validation logic with prompt requirements, enable workflow-level validation, and track expected file formats
**Simplified Approach:** Use existing document metadata (name, size, format, mimeType) - no summary fields needed
---
## Executive Summary
This proposal addresses:
1. **Validation alignment**: What prompts ask for matches what validators check
2. **Workflow-level validation**: Check ALL deliverables from ALL tasks against original user request
3. **Format tracking**: Track expected formats (list) at workflow and task levels
4. **Adaptive task planning**: Next task uses ALL workflow data (messages, document metadata) to refine objective
**Key Simplification:** Actions deliver documents with metadata (as today). No summary fields needed - use existing document metadata.
---
## 1. ActionResult Class Changes
**File:** `gateway/modules/datamodels/datamodelChat.py` (lines 483-521)
### NO CHANGES NEEDED
**Current Structure (KEEP ALL - ALL USED):**
- ✅ `success: bool` - Used by validation
- ✅ `error: Optional[str]` - Used for error handling
- ✅ `documents: List[ActionDocument]` - Contains document metadata (name, data, mimeType)
- ✅ `resultLabel: Optional[str]` - Used for document routing
**Documents already provide all needed metadata:**
- `documentName` - File name
- `documentData` - Content
- `mimeType` - MIME type (can derive format from this)
**No summary field needed** - document metadata is sufficient.
---
## 2. TaskResult Class Changes
**File:** `gateway/modules/datamodels/datamodelChat.py` (lines 718-736)
### NO CHANGES NEEDED
**Current Structure (KEEP ALL - ALL USED):**
- ✅ `taskId: str` - Task identification
- ✅ `status: TaskStatus` - Task status tracking
- ✅ `success: bool` - Success flag
- ✅ `feedback: Optional[str]` - Task feedback
- ✅ `error: Optional[str]` - Error message
**Document metadata available from workflow:**
- Can extract delivered formats from documents in workflow messages
- No need to store separately - use existing document metadata
---
## 3. TaskStep Class Changes
**File:** `gateway/modules/datamodels/datamodelChat.py` (lines 790-825)
### Modify
- Change `expectedFormat: Optional[str]``expectedFormats: Optional[List[str]]`
- Keep `dataType` and `qualityRequirements` as-is
### Modified Class:
```python
class TaskStep(BaseModel):
id: str
objective: str
dependencies: Optional[list[str]] = Field(default_factory=list)
successCriteria: Optional[list[str]] = Field(default_factory=list)
estimatedComplexity: Optional[str] = None
userMessage: Optional[str] = Field(
None, description="User-friendly message in user's language"
)
# Format details extracted from intent analysis
dataType: Optional[str] = Field(
None, description="Expected data type (text, numbers, documents, etc.)"
)
expectedFormats: Optional[List[str]] = Field(
None, description="Expected output file format extensions (e.g., ['docx', 'pdf', 'xlsx']). Use actual file extensions, not conceptual terms."
)
qualityRequirements: Optional[Dict[str, Any]] = Field(
None, description="Quality requirements and constraints"
)
```
### Register Labels
Update:
```python
"expectedFormats": {"en": "Expected Formats", "fr": "Formats attendus"}
```
---
## 4. ChatWorkflow Class Changes (for Workflow-Level Tracking)
**File:** `gateway/modules/datamodels/datamodelChat.py` (find ChatWorkflow class)
### Add (if not exists)
```python
expectedFormats: Optional[List[str]] = Field(
None,
description="List of expected file format extensions from user request (e.g., ['xlsx', 'pdf']). Extracted during intent analysis."
)
```
Note: `_workflowIntent` is already stored as a dict (not a model field), so `expectedFormats` can be extracted from there, but having it as an explicit field makes it easier to query.
---
## 5. ActionItem Class Review
**File:** `gateway/modules/datamodels/datamodelChat.py` (lines 652-715)
### Current Structure (ALL USED - KEEP):
- ✅ `id: str` - Used for action identification
- ✅ `execMethod: str` - Used for action execution
- ✅ `execAction: str` - Used for action execution
- ✅ `execParameters: Dict[str, Any]` - Used for action execution
- ✅ `execResultLabel: Optional[str]` - Used for document routing
- ✅ `expectedDocumentFormats: Optional[List[Dict[str, str]]]` - Used by action planning
- ✅ `userMessage: Optional[str]` - Used for user communication
- ✅ `status: TaskStatus` - Used for tracking
- ✅ `error: Optional[str]` - Used for error handling
- ✅ `retryCount: int` - Used for retry logic
- ✅ `retryMax: int` - Used for retry logic
- ✅ `processingTime: Optional[float]` - Used for performance tracking
- ✅ `timestamp: float` - Used for ordering/auditing
- ✅ `result: Optional[str]` - Used to store action result text
**NO CHANGES NEEDED** - All attributes are used
---
## 6. Summary of Changes
### Classes to Modify:
1. ✅ **TaskStep** - Change `expectedFormat` (str) → `expectedFormats` (List[str])
2. ✅ **ChatWorkflow** - Add `expectedFormats` (optional, for explicit tracking)
### Classes to Review (NO CHANGES):
- ✅ **ActionResult** - Keep as-is, documents already have metadata
- ✅ **TaskResult** - Keep as-is, no summary needed
- ✅ **ActionDocument** - Already correct (documentName, documentData, mimeType)
- ✅ **ActionItem** - All attributes used
- ✅ **Observation** - Already has contentValidation field
- ✅ **TaskItem** - Used for database storage, separate from TaskStep
---
## 7. Implementation Impact
### Files That Will Need Updates:
1. **datamodelChat.py** - Class definitions (this proposal)
- Change `expectedFormat``expectedFormats` in TaskStep
- Add `expectedFormats` to ChatWorkflow (optional)
2. **taskPlanner.py** - Populate `expectedFormats` list instead of single `expectedFormat`
- **Adaptive planning:** Use ALL workflow data (messages, document metadata) to refine next task objective
- Extract delivered formats from workflow documents
- Compare what was delivered vs. what was planned
3. **contentValidator.py** - Use `expectedFormats` list for validation
- **Action-level validation:** Check action results against task objective (already exists)
- **Task-level validation:** Validate THIS task's deliverables against THIS task's expectations
- Uses document metadata (name, size, format, mimeType) - no summaries needed
4. **intentAnalyzer.py** - Fix prompt to ask for actual file format extensions
- Change from conceptual terms ("raw_data", "formatted") to actual extensions ("pdf", "docx", "xlsx")
5. **promptGenerationTaskplan.py** - Ask for `expectedFormats` in task planning
- **Adaptive planning:** Include ALL workflow data (messages, document names/sizes/formats/metadata) when planning next task
- Show what was actually delivered to help refine objective
6. **workflowManager.py** - Pass ALL workflow data to next task planning
- Messages (text content)
- Document metadata (names, sizes, formats, mimeTypes)
- Validation results
### Key Implementation Points:
- **No summary fields:** Use existing document metadata (name, size, format, mimeType)
- **Adaptive task planning:** Next task receives ALL workflow data (messages + document metadata) to refine objective
- **Validation scope:** Task validation checks ONLY that task's actions, not all workflow actions
- **After each action:** Validate against task objective → decide if complete or next action needed
---
## 8. Validation Logic Alignment
### Action-Level Validation (Within Task):
- **When:** After each action execution within a task
- **Checks:** Action results against task objective
- **Against:** Action documents (name, size, format, mimeType metadata)
- **Purpose:** Decide if task is complete or next action needed
- **Triggers:** Continue to next action if incomplete, complete task if done
### Task Planning (Adaptive - Uses ALL Workflow Data):
- **Input:** ALL workflow data available:
- All messages (text content)
- All document metadata (names, sizes, formats/extensions, mimeTypes)
- Previous task validation results
- **Process:**
- Extract delivered formats from all workflow documents
- Compare what was ACTUALLY delivered vs. what was PLANNED
- Refine next task objective:
- Deliver MORE if previous tasks delivered less than expected
- Deliver LESS if previous tasks already delivered more
- Adapt to actual workflow progress
### Task-Level Validation (Task Completion):
- **When:** After ALL actions in a task complete
- **Checks:** Task objective, task `expectedFormats`, task `successCriteria`
- **Against:** Documents from THIS task only (extract formats from document metadata)
- **Purpose:** Verify THIS task delivered what was expected for THIS task scope
- **Output:** Validation result (used in workflow data for next task planning)
### Workflow-Level Validation (Final):
- **When:** After ALL tasks complete
- **Checks:** Original user request, workflow `expectedFormats`, workflow success criteria
- **Against:** ALL documents from ALL tasks (extract formats from document metadata)
- **Purpose:** Final verification that complete workflow delivered what user requested
- **Triggers:** New compensatory task if validation fails (missing deliverables)
---
## 9. Next Steps
1. **Review and approve this proposal**
2. **Implement class changes** in datamodelChat.py
3. **Update intent analyzer prompt** to request actual file format extensions
4. **Update task planning prompt** to request `expectedFormats` list
5. **Update AI generation prompts** to include summary instruction
6. **Implement aggregation logic** for summaries at task/workflow levels
7. **Implement workflow-level validation** method
8. **Update all references** from `expectedFormat` to `expectedFormats`
---
## Questions Answered
**Document metadata:** Use existing document fields (name, size, format from mimeType/extensions) - no summaries needed
**Format extraction:** Extract formats from document metadata (mimeType or file extensions)
**Task validation scope:** Task validation checks ONLY actions in that task, not all workflow actions
**Adaptive planning:** Next task uses ALL workflow data (messages + document metadata) to refine objective
**After each action:** Validate against task objective → decide complete or next action needed
---
## 10. Validation Flow Clarification
### Simplified Flow:
1. **Within Task (Action-by-Action):**
- Action executes → delivers documents with metadata
- Validate action results against task objective
- If incomplete → next action needed
- If complete → task done
2. **Task Planning (Adaptive):**
- Receives: ALL workflow data (messages, document metadata from all previous tasks)
- Extracts: Delivered formats from document metadata (file extensions/mimeTypes)
- Compares: What was actually delivered vs. what was planned
- Refines: Next task objective (may need more/less based on actual progress)
3. **Task Completion:**
- Validate: THIS task's documents (extract formats from metadata) against THIS task's expectations
- Result: Used in workflow data for next task planning
4. **Workflow Completion:**
- Final validation: All documents (extract formats from metadata) meet original user request
- If missing: Create compensatory task
---
**Status:** Ready for implementation after approval

View file

@ -417,6 +417,13 @@ class ChatWorkflow(BaseModel):
frontend_readonly=False,
frontend_required=False,
)
expectedFormats: Optional[List[str]] = Field(
None,
description="List of expected file format extensions from user request (e.g., ['xlsx', 'pdf']). Extracted during intent analysis.",
frontend_type="text",
frontend_readonly=True,
frontend_required=False,
)
registerModelLabels(
@ -440,6 +447,7 @@ registerModelLabels(
"tasks": {"en": "Tasks", "fr": "Tâches"},
"workflowMode": {"en": "Workflow Mode", "fr": "Mode de workflow"},
"maxSteps": {"en": "Max Steps", "fr": "Étapes max"},
"expectedFormats": {"en": "Expected Formats", "fr": "Formats attendus"},
},
)
@ -800,8 +808,8 @@ class TaskStep(BaseModel):
dataType: Optional[str] = Field(
None, description="Expected data type (text, numbers, documents, etc.)"
)
expectedFormat: Optional[str] = Field(
None, description="Expected output format (json, csv, markdown, etc.)"
expectedFormats: Optional[List[str]] = Field(
None, description="Expected output file format extensions (e.g., ['docx', 'pdf', 'xlsx']). Use actual file extensions, not conceptual terms."
)
qualityRequirements: Optional[Dict[str, Any]] = Field(
None, description="Quality requirements and constraints"
@ -821,6 +829,7 @@ registerModelLabels(
"fr": "Complexité estimée",
},
"userMessage": {"en": "User Message", "fr": "Message utilisateur"},
"expectedFormats": {"en": "Expected Formats", "fr": "Formats attendus"},
},
)

View file

@ -263,22 +263,22 @@ class ContentValidator:
# Use taskStep format fields if available, otherwise fall back to intent
dataType = None
expectedFormat = None
expectedFormats = None
if taskStep:
if hasattr(taskStep, 'dataType') and taskStep.dataType:
dataType = taskStep.dataType
elif isinstance(taskStep, dict):
dataType = taskStep.get('dataType')
if hasattr(taskStep, 'expectedFormat') and taskStep.expectedFormat:
expectedFormat = taskStep.expectedFormat
if hasattr(taskStep, 'expectedFormats') and taskStep.expectedFormats:
expectedFormats = taskStep.expectedFormats
elif isinstance(taskStep, dict):
expectedFormat = taskStep.get('expectedFormat')
expectedFormats = taskStep.get('expectedFormats')
# Fallback to intent if taskStep format fields not available
if not dataType:
dataType = intent.get('dataType', 'unknown')
if not expectedFormat:
expectedFormat = intent.get('expectedFormat', 'unknown')
if not expectedFormats:
expectedFormats = intent.get('expectedFormats', [])
# Determine objective text and label
objectiveText = taskObjective if taskObjective else intent.get('primaryGoal', 'Unknown')
@ -299,7 +299,7 @@ class ContentValidator:
{objectiveLabel}: '{objectiveText}'
EXPECTED DATA TYPE: {dataType}
EXPECTED FORMAT: {expectedFormat}
EXPECTED FORMATS: {expectedFormats if expectedFormats else ['any']}
SUCCESS CRITERIA ({criteriaCount} items): {successCriteria}
VALIDATION RULES:

View file

@ -50,8 +50,10 @@ You are an intent analyzer. Analyze the user's request to understand what they w
Analyze the user's intent and determine:
1. What type of data/content they want (numbers, text, documents, analysis, code, etc.)
2. What format they expect (raw data, formatted, structured, visual, etc.)
3. What quality requirements they have (accuracy, completeness, format)
2. What file format(s) they expect - provide matching file format extensions list
- If multiple formats requested, list all of them (e.g., ["xlsx", "pdf"])
- If format is unclear or not specified, use empty list []
3. What quality requirements they have (accuracy, completeness)
4. What specific success criteria define completion
5. What language the user is communicating in (detect from the user request)
@ -60,11 +62,10 @@ CRITICAL: Respond with ONLY the JSON object below. Do not include any explanator
{{
"primaryGoal": "The main objective the user wants to achieve",
"dataType": "numbers|text|documents|analysis|code|unknown",
"expectedFormat": "raw_data|formatted|structured|visual|unknown",
"expectedFormats": ["pdf", "docx", "xlsx", "txt", "json", "csv", "html", "md"],
"qualityRequirements": {{
"accuracyThreshold": 0.0-1.0,
"completenessThreshold": 0.0-1.0,
"formatRequirement": "any|formatted|raw|structured"
"completenessThreshold": 0.0-1.0
}},
"successCriteria": ["specific criterion 1", "specific criterion 2"],
"languageUserDetected": "en",

View file

@ -116,53 +116,56 @@ class LearningEngine:
def _getStrategyKey(self, intent: Dict[str, Any]) -> str:
"""Gets strategy key based on intent"""
dataType = intent.get('dataType', 'unknown')
expectedFormat = intent.get('expectedFormat', 'unknown')
return f"{dataType}_{expectedFormat}"
expectedFormats = intent.get('expectedFormats', [])
formatKey = '_'.join(expectedFormats) if expectedFormats else 'unknown'
return f"{dataType}_{formatKey}"
def _createDefaultStrategy(self, intent: Dict[str, Any]) -> Dict[str, Any]:
"""Creates a default strategy for the intent"""
dataType = intent.get('dataType', 'unknown')
expectedFormat = intent.get('expectedFormat', 'unknown')
expectedFormats = intent.get('expectedFormats', [])
formatStr = ', '.join(expectedFormats) if expectedFormats else 'any'
formatKey = '_'.join(expectedFormats) if expectedFormats else 'unknown'
# Create strategy based on intent type
if dataType == 'numbers':
return {
'strategyId': f"numbers_{expectedFormat}",
'strategyId': f"numbers_{formatKey}",
'successfulActions': [],
'failedActions': [],
'successRate': 0.5,
'lastModified': datetime.now(timezone.utc).timestamp(),
'recommendedPrompt': f"Deliver {dataType} data in {expectedFormat} format. Provide actual numbers, not code to generate them.",
'recommendedPrompt': f"Deliver {dataType} data in {formatStr} format. Provide actual numbers, not code to generate them.",
'avoidPrompt': "Do not ask AI to write code when user wants data. Deliver the data directly."
}
elif dataType == 'text':
return {
'strategyId': f"text_{expectedFormat}",
'strategyId': f"text_{formatKey}",
'successfulActions': [],
'failedActions': [],
'successRate': 0.5,
'lastModified': datetime.now(timezone.utc).timestamp(),
'recommendedPrompt': f"Generate {dataType} content in {expectedFormat} format.",
'recommendedPrompt': f"Generate {dataType} content in {formatStr} format.",
'avoidPrompt': "Ensure content is readable and well-structured."
}
elif dataType == 'documents':
return {
'strategyId': f"documents_{expectedFormat}",
'strategyId': f"documents_{formatKey}",
'successfulActions': [],
'failedActions': [],
'successRate': 0.5,
'lastModified': datetime.now(timezone.utc).timestamp(),
'recommendedPrompt': f"Create {dataType} in {expectedFormat} format with proper structure.",
'recommendedPrompt': f"Create {dataType} in {formatStr} format with proper structure.",
'avoidPrompt': "Ensure document is properly formatted and organized."
}
else:
return {
'strategyId': f"unknown_{expectedFormat}",
'strategyId': f"unknown_{formatKey}",
'successfulActions': [],
'failedActions': [],
'successRate': 0.5,
'lastModified': datetime.now(timezone.utc).timestamp(),
'recommendedPrompt': f"Deliver {dataType} content in {expectedFormat} format.",
'recommendedPrompt': f"Deliver {dataType} content in {formatStr} format.",
'avoidPrompt': "Ensure content matches user requirements."
}

View file

@ -163,8 +163,8 @@ class TaskPlanner:
if isinstance(workflowIntent, dict):
if 'dataType' in workflowIntent and 'dataType' not in taskDict:
taskDict['dataType'] = workflowIntent.get('dataType')
if 'expectedFormat' in workflowIntent and 'expectedFormat' not in taskDict:
taskDict['expectedFormat'] = workflowIntent.get('expectedFormat')
if 'expectedFormats' in workflowIntent and 'expectedFormats' not in taskDict:
taskDict['expectedFormats'] = workflowIntent.get('expectedFormats')
if 'qualityRequirements' in workflowIntent and 'qualityRequirements' not in taskDict:
taskDict['qualityRequirements'] = workflowIntent.get('qualityRequirements')

View file

@ -242,12 +242,12 @@ class ActionplanMode(BaseMode):
self.workflowIntent = await self.intentAnalyzer.analyzeUserIntent(originalPrompt, context)
logger.warning(f"Workflow intent not found in workflow object, analyzed fresh")
# Task-level intent is NOT needed - use task.objective + task format fields (dataType, expectedFormat, qualityRequirements)
# Task-level intent is NOT needed - use task.objective + task format fields (dataType, expectedFormats, qualityRequirements)
# These format fields are populated from workflow intent during task planning
self.taskIntent = None # Removed redundant task-level intent analysis
logger.info(f"Workflow intent: {self.workflowIntent}")
if taskStep.dataType or taskStep.expectedFormat or taskStep.qualityRequirements:
logger.info(f"Task format info: dataType={taskStep.dataType}, expectedFormat={taskStep.expectedFormat}")
if taskStep.dataType or taskStep.expectedFormats or taskStep.qualityRequirements:
logger.info(f"Task format info: dataType={taskStep.dataType}, expectedFormats={taskStep.expectedFormats}")
# Reset progress tracking for new task
self.progressTracker.reset()

View file

@ -61,12 +61,12 @@ class DynamicMode(BaseMode):
self.workflowIntent = await self.intentAnalyzer.analyzeUserIntent(original_prompt, context)
logger.warning(f"Workflow intent not found in workflow object, analyzed fresh")
# Task-level intent is NOT needed - use task.objective + task format fields (dataType, expectedFormat, qualityRequirements)
# Task-level intent is NOT needed - use task.objective + task format fields (dataType, expectedFormats, qualityRequirements)
# These format fields are populated from workflow intent during task planning
self.taskIntent = None # Removed redundant task-level intent analysis
logger.info(f"Workflow intent: {self.workflowIntent}")
if taskStep.dataType or taskStep.expectedFormat or taskStep.qualityRequirements:
logger.info(f"Task format info: dataType={taskStep.dataType}, expectedFormat={taskStep.expectedFormat}")
if taskStep.dataType or taskStep.expectedFormats or taskStep.qualityRequirements:
logger.info(f"Task format info: dataType={taskStep.dataType}, expectedFormats={taskStep.expectedFormats}")
# NEW: Reset progress tracking for new task
self.progressTracker.reset()

View file

@ -82,7 +82,8 @@ Break down user requests into logical, executable task steps.
"dependencies": ["task_0"],
"successCriteria": ["measurable criteria 1", "measurable criteria 2"],
"estimatedComplexity": "low|medium|high",
"userMessage": "What this task will accomplish in language '{{KEY:USER_LANGUAGE}}'"
"userMessage": "What this task will accomplish in language '{{KEY:USER_LANGUAGE}}'",
"expectedFormats": ["pdf", "docx", "xlsx", "txt", "json", "csv", "html", "md",...]
}}
],
}}