From 729d4346d618979e2fc0141340b9f41f449def94 Mon Sep 17 00:00:00 2001 From: ppf30 Date: Fri, 28 Nov 2025 12:37:04 +0100 Subject: [PATCH 01/10] modelo subido en model --- model/group1-shard1of1.bin:Zone.Identifier | Bin 0 -> 25 bytes model/model.json:Zone.Identifier | Bin 0 -> 25 bytes 2 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 model/group1-shard1of1.bin:Zone.Identifier create mode 100644 model/model.json:Zone.Identifier diff --git a/model/group1-shard1of1.bin:Zone.Identifier b/model/group1-shard1of1.bin:Zone.Identifier new file mode 100644 index 0000000000000000000000000000000000000000..d6c1ec682968c796b9f5e9e080cc6f674b57c766 GIT binary patch literal 25 dcma!!%Fjy;DN4*MPD?F{<>dl#JyUFr831@K2xdl#JyUFr831@K2x Date: Fri, 28 Nov 2025 13:48:08 +0100 Subject: [PATCH 02/10] borrado --- model/group1-shard1of1.bin:Zone.Identifier | Bin 25 -> 0 bytes model/model.json:Zone.Identifier | Bin 25 -> 0 bytes 2 files changed, 0 insertions(+), 0 deletions(-) delete mode 100644 model/group1-shard1of1.bin:Zone.Identifier delete mode 100644 model/model.json:Zone.Identifier diff --git a/model/group1-shard1of1.bin:Zone.Identifier b/model/group1-shard1of1.bin:Zone.Identifier deleted file mode 100644 index d6c1ec682968c796b9f5e9e080cc6f674b57c766..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 25 dcma!!%Fjy;DN4*MPD?F{<>dl#JyUFr831@K2xdl#JyUFr831@K2x Date: Fri, 28 Nov 2025 15:24:02 +0100 Subject: [PATCH 03/10] s3 --- package.json | 6 +++--- server.js | 1 + 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/package.json b/package.json index 7e709810..4c9f6328 100644 --- a/package.json +++ b/package.json @@ -11,11 +11,11 @@ }, "author": "Iren Lorenzo Fonseca", "license": "ISC", - "bugs": { - }, + "bugs": {}, "dependencies": { "@tensorflow/tfjs": "^4.22.0", "@tensorflow/tfjs-backend-wasm": "^4.22.0", + "dotenv": "^17.2.3", "express": "^5.1.0" } -} \ No newline at end of file +} diff --git a/server.js b/server.js index 4d44675f..eac84d3c 100644 --- a/server.js +++ b/server.js @@ -1,5 +1,6 @@ // server.js // Entry point del servicio PREDICT +require("dotenv").config(); const express = require("express"); const path = require("path"); From 73538ed928d46efd67155c2c0a8c34ad2473e158 Mon Sep 17 00:00:00 2001 From: ppf30 Date: Fri, 28 Nov 2025 16:42:37 +0100 Subject: [PATCH 04/10] docker --- .dockerignore | 8 ++++++++ dockerfile | 26 ++++++++++++++++++++++++++ 2 files changed, 34 insertions(+) create mode 100644 .dockerignore create mode 100644 dockerfile diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 00000000..ed60f1f4 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,8 @@ +node_modules +npm-debug.log +Dockerfile +docker-compose.yml +.vscode +.git +.gitignore +.env \ No newline at end of file diff --git a/dockerfile b/dockerfile new file mode 100644 index 00000000..8990ac6c --- /dev/null +++ b/dockerfile @@ -0,0 +1,26 @@ +# Imagen base con Node 22 +FROM node:22-slim + + +# Directorio de trabajo dentro del contenedor +WORKDIR /usr/src/app + + +# Copiamos primero manifiestos para cachear dependencias +COPY package*.json ./ + + +# Instalamos dependencias de producción +RUN npm ci --omit=dev + + +# Copiamos el resto del código (incluye /model) +COPY . . + + +# El servicio escucha en 3002 +EXPOSE 3002 + + +# Comando de arranque +CMD ["node", "server.js"] \ No newline at end of file From 71ed90c7d14ec1a3469bcf4841649d0ff24f7750 Mon Sep 17 00:00:00 2001 From: ppf30 Date: Wed, 10 Dec 2025 20:21:46 +0100 Subject: [PATCH 05/10] =?UTF-8?q?revisi=C3=B3n=20server.js?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- server.js | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/server.js b/server.js index eac84d3c..16a175df 100644 --- a/server.js +++ b/server.js @@ -4,6 +4,7 @@ require("dotenv").config(); const express = require("express"); const path = require("path"); +const mongoose = require("mongoose"); const predictRoutes = require("./routes/predictRoutes"); const { initModel } = require("./services/tfModelService"); @@ -20,6 +21,7 @@ app.use("/model", express.static(modelDir)); app.use("/", predictRoutes); // Arranque del servidor + carga del modelo +/* app.listen(PORT, async () => { const serverUrl = `http://localhost:${PORT}`; console.log(`[PREDICT] Servicio escuchando en ${serverUrl}`); @@ -31,3 +33,14 @@ app.listen(PORT, async () => { process.exit(1); } }); +*/ +// conectar a Mongo +mongoose + .connect(process.env.MONGO_URI) + .then(() => console.log("MongoDB Atlas conectado (ACQUIRE)")) + .catch((err) => { + console.error("Error Mongo:", err); + process.exit(1); + }); +app.use("/", acquireRoutes); +app.use(express.json()); From 58912c0a2b044eb92580d56d16c359a4bedda2f4 Mon Sep 17 00:00:00 2001 From: ppf30 Date: Fri, 12 Dec 2025 12:29:50 +0100 Subject: [PATCH 06/10] Subiendo todo From ca28fefc17fde2078104f05cb50ba5423ea9f9be Mon Sep 17 00:00:00 2001 From: ppf30 Date: Fri, 12 Dec 2025 12:56:02 +0100 Subject: [PATCH 07/10] =?UTF-8?q?completado=20sever.js=20con=20la=20conexi?= =?UTF-8?q?=C3=B3na=20mongoDB=20y=20al=20modelo=20predictivo?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- server.js | 36 ++++++++++++++++++------------------ 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/server.js b/server.js index 16a175df..f87007bf 100644 --- a/server.js +++ b/server.js @@ -3,6 +3,7 @@ require("dotenv").config(); const express = require("express"); +const app = express(); const path = require("path"); const mongoose = require("mongoose"); const predictRoutes = require("./routes/predictRoutes"); @@ -10,37 +11,36 @@ const { initModel } = require("./services/tfModelService"); const PORT = process.env.PORT || 3002; -const app = express(); + app.use(express.json()); // Servir la carpeta del modelo TFJS (model/model.json + pesos) const modelDir = path.resolve(__dirname, "model"); app.use("/model", express.static(modelDir)); -// Rutas del servicio PREDICT + +// conectar a Mongo +mongoose + .connect(process.env.MONGO_URI) + .then(() => console.log("MongoDB conectado (PREDICT)")) + .catch((err) => { + console.error("Error al conectar MongoDB:", err); + process.exit(1); + }); + app.use("/", predictRoutes); -// Arranque del servidor + carga del modelo -/* + app.listen(PORT, async () => { - const serverUrl = `http://localhost:${PORT}`; - console.log(`[PREDICT] Servicio escuchando en ${serverUrl}`); + const serverUrl = "http://localhost:${PORT}"; + console.log("PREDICT escuchando en ${serverUrl}"); try { + // Inicializa el modelo predictivo await initModel(serverUrl); + console.log(" Modelo predictivo cargado correctamente."); } catch (err) { - console.error("Error al inicializar modelo:", err); + console.error("Error al inicializar el modelo predictivo:", err); process.exit(1); } }); -*/ -// conectar a Mongo -mongoose - .connect(process.env.MONGO_URI) - .then(() => console.log("MongoDB Atlas conectado (ACQUIRE)")) - .catch((err) => { - console.error("Error Mongo:", err); - process.exit(1); - }); -app.use("/", acquireRoutes); -app.use(express.json()); From 2aae4ee730764718574f3f1902ad9996a96d98c6 Mon Sep 17 00:00:00 2001 From: ppf30 Date: Fri, 12 Dec 2025 16:58:02 +0100 Subject: [PATCH 08/10] =?UTF-8?q?correcci=C3=B3n=20problemas=20con=20mongo?= =?UTF-8?q?=20db.=20Ya=20funciona=20en=20postman?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- model/Prediction.js | 24 ++++++++++++++++++++++++ package.json | 5 ++++- server.js | 13 +++++++------ 3 files changed, 35 insertions(+), 7 deletions(-) create mode 100644 model/Prediction.js diff --git a/model/Prediction.js b/model/Prediction.js new file mode 100644 index 00000000..314fb07d --- /dev/null +++ b/model/Prediction.js @@ -0,0 +1,24 @@ +'use strict' + +const mongoose = require('mongoose'); +const Schema = mongoose.Schema; + +const PredictionSchema = new Schema({ + source: String, + timestamp: { type: Date, default: Date.now }, // Cuándo se hizo + latencyMs: Number, // Cuánto tardó + features: [Number], // Los datos de entrada (array de números) + prediction: Number, + + featureCount: Number, + scalerVersion: String, + createdAt: { type: Date, default: Date.now }, + targetDate: Date, + dailyValues: [Number], // Valores diarios originales sin escalar + + // Metadatos de Kunna (alias, fechas usadas...) + + // El resultado (array de números) +}); + +module.exports = mongoose.model('Prediction', PredictionSchema); \ No newline at end of file diff --git a/package.json b/package.json index 4c9f6328..42a9fc1b 100644 --- a/package.json +++ b/package.json @@ -16,6 +16,9 @@ "@tensorflow/tfjs": "^4.22.0", "@tensorflow/tfjs-backend-wasm": "^4.22.0", "dotenv": "^17.2.3", - "express": "^5.1.0" + "express": "^5.1.0", + "mongodb": "^7.0.0", + "mongoose": "^9.0.1" } } + diff --git a/server.js b/server.js index f87007bf..f853a215 100644 --- a/server.js +++ b/server.js @@ -14,10 +14,6 @@ const PORT = process.env.PORT || 3002; app.use(express.json()); -// Servir la carpeta del modelo TFJS (model/model.json + pesos) -const modelDir = path.resolve(__dirname, "model"); -app.use("/model", express.static(modelDir)); - // conectar a Mongo mongoose @@ -28,12 +24,17 @@ mongoose process.exit(1); }); +// Servir la carpeta del modelo TFJS (model/model.json + pesos) +const modelDir = path.resolve(__dirname, "model"); +app.use("/model", express.static(modelDir)); + + app.use("/", predictRoutes); app.listen(PORT, async () => { - const serverUrl = "http://localhost:${PORT}"; - console.log("PREDICT escuchando en ${serverUrl}"); + const serverUrl = `http://localhost:${PORT}`; + console.log(`PREDICT escuchando en ${serverUrl}`); try { // Inicializa el modelo predictivo From 17beb328aa30b0cc88ad7f9ec07de4af6654b633 Mon Sep 17 00:00:00 2001 From: ppf30 Date: Wed, 17 Dec 2025 09:47:11 +0100 Subject: [PATCH 09/10] =?UTF-8?q?revisi=C3=B3n=20del=20c=C3=B3digo=20y=20c?= =?UTF-8?q?orrecci=C3=B3n=20de=20comentarios?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- model/Prediction.js | 13 ++++++------- services/tfModelService.js | 11 ++--------- 2 files changed, 8 insertions(+), 16 deletions(-) diff --git a/model/Prediction.js b/model/Prediction.js index 314fb07d..da618943 100644 --- a/model/Prediction.js +++ b/model/Prediction.js @@ -1,3 +1,4 @@ +//model/prediction.js 'use strict' const mongoose = require('mongoose'); @@ -5,20 +6,18 @@ const Schema = mongoose.Schema; const PredictionSchema = new Schema({ source: String, - timestamp: { type: Date, default: Date.now }, // Cuándo se hizo - latencyMs: Number, // Cuánto tardó - features: [Number], // Los datos de entrada (array de números) + timestamp: { type: Date, default: Date.now }, + latencyMs: Number, + features: [Number], prediction: Number, featureCount: Number, scalerVersion: String, createdAt: { type: Date, default: Date.now }, targetDate: Date, - dailyValues: [Number], // Valores diarios originales sin escalar + dailyValues: [Number], - // Metadatos de Kunna (alias, fechas usadas...) - - // El resultado (array de números) + }); module.exports = mongoose.model('Prediction', PredictionSchema); \ No newline at end of file diff --git a/services/tfModelService.js b/services/tfModelService.js index aff8b787..01af4168 100644 --- a/services/tfModelService.js +++ b/services/tfModelService.js @@ -34,10 +34,7 @@ function wasmFileDirUrl() { return pathToFileURL(distFsPath + path.sep).href; } -/** - * Inicializa backend WASM y carga el GraphModel - * serverUrl: ej. http://localhost:3002 - */ + async function initModel(serverUrl) { const wasmPath = wasmFileDirUrl(); wasmBackend.setWasmPaths(wasmPath); @@ -66,7 +63,6 @@ async function initModel(serverUrl) { throw new Error("No se ha podido detectar inputName/outputName/inputDim"); } - // Warm-up const Xwarm = tf.zeros([1, inputDim], "float32"); let out; if (typeof model.executeAsync === "function") { @@ -83,10 +79,7 @@ async function initModel(serverUrl) { console.log("[TF] Modelo listo."); } -/** - * Ejecuta el modelo con un vector de features - * Devuelve un escalar >= 0 - */ + async function predict(features) { if (!ready || !model) { throw new Error("Model not ready"); From 084cad5fd8e21d978b21f21a9d0665cb2a9eda47 Mon Sep 17 00:00:00 2001 From: ppf30 Date: Wed, 17 Dec 2025 19:46:01 +0100 Subject: [PATCH 10/10] predict funciona pero no almacena las predicciones en MongoDB --- controllers/predictController.js | 32 +++++++++++++++++++++++++------- model/Prediction.js | 2 +- server.js | 11 +++++------ services/tfModelService.js | 13 ++++++++++--- 4 files changed, 41 insertions(+), 17 deletions(-) diff --git a/controllers/predictController.js b/controllers/predictController.js index 20bc61e3..3da42fbd 100644 --- a/controllers/predictController.js +++ b/controllers/predictController.js @@ -1,5 +1,7 @@ + // controllers/predictController.js const { getModelInfo, predict } = require("../services/tfModelService"); +const Prediction = require("../model/Prediction"); function health(req, res) { res.json({ @@ -60,20 +62,36 @@ async function doPredict(req, res) { }); } - const prediction = await predict(features); + // Ejecutar el modelo + const predictionValue = await predict(features); const latencyMs = Date.now() - start; - const timestamp = new Date().toISOString(); + const timestamp = new Date(); - // De momento sin MongoDB → predictionId null - res.status(201).json({ - predictionId: null, - prediction, + // Guardar en Mongo solo campos válidos + const responsePred = await Prediction.create({ + features, + prediction: predictionValue, timestamp, + latencyMs, + featureCount: meta.featureCount, + scalerVersion: meta.scalerVersion || "v1", + createdAt: timestamp, + predictGroup: "predict" + }); + + res.status(201).json({ + predictionId: responsePred._id, + prediction: predictionValue, + timestamp: timestamp.toISOString(), latencyMs }); + } catch (err) { console.error("Error en /predict:", err); - res.status(500).json({ error: "Internal error" }); + + res.status(500).json({ + error: "Internal error", + }); } } diff --git a/model/Prediction.js b/model/Prediction.js index da618943..c7cbe82c 100644 --- a/model/Prediction.js +++ b/model/Prediction.js @@ -20,4 +20,4 @@ const PredictionSchema = new Schema({ }); -module.exports = mongoose.model('Prediction', PredictionSchema); \ No newline at end of file +module.exports = mongoose.model("Prediction", PredictionSchema); \ No newline at end of file diff --git a/server.js b/server.js index f853a215..5b7adcac 100644 --- a/server.js +++ b/server.js @@ -10,20 +10,19 @@ const predictRoutes = require("./routes/predictRoutes"); const { initModel } = require("./services/tfModelService"); const PORT = process.env.PORT || 3002; - - -app.use(express.json()); - +const MONGO_URI = process.env.MONGO_URI; // conectar a Mongo mongoose - .connect(process.env.MONGO_URI) - .then(() => console.log("MongoDB conectado (PREDICT)")) + .connect(MONGO_URI) + .then(() => {console.log("MongoDB conectado (PREDICT)")}) .catch((err) => { console.error("Error al conectar MongoDB:", err); process.exit(1); }); +app.use(express.json()); + // Servir la carpeta del modelo TFJS (model/model.json + pesos) const modelDir = path.resolve(__dirname, "model"); app.use("/model", express.static(modelDir)); diff --git a/services/tfModelService.js b/services/tfModelService.js index 01af4168..71ec499b 100644 --- a/services/tfModelService.js +++ b/services/tfModelService.js @@ -34,7 +34,10 @@ function wasmFileDirUrl() { return pathToFileURL(distFsPath + path.sep).href; } - +/** + * Inicializa backend WASM y carga el GraphModel + * serverUrl: ej. http://localhost:3002 + */ async function initModel(serverUrl) { const wasmPath = wasmFileDirUrl(); wasmBackend.setWasmPaths(wasmPath); @@ -63,6 +66,7 @@ async function initModel(serverUrl) { throw new Error("No se ha podido detectar inputName/outputName/inputDim"); } + // Warm-up const Xwarm = tf.zeros([1, inputDim], "float32"); let out; if (typeof model.executeAsync === "function") { @@ -79,7 +83,10 @@ async function initModel(serverUrl) { console.log("[TF] Modelo listo."); } - +/** + * Ejecuta el modelo con un vector de features + * Devuelve un escalar >= 0 + */ async function predict(features) { if (!ready || !model) { throw new Error("Model not ready"); @@ -115,4 +122,4 @@ module.exports = { initModel, getModelInfo, predict -}; +}; \ No newline at end of file