Files
genesis-2/supabase/functions/create-chat-conversation/lib/plan.ts
T

104 lines
3.1 KiB
TypeScript

import { HttpError } from "./errors.ts";
export function pickSchemaFields(
definicion: any,
campos: string[],
) {
if (!definicion || definicion.type !== "object" || !definicion.properties) {
return definicion;
}
const extra = {
properties: {
"ai-message": {
type: "string",
description:
"Mensaje breve para el usuario final confirmando qué se mejoró y qué se hizo.",
examples: [
"Listo: mejoré la redacción del perfil de ingreso y propuse un tema de investigación alineado al plan.",
],
},
"is-refusal": {
type: "boolean",
description:
"Indica si el plan fue rechazado por el modelo. En caso de ser true, se espera un mensaje de rechazo en `ai-message`.",
},
},
};
const out = structuredClone(definicion);
// Si piden campos, filtramos propiedades/required a esos campos
const entries = Object.entries(out.properties).filter(([k]) =>
campos.includes(k)
);
out.properties = Object.fromEntries(entries);
if (Array.isArray(out.required)) {
out.required = out.required.filter((k: string) => campos.includes(k));
}
// Siempre agregamos ai-message
out.properties = { ...out.properties, ...extra.properties };
out.required = Array.isArray(out.required)
? [...new Set([...out.required, ...Object.keys(extra.properties)])]
: Object.keys(extra.properties);
return out;
}
export function pickSchemaAsignaturaFields(
definicion: any,
campos: string[],
) {
// 1. Validación inicial
if (!definicion || definicion.type !== "object" || !definicion.properties) {
return {
type: "object",
properties: {
"ai-message": { type: "string" },
"is_refusal": { type: "boolean" }
},
required: ["ai-message", "is_refusal"],
additionalProperties: false
};
}
// 2. Filtrar solo las propiedades técnicas que el usuario pidió
const filteredProperties = Object.fromEntries(
Object.entries(definicion.properties).filter(([k]) => campos.includes(k))
);
// 3. RECONSTRUIR el esquema incluyendo SIEMPRE los campos de control
const finalSchema = {
type: "object",
properties: {
"ai-message": {
type: "string",
description: "Tu respuesta conversacional dirigida al profesor."
},
"is_refusal": {
type: "boolean",
description: "Indica si la solicitud es inapropiada o no relacionada."
},
...filteredProperties // Aquí entran objetivo, contenido, etc.
},
// Forzamos que ai-message e is_refusal sean obligatorios siempre
required: ["ai-message", "is_refusal", ...Object.keys(filteredProperties)],
additionalProperties: false
};
return finalSchema;
}
export function safePlanForPrompt(plan: any) {
const copy = structuredClone(plan);
if (copy?.estructuras_plan) delete copy.estructuras_plan;
return copy;
}
export function assertUuid(v: string, name: string) {
// validación ligera
if (!v || typeof v !== "string" || v.length < 10) {
throw new HttpError(400, "bad_input", `Invalid ${name}`);
}
}