fix(ai): replace generic record checks
This commit is contained in:
@@ -103,14 +103,11 @@ export class PiMessagesResponseError extends Error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function isRecord(value: unknown): value is Record<string, unknown> {
|
|
||||||
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
||||||
}
|
|
||||||
|
|
||||||
function parsePiMessagesErrorBody(body: string): PiMessagesErrorBody | undefined {
|
function parsePiMessagesErrorBody(body: string): PiMessagesErrorBody | undefined {
|
||||||
try {
|
try {
|
||||||
const parsed = JSON.parse(body) as unknown;
|
const parsed = JSON.parse(body) as PiMessagesErrorBody | null;
|
||||||
return isRecord(parsed) && isRecord(parsed.error) ? (parsed as PiMessagesErrorBody) : undefined;
|
const error = parsed?.error;
|
||||||
|
return parsed && typeof error === "object" && error !== null && !Array.isArray(error) ? parsed : undefined;
|
||||||
} catch {
|
} catch {
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,28 +23,29 @@ export type RadiusOAuthCredential = OAuthCredential & {
|
|||||||
gatewayConfig?: RadiusGatewayConfig;
|
gatewayConfig?: RadiusGatewayConfig;
|
||||||
};
|
};
|
||||||
|
|
||||||
function isRecord(value: unknown): value is Record<string, unknown> {
|
|
||||||
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
||||||
}
|
|
||||||
|
|
||||||
function isRadiusGatewayModel(value: unknown): value is RadiusGatewayModel {
|
function isRadiusGatewayModel(value: unknown): value is RadiusGatewayModel {
|
||||||
|
if (typeof value !== "object" || value === null || Array.isArray(value)) return false;
|
||||||
|
const model = value as Partial<RadiusGatewayModel>;
|
||||||
return (
|
return (
|
||||||
isRecord(value) &&
|
typeof model.id === "string" &&
|
||||||
typeof value.id === "string" &&
|
typeof model.name === "string" &&
|
||||||
typeof value.name === "string" &&
|
typeof model.reasoning === "boolean" &&
|
||||||
typeof value.reasoning === "boolean" &&
|
Array.isArray(model.input) &&
|
||||||
Array.isArray(value.input) &&
|
typeof model.cost === "object" &&
|
||||||
isRecord(value.cost) &&
|
model.cost !== null &&
|
||||||
typeof value.contextWindow === "number" &&
|
!Array.isArray(model.cost) &&
|
||||||
typeof value.maxTokens === "number"
|
typeof model.contextWindow === "number" &&
|
||||||
|
typeof model.maxTokens === "number"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function sanitizeRadiusGatewayConfig(config: unknown): RadiusGatewayConfig | undefined {
|
function sanitizeRadiusGatewayConfig(config: unknown): RadiusGatewayConfig | undefined {
|
||||||
if (!isRecord(config) || typeof config.baseUrl !== "string" || !Array.isArray(config.models)) return undefined;
|
if (typeof config !== "object" || config === null || Array.isArray(config)) return undefined;
|
||||||
|
const { baseUrl, models } = config as Partial<RadiusGatewayConfig>;
|
||||||
|
if (typeof baseUrl !== "string" || !Array.isArray(models)) return undefined;
|
||||||
return {
|
return {
|
||||||
baseUrl: config.baseUrl,
|
baseUrl,
|
||||||
models: config.models.filter(isRadiusGatewayModel).map((model) => ({ ...model })),
|
models: models.filter(isRadiusGatewayModel).map((model) => ({ ...model })),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -65,10 +65,6 @@ function readJson(path) {
|
|||||||
return JSON.parse(readFileSync(path, "utf8"));
|
return JSON.parse(readFileSync(path, "utf8"));
|
||||||
}
|
}
|
||||||
|
|
||||||
function isRecord(value) {
|
|
||||||
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
||||||
}
|
|
||||||
|
|
||||||
function validateBundle(inputDir) {
|
function validateBundle(inputDir) {
|
||||||
const modelsPath = join(inputDir, "models.json");
|
const modelsPath = join(inputDir, "models.json");
|
||||||
const providerIndexPath = join(inputDir, "providers.json");
|
const providerIndexPath = join(inputDir, "providers.json");
|
||||||
@@ -77,7 +73,9 @@ function validateBundle(inputDir) {
|
|||||||
const models = JSON.parse(modelsBytes.toString("utf8"));
|
const models = JSON.parse(modelsBytes.toString("utf8"));
|
||||||
const providerIds = readJson(providerIndexPath);
|
const providerIds = readJson(providerIndexPath);
|
||||||
|
|
||||||
if (!isRecord(models)) throw new Error("models.json must contain an object");
|
if (typeof models !== "object" || models === null || Array.isArray(models)) {
|
||||||
|
throw new Error("models.json must contain an object");
|
||||||
|
}
|
||||||
if (!Array.isArray(providerIds) || !providerIds.every((value) => typeof value === "string")) {
|
if (!Array.isArray(providerIds) || !providerIds.every((value) => typeof value === "string")) {
|
||||||
throw new Error("providers.json must contain an array of provider IDs");
|
throw new Error("providers.json must contain an array of provider IDs");
|
||||||
}
|
}
|
||||||
@@ -93,13 +91,21 @@ function validateBundle(inputDir) {
|
|||||||
let modelCount = 0;
|
let modelCount = 0;
|
||||||
for (const providerId of providerIds) {
|
for (const providerId of providerIds) {
|
||||||
const providerModels = models[providerId];
|
const providerModels = models[providerId];
|
||||||
if (!isRecord(providerModels)) throw new Error(`Provider catalog must be an object: ${providerId}`);
|
if (typeof providerModels !== "object" || providerModels === null || Array.isArray(providerModels)) {
|
||||||
|
throw new Error(`Provider catalog must be an object: ${providerId}`);
|
||||||
|
}
|
||||||
const providerFile = readJson(join(providersDir, `${providerId}.json`));
|
const providerFile = readJson(join(providersDir, `${providerId}.json`));
|
||||||
if (!isDeepStrictEqual(providerFile, providerModels)) {
|
if (!isDeepStrictEqual(providerFile, providerModels)) {
|
||||||
throw new Error(`Provider shard does not match models.json: ${providerId}`);
|
throw new Error(`Provider shard does not match models.json: ${providerId}`);
|
||||||
}
|
}
|
||||||
for (const [modelId, model] of Object.entries(providerModels)) {
|
for (const [modelId, model] of Object.entries(providerModels)) {
|
||||||
if (!isRecord(model) || model.id !== modelId || model.provider !== providerId) {
|
if (
|
||||||
|
typeof model !== "object" ||
|
||||||
|
model === null ||
|
||||||
|
Array.isArray(model) ||
|
||||||
|
model.id !== modelId ||
|
||||||
|
model.provider !== providerId
|
||||||
|
) {
|
||||||
throw new Error(`Invalid model entry: ${providerId}/${modelId}`);
|
throw new Error(`Invalid model entry: ${providerId}/${modelId}`);
|
||||||
}
|
}
|
||||||
modelCount++;
|
modelCount++;
|
||||||
@@ -181,13 +187,20 @@ function uploadJson(bucket, endpoint, sourcePath, key, cacheControl) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function validateIndex(index) {
|
function validateIndex(index) {
|
||||||
if (!isRecord(index) || index.schemaVersion !== CATALOG_SCHEMA_VERSION) {
|
if (
|
||||||
|
typeof index !== "object" ||
|
||||||
|
index === null ||
|
||||||
|
Array.isArray(index) ||
|
||||||
|
index.schemaVersion !== CATALOG_SCHEMA_VERSION
|
||||||
|
) {
|
||||||
throw new Error(`Existing ${CATALOG_INDEX_KEY} has an unsupported schema`);
|
throw new Error(`Existing ${CATALOG_INDEX_KEY} has an unsupported schema`);
|
||||||
}
|
}
|
||||||
if (!Array.isArray(index.catalogs)) throw new Error(`Existing ${CATALOG_INDEX_KEY} has no catalogs array`);
|
if (!Array.isArray(index.catalogs)) throw new Error(`Existing ${CATALOG_INDEX_KEY} has no catalogs array`);
|
||||||
for (const catalog of index.catalogs) {
|
for (const catalog of index.catalogs) {
|
||||||
if (
|
if (
|
||||||
!isRecord(catalog) ||
|
typeof catalog !== "object" ||
|
||||||
|
catalog === null ||
|
||||||
|
Array.isArray(catalog) ||
|
||||||
typeof catalog.minimumPiVersion !== "string" ||
|
typeof catalog.minimumPiVersion !== "string" ||
|
||||||
typeof catalog.revision !== "string"
|
typeof catalog.revision !== "string"
|
||||||
) {
|
) {
|
||||||
|
|||||||
Reference in New Issue
Block a user