239 lines
9.2 KiB
PowerShell
239 lines
9.2 KiB
PowerShell
[CmdletBinding()]
|
|
param(
|
|
[Parameter(Mandatory = $true)]
|
|
[string]$ReleaseRoot,
|
|
[Parameter(Mandatory = $true)]
|
|
[string]$CandidateRoot,
|
|
[Parameter(Mandatory = $true)]
|
|
[ValidatePattern("^[A-Za-z0-9._-]{1,96}$")]
|
|
[string]$RunId,
|
|
[string]$OutputRoot = "D:\NDC_MISSIONCORE\runtime\results\m48n-native-parity"
|
|
)
|
|
|
|
$ErrorActionPreference = "Stop"
|
|
$ProgressPreference = "SilentlyContinue"
|
|
|
|
function Assert-LastExitCode([string]$Operation) {
|
|
if ($LASTEXITCODE -ne 0) { throw "$Operation failed with exit code $LASTEXITCODE" }
|
|
}
|
|
|
|
function Get-Sha256([string]$Path) {
|
|
return (Get-FileHash -LiteralPath $Path -Algorithm SHA256).Hash.ToLowerInvariant()
|
|
}
|
|
|
|
function Resolve-DDirectory([string]$Path, [string]$Label, [bool]$Create) {
|
|
if ($Create -and -not (Test-Path -LiteralPath $Path)) {
|
|
$null = New-Item -ItemType Directory -Path $Path
|
|
}
|
|
$item = Get-Item -LiteralPath (Resolve-Path -LiteralPath $Path).Path -Force
|
|
if (
|
|
-not $item.PSIsContainer -or
|
|
($item.Attributes -band [IO.FileAttributes]::ReparsePoint) -or
|
|
[IO.Path]::GetPathRoot($item.FullName).TrimEnd("\") -ine "D:"
|
|
) {
|
|
throw "$Label must be a real D: directory"
|
|
}
|
|
return $item.FullName
|
|
}
|
|
|
|
function Assert-RegularFile([string]$Path, [string]$Label) {
|
|
$item = Get-Item -LiteralPath (Resolve-Path -LiteralPath $Path).Path -Force
|
|
if ($item.PSIsContainer -or ($item.Attributes -band [IO.FileAttributes]::ReparsePoint)) {
|
|
throw "$Label must be a regular file"
|
|
}
|
|
return $item.FullName
|
|
}
|
|
|
|
function Convert-ToDockerPath([string]$Path) { return $Path.Replace("\", "/") }
|
|
|
|
function Get-Container([string]$Name) {
|
|
$rows = @((& docker inspect $Name) | ConvertFrom-Json)
|
|
Assert-LastExitCode "Docker inspection for $Name"
|
|
if ($rows.Count -ne 1) { throw "Container identity for $Name is not unique" }
|
|
return $rows[0]
|
|
}
|
|
|
|
if ($env:COMPUTERNAME -cne "DESKTOP-OPJ8J04") {
|
|
throw "M48N native parity is pinned to DESKTOP-OPJ8J04"
|
|
}
|
|
|
|
$release = Resolve-DDirectory $ReleaseRoot "M48N release root" $false
|
|
$candidate = Resolve-DDirectory $CandidateRoot "M48N candidate root" $false
|
|
$output = Resolve-DDirectory $OutputRoot "M48N parity output root" $true
|
|
$runOutput = Join-Path $output $RunId
|
|
if (Test-Path -LiteralPath $runOutput) { throw "M48N parity output already exists" }
|
|
$null = New-Item -ItemType Directory -Path $runOutput
|
|
$runOutput = Resolve-DDirectory $runOutput "M48N parity run output" $false
|
|
|
|
$runner = Assert-RegularFile (
|
|
Join-Path $release "run_m48n_native_parity_worker.py"
|
|
) "native parity runner"
|
|
$modelConfig = Assert-RegularFile (
|
|
Join-Path $release "rf_detr_large_native_kb4_config.pbtxt"
|
|
) "native Triton config"
|
|
$engine = Assert-RegularFile (
|
|
Join-Path $candidate "rf-detr-native-uint8.plan"
|
|
) "native TensorRT engine"
|
|
$engineSha256 = Get-Sha256 $engine
|
|
if ($engineSha256 -cne "b8a40b3580edff001ec9680de68707242294ff590ab296000fae371f1083f695") {
|
|
throw "native TensorRT engine SHA-256 changed"
|
|
}
|
|
$video = Assert-RegularFile (
|
|
"D:\NDC_MISSIONCORE\runtime\experiments\e46e\inputs" +
|
|
"\right-cadd1696ff000904eb78633a0a8418104b8024f178b91f3421789021ccb160e8.mp4"
|
|
) "RAVNOVES00 video"
|
|
$videoSha256 = Get-Sha256 $video
|
|
if ($videoSha256 -cne "cadd1696ff000904eb78633a0a8418104b8024f178b91f3421789021ccb160e8") {
|
|
throw "RAVNOVES00 video SHA-256 changed"
|
|
}
|
|
$mask = Assert-RegularFile (
|
|
"D:\NDC_MISSIONCORE\runtime\inputs\e2" +
|
|
"\valid-fov-mask-b4dd8ddf2b87c1d520ee8a0868c4fea062d7c14d1bae73ccabd3abe1f3acbac2" +
|
|
"\mask.png"
|
|
) "valid-FOV mask"
|
|
$maskSha256 = Get-Sha256 $mask
|
|
if ($maskSha256 -cne "a40cee06b7c6f69b6a09a11563dcfd237f3de833b1ccd31459e66692e528ba63") {
|
|
throw "valid-FOV mask SHA-256 changed"
|
|
}
|
|
$checkpoint = Assert-RegularFile (
|
|
"D:\NDC_MISSIONCORE\runtime\experiments\m48t-fixed-detector-20260825T095425Z" +
|
|
"\weights\rf-detr-large-2026.pth"
|
|
) "RF-DETR checkpoint"
|
|
$checkpointSha256 = Get-Sha256 $checkpoint
|
|
if ($checkpointSha256 -cne "0f4e20e19a99c0f8a62b5685f57f6c8b5c371c59081feda6752a0561a79ccf38") {
|
|
throw "RF-DETR checkpoint SHA-256 changed"
|
|
}
|
|
|
|
$historical = Get-Container "ndc-mission-core-triton"
|
|
if (-not $historical.State.Running -or $historical.State.Health.Status -cne "healthy") {
|
|
throw "Historical Triton must remain healthy"
|
|
}
|
|
$historicalId = [string]$historical.Id
|
|
$baseImage = (
|
|
"nvcr.io/nvidia/tritonserver:26.06-py3@" +
|
|
"sha256:58df7489c3f2276f9591d500a012dee03e23d35543ce3c390b4c001e6bf90794"
|
|
)
|
|
& docker image inspect $baseImage *> $null
|
|
Assert-LastExitCode "pinned base image inspection"
|
|
$runtimeVolume = "ndc-mission-core-m48t-upstream-parity-env"
|
|
if (-not (& docker volume ls --quiet --filter "name=^$runtimeVolume$")) {
|
|
throw "pinned RF-DETR dependency volume is unavailable"
|
|
}
|
|
|
|
$modelRoot = Join-Path $runOutput "triton-models"
|
|
$modelDirectory = Join-Path $modelRoot "rf_detr_large_native_kb4"
|
|
$modelVersionDirectory = Join-Path $modelDirectory "1"
|
|
$null = New-Item -ItemType Directory -Path $modelVersionDirectory
|
|
Copy-Item -LiteralPath $modelConfig -Destination (Join-Path $modelDirectory "config.pbtxt")
|
|
Copy-Item -LiteralPath $engine -Destination (Join-Path $modelVersionDirectory "model.plan")
|
|
if ((Get-Sha256 (Join-Path $modelVersionDirectory "model.plan")) -cne $engineSha256) {
|
|
throw "staged native TensorRT engine SHA-256 changed"
|
|
}
|
|
|
|
$tritonName = "ndc-mission-core-m48n-native-parity-triton"
|
|
$runnerName = "ndc-mission-core-m48n-native-parity"
|
|
foreach ($name in @($tritonName, $runnerName)) {
|
|
if (& docker ps -a --format "{{.Names}}" --filter "name=^/$name$") {
|
|
throw "M48N parity container $name already exists"
|
|
}
|
|
}
|
|
|
|
try {
|
|
& docker create `
|
|
--name $tritonName `
|
|
--read-only `
|
|
--security-opt "no-new-privileges:true" `
|
|
--cap-drop ALL `
|
|
--pids-limit 512 `
|
|
--shm-size 1g `
|
|
--gpus all `
|
|
--tmpfs "/tmp:rw,noexec,nosuid,size=2g" `
|
|
--health-cmd "curl --fail --silent http://127.0.0.1:8000/v2/health/ready" `
|
|
--health-interval 5s `
|
|
--health-timeout 3s `
|
|
--health-start-period 20s `
|
|
--health-retries 24 `
|
|
-v ((Convert-ToDockerPath $modelRoot) + ":/models:ro") `
|
|
$baseImage `
|
|
tritonserver `
|
|
--model-repository=/models `
|
|
--model-control-mode=explicit `
|
|
--load-model=rf_detr_large_native_kb4 `
|
|
--disable-auto-complete-config `
|
|
--strict-readiness=true `
|
|
--exit-on-error=true `
|
|
--allow-http=true `
|
|
--allow-grpc=false `
|
|
--allow-metrics=false *> $null
|
|
Assert-LastExitCode "M48N parity Triton creation"
|
|
& docker start $tritonName *> $null
|
|
Assert-LastExitCode "M48N parity Triton start"
|
|
$ready = $false
|
|
foreach ($attempt in 1..60) {
|
|
Start-Sleep -Seconds 2
|
|
$candidateContainer = Get-Container $tritonName
|
|
if (-not $candidateContainer.State.Running) {
|
|
& docker logs $tritonName
|
|
throw "M48N parity Triton stopped during startup"
|
|
}
|
|
if ($candidateContainer.State.Health.Status -ceq "healthy") {
|
|
$ready = $true
|
|
break
|
|
}
|
|
}
|
|
if (-not $ready) { throw "M48N parity Triton did not become healthy" }
|
|
|
|
& docker run `
|
|
--name $runnerName `
|
|
--network ("container:{0}" -f $tritonName) `
|
|
--read-only `
|
|
--security-opt "no-new-privileges:true" `
|
|
--cap-drop ALL `
|
|
--pids-limit 512 `
|
|
--gpus all `
|
|
--shm-size 2g `
|
|
--tmpfs "/tmp:rw,noexec,nosuid,size=8g" `
|
|
-e "PYTHONDONTWRITEBYTECODE=1" `
|
|
-e "PYTHONPATH=/opt/parity" `
|
|
-v ($runtimeVolume + ":/opt/parity:ro") `
|
|
-v ((Convert-ToDockerPath $release) + ":/release:ro") `
|
|
-v ((Convert-ToDockerPath $video) + ":/input/video.mp4:ro") `
|
|
-v ((Convert-ToDockerPath $mask) + ":/input/mask.png:ro") `
|
|
-v ((Convert-ToDockerPath $checkpoint) + ":/model/rf-detr-large-2026.pth:ro") `
|
|
-v ((Convert-ToDockerPath $runOutput) + ":/output:rw") `
|
|
--entrypoint python3 `
|
|
$baseImage `
|
|
/release/run_m48n_native_parity_worker.py `
|
|
--video /input/video.mp4 `
|
|
--expected-video-sha256 $videoSha256 `
|
|
--mask /input/mask.png `
|
|
--expected-mask-sha256 $maskSha256 `
|
|
--checkpoint /model/rf-detr-large-2026.pth `
|
|
--expected-checkpoint-sha256 $checkpointSha256 `
|
|
--engine-sha256 $engineSha256 `
|
|
--triton-origin http://127.0.0.1:8000 `
|
|
--output /output/result.json
|
|
Assert-LastExitCode "M48N native PyTorch/TensorRT parity"
|
|
if (-not (Test-Path -LiteralPath (Join-Path $runOutput "result.json") -PathType Leaf)) {
|
|
throw "M48N native parity result was not written"
|
|
}
|
|
} finally {
|
|
foreach ($name in @($runnerName, $tritonName)) {
|
|
if (& docker ps -a --format "{{.Names}}" --filter "name=^/$name$") {
|
|
& docker rm -f $name *> $null
|
|
}
|
|
}
|
|
$historicalAfter = Get-Container "ndc-mission-core-triton"
|
|
if (
|
|
$historicalAfter.Id -cne $historicalId -or
|
|
-not $historicalAfter.State.Running -or
|
|
$historicalAfter.State.Health.Status -cne "healthy"
|
|
) {
|
|
throw "Historical Triton changed during M48N parity"
|
|
}
|
|
}
|
|
|
|
Write-Output ("M48N_NATIVE_PARITY_RESULT={0}" -f (Join-Path $runOutput "result.json"))
|
|
Write-Output "HISTORICAL_TRITON_ACTION=none"
|
|
Write-Output "SEMANTIC_AUTHORITY_CHANGED=false"
|