ingest.js 3.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110
  1. import { Router } from "express";
  2. import { z } from "zod";
  3. import multer from "multer";
  4. import { extractDocumentsFromUpload, ingestDocuments } from "../chat/ingest.js";
  5. import fs from "node:fs";
  6. export const ingestRouter = Router();
  7. const upload = multer({
  8. storage: multer.memoryStorage(),
  9. limits: { fileSize: 25 * 1024 * 1024 }
  10. });
  11. const docSchema = z.object({
  12. id: z.string().min(1).optional(),
  13. text: z.string().min(1),
  14. source: z.string().min(1).optional(),
  15. metadata: z.record(z.any()).optional()
  16. });
  17. const ingestBodySchema = z.object({
  18. documents: z.array(docSchema).min(1)
  19. });
  20. ingestRouter.post("/", async (req, res, next) => {
  21. try {
  22. const body = ingestBodySchema.parse(req.body);
  23. const result = await ingestDocuments(body.documents);
  24. res.json(result);
  25. } catch (err) {
  26. next(err);
  27. }
  28. });
  29. ingestRouter.post("/file", upload.single("file"), async (req, res, next) => {
  30. try {
  31. const f = req.file;
  32. if (!f?.buffer) {
  33. res.status(400).json({ error: "file_required" });
  34. return;
  35. }
  36. const source = typeof req.body?.source === "string" && req.body.source.trim() ? req.body.source.trim() : undefined;
  37. // #region debug-point A:ingest-file-start
  38. (() => {
  39. let u = "http://127.0.0.1:7777/event";
  40. let s = "image-rag-miss";
  41. try {
  42. const e = fs.readFileSync(".dbg/image-rag-miss.env", "utf8");
  43. u = e.match(/DEBUG_SERVER_URL=(.+)/)?.[1] || u;
  44. s = e.match(/DEBUG_SESSION_ID=(.+)/)?.[1] || s;
  45. } catch {}
  46. fetch(u, {
  47. method: "POST",
  48. headers: { "content-type": "application/json" },
  49. body: JSON.stringify({
  50. sessionId: s,
  51. runId: "post",
  52. hypothesisId: "A",
  53. location: "api/routes/ingest.js",
  54. msg: "[DEBUG] /api/ingest/file received",
  55. data: { filename: f.originalname, mimeType: f.mimetype, bytes: f.size ?? null, source: source ?? null },
  56. ts: Date.now()
  57. })
  58. }).catch(() => {});
  59. })();
  60. // #endregion
  61. const docs = await extractDocumentsFromUpload({
  62. buffer: f.buffer,
  63. filename: f.originalname,
  64. mimeType: f.mimetype,
  65. source
  66. });
  67. const extractedChars = docs.reduce((acc, d) => acc + (d?.text?.length ?? 0), 0);
  68. // #region debug-point A:ingest-file-extracted
  69. (() => {
  70. let u = "http://127.0.0.1:7777/event";
  71. let s = "image-rag-miss";
  72. try {
  73. const e = fs.readFileSync(".dbg/image-rag-miss.env", "utf8");
  74. u = e.match(/DEBUG_SERVER_URL=(.+)/)?.[1] || u;
  75. s = e.match(/DEBUG_SESSION_ID=(.+)/)?.[1] || s;
  76. } catch {}
  77. fetch(u, {
  78. method: "POST",
  79. headers: { "content-type": "application/json" },
  80. body: JSON.stringify({
  81. sessionId: s,
  82. runId: "post",
  83. hypothesisId: "A",
  84. location: "api/routes/ingest.js",
  85. msg: "[DEBUG] /api/ingest/file extracted",
  86. data: { documents: docs.length, extractedChars, head: String(docs?.[0]?.text ?? "").slice(0, 160) },
  87. ts: Date.now()
  88. })
  89. }).catch(() => {});
  90. })();
  91. // #endregion
  92. if (!extractedChars) {
  93. res.status(400).json({ error: "empty_extracted_text" });
  94. return;
  95. }
  96. const result = await ingestDocuments(docs);
  97. res.json({ ...result, documents: docs.length, extractedChars });
  98. } catch (err) {
  99. next(err);
  100. }
  101. });