microsoft/hve-core

Public

mirrored from https://github.com/microsoft/hve-coreAvailable

CodeCommitsIssuesPull requestsActionsInsightsSecurity
feat/1637-l4-tests

Branches

Tags

  • No tags available.
0Branches0Tags
Go to file
Add file
Code

Clone

HTTPS

Download ZIP

scripts/evals/Invoke-ArtifactModeration.ps1

176lines · modecode

1#!/usr/bin/env pwsh
2# Copyright (c) Microsoft Corporation.
3# SPDX-License-Identifier: MIT
4#Requires -Version 7.0
5
6<#
7.SYNOPSIS
8 Content-moderation pre-job for all eval specs plus changed AI artifacts.
9
10.DESCRIPTION
11 Enumerates every stimulus / eval spec file under the eval root and combines
12 them with the agent/prompt/instruction/skill files in the PR change set
13 (read from the changed-artifact manifest). The combined, de-duplicated file
14 list is moderated whole-file via Invoke-ContentModeration.ps1.
15
16 Designed to run as an isolated pre-job before vally eval execution so that
17 flagged content blocks the pipeline with a dedicated, clean log. Exit code
18 is propagated from the moderation wrapper: 0 clean, 1 flagged (block),
19 2 error.
20
21.PARAMETER ManifestPath
22 Path to the changed-artifact manifest. Defaults to
23 logs/changed-ai-artifacts.json. When absent, only eval specs are moderated.
24
25.PARAMETER EvalRoot
26 Root directory of eval specs. Defaults to evals.
27
28.PARAMETER OutFile
29 Output path for moderation results. Defaults to
30 logs/moderation-artifacts.json.
31
32.PARAMETER Threshold
33 Toxicity threshold (0.0-1.0). Defaults to 0.5.
34
35.PARAMETER Model
36 Detoxify model variant: original, unbiased, multilingual. Defaults to
37 unbiased.
38
39.PARAMETER RepoRoot
40 Repository root directory. Defaults to git repo root or script directory.
41
42.NOTES
43 Runs via: npm run eval:moderate:artifacts
44#>
45[CmdletBinding()]
46param(
47 [Parameter(Mandatory = $false)]
48 [string]$ManifestPath,
49
50 [Parameter(Mandatory = $false)]
51 [string]$EvalRoot,
52
53 [Parameter(Mandatory = $false)]
54 [string]$OutFile,
55
56 [Parameter(Mandatory = $false)]
57 [double]$Threshold = 0.5,
58
59 [Parameter(Mandatory = $false)]
60 [ValidateSet('original', 'unbiased', 'multilingual')]
61 [string]$Model = 'unbiased',
62
63 [Parameter(Mandatory = $false)]
64 [string]$RepoRoot
65)
66
67$ErrorActionPreference = 'Stop'
68
69function Resolve-RepoRoot {
70 [CmdletBinding()]
71 [OutputType([string])]
72 param([string]$Hint)
73
74 if (-not [string]::IsNullOrWhiteSpace($Hint)) {
75 return (Resolve-Path -LiteralPath $Hint).ProviderPath
76 }
77
78 try {
79 $gitRoot = git rev-parse --show-toplevel 2>$null
80 if ($LASTEXITCODE -eq 0 -and -not [string]::IsNullOrWhiteSpace($gitRoot)) {
81 return (Resolve-Path -LiteralPath $gitRoot.Trim()).ProviderPath
82 }
83 }
84 catch { $null = $_ }
85
86 return (Resolve-Path -LiteralPath (Join-Path $PSScriptRoot '..')).ProviderPath
87}
88
89function Resolve-PathFromRoot {
90 [CmdletBinding()]
91 [OutputType([string])]
92 param(
93 [Parameter(Mandatory = $true)][string]$Path,
94 [Parameter(Mandatory = $true)][string]$RepoRoot
95 )
96
97 if ([System.IO.Path]::IsPathRooted($Path)) { return $Path }
98 return (Join-Path -Path $RepoRoot -ChildPath $Path)
99}
100
101if ($MyInvocation.InvocationName -eq '.') { return }
102
103$resolvedRoot = Resolve-RepoRoot -Hint $RepoRoot
104
105if ([string]::IsNullOrWhiteSpace($ManifestPath)) { $ManifestPath = 'logs/changed-ai-artifacts.json' }
106if ([string]::IsNullOrWhiteSpace($EvalRoot)) { $EvalRoot = 'evals' }
107if ([string]::IsNullOrWhiteSpace($OutFile)) { $OutFile = 'logs/moderation-artifacts.json' }
108
109$resolvedManifest = Resolve-PathFromRoot -Path $ManifestPath -RepoRoot $resolvedRoot
110$resolvedEvalRoot = Resolve-PathFromRoot -Path $EvalRoot -RepoRoot $resolvedRoot
111$resolvedOutFile = Resolve-PathFromRoot -Path $OutFile -RepoRoot $resolvedRoot
112
113if (-not (Test-Path -LiteralPath $resolvedEvalRoot -PathType Container)) {
114 Write-Host "::error::Eval root not found: $resolvedEvalRoot"
115 exit 2
116}
117
118$outDir = Split-Path -Parent $resolvedOutFile
119if ($outDir -and -not (Test-Path -LiteralPath $outDir)) {
120 New-Item -ItemType Directory -Path $outDir -Force | Out-Null
121}
122
123$fileSet = [System.Collections.Generic.HashSet[string]]::new([System.StringComparer]::OrdinalIgnoreCase)
124
125# Collect every stimulus / eval spec file under the eval root.
126$specFiles = Get-ChildItem -LiteralPath $resolvedEvalRoot -Recurse -File -Include '*.yaml', '*.yml' -ErrorAction SilentlyContinue
127foreach ($spec in $specFiles) {
128 $null = $fileSet.Add($spec.FullName)
129}
130
131# Collect changed artifact files from the manifest (skip deletions and missing files).
132if (Test-Path -LiteralPath $resolvedManifest -PathType Leaf) {
133 $manifest = Get-Content -LiteralPath $resolvedManifest -Raw | ConvertFrom-Json
134 $artifacts = @()
135 if ($null -ne $manifest -and $null -ne $manifest.artifacts) {
136 $artifacts = @($manifest.artifacts | Where-Object { [string]$_.status -ne 'D' })
137 }
138
139 foreach ($artifact in $artifacts) {
140 $artifactPath = [string]$artifact.path
141 if ([string]::IsNullOrWhiteSpace($artifactPath)) { continue }
142 $absolute = Resolve-PathFromRoot -Path $artifactPath -RepoRoot $resolvedRoot
143 if (Test-Path -LiteralPath $absolute -PathType Leaf) {
144 $null = $fileSet.Add(((Resolve-Path -LiteralPath $absolute).ProviderPath))
145 }
146 else {
147 Write-Warning "Artifact file not found: $artifactPath"
148 }
149 }
150}
151else {
152 Write-Warning "Manifest not found: $resolvedManifest; moderating eval specs only."
153}
154
155$files = @($fileSet)
156
157if ($files.Count -eq 0) {
158 $empty = [ordered]@{
159 scope = 'artifacts'
160 model = $Model
161 threshold = $Threshold
162 flagged = $false
163 results = @()
164 }
165 $empty | ConvertTo-Json -Depth 6 | Set-Content -LiteralPath $resolvedOutFile -Encoding utf8NoBOM
166 Write-Host "No eval specs or changed AI artifacts to moderate. Empty log written to $resolvedOutFile"
167 exit 0
168}
169
170Write-Host "Moderating $($files.Count) file(s) (all eval specs + changed artifacts)."
171
172$moderationScript = Join-Path $PSScriptRoot 'Invoke-ContentModeration.ps1'
173& $moderationScript -FileList $files -Scope 'artifacts' -OutFile $resolvedOutFile -Threshold $Threshold -Model $Model -RepoRoot $resolvedRoot
174$exitCode = $LASTEXITCODE
175
176exit $exitCode
177