From 7ee9944ab572e23b2b7e31ceef66bc7ee8a4cde4 Mon Sep 17 00:00:00 2001 From: Alejandro Rosales Date: Tue, 24 Feb 2026 15:20:19 -0600 Subject: [PATCH] feat: add @toon-format/toon dependency and integrate into OpenAI notebooks - Updated deno.lock and package.json to include @toon-format/toon@^2.1.0. - Modified background-openai.ipynb to handle error responses and adjust execution counts. - Enhanced definitions-openai.ipynb to include additional metadata for perishables and origen schemas. - Refactored embeddings-openai.ipynb to improve error handling and output formatting. - Updated create-chat-conversation function to append messages and responses to conversations. - Added SQL functions for appending conversation data to the database. --- deno.lock | 8 +- notebooks/background-openai.ipynb | 64 ++++----- notebooks/definitions-openai.ipynb | 12 +- notebooks/embeddings-openai.ipynb | 127 ++++++++---------- package.json | 1 + .../create-chat-conversation/index.ts | 93 +++++++++++++ ...0223212325_agregar_nombre_conversacion.sql | 18 +++ .../20260224211719_agregar_conversaciones.sql | 21 +++ 8 files changed, 234 insertions(+), 110 deletions(-) create mode 100644 supabase/migrations/20260224211719_agregar_conversaciones.sql diff --git a/deno.lock b/deno.lock index 5aae0d0..f51e275 100644 --- a/deno.lock +++ b/deno.lock @@ -20,6 +20,7 @@ "jsr:@zod/zod@^4.3.5": "4.3.5", "npm:@supabase/supabase-js@2": "2.90.1", "npm:@supabase/supabase-js@^2.90.1": "2.90.1", + "npm:@toon-format/toon@^2.1.0": "2.1.0", "npm:@types/bun@^1.3.5": "1.3.5", "npm:deno@^2.6.4": "2.6.4", "npm:openai@6.16.0": "6.16.0_zod@3.25.76", @@ -182,6 +183,9 @@ "@supabase/storage-js" ] }, + "@toon-format/toon@2.1.0": { + "integrity": "sha512-JwWptdF5eOA0HaQxbKAzkpQtR4wSWTEfDlEy/y3/4okmOAX1qwnpLZMmtEWr+ncAhTTY1raCKH0kteHhSXnQqg==" + }, "@types/bun@1.3.5": { "integrity": "sha512-RnygCqNrd3srIPEWBd5LFeUYG7plCoH2Yw9WaZGyNmdTEei+gWaHqydbaIRkIkcbXwhBT94q78QljxN0Sk838w==", "dependencies": [ @@ -515,8 +519,7 @@ "minipass", "minizlib", "yallist" - ], - "deprecated": true + ] }, "tr46@0.0.3": { "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==" @@ -587,6 +590,7 @@ "packageJson": { "dependencies": [ "npm:@supabase/supabase-js@^2.90.1", + "npm:@toon-format/toon@^2.1.0", "npm:@types/bun@^1.3.5", "npm:deno@^2.6.4", "npm:openai@^6.16.0", diff --git a/notebooks/background-openai.ipynb b/notebooks/background-openai.ipynb index 4f33a78..66d066a 100644 --- a/notebooks/background-openai.ipynb +++ b/notebooks/background-openai.ipynb @@ -2,7 +2,7 @@ "cells": [ { "cell_type": "code", - "execution_count": null, + "execution_count": 1, "id": "bb61ec88", "metadata": {}, "outputs": [], @@ -19,7 +19,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 2, "id": "8baca5f2", "metadata": {}, "outputs": [], @@ -32,7 +32,7 @@ }, { "cell_type": "code", - "execution_count": 19, + "execution_count": 7, "id": "26b3bb36", "metadata": {}, "outputs": [], @@ -55,28 +55,36 @@ }, { "cell_type": "code", - "execution_count": 21, + "execution_count": 9, "id": "8abb080e", "metadata": {}, "outputs": [ { "data": { "text/plain": [ - "\u001b[32m\"e39952c1-a53b-43bf-8033-feda493a0651\"\u001b[39m" + "{\n", + " message: \u001b[32m\"TypeError: error sending request for url (http://127.0.0.1:54321/rest/v1/planes_estudio?select=*): client error (Connect): tcp connect error: No se puede establecer una conexión ya que el equipo de destino denegó expresamente dicha conexión. (os error 10061)\"\u001b[39m,\n", + " details: \u001b[32m\"TypeError: error sending request for url (http://127.0.0.1:54321/rest/v1/planes_estudio?select=*): client error (Connect): tcp connect error: No se puede establecer una conexión ya que el equipo de destino denegó expresamente dicha conexión. (os error 10061)\\n\"\u001b[39m +\n", + " \u001b[32m\" at async mainFetch (ext:deno_fetch/26_fetch.js:192:12)\\n\"\u001b[39m +\n", + " \u001b[32m\" at async fetch (ext:deno_fetch/26_fetch.js:475:11)\\n\"\u001b[39m +\n", + " \u001b[32m\" at async :2:31\"\u001b[39m,\n", + " hint: \u001b[32m\"\"\u001b[39m,\n", + " code: \u001b[32m\"\"\u001b[39m\n", + "}" ] }, - "execution_count": 21, + "execution_count": 9, "metadata": {}, "output_type": "execute_result" } ], "source": [ - "plan.id" + "error" ] }, { "cell_type": "code", - "execution_count": 22, + "execution_count": 12, "id": "a382bdc6", "metadata": {}, "outputs": [ @@ -84,9 +92,9 @@ "data": { "text/plain": [ "{\n", - " id: \u001b[32m\"resp_0f1906be7e83879e00699895d7f1cc8193a960e6ccdab1d377\"\u001b[39m,\n", + " id: \u001b[32m\"resp_0ae72cbd0894279200699c8e42941c8192ba4a09e280b21a17\"\u001b[39m,\n", " object: \u001b[32m\"response\"\u001b[39m,\n", - " created_at: \u001b[33m1771607511\u001b[39m,\n", + " created_at: \u001b[33m1771867714\u001b[39m,\n", " status: \u001b[32m\"queued\"\u001b[39m,\n", " background: \u001b[33mtrue\u001b[39m,\n", " completed_at: \u001b[1mnull\u001b[22m,\n", @@ -130,15 +138,12 @@ " truncation: \u001b[32m\"disabled\"\u001b[39m,\n", " usage: \u001b[1mnull\u001b[22m,\n", " user: \u001b[1mnull\u001b[22m,\n", - " metadata: {\n", - " tabla: \u001b[32m\"planes_estudio\"\u001b[39m,\n", - " id: \u001b[32m\"e39952c1-a53b-43bf-8033-feda493a0651\"\u001b[39m\n", - " },\n", + " metadata: { tabla: \u001b[32m\"planes_estudio\"\u001b[39m, id: \u001b[32m\"2\"\u001b[39m },\n", " output_text: \u001b[32m\"\"\u001b[39m\n", "}" ] }, - "execution_count": 22, + "execution_count": 12, "metadata": {}, "output_type": "execute_result" } @@ -150,7 +155,7 @@ " input: \"Hola mundo\",\n", " metadata: {\n", " tabla: \"planes_estudio\",\n", - " id: plan.id,\n", + " id: '2',\n", " },\n", " text: {\n", " format: {\n", @@ -183,7 +188,7 @@ }, { "cell_type": "code", - "execution_count": 23, + "execution_count": 13, "id": "6b2470ed", "metadata": {}, "outputs": [ @@ -200,13 +205,13 @@ "data": { "text/plain": [ "{\n", - " id: \u001b[32m\"resp_0f1906be7e83879e00699895d7f1cc8193a960e6ccdab1d377\"\u001b[39m,\n", + " id: \u001b[32m\"resp_0ae72cbd0894279200699c8e42941c8192ba4a09e280b21a17\"\u001b[39m,\n", " object: \u001b[32m\"response\"\u001b[39m,\n", - " created_at: \u001b[33m1771607511\u001b[39m,\n", + " created_at: \u001b[33m1771867714\u001b[39m,\n", " status: \u001b[32m\"completed\"\u001b[39m,\n", " background: \u001b[33mtrue\u001b[39m,\n", " billing: { payer: \u001b[32m\"developer\"\u001b[39m },\n", - " completed_at: \u001b[33m1771607519\u001b[39m,\n", + " completed_at: \u001b[33m1771867721\u001b[39m,\n", " error: \u001b[1mnull\u001b[22m,\n", " frequency_penalty: \u001b[33m0\u001b[39m,\n", " incomplete_details: \u001b[1mnull\u001b[22m,\n", @@ -216,12 +221,12 @@ " model: \u001b[32m\"gpt-5-mini-2025-08-07\"\u001b[39m,\n", " output: [\n", " {\n", - " id: \u001b[32m\"rs_0f1906be7e83879e00699895d976548193ad40e9aae0b1b4f2\"\u001b[39m,\n", + " id: \u001b[32m\"rs_0ae72cbd0894279200699c8e43cd4c8192bcbc70792b6aa621\"\u001b[39m,\n", " type: \u001b[32m\"reasoning\"\u001b[39m,\n", " summary: []\n", " },\n", " {\n", - " id: \u001b[32m\"msg_0f1906be7e83879e00699895ddf62881938811097ba62e8724\"\u001b[39m,\n", + " id: \u001b[32m\"msg_0ae72cbd0894279200699c8e48976481928e1f205210636717\"\u001b[39m,\n", " type: \u001b[32m\"message\"\u001b[39m,\n", " status: \u001b[32m\"completed\"\u001b[39m,\n", " content: [\n", @@ -229,7 +234,7 @@ " type: \u001b[32m\"output_text\"\u001b[39m,\n", " annotations: [],\n", " logprobs: [],\n", - " text: \u001b[32m'{\"ai-message\":\"¡Hola! ¿Cómo puedo ayudarte hoy? Si te refieres al clásico “Hola mundo” en programación, puedo mostrarte ejemplos en varios lenguajes, explicar su historia o ayudarte a crear tu primer programa. ¿Qué prefieres?\",\"suggested-topic\":\"El uso del programa “Hola mundo” como herramienta pedagógica en la enseñanza de la programación: impacto en la comprensión inicial, variaciones por lenguaje y mejores prácticas didácticas.\"}'\u001b[39m\n", + " text: \u001b[32m'{\"ai-message\":\"¡Hola! ¿En qué puedo ayudarte hoy? Puedo mostrar ejemplos de \\\\\"Hola mundo\\\\\" en distintos lenguajes, explicar conceptos de programación, ayudar con una tarea o sugerir recursos. Dime qué prefieres.\",\"suggested-topic\":\"El papel del programa \\\\\"Hola Mundo\\\\\" en la enseñanza de la programación: comparación práctica entre Python, Java y C (sintaxis, tiempo de arranque, facilidad de aprendizaje y percepción del estudiante).\"}'\u001b[39m\n", " }\n", " ],\n", " role: \u001b[32m\"assistant\"\u001b[39m\n", @@ -268,20 +273,17 @@ " usage: {\n", " input_tokens: \u001b[33m167\u001b[39m,\n", " input_tokens_details: { cached_tokens: \u001b[33m0\u001b[39m },\n", - " output_tokens: \u001b[33m437\u001b[39m,\n", + " output_tokens: \u001b[33m482\u001b[39m,\n", " output_tokens_details: { reasoning_tokens: \u001b[33m320\u001b[39m },\n", - " total_tokens: \u001b[33m604\u001b[39m\n", + " total_tokens: \u001b[33m649\u001b[39m\n", " },\n", " user: \u001b[1mnull\u001b[22m,\n", - " metadata: {\n", - " tabla: \u001b[32m\"planes_estudio\"\u001b[39m,\n", - " id: \u001b[32m\"e39952c1-a53b-43bf-8033-feda493a0651\"\u001b[39m\n", - " },\n", - " output_text: \u001b[32m'{\"ai-message\":\"¡Hola! ¿Cómo puedo ayudarte hoy? Si te refieres al clásico “Hola mundo” en programación, puedo mostrarte ejemplos en varios lenguajes, explicar su historia o ayudarte a crear tu primer programa. ¿Qué prefieres?\",\"suggested-topic\":\"El uso del programa “Hola mundo” como herramienta pedagógica en la enseñanza de la programación: impacto en la comprensión inicial, variaciones por lenguaje y mejores prácticas didácticas.\"}'\u001b[39m\n", + " metadata: { tabla: \u001b[32m\"planes_estudio\"\u001b[39m, id: \u001b[32m\"2\"\u001b[39m },\n", + " output_text: \u001b[32m'{\"ai-message\":\"¡Hola! ¿En qué puedo ayudarte hoy? Puedo mostrar ejemplos de \\\\\"Hola mundo\\\\\" en distintos lenguajes, explicar conceptos de programación, ayudar con una tarea o sugerir recursos. Dime qué prefieres.\",\"suggested-topic\":\"El papel del programa \\\\\"Hola Mundo\\\\\" en la enseñanza de la programación: comparación práctica entre Python, Java y C (sintaxis, tiempo de arranque, facilidad de aprendizaje y percepción del estudiante).\"}'\u001b[39m\n", "}" ] }, - "execution_count": 23, + "execution_count": 13, "metadata": {}, "output_type": "execute_result" } diff --git a/notebooks/definitions-openai.ipynb b/notebooks/definitions-openai.ipynb index ae139c7..ee796f7 100644 --- a/notebooks/definitions-openai.ipynb +++ b/notebooks/definitions-openai.ipynb @@ -83,11 +83,13 @@ }, { "cell_type": "code", - "execution_count": 26, + "execution_count": null, "id": "1403c789", "metadata": {}, "outputs": [], "source": [ + "import { title } from \"node:process\";\n", + "\n", "const combinedSchema = {\n", " type: \"object\",\n", " $defs: {\n", @@ -126,10 +128,12 @@ " fabricante: { type: \"string\", maxLength: 100 },\n", "\n", " // Agregar campo del preset\n", - " perishables: { $ref: \"#/$defs/perishables\" },\n", - " origen: { $ref: \"#/$defs/origen\" },\n", + " perishables: {\n", + " $ref: \"#/$defs/perishables\",\n", + " \"x-column\": \"perishables\",\n", + " },\n", "\n", - " perecedero: { type: \"null\", \"x-column\": \"perishable\", const: null },\n", + " origen: { $ref: \"#/$defs/origen\" },\n", " },\n", " required: [\"nombre\", \"fabricante\", \"perishables\", \"origen\", \"perecedero\"],\n", " additionalProperties: false,\n", diff --git a/notebooks/embeddings-openai.ipynb b/notebooks/embeddings-openai.ipynb index d2f1083..5480cb1 100644 --- a/notebooks/embeddings-openai.ipynb +++ b/notebooks/embeddings-openai.ipynb @@ -2,7 +2,7 @@ "cells": [ { "cell_type": "code", - "execution_count": 3, + "execution_count": 2, "id": "902bff53", "metadata": {}, "outputs": [], @@ -19,7 +19,7 @@ }, { "cell_type": "code", - "execution_count": 4, + "execution_count": 3, "id": "3b0acbc3", "metadata": {}, "outputs": [], @@ -32,63 +32,18 @@ }, { "cell_type": "code", - "execution_count": 8, + "execution_count": 1, "id": "dae842ad", "metadata": {}, "outputs": [ { - "name": "stdout", - "output_type": "stream", - "text": [ - "{\n", - " object: \"list\",\n", - " data: [\n", - " {\n", - " object: \"embedding\",\n", - " index: 0,\n", - " embedding: [\n", - " 0.017360063269734383, 0.022553985938429832, 0.025286488234996796,\n", - " -0.009742671623826027, -0.016438385471701622, -0.014725150540471077,\n", - " -0.020840751007199287, 0.02215278521180153, -0.02024437114596367,\n", - " 0.007834257557988167, 0.0029981620609760284, -0.04109596461057663,\n", - " 0.030165957286953926, -0.03402615711092949, 0.0008369643473997712,\n", - " 0.00044016868923790753, -0.027129843831062317, -0.04248390346765518,\n", - " -0.058683738112449646, 0.034655068069696426, 0.013738414272665977,\n", - " 0.02060219831764698, 0.016687780618667603, 0.0059637944214046,\n", - " -0.010306521318852901, 0.014898642897605896, -0.008826415985822678,\n", - " 0.042136918753385544, 0.009477011859416962, -0.03953453525900841,\n", - " -0.012198670767247677, -0.040055014193058014, -0.019409440457820892,\n", - " -0.0210576169192791, -0.0392959825694561, -0.005996324121952057,\n", - " -0.0022486215457320213, 0.05209103599190712, 0.0009325206046923995,\n", - " 0.013163721188902855, 0.0505296029150486, 0.014540815725922585,\n", - " 0.0027921402361243963, 0.03190087899565697, 0.00004917589103570208,\n", - " 0.03209605813026428, -0.038363464176654816, -0.02424553595483303,\n", - " 0.014096241444349289, 0.02559009939432144, -0.06089576333761215,\n", - " -0.011016755364835262, -0.026696112006902695, 0.050659723579883575,\n", - " 0.019561246037483215, 0.050833214074373245, 0.014529972337186337,\n", - " -0.023204581812024117, -0.014399852603673935, -0.009292676113545895,\n", - " 0.003149967873468995, 0.02007087878882885, 0.025503354147076607,\n", - " 0.0230094026774168, 0.04471761733293533, 0.01479020994156599,\n", - " -0.06692461669445038, 0.03591288626194, -0.037127330899238586,\n", - " 0.029558734968304634, -0.017576929181814194, 0.01953955926001072,\n", - " -0.027888871729373932, -0.0041095963679254055, 0.0004940461367368698,\n", - " -0.011266149580478668, -0.04200679808855057, 0.03537072241306305,\n", - " -0.0015058581484481692, -0.05551750585436821, -0.05287174880504608,\n", - " 0.016373327001929283, 0.005573437083512545, -0.01235047634691,\n", - " -0.009005329571664333, -0.034047845751047134, -0.018368486315011978,\n", - " -0.00271217105910182, 0.007731246296316385, -0.020981714129447937,\n", - " -0.040900785475969315, -0.03727913647890091, -0.059030722826719284,\n", - " -0.04536821320652962, 0.02257567271590233, 0.08579189330339432,\n", - " 0.014605875127017498, -0.030686434358358383, -0.0019084142986685038,\n", - " 0.02216362953186035,\n", - " ... 1436 more items\n", - " ]\n", - " }\n", - " ],\n", - " model: \"text-embedding-3-small\",\n", - " usage: { prompt_tokens: 13, total_tokens: 13 }\n", - "}\n", - "✅ Insertados textos con embeddings\n" + "ename": "ReferenceError", + "evalue": "openai is not defined", + "output_type": "error", + "traceback": [ + "Stack trace:", + "ReferenceError: openai is not defined", + " at :5:15" ] } ], @@ -120,7 +75,7 @@ }, { "cell_type": "code", - "execution_count": 13, + "execution_count": 12, "id": "2d8fa9a9", "metadata": {}, "outputs": [ @@ -129,20 +84,26 @@ "output_type": "stream", "text": [ "🔎 Consulta: ¿Cómo son buscados vectores?\n", - "\n", - "- Los vectores se usan con el cliente de OPENAI embeddings\n", - " Similaridad: 0.5639\n", - "\n", - "- Supabase permite almacenar vectores usando pgvector.\n", - " Similaridad: 0.4758\n", - "\n", - "- Los embeddings convierten texto en representaciones numéricas.\n", - " Similaridad: 0.3645\n", "\n" ] + }, + { + "data": { + "text/plain": [ + "\u001b[32m\"code: PGRST202\\n\"\u001b[39m +\n", + " \u001b[32m'details: \"Searched for the function public.buscar_documentos with parameters match_count, query_embedding or with a single unnamed json/jsonb parameter, but no matches were found in the schema cache.\"\\n'\u001b[39m +\n", + " \u001b[32m\"hint: Perhaps you meant to call the function public.unaccent\\n\"\u001b[39m +\n", + " \u001b[32m'message: \"Could not find the function public.buscar_documentos(match_count, query_embedding) in the schema cache\"'\u001b[39m" + ] + }, + "execution_count": 12, + "metadata": {}, + "output_type": "execute_result" } ], "source": [ + "import { encode } from \"@toon-format/toon\";\n", + "\n", "const consulta = \"¿Cómo son buscados vectores?\";\n", "\n", "const emb = await openai.embeddings.create({\n", @@ -157,22 +118,42 @@ " match_count: 3,\n", "});\n", "\n", - "if (error) throw error;\n", + "/* if (error) throw error; */\n", "\n", "console.log(`🔎 Consulta: ${consulta}\\n`);\n", - "for (const r of data ?? []) {\n", - " console.log(`- ${r.contenido}`);\n", - " console.log(` Similaridad: ${Number(r.similarity).toFixed(4)}\\n`);\n", - "}\n" + "encode(error, {\n", + " indent: 2,\n", + " delimiter: \",\",\n", + " keyFolding: \"off\",\n", + " flattenDepth: Infinity,\n", + "});\n" ] }, { "cell_type": "code", - "execution_count": null, + "execution_count": 10, "id": "791a94d8", "metadata": {}, - "outputs": [], - "source": [] + "outputs": [ + { + "data": { + "text/plain": [ + "{\n", + " code: \u001b[32m\"PGRST202\"\u001b[39m,\n", + " details: \u001b[32m\"Searched for the function public.buscar_documentos with parameters match_count, query_embedding or with a single unnamed json/jsonb parameter, but no matches were found in the schema cache.\"\u001b[39m,\n", + " hint: \u001b[32m\"Perhaps you meant to call the function public.unaccent\"\u001b[39m,\n", + " message: \u001b[32m\"Could not find the function public.buscar_documentos(match_count, query_embedding) in the schema cache\"\u001b[39m\n", + "}" + ] + }, + "execution_count": 10, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "error" + ] } ], "metadata": { diff --git a/package.json b/package.json index a0f5f90..f5c50a5 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,7 @@ { "dependencies": { "@supabase/supabase-js": "^2.90.1", + "@toon-format/toon": "^2.1.0", "deno": "^2.6.4", "openai": "^6.16.0", "supabase": "^2.72.6" diff --git a/supabase/functions/create-chat-conversation/index.ts b/supabase/functions/create-chat-conversation/index.ts index 6057960..73bf4b0 100644 --- a/supabase/functions/create-chat-conversation/index.ts +++ b/supabase/functions/create-chat-conversation/index.ts @@ -285,6 +285,11 @@ app.post(`${prefix}/conversations/:id/messages`, async (c) => { "Excelente, actualmente tu plan de estudio tiene una redacción clara, pero podrías mejorar el perfil de ingreso para hacerlo más atractivo.", ], }, + "is_refusal": { + type: "boolean", + description: + "Indica si la respuesta es un refusal (es decir, la pregunta no tiene que ver con el plan de estudio)", + }, }, }, }, @@ -301,6 +306,56 @@ app.post(`${prefix}/conversations/:id/messages`, async (c) => { const model = CREATE_CHAT_CONVERSATION_STRUCTURED_MODELO; const prompt = body.user_prompt ?? body.content; + // append message of the user to conversacion_json (which guarantees a JSONB default to '[]') + /** + * appended includes timestamp, user, prompt and fields (if any) + */ + + type AppendedMessage = { + timestamp: string; + user: string; + prompt: string; + fields?: string[]; + }; + + type AppendedResponse = { + timestamp: string; + user: "assistant"; + refusal: boolean; + message: string; + recommendations?: { + texto_mejora: string; + campo_afectado: string; + aplicada: false; + }; + }; + + type AppendedItem = AppendedMessage | AppendedResponse; + + let appended: AppendedItem = { + timestamp: new Date().toISOString(), + user: /* user.email ?? user.id ??*/ "unknown", + prompt, + fields: body.campos, + }; + + const { error: appendErr } = await supabase.rpc( + "append_conversacion_plan", + { + p_id: conversation_plan_id, + p_appended: JSON.stringify(appended), + }, + ); + + if (appendErr) { + throw new HttpError( + 500, + "append_conversation_failed", + "No se pudo agregar el mensaje a la conversación", + appendErr, + ); + } + const resp = await openai.responses.create({ conversation: row.openai_conversation_id, model, @@ -322,6 +377,44 @@ app.post(`${prefix}/conversations/:id/messages`, async (c) => { ], }); + const respuestaJSON = JSON.parse(resp.output_text ?? "{}"); + + // Now an item with the assistant response and the structured data (if any) should be + appended = { + timestamp: new Date().toISOString(), + user: "assistant", + refusal: resp.output_text === "refusal", + // the ai-message field is the response + message: respuestaJSON?.["ai-message"] ?? "", + recommendations: resp.output_text + ? Object.entries(respuestaJSON).filter(([k]) => k !== "ai-message") + .map( + ([campo_afectado, texto_mejora]) => ({ + campo_afectado, + texto_mejora, + aplicada: false, + }), + ) + : undefined, + } as AppendedResponse; + + const { error: appendRespErr } = await supabase.rpc( + "append_conversacion_plan", + { + p_id: conversation_plan_id, + p_appended: JSON.stringify(appended), + }, + ); + + if (appendRespErr) { + throw new HttpError( + 500, + "append_response_failed", + "No se pudo agregar la respuesta a la conversación", + appendRespErr, + ); + } + return withCors(jsonResponse({ ok: true, openai_response_id: resp.id, diff --git a/supabase/migrations/20260223212325_agregar_nombre_conversacion.sql b/supabase/migrations/20260223212325_agregar_nombre_conversacion.sql index 4fb2325..6a501b9 100644 --- a/supabase/migrations/20260223212325_agregar_nombre_conversacion.sql +++ b/supabase/migrations/20260223212325_agregar_nombre_conversacion.sql @@ -2,4 +2,22 @@ alter table "public"."conversaciones_plan" add column "nombre" text default ('Ch alter table "public"."conversaciones_plan" alter column "conversacion_json" set default '[]'::jsonb; +CREATE OR REPLACE FUNCTION public.append_conversacion_asignatura(p_id uuid, p_append jsonb) + RETURNS void + LANGUAGE sql +AS $function$ + update conversaciones_asignatura + set conversacion_json = coalesce(conversacion_json, '[]'::jsonb) || p_append + where id = p_id; +$function$ +; +CREATE OR REPLACE FUNCTION public.append_conversacion_plan(p_id uuid, p_append jsonb) + RETURNS void + LANGUAGE sql +AS $function$ + update conversaciones_plan + set conversacion_json = coalesce(conversacion_json, '[]'::jsonb) || p_append + where id = p_id; +$function$ +; \ No newline at end of file diff --git a/supabase/migrations/20260224211719_agregar_conversaciones.sql b/supabase/migrations/20260224211719_agregar_conversaciones.sql new file mode 100644 index 0000000..4233c9a --- /dev/null +++ b/supabase/migrations/20260224211719_agregar_conversaciones.sql @@ -0,0 +1,21 @@ +CREATE OR REPLACE FUNCTION public.append_conversacion_asignatura(p_id uuid, p_append jsonb) + RETURNS void + LANGUAGE sql +AS $function$ + update conversaciones_asignatura + set conversacion_json = coalesce(conversacion_json, '[]'::jsonb) || p_append + where id = p_id; +$function$ +; + +CREATE OR REPLACE FUNCTION public.append_conversacion_plan(p_id uuid, p_append jsonb) + RETURNS void + LANGUAGE sql +AS $function$ + update conversaciones_plan + set conversacion_json = coalesce(conversacion_json, '[]'::jsonb) || p_append + where id = p_id; +$function$ +; + +