Se inserta el plan de estudios y se mandan crear los datos generales en segundo plano, y la edge funcion de openai-webhook-responses recibe la respuesta para actualizar el plan de estudio con los datos generales

This commit is contained in:
2026-02-25 17:39:09 -06:00
parent 9263587f0b
commit 527f3aa56f
7 changed files with 254 additions and 137 deletions
@@ -27,20 +27,32 @@ async function handleCompletedResponse(
switch (metadata.tabla) {
case "planes_estudio":
handlePlanesEstudioResponse(response);
await handlePlanesEstudioResponse(response);
break;
default:
console.warn("Tabla no reconocida:", metadata.tabla);
}
const output_text = response.output
.filter((item: any) => item.type === "message")
.flatMap((item: any) => item.content)
.filter((contentItem: any) => contentItem.type === "output_text")
.map((contentItem: any) => contentItem.text)
const direct = (response as unknown as { output_text?: unknown }).output_text;
if (typeof direct === "string" && direct.length) {
console.log("Response output:", direct);
return;
}
const output = (response as unknown as { output?: unknown }).output;
if (!Array.isArray(output)) {
console.log("Response output: (no output)");
return;
}
const outputText = output
.filter((item) => (item as { type?: unknown })?.type === "message")
.flatMap((item) => (item as { content?: unknown })?.content ?? [])
.filter((c) => (c as { type?: unknown })?.type === "output_text")
.map((c) => String((c as { text?: unknown })?.text ?? ""))
.join("");
console.log("Response output:", output_text);
console.log("Response output:", outputText);
}
Deno.serve(async (req) => {
@@ -1,6 +1,108 @@
import type { OpenAI } from "openai";
import { supabase } from "../supabase.ts";
import type { TablesUpdate } from "../../_shared/database.types.ts";
import type { Json } from "../../_shared/database.types.ts";
import { ResponseMetadata } from "../../_shared/utils.ts";
export function handleCrearPlanEstudio(response: OpenAI.Responses.Response) {
async function getEstadoId(clave: string): Promise<string | null> {
const { data, error } = await supabase
.from("estados_plan")
.select("id")
.eq("clave", clave)
.maybeSingle();
if (error) {
console.warn("No se pudo obtener estado:", clave, error);
return null;
}
return data?.id ?? null;
}
async function marcarFallido(planId: string): Promise<void> {
const fallId = await getEstadoId("FALLDA");
if (!fallId) {
console.warn("No existe estado FALLDA; no se pudo marcar fallo", {
planId,
});
return;
}
const { error } = await supabase
.from("planes_estudio")
.update({ estado_actual_id: fallId })
.eq("id", planId);
if (error) {
console.warn("No se pudo marcar plan como FALLDA", { planId, error });
}
}
function extractOutputText(response: OpenAI.Responses.Response): string {
const direct = (response as unknown as { output_text?: unknown }).output_text;
if (typeof direct === "string") return direct;
const output = (response as unknown as { output?: unknown }).output;
if (!Array.isArray(output)) return "";
// Fallback similar al usado en index.ts
try {
return output
.filter((item) => (item as { type?: unknown })?.type === "message")
.flatMap((item) => (item as { content?: unknown })?.content ?? [])
.filter((c) => (c as { type?: unknown })?.type === "output_text")
.map((c) => String((c as { text?: unknown })?.text ?? ""))
.join("");
} catch {
return "";
}
}
export async function handleCrearPlanEstudio(
response: OpenAI.Responses.Response,
): Promise<void> {
const metadata = response.metadata as ResponseMetadata | null;
const planId = metadata?.id;
if (!planId) {
console.warn("No se recibió metadata.id para actualizar el plan");
return;
}
try {
const borradorId = await getEstadoId("BORRADOR");
if (!borradorId) {
console.warn("No existe estado BORRADOR");
await marcarFallido(planId);
return;
}
const outputText = extractOutputText(response);
if (!outputText) {
console.warn("La respuesta no contiene output_text");
await marcarFallido(planId);
return;
}
let datos: Json;
try {
datos = JSON.parse(outputText) as Json;
} catch (e) {
console.warn("No se pudo parsear JSON de la respuesta", e);
await marcarFallido(planId);
return;
}
const { error } = await supabase
.from("planes_estudio")
.update({ datos, estado_actual_id: borradorId })
.eq("id", planId);
if (error) {
console.warn("No se pudo actualizar el plan con datos", {
planId,
error,
});
await marcarFallido(planId);
return;
}
} catch (e) {
console.warn("Fallo inesperado procesando plan", { planId, e });
await marcarFallido(planId);
return;
}
}
@@ -1,9 +1,10 @@
import { ResponseMetadata } from "../../_shared/utils.ts";
import type { OpenAI } from "openai";
import { handleCrearPlanEstudio } from "./crear.ts";
export function handlePlanesEstudioResponse(
export async function handlePlanesEstudioResponse(
response: OpenAI.Responses.Response,
) {
): Promise<void> {
const metadata = response.metadata as ResponseMetadata | null;
if (!metadata || !metadata.accion) {
console.warn("No se recibió acción en la metadata");
@@ -12,14 +13,14 @@ export function handlePlanesEstudioResponse(
switch (metadata.accion) {
case "crear":
// Lógica para crear un plan de estudio
break;
await handleCrearPlanEstudio(response);
return;
case "actualizar":
// Lógica para actualizar un plan de estudio
break;
return;
case "eliminar":
// Lógica para eliminar un plan de estudio
break;
return;
default:
console.warn("Acción no reconocida:", metadata.accion);
}
@@ -1,15 +1,19 @@
import { createClient } from "@supabase/supabase-js";
import type { Database } from "../_shared/database.types.ts";
const SUPABASE_URL = Deno.env.get("SUPABASE_URL");
const SUPABASE_ANON_KEY = Deno.env.get("SUPABASE_ANON_KEY");
const SUPABASE_SERVICE_ROLE_KEY = Deno.env.get("SUPABASE_SERVICE_ROLE_KEY");
if (!SUPABASE_URL || !SUPABASE_ANON_KEY) {
if (!SUPABASE_URL || (!SUPABASE_SERVICE_ROLE_KEY && !SUPABASE_ANON_KEY)) {
console.warn("Missing Supabase environment variables");
}
const supabase = createClient(
const supabaseKey = (SUPABASE_SERVICE_ROLE_KEY ?? SUPABASE_ANON_KEY) as string;
const supabase = createClient<Database>(
SUPABASE_URL as string,
SUPABASE_ANON_KEY as string,
supabaseKey,
);
export { supabase };