-
-
Notifications
You must be signed in to change notification settings - Fork 34
Open
Labels
enhancementNew feature or requestNew feature or request
Description
Description
Enhance table suggestion handling to properly support AI-generated suggestions for table data, leveraging workspace schema configuration for intelligent suggestion processing, validation, and user review. This improvement enables better integration of AI suggestions with the table reference resolution system.
Problem
- Suggestions for table data are not well-supported in the current system
- Complex to manage suggestions for tables with references and schema relationships
- Difficult to merge or partially accept table suggestions
- No clear UI for reviewing and accepting table suggestions with schema context
- Limited validation for table suggestions against workspace schema configuration
- No integration with workspace-level reference resolution
Proposed Solution
- Leverage Workspace Schema Configuration: Use schema definitions for intelligent suggestion processing
- Enhance Backend Suggestion Model: Improve suggestion handling for table data with schema awareness
- Implement Schema-Aware Validation: Validate table suggestions against workspace schema definitions
- Create Advanced UI Components: Build intuitive interfaces for table suggestion review and acceptance
- Integrate with Reference Resolution: Handle references in table suggestions using the enhanced resolution system
Implementation Details
Dependencies
- Workspace schema configuration infrastructure must be implemented
- SchemaService should provide table schema definitions and validation ([Refactor] Improve table reference resolution system #92)
- TableField and TableQuestion types should be available ([Feature] Implement TableField type in backend #90, [Feature] Implement TableQuestion type and improve table answer handling #91)
- Enhanced table reference resolution system should be in place ([Refactor] Improve table reference resolution system #92)
Backend Changes
-
Enhance Suggestion model for table data:
class Suggestion(DatabaseModel): # Existing fields... def validate_table_suggestion(self, workspace_id: UUID) -> List[ValidationError]: """Validate table suggestion against workspace schema configuration""" if not self.question.is_table: return [] schema_service = SchemaService(workspace_id) return schema_service.validate_table_suggestion( self.question.name, self.value, workspace_id ) def resolve_suggestion_references(self, workspace_id: UUID) -> dict: """Resolve references in table suggestion data""" if not self.question.is_table: return self.value schema_service = SchemaService(workspace_id) return schema_service.resolve_table_references( self.value, workspace_id )
-
Enhance SchemaService for suggestion handling:
class SchemaService: def validate_table_suggestion(self, question_name: str, suggestion_data: dict, workspace_id: UUID) -> List[ValidationError]: """Validate table suggestion against question schema definition""" question_schema = self.get_question_schema(question_name) validation_errors = [] # Validate table structure structure_errors = self._validate_table_structure(suggestion_data, question_schema) validation_errors.extend(structure_errors) # Validate references reference_errors = self._validate_suggestion_references(suggestion_data, workspace_id) validation_errors.extend(reference_errors) return validation_errors def merge_table_suggestions(self, current_data: dict, suggestion_data: dict, merge_strategy: str = "append") -> dict: """Merge table suggestion with current data using specified strategy""" if merge_strategy == "append": return self._append_suggestion_rows(current_data, suggestion_data) elif merge_strategy == "replace": return suggestion_data elif merge_strategy == "merge": return self._merge_suggestion_data(current_data, suggestion_data) else: raise ValueError(f"Unknown merge strategy: {merge_strategy}") def generate_suggestion_conflicts(self, current_data: dict, suggestion_data: dict, workspace_id: UUID) -> List[dict]: """Identify conflicts between current data and suggestions""" conflicts = [] # Check for reference conflicts ref_conflicts = self._check_reference_conflicts(current_data, suggestion_data, workspace_id) conflicts.extend(ref_conflicts) # Check for data conflicts data_conflicts = self._check_data_conflicts(current_data, suggestion_data) conflicts.extend(data_conflicts) return conflicts
-
Add suggestion processing API endpoints:
@router.post("/workspaces/{workspace_id}/suggestions/{suggestion_id}/validate") async def validate_table_suggestion( workspace_id: UUID, suggestion_id: UUID, db: AsyncSession = Depends(get_async_db) ): suggestion = await get_suggestion_by_id(db, suggestion_id) validation_errors = suggestion.validate_table_suggestion(workspace_id) return { "valid": len(validation_errors) == 0, "errors": validation_errors, "resolved_data": suggestion.resolve_suggestion_references(workspace_id) } @router.post("/workspaces/{workspace_id}/suggestions/{suggestion_id}/merge") async def merge_table_suggestion( workspace_id: UUID, suggestion_id: UUID, merge_request: TableSuggestionMergeRequest, db: AsyncSession = Depends(get_async_db) ): suggestion = await get_suggestion_by_id(db, suggestion_id) record = await get_record_by_id(db, suggestion.record_id) schema_service = SchemaService(workspace_id, db) # Get current table data current_data = record.responses[0].values.get(suggestion.question.name, {}) # Merge with suggestion merged_data = schema_service.merge_table_suggestions( current_data, suggestion.value, merge_request.strategy ) # Validate merged result validation_errors = schema_service.validate_table_suggestion( suggestion.question.name, merged_data, workspace_id ) if validation_errors: return {"success": False, "errors": validation_errors} # Update record with merged data await update_record_response(db, record.id, { suggestion.question.name: merged_data }) return {"success": True, "merged_data": merged_data} @router.get("/workspaces/{workspace_id}/suggestions/{suggestion_id}/conflicts") async def get_suggestion_conflicts( workspace_id: UUID, suggestion_id: UUID, db: AsyncSession = Depends(get_async_db) ): suggestion = await get_suggestion_by_id(db, suggestion_id) record = await get_record_by_id(db, suggestion.record_id) schema_service = SchemaService(workspace_id, db) current_data = record.responses[0].values.get(suggestion.question.name, {}) conflicts = schema_service.generate_suggestion_conflicts( current_data, suggestion.value, workspace_id ) return {"conflicts": conflicts}
Frontend Changes
-
Enhance Suggestion entity for table handling:
export class Suggestion implements Answer { // Existing properties... async getResolvedTableValue(workspaceId: string): Promise<TableData> { if (!this.questionType.isTableType) { return this.value as TableData; } const response = await suggestionService.validateTableSuggestion( workspaceId, this.id ); return new TableData( response.resolved_data.data, response.resolved_data.schema, response.resolved_data.reference ); } async getConflicts(workspaceId: string): Promise<SuggestionConflict[]> { if (!this.questionType.isTableType) { return []; } const response = await suggestionService.getSuggestionConflicts( workspaceId, this.id ); return response.conflicts.map(conflict => new SuggestionConflict(conflict)); } }
-
Create advanced table suggestion UI components:
<!-- TableSuggestionReview.vue --> <template> <div class="table-suggestion-review"> <div class="suggestion-header"> <h3>Table Suggestion Review</h3> <div class="suggestion-meta"> <span>Agent: {{ suggestion.agent }}</span> <span>Score: {{ suggestion.score.fixed }}</span> <span>Updated: {{ suggestion.updatedAt | formatDate }}</span> </div> </div> <div v-if="conflicts.length > 0" class="conflicts-section"> <h4>Conflicts Detected</h4> <div v-for="conflict in conflicts" :key="conflict.id" class="conflict-item"> <ConflictResolver :conflict="conflict" @resolve="handleConflictResolve" /> </div> </div> <div class="data-comparison"> <div class="current-data"> <h4>Current Data</h4> <TableRenderer :table-data="currentData" :readonly="true" /> </div> <div class="suggested-data"> <h4>Suggested Data</h4> <TableRenderer :table-data="suggestedData" :readonly="true" /> </div> <div class="merged-preview" v-if="mergedData"> <h4>Merged Preview</h4> <TableRenderer :table-data="mergedData" :readonly="true" /> </div> </div> <div class="merge-options"> <div class="merge-strategy"> <label>Merge Strategy:</label> <select v-model="selectedStrategy" @change="updateMergePreview"> <option value="append">Append new rows</option> <option value="replace">Replace all data</option> <option value="merge">Smart merge</option> </select> </div> <div class="partial-selection" v-if="selectedStrategy === 'append'"> <h5>Select rows to accept:</h5> <div v-for="(row, index) in suggestedData.data" :key="index" class="row-selector"> <input type="checkbox" :id="`row-${index}`" v-model="selectedRows[index]" @change="updateMergePreview" /> <label :for="`row-${index}`">Row {{ index + 1 }}</label> <TableRowPreview :row="row" :schema="suggestedData.schema" /> </div> </div> </div> <div class="action-buttons"> <button @click="acceptSuggestion" :disabled="!canAccept" class="btn-accept"> Accept Suggestion </button> <button @click="rejectSuggestion" class="btn-reject"> Reject Suggestion </button> <button @click="requestModification" class="btn-modify"> Request Modification </button> </div> </div> </template> <script> export default { props: { suggestion: Object, currentData: Object, workspaceId: String, }, data() { return { suggestedData: null, mergedData: null, conflicts: [], selectedStrategy: 'append', selectedRows: {}, loading: false, }; }, async mounted() { await this.loadSuggestionData(); }, computed: { canAccept() { return this.conflicts.length === 0 && this.mergedData !== null; }, }, methods: { async loadSuggestionData() { this.loading = true; try { // Load resolved suggestion data this.suggestedData = await this.suggestion.getResolvedTableValue(this.workspaceId); // Load conflicts this.conflicts = await this.suggestion.getConflicts(this.workspaceId); // Generate initial merge preview await this.updateMergePreview(); } catch (error) { this.$toast.error('Failed to load suggestion data'); } finally { this.loading = false; } }, async updateMergePreview() { try { const response = await suggestionService.previewMerge( this.workspaceId, this.suggestion.id, { strategy: this.selectedStrategy, selectedRows: this.selectedRows, } ); this.mergedData = new TableData( response.merged_data.data, response.merged_data.schema, response.merged_data.reference ); } catch (error) { this.$toast.error('Failed to generate merge preview'); } }, async acceptSuggestion() { try { await suggestionService.mergeSuggestion( this.workspaceId, this.suggestion.id, { strategy: this.selectedStrategy, selectedRows: this.selectedRows, } ); this.$emit('accepted', this.mergedData); this.$toast.success('Suggestion accepted successfully'); } catch (error) { this.$toast.error('Failed to accept suggestion'); } }, // Additional methods... }, }; </script>
-
Create suggestion conflict resolution components:
<!-- ConflictResolver.vue --> <template> <div class="conflict-resolver"> <div class="conflict-description"> <h5>{{ conflict.type }} Conflict</h5> <p>{{ conflict.description }}</p> </div> <div class="conflict-options"> <div class="option" v-for="option in conflict.options" :key="option.id"> <input type="radio" :id="option.id" :value="option.value" v-model="selectedResolution" /> <label :for="option.id">{{ option.label }}</label> <div class="option-preview">{{ option.preview }}</div> </div> </div> <button @click="resolveConflict" :disabled="!selectedResolution"> Resolve Conflict </button> </div> </template>
Related Files
extralit/argilla-server/src/argilla_server/models/database.py- Enhanced Suggestion modelextralit/argilla-server/src/argilla_server/services/SchemaService.py- Suggestion validation and merging logicextralit/argilla-server/src/argilla_server/api/handlers/v1/suggestions/- Enhanced suggestion handlersextralit/argilla-frontend/v1/domain/entities/question/Suggestion.ts- Enhanced Suggestion entityextralit/argilla-frontend/components/features/table-suggestions/- New suggestion UI componentsextralit/argilla-frontend/v1/infrastructure/services/SuggestionService.ts- Suggestion API client
Acceptance Criteria
- Table suggestions are properly validated against workspace schema configuration
- Backend provides APIs for suggestion validation, conflict detection, and merging
- Partial acceptance of table suggestions is supported with multiple merge strategies
- UI clearly displays table suggestions with resolved references and schema context
- Reference conflicts in table suggestions are properly identified and resolvable
- Users can efficiently review table suggestions with side-by-side comparison
- Suggestion merging maintains data integrity and reference consistency
- Multi-user suggestion scenarios are handled correctly
- Performance is optimized for large table suggestions
- Integration tests verify table suggestion functionality
- Error handling provides meaningful feedback for suggestion conflicts
- The system maintains backward compatibility with existing suggestion data
Related Issues
This is part of the strategic workspace-level schema management enhancement:
- Depends on: Workspace Schema Configuration Infrastructure
- Depends on: [Feature] Implement TableField type in backend #90 (TableField), [Feature] Implement TableQuestion type and improve table answer handling #91 (TableQuestion) for proper table types
- Depends on: [Refactor] Improve table reference resolution system #92 (Enhanced table reference resolution)
- Part of: [Refactor] Phase 3: Refactor and enhance table reference resolution #97 (Phase 3: Table reference resolution)
- Enables: Advanced table annotation workflows
Metadata
Metadata
Assignees
Labels
enhancementNew feature or requestNew feature or request