@futdevpro/nts-dynamo 1.15.53 → 1.15.55

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (330) hide show
  1. package/.c8rc.json +26 -26
  2. package/.copilot/patterns.json +7 -7
  3. package/.cursor/rules/__assistant_guide.mdc +30 -30
  4. package/.cursor/rules/_ag_backend-structure.mdc +85 -85
  5. package/.cursor/rules/_ag_backend.mdc +16 -16
  6. package/.cursor/rules/_ag_frontend-structure.mdc +86 -86
  7. package/.cursor/rules/_ag_frontend.mdc +39 -39
  8. package/.cursor/rules/_ag_import-rules.mdc +44 -44
  9. package/.cursor/rules/_ag_naming.mdc +115 -115
  10. package/.cursor/rules/_ag_should-be.mdc +6 -6
  11. package/.cursor/rules/ai_development_guide.md +60 -60
  12. package/.cursor/rules/cursor-rules.md +160 -160
  13. package/.cursor/rules/default-command.mdc +464 -464
  14. package/.cursor/rules/error_code_pattern.md +39 -39
  15. package/.cursor/rules/saved rule mcp server use.md +15 -15
  16. package/.dynamo/pipeline.cicd.config.json +19 -1
  17. package/.github/workflows/main.yml +432 -432
  18. package/.vscode/settings.json +10 -10
  19. package/HOWTO.md +15 -15
  20. package/LICENSE +21 -21
  21. package/__documentations/nts-integration-tests-2026-03-17.md +26 -26
  22. package/_specifications/BACKLOG.md +92 -92
  23. package/_specifications/TODO.md +15 -15
  24. package/_specifications/agent.md +138 -138
  25. package/build/_models/interfaces/global-log-settings.interface.d.ts +26 -0
  26. package/build/_models/interfaces/global-log-settings.interface.d.ts.map +1 -1
  27. package/build/_services/server/app.server.d.ts +16 -0
  28. package/build/_services/server/app.server.d.ts.map +1 -1
  29. package/build/_services/server/app.server.js +24 -0
  30. package/build/_services/server/app.server.js.map +1 -1
  31. package/eslint.config.js +3 -3
  32. package/nodemon.json +24 -24
  33. package/package.json +361 -361
  34. package/pnpm-workspace.yaml +4 -4
  35. package/scripts/run-coverage-tests.js +28 -28
  36. package/spec/support/helpers/spec-reporter-loader.js +359 -359
  37. package/spec/support/helpers/ts-node-helper.js +93 -93
  38. package/spec/support/jasmine.coverage.json +24 -24
  39. package/spec/support/jasmine.json +24 -24
  40. package/src/_collections/archive.util.spec.ts +57 -57
  41. package/src/_collections/archive.util.ts +18 -18
  42. package/src/_collections/atlas-default-db-options.const.ts +9 -9
  43. package/src/_collections/default-fallback-cache-max-age.const.spec.ts +11 -11
  44. package/src/_collections/default-fallback-cache-max-age.const.ts +2 -2
  45. package/src/_collections/default-not-found-page.const.spec.ts +19 -19
  46. package/src/_collections/default-not-found-page.const.ts +22 -22
  47. package/src/_collections/default-socket-path.const.spec.ts +12 -12
  48. package/src/_collections/default-socket-path.const.ts +2 -2
  49. package/src/_collections/get-environment-settings.util.spec.ts +210 -210
  50. package/src/_collections/get-environment-settings.util.ts +48 -48
  51. package/src/_collections/global-settings.const.ts +70 -70
  52. package/src/_collections/sample.env +21 -21
  53. package/src/_collections/star.controller.spec.ts +224 -224
  54. package/src/_collections/star.controller.ts +129 -129
  55. package/src/_enums/data-model-type.enum.ts +14 -14
  56. package/src/_enums/data-service-function.enum.ts +24 -24
  57. package/src/_enums/predefined-data-types.enum.ts +16 -16
  58. package/src/_enums/route-security.enum.ts +12 -12
  59. package/src/_models/control-models/api-call-params.control-model.spec.ts +152 -152
  60. package/src/_models/control-models/api-call-params.control-model.ts +142 -142
  61. package/src/_models/control-models/app-ext-system-controls.control-model.spec.ts +52 -52
  62. package/src/_models/control-models/app-ext-system-controls.control-model.ts +9 -9
  63. package/src/_models/control-models/app-params.control-model.spec.ts +225 -225
  64. package/src/_models/control-models/app-params.control-model.ts +136 -136
  65. package/src/_models/control-models/app-system-controls.control-model.spec.ts +31 -31
  66. package/src/_models/control-models/app-system-controls.control-model.ts +9 -9
  67. package/src/_models/control-models/endpoint-params.control-model.spec.ts +627 -627
  68. package/src/_models/control-models/endpoint-params.control-model.ts +627 -627
  69. package/src/_models/control-models/http-settings.control-model.spec.ts +77 -77
  70. package/src/_models/control-models/http-settings.control-model.ts +37 -37
  71. package/src/_models/control-models/system-control.control-model.spec.ts +27 -27
  72. package/src/_models/control-models/system-control.control-model.ts +12 -12
  73. package/src/_models/interfaces/certification-settings.interface.ts +7 -7
  74. package/src/_models/interfaces/environment-settings.interface.ts +59 -59
  75. package/src/_models/interfaces/global-log-settings.interface.ts +171 -144
  76. package/src/_models/interfaces/global-service-settings.interface.ts +47 -47
  77. package/src/_models/interfaces/routing-module-settings.interface.ts +21 -21
  78. package/src/_models/interfaces/static-client-settings.interface.spec.ts +29 -29
  79. package/src/_models/interfaces/static-client-settings.interface.ts +28 -28
  80. package/src/_models/types/db-update.type.ts +100 -100
  81. package/src/_modules/ai/_models/ai-input-interfaces.ts +117 -117
  82. package/src/_modules/ai/_models/ai-test-generation-result.interface.ts +16 -16
  83. package/src/_modules/ai/_modules/anthropic/_services/aai-user-key.control-service.ts +138 -138
  84. package/src/_modules/ai/_modules/anthropic/index.ts +5 -5
  85. package/src/_modules/ai/_modules/document-ai/_collections/dai-chunking.util.spec.ts +242 -242
  86. package/src/_modules/ai/_modules/document-ai/_collections/dai-chunking.util.ts +639 -639
  87. package/src/_modules/ai/_modules/document-ai/_collections/dai-document.util.spec.ts +209 -209
  88. package/src/_modules/ai/_modules/document-ai/_collections/dai-document.util.ts +85 -85
  89. package/src/_modules/ai/_modules/document-ai/_enums/dai-compare-result-type.enum.ts +7 -7
  90. package/src/_modules/ai/_modules/document-ai/_models/data-models/dai-doc-chunk.data-model.ts +146 -146
  91. package/src/_modules/ai/_modules/document-ai/_models/data-models/dai-doc-page.data-model.ts +162 -162
  92. package/src/_modules/ai/_modules/document-ai/_models/data-models/dai-document.data-model.ts +99 -99
  93. package/src/_modules/ai/_modules/document-ai/_models/interfaces/dai-doc-chunk-compare-result.interface.ts +18 -18
  94. package/src/_modules/ai/_modules/document-ai/_models/interfaces/dai-doc-page-compare-result.interface.ts +19 -19
  95. package/src/_modules/ai/_modules/document-ai/_models/interfaces/dai-document-compare-result.interface.ts +25 -25
  96. package/src/_modules/ai/_modules/document-ai/index.ts +28 -28
  97. package/src/_modules/ai/_modules/fdp-ai/_services/fdpai-user-key.control-service.ts +189 -189
  98. package/src/_modules/ai/_modules/fdp-ai/index.ts +5 -5
  99. package/src/_modules/ai/_modules/open-ai/_collections/oai-global-settings.const.ts +9 -9
  100. package/src/_modules/ai/_modules/open-ai/_collections/oai-llm-predefined-requests-hu.conts.ts +82 -82
  101. package/src/_modules/ai/_modules/open-ai/_collections/oai-llm-predefined-requests.conts.ts +75 -75
  102. package/src/_modules/ai/_modules/open-ai/_enums/oai-gpt-message-role.enum.ts +45 -45
  103. package/src/_modules/ai/_modules/open-ai/_models/interfaces/oai-global-settings.interface.ts +7 -7
  104. package/src/_modules/ai/_modules/open-ai/_models/interfaces/oai-gpt-message.interface.ts +7 -7
  105. package/src/_modules/ai/_modules/open-ai/_models/interfaces/oai-llm-predefined-requests.interface.ts +57 -57
  106. package/src/_modules/ai/_modules/open-ai/_services/data-services/oai-doc-chunk-data.service.ts +292 -292
  107. package/src/_modules/ai/_modules/open-ai/_services/data-services/oai-document.data-service.spec.ts +342 -342
  108. package/src/_modules/ai/_modules/open-ai/_services/data-services/oai-vector-data.service.spec.ts +550 -550
  109. package/src/_modules/ai/_modules/open-ai/_services/data-services/oai-vector-data.service.ts +630 -630
  110. package/src/_modules/ai/_modules/open-ai/_services/oai-embedding.control-service.spec.ts +332 -332
  111. package/src/_modules/ai/_modules/open-ai/_services/oai-llm-chat.service-base.spec.ts +462 -462
  112. package/src/_modules/ai/_modules/open-ai/_services/oai-llm-chat.service-base.ts +634 -634
  113. package/src/_modules/ai/_modules/open-ai/_services/oai-llm.service-base.spec.ts +489 -489
  114. package/src/_modules/ai/_modules/open-ai/_services/oai-llm.service-base.tools.spec.ts +173 -173
  115. package/src/_modules/ai/_modules/open-ai/_services/oai-llm.service-base.ts +1033 -1033
  116. package/src/_modules/ai/_modules/open-ai/_services/oai-user-key.control-service.ts +157 -157
  117. package/src/_modules/ai/_services/ai-embedding.service-base.spec.ts +98 -98
  118. package/src/_modules/ai/_services/ai-embedding.service-base.ts +48 -48
  119. package/src/_modules/ai/_services/ai-llm-chat.service-base.spec.ts +229 -229
  120. package/src/_modules/ai/_services/ai-llm-chat.service-base.ts +68 -68
  121. package/src/_modules/ai/_services/ai-llm.service-base.spec.ts +250 -250
  122. package/src/_modules/ai/_services/ai-llm.service-base.ts +519 -519
  123. package/src/_modules/ai/_services/ai-provider.service-base.spec.ts +158 -158
  124. package/src/_modules/ai/_services/ai-user-key.service-base.ts +59 -59
  125. package/src/_modules/ai/index.ts +13 -13
  126. package/src/_modules/assistant/_collections/ass-global-settings.const.ts +13 -13
  127. package/src/_modules/assistant/_collections/ass.util.spec.ts +176 -176
  128. package/src/_modules/assistant/_collections/ass.util.ts +50 -50
  129. package/src/_modules/assistant/_models/ass-global-settings.interface.ts +15 -15
  130. package/src/_modules/assistant/_services/ass-io.control-service.spec.ts +140 -140
  131. package/src/_modules/assistant/_services/ass-main.control-service.spec.ts +192 -192
  132. package/src/_modules/assistant/_services/ass-main.control-service.ts +107 -107
  133. package/src/_modules/bot/_collections/bot-default-commands.const.ts +12 -12
  134. package/src/_modules/bot/_collections/bot-global-settings.const.ts +39 -39
  135. package/src/_modules/bot/_models/bot-channel-wrapper.interface.ts +62 -62
  136. package/src/_modules/bot/_models/bot-command.interface.ts +8 -8
  137. package/src/_modules/bot/_models/bot-global-settings.interface.ts +96 -96
  138. package/src/_modules/bot/_models/bot-last-mention-date.interface.ts +6 -6
  139. package/src/_modules/bot/_models/bot-last-message-date.interface.ts +5 -5
  140. package/src/_modules/bot/_models/bot-user-wrapper.interface.ts +41 -41
  141. package/src/_modules/bot/_modules/discord-bot/_models/dib-platform.types.ts +9 -9
  142. package/src/_modules/bot/_modules/discord-bot/_services/dib-messaging-provider.control-service.spec.ts +431 -431
  143. package/src/_modules/bot/_modules/dynamo-bot/_collections/dyb-operations.util.spec.ts +160 -160
  144. package/src/_modules/bot/_modules/dynamo-bot/_collections/dyb-operations.util.ts +55 -55
  145. package/src/_modules/bot/_modules/dynamo-bot/_models/dyb-platform.types.ts +15 -15
  146. package/src/_modules/bot/_modules/dynamo-bot/_services/dyb-messaging-provider.control-service.spec.ts +374 -374
  147. package/src/_modules/bot/_modules/dynamo-bot/_services/dyb-messaging-provider.control-service.ts +447 -447
  148. package/src/_modules/bot/_modules/dynamo-bot/index.ts +15 -15
  149. package/src/_modules/bot/_modules/slack-bot/_models/slb-platform.types.ts +9 -9
  150. package/src/_modules/bot/_modules/slack-bot/_services/slb-messaging-provider.control-service.spec.ts +344 -344
  151. package/src/_modules/bot/_modules/slack-bot/_services/slb-messaging-provider.control-service.ts +197 -197
  152. package/src/_modules/bot/_modules/teams-bot/_models/teb-platform.types.ts +9 -9
  153. package/src/_modules/bot/_modules/teams-bot/_services/teb-messaging-provider.control-service.spec.ts +345 -345
  154. package/src/_modules/bot/_modules/teams-bot/_services/teb-messaging-provider.control-service.ts +197 -197
  155. package/src/_modules/bot/_services/bot-commands.control-service.spec.ts +116 -116
  156. package/src/_modules/bot/_services/bot-io.control-service.spec.ts +285 -285
  157. package/src/_modules/bot/_services/bot-main.control-service.spec.ts +208 -208
  158. package/src/_modules/bot/_services/bot-messaging-provider.service-base.spec.ts +349 -349
  159. package/src/_modules/bot/_services/bot-routines.control-service.spec.ts +111 -111
  160. package/src/_modules/custom-data/custom-data.controller.spec.ts +49 -49
  161. package/src/_modules/custom-data/custom-data.controller.ts +67 -67
  162. package/src/_modules/custom-data/custom-data.data-service.spec.ts +54 -54
  163. package/src/_modules/custom-data/custom-data.data-service.ts +21 -21
  164. package/src/_modules/custom-data/get-custom-data-routing-module.util.spec.ts +28 -28
  165. package/src/_modules/custom-data/get-custom-data-routing-module.util.ts +24 -24
  166. package/src/_modules/custom-data/index.ts +9 -9
  167. package/src/_modules/defaults/_collections/default-endpoints.util.ts +487 -487
  168. package/src/_modules/defaults/_models/default-user.data-model.ts +72 -72
  169. package/src/_modules/defaults/_services/default-auth.service.spec.ts +269 -269
  170. package/src/_modules/defaults/_services/default-auth.service.ts +177 -177
  171. package/src/_modules/defaults/_services/default-socket-events.service.spec.ts +42 -42
  172. package/src/_modules/defaults/_services/default-socket-events.service.ts +61 -61
  173. package/src/_modules/defaults/_services/default-user.data-service.spec.ts +187 -187
  174. package/src/_modules/defaults/_services/default-user.data-service.ts +98 -98
  175. package/src/_modules/defaults/index.ts +17 -17
  176. package/src/_modules/discord-assistant/_collections/dias-global-settings.const.ts +19 -19
  177. package/src/_modules/discord-assistant/_collections/dias.util.spec.ts +366 -366
  178. package/src/_modules/discord-assistant/_collections/dias.util.ts +132 -132
  179. package/src/_modules/discord-assistant/_models/dias-global-settings.interface.ts +19 -19
  180. package/src/_modules/discord-assistant/_models/dias-knowledge.data-model.ts +52 -52
  181. package/src/_modules/discord-assistant/_services/dias-chunk.data-service.ts +177 -177
  182. package/src/_modules/discord-assistant/_services/dias-io.control-service.spec.ts +108 -108
  183. package/src/_modules/discord-assistant/_services/dias-io.control-service.ts +69 -69
  184. package/src/_modules/discord-assistant/_services/dias-main.control-service.spec.ts +22 -22
  185. package/src/_modules/discord-assistant/_services/dias-main.control-service.ts +27 -27
  186. package/src/_modules/discord-assistant/_services/dias.service-base.spec.ts +195 -195
  187. package/src/_modules/discord-assistant/_services/dias.service-base.ts +76 -76
  188. package/src/_modules/discord-assistant/index.ts +38 -38
  189. package/src/_modules/discord-assistant-voiced/_services/dias-discord-bot.control-service.spec.ts +34 -34
  190. package/src/_modules/discord-assistant-voiced/_services/dias-discord-bot.control-service.ts +11 -11
  191. package/src/_modules/discord-assistant-voiced/index.ts +36 -36
  192. package/src/_modules/discord-bot/_collections/dibo-default-commands.const.ts +16 -16
  193. package/src/_modules/discord-bot/_collections/dibo-global-settings.conts.ts +55 -55
  194. package/src/_modules/discord-bot/_collections/dibo-operations.util.spec.ts +214 -214
  195. package/src/_modules/discord-bot/_collections/dibo-operations.util.ts +387 -387
  196. package/src/_modules/discord-bot/_models/dibo-command.interface.ts +12 -12
  197. package/src/_modules/discord-bot/_models/dibo-global-settings.interface.ts +98 -98
  198. package/src/_modules/discord-bot/_models/dibo-last-mention-date.inteface.ts +7 -7
  199. package/src/_modules/discord-bot/_models/dibo-last-message-date.interface.ts +6 -6
  200. package/src/_modules/discord-bot/_services/dibo-commands.control-service.spec.ts +154 -154
  201. package/src/_modules/discord-bot/_services/dibo-commands.control-service.ts +153 -153
  202. package/src/_modules/discord-bot/_services/dibo-io.control-service.spec.ts +264 -264
  203. package/src/_modules/discord-bot/_services/dibo-io.control-service.ts +306 -306
  204. package/src/_modules/discord-bot/_services/dibo-main.control-service.spec.ts +408 -408
  205. package/src/_modules/discord-bot/_services/dibo-main.control-service.ts +487 -487
  206. package/src/_modules/discord-bot/_services/dibo-routines.control-service.spec.ts +105 -105
  207. package/src/_modules/discord-bot/index.ts +36 -36
  208. package/src/_modules/local-vector-search/_enums/lvs-search-mode.enum.ts +35 -35
  209. package/src/_modules/local-vector-search/_models/lvs-search-result.interface.ts +17 -17
  210. package/src/_modules/local-vector-search/_services/lvs-doc-chunk-data.service.spec.ts +418 -418
  211. package/src/_modules/local-vector-search/_services/lvs-doc-chunk-data.service.ts +276 -276
  212. package/src/_modules/local-vector-search/_services/lvs-local-vector-search.data-service.spec.ts +480 -480
  213. package/src/_modules/local-vector-search/_services/lvs-local-vector-search.data-service.ts +416 -416
  214. package/src/_modules/local-vector-search/_services/lvs-vector-pool.control-service.spec.ts +393 -393
  215. package/src/_modules/local-vector-search/_services/lvs-vector-pool.control-service.ts +220 -220
  216. package/src/_modules/local-vector-search/index.ts +11 -11
  217. package/src/_modules/logs/index.ts +11 -11
  218. package/src/_modules/messaging/README.md +354 -354
  219. package/src/_modules/messaging/_collections/get-messaging-routing-module.util.ts +26 -26
  220. package/src/_modules/messaging/_collections/msg-global-settings.const.ts +22 -22
  221. package/src/_modules/messaging/_collections/msg.util.spec.ts +226 -226
  222. package/src/_modules/messaging/_models/msg-global-settings.interface.ts +37 -37
  223. package/src/_modules/messaging/_services/msg-conversation.data-service.ts +146 -146
  224. package/src/_modules/messaging/_services/msg-events.service.spec.ts +219 -219
  225. package/src/_modules/messaging/_services/msg-events.service.ts +267 -267
  226. package/src/_modules/messaging/_services/msg-integration.control-service.ts +179 -179
  227. package/src/_modules/messaging/_services/msg-main.control-service.spec.ts +147 -147
  228. package/src/_modules/messaging/_services/msg-main.control-service.ts +571 -571
  229. package/src/_modules/messaging/_services/msg-message.data-service.ts +129 -129
  230. package/src/_modules/messaging/_services/msg.controller.spec.ts +201 -201
  231. package/src/_modules/messaging/index.ts +30 -30
  232. package/src/_modules/mock/app-extended-server.mock.ts +201 -201
  233. package/src/_modules/mock/app-integration-test.mock.ts +51 -51
  234. package/src/_modules/mock/app-params.mock.spec.ts +21 -21
  235. package/src/_modules/mock/app-params.mock.ts +9 -9
  236. package/src/_modules/mock/app-server.mock.ts +188 -188
  237. package/src/_modules/mock/auth-service.mock.spec.ts +47 -47
  238. package/src/_modules/mock/auth-service.mock.ts +28 -28
  239. package/src/_modules/mock/controller.mock.spec.ts +26 -26
  240. package/src/_modules/mock/controller.mock.ts +16 -16
  241. package/src/_modules/mock/data-model.mock.spec.ts +111 -111
  242. package/src/_modules/mock/data-model.mock.ts +82 -82
  243. package/src/_modules/mock/email-service-collection.mock.spec.ts +24 -24
  244. package/src/_modules/mock/email-service-collection.mock.ts +15 -15
  245. package/src/_modules/mock/email-service.mock.spec.ts +17 -17
  246. package/src/_modules/mock/email-service.mock.ts +20 -20
  247. package/src/_modules/mock/email-template.mock.html +14 -14
  248. package/src/_modules/mock/endpoint.mock.ts +91 -91
  249. package/src/_modules/mock/socket-client.mock.spec.ts +40 -40
  250. package/src/_modules/mock/socket-client.mock.ts +45 -45
  251. package/src/_modules/mock/socket-server.mock.spec.ts +44 -44
  252. package/src/_modules/mock/socket-server.mock.ts +46 -46
  253. package/src/_modules/oauth2/_routes/oauth2.controller.spec.ts +107 -107
  254. package/src/_modules/oauth2/_routes/oauth2.controller.ts +98 -98
  255. package/src/_modules/oauth2/_services/oauth2.auth-service.spec.ts +254 -254
  256. package/src/_modules/oauth2/_services/oauth2.auth-service.ts +232 -232
  257. package/src/_modules/oauth2/_services/oauth2.control-service.spec.ts +585 -585
  258. package/src/_modules/oauth2/_services/oauth2.control-service.ts +653 -653
  259. package/src/_modules/oauth2/index.ts +17 -17
  260. package/src/_modules/server/errors/errors.control-service.spec.ts +238 -238
  261. package/src/_modules/server/errors/errors.control-service.ts +85 -85
  262. package/src/_modules/server/errors/errors.controller.spec.ts +241 -241
  263. package/src/_modules/server/errors/errors.controller.ts +431 -431
  264. package/src/_modules/server/errors/errors.data-service.spec.ts +361 -361
  265. package/src/_modules/server/index.ts +30 -30
  266. package/src/_modules/server/server-status/server-status-snapshot.control-service.spec.ts +70 -70
  267. package/src/_modules/server/server-status/server-status-snapshot.control-service.ts +17 -17
  268. package/src/_modules/server/server-status/server-status-snapshot.data-service.spec.ts +77 -77
  269. package/src/_modules/server/server-status/server-status-snapshot.data-service.ts +37 -37
  270. package/src/_modules/server/server-status/server-status.control-service.spec.ts +576 -576
  271. package/src/_modules/server/server-status/server-status.control-service.ts +396 -396
  272. package/src/_modules/server/server-status/server-status.controller.spec.ts +240 -240
  273. package/src/_modules/server/server-status/server-status.controller.ts +253 -253
  274. package/src/_modules/socket/_enums/socket-security.enum.ts +11 -11
  275. package/src/_modules/socket/_models/socket-client-service-params.control-model.spec.ts +32 -32
  276. package/src/_modules/socket/_models/socket-client-service-params.control-model.ts +22 -22
  277. package/src/_modules/socket/_models/socket-presence.control-model.spec.ts +164 -164
  278. package/src/_modules/socket/_models/socket-presence.control-model.ts +210 -210
  279. package/src/_modules/socket/_models/socket-server-service-params.control-model.spec.ts +46 -46
  280. package/src/_modules/socket/_models/socket-server-service-params.control-model.ts +22 -22
  281. package/src/_modules/socket/_services/socket-client.service.spec.ts +15 -15
  282. package/src/_modules/socket/_services/socket-client.service.ts +260 -260
  283. package/src/_modules/socket/_services/socket-server.service.spec.ts +11 -11
  284. package/src/_modules/socket/app-extended.integration.spec.ts +85 -85
  285. package/src/_modules/socket/app-extended.server.ts +630 -630
  286. package/src/_modules/socket/index.ts +42 -42
  287. package/src/_modules/test/get-test-routing-module.util.spec.ts +28 -28
  288. package/src/_modules/test/get-test-routing-module.util.ts +23 -23
  289. package/src/_modules/test/index.ts +11 -11
  290. package/src/_modules/test/test.controller.spec.ts +72 -72
  291. package/src/_modules/test/test.controller.ts +115 -115
  292. package/src/_modules/usage/get-usage-routing-module.util.ts +22 -22
  293. package/src/_modules/usage/index.ts +15 -15
  294. package/src/_modules/usage/usage.controller.spec.ts +81 -81
  295. package/src/_modules/usage/usage.controller.ts +126 -126
  296. package/src/_modules/usage/usage.data-service.spec.ts +332 -332
  297. package/src/_modules/usage/usage.data-service.ts +185 -185
  298. package/src/_services/base/api.service-base.spec.ts +125 -125
  299. package/src/_services/base/api.service-base.ts +74 -74
  300. package/src/_services/base/archive-data.service.spec.ts +196 -196
  301. package/src/_services/base/archive-data.service.ts +216 -216
  302. package/src/_services/base/data.service.spec.ts +674 -674
  303. package/src/_services/base/data.service.ts +2719 -2719
  304. package/src/_services/base/db.service.spec.ts +73 -73
  305. package/src/_services/base/db.service.ts +1575 -1575
  306. package/src/_services/base/singleton.service-base.spec.ts +28 -28
  307. package/src/_services/base/singleton.service-base.ts +24 -24
  308. package/src/_services/base/singleton.service.spec.ts +114 -114
  309. package/src/_services/base/singleton.service.ts +38 -38
  310. package/src/_services/core/api.service.spec.ts +140 -140
  311. package/src/_services/core/auth.service.spec.ts +159 -159
  312. package/src/_services/core/auth.service.ts +174 -174
  313. package/src/_services/core/email.service.spec.ts +85 -85
  314. package/src/_services/core/email.service.ts +742 -742
  315. package/src/_services/core/global.service.spec.ts +275 -275
  316. package/src/_services/core/global.service.ts +461 -461
  317. package/src/_services/core/service-collection.service.spec.ts +46 -46
  318. package/src/_services/core/service-collection.service.ts +6 -6
  319. package/src/_services/route/controller.service.spec.ts +53 -53
  320. package/src/_services/route/controller.service.ts +148 -148
  321. package/src/_services/route/routing-module.service.spec.ts +98 -98
  322. package/src/_services/route/routing-module.service.ts +330 -330
  323. package/src/_services/server/app.server.ts +1713 -1672
  324. package/src/_services/shared.static-service.spec.ts +99 -99
  325. package/src/_services/shared.static-service.ts +78 -78
  326. package/src/index.ts +95 -95
  327. package/tsconfig.app.json +12 -12
  328. package/tsconfig.json +42 -42
  329. package/.dynamo/logs/cicd-pipeline/output.log +0 -2682
  330. package/.dynamo/logs/cicd-pipeline/status.json +0 -351
@@ -1,519 +1,519 @@
1
- import { DyNTS_AI_Provider_ServiceBase } from './ai-provider.service-base';
2
- import {
3
- DyFM_AI_CallSettings,
4
- DyFM_AI_Message,
5
- DyFM_AI_LLM_Response,
6
- DyFM_AI_MessageRole,
7
- DyFM_AI_Tool,
8
- DyFM_AI_ToolCall,
9
- DyFM_AI_ToolResult,
10
- DyFM_AI_ToolHandlers,
11
- DyFM_AI_ModelInfo,
12
- DyFM_AI_ModelRegistry_Util,
13
- } from '@futdevpro/fsm-dynamo/ai';
14
- import { DyFM_Error, DyFM_Error_Settings, DyFM_getLocalStackLocation, DyFM_Log, DyFM_Object } from '@futdevpro/fsm-dynamo';
15
- import {
16
- DyFM_AI_GenericSelect_Input,
17
- DyFM_AI_GenericMultiSelect_Input,
18
- DyFM_AI_JSONKeysDescription_Input,
19
- DyFM_AI_JSONExactKeys_Input,
20
- DyFM_AI_Message_Input,
21
- } from '../_models/ai-input-interfaces';
22
- import { Items } from 'openai/resources/conversations/items';
23
-
24
- /**
25
- * Abstract base class for LLM services
26
- * Defines all methods that must be implemented by AI providers
27
- */
28
- export abstract class DyNTS_AI_LLM_ServiceBase<
29
- T_AISettings extends DyFM_AI_CallSettings = DyFM_AI_CallSettings
30
- > extends DyNTS_AI_Provider_ServiceBase {
31
- /** Default settings for LLM calls */
32
- abstract readonly defaultSettings: T_AISettings;
33
-
34
- /** Default model to use for LLM calls */
35
- /* abstract readonly defaultModel: string; */
36
-
37
- /** Provider-specific predefined requests */
38
- abstract readonly predefinedRequests: any;
39
-
40
- _debugLog: boolean = false;
41
- get debugLog(): boolean {
42
- return this.defaultSettings?.debugLog ?? this._debugLog;
43
- }
44
-
45
- get defaultSystemPrompt(): string {
46
- return this.defaultSettings.systemPrompt;
47
- }
48
-
49
- get defaultModel(): string {
50
- return this.defaultSettings.useModel;
51
- }
52
-
53
- defaultLogReplacer: string = '...long-context...';
54
-
55
- //////////////////////////////////////////////////////////////////////////////////////////
56
- // FUNCTION CALLING (TOOL USE) — FR-047 //
57
- //////////////////////////////////////////////////////////////////////////////////////////
58
- // Provider-agnostic agent-loop. The loop logic lives here ONCE (ported from the legacy
59
- // FDPNTS_GPT_ControlService.getAnswerWithTools); each provider only overrides
60
- // `callModelWithTools` (one provider turn) and `getModelRegistry` (capability honesty).
61
- // Tools are a REQUEST parameter (not on settings), per the FR-047 design.
62
-
63
- /**
64
- * A provider tool-kepes modell-registry-je (override providerenkent — pl. DyFM_OAI_Models).
65
- * Ures default → a modell tool-kepessege ismeretlen → a precheck elutasitja (honesty).
66
- */
67
- protected getModelRegistry(): DyFM_AI_ModelInfo[] {
68
- return [];
69
- }
70
-
71
- /**
72
- * Ellenorzi, hogy a feloldott modell tamogatja-e a function calling-ot; ha nem, beszedes
73
- * hibaval elhasal MIELOTT barmilyen API-hivas tortenne (kritikus pl. a Local providernel).
74
- */
75
- protected assertToolsSupported(modelId: string): void {
76
- if (!DyFM_AI_ModelRegistry_Util.modelSupportsTools(this.getModelRegistry(), modelId)) {
77
- throw new DyFM_Error({
78
- message: `Model '${modelId}' does not support function calling (provider: ${this.aiProvider})`,
79
- userMessage: `The selected AI model does not support tools.`,
80
- errorCode: 'DyNTS-AILSB-TLC0',
81
- });
82
- }
83
- }
84
-
85
- /**
86
- * GPT valasz Function Calling Agent Tool-okkal — provider-agnosztikus agent-loop.
87
- * @description A `tools` definiciok + `toolHandlers` alapjan tool-loop-ot futtat: hivja a
88
- * modellt (callModelWithTools) → ha a valaszban tool-call van, lefuttatja a regisztralt
89
- * handler-t (runToolCall, never-throw) es az eredmenyt visszafuzi → ismetli, amig a model
90
- * vegso (tool-call nelkuli) valaszt ad, vagy a maxIterations limitet eleri. A tool-ok a
91
- * REQUEST-parameterben jonnek, nem a settings-ben.
92
- */
93
- async requestWithTools(
94
- set: {
95
- conversation: DyFM_AI_Message[];
96
- tools: DyFM_AI_Tool[];
97
- toolHandlers: DyFM_AI_ToolHandlers;
98
- settings?: T_AISettings;
99
- issuer: string;
100
- maxIterations?: number;
101
- }
102
- ): Promise<DyFM_AI_LLM_Response> {
103
- const modelId: string = (set.settings?.useModel ?? this.defaultModel) as string;
104
-
105
- if (!modelId) {
106
- throw new DyFM_Error({
107
- message: `No model configured for function calling (provider: ${this.aiProvider})`,
108
- userMessage: `No AI model is configured for tools.`,
109
- errorCode: 'DyNTS-AILSB-TLM0',
110
- });
111
- }
112
-
113
- this.assertToolsSupported(modelId);
114
-
115
- return this.runToolLoop(set);
116
- }
117
-
118
- /**
119
- * Egy provider-kor a tool-loop-ban: elkuldi a beszelgetest + tool-okat, normalizalt valaszt ad.
120
- * @description Override-olando providerenkent (OpenAI / Anthropic / Google / …). A default
121
- * elhasal — egy provider, ami nem implementalja, tool-use-t nem tud kiszolgalni.
122
- */
123
- protected async callModelWithTools(
124
- _set: {
125
- conversation: DyFM_AI_Message[];
126
- tools: DyFM_AI_Tool[];
127
- settings?: T_AISettings;
128
- issuer: string;
129
- }
130
- ): Promise<DyFM_AI_LLM_Response> {
131
- throw new DyFM_Error({
132
- message: `callModelWithTools is not implemented for provider '${this.aiProvider}'`,
133
- userMessage: `Function calling is not available for this AI provider yet.`,
134
- errorCode: 'DyNTS-AILSB-TLN0',
135
- });
136
- }
137
-
138
- /**
139
- * A provider-agnosztikus agent-loop torzse. A bemeno `conversation` ele a provider teszi a
140
- * system-message-et (callModelWithTools); a loop a tool-call/tool-result uzeneteket fuzi hozza.
141
- */
142
- protected async runToolLoop(
143
- set: {
144
- conversation: DyFM_AI_Message[];
145
- tools: DyFM_AI_Tool[];
146
- toolHandlers: DyFM_AI_ToolHandlers;
147
- settings?: T_AISettings;
148
- issuer: string;
149
- maxIterations?: number;
150
- }
151
- ): Promise<DyFM_AI_LLM_Response> {
152
- const maxIterations: number = set.maxIterations ?? 8;
153
- const conversation: DyFM_AI_Message[] = [...set.conversation];
154
-
155
- for (let iteration: number = 0; iteration < maxIterations; iteration++) {
156
- const response: DyFM_AI_LLM_Response = await this.callModelWithTools({
157
- conversation: conversation,
158
- tools: set.tools,
159
- settings: set.settings,
160
- issuer: set.issuer,
161
- });
162
-
163
- // nincs tool-hivas → ez a vegso valasz
164
- if (!response.toolCalls?.length) {
165
- return response;
166
- }
167
-
168
- // a model tool-hivo (assistant) uzenetet visszatesszuk a kontextusba
169
- conversation.push({
170
- role: DyFM_AI_MessageRole.assistant,
171
- content: response.content ?? '',
172
- toolCalls: response.toolCalls,
173
- });
174
-
175
- // minden tool-hivast lefuttatunk (never-throw), es az eredmenyt visszaadjuk a modellnek
176
- const results: DyFM_AI_ToolResult[] = await Promise.all(
177
- response.toolCalls.map((call: DyFM_AI_ToolCall) => this.runToolCall(call, set.toolHandlers))
178
- );
179
-
180
- results.forEach((result: DyFM_AI_ToolResult) => {
181
- conversation.push({
182
- role: DyFM_AI_MessageRole.tool,
183
- content: result.content,
184
- toolCallId: result.toolCallId,
185
- });
186
- });
187
- }
188
-
189
- // a tool-loop nem konvergalt a limiten belul
190
- throw new DyFM_Error({
191
- message: `Tool loop did not converge within ${maxIterations} iterations`,
192
- userMessage: `We encountered an error while running AI tools, please contact the responsible development team.`,
193
- errorCode: 'DyNTS-AILSB-TL0',
194
- });
195
- }
196
-
197
- /**
198
- * Egy tool-hivas lefuttatasa a regisztralt handler-rel. SOHA nem dob — a tool-hiba string-kent
199
- * megy vissza a modellnek (hogy korrigalhasson), hianyzo handler eseten is informativ uzenet
200
- * (soha nem [object Object]).
201
- */
202
- protected async runToolCall(
203
- call: DyFM_AI_ToolCall,
204
- toolHandlers: DyFM_AI_ToolHandlers
205
- ): Promise<DyFM_AI_ToolResult> {
206
- const handler = toolHandlers[call.name];
207
-
208
- if (!handler) {
209
- return {
210
- toolCallId: call.id,
211
- content: `ERROR: no handler registered for tool '${call.name}'`,
212
- isError: true,
213
- };
214
- }
215
-
216
- try {
217
- return {
218
- toolCallId: call.id,
219
- content: await handler(call.arguments),
220
- };
221
- } catch (error) {
222
- return {
223
- toolCallId: call.id,
224
- content: `ERROR executing tool '${call.name}': ` +
225
- `${error instanceof Error ? error.message : String(error)}`,
226
- isError: true,
227
- };
228
- }
229
- }
230
-
231
- // Core abstract methods
232
- /**
233
- * Call LLM with system and user messages
234
- */
235
- /* abstract callLLM(
236
- systemMessage: string,
237
- userMessage: string,
238
- settings?: DyFM_AI_CallSettings,
239
- issuer?: string
240
- ): Promise<string>; */
241
-
242
- /**
243
- * Call LLM with message history
244
- */
245
- /* abstract callLLMWithHistory(
246
- messages: DyFM_AI_Message[],
247
- settings?: DyFM_AI_CallSettings,
248
- issuer?: string
249
- ): Promise<string>; */
250
-
251
- /**
252
- * Call LLM and return raw response
253
- */
254
- /* abstract callLLMRaw(
255
- messages: DyFM_AI_Message[],
256
- settings?: DyFM_AI_CallSettings,
257
- issuer?: string
258
- ): Promise<DyFM_AI_LLM_Response>; */
259
-
260
- // Question methods (from OAI_LLM_ServiceBase)
261
- abstract requestSimpleMessage(set: DyFM_AI_Message_Input<T_AISettings>): Promise<string>;
262
- abstract requestYesNo(set: DyFM_AI_Message_Input<T_AISettings>): Promise<boolean>;
263
- abstract requestPercentage(set: DyFM_AI_Message_Input<T_AISettings>): Promise<number>;
264
-
265
- /* abstract askSelectQuestion(set: DyFM_AI_ListSelect_Input): Promise<string>; */
266
- abstract requestSelect<T>(
267
- set: DyFM_AI_GenericSelect_Input<T, T_AISettings>
268
- ): Promise<T | { unparsableResult: string }>;
269
- /* abstract askMultipleSelectQuestionWithOptions(set: DyFM_AI_MultiSelect_Input): Promise<string[]>; */
270
- abstract requestMultiselect<T>(
271
- set: DyFM_AI_GenericMultiSelect_Input<T, T_AISettings>
272
- ): Promise<T[] | { unparsableResult: string }>;
273
-
274
- abstract requestJSON<T>(set: DyFM_AI_Message_Input<T_AISettings>): Promise<T | { unparsableResult: string }>;
275
- abstract requestJSONQuestionWithKeysDescription<T>(
276
- set: DyFM_AI_JSONKeysDescription_Input<T_AISettings>
277
- ): Promise<T | { unparsableResult: string }>;
278
- /* abstract askJSONQuestionWithExactKeys(set: DyFM_AI_JSONExactKeys_Input): Promise<object>; */
279
- abstract requestJSONWithExactKeys<T>(
280
- set: DyFM_AI_JSONExactKeys_Input<T_AISettings>
281
- ): Promise<T | { unparsableResult: string }>;
282
- /* abstract sendMessage(set: DyFM_AI_SimpleMessage_Input): Promise<string>; */
283
-
284
- /* abstract requestStringList(set: DyFM_AI_Base_Input<T_AISettings>): Promise<string[] | { unparsableResult: string }>; */
285
- abstract requestList<T>(set: DyFM_AI_Message_Input<T_AISettings>): Promise<T[] | { unparsableResult: string }>;
286
-
287
- // Helper methods
288
- /* protected abstract getDefaultErrorSettings(
289
- method: string,
290
- error: any,
291
- issuer?: string
292
- ): DyFM_Error_Settings; */
293
-
294
- /* protected abstract getTextListAsText(list: string[]): string; */
295
-
296
- /* protected abstract logQuestion(set: DyFM_AI_Base_Input<T_AISettings>): void; */
297
-
298
- protected convertAnswerToBoolean(answer: string): boolean {
299
- return answer.toUpperCase().includes(this.predefinedRequests.yesNo.upperCaseYes);
300
- }
301
-
302
- protected convertAnswerToNumber(answer: string, message: string): number {
303
- if (this.isAnswerValid(answer, message)) {
304
- return null;
305
- }
306
-
307
- if (isNaN(+answer)) {
308
- DyFM_Log.T_error(
309
- 'DyNTS_AI_LLMChat_ServiceBase.convertAnswerToNumber got an invalid answer',
310
- {
311
- question: message,
312
- answer: answer,
313
- }
314
- );
315
-
316
- return null;
317
- }
318
-
319
- return +answer;
320
- }
321
-
322
- protected convertAnswerToSelectOption<T>(answer: string, message: string, options: T[]): T {
323
- if (this.isAnswerValid(answer, message)) {
324
- return null;
325
- }
326
-
327
- answer = answer.toLocaleUpperCase();
328
-
329
- const stringifiedOptions: string[] = this.stringifySelectOptions(options);
330
- for (const stringifiedItem of stringifiedOptions) {
331
- if (answer.includes(stringifiedItem.toLocaleUpperCase())) {
332
- const parsedItem: T | { unparsableResult: string } = DyFM_Object.safeParseJSON<T>(stringifiedItem);
333
-
334
- if ((parsedItem as { unparsableResult: string }).unparsableResult) {
335
- DyFM_Log.T_error(
336
- 'DyNTS_AI_LLMChat_ServiceBase.convertAnswerToSelectOption got an invalid answer',
337
- {
338
- question: message,
339
- answer: answer,
340
- }
341
- );
342
-
343
- return stringifiedItem as T;
344
- } else {
345
- return parsedItem as T;
346
- }
347
- }
348
- }
349
-
350
- return null;
351
- }
352
-
353
- protected convertAnswerToSelectOptions<T>(answer: string, message: string, options: T[]): T[] {
354
- if (this.isAnswerValid(answer, message)) {
355
- return null;
356
- }
357
-
358
- const enrichedOptions: { stringifiedOption: string, parsedOption: T }[] = options.map(
359
- (option: T) => ({
360
- stringifiedOption: this.stringifySelectOption(option),
361
- parsedOption: option
362
- })
363
- );
364
- const result: T[] = [];
365
-
366
- for (const item of enrichedOptions) {
367
- if (answer.includes(item.stringifiedOption)) {
368
- result.push(item.parsedOption);
369
- }
370
- }
371
-
372
- return result;
373
- }
374
-
375
- protected convertAnswerToJSON<T>(answer: string, message: string): T | { unparsableResult: string } {
376
- if (this.isAnswerValid(answer, message)) {
377
- return { unparsableResult: answer };
378
- }
379
-
380
- const parsedItem: T | { unparsableResult: string } = DyFM_Object.safeParseJSON<T>(answer);
381
- if ((parsedItem as { unparsableResult: string }).unparsableResult) {
382
- DyFM_Log.T_error(
383
- 'DyNTS_AI_LLMChat_ServiceBase.convertAnswerToJSON got an invalid answer',
384
- {
385
- question: message,
386
- answer: answer,
387
- }
388
- );
389
-
390
- return { unparsableResult: answer };
391
- }
392
-
393
- return parsedItem as T;
394
- }
395
-
396
- protected convertAnswerToList<T>(answer: string, message: string): T[] | { unparsableResult: string } {
397
- if (this.isAnswerValid(answer, message)) {
398
- return { unparsableResult: answer };
399
- }
400
-
401
- // Check if safeParseJSON returns unparsableResult before calling safeParseList
402
- // because safeParseList doesn't properly handle unparsableResult
403
- const parsedCheck: T[] | { unparsableResult: string } = DyFM_Object.safeParseJSON<T[]>(answer, true);
404
- if ((parsedCheck as { unparsableResult: string }).unparsableResult) {
405
- DyFM_Log.T_error(
406
- 'DyNTS_AI_LLMChat_ServiceBase.convertAnswerToList got an invalid answer',
407
- {
408
- question: message,
409
- answer: answer,
410
- }
411
- );
412
-
413
- return { unparsableResult: answer };
414
- }
415
-
416
- return DyFM_Object.safeParseList<T[]>(answer);
417
- }
418
-
419
- protected stringifySelectOptions<T>(options: T[]): string[] {
420
- return options.map(item => this.stringifySelectOption(item));
421
- }
422
-
423
- protected stringifySelectOption<T>(option: T): string {
424
- return JSON.stringify(option);
425
- }
426
-
427
- protected isAnswerValid(answer: string, message: string): boolean {
428
- if (!answer?.trim?.()?.length) {
429
- DyFM_Log.T_error(
430
- 'DyNTS_AI_LLMChat_ServiceBase.convertAnswerToSelectOption got an invalid answer',
431
- {
432
- question: message,
433
- answer: answer,
434
- }
435
- );
436
-
437
- return true;
438
- }
439
-
440
- return false;
441
- }
442
-
443
- /**
444
- * olvasható mondatszerű-listaszerű formába teszi a listaelemeket
445
- * pl.: ['a', 'b', 'c'] -> '"a", "b" or "c"'
446
- */
447
- protected getTextListAsText(list: string[]): string {
448
- list = list.filter(item => item?.trim()).map(item => `"${item}"`);
449
-
450
- /* list = list.map(item => item.toLocaleLowerCase()); */
451
-
452
- list.push(list.pop() + ' or ' + list.pop());
453
-
454
- return list.join(', ');
455
- }
456
-
457
- protected logQuestion(
458
- set: DyFM_AI_Message_Input<T_AISettings>
459
- ): void {
460
- if (set.settings?.debugLog ?? this._debugLog) {
461
- console.log('\n - ', set.message);
462
- }
463
- }
464
-
465
- protected getDefaultSystemMessage(settings: T_AISettings): DyFM_AI_Message {
466
- return {
467
- role: DyFM_AI_MessageRole.system,
468
- content: settings?.systemPrompt || this.defaultSystemPrompt,
469
- };
470
- }
471
-
472
- protected validateConversation(conversation: DyFM_AI_Message[]): void {
473
- conversation.forEach((message: DyFM_AI_Message, index: number) => {
474
- if (!message.role) {
475
- throw new DyFM_Error({
476
- message: `Message has no role at index ${index}`,
477
- additionalContent: {
478
- invalidMessage: message,
479
- conversation: conversation,
480
- }
481
- });
482
- }
483
- });
484
-
485
- conversation = conversation.filter(message => message.content);
486
- }
487
-
488
- protected logAnswer(answer: string): void {
489
- if (this._debugLog) {
490
- console.log(' - answer: ', answer);
491
- }
492
- }
493
-
494
-
495
-
496
- //////////////////////////////////////////////////////////////////////////////////////////
497
- // LLM CHAT METHODS //
498
- //////////////////////////////////////////////////////////////////////////////////////////
499
-
500
-
501
- protected logConversation(
502
- set: {
503
- conversation: DyFM_AI_Message[],
504
- debugLog?: boolean,
505
- /** this is used to readably replace too long contents to eg '...' in logs */
506
- replaceThisInLog?: string,
507
- }
508
- ) {
509
- if (set.debugLog || this._debugLog) {
510
- DyFM_Log.info('Conversation log at', DyFM_getLocalStackLocation());
511
-
512
- set.conversation.forEach(message => {
513
- console.log(
514
- ` - ${message.role}: ${message.content.replace(set.replaceThisInLog, this.defaultLogReplacer)}`
515
- );
516
- });
517
- }
518
- }
519
- }
1
+ import { DyNTS_AI_Provider_ServiceBase } from './ai-provider.service-base';
2
+ import {
3
+ DyFM_AI_CallSettings,
4
+ DyFM_AI_Message,
5
+ DyFM_AI_LLM_Response,
6
+ DyFM_AI_MessageRole,
7
+ DyFM_AI_Tool,
8
+ DyFM_AI_ToolCall,
9
+ DyFM_AI_ToolResult,
10
+ DyFM_AI_ToolHandlers,
11
+ DyFM_AI_ModelInfo,
12
+ DyFM_AI_ModelRegistry_Util,
13
+ } from '@futdevpro/fsm-dynamo/ai';
14
+ import { DyFM_Error, DyFM_Error_Settings, DyFM_getLocalStackLocation, DyFM_Log, DyFM_Object } from '@futdevpro/fsm-dynamo';
15
+ import {
16
+ DyFM_AI_GenericSelect_Input,
17
+ DyFM_AI_GenericMultiSelect_Input,
18
+ DyFM_AI_JSONKeysDescription_Input,
19
+ DyFM_AI_JSONExactKeys_Input,
20
+ DyFM_AI_Message_Input,
21
+ } from '../_models/ai-input-interfaces';
22
+ import { Items } from 'openai/resources/conversations/items';
23
+
24
+ /**
25
+ * Abstract base class for LLM services
26
+ * Defines all methods that must be implemented by AI providers
27
+ */
28
+ export abstract class DyNTS_AI_LLM_ServiceBase<
29
+ T_AISettings extends DyFM_AI_CallSettings = DyFM_AI_CallSettings
30
+ > extends DyNTS_AI_Provider_ServiceBase {
31
+ /** Default settings for LLM calls */
32
+ abstract readonly defaultSettings: T_AISettings;
33
+
34
+ /** Default model to use for LLM calls */
35
+ /* abstract readonly defaultModel: string; */
36
+
37
+ /** Provider-specific predefined requests */
38
+ abstract readonly predefinedRequests: any;
39
+
40
+ _debugLog: boolean = false;
41
+ get debugLog(): boolean {
42
+ return this.defaultSettings?.debugLog ?? this._debugLog;
43
+ }
44
+
45
+ get defaultSystemPrompt(): string {
46
+ return this.defaultSettings.systemPrompt;
47
+ }
48
+
49
+ get defaultModel(): string {
50
+ return this.defaultSettings.useModel;
51
+ }
52
+
53
+ defaultLogReplacer: string = '...long-context...';
54
+
55
+ //////////////////////////////////////////////////////////////////////////////////////////
56
+ // FUNCTION CALLING (TOOL USE) — FR-047 //
57
+ //////////////////////////////////////////////////////////////////////////////////////////
58
+ // Provider-agnostic agent-loop. The loop logic lives here ONCE (ported from the legacy
59
+ // FDPNTS_GPT_ControlService.getAnswerWithTools); each provider only overrides
60
+ // `callModelWithTools` (one provider turn) and `getModelRegistry` (capability honesty).
61
+ // Tools are a REQUEST parameter (not on settings), per the FR-047 design.
62
+
63
+ /**
64
+ * A provider tool-kepes modell-registry-je (override providerenkent — pl. DyFM_OAI_Models).
65
+ * Ures default → a modell tool-kepessege ismeretlen → a precheck elutasitja (honesty).
66
+ */
67
+ protected getModelRegistry(): DyFM_AI_ModelInfo[] {
68
+ return [];
69
+ }
70
+
71
+ /**
72
+ * Ellenorzi, hogy a feloldott modell tamogatja-e a function calling-ot; ha nem, beszedes
73
+ * hibaval elhasal MIELOTT barmilyen API-hivas tortenne (kritikus pl. a Local providernel).
74
+ */
75
+ protected assertToolsSupported(modelId: string): void {
76
+ if (!DyFM_AI_ModelRegistry_Util.modelSupportsTools(this.getModelRegistry(), modelId)) {
77
+ throw new DyFM_Error({
78
+ message: `Model '${modelId}' does not support function calling (provider: ${this.aiProvider})`,
79
+ userMessage: `The selected AI model does not support tools.`,
80
+ errorCode: 'DyNTS-AILSB-TLC0',
81
+ });
82
+ }
83
+ }
84
+
85
+ /**
86
+ * GPT valasz Function Calling Agent Tool-okkal — provider-agnosztikus agent-loop.
87
+ * @description A `tools` definiciok + `toolHandlers` alapjan tool-loop-ot futtat: hivja a
88
+ * modellt (callModelWithTools) → ha a valaszban tool-call van, lefuttatja a regisztralt
89
+ * handler-t (runToolCall, never-throw) es az eredmenyt visszafuzi → ismetli, amig a model
90
+ * vegso (tool-call nelkuli) valaszt ad, vagy a maxIterations limitet eleri. A tool-ok a
91
+ * REQUEST-parameterben jonnek, nem a settings-ben.
92
+ */
93
+ async requestWithTools(
94
+ set: {
95
+ conversation: DyFM_AI_Message[];
96
+ tools: DyFM_AI_Tool[];
97
+ toolHandlers: DyFM_AI_ToolHandlers;
98
+ settings?: T_AISettings;
99
+ issuer: string;
100
+ maxIterations?: number;
101
+ }
102
+ ): Promise<DyFM_AI_LLM_Response> {
103
+ const modelId: string = (set.settings?.useModel ?? this.defaultModel) as string;
104
+
105
+ if (!modelId) {
106
+ throw new DyFM_Error({
107
+ message: `No model configured for function calling (provider: ${this.aiProvider})`,
108
+ userMessage: `No AI model is configured for tools.`,
109
+ errorCode: 'DyNTS-AILSB-TLM0',
110
+ });
111
+ }
112
+
113
+ this.assertToolsSupported(modelId);
114
+
115
+ return this.runToolLoop(set);
116
+ }
117
+
118
+ /**
119
+ * Egy provider-kor a tool-loop-ban: elkuldi a beszelgetest + tool-okat, normalizalt valaszt ad.
120
+ * @description Override-olando providerenkent (OpenAI / Anthropic / Google / …). A default
121
+ * elhasal — egy provider, ami nem implementalja, tool-use-t nem tud kiszolgalni.
122
+ */
123
+ protected async callModelWithTools(
124
+ _set: {
125
+ conversation: DyFM_AI_Message[];
126
+ tools: DyFM_AI_Tool[];
127
+ settings?: T_AISettings;
128
+ issuer: string;
129
+ }
130
+ ): Promise<DyFM_AI_LLM_Response> {
131
+ throw new DyFM_Error({
132
+ message: `callModelWithTools is not implemented for provider '${this.aiProvider}'`,
133
+ userMessage: `Function calling is not available for this AI provider yet.`,
134
+ errorCode: 'DyNTS-AILSB-TLN0',
135
+ });
136
+ }
137
+
138
+ /**
139
+ * A provider-agnosztikus agent-loop torzse. A bemeno `conversation` ele a provider teszi a
140
+ * system-message-et (callModelWithTools); a loop a tool-call/tool-result uzeneteket fuzi hozza.
141
+ */
142
+ protected async runToolLoop(
143
+ set: {
144
+ conversation: DyFM_AI_Message[];
145
+ tools: DyFM_AI_Tool[];
146
+ toolHandlers: DyFM_AI_ToolHandlers;
147
+ settings?: T_AISettings;
148
+ issuer: string;
149
+ maxIterations?: number;
150
+ }
151
+ ): Promise<DyFM_AI_LLM_Response> {
152
+ const maxIterations: number = set.maxIterations ?? 8;
153
+ const conversation: DyFM_AI_Message[] = [...set.conversation];
154
+
155
+ for (let iteration: number = 0; iteration < maxIterations; iteration++) {
156
+ const response: DyFM_AI_LLM_Response = await this.callModelWithTools({
157
+ conversation: conversation,
158
+ tools: set.tools,
159
+ settings: set.settings,
160
+ issuer: set.issuer,
161
+ });
162
+
163
+ // nincs tool-hivas → ez a vegso valasz
164
+ if (!response.toolCalls?.length) {
165
+ return response;
166
+ }
167
+
168
+ // a model tool-hivo (assistant) uzenetet visszatesszuk a kontextusba
169
+ conversation.push({
170
+ role: DyFM_AI_MessageRole.assistant,
171
+ content: response.content ?? '',
172
+ toolCalls: response.toolCalls,
173
+ });
174
+
175
+ // minden tool-hivast lefuttatunk (never-throw), es az eredmenyt visszaadjuk a modellnek
176
+ const results: DyFM_AI_ToolResult[] = await Promise.all(
177
+ response.toolCalls.map((call: DyFM_AI_ToolCall) => this.runToolCall(call, set.toolHandlers))
178
+ );
179
+
180
+ results.forEach((result: DyFM_AI_ToolResult) => {
181
+ conversation.push({
182
+ role: DyFM_AI_MessageRole.tool,
183
+ content: result.content,
184
+ toolCallId: result.toolCallId,
185
+ });
186
+ });
187
+ }
188
+
189
+ // a tool-loop nem konvergalt a limiten belul
190
+ throw new DyFM_Error({
191
+ message: `Tool loop did not converge within ${maxIterations} iterations`,
192
+ userMessage: `We encountered an error while running AI tools, please contact the responsible development team.`,
193
+ errorCode: 'DyNTS-AILSB-TL0',
194
+ });
195
+ }
196
+
197
+ /**
198
+ * Egy tool-hivas lefuttatasa a regisztralt handler-rel. SOHA nem dob — a tool-hiba string-kent
199
+ * megy vissza a modellnek (hogy korrigalhasson), hianyzo handler eseten is informativ uzenet
200
+ * (soha nem [object Object]).
201
+ */
202
+ protected async runToolCall(
203
+ call: DyFM_AI_ToolCall,
204
+ toolHandlers: DyFM_AI_ToolHandlers
205
+ ): Promise<DyFM_AI_ToolResult> {
206
+ const handler = toolHandlers[call.name];
207
+
208
+ if (!handler) {
209
+ return {
210
+ toolCallId: call.id,
211
+ content: `ERROR: no handler registered for tool '${call.name}'`,
212
+ isError: true,
213
+ };
214
+ }
215
+
216
+ try {
217
+ return {
218
+ toolCallId: call.id,
219
+ content: await handler(call.arguments),
220
+ };
221
+ } catch (error) {
222
+ return {
223
+ toolCallId: call.id,
224
+ content: `ERROR executing tool '${call.name}': ` +
225
+ `${error instanceof Error ? error.message : String(error)}`,
226
+ isError: true,
227
+ };
228
+ }
229
+ }
230
+
231
+ // Core abstract methods
232
+ /**
233
+ * Call LLM with system and user messages
234
+ */
235
+ /* abstract callLLM(
236
+ systemMessage: string,
237
+ userMessage: string,
238
+ settings?: DyFM_AI_CallSettings,
239
+ issuer?: string
240
+ ): Promise<string>; */
241
+
242
+ /**
243
+ * Call LLM with message history
244
+ */
245
+ /* abstract callLLMWithHistory(
246
+ messages: DyFM_AI_Message[],
247
+ settings?: DyFM_AI_CallSettings,
248
+ issuer?: string
249
+ ): Promise<string>; */
250
+
251
+ /**
252
+ * Call LLM and return raw response
253
+ */
254
+ /* abstract callLLMRaw(
255
+ messages: DyFM_AI_Message[],
256
+ settings?: DyFM_AI_CallSettings,
257
+ issuer?: string
258
+ ): Promise<DyFM_AI_LLM_Response>; */
259
+
260
+ // Question methods (from OAI_LLM_ServiceBase)
261
+ abstract requestSimpleMessage(set: DyFM_AI_Message_Input<T_AISettings>): Promise<string>;
262
+ abstract requestYesNo(set: DyFM_AI_Message_Input<T_AISettings>): Promise<boolean>;
263
+ abstract requestPercentage(set: DyFM_AI_Message_Input<T_AISettings>): Promise<number>;
264
+
265
+ /* abstract askSelectQuestion(set: DyFM_AI_ListSelect_Input): Promise<string>; */
266
+ abstract requestSelect<T>(
267
+ set: DyFM_AI_GenericSelect_Input<T, T_AISettings>
268
+ ): Promise<T | { unparsableResult: string }>;
269
+ /* abstract askMultipleSelectQuestionWithOptions(set: DyFM_AI_MultiSelect_Input): Promise<string[]>; */
270
+ abstract requestMultiselect<T>(
271
+ set: DyFM_AI_GenericMultiSelect_Input<T, T_AISettings>
272
+ ): Promise<T[] | { unparsableResult: string }>;
273
+
274
+ abstract requestJSON<T>(set: DyFM_AI_Message_Input<T_AISettings>): Promise<T | { unparsableResult: string }>;
275
+ abstract requestJSONQuestionWithKeysDescription<T>(
276
+ set: DyFM_AI_JSONKeysDescription_Input<T_AISettings>
277
+ ): Promise<T | { unparsableResult: string }>;
278
+ /* abstract askJSONQuestionWithExactKeys(set: DyFM_AI_JSONExactKeys_Input): Promise<object>; */
279
+ abstract requestJSONWithExactKeys<T>(
280
+ set: DyFM_AI_JSONExactKeys_Input<T_AISettings>
281
+ ): Promise<T | { unparsableResult: string }>;
282
+ /* abstract sendMessage(set: DyFM_AI_SimpleMessage_Input): Promise<string>; */
283
+
284
+ /* abstract requestStringList(set: DyFM_AI_Base_Input<T_AISettings>): Promise<string[] | { unparsableResult: string }>; */
285
+ abstract requestList<T>(set: DyFM_AI_Message_Input<T_AISettings>): Promise<T[] | { unparsableResult: string }>;
286
+
287
+ // Helper methods
288
+ /* protected abstract getDefaultErrorSettings(
289
+ method: string,
290
+ error: any,
291
+ issuer?: string
292
+ ): DyFM_Error_Settings; */
293
+
294
+ /* protected abstract getTextListAsText(list: string[]): string; */
295
+
296
+ /* protected abstract logQuestion(set: DyFM_AI_Base_Input<T_AISettings>): void; */
297
+
298
+ protected convertAnswerToBoolean(answer: string): boolean {
299
+ return answer.toUpperCase().includes(this.predefinedRequests.yesNo.upperCaseYes);
300
+ }
301
+
302
+ protected convertAnswerToNumber(answer: string, message: string): number {
303
+ if (this.isAnswerValid(answer, message)) {
304
+ return null;
305
+ }
306
+
307
+ if (isNaN(+answer)) {
308
+ DyFM_Log.T_error(
309
+ 'DyNTS_AI_LLMChat_ServiceBase.convertAnswerToNumber got an invalid answer',
310
+ {
311
+ question: message,
312
+ answer: answer,
313
+ }
314
+ );
315
+
316
+ return null;
317
+ }
318
+
319
+ return +answer;
320
+ }
321
+
322
+ protected convertAnswerToSelectOption<T>(answer: string, message: string, options: T[]): T {
323
+ if (this.isAnswerValid(answer, message)) {
324
+ return null;
325
+ }
326
+
327
+ answer = answer.toLocaleUpperCase();
328
+
329
+ const stringifiedOptions: string[] = this.stringifySelectOptions(options);
330
+ for (const stringifiedItem of stringifiedOptions) {
331
+ if (answer.includes(stringifiedItem.toLocaleUpperCase())) {
332
+ const parsedItem: T | { unparsableResult: string } = DyFM_Object.safeParseJSON<T>(stringifiedItem);
333
+
334
+ if ((parsedItem as { unparsableResult: string }).unparsableResult) {
335
+ DyFM_Log.T_error(
336
+ 'DyNTS_AI_LLMChat_ServiceBase.convertAnswerToSelectOption got an invalid answer',
337
+ {
338
+ question: message,
339
+ answer: answer,
340
+ }
341
+ );
342
+
343
+ return stringifiedItem as T;
344
+ } else {
345
+ return parsedItem as T;
346
+ }
347
+ }
348
+ }
349
+
350
+ return null;
351
+ }
352
+
353
+ protected convertAnswerToSelectOptions<T>(answer: string, message: string, options: T[]): T[] {
354
+ if (this.isAnswerValid(answer, message)) {
355
+ return null;
356
+ }
357
+
358
+ const enrichedOptions: { stringifiedOption: string, parsedOption: T }[] = options.map(
359
+ (option: T) => ({
360
+ stringifiedOption: this.stringifySelectOption(option),
361
+ parsedOption: option
362
+ })
363
+ );
364
+ const result: T[] = [];
365
+
366
+ for (const item of enrichedOptions) {
367
+ if (answer.includes(item.stringifiedOption)) {
368
+ result.push(item.parsedOption);
369
+ }
370
+ }
371
+
372
+ return result;
373
+ }
374
+
375
+ protected convertAnswerToJSON<T>(answer: string, message: string): T | { unparsableResult: string } {
376
+ if (this.isAnswerValid(answer, message)) {
377
+ return { unparsableResult: answer };
378
+ }
379
+
380
+ const parsedItem: T | { unparsableResult: string } = DyFM_Object.safeParseJSON<T>(answer);
381
+ if ((parsedItem as { unparsableResult: string }).unparsableResult) {
382
+ DyFM_Log.T_error(
383
+ 'DyNTS_AI_LLMChat_ServiceBase.convertAnswerToJSON got an invalid answer',
384
+ {
385
+ question: message,
386
+ answer: answer,
387
+ }
388
+ );
389
+
390
+ return { unparsableResult: answer };
391
+ }
392
+
393
+ return parsedItem as T;
394
+ }
395
+
396
+ protected convertAnswerToList<T>(answer: string, message: string): T[] | { unparsableResult: string } {
397
+ if (this.isAnswerValid(answer, message)) {
398
+ return { unparsableResult: answer };
399
+ }
400
+
401
+ // Check if safeParseJSON returns unparsableResult before calling safeParseList
402
+ // because safeParseList doesn't properly handle unparsableResult
403
+ const parsedCheck: T[] | { unparsableResult: string } = DyFM_Object.safeParseJSON<T[]>(answer, true);
404
+ if ((parsedCheck as { unparsableResult: string }).unparsableResult) {
405
+ DyFM_Log.T_error(
406
+ 'DyNTS_AI_LLMChat_ServiceBase.convertAnswerToList got an invalid answer',
407
+ {
408
+ question: message,
409
+ answer: answer,
410
+ }
411
+ );
412
+
413
+ return { unparsableResult: answer };
414
+ }
415
+
416
+ return DyFM_Object.safeParseList<T[]>(answer);
417
+ }
418
+
419
+ protected stringifySelectOptions<T>(options: T[]): string[] {
420
+ return options.map(item => this.stringifySelectOption(item));
421
+ }
422
+
423
+ protected stringifySelectOption<T>(option: T): string {
424
+ return JSON.stringify(option);
425
+ }
426
+
427
+ protected isAnswerValid(answer: string, message: string): boolean {
428
+ if (!answer?.trim?.()?.length) {
429
+ DyFM_Log.T_error(
430
+ 'DyNTS_AI_LLMChat_ServiceBase.convertAnswerToSelectOption got an invalid answer',
431
+ {
432
+ question: message,
433
+ answer: answer,
434
+ }
435
+ );
436
+
437
+ return true;
438
+ }
439
+
440
+ return false;
441
+ }
442
+
443
+ /**
444
+ * olvasható mondatszerű-listaszerű formába teszi a listaelemeket
445
+ * pl.: ['a', 'b', 'c'] -> '"a", "b" or "c"'
446
+ */
447
+ protected getTextListAsText(list: string[]): string {
448
+ list = list.filter(item => item?.trim()).map(item => `"${item}"`);
449
+
450
+ /* list = list.map(item => item.toLocaleLowerCase()); */
451
+
452
+ list.push(list.pop() + ' or ' + list.pop());
453
+
454
+ return list.join(', ');
455
+ }
456
+
457
+ protected logQuestion(
458
+ set: DyFM_AI_Message_Input<T_AISettings>
459
+ ): void {
460
+ if (set.settings?.debugLog ?? this._debugLog) {
461
+ console.log('\n - ', set.message);
462
+ }
463
+ }
464
+
465
+ protected getDefaultSystemMessage(settings: T_AISettings): DyFM_AI_Message {
466
+ return {
467
+ role: DyFM_AI_MessageRole.system,
468
+ content: settings?.systemPrompt || this.defaultSystemPrompt,
469
+ };
470
+ }
471
+
472
+ protected validateConversation(conversation: DyFM_AI_Message[]): void {
473
+ conversation.forEach((message: DyFM_AI_Message, index: number) => {
474
+ if (!message.role) {
475
+ throw new DyFM_Error({
476
+ message: `Message has no role at index ${index}`,
477
+ additionalContent: {
478
+ invalidMessage: message,
479
+ conversation: conversation,
480
+ }
481
+ });
482
+ }
483
+ });
484
+
485
+ conversation = conversation.filter(message => message.content);
486
+ }
487
+
488
+ protected logAnswer(answer: string): void {
489
+ if (this._debugLog) {
490
+ console.log(' - answer: ', answer);
491
+ }
492
+ }
493
+
494
+
495
+
496
+ //////////////////////////////////////////////////////////////////////////////////////////
497
+ // LLM CHAT METHODS //
498
+ //////////////////////////////////////////////////////////////////////////////////////////
499
+
500
+
501
+ protected logConversation(
502
+ set: {
503
+ conversation: DyFM_AI_Message[],
504
+ debugLog?: boolean,
505
+ /** this is used to readably replace too long contents to eg '...' in logs */
506
+ replaceThisInLog?: string,
507
+ }
508
+ ) {
509
+ if (set.debugLog || this._debugLog) {
510
+ DyFM_Log.info('Conversation log at', DyFM_getLocalStackLocation());
511
+
512
+ set.conversation.forEach(message => {
513
+ console.log(
514
+ ` - ${message.role}: ${message.content.replace(set.replaceThisInLog, this.defaultLogReplacer)}`
515
+ );
516
+ });
517
+ }
518
+ }
519
+ }