57 lines
3.9 KiB
TypeScript
57 lines
3.9 KiB
TypeScript
import { readFileSync } from "node:fs";
|
|
import { rootPath } from "./src/config";
|
|
|
|
function assertCondition(condition: unknown, message: string, detail: unknown = {}): void {
|
|
if (!condition) throw new Error(`${message}: ${JSON.stringify(detail)}`);
|
|
}
|
|
|
|
const configPath = rootPath("config", "platform-infra", "sub2api-codex-pool.yaml");
|
|
const parsed = Bun.YAML.parse(readFileSync(configPath, "utf8")) as {
|
|
pool?: {
|
|
defaultAccountCapacity?: number;
|
|
minOwnerConcurrency?: number;
|
|
defaultTempUnschedulable?: {
|
|
enabled?: boolean;
|
|
rules?: Array<{ statusCode?: number; keywords?: string[]; durationMinutes?: number }>;
|
|
};
|
|
};
|
|
profiles?: { entries?: Array<{ profile?: string; accountName?: string; capacity?: number; openaiResponsesWebSocketsV2Mode?: string | null }> };
|
|
localCodex?: { responsesSmokeModel?: string };
|
|
};
|
|
|
|
const entries = parsed.profiles?.entries ?? [];
|
|
const rules = parsed.pool?.defaultTempUnschedulable?.rules ?? [];
|
|
const defaultCapacity = parsed.pool?.defaultAccountCapacity ?? 0;
|
|
const desiredCapacity = entries.reduce((total, entry) => total + (entry.capacity ?? defaultCapacity), 0);
|
|
const allowedWebSocketModes = new Set(["off", "ctx_pool", "passthrough"]);
|
|
|
|
assertCondition(entries.length > 0, "Codex pool must declare YAML-managed profile entries", parsed.profiles);
|
|
assertCondition(Number.isInteger(defaultCapacity) && defaultCapacity > 0, "defaultAccountCapacity must be a positive integer", parsed.pool);
|
|
assertCondition(entries.every((entry) => typeof entry.profile === "string" && entry.profile.length > 0), "profile entries must declare profile names", entries);
|
|
assertCondition(entries.every((entry) => typeof entry.accountName === "string" && entry.accountName.length > 0), "profile entries must declare account names", entries);
|
|
assertCondition(entries.every((entry) => entry.capacity === undefined || (Number.isInteger(entry.capacity) && entry.capacity > 0)), "profile capacity overrides must be positive integers when declared", entries);
|
|
assertCondition(
|
|
entries.every((entry) => entry.openaiResponsesWebSocketsV2Mode === undefined || entry.openaiResponsesWebSocketsV2Mode === null || allowedWebSocketModes.has(entry.openaiResponsesWebSocketsV2Mode)),
|
|
"profile WebSocket mode overrides must use supported values when declared",
|
|
entries,
|
|
);
|
|
assertCondition((parsed.pool?.minOwnerConcurrency ?? 0) >= desiredCapacity, "pool owner concurrency must not bottleneck the declared account capacity set", { minOwnerConcurrency: parsed.pool?.minOwnerConcurrency, desiredCapacity });
|
|
if (parsed.pool?.defaultTempUnschedulable?.enabled === true) {
|
|
assertCondition(rules.length > 0, "enabled temporary unschedulable policy must declare rules", parsed.pool?.defaultTempUnschedulable);
|
|
assertCondition(rules.every((rule) => Number.isInteger(rule.statusCode) && (rule.statusCode ?? 0) >= 100 && (rule.statusCode ?? 0) <= 599), "temporary unschedulable rules must declare valid HTTP status codes", rules);
|
|
assertCondition(rules.every((rule) => Array.isArray(rule.keywords) && rule.keywords.length > 0), "temporary unschedulable rules must declare non-empty keywords", rules);
|
|
assertCondition(rules.every((rule) => Number.isInteger(rule.durationMinutes) && (rule.durationMinutes ?? 0) > 0), "temporary unschedulable rules must declare positive cooldown durations", rules);
|
|
}
|
|
assertCondition(typeof parsed.localCodex?.responsesSmokeModel === "string" && parsed.localCodex.responsesSmokeModel.length > 0, "localCodex.responsesSmokeModel must be declared for Responses smoke validation", parsed.localCodex);
|
|
|
|
console.log(JSON.stringify({
|
|
ok: true,
|
|
checks: [
|
|
"routing config is schema-valid without profile-specific test gates",
|
|
"pool owner concurrency covers the YAML account capacity set",
|
|
"optional WebSocket mode overrides use supported values",
|
|
"temporary unschedulable rules are structurally valid when enabled",
|
|
"Responses smoke model is YAML-declared",
|
|
],
|
|
}));
|