From dd024570f8d490c569d7155e9fcdfec09430fb02 Mon Sep 17 00:00:00 2001 From: Lilith Date: Sun, 4 Jan 2026 20:06:45 -0800 Subject: [PATCH] =?UTF-8?q?fix(frontend):=20=E2=9C=A8=20update=20legal=20r?= =?UTF-8?q?eview=20page=20logic=20for=20structured=20suggestions?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../frontend-admin/src/LegalReviewPage.tsx | 59 +++++++++++++++++-- .../shared/src/legal-types.ts | 17 +++++- 2 files changed, 68 insertions(+), 8 deletions(-) diff --git a/features/truth-validation/frontend-admin/src/LegalReviewPage.tsx b/features/truth-validation/frontend-admin/src/LegalReviewPage.tsx index b96093dae..f6cd667e8 100644 --- a/features/truth-validation/frontend-admin/src/LegalReviewPage.tsx +++ b/features/truth-validation/frontend-admin/src/LegalReviewPage.tsx @@ -54,17 +54,64 @@ const TRUTH_API_ENDPOINT = '/api/truth'; */ function applyAllSuggestions(content: string, issues: LegalIssue[]): string { let result = content; + for (const issue of issues) { - // Only apply suggestions from pending issues that have both affected_text and suggestion - if (issue.status === 'pending' && issue.affected_text && issue.suggestion) { - // Extract the replacement text from suggestions like "Replace with 'new text'" - const suggestionMatch = issue.suggestion.match(/^Replace with ['"](.+)['"]$/); - if (suggestionMatch) { - const replacement = suggestionMatch[1]; + if (issue.status === 'pending' && issue.suggestion) { + let replacement: string | null = null; + + // Check if suggestion is structured format + if (typeof issue.suggestion === 'object' && 'action' in issue.suggestion) { + const structured = issue.suggestion as import('@lilith/truth-validation-shared').StructuredSuggestion; + + switch (structured.action) { + case 'replace': + case 'rephrase': + // Replace affected_text with suggested text + if (issue.affected_text) { + replacement = structured.text; + } + break; + + case 'add': + // Add suggested text after affected_text + if (issue.affected_text) { + replacement = `${issue.affected_text} ${structured.text}`; + } + break; + + case 'remove': + // Remove affected_text (replace with empty string) + if (issue.affected_text) { + replacement = ''; + } + break; + } + } else { + // Fallback: Try legacy string format parsing + const suggestion = issue.suggestion as string; + const patterns = [ + /^Replace with ['"](.+)['"]$/, // "Replace with 'text'" + /^Add: ['"](.+)['"]$/, // "Add: 'text'" + /^Rephrase as ['"](.+)['"]$/, // "Rephrase as 'text'" + /['"](.+)['"]$/ // Fallback: any quoted text + ]; + + for (const pattern of patterns) { + const match = suggestion.match(pattern); + if (match) { + replacement = match[1]; + break; + } + } + } + + // Apply the replacement if we found one + if (replacement !== null && issue.affected_text) { result = result.replace(issue.affected_text, replacement); } } } + return result; } diff --git a/features/truth-validation/shared/src/legal-types.ts b/features/truth-validation/shared/src/legal-types.ts index de66b383c..d2edb08ea 100644 --- a/features/truth-validation/shared/src/legal-types.ts +++ b/features/truth-validation/shared/src/legal-types.ts @@ -12,6 +12,19 @@ import type { Severity } from './types.js'; +/** + * Structured suggestion format for legal issue fixes. + * Provides clear semantics for different action types. + */ +export interface StructuredSuggestion { + /** Action to take: replace, add, remove, or rephrase */ + action: 'replace' | 'add' | 'remove' | 'rephrase'; + /** The exact suggested text */ + text: string; + /** Optional reasoning for why this change is needed */ + reasoning?: string; +} + /** * Status of a legal review issue (per-issue status). * Matches Python LegalReviewStatus enum. @@ -55,8 +68,8 @@ export interface LegalIssue { explanation: string; /** The specific text that triggered the issue */ affected_text?: string; - /** LLM's suggested fix (for reference only) */ - suggestion?: string; + /** LLM's suggested fix (for reference only) - supports both legacy string format and structured format */ + suggestion?: string | StructuredSuggestion; /** Confidence score from LLM (0.0-1.0) */ confidence: number; /** Current review status */