UNPKG

node-llama-cpp

Version:

Run AI models locally on your machine with node.js bindings for llama.cpp. Enforce a JSON schema on the model output on the generation level

205 lines 10.1 kB
import { isGbnfJsonArraySchema, isGbnfJsonBasicSchemaIncludesType, isGbnfJsonBasicStringSchema, isGbnfJsonConstSchema, isGbnfJsonEnumSchema, isGbnfJsonFormatStringSchema, isGbnfJsonObjectSchema, isGbnfJsonOneOfSchema, isGbnfJsonRefSchema } from "./gbnfJson/types.js"; import { DefScopeDefs, joinDefs } from "./gbnfJson/utils/defsScope.js"; const maxTypeRepetition = 10; export function getTypeScriptTypeStringForGbnfJsonSchema(schema) { return _getTypeScriptTypeStringForGbnfJsonSchema(schema); } function _getTypeScriptTypeStringForGbnfJsonSchema(schema, printedDefs = new Set(), defs = {}, defScopeDefs = new DefScopeDefs()) { if (isGbnfJsonRefSchema(schema)) { const currentDefs = joinDefs(defs, schema.$defs); defScopeDefs.registerDefs(currentDefs); const ref = schema?.$ref; const referencePrefix = "#/$defs/"; if (ref == null || !ref.startsWith(referencePrefix)) return "any"; const defName = ref.slice(referencePrefix.length); const def = currentDefs[defName]; if (def == null) return "any"; else if (printedDefs.has(def)) { return [ "/* ", defName .replaceAll("\n", " ") .replaceAll("*/", "* /"), " type */ any" ].join(""); } const scopeDefs = defScopeDefs.defScopeDefs.get([defName, def]); if (scopeDefs == null) return "any"; printedDefs.add(def); return [ "/* Type: ", defName .replaceAll("\n", " ") .replaceAll("*/", "* /"), " */ ", _getTypeScriptTypeStringForGbnfJsonSchema(def, printedDefs, scopeDefs, defScopeDefs) ].join(""); } else if (isGbnfJsonOneOfSchema(schema)) { const currentDefs = joinDefs(defs, schema.$defs); defScopeDefs.registerDefs(currentDefs); const values = schema.oneOf .map((altSchema) => _getTypeScriptTypeStringForGbnfJsonSchema(altSchema, printedDefs, currentDefs, defScopeDefs)); return values.join(" | "); } else if (isGbnfJsonConstSchema(schema)) { return JSON.stringify(schema.const) ?? ""; } else if (isGbnfJsonEnumSchema(schema)) { return schema.enum .map((item) => JSON.stringify(item) ?? "") .filter((item) => item !== "") .join(" | "); } else if (isGbnfJsonObjectSchema(schema)) { const currentDefs = joinDefs(defs, schema.$defs); defScopeDefs.registerDefs(currentDefs); let addNewline = false; const valueTypes = Object.entries(schema.properties ?? {}) .map(([propName, propSchema]) => { const escapedValue = JSON.stringify(propName) ?? ""; const keyText = escapedValue.slice(1, -1) === propName ? propName : escapedValue; const valueType = _getTypeScriptTypeStringForGbnfJsonSchema(propSchema, printedDefs, currentDefs, defScopeDefs); if (keyText === "" || valueType === "") return ""; const mapping = keyText + ": " + valueType; const description = (propSchema.description != null && propSchema.description !== "") ? [propSchema.description] : []; const propInfo = []; if (isGbnfJsonBasicStringSchema(propSchema)) { if (propSchema.minLength != null && propSchema.minLength > 0) propInfo.push("minimum length: " + String(Math.floor(propSchema.minLength))); if (propSchema.maxLength != null) propInfo.push("maximum length: " + String(Math.floor(Math.max(propSchema.maxLength, propSchema.minLength ?? 0, 0)))); } else if (isGbnfJsonFormatStringSchema(propSchema)) { if (propSchema.format === "date-time") propInfo.push("format: ISO 8601 date-time"); else propInfo.push("format: " + String(propSchema.format)); } else if (isGbnfJsonArraySchema(propSchema)) { if (propSchema.minItems != null && propSchema.minItems > maxTypeRepetition) propInfo.push("minimum items: " + String(Math.floor(propSchema.minItems))); if (propSchema.maxItems != null) propInfo.push("maximum items: " + String(Math.floor(Math.max(propSchema.maxItems, propSchema.minItems ?? 0, 0)))); } else if (isGbnfJsonObjectSchema(propSchema)) { if (propSchema.minProperties != null && propSchema.minProperties > 0) propInfo.push("minimum number of properties: " + String(Math.floor(propSchema.minProperties))); if (propSchema.maxProperties != null) propInfo.push("maximum number of properties: " + String(Math.floor(Math.max(propSchema.maxProperties, propSchema.minProperties ?? 0, 0)))); } if (propInfo.length > 0) description.push(propInfo.join(", ")); if (description.length > 0) { addNewline = true; return [ "\n", "// ", description .join("\n") .split("\n") .join("\n// "), "\n", mapping ].join(""); } return mapping; }) .filter((item) => item !== ""); const knownPropertiesMapSyntax = [ "{", (addNewline && valueTypes.length > 0) ? [ "\n ", valueTypes .map((value) => value.split("\n").join("\n ")) .join(",\n ") .trimStart(), "\n" ].join("") : valueTypes.join(", "), "}" ].join(""); const additionalPropertiesMapSyntax = (schema.additionalProperties == null || schema.additionalProperties == false) ? undefined : schema.additionalProperties === true ? "{[key: string]: any}" : schema.additionalProperties != null ? ["{[key: string]: ", _getTypeScriptTypeStringForGbnfJsonSchema(schema.additionalProperties), "}"].join("") : undefined; if (valueTypes.length === 0 && additionalPropertiesMapSyntax != null) return additionalPropertiesMapSyntax; else if (additionalPropertiesMapSyntax != null) return [knownPropertiesMapSyntax, " & ", additionalPropertiesMapSyntax].join(""); return knownPropertiesMapSyntax; } else if (isGbnfJsonArraySchema(schema)) { const currentDefs = joinDefs(defs, schema.$defs); defScopeDefs.registerDefs(currentDefs); if (schema.maxItems === 0) return "[]"; if (schema.prefixItems != null && schema.prefixItems.length > 0) { const valueTypes = schema.prefixItems.map((item) => _getTypeScriptTypeStringForGbnfJsonSchema(item)); const restType = schema.items != null ? _getTypeScriptTypeStringForGbnfJsonSchema(schema.items, printedDefs, currentDefs, defScopeDefs) : "any"; if (schema.minItems != null) { for (let i = schema.prefixItems.length; i < Math.min(schema.prefixItems.length + maxTypeRepetition, schema.minItems); i++) valueTypes.push(restType); } if (schema.maxItems == null || schema.maxItems > valueTypes.length) valueTypes.push("..." + wrapWithParensIfNeeded(restType) + "[]"); return "[" + valueTypes.join(", ") + "]"; } else if (schema.items != null) { const valuesType = _getTypeScriptTypeStringForGbnfJsonSchema(schema.items, printedDefs, currentDefs, defScopeDefs); if (valuesType === "") return "[]"; if (schema.minItems != null) { if (schema.minItems === schema.maxItems) { if (schema.minItems < maxTypeRepetition) return "[" + (valuesType + ", ").repeat(schema.minItems).slice(0, -", ".length) + "]"; else return [ "[", (valuesType + ", ").repeat(maxTypeRepetition), "...", wrapWithParensIfNeeded(valuesType), "[]", "]" ].join(""); } else if (schema.minItems <= 0) return wrapWithParensIfNeeded(valuesType) + "[]"; else if (schema.minItems < maxTypeRepetition) return "[" + (valuesType + ", ").repeat(schema.minItems) + "..." + wrapWithParensIfNeeded(valuesType) + "[]]"; else return wrapWithParensIfNeeded(valuesType) + "[]"; } return wrapWithParensIfNeeded(valuesType) + "[]"; } return "any[]"; } const types = []; if (isGbnfJsonBasicSchemaIncludesType(schema, "string")) types.push("string"); if (isGbnfJsonBasicSchemaIncludesType(schema, "number")) types.push("number"); if (isGbnfJsonBasicSchemaIncludesType(schema, "integer")) types.push("bigint"); if (isGbnfJsonBasicSchemaIncludesType(schema, "boolean")) types.push("boolean"); if (isGbnfJsonBasicSchemaIncludesType(schema, "null")) types.push("null"); return types.join(" | "); } function wrapWithParensIfNeeded(text) { if (text.includes(" ") || text.includes("|") || text.includes("&") || text.includes("\n") || text.includes("\t")) return "(" + text + ")"; return text; } //# sourceMappingURL=getTypeScriptTypeStringForGbnfJsonSchema.js.map