forked from CopilotKit/CopilotKit
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathvalidate-constraints.ts
More file actions
139 lines (118 loc) · 3.99 KB
/
Copy pathvalidate-constraints.ts
File metadata and controls
139 lines (118 loc) · 3.99 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
/**
* Constraint Validator
*
* Validates that a manifest's declared demos are compatible with its
* declared generative_ui approaches and interaction_modalities.
*
* Usage:
* npx tsx showcase/scripts/validate-constraints.ts <slug>
* npx tsx showcase/scripts/validate-constraints.ts --all
*/
import fs from "fs";
import path from "path";
import { fileURLToPath } from "url";
import yaml from "yaml";
const __dirname = path.dirname(fileURLToPath(import.meta.url));
const ROOT = path.resolve(__dirname, "..");
const CONSTRAINTS_PATH = path.join(ROOT, "shared", "constraints.yaml");
const PACKAGES_DIR = path.join(ROOT, "integrations");
interface Constraints {
generative_ui: Record<string, { allowed?: string[]; excluded?: string[] }>;
interaction_modalities: Record<string, { excluded?: string[] }>;
}
interface Manifest {
slug: string;
generative_ui?: string[];
interaction_modalities?: string[];
demos: Array<{ id: string; name: string }>;
}
function loadConstraints(): Constraints {
const raw = fs.readFileSync(CONSTRAINTS_PATH, "utf-8");
return yaml.parse(raw) as Constraints;
}
function loadManifest(slug: string): Manifest {
const manifestPath = path.join(PACKAGES_DIR, slug, "manifest.yaml");
const raw = fs.readFileSync(manifestPath, "utf-8");
return yaml.parse(raw) as Manifest;
}
export function validateManifestConstraints(
manifest: Manifest,
constraints: Constraints,
): string[] {
const errors: string[] = [];
// Skip if manifest doesn't declare these optional fields
const genUiApproaches = manifest.generative_ui;
const modalities = manifest.interaction_modalities;
for (const demo of manifest.demos) {
// Generative UI validation (allowlist-based)
if (genUiApproaches && genUiApproaches.length > 0) {
const allowedByAny = genUiApproaches.some((approach) => {
const rule = constraints.generative_ui[approach];
return rule?.allowed?.includes(demo.id) ?? false;
});
if (!allowedByAny) {
const declared = genUiApproaches.join(", ");
errors.push(
`[${manifest.slug}] Demo '${demo.id}' is not allowed by any declared generative_ui approach [${declared}]`,
);
}
}
// Interaction modality validation (denylist-based)
if (modalities && modalities.length > 0) {
const excludedByAll = modalities.every((modality) => {
const rule = constraints.interaction_modalities[modality];
return rule?.excluded?.includes(demo.id) ?? false;
});
if (excludedByAll) {
const declared = modalities.join(", ");
errors.push(
`[${manifest.slug}] Demo '${demo.id}' is excluded by all declared interaction_modalities [${declared}]`,
);
}
}
}
return errors;
}
function main() {
const args = process.argv.slice(2);
if (args.length === 0 || args[0] === "--help") {
console.log("Usage:");
console.log(" npx tsx validate-constraints.ts <slug>");
console.log(" npx tsx validate-constraints.ts --all");
process.exit(0);
}
const constraints = loadConstraints();
let slugs: string[];
if (args[0] === "--all") {
slugs = fs
.readdirSync(PACKAGES_DIR, { withFileTypes: true })
.filter((d) => d.isDirectory())
.filter((d) =>
fs.existsSync(path.join(PACKAGES_DIR, d.name, "manifest.yaml")),
)
.map((d) => d.name);
} else {
slugs = [args[0]];
}
let allErrors: string[] = [];
for (const slug of slugs) {
const manifest = loadManifest(slug);
const errors = validateManifestConstraints(manifest, constraints);
if (errors.length > 0) {
allErrors.push(...errors);
} else {
console.log(` OK: ${slug} (constraints valid)`);
}
}
if (allErrors.length > 0) {
console.error("\nConstraint validation errors:");
for (const err of allErrors) {
console.error(` ERROR: ${err}`);
}
process.exit(1);
}
}
// Only run main when executed directly (not when imported as a module)
if (process.argv[1] === fileURLToPath(import.meta.url)) {
main();
}