Loading files
This file is pinned to the selected release. Download the original to open it in your design tool.
e934a3ad8This file is pinned to the selected release. Download the original to open it in your design tool.
import {
clearValidationCache,
getLastMeshData,
validateStl,
type ValidationResult,
} from "@/lib/mesh-validator";
import { validatePreviewAgainstRequest } from "@/lib/visual-validator";
import { checkCompile } from "@/lib/validation/compile-check";
import { checkBoundingBox } from "@/lib/validation/bbox-check";
import { checkComponents } from "@/lib/validation/component-check";
import { checkHoleCount, inferExpectedMinimumHoleCount } from "@/lib/validation/hole-check";
import { computeReport } from "@/lib/validation/report";
import type { ValidationCheck, ValidationReport } from "@/lib/validation/validation-types";
import type { CadValidationTargets } from "@/lib/harness/types";
import { cleanupArtifactWorkspace } from "@/lib/tools/artifact-store";
export { clearValidationCache, validateStl, validatePreviewAgainstRequest };
export type { ValidationResult, ValidationCheck, ValidationReport };
export interface ValidateRenderedInput {
jobId?: string;
inputRequest: string;
partFamily: string | null;
scadSource: string;
stlFilePath: string;
previewImagePath: string;
wallThickness?: number;
renderLog?: {
openscad_version: string;
render_time_ms: number;
stl_triangles: number;
stl_vertices: number;
png_resolution: string | null;
warnings: string[];
};
validationTargets?: CadValidationTargets;
skipVisual?: boolean;
}
/**
* Run all deterministic validation checks against rendered artifacts.
*
* Combines:
* - Mesh validation (Python/trimesh — R001/R002/R003)
* - Visual validation (vision LLM — V001)
* - Compile check (C001 — render log analysis)
* - Bounding box check (B001 — vs validation_targets.expected_bbox)
* - Component check (C002 — floating parts detection)
* - Hole count check (H001 — Euler characteristic genus estimate)
*/
export async function validateRenderedArtifacts(
input: ValidateRenderedInput
): Promise<ValidationResult[]> {
try {
return await runRenderedValidation(input);
} finally {
await cleanupArtifactWorkspace(input.stlFilePath);
}
}
async function runRenderedValidation(
input: ValidateRenderedInput
): Promise<ValidationResult[]> {
const rawMeshResults = await validateStl(input.stlFilePath, input.wallThickness, input.jobId);
const meshResults = input.validationTargets?.manufacturing_mode === "display"
? rawMeshResults.map((rule) => rule.rule_id === "R001"
? { ...rule, passed: false, status: "SKIP" as const, is_critical: false,
message: "Skipped — FDM wall thickness is not applicable to the explicitly requested display model" }
: rule)
: rawMeshResults;
// Visual validation runs only when explicitly requested (Phase 4: user-triggered)
const visualResults: ValidationResult[] = input.skipVisual
? []
: await validatePreviewAgainstRequest({
inputRequest: input.inputRequest,
partFamily: input.partFamily,
previewImagePath: input.previewImagePath,
});
// New deterministic checks (Phase 2)
const additionalChecks: ValidationCheck[] = [];
// C001 — Compile check
if (input.renderLog) {
additionalChecks.push(checkCompile(input.renderLog));
}
// Mesh-derived checks (B001, C002, H001)
const meshData = getLastMeshData(input.jobId ?? "__last__");
if (meshData) {
additionalChecks.push(
checkBoundingBox(meshData, input.validationTargets?.expected_bbox)
);
additionalChecks.push(checkComponents(meshData, input.validationTargets));
// Estimate required holes from validation targets
const expectedMinHoles = inferExpectedMinimumHoleCount(
input.validationTargets?.required_feature_checks,
);
additionalChecks.push(checkHoleCount(meshData, expectedMinHoles));
}
// Convert ValidationCheck → ValidationResult for backward compat
const additionalResults: ValidationResult[] = additionalChecks.map((check) => ({
rule_id: check.rule_id,
rule_name: check.rule_name,
level: check.level,
passed: check.passed,
status: check.status,
is_critical: check.is_critical,
message: check.message,
}));
return [...meshResults, ...visualResults, ...additionalResults];
}
export function getCriticalValidationFailures(results: ValidationResult[]): ValidationResult[] {
return results.filter((rule) =>
(rule.status === "FAIL" || (!rule.status && !rule.passed)) && rule.is_critical,
);
}
/**
* Build a structured validation report from results array.
*/
export function buildValidationReport(results: ValidationResult[]): ValidationReport {
const checks: ValidationCheck[] = results.map((r) => ({
rule_id: r.rule_id,
rule_name: r.rule_name,
level: r.level as ValidationCheck["level"],
passed: r.passed,
status: r.status,
is_critical: r.is_critical,
message: r.message,
}));
return computeReport(checks);
}