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[], ) { console.log(campos); console.log(definicion); if (!definicion || definicion.type !== "object" || !definicion.properties) { return definicion; } // Campos extra de control const extra = { properties: { "ai-message": { type: "string", description: "Tu respuesta conversacional dirigida al profesor explicando qué mejoraste.", }, "is_refusal": { type: "boolean", description: "Indica si la solicitud es inapropiada.", }, }, }; const out = structuredClone(definicion); // 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) ); out.properties = Object.fromEntries(entries); if (Array.isArray(out.required)) { out.required = out.required.filter((k: string) => campos.includes(k)); } // 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.additionalProperties = false; return out; } 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}`); } } export function safeAsignaturaForPrompt(asignatura: any) { const copy = structuredClone(asignatura); // Eliminamos la definición de la estructura para que no ensucie el prompt // y solo queden los datos reales de la asignatura if (copy?.estructuras_asignatura) delete copy.estructuras_asignatura; return copy; } export function getAsignaturaSystemPrompt(asignatura: any, campos: string[]) { const asignaturaLimpia = safeAsignaturaForPrompt(asignatura); const nombreAsig = asignaturaLimpia?.nombre || "la asignatura"; if (campos.length === 0) { return `Eres un asistente experto en diseño curricular. Tu objetivo es ayudar al profesor a mejorar su asignatura: "${nombreAsig}". DATOS ACTUALES: ${JSON.stringify(asignaturaLimpia)}. COMPORTAMIENTO: - El usuario aún no ha seleccionado campos específicos para mejorar. - NO propongas cambios técnicos detallados ni rellenes campos del JSON todavía. - Saluda cordialmente y menciona qué partes de esta asignatura puedes ayudar a mejorar (objetivos, contenidos, criterios, etc.). - Mantén una conversación fluida y espera a que el usuario elija qué quiere trabajar.`; } return `Eres un asistente experto en diseño curricular trabajando sobre: "${nombreAsig}". DATOS ACTUALES: ${JSON.stringify(asignaturaLimpia)}. TAREA CRÍTICA: El usuario ha solicitado mejorar estos ${campos.length} campos: ${campos.join(", ")}. REGLAS DE ORO: 1. Debes proporcionar una propuesta de mejora para CADA UNO de los campos solicitados. 2. No omitas ninguno. Si un campo no requiere cambios drásticos, optimiza su redacción técnica. 3. En el JSON, cada campo debe contener tu propuesta de texto mejorado. 4. En 'ai-message', resume los cambios hechos en cada uno de los campos solicitados.`; }