@autobe/agent
Version:
AI backend server code generator
220 lines (192 loc) • 6.99 kB
text/typescript
import { AutoBeOpenApi } from "@autobe/interface";
import { StringUtil } from "@autobe/utils";
import typia, { IValidation } from "typia";
import { AutoBeSystemPromptConstant } from "../../../constants/AutoBeSystemPromptConstant";
export const fulfillJsonSchemaErrorMessages = (
errors: IValidation.IError[],
): void => {
for (const e of errors)
fulfillTypeAsArrayError(e) ||
fulfillEnumInsteadOfConstError(e) ||
fulfillNoRequiredError(e) ||
fulfillObjectMetadataMisplacement(e) ||
fulfillNestedObjectError(e) ||
fulfillJsonSchemaFormat(e);
};
const fulfillTypeAsArrayError = (e: IValidation.IError): boolean => {
if (
// type := ["number", "string", ...] case
isInvalidJsonSchema(e) &&
typia.is<{ type: string[] }>(e.value) === true
) {
e.description =
AutoBeSystemPromptConstant.INTERFACE_SCHEMA_MISSING_TYPE_ARRAY.replace(
"${{JSON}}",
StringUtil.trim`
{
"oneOf": [
${e.value.type.map((t) => ` { "type": ${JSON.stringify(t)}, ... },`).join("\n")}
],${"description" in e.value ? `\n "description": ${JSON.stringify(e.value.description)},` : ""}
}
`,
);
return true;
}
return false;
};
const fulfillEnumInsteadOfConstError = (e: IValidation.IError): boolean => {
if (
// enum to const
isInvalidJsonSchema(e) &&
// biome-ignore lint: intended
typia.is<{ enum: any[] }>(e.value) === true
) {
e.description = StringUtil.trim`
**Invalid Schema: "enum" keyword is not supported in AutoBE.**
The "enum" keyword is prohibited. AutoBE requires you to use the "oneOf"
construct with individual "const" values instead. This design ensures
better type safety and documentation clarity.
Convert your schema to the following format:
\`\`\`json
{
"oneOf": [
${e.value.enum.map((t) => ` { "const": ${JSON.stringify(t)} },`).join("\n")}
],${"description" in e.value ? `\n "description": ${JSON.stringify(e.value.description)},` : ""}
}
\`\`\`
You must make this correction. The validator will continue to reject your
schema until you replace "enum" with the proper "oneOf" + "const" structure.
`;
return true;
}
return false;
};
const fulfillNoRequiredError = (e: IValidation.IError): boolean => {
if (
// no required property
e.value === undefined &&
e.path.endsWith(".required") &&
e.expected === "Array<string>"
) {
e.description =
AutoBeSystemPromptConstant.INTERFACE_SCHEMA_MISSING_REQUIRED;
return true;
}
return false;
};
const fulfillObjectMetadataMisplacement = (e: IValidation.IError): boolean => {
if (isInvalidJsonSchema(e) === false) return false;
const validate = (props: {
key: string;
expected: string;
actual: string;
place: string;
purpose: string;
}): boolean => {
e.expected = "undefined";
e.description = StringUtil.trim`
**Structural Error: "${props.key}" is in the wrong location**
You placed "${props.key}" inside the "properties" object, but it is a
metadata field that belongs at the object type level, outside of "properties".
- Your placement: \`${props.actual}\`
- Correct placement: \`${props.expected}\`
The "${props.key}" field describes ${props.purpose} and must be placed
at the schema's top level alongside "type" and "properties".
**Your current structure (incorrect)**:
\`\`\`json
{
"type": "object",
"properties": {
...,
"${props.key}": ${JSON.stringify(e.value)}
}
}
\`\`\`
**Required structure**:
\`\`\`json
{
"type": "object",
"${props.key}": ${JSON.stringify(e.value)},
"properties": { ... }
}
\`\`\`
Move "${props.key}" from ${e.path} to ${props.place}. The validator will
continue to reject your schema until this structural correction is made.
`;
return true;
};
if (
e.path.endsWith(`.properties.required`) === true &&
Array.isArray(e.value) === true
)
return validate({
key: "required",
expected: `AutoBeOpenApi.IJsonSchemaDescriptive.IObject.required`,
actual: `AutoBeOpenApi.IJsonSchemaDescriptive.IObject.properties.required`,
place: e.path.replace(`.properties.required`, `.required`),
purpose: "which properties are mandatory",
});
return false;
};
const fulfillNestedObjectError = (e: IValidation.IError): boolean => {
if (isExcludedObjectType(e) === true) {
// nested object
e.description =
AutoBeSystemPromptConstant.INTERFACE_SCHEMA_MISSING_NESTED_OBJECT;
return true;
}
return false;
};
const fulfillJsonSchemaFormat = (e: IValidation.IError): boolean => {
const supported: string[] =
typia.misc.literals<
Required<AutoBeOpenApi.IJsonSchema.IString>["format"]
>();
if (
e.path.endsWith(".format") &&
typeof e.value === "string" &&
supported.every((s) => e.expected.includes(JSON.stringify(s)))
) {
e.expected = "undefined";
e.description = StringUtil.trim`
**Invalid "format" Value: ${JSON.stringify(e.value)} is not supported.**
The "format" property value ${JSON.stringify(e.value)} is not supported in the
JSOn schema specification (type: \`AutoBeOpenApi.IJsonSchema.IString.format\`).
Supported values are:
${supported.map((s) => `- ${s}`).join("\n")}
If your intended format does not exactly match one of the above values,
you must remove the "format" property entirely from this schema.
Do not attempt to use similar or alternative format strings.
Simply delete the "format" property — it is optional.
The validator will continue to reject your schema until the "format"
property is either removed or set to an exactly matching supported value.
`;
return true;
}
return false;
};
const isExcludedObjectType = (error: IValidation.IError): boolean =>
error.expected.includes("|") &&
((error.expected.includes("AutoBeOpenApi.IJsonSchema.IConstant") &&
error.expected.includes("AutoBeOpenApi.IJsonSchema.IArray")) ||
(error.expected.includes(
"AutoBeOpenApi.IJsonSchemaDescriptive.IConstant",
) &&
error.expected.includes("AutoBeOpenApi.IJsonSchemaDescriptive.IArray")) ||
(error.expected.includes("AutoBeOpenApi.IJsonSchemaProperty.IConstant") &&
error.expected.includes("AutoBeOpenApi.IJsonSchemaProperty.IArray"))) &&
typia.is<{
type: "object";
}>(error.value) === true;
const isInvalidJsonSchema = (e: IValidation.IError): boolean =>
e.expected.startsWith("(") &&
e.expected.endsWith(")") &&
e.expected.includes("|") &&
e.expected
.split("|")
.map((s) => s.trim())
.some(
(s) =>
s.startsWith("AutoBeOpenApi.IJsonSchema.") ||
s.startsWith("AutoBeOpenApi.IJsonSchemaDescriptive."),
);