Agregar contexto de la asignatura al chat y se agrega estructura de contenido tematico y de criterios de evaluación para la respuesta estructurada
Supa-backup / run_db_backup (pull_request) Successful in 38s
CI / test (pull_request) Successful in 44s
Deploy Function / deploy (pull_request) Successful in 25s
Deploy Migrations to Production / deploy (pull_request) Successful in 9s

fix #55
This commit is contained in:
2026-03-12 16:02:38 -06:00
parent 0a1f61f587
commit 631e85b3d6
2 changed files with 120 additions and 25 deletions
+94
View File
@@ -0,0 +1,94 @@
export const definicionesDeEstructurasDeColumnas = {
contenido_tematico: {
"type": "object",
"properties": {
"x-column": {
"type": "string",
"enum": [
"contenido_tematico",
],
},
"x-definicion": {
"type": "array",
"items": {
"type": "object",
"properties": {
"unidad": {
"type": "integer",
},
"titulo": {
"type": "string",
},
"temas": {
"type": "array",
"items": {
"type": "object",
"properties": {
"nombre": {
"type": "string",
},
"horasEstimadas": {
"type": "integer",
"description":
"Horas del tema. La suma de todas las horasEstimadas debe ser igual a las horas académicas del prompt.",
},
},
"required": [
"nombre",
"horasEstimadas",
],
"additionalProperties": false,
},
},
},
"required": [
"unidad",
"titulo",
"temas",
],
"additionalProperties": false,
},
},
},
"required": [
"x-column",
"x-definicion",
],
"additionalProperties": false,
},
criterios_de_evaluacion: {
"type": "object",
"properties": {
"x-column": {
"type": "string",
"enum": [
"criterios_de_evaluacion",
],
},
"x-definicion": {
"type": "array",
"items": {
"type": "object",
"properties": {
"criterio": {
"type": "string",
},
"porcentaje": {
"type": "integer",
},
},
"required": [
"criterio",
"porcentaje",
],
"additionalProperties": false,
},
},
},
"required": [
"x-column",
"x-definicion",
],
"additionalProperties": false,
},
} as const;
@@ -1,5 +1,5 @@
import { definicionesDeEstructurasDeColumnas } from "../../_shared/estructuras.ts";
import { HttpError } from "./errors.ts";
export function pickSchemaFields(
definicion: any,
campos: string[],
@@ -45,17 +45,15 @@ export function pickSchemaFields(
return out;
}
export function pickSchemaAsignaturaFields(
definicion: any,
campos: string[],
) {
console.log(campos);
console.log(definicion);
if (!definicion || definicion.type !== "object" || !definicion.properties) {
return definicion;
}
// Campos extra de control
const extra = {
properties: {
"ai-message": {
@@ -70,33 +68,36 @@ export function pickSchemaAsignaturaFields(
};
const out = structuredClone(definicion);
const finalProperties: Record<string, any> = {};
const finalRequired: string[] = [];
// FILTRADO: Si el usuario pidió campos específicos, filtramos.
// Si campos está vacío, la lógica de Planes devolvería todo,
// pero aquí asumimos que siempre vienen campos si isStructured es true.
const entries = Object.entries(out.properties).filter(([k]) =>
campos.includes(k)
);
campos.forEach((key) => {
out.properties = Object.fromEntries(entries);
// REVISIÓN DE ESTRUCTURA ESPECIAL
if (key in definicionesDeEstructurasDeColumnas) {
// Extraemos solo el esquema del array que vive en 'x-definicion'
// Esto hace que la IA reciba el esquema del array directamente
const especial = (definicionesDeEstructurasDeColumnas as any)[key];
if (Array.isArray(out.required)) {
out.required = out.required.filter((k: string) => campos.includes(k));
finalProperties[key] = especial.properties["x-definicion"];
finalRequired.push(key);
}
// CAMPOS NORMALES (los que están en 'datos')
else if (out.properties[key]) {
finalProperties[key] = out.properties[key];
if (out.required?.includes(key)) {
finalRequired.push(key);
}
}
});
// SIEMPRE agregamos los campos de control (ai-message, is_refusal)
out.properties = { ...out.properties, ...extra.properties };
// Reconstruimos los requeridos para que OpenAI sepa que DEBE llenar los campos solicitados
out.required = Array.isArray(out.required)
? [...new Set([...out.required, ...Object.keys(extra.properties)])]
: Object.keys(extra.properties);
// Forzamos que no invente campos
out.properties = { ...finalProperties, ...extra.properties };
out.required = [...new Set([...finalRequired, ...Object.keys(extra.properties)])];
out.additionalProperties = false;
return out;
}
export function safePlanForPrompt(plan: any) {
const copy = structuredClone(plan);
if (copy?.estructuras_plan) delete copy.estructuras_plan;