Private/Parse-AIResponse.ps1
|
function Parse-AIResponse { <# .SYNOPSIS Parse AI response with multi-tier fallback strategy .DESCRIPTION Attempts to parse AI response as JSON using multiple strategies: 1. Direct JSON parse 2. Extract JSON from markdown code blocks 3. Extract any JSON array pattern 4. Return empty array on failure .PARAMETER ResponseText Raw text response from AI provider .EXAMPLE $violations = Parse-AIResponse -ResponseText $response .OUTPUTS System.Array - Array of violation objects .NOTES Author: waldo Version: 1.0.0 #> [CmdletBinding()] param( [Parameter(Mandatory = $true)] [string]$ResponseText ) begin { Write-Verbose "Starting $($MyInvocation.MyCommand.Name)" } process { try { # Tier 1: Direct JSON parse try { $json = $ResponseText | ConvertFrom-Json if (Test-ViolationArray -Violations $json) { Write-Verbose "Successfully parsed JSON (direct)" return $json } } catch { Write-Verbose "Direct JSON parse failed, trying extraction..." } # Tier 2: Extract JSON from markdown code blocks if ($ResponseText -match '(?s)```json\s*\n(.*?)\n```') { try { $json = $matches[1] | ConvertFrom-Json if (Test-ViolationArray -Violations $json) { Write-Verbose "Successfully parsed JSON (markdown extraction)" return $json } } catch { Write-Verbose "Markdown extraction failed..." } } # Tier 3: Try to extract any JSON array if ($ResponseText -match '(?s)\[\s*\{.*?\}\s*\]') { try { $json = $matches[0] | ConvertFrom-Json if (Test-ViolationArray -Violations $json) { Write-Verbose "Successfully parsed JSON (array extraction)" return $json } } catch { Write-Verbose "Array extraction failed..." } } # Tier 4: Give up gracefully Write-Verbose "Could not parse AI response as JSON. Raw response (truncated):" Write-Verbose $ResponseText.Substring(0, [Math]::Min(500, $ResponseText.Length)) return @() } catch { Write-Error "Error in $($MyInvocation.MyCommand.Name): $_" throw } } end { Write-Verbose "Completed $($MyInvocation.MyCommand.Name)" } } function Test-ViolationArray { <# .SYNOPSIS Validate that parsed JSON matches expected violation schema .PARAMETER Violations Parsed JSON object to validate .OUTPUTS System.Boolean - True if valid, false otherwise #> param($Violations) if ($null -eq $Violations) { return $false } # Allow empty arrays if ($Violations -is [array] -and $Violations.Count -eq 0) { return $true } # Ensure it's an array if ($Violations -isnot [array]) { $Violations = @($Violations) } # Validate schema foreach ($v in $Violations) { if (-not $v.PSObject.Properties['file']) { Write-Verbose "Missing 'file' property" return $false } if (-not $v.PSObject.Properties['line']) { Write-Verbose "Missing 'line' property" return $false } if (-not $v.PSObject.Properties['severity']) { Write-Verbose "Missing 'severity' property" return $false } if (-not $v.PSObject.Properties['message']) { Write-Verbose "Missing 'message' property" return $false } # Validate severity values if ($v.severity -notin @('error', 'warning', 'info')) { Write-Verbose "Invalid severity: $($v.severity)" return $false } } return $true } |