Files
pikasTech-unidesk/scripts/platform-infra-sub2api-codex-routing-contract-test.ts
T

66 lines
4.8 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;
defaultAccountLoadFactor?: number;
minOwnerConcurrency?: number;
defaultTempUnschedulable?: {
enabled?: boolean;
rules?: Array<{ statusCode?: number; keywords?: string[]; durationMinutes?: number }>;
};
};
profiles?: { entries?: Array<{ profile?: string; accountName?: string; capacity?: number; loadFactor?: 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 defaultLoadFactor = parsed.pool?.defaultAccountLoadFactor ?? 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(Number.isInteger(defaultLoadFactor) && defaultLoadFactor > 0, "defaultAccountLoadFactor 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.loadFactor === undefined || (Number.isInteger(entry.loadFactor) && entry.loadFactor > 0)), "profile load factor 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);
const gateway502Rule = rules.find((rule) => rule.statusCode === 502);
const gateway502Keywords = new Set((gateway502Rule?.keywords ?? []).map((keyword) => keyword.toLowerCase()));
assertCondition(gateway502Keywords.has("recovered upstream error"), "502 temporary-unschedulable rule must catch recovered upstream error wrappers", gateway502Rule);
}
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",
"profile load factor overrides are YAML-controlled positive integers",
"optional WebSocket mode overrides use supported values",
"temporary unschedulable rules are structurally valid when enabled",
"generic recovered upstream error wrappers are caught by cooldown rules",
"Responses smoke model is YAML-declared",
],
}));