Dr-Swopt пре 3 дана
родитељ
комит
8a26f34beb

+ 5 - 1
nest-cli.json

@@ -5,7 +5,11 @@
   "compilerOptions": {
     "deleteOutDir": true,
     "assets": [
-      "**/*.proto"
+      "**/*.proto",
+      {
+        "include": "FFB/prompts/**/*",
+        "outDir": "dist/"
+      }
     ],
     "watchAssets": true
   }

+ 360 - 3
package-lock.json

@@ -11,6 +11,7 @@
       "dependencies": {
         "@grpc/grpc-js": "^1.14.2",
         "@grpc/proto-loader": "^0.8.0",
+        "@langchain/core": "^1.1.8",
         "@nestjs/common": "^11.1.3",
         "@nestjs/core": "^11.1.3",
         "@nestjs/jwt": "^11.0.0",
@@ -29,6 +30,7 @@
         "express-list-endpoints": "^7.1.1",
         "express-session": "^1.18.1",
         "google-auth-library": "^10.5.0",
+        "langchain": "^1.2.3",
         "mongodb": "^7.0.0",
         "mongoose": "^8.19.1",
         "passport": "^0.7.0",
@@ -739,6 +741,12 @@
       "dev": true,
       "license": "MIT"
     },
+    "node_modules/@cfworker/json-schema": {
+      "version": "4.1.1",
+      "resolved": "https://registry.npmjs.org/@cfworker/json-schema/-/json-schema-4.1.1.tgz",
+      "integrity": "sha512-gAmrUZSGtKc3AiBL71iNWxDsyUC5uMaKKGdvzYsBoTW/xi42JQHl7eKV2OYzCUqvc+D2RCcf7EXY2iCyFIk6og==",
+      "license": "MIT"
+    },
     "node_modules/@colors/colors": {
       "version": "1.5.0",
       "resolved": "https://registry.npmjs.org/@colors/colors/-/colors-1.5.0.tgz",
@@ -2138,6 +2146,164 @@
         "url": "https://opencollective.com/js-sdsl"
       }
     },
+    "node_modules/@langchain/core": {
+      "version": "1.1.8",
+      "resolved": "https://registry.npmjs.org/@langchain/core/-/core-1.1.8.tgz",
+      "integrity": "sha512-kIUidOgc0ZdyXo4Ahn9Zas+OayqOfk4ZoKPi7XaDipNSWSApc2+QK5BVcjvwtzxstsNOrmXJiJWEN6WPF/MvAw==",
+      "license": "MIT",
+      "dependencies": {
+        "@cfworker/json-schema": "^4.0.2",
+        "ansi-styles": "^5.0.0",
+        "camelcase": "6",
+        "decamelize": "1.2.0",
+        "js-tiktoken": "^1.0.12",
+        "langsmith": ">=0.4.0 <1.0.0",
+        "mustache": "^4.2.0",
+        "p-queue": "^6.6.2",
+        "uuid": "^10.0.0",
+        "zod": "^3.25.76 || ^4"
+      },
+      "engines": {
+        "node": ">=20"
+      }
+    },
+    "node_modules/@langchain/core/node_modules/ansi-styles": {
+      "version": "5.2.0",
+      "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz",
+      "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==",
+      "license": "MIT",
+      "engines": {
+        "node": ">=10"
+      },
+      "funding": {
+        "url": "https://github.com/chalk/ansi-styles?sponsor=1"
+      }
+    },
+    "node_modules/@langchain/core/node_modules/camelcase": {
+      "version": "6.3.0",
+      "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz",
+      "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==",
+      "license": "MIT",
+      "engines": {
+        "node": ">=10"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/@langchain/core/node_modules/langsmith": {
+      "version": "0.4.4",
+      "resolved": "https://registry.npmjs.org/langsmith/-/langsmith-0.4.4.tgz",
+      "integrity": "sha512-rpLzrklyL7fIP/8wwrSv2tKDwMJTvkhgWeKxDvmbAB2n/p5FzqujEWCpA//u9hnrdmXZc1dCJZ+iqN6KgaEoEA==",
+      "license": "MIT",
+      "dependencies": {
+        "@types/uuid": "^10.0.0",
+        "chalk": "^4.1.2",
+        "console-table-printer": "^2.12.1",
+        "p-queue": "^6.6.2",
+        "semver": "^7.6.3",
+        "uuid": "^10.0.0"
+      },
+      "peerDependencies": {
+        "@opentelemetry/api": "*",
+        "@opentelemetry/exporter-trace-otlp-proto": "*",
+        "@opentelemetry/sdk-trace-base": "*",
+        "openai": "*"
+      },
+      "peerDependenciesMeta": {
+        "@opentelemetry/api": {
+          "optional": true
+        },
+        "@opentelemetry/exporter-trace-otlp-proto": {
+          "optional": true
+        },
+        "@opentelemetry/sdk-trace-base": {
+          "optional": true
+        },
+        "openai": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/@langchain/langgraph": {
+      "version": "1.0.7",
+      "resolved": "https://registry.npmjs.org/@langchain/langgraph/-/langgraph-1.0.7.tgz",
+      "integrity": "sha512-EBGqNOWoRiEoLUaeuiXRpUM8/DE6QcwiirNyd97XhezStebBoTTilWH8CUt6S94JRGl5zwfBBRHfzotDnZS/eA==",
+      "license": "MIT",
+      "dependencies": {
+        "@langchain/langgraph-checkpoint": "^1.0.0",
+        "@langchain/langgraph-sdk": "~1.3.1",
+        "uuid": "^10.0.0"
+      },
+      "engines": {
+        "node": ">=18"
+      },
+      "peerDependencies": {
+        "@langchain/core": "^1.0.1",
+        "zod": "^3.25.32 || ^4.1.0",
+        "zod-to-json-schema": "^3.x"
+      },
+      "peerDependenciesMeta": {
+        "zod-to-json-schema": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/@langchain/langgraph-checkpoint": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/@langchain/langgraph-checkpoint/-/langgraph-checkpoint-1.0.0.tgz",
+      "integrity": "sha512-xrclBGvNCXDmi0Nz28t3vjpxSH6UYx6w5XAXSiiB1WEdc2xD2iY/a913I3x3a31XpInUW/GGfXXfePfaghV54A==",
+      "license": "MIT",
+      "dependencies": {
+        "uuid": "^10.0.0"
+      },
+      "engines": {
+        "node": ">=18"
+      },
+      "peerDependencies": {
+        "@langchain/core": "^1.0.1"
+      }
+    },
+    "node_modules/@langchain/langgraph-sdk": {
+      "version": "1.3.1",
+      "resolved": "https://registry.npmjs.org/@langchain/langgraph-sdk/-/langgraph-sdk-1.3.1.tgz",
+      "integrity": "sha512-zTi7DZHwqtMEzapvm3I1FL4Q7OZsxtq9tTXy6s2gcCxyIU3sphqRboqytqVN7dNHLdTCLb8nXy49QKurs2MIBg==",
+      "license": "MIT",
+      "dependencies": {
+        "p-queue": "^6.6.2",
+        "p-retry": "4",
+        "uuid": "^9.0.0"
+      },
+      "peerDependencies": {
+        "@langchain/core": "^1.0.1",
+        "react": "^18 || ^19",
+        "react-dom": "^18 || ^19"
+      },
+      "peerDependenciesMeta": {
+        "@langchain/core": {
+          "optional": true
+        },
+        "react": {
+          "optional": true
+        },
+        "react-dom": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/@langchain/langgraph-sdk/node_modules/uuid": {
+      "version": "9.0.1",
+      "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz",
+      "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==",
+      "funding": [
+        "https://github.com/sponsors/broofa",
+        "https://github.com/sponsors/ctavan"
+      ],
+      "license": "MIT",
+      "bin": {
+        "uuid": "dist/bin/uuid"
+      }
+    },
     "node_modules/@levischuck/tiny-cbor": {
       "version": "0.2.11",
       "resolved": "https://registry.npmjs.org/@levischuck/tiny-cbor/-/tiny-cbor-0.2.11.tgz",
@@ -3956,6 +4122,12 @@
       "dev": true,
       "license": "MIT"
     },
+    "node_modules/@types/retry": {
+      "version": "0.12.0",
+      "resolved": "https://registry.npmjs.org/@types/retry/-/retry-0.12.0.tgz",
+      "integrity": "sha512-wWKOClTTiizcZhXnPY4wikVAwmdYHp8q6DmC+EJUzAMsycb7HB32Kh9RN4+0gExjmPmZSAQjgURXIGATPegAvA==",
+      "license": "MIT"
+    },
     "node_modules/@types/send": {
       "version": "0.17.5",
       "resolved": "https://registry.npmjs.org/@types/send/-/send-0.17.5.tgz",
@@ -4010,6 +4182,12 @@
         "@types/superagent": "^8.1.0"
       }
     },
+    "node_modules/@types/uuid": {
+      "version": "10.0.0",
+      "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-10.0.0.tgz",
+      "integrity": "sha512-7gqG38EyHgyP1S+7+xomFtL+ZNHcKv6DwNaCZmJmo1vgMugyF3TCnXVg4t1uk89mLNwnLtnY3TpOpCOyp1/xHQ==",
+      "license": "MIT"
+    },
     "node_modules/@types/validator": {
       "version": "13.15.3",
       "resolved": "https://registry.npmjs.org/@types/validator/-/validator-13.15.3.tgz",
@@ -6131,7 +6309,6 @@
       "version": "4.1.2",
       "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
       "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
-      "dev": true,
       "license": "MIT",
       "dependencies": {
         "ansi-styles": "^4.1.0",
@@ -6457,6 +6634,15 @@
         "node": "^14.18.0 || >=16.10.0"
       }
     },
+    "node_modules/console-table-printer": {
+      "version": "2.15.0",
+      "resolved": "https://registry.npmjs.org/console-table-printer/-/console-table-printer-2.15.0.tgz",
+      "integrity": "sha512-SrhBq4hYVjLCkBVOWaTzceJalvn5K1Zq5aQA6wXC/cYjI3frKWNPEMK3sZsJfNNQApvCQmgBcc13ZKmFj8qExw==",
+      "license": "MIT",
+      "dependencies": {
+        "simple-wcswidth": "^1.1.2"
+      }
+    },
     "node_modules/content-disposition": {
       "version": "0.5.4",
       "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz",
@@ -6605,6 +6791,15 @@
         }
       }
     },
+    "node_modules/decamelize": {
+      "version": "1.2.0",
+      "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz",
+      "integrity": "sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==",
+      "license": "MIT",
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
     "node_modules/decompress-response": {
       "version": "6.0.0",
       "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz",
@@ -7254,6 +7449,12 @@
         "node": ">= 0.6"
       }
     },
+    "node_modules/eventemitter3": {
+      "version": "4.0.7",
+      "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz",
+      "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==",
+      "license": "MIT"
+    },
     "node_modules/events": {
       "version": "3.3.0",
       "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz",
@@ -8338,7 +8539,6 @@
       "version": "4.0.0",
       "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
       "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
-      "dev": true,
       "license": "MIT",
       "engines": {
         "node": ">=8"
@@ -9602,6 +9802,15 @@
         "url": "https://github.com/chalk/supports-color?sponsor=1"
       }
     },
+    "node_modules/js-tiktoken": {
+      "version": "1.0.21",
+      "resolved": "https://registry.npmjs.org/js-tiktoken/-/js-tiktoken-1.0.21.tgz",
+      "integrity": "sha512-biOj/6M5qdgx5TKjDnFT1ymSpM5tbd3ylwDtrQvFQSu0Z7bBYko2dF+W/aUkXUPuk6IVpRxk/3Q2sHOzGlS36g==",
+      "license": "MIT",
+      "dependencies": {
+        "base64-js": "^1.5.1"
+      }
+    },
     "node_modules/js-tokens": {
       "version": "4.0.0",
       "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
@@ -9777,6 +9986,59 @@
         "node": ">=0.10.0"
       }
     },
+    "node_modules/langchain": {
+      "version": "1.2.3",
+      "resolved": "https://registry.npmjs.org/langchain/-/langchain-1.2.3.tgz",
+      "integrity": "sha512-3k986xJuqg4az53JxV5LnGlOzIXF1d9Kq6Y9s7XjitvzhpsbFuTDV5/kiF4cx3pkNGyw0mUXC4tLz9RxucO0hw==",
+      "license": "MIT",
+      "dependencies": {
+        "@langchain/langgraph": "^1.0.0",
+        "@langchain/langgraph-checkpoint": "^1.0.0",
+        "langsmith": ">=0.4.0 <1.0.0",
+        "uuid": "^10.0.0",
+        "zod": "^3.25.76 || ^4"
+      },
+      "engines": {
+        "node": ">=20"
+      },
+      "peerDependencies": {
+        "@langchain/core": "1.1.8"
+      }
+    },
+    "node_modules/langchain/node_modules/langsmith": {
+      "version": "0.4.4",
+      "resolved": "https://registry.npmjs.org/langsmith/-/langsmith-0.4.4.tgz",
+      "integrity": "sha512-rpLzrklyL7fIP/8wwrSv2tKDwMJTvkhgWeKxDvmbAB2n/p5FzqujEWCpA//u9hnrdmXZc1dCJZ+iqN6KgaEoEA==",
+      "license": "MIT",
+      "dependencies": {
+        "@types/uuid": "^10.0.0",
+        "chalk": "^4.1.2",
+        "console-table-printer": "^2.12.1",
+        "p-queue": "^6.6.2",
+        "semver": "^7.6.3",
+        "uuid": "^10.0.0"
+      },
+      "peerDependencies": {
+        "@opentelemetry/api": "*",
+        "@opentelemetry/exporter-trace-otlp-proto": "*",
+        "@opentelemetry/sdk-trace-base": "*",
+        "openai": "*"
+      },
+      "peerDependenciesMeta": {
+        "@opentelemetry/api": {
+          "optional": true
+        },
+        "@opentelemetry/exporter-trace-otlp-proto": {
+          "optional": true
+        },
+        "@opentelemetry/sdk-trace-base": {
+          "optional": true
+        },
+        "openai": {
+          "optional": true
+        }
+      }
+    },
     "node_modules/leven": {
       "version": "3.1.0",
       "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz",
@@ -10463,6 +10725,15 @@
         "node": ">= 0.6"
       }
     },
+    "node_modules/mustache": {
+      "version": "4.2.0",
+      "resolved": "https://registry.npmjs.org/mustache/-/mustache-4.2.0.tgz",
+      "integrity": "sha512-71ippSywq5Yb7/tVYyGbkBggbU8H3u5Rz56fH60jGFgr8uHwxs+aSKeqmluIVzM0m0kB7xQjKS6qPfd0b2ZoqQ==",
+      "license": "MIT",
+      "bin": {
+        "mustache": "bin/mustache"
+      }
+    },
     "node_modules/mute-stream": {
       "version": "2.0.0",
       "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-2.0.0.tgz",
@@ -10798,6 +11069,15 @@
         "node": ">=12.20"
       }
     },
+    "node_modules/p-finally": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz",
+      "integrity": "sha512-LICb2p9CB7FS+0eR1oqWnHhp0FljGLZCWBE9aix0Uye9W8LTQPwMTYVGWQWIw9RdQiDg4+epXQODwIYJtSJaow==",
+      "license": "MIT",
+      "engines": {
+        "node": ">=4"
+      }
+    },
     "node_modules/p-limit": {
       "version": "3.1.0",
       "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz",
@@ -10830,6 +11110,47 @@
         "url": "https://github.com/sponsors/sindresorhus"
       }
     },
+    "node_modules/p-queue": {
+      "version": "6.6.2",
+      "resolved": "https://registry.npmjs.org/p-queue/-/p-queue-6.6.2.tgz",
+      "integrity": "sha512-RwFpb72c/BhQLEXIZ5K2e+AhgNVmIejGlTgiB9MzZ0e93GRvqZ7uSi0dvRF7/XIXDeNkra2fNHBxTyPDGySpjQ==",
+      "license": "MIT",
+      "dependencies": {
+        "eventemitter3": "^4.0.4",
+        "p-timeout": "^3.2.0"
+      },
+      "engines": {
+        "node": ">=8"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/p-retry": {
+      "version": "4.6.2",
+      "resolved": "https://registry.npmjs.org/p-retry/-/p-retry-4.6.2.tgz",
+      "integrity": "sha512-312Id396EbJdvRONlngUx0NydfrIQ5lsYu0znKVUzVvArzEIt08V1qhtyESbGVd1FGX7UKtiFp5uwKZdM8wIuQ==",
+      "license": "MIT",
+      "dependencies": {
+        "@types/retry": "0.12.0",
+        "retry": "^0.13.1"
+      },
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/p-timeout": {
+      "version": "3.2.0",
+      "resolved": "https://registry.npmjs.org/p-timeout/-/p-timeout-3.2.0.tgz",
+      "integrity": "sha512-rhIwUycgwwKcP9yTOOFK/AKsAopjjCakVqLHePO3CC6Mir1Z99xT+R63jZxAT5lFZLa2inS5h+ZS2GvR99/FBg==",
+      "license": "MIT",
+      "dependencies": {
+        "p-finally": "^1.0.0"
+      },
+      "engines": {
+        "node": ">=8"
+      }
+    },
     "node_modules/p-try": {
       "version": "2.2.0",
       "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz",
@@ -11512,6 +11833,15 @@
       "dev": true,
       "license": "ISC"
     },
+    "node_modules/retry": {
+      "version": "0.13.1",
+      "resolved": "https://registry.npmjs.org/retry/-/retry-0.13.1.tgz",
+      "integrity": "sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg==",
+      "license": "MIT",
+      "engines": {
+        "node": ">= 4"
+      }
+    },
     "node_modules/reusify": {
       "version": "1.1.0",
       "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz",
@@ -11942,6 +12272,12 @@
         "url": "https://github.com/sponsors/isaacs"
       }
     },
+    "node_modules/simple-wcswidth": {
+      "version": "1.1.2",
+      "resolved": "https://registry.npmjs.org/simple-wcswidth/-/simple-wcswidth-1.1.2.tgz",
+      "integrity": "sha512-j7piyCjAeTDSjzTSQ7DokZtMNwNlEAyxqSZeCS+CXH7fJ4jx3FuJ/mTW3mE+6JLs4VJBbcll0Kjn+KXI5t21Iw==",
+      "license": "MIT"
+    },
     "node_modules/slash": {
       "version": "3.0.0",
       "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz",
@@ -12467,7 +12803,6 @@
       "version": "7.2.0",
       "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
       "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
-      "dev": true,
       "license": "MIT",
       "dependencies": {
         "has-flag": "^4.0.0"
@@ -13283,6 +13618,19 @@
         "node": ">= 0.4.0"
       }
     },
+    "node_modules/uuid": {
+      "version": "10.0.0",
+      "resolved": "https://registry.npmjs.org/uuid/-/uuid-10.0.0.tgz",
+      "integrity": "sha512-8XkAphELsDnEGrDxUOHB3RGvXz6TeuYSGEZBOjtTtPm2lwhGBjLgOzLHB63IUWfBpNucQjND6d3AOudO+H3RWQ==",
+      "funding": [
+        "https://github.com/sponsors/broofa",
+        "https://github.com/sponsors/ctavan"
+      ],
+      "license": "MIT",
+      "bin": {
+        "uuid": "dist/bin/uuid"
+      }
+    },
     "node_modules/v8-compile-cache-lib": {
       "version": "3.0.1",
       "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz",
@@ -13845,6 +14193,15 @@
       "funding": {
         "url": "https://github.com/sponsors/sindresorhus"
       }
+    },
+    "node_modules/zod": {
+      "version": "4.3.5",
+      "resolved": "https://registry.npmjs.org/zod/-/zod-4.3.5.tgz",
+      "integrity": "sha512-k7Nwx6vuWx1IJ9Bjuf4Zt1PEllcwe7cls3VNzm4CQ1/hgtFUK2bRNG3rvnpPUhFjmqJKAKtjV576KnUkHocg/g==",
+      "license": "MIT",
+      "funding": {
+        "url": "https://github.com/sponsors/colinhacks"
+      }
     }
   }
 }

+ 2 - 0
package.json

@@ -22,6 +22,7 @@
   "dependencies": {
     "@grpc/grpc-js": "^1.14.2",
     "@grpc/proto-loader": "^0.8.0",
+    "@langchain/core": "^1.1.8",
     "@nestjs/common": "^11.1.3",
     "@nestjs/core": "^11.1.3",
     "@nestjs/jwt": "^11.0.0",
@@ -40,6 +41,7 @@
     "express-list-endpoints": "^7.1.1",
     "express-session": "^1.18.1",
     "google-auth-library": "^10.5.0",
+    "langchain": "^1.2.3",
     "mongodb": "^7.0.0",
     "mongoose": "^8.19.1",
     "passport": "^0.7.0",

+ 0 - 0
src/FFB/gemini-embedding.service.ts → src/FFB/embeddings/gemini-embedding.service.ts


+ 32 - 0
src/FFB/external/gemini.api.ts

@@ -0,0 +1,32 @@
+import axios from "axios";
+
+export async function callGemini(prompt: string): Promise<string> {
+    const apiKey = process.env.GOOGLE_API_KEY;
+    if (!apiKey) throw new Error("Missing GOOGLE_API_KEY");
+
+    const url =
+        "https://generativelanguage.googleapis.com/v1beta/models/gemini-2.5-flash:generateContent";
+
+    const body = {
+        contents: [{ role: 'user', parts: [{ text: prompt }] }],
+    };
+
+    try {
+        const response = await axios.post(url, body, {
+            headers: {
+                'Content-Type': 'application/json',
+                'x-goog-api-key': apiKey,
+            },
+        });
+
+        const text = response.data?.candidates?.[0]?.content?.parts
+            ?.map((p: any) => p.text)
+            .join(' ') ?? '';
+
+        if (!text) throw new Error("No text generated by Gemini");
+        return text;
+    } catch (err: any) {
+        console.error("Failed to call Gemini:", err.response?.data || err.message);
+        throw err;
+    }
+}

+ 4 - 4
src/FFB/ffb-production.controller.ts

@@ -1,8 +1,8 @@
 import { Controller, Get, Post, Delete, Body, Param, Query } from '@nestjs/common';
-import { FFBProduction } from './ffb-production.schema';
-import { FFBProductionService } from './ffb-production.service';
-import { FFBResultCompilerService } from './ffb-result-compiler.service';
-import { FFBQueryAgentService } from './ffb-query-agent.service';
+import { FFBProduction } from './schemas/ffb-production.schema';
+import { FFBProductionService } from './services/ffb-production.service';
+import { FFBResultCompilerService } from './services/ffb-result-compiler.service';
+import { FFBQueryAgentService } from './services/ffb-query-agent.service';
 
 @Controller('ffb-production')
 export class FFBProductionController {

+ 5 - 5
src/FFB/ffb-production.module.ts

@@ -1,12 +1,12 @@
 import { Module } from '@nestjs/common';
 import { FFBProductionController } from './ffb-production.controller';
-import { FFBProductionService } from './ffb-production.service';
+import { FFBProductionService } from './services/ffb-production.service';
 import { MongoModule } from 'src/mongo/mongo.module';
-import { FFBVectorService } from './ffb-vector.service';
-import { FFBResultCompilerService } from './ffb-result-compiler.service';
+import { FFBVectorService } from './services/ffb-vector.service';
+import { FFBResultCompilerService } from './services/ffb-result-compiler.service';
 import { FFBGateway } from './ffb.gateway';
-import { FFBQueryAgentService } from './ffb-query-agent.service';
-import { GeminiEmbeddingService } from './gemini-embedding.service';
+import { FFBQueryAgentService } from './services/ffb-query-agent.service';
+import { GeminiEmbeddingService } from './embeddings/gemini-embedding.service';
 
 @Module({
   imports: [

+ 0 - 71
src/FFB/ffb-result-compiler.service.ts

@@ -1,71 +0,0 @@
-import { Injectable, OnModuleInit } from '@nestjs/common';
-import * as fs from 'fs';
-import * as path from 'path';
-import axios from 'axios';
-
-@Injectable()
-export class FFBResultCompilerService implements OnModuleInit {
-  private systemPrompt: any;
-
-  async onModuleInit() {
-    const filePath = path.join(process.cwd(), 'AgentResultCompiler.json');
-    const data = fs.readFileSync(filePath, 'utf-8');
-    this.systemPrompt = JSON.parse(data);
-  }
-
-  async compile(rawResults: any[]): Promise<string> {
-    const promptText = this.buildPrompt(rawResults);
-    const response = await this.callGemini(promptText);
-    return response.trim();
-  }
-
-  private buildPrompt(results: any[]): string {
-    // Build example text for LLM
-    const examplesText = (this.systemPrompt.examples || [])
-      .map(
-        (ex: any) =>
-          `Results: ${JSON.stringify(ex.results, null, 2)}\nAnswer: ${ex.answer}`
-      )
-      .join('\n\n');
-
-    return `
-${this.systemPrompt.instructions}
-
-${examplesText}
-
-Here are the raw results from MongoDB: ${JSON.stringify(results, null, 2)}
-Please produce a clear, concise natural language answer for the user.
-`;
-  }
-
-  private async callGemini(prompt: string): Promise<string> {
-    const apiKey = process.env.GOOGLE_API_KEY;
-    if (!apiKey) throw new Error("Missing GOOGLE_API_KEY");
-
-    const url =
-      "https://generativelanguage.googleapis.com/v1beta/models/gemini-2.5-flash:generateContent";
-
-    const body = {
-      contents: [{ role: 'user', parts: [{ text: prompt }] }],
-    };
-
-    try {
-      const response = await axios.post(url, body, {
-        headers: {
-          'Content-Type': 'application/json',
-          'x-goog-api-key': apiKey,
-        },
-      });
-
-      const text = response.data?.candidates?.[0]?.content?.parts
-        ?.map((p: any) => p.text)
-        .join(' ') ?? '';
-
-      if (!text) throw new Error("No text generated by Gemini");
-      return text;
-    } catch (err: any) {
-      console.error("Failed to call Gemini:", err.response?.data || err.message);
-      throw err;
-    }
-  }
-}

+ 0 - 0
AgentResultCompiler.json → src/FFB/prompts/CompilerAgent.json


+ 0 - 0
QueryAgent.json → src/FFB/prompts/QueryAgent.json


+ 10 - 3
src/mongo/mongo-ffb-production.repository.ts → src/FFB/repo/mongo-ffb-production.repository.ts

@@ -1,5 +1,5 @@
 import { Db, ObjectId, WithId } from 'mongodb';
-import { FFBProduction } from 'src/FFB/ffb-production.schema';
+import { FFBProduction } from 'src/FFB/schemas/ffb-production.schema';
 
 export class FFBProductionRepository {
   private readonly collectionName = 'FFB Production';
@@ -77,9 +77,16 @@ export class FFBProductionRepository {
   async aggregate(pipeline: Array<Record<string, any>>): Promise<any[]> {
     // Optional: log the pipeline for debugging
     // console.log('Executing aggregation pipeline:', JSON.stringify(pipeline, null, 2));
-
+    const pipelineWithDates = pipeline.map(stage => {
+      if ('$match' in stage && 'productionDate' in stage.$match) {
+        const pd = stage.$match.productionDate;
+        if (pd.$gte) pd.$gte = new Date(pd.$gte);
+        if (pd.$lte) pd.$lte = new Date(pd.$lte);
+      }
+      return stage;
+    });
     // Execute aggregation
-    const results = await this.collection.aggregate(pipeline).toArray();
+    const results = await this.collection.aggregate(pipelineWithDates).toArray();
     // console.log('Aggregation results:', results);
 
     // Optional: strip out any internal vector fields if accidentally included

+ 0 - 0
src/FFB/ffb-production.schema.ts → src/FFB/schemas/ffb-production.schema.ts


+ 3 - 3
src/FFB/ffb-production.service.ts → src/FFB/services/ffb-production.service.ts

@@ -1,7 +1,7 @@
 import { Injectable } from '@nestjs/common';
-import { MongoCoreService } from '../mongo/mongo-core.service';
-import { FFBProduction } from './ffb-production.schema';
-import { FFBProductionRepository } from 'src/mongo/mongo-ffb-production.repository';
+import { MongoCoreService } from '../../mongo/mongo-core.service';
+import { FFBProduction } from '../schemas/ffb-production.schema';
+import { FFBProductionRepository } from 'src/FFB/repo/mongo-ffb-production.repository';
 import { FFBVectorService } from './ffb-vector.service';
 
 @Injectable()

+ 8 - 31
src/FFB/ffb-query-agent.service.ts → src/FFB/services/ffb-query-agent.service.ts

@@ -1,11 +1,11 @@
 import { Injectable, OnModuleInit } from "@nestjs/common";
 import { MongoCoreService } from "src/mongo/mongo-core.service";
-import { FFBProductionRepository } from "src/mongo/mongo-ffb-production.repository";
-import { FFBGateway } from "./ffb.gateway";
+import { FFBProductionRepository } from "src/FFB/repo/mongo-ffb-production.repository";
+import { FFBGateway } from "../ffb.gateway";
 import path from "path";
 import fs from "fs";
-import axios from "axios";
-import { GeminiEmbeddingService } from "./gemini-embedding.service";
+import { GeminiEmbeddingService } from "../embeddings/gemini-embedding.service";
+import { callGemini } from "../external/gemini.api";
 
 @Injectable()
 export class FFBQueryAgentService implements OnModuleInit {
@@ -19,7 +19,9 @@ export class FFBQueryAgentService implements OnModuleInit {
   ) { }
 
   async onModuleInit() {
-    const filePath = path.join(process.cwd(), 'QueryAgent.json');
+    // __dirname points to the directory of the current file in dist (e.g., dist/FFB/services)
+    const filePath = path.join(__dirname, '..', 'prompts', 'QueryAgent.json');
+
     const data = fs.readFileSync(filePath, 'utf-8');
     this.systemPrompt = JSON.parse(data);
   }
@@ -47,31 +49,6 @@ Q: "${userMessage}"
 `;
   }
 
-
-  private async callGemini(prompt: string): Promise<string> {
-    const apiKey = process.env.GOOGLE_API_KEY;
-    if (!apiKey) throw new Error('Missing GOOGLE_API_KEY');
-
-    const url =
-      'https://generativelanguage.googleapis.com/v1beta/models/gemini-2.5-flash:generateContent';
-
-    const body = { contents: [{ role: 'user', parts: [{ text: prompt }] }] };
-
-    try {
-      const response = await axios.post(url, body, {
-        headers: { 'Content-Type': 'application/json', 'x-goog-api-key': apiKey },
-      });
-
-      const text =
-        response.data?.candidates?.[0]?.content?.parts?.map((p: any) => p.text).join(' ') ?? '';
-      if (!text) throw new Error('No text generated by Gemini');
-      return text;
-    } catch (err: any) {
-      console.error('Failed to call Gemini:', err.response?.data || err.message);
-      throw err;
-    }
-  }
-
   private sanitizeLLMOutput(text: string): string {
     let sanitized = text
       .trim()
@@ -132,7 +109,7 @@ Q: "${userMessage}"
     const promptText = this.buildPrompt(userMessage, ffbFields);
 
     // 3. Call LLM
-    const llmResponse = await this.callGemini(promptText);
+    const llmResponse = await callGemini(promptText);
 
     // 4. Sanitize + parse
     const sanitized = this.sanitizeLLMOutput(llmResponse);

+ 44 - 0
src/FFB/services/ffb-result-compiler.service.ts

@@ -0,0 +1,44 @@
+import { Injectable, OnModuleInit } from '@nestjs/common';
+import * as fs from 'fs';
+import * as path from 'path';
+import { callGemini } from '../external/gemini.api';
+
+@Injectable()
+export class FFBResultCompilerService implements OnModuleInit {
+  private systemPrompt: any;
+
+  async onModuleInit() {
+    // __dirname points to the directory of the current file in dist (e.g., dist/FFB/services)
+    const filePath = path.join(__dirname, '..', 'prompts', 'CompilerAgent.json');
+
+    const data = fs.readFileSync(filePath, 'utf-8');
+    this.systemPrompt = JSON.parse(data);
+  }
+
+  async compile(rawResults: any[]): Promise<string> {
+    const promptText = this.buildPrompt(rawResults);
+    const response = await callGemini(promptText);
+    return response.trim();
+  }
+
+  private buildPrompt(results: any[]): string {
+    // Build example text for LLM
+    const examplesText = (this.systemPrompt.examples || [])
+      .map(
+        (ex: any) =>
+          `Results: ${JSON.stringify(ex.results, null, 2)}\nAnswer: ${ex.answer}`
+      )
+      .join('\n\n');
+
+    return `
+${this.systemPrompt.instructions}
+
+${examplesText}
+
+Here are the raw results from MongoDB: ${JSON.stringify(results, null, 2)}
+Please produce a clear, concise natural language answer for the user.
+`;
+  }
+
+
+}

+ 3 - 3
src/FFB/ffb-vector.service.ts → src/FFB/services/ffb-vector.service.ts

@@ -1,8 +1,8 @@
 import { Injectable, OnModuleInit } from '@nestjs/common';
 import { MongoCoreService } from 'src/mongo/mongo-core.service';
-import { FFBProductionRepository } from 'src/mongo/mongo-ffb-production.repository';
-import { FFBProduction } from './ffb-production.schema';
-import { GeminiEmbeddingService } from './gemini-embedding.service';
+import { FFBProductionRepository } from 'src/FFB/repo/mongo-ffb-production.repository';
+import { FFBProduction } from '../schemas/ffb-production.schema';
+import { GeminiEmbeddingService } from '../embeddings/gemini-embedding.service';
 
 @Injectable()
 export class FFBVectorService implements OnModuleInit {