@futdevpro/nts-dynamo 1.15.55 → 1.15.57

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 (343) 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/logs/cicd-pipeline/output.log +4691 -0
  17. package/.dynamo/logs/cicd-pipeline/status.json +396 -0
  18. package/.github/workflows/main.yml +432 -432
  19. package/.vscode/settings.json +10 -10
  20. package/HOWTO.md +15 -15
  21. package/LICENSE +21 -21
  22. package/__documentations/2026-06-15-fr193-memory-guard.md +73 -0
  23. package/__documentations/nts-integration-tests-2026-03-17.md +26 -26
  24. package/_specifications/BACKLOG.md +92 -92
  25. package/_specifications/TODO.md +15 -15
  26. package/_specifications/agent.md +138 -138
  27. package/build/_collections/global-settings.const.d.ts.map +1 -1
  28. package/build/_collections/global-settings.const.js +11 -0
  29. package/build/_collections/global-settings.const.js.map +1 -1
  30. package/build/_models/interfaces/global-settings.interface.d.ts +21 -0
  31. package/build/_models/interfaces/global-settings.interface.d.ts.map +1 -1
  32. package/build/_services/core/memory-guard.service.d.ts +111 -0
  33. package/build/_services/core/memory-guard.service.d.ts.map +1 -0
  34. package/build/_services/core/memory-guard.service.js +198 -0
  35. package/build/_services/core/memory-guard.service.js.map +1 -0
  36. package/build/_services/server/app.server.d.ts.map +1 -1
  37. package/build/_services/server/app.server.js +13 -0
  38. package/build/_services/server/app.server.js.map +1 -1
  39. package/build/index.d.ts +1 -0
  40. package/build/index.d.ts.map +1 -1
  41. package/build/index.js +1 -0
  42. package/build/index.js.map +1 -1
  43. package/eslint.config.js +3 -3
  44. package/nodemon.json +24 -24
  45. package/package.json +361 -361
  46. package/pnpm-workspace.yaml +4 -4
  47. package/scripts/run-coverage-tests.js +28 -28
  48. package/spec/support/helpers/spec-reporter-loader.js +359 -359
  49. package/spec/support/helpers/ts-node-helper.js +93 -93
  50. package/spec/support/jasmine.coverage.json +24 -24
  51. package/spec/support/jasmine.json +24 -24
  52. package/src/_collections/archive.util.spec.ts +57 -57
  53. package/src/_collections/archive.util.ts +18 -18
  54. package/src/_collections/atlas-default-db-options.const.ts +9 -9
  55. package/src/_collections/default-fallback-cache-max-age.const.spec.ts +11 -11
  56. package/src/_collections/default-fallback-cache-max-age.const.ts +2 -2
  57. package/src/_collections/default-not-found-page.const.spec.ts +19 -19
  58. package/src/_collections/default-not-found-page.const.ts +22 -22
  59. package/src/_collections/default-socket-path.const.spec.ts +12 -12
  60. package/src/_collections/default-socket-path.const.ts +2 -2
  61. package/src/_collections/get-environment-settings.util.spec.ts +210 -210
  62. package/src/_collections/get-environment-settings.util.ts +48 -48
  63. package/src/_collections/global-settings.const.ts +82 -70
  64. package/src/_collections/sample.env +21 -21
  65. package/src/_collections/star.controller.spec.ts +224 -224
  66. package/src/_collections/star.controller.ts +129 -129
  67. package/src/_enums/data-model-type.enum.ts +14 -14
  68. package/src/_enums/data-service-function.enum.ts +24 -24
  69. package/src/_enums/predefined-data-types.enum.ts +16 -16
  70. package/src/_enums/route-security.enum.ts +12 -12
  71. package/src/_models/control-models/api-call-params.control-model.spec.ts +152 -152
  72. package/src/_models/control-models/api-call-params.control-model.ts +142 -142
  73. package/src/_models/control-models/app-ext-system-controls.control-model.spec.ts +52 -52
  74. package/src/_models/control-models/app-ext-system-controls.control-model.ts +9 -9
  75. package/src/_models/control-models/app-params.control-model.spec.ts +225 -225
  76. package/src/_models/control-models/app-params.control-model.ts +136 -136
  77. package/src/_models/control-models/app-system-controls.control-model.spec.ts +31 -31
  78. package/src/_models/control-models/app-system-controls.control-model.ts +9 -9
  79. package/src/_models/control-models/endpoint-params.control-model.spec.ts +627 -627
  80. package/src/_models/control-models/endpoint-params.control-model.ts +627 -627
  81. package/src/_models/control-models/http-settings.control-model.spec.ts +77 -77
  82. package/src/_models/control-models/http-settings.control-model.ts +37 -37
  83. package/src/_models/control-models/system-control.control-model.spec.ts +27 -27
  84. package/src/_models/control-models/system-control.control-model.ts +12 -12
  85. package/src/_models/interfaces/certification-settings.interface.ts +7 -7
  86. package/src/_models/interfaces/environment-settings.interface.ts +59 -59
  87. package/src/_models/interfaces/global-log-settings.interface.ts +171 -171
  88. package/src/_models/interfaces/global-service-settings.interface.ts +47 -47
  89. package/src/_models/interfaces/global-settings.interface.ts +23 -1
  90. package/src/_models/interfaces/routing-module-settings.interface.ts +21 -21
  91. package/src/_models/interfaces/static-client-settings.interface.spec.ts +29 -29
  92. package/src/_models/interfaces/static-client-settings.interface.ts +28 -28
  93. package/src/_models/types/db-update.type.ts +100 -100
  94. package/src/_modules/ai/_models/ai-input-interfaces.ts +117 -117
  95. package/src/_modules/ai/_models/ai-test-generation-result.interface.ts +16 -16
  96. package/src/_modules/ai/_modules/anthropic/_services/aai-user-key.control-service.ts +138 -138
  97. package/src/_modules/ai/_modules/anthropic/index.ts +5 -5
  98. package/src/_modules/ai/_modules/document-ai/_collections/dai-chunking.util.spec.ts +242 -242
  99. package/src/_modules/ai/_modules/document-ai/_collections/dai-chunking.util.ts +639 -639
  100. package/src/_modules/ai/_modules/document-ai/_collections/dai-document.util.spec.ts +209 -209
  101. package/src/_modules/ai/_modules/document-ai/_collections/dai-document.util.ts +85 -85
  102. package/src/_modules/ai/_modules/document-ai/_enums/dai-compare-result-type.enum.ts +7 -7
  103. package/src/_modules/ai/_modules/document-ai/_models/data-models/dai-doc-chunk.data-model.ts +146 -146
  104. package/src/_modules/ai/_modules/document-ai/_models/data-models/dai-doc-page.data-model.ts +162 -162
  105. package/src/_modules/ai/_modules/document-ai/_models/data-models/dai-document.data-model.ts +99 -99
  106. package/src/_modules/ai/_modules/document-ai/_models/interfaces/dai-doc-chunk-compare-result.interface.ts +18 -18
  107. package/src/_modules/ai/_modules/document-ai/_models/interfaces/dai-doc-page-compare-result.interface.ts +19 -19
  108. package/src/_modules/ai/_modules/document-ai/_models/interfaces/dai-document-compare-result.interface.ts +25 -25
  109. package/src/_modules/ai/_modules/document-ai/index.ts +28 -28
  110. package/src/_modules/ai/_modules/fdp-ai/_services/fdpai-user-key.control-service.ts +189 -189
  111. package/src/_modules/ai/_modules/fdp-ai/index.ts +5 -5
  112. package/src/_modules/ai/_modules/open-ai/_collections/oai-global-settings.const.ts +9 -9
  113. package/src/_modules/ai/_modules/open-ai/_collections/oai-llm-predefined-requests-hu.conts.ts +82 -82
  114. package/src/_modules/ai/_modules/open-ai/_collections/oai-llm-predefined-requests.conts.ts +75 -75
  115. package/src/_modules/ai/_modules/open-ai/_enums/oai-gpt-message-role.enum.ts +45 -45
  116. package/src/_modules/ai/_modules/open-ai/_models/interfaces/oai-global-settings.interface.ts +7 -7
  117. package/src/_modules/ai/_modules/open-ai/_models/interfaces/oai-gpt-message.interface.ts +7 -7
  118. package/src/_modules/ai/_modules/open-ai/_models/interfaces/oai-llm-predefined-requests.interface.ts +57 -57
  119. package/src/_modules/ai/_modules/open-ai/_services/data-services/oai-doc-chunk-data.service.ts +292 -292
  120. package/src/_modules/ai/_modules/open-ai/_services/data-services/oai-document.data-service.spec.ts +342 -342
  121. package/src/_modules/ai/_modules/open-ai/_services/data-services/oai-vector-data.service.spec.ts +550 -550
  122. package/src/_modules/ai/_modules/open-ai/_services/data-services/oai-vector-data.service.ts +630 -630
  123. package/src/_modules/ai/_modules/open-ai/_services/oai-embedding.control-service.spec.ts +332 -332
  124. package/src/_modules/ai/_modules/open-ai/_services/oai-llm-chat.service-base.spec.ts +462 -462
  125. package/src/_modules/ai/_modules/open-ai/_services/oai-llm-chat.service-base.ts +634 -634
  126. package/src/_modules/ai/_modules/open-ai/_services/oai-llm.service-base.spec.ts +489 -489
  127. package/src/_modules/ai/_modules/open-ai/_services/oai-llm.service-base.tools.spec.ts +173 -173
  128. package/src/_modules/ai/_modules/open-ai/_services/oai-llm.service-base.ts +1033 -1033
  129. package/src/_modules/ai/_modules/open-ai/_services/oai-user-key.control-service.ts +157 -157
  130. package/src/_modules/ai/_services/ai-embedding.service-base.spec.ts +98 -98
  131. package/src/_modules/ai/_services/ai-embedding.service-base.ts +48 -48
  132. package/src/_modules/ai/_services/ai-llm-chat.service-base.spec.ts +229 -229
  133. package/src/_modules/ai/_services/ai-llm-chat.service-base.ts +68 -68
  134. package/src/_modules/ai/_services/ai-llm.service-base.spec.ts +250 -250
  135. package/src/_modules/ai/_services/ai-llm.service-base.ts +519 -519
  136. package/src/_modules/ai/_services/ai-provider.service-base.spec.ts +158 -158
  137. package/src/_modules/ai/_services/ai-user-key.service-base.ts +59 -59
  138. package/src/_modules/ai/index.ts +13 -13
  139. package/src/_modules/assistant/_collections/ass-global-settings.const.ts +13 -13
  140. package/src/_modules/assistant/_collections/ass.util.spec.ts +176 -176
  141. package/src/_modules/assistant/_collections/ass.util.ts +50 -50
  142. package/src/_modules/assistant/_models/ass-global-settings.interface.ts +15 -15
  143. package/src/_modules/assistant/_services/ass-io.control-service.spec.ts +140 -140
  144. package/src/_modules/assistant/_services/ass-main.control-service.spec.ts +192 -192
  145. package/src/_modules/assistant/_services/ass-main.control-service.ts +107 -107
  146. package/src/_modules/bot/_collections/bot-default-commands.const.ts +12 -12
  147. package/src/_modules/bot/_collections/bot-global-settings.const.ts +39 -39
  148. package/src/_modules/bot/_models/bot-channel-wrapper.interface.ts +62 -62
  149. package/src/_modules/bot/_models/bot-command.interface.ts +8 -8
  150. package/src/_modules/bot/_models/bot-global-settings.interface.ts +96 -96
  151. package/src/_modules/bot/_models/bot-last-mention-date.interface.ts +6 -6
  152. package/src/_modules/bot/_models/bot-last-message-date.interface.ts +5 -5
  153. package/src/_modules/bot/_models/bot-user-wrapper.interface.ts +41 -41
  154. package/src/_modules/bot/_modules/discord-bot/_models/dib-platform.types.ts +9 -9
  155. package/src/_modules/bot/_modules/discord-bot/_services/dib-messaging-provider.control-service.spec.ts +431 -431
  156. package/src/_modules/bot/_modules/dynamo-bot/_collections/dyb-operations.util.spec.ts +160 -160
  157. package/src/_modules/bot/_modules/dynamo-bot/_collections/dyb-operations.util.ts +55 -55
  158. package/src/_modules/bot/_modules/dynamo-bot/_models/dyb-platform.types.ts +15 -15
  159. package/src/_modules/bot/_modules/dynamo-bot/_services/dyb-messaging-provider.control-service.spec.ts +374 -374
  160. package/src/_modules/bot/_modules/dynamo-bot/_services/dyb-messaging-provider.control-service.ts +447 -447
  161. package/src/_modules/bot/_modules/dynamo-bot/index.ts +15 -15
  162. package/src/_modules/bot/_modules/slack-bot/_models/slb-platform.types.ts +9 -9
  163. package/src/_modules/bot/_modules/slack-bot/_services/slb-messaging-provider.control-service.spec.ts +344 -344
  164. package/src/_modules/bot/_modules/slack-bot/_services/slb-messaging-provider.control-service.ts +197 -197
  165. package/src/_modules/bot/_modules/teams-bot/_models/teb-platform.types.ts +9 -9
  166. package/src/_modules/bot/_modules/teams-bot/_services/teb-messaging-provider.control-service.spec.ts +345 -345
  167. package/src/_modules/bot/_modules/teams-bot/_services/teb-messaging-provider.control-service.ts +197 -197
  168. package/src/_modules/bot/_services/bot-commands.control-service.spec.ts +116 -116
  169. package/src/_modules/bot/_services/bot-io.control-service.spec.ts +285 -285
  170. package/src/_modules/bot/_services/bot-main.control-service.spec.ts +208 -208
  171. package/src/_modules/bot/_services/bot-messaging-provider.service-base.spec.ts +349 -349
  172. package/src/_modules/bot/_services/bot-routines.control-service.spec.ts +111 -111
  173. package/src/_modules/custom-data/custom-data.controller.spec.ts +49 -49
  174. package/src/_modules/custom-data/custom-data.controller.ts +67 -67
  175. package/src/_modules/custom-data/custom-data.data-service.spec.ts +54 -54
  176. package/src/_modules/custom-data/custom-data.data-service.ts +21 -21
  177. package/src/_modules/custom-data/get-custom-data-routing-module.util.spec.ts +28 -28
  178. package/src/_modules/custom-data/get-custom-data-routing-module.util.ts +24 -24
  179. package/src/_modules/custom-data/index.ts +9 -9
  180. package/src/_modules/defaults/_collections/default-endpoints.util.ts +487 -487
  181. package/src/_modules/defaults/_models/default-user.data-model.ts +72 -72
  182. package/src/_modules/defaults/_services/default-auth.service.spec.ts +269 -269
  183. package/src/_modules/defaults/_services/default-auth.service.ts +177 -177
  184. package/src/_modules/defaults/_services/default-socket-events.service.spec.ts +42 -42
  185. package/src/_modules/defaults/_services/default-socket-events.service.ts +61 -61
  186. package/src/_modules/defaults/_services/default-user.data-service.spec.ts +187 -187
  187. package/src/_modules/defaults/_services/default-user.data-service.ts +98 -98
  188. package/src/_modules/defaults/index.ts +17 -17
  189. package/src/_modules/discord-assistant/_collections/dias-global-settings.const.ts +19 -19
  190. package/src/_modules/discord-assistant/_collections/dias.util.spec.ts +366 -366
  191. package/src/_modules/discord-assistant/_collections/dias.util.ts +132 -132
  192. package/src/_modules/discord-assistant/_models/dias-global-settings.interface.ts +19 -19
  193. package/src/_modules/discord-assistant/_models/dias-knowledge.data-model.ts +52 -52
  194. package/src/_modules/discord-assistant/_services/dias-chunk.data-service.ts +177 -177
  195. package/src/_modules/discord-assistant/_services/dias-io.control-service.spec.ts +108 -108
  196. package/src/_modules/discord-assistant/_services/dias-io.control-service.ts +69 -69
  197. package/src/_modules/discord-assistant/_services/dias-main.control-service.spec.ts +22 -22
  198. package/src/_modules/discord-assistant/_services/dias-main.control-service.ts +27 -27
  199. package/src/_modules/discord-assistant/_services/dias.service-base.spec.ts +195 -195
  200. package/src/_modules/discord-assistant/_services/dias.service-base.ts +76 -76
  201. package/src/_modules/discord-assistant/index.ts +38 -38
  202. package/src/_modules/discord-assistant-voiced/_services/dias-discord-bot.control-service.spec.ts +34 -34
  203. package/src/_modules/discord-assistant-voiced/_services/dias-discord-bot.control-service.ts +11 -11
  204. package/src/_modules/discord-assistant-voiced/index.ts +36 -36
  205. package/src/_modules/discord-bot/_collections/dibo-default-commands.const.ts +16 -16
  206. package/src/_modules/discord-bot/_collections/dibo-global-settings.conts.ts +55 -55
  207. package/src/_modules/discord-bot/_collections/dibo-operations.util.spec.ts +214 -214
  208. package/src/_modules/discord-bot/_collections/dibo-operations.util.ts +387 -387
  209. package/src/_modules/discord-bot/_models/dibo-command.interface.ts +12 -12
  210. package/src/_modules/discord-bot/_models/dibo-global-settings.interface.ts +98 -98
  211. package/src/_modules/discord-bot/_models/dibo-last-mention-date.inteface.ts +7 -7
  212. package/src/_modules/discord-bot/_models/dibo-last-message-date.interface.ts +6 -6
  213. package/src/_modules/discord-bot/_services/dibo-commands.control-service.spec.ts +154 -154
  214. package/src/_modules/discord-bot/_services/dibo-commands.control-service.ts +153 -153
  215. package/src/_modules/discord-bot/_services/dibo-io.control-service.spec.ts +264 -264
  216. package/src/_modules/discord-bot/_services/dibo-io.control-service.ts +306 -306
  217. package/src/_modules/discord-bot/_services/dibo-main.control-service.spec.ts +408 -408
  218. package/src/_modules/discord-bot/_services/dibo-main.control-service.ts +487 -487
  219. package/src/_modules/discord-bot/_services/dibo-routines.control-service.spec.ts +105 -105
  220. package/src/_modules/discord-bot/index.ts +36 -36
  221. package/src/_modules/local-vector-search/_enums/lvs-search-mode.enum.ts +35 -35
  222. package/src/_modules/local-vector-search/_models/lvs-search-result.interface.ts +17 -17
  223. package/src/_modules/local-vector-search/_services/lvs-doc-chunk-data.service.spec.ts +418 -418
  224. package/src/_modules/local-vector-search/_services/lvs-doc-chunk-data.service.ts +276 -276
  225. package/src/_modules/local-vector-search/_services/lvs-local-vector-search.data-service.spec.ts +480 -480
  226. package/src/_modules/local-vector-search/_services/lvs-local-vector-search.data-service.ts +416 -416
  227. package/src/_modules/local-vector-search/_services/lvs-vector-pool.control-service.spec.ts +393 -393
  228. package/src/_modules/local-vector-search/_services/lvs-vector-pool.control-service.ts +220 -220
  229. package/src/_modules/local-vector-search/index.ts +11 -11
  230. package/src/_modules/logs/index.ts +11 -11
  231. package/src/_modules/messaging/README.md +354 -354
  232. package/src/_modules/messaging/_collections/get-messaging-routing-module.util.ts +26 -26
  233. package/src/_modules/messaging/_collections/msg-global-settings.const.ts +22 -22
  234. package/src/_modules/messaging/_collections/msg.util.spec.ts +226 -226
  235. package/src/_modules/messaging/_models/msg-global-settings.interface.ts +37 -37
  236. package/src/_modules/messaging/_services/msg-conversation.data-service.ts +146 -146
  237. package/src/_modules/messaging/_services/msg-events.service.spec.ts +219 -219
  238. package/src/_modules/messaging/_services/msg-events.service.ts +267 -267
  239. package/src/_modules/messaging/_services/msg-integration.control-service.ts +179 -179
  240. package/src/_modules/messaging/_services/msg-main.control-service.spec.ts +147 -147
  241. package/src/_modules/messaging/_services/msg-main.control-service.ts +571 -571
  242. package/src/_modules/messaging/_services/msg-message.data-service.ts +129 -129
  243. package/src/_modules/messaging/_services/msg.controller.spec.ts +201 -201
  244. package/src/_modules/messaging/index.ts +30 -30
  245. package/src/_modules/mock/app-extended-server.mock.ts +201 -201
  246. package/src/_modules/mock/app-integration-test.mock.ts +51 -51
  247. package/src/_modules/mock/app-params.mock.spec.ts +21 -21
  248. package/src/_modules/mock/app-params.mock.ts +9 -9
  249. package/src/_modules/mock/app-server.mock.ts +188 -188
  250. package/src/_modules/mock/auth-service.mock.spec.ts +47 -47
  251. package/src/_modules/mock/auth-service.mock.ts +28 -28
  252. package/src/_modules/mock/controller.mock.spec.ts +26 -26
  253. package/src/_modules/mock/controller.mock.ts +16 -16
  254. package/src/_modules/mock/data-model.mock.spec.ts +111 -111
  255. package/src/_modules/mock/data-model.mock.ts +82 -82
  256. package/src/_modules/mock/email-service-collection.mock.spec.ts +24 -24
  257. package/src/_modules/mock/email-service-collection.mock.ts +15 -15
  258. package/src/_modules/mock/email-service.mock.spec.ts +17 -17
  259. package/src/_modules/mock/email-service.mock.ts +20 -20
  260. package/src/_modules/mock/email-template.mock.html +14 -14
  261. package/src/_modules/mock/endpoint.mock.ts +91 -91
  262. package/src/_modules/mock/socket-client.mock.spec.ts +40 -40
  263. package/src/_modules/mock/socket-client.mock.ts +45 -45
  264. package/src/_modules/mock/socket-server.mock.spec.ts +44 -44
  265. package/src/_modules/mock/socket-server.mock.ts +46 -46
  266. package/src/_modules/oauth2/_routes/oauth2.controller.spec.ts +107 -107
  267. package/src/_modules/oauth2/_routes/oauth2.controller.ts +98 -98
  268. package/src/_modules/oauth2/_services/oauth2.auth-service.spec.ts +254 -254
  269. package/src/_modules/oauth2/_services/oauth2.auth-service.ts +232 -232
  270. package/src/_modules/oauth2/_services/oauth2.control-service.spec.ts +585 -585
  271. package/src/_modules/oauth2/_services/oauth2.control-service.ts +653 -653
  272. package/src/_modules/oauth2/index.ts +17 -17
  273. package/src/_modules/server/errors/errors.control-service.spec.ts +238 -238
  274. package/src/_modules/server/errors/errors.control-service.ts +85 -85
  275. package/src/_modules/server/errors/errors.controller.spec.ts +241 -241
  276. package/src/_modules/server/errors/errors.controller.ts +431 -431
  277. package/src/_modules/server/errors/errors.data-service.spec.ts +361 -361
  278. package/src/_modules/server/index.ts +30 -30
  279. package/src/_modules/server/server-status/server-status-snapshot.control-service.spec.ts +70 -70
  280. package/src/_modules/server/server-status/server-status-snapshot.control-service.ts +17 -17
  281. package/src/_modules/server/server-status/server-status-snapshot.data-service.spec.ts +77 -77
  282. package/src/_modules/server/server-status/server-status-snapshot.data-service.ts +37 -37
  283. package/src/_modules/server/server-status/server-status.control-service.spec.ts +576 -576
  284. package/src/_modules/server/server-status/server-status.control-service.ts +396 -396
  285. package/src/_modules/server/server-status/server-status.controller.spec.ts +240 -240
  286. package/src/_modules/server/server-status/server-status.controller.ts +253 -253
  287. package/src/_modules/socket/_enums/socket-security.enum.ts +11 -11
  288. package/src/_modules/socket/_models/socket-client-service-params.control-model.spec.ts +32 -32
  289. package/src/_modules/socket/_models/socket-client-service-params.control-model.ts +22 -22
  290. package/src/_modules/socket/_models/socket-presence.control-model.spec.ts +164 -164
  291. package/src/_modules/socket/_models/socket-presence.control-model.ts +210 -210
  292. package/src/_modules/socket/_models/socket-server-service-params.control-model.spec.ts +46 -46
  293. package/src/_modules/socket/_models/socket-server-service-params.control-model.ts +22 -22
  294. package/src/_modules/socket/_services/socket-client.service.spec.ts +15 -15
  295. package/src/_modules/socket/_services/socket-client.service.ts +260 -260
  296. package/src/_modules/socket/_services/socket-server.service.spec.ts +11 -11
  297. package/src/_modules/socket/app-extended.integration.spec.ts +85 -85
  298. package/src/_modules/socket/app-extended.server.ts +630 -630
  299. package/src/_modules/socket/index.ts +42 -42
  300. package/src/_modules/test/get-test-routing-module.util.spec.ts +28 -28
  301. package/src/_modules/test/get-test-routing-module.util.ts +23 -23
  302. package/src/_modules/test/index.ts +11 -11
  303. package/src/_modules/test/test.controller.spec.ts +72 -72
  304. package/src/_modules/test/test.controller.ts +115 -115
  305. package/src/_modules/usage/get-usage-routing-module.util.ts +22 -22
  306. package/src/_modules/usage/index.ts +15 -15
  307. package/src/_modules/usage/usage.controller.spec.ts +81 -81
  308. package/src/_modules/usage/usage.controller.ts +126 -126
  309. package/src/_modules/usage/usage.data-service.spec.ts +332 -332
  310. package/src/_modules/usage/usage.data-service.ts +185 -185
  311. package/src/_services/base/api.service-base.spec.ts +125 -125
  312. package/src/_services/base/api.service-base.ts +74 -74
  313. package/src/_services/base/archive-data.service.spec.ts +196 -196
  314. package/src/_services/base/archive-data.service.ts +216 -216
  315. package/src/_services/base/data.service.spec.ts +674 -674
  316. package/src/_services/base/data.service.ts +2719 -2719
  317. package/src/_services/base/db.service.spec.ts +73 -73
  318. package/src/_services/base/db.service.ts +1575 -1575
  319. package/src/_services/base/singleton.service-base.spec.ts +28 -28
  320. package/src/_services/base/singleton.service-base.ts +24 -24
  321. package/src/_services/base/singleton.service.spec.ts +114 -114
  322. package/src/_services/base/singleton.service.ts +38 -38
  323. package/src/_services/core/api.service.spec.ts +140 -140
  324. package/src/_services/core/auth.service.spec.ts +159 -159
  325. package/src/_services/core/auth.service.ts +174 -174
  326. package/src/_services/core/email.service.spec.ts +85 -85
  327. package/src/_services/core/email.service.ts +742 -742
  328. package/src/_services/core/global.service.spec.ts +275 -275
  329. package/src/_services/core/global.service.ts +461 -461
  330. package/src/_services/core/memory-guard.service.spec.ts +165 -0
  331. package/src/_services/core/memory-guard.service.ts +281 -0
  332. package/src/_services/core/service-collection.service.spec.ts +46 -46
  333. package/src/_services/core/service-collection.service.ts +6 -6
  334. package/src/_services/route/controller.service.spec.ts +53 -53
  335. package/src/_services/route/controller.service.ts +148 -148
  336. package/src/_services/route/routing-module.service.spec.ts +98 -98
  337. package/src/_services/route/routing-module.service.ts +330 -330
  338. package/src/_services/server/app.server.ts +1726 -1713
  339. package/src/_services/shared.static-service.spec.ts +99 -99
  340. package/src/_services/shared.static-service.ts +78 -78
  341. package/src/index.ts +96 -95
  342. package/tsconfig.app.json +12 -12
  343. package/tsconfig.json +42 -42
@@ -1,489 +1,489 @@
1
-
2
- import { DyNTS_OAI_LLM_ServiceBase } from './oai-llm.service-base';
3
- import { DyFM_OAI_Settings, DyFM_OAI_Model, DyFM_OAI_CallSettings } from '@futdevpro/fsm-dynamo/ai/open-ai';
4
- import { DyFM_AI_MessageRole, DyFM_AI_Message } from '@futdevpro/fsm-dynamo/ai';
5
- import { DyFM_Error, DyFM_Log } from '@futdevpro/fsm-dynamo';
6
- import { ChatCompletion } from 'openai/resources';
7
-
8
- describe('| DyNTS_OAI_LLM_ServiceBase', () => {
9
- let service: DyNTS_OAI_LLM_ServiceBase;
10
- let mockOpenAI: any;
11
- let mockChatCompletionsCreate: jasmine.Spy;
12
- let mockModelsList: jasmine.Spy;
13
-
14
- const mockSettings: DyFM_OAI_Settings = {
15
- config: {
16
- apiKey: 'test-api-key',
17
- organization: 'test-org',
18
- },
19
- defaultSettings: {
20
- useModel: DyFM_OAI_Model.gpt4o_mini,
21
- systemPrompt: 'Test system prompt',
22
- temperature: 0.7,
23
- maxTokens: 1000,
24
- },
25
- };
26
-
27
- beforeEach(() => {
28
- mockChatCompletionsCreate = jasmine.createSpy('chat.completions.create').and.returnValue({
29
- id: 'chatcmpl-123',
30
- object: 'chat.completion',
31
- created: 1234567890,
32
- model: 'gpt-4o-mini',
33
- choices: [
34
- {
35
- index: 0,
36
- message: {
37
- role: 'assistant',
38
- content: 'Test response',
39
- },
40
- finish_reason: 'stop',
41
- },
42
- ],
43
- usage: {
44
- prompt_tokens: 10,
45
- completion_tokens: 5,
46
- total_tokens: 15,
47
- },
48
- });
49
-
50
- mockModelsList = jasmine.createSpy('models.list').and.returnValue({
51
- data: [],
52
- });
53
-
54
- mockOpenAI = {
55
- chat: {
56
- completions: {
57
- create: mockChatCompletionsCreate,
58
- },
59
- },
60
- models: {
61
- list: mockModelsList,
62
- },
63
- };
64
-
65
- service = new DyNTS_OAI_LLM_ServiceBase(mockSettings);
66
- (service as any).openai = mockOpenAI;
67
- });
68
-
69
- describe('| constructor', () => {
70
- it('| should initialize with provided settings', () => {
71
- expect(service.defaultSettings).toEqual(mockSettings.defaultSettings);
72
- expect(service.aiProvider).toBeDefined();
73
- expect(service.capabilities).toBeDefined();
74
- });
75
-
76
- it('| should initialize with default settings when not provided', () => {
77
- // Creating service without settings requires OPENAI_API_KEY env var or throws
78
- // Test that service creation with settings works and uses defaults for missing values
79
- const serviceWithPartialSettings = new DyNTS_OAI_LLM_ServiceBase({
80
- config: { apiKey: 'test-key' },
81
- });
82
- expect(serviceWithPartialSettings.defaultSettings).toBeDefined();
83
- });
84
-
85
- it('| should set defaultModel from defaultSettings', () => {
86
- expect(service.defaultModel).toBe(DyFM_OAI_Model.gpt4o_mini);
87
- });
88
- });
89
-
90
- describe('| setup', () => {
91
- it('| should reinitialize OpenAI client with new config', () => {
92
- const newConfig = {
93
- apiKey: 'new-api-key',
94
- organization: 'new-org',
95
- };
96
-
97
- service.setup(newConfig);
98
-
99
- expect((service as any).openai).toBeDefined();
100
- });
101
- });
102
-
103
- describe('| requestYesNo', () => {
104
- it('| should return true when answer contains YES', async () => {
105
- mockChatCompletionsCreate.and.returnValue({
106
- choices: [
107
- {
108
- message: {
109
- content: 'YES',
110
- },
111
- },
112
- ],
113
- });
114
-
115
- spyOn(service as any, 'convertAnswerToBoolean').and.returnValue(true);
116
-
117
- const result = await service.requestYesNo({
118
- message: 'Is this a test?',
119
- issuer: 'test-issuer',
120
- });
121
-
122
- expect(result).toBe(true);
123
- expect((service as any).convertAnswerToBoolean).toHaveBeenCalled();
124
- });
125
-
126
- it('| should append predefined request to message', async () => {
127
- spyOn(service, 'requestSimpleMessage').and.returnValue(Promise.resolve('YES'));
128
-
129
- await service.requestYesNo({
130
- message: 'Is this a test?',
131
- issuer: 'test-issuer',
132
- });
133
-
134
- expect(service.requestSimpleMessage).toHaveBeenCalled();
135
- const callArgs = (service.requestSimpleMessage as jasmine.Spy).calls.mostRecent().args[0];
136
- expect(callArgs.message).toContain('Is this a test?');
137
- });
138
- });
139
-
140
- describe('| requestPercentage', () => {
141
- it('| should return number from answer', async () => {
142
- spyOn(service, 'requestSimpleMessage').and.returnValue(Promise.resolve('75'));
143
- spyOn(service as any, 'convertAnswerToNumber').and.returnValue(75);
144
-
145
- const result = await service.requestPercentage({
146
- message: 'What percentage?',
147
- issuer: 'test-issuer',
148
- });
149
-
150
- expect(result).toBe(75);
151
- expect((service as any).convertAnswerToNumber).toHaveBeenCalled();
152
- });
153
- });
154
-
155
- describe('| requestSelect', () => {
156
- it('| should return selected option', async () => {
157
- const options = ['option1', 'option2', 'option3'];
158
- spyOn(service, 'requestSimpleMessage').and.returnValue(Promise.resolve('option1'));
159
- spyOn(service as any, 'stringifySelectOptions').and.returnValue(['option1', 'option2', 'option3']);
160
- spyOn(service as any, 'getTextListAsText').and.returnValue('option1, option2, option3');
161
- spyOn(service as any, 'convertAnswerToSelectOption').and.returnValue('option1');
162
-
163
- const result = await service.requestSelect({
164
- message: 'Select an option',
165
- selectFrom: options,
166
- issuer: 'test-issuer',
167
- });
168
-
169
- expect(result).toBe('option1');
170
- });
171
- });
172
-
173
- describe('| requestMultiselect', () => {
174
- it('| should return multiple selected options', async () => {
175
- const options = ['option1', 'option2', 'option3'];
176
- spyOn(service, 'requestSimpleMessage').and.returnValue(Promise.resolve('option1 and option2'));
177
- spyOn(service as any, 'stringifySelectOptions').and.returnValue(['option1', 'option2', 'option3']);
178
- spyOn(service as any, 'getTextListAsText').and.returnValue('option1, option2, option3');
179
- spyOn(service as any, 'convertAnswerToSelectOptions').and.returnValue(['option1', 'option2']);
180
-
181
- const result = await service.requestMultiselect({
182
- message: 'Select options',
183
- options: options,
184
- issuer: 'test-issuer',
185
- });
186
-
187
- expect(result).toEqual(['option1', 'option2']);
188
- });
189
- });
190
-
191
- describe('| requestJSON', () => {
192
- it('| should return parsed JSON', async () => {
193
- const jsonString = '{"key": "value"}';
194
- spyOn(service, 'requestSimpleMessage').and.returnValue(Promise.resolve(jsonString));
195
- spyOn(service as any, 'convertAnswerToJSON').and.returnValue({ key: 'value' });
196
-
197
- const result = await service.requestJSON<{ key: string }>({
198
- message: 'Return JSON',
199
- issuer: 'test-issuer',
200
- });
201
-
202
- expect(result).toEqual({ key: 'value' });
203
- });
204
- });
205
-
206
- describe('| requestList', () => {
207
- it('| should return list of items', async () => {
208
- spyOn(service, 'requestSimpleMessage').and.returnValue(Promise.resolve('item1, item2, item3'));
209
- spyOn(service as any, 'convertAnswerToList').and.returnValue(['item1', 'item2', 'item3']);
210
-
211
- const result = await service.requestList<string>({
212
- message: 'Return a list',
213
- issuer: 'test-issuer',
214
- });
215
-
216
- expect(result).toEqual(['item1', 'item2', 'item3']);
217
- });
218
- });
219
-
220
- describe('| requestSimpleMessage', () => {
221
- it('| should resolve message and return answer', async () => {
222
- spyOn(service as any, 'logQuestion');
223
- spyOn(service as any, 'logAnswer');
224
- spyOn(service, 'resolveMessage').and.returnValue(Promise.resolve('Test response'));
225
-
226
- const result = await service.requestSimpleMessage({
227
- message: 'Test question',
228
- issuer: 'test-issuer',
229
- });
230
-
231
- expect(result).toBe('Test response');
232
- expect((service as any).logQuestion).toHaveBeenCalled();
233
- expect((service as any).logAnswer).toHaveBeenCalled();
234
- });
235
-
236
- it('| should add user message to conversation', async () => {
237
- const conversation: DyFM_AI_Message[] = [];
238
- spyOn(service, 'resolveMessage').and.returnValue(Promise.resolve('Test response'));
239
-
240
- await service.requestSimpleMessage({
241
- message: 'Test question',
242
- conversation: conversation,
243
- issuer: 'test-issuer',
244
- });
245
-
246
- expect(conversation.length).toBe(1);
247
- expect(conversation[0].role).toBe(DyFM_AI_MessageRole.user);
248
- expect(conversation[0].content).toBe('Test question');
249
- });
250
- });
251
-
252
- describe('| resolveMessage', () => {
253
- it('| should return string answer when getFullResponse is false', async () => {
254
- const conversation: DyFM_AI_Message[] = [
255
- { role: DyFM_AI_MessageRole.user, content: 'Test question' },
256
- ];
257
-
258
- const result = await service.resolveMessage({
259
- conversation: conversation,
260
- issuer: 'test-issuer',
261
- getFullResponse: false,
262
- });
263
-
264
- expect(result).toBe('Test response');
265
- expect(mockChatCompletionsCreate).toHaveBeenCalled();
266
- });
267
-
268
- it('| should return full ChatCompletion when getFullResponse is true', async () => {
269
- const conversation: DyFM_AI_Message[] = [
270
- { role: DyFM_AI_MessageRole.user, content: 'Test question' },
271
- ];
272
-
273
- const fullResponse: ChatCompletion = {
274
- id: 'chatcmpl-123',
275
- object: 'chat.completion',
276
- created: 1234567890,
277
- model: 'gpt-4o-mini',
278
- choices: [
279
- {
280
- index: 0,
281
- message: {
282
- role: 'assistant',
283
- content: 'Test response',
284
- refusal: null,
285
- },
286
- logprobs: null,
287
- finish_reason: 'stop',
288
- },
289
- ],
290
- usage: {
291
- prompt_tokens: 10,
292
- completion_tokens: 5,
293
- total_tokens: 15,
294
- },
295
- };
296
-
297
- mockChatCompletionsCreate.and.returnValue(fullResponse);
298
-
299
- const result = await service.resolveMessage({
300
- conversation: conversation,
301
- issuer: 'test-issuer',
302
- getFullResponse: true,
303
- });
304
-
305
- expect(result).toEqual(fullResponse);
306
- });
307
-
308
- it('| should add system message to conversation', async () => {
309
- const conversation: DyFM_AI_Message[] = [
310
- { role: DyFM_AI_MessageRole.user, content: 'Test question' },
311
- ];
312
-
313
- spyOn(service as any, 'getDefaultSystemMessage').and.returnValue({
314
- role: DyFM_AI_MessageRole.system,
315
- content: 'System prompt',
316
- });
317
-
318
- await service.resolveMessage({
319
- conversation: conversation,
320
- issuer: 'test-issuer',
321
- });
322
-
323
- expect(conversation[0].role).toBe(DyFM_AI_MessageRole.system);
324
- });
325
-
326
- it('| should handle API errors', async () => {
327
- const conversation: DyFM_AI_Message[] = [
328
- { role: DyFM_AI_MessageRole.user, content: 'Test question' },
329
- ];
330
-
331
- mockChatCompletionsCreate.and.throwError(new Error('API Error'));
332
-
333
- try {
334
- await service.resolveMessage({
335
- conversation: conversation,
336
- issuer: 'test-issuer',
337
- });
338
- fail('Should have thrown an error');
339
- } catch (error) {
340
- expect(error).toBeInstanceOf(DyFM_Error);
341
- expect((error as DyFM_Error)._errorCode).toContain('DyNTS-OLSB-RM0');
342
- }
343
- });
344
- });
345
-
346
- describe('| FR-002 cost-event emit', () => {
347
-
348
- it('| should emit cost-event with llm-completion callType for single user message', async () => {
349
- const received: any[] = [];
350
- (service as any).onCostEvent = (e: any) => { received.push(e); };
351
-
352
- await service.resolveMessage({
353
- conversation: [{ role: DyFM_AI_MessageRole.user, content: 'Q?' }],
354
- issuer: 'llm-issuer-1',
355
- });
356
-
357
- expect(received.length).toBe(1);
358
- expect(received[0].callType).toBe('llm-completion');
359
- expect(received[0].provider).toBe('open-ai');
360
- expect(received[0].model).toBe(DyFM_OAI_Model.gpt4o_mini);
361
- expect(received[0].tokensUsed.input).toBe(10);
362
- expect(received[0].tokensUsed.output).toBe(5);
363
- expect(received[0].tokensUsed.total).toBe(15);
364
- expect(received[0].issuer).toBe('llm-issuer-1');
365
- expect(received[0].timestamp).toBeInstanceOf(Date);
366
- expect(received[0].durationMs).toBeGreaterThanOrEqual(0);
367
- expect(received[0].estimatedCostUsd).toBeUndefined();
368
- });
369
-
370
- it('| should emit cost-event with llm-chat callType for multi-message conversation', async () => {
371
- const received: any[] = [];
372
- (service as any).onCostEvent = (e: any) => { received.push(e); };
373
-
374
- await service.resolveMessage({
375
- conversation: [
376
- { role: DyFM_AI_MessageRole.user, content: 'Q1?' },
377
- { role: DyFM_AI_MessageRole.assistant, content: 'A1.' },
378
- { role: DyFM_AI_MessageRole.user, content: 'Q2?' },
379
- ],
380
- issuer: 'llm-issuer-chat',
381
- });
382
-
383
- expect(received.length).toBe(1);
384
- expect(received[0].callType).toBe('llm-chat');
385
- });
386
-
387
- it('| constructor accepts onCostEvent option (non-breaking additive)', () => {
388
- const cb: any = jasmine.createSpy('onCostEvent');
389
- const s: DyNTS_OAI_LLM_ServiceBase = new DyNTS_OAI_LLM_ServiceBase({
390
- ...mockSettings,
391
- onCostEvent: cb,
392
- });
393
-
394
- expect((s as any).onCostEvent).toBe(cb);
395
- });
396
- });
397
-
398
- describe('| getMessageCreateInput', () => {
399
- it('| should create input with default settings', () => {
400
- const conversation: DyFM_AI_Message[] = [
401
- { role: DyFM_AI_MessageRole.user, content: 'Test question' },
402
- ];
403
-
404
- const result = (service as any).getMessageCreateInput({
405
- conversation: conversation,
406
- issuer: 'test-issuer',
407
- });
408
-
409
- expect(result.model).toBe(DyFM_OAI_Model.gpt4o_mini);
410
- expect(result.messages).toBeDefined();
411
- expect(result.temperature).toBe(0.7);
412
- expect(result.max_completion_tokens).toBe(1000);
413
- });
414
-
415
- it('| should use provided settings when available', () => {
416
- const conversation: DyFM_AI_Message[] = [
417
- { role: DyFM_AI_MessageRole.user, content: 'Test question' },
418
- ];
419
-
420
- const customSettings: DyFM_OAI_CallSettings = {
421
- useModel: DyFM_OAI_Model.gpt4o,
422
- temperature: 0.9,
423
- maxTokens: 2000,
424
- };
425
-
426
- const result = (service as any).getMessageCreateInput({
427
- conversation: conversation,
428
- issuer: 'test-issuer',
429
- settings: customSettings,
430
- });
431
-
432
- expect(result.model).toBe(DyFM_OAI_Model.gpt4o);
433
- expect(result.temperature).toBe(0.9);
434
- expect(result.max_completion_tokens).toBe(2000);
435
- });
436
-
437
- it('| should handle errors', () => {
438
- // Test that the method handles edge cases gracefully
439
- // Passing null conversation may or may not throw depending on implementation
440
- try {
441
- const result = (service as any).getMessageCreateInput({
442
- conversation: null as any,
443
- issuer: 'test-issuer',
444
- });
445
- // If it doesn't throw, verify it returns something
446
- expect(result).toBeDefined();
447
- } catch (error) {
448
- // If it throws, it should be a DyFM_Error
449
- expect(error).toBeInstanceOf(DyFM_Error);
450
- }
451
- });
452
- });
453
-
454
- describe('| testConnection', () => {
455
- it('| should return true when connection is successful', async () => {
456
- const result = await service.testConnection('test-issuer');
457
-
458
- expect(result).toBe(true);
459
- expect(mockModelsList).toHaveBeenCalled();
460
- });
461
-
462
- it('| should return false when connection fails', async () => {
463
- const logSpy = spyOn(DyFM_Log, 'error');
464
- mockModelsList.and.throwError(new Error('Connection failed'));
465
-
466
- const result = await service.testConnection('test-issuer');
467
-
468
- expect(result).toBe(false);
469
- expect(logSpy).toHaveBeenCalled();
470
- });
471
- });
472
-
473
- describe('| properties', () => {
474
- it('| should have aiProvider property', () => {
475
- expect(service.aiProvider).toBeDefined();
476
- });
477
-
478
- it('| should have capabilities property', () => {
479
- expect(service.capabilities).toBeDefined();
480
- expect(service.capabilities.chat).toBe(true);
481
- expect(service.capabilities.embeddings).toBe(true);
482
- });
483
-
484
- it('| should have predefinedRequests property', () => {
485
- expect(service.predefinedRequests).toBeDefined();
486
- });
487
- });
488
- });
489
-
1
+
2
+ import { DyNTS_OAI_LLM_ServiceBase } from './oai-llm.service-base';
3
+ import { DyFM_OAI_Settings, DyFM_OAI_Model, DyFM_OAI_CallSettings } from '@futdevpro/fsm-dynamo/ai/open-ai';
4
+ import { DyFM_AI_MessageRole, DyFM_AI_Message } from '@futdevpro/fsm-dynamo/ai';
5
+ import { DyFM_Error, DyFM_Log } from '@futdevpro/fsm-dynamo';
6
+ import { ChatCompletion } from 'openai/resources';
7
+
8
+ describe('| DyNTS_OAI_LLM_ServiceBase', () => {
9
+ let service: DyNTS_OAI_LLM_ServiceBase;
10
+ let mockOpenAI: any;
11
+ let mockChatCompletionsCreate: jasmine.Spy;
12
+ let mockModelsList: jasmine.Spy;
13
+
14
+ const mockSettings: DyFM_OAI_Settings = {
15
+ config: {
16
+ apiKey: 'test-api-key',
17
+ organization: 'test-org',
18
+ },
19
+ defaultSettings: {
20
+ useModel: DyFM_OAI_Model.gpt4o_mini,
21
+ systemPrompt: 'Test system prompt',
22
+ temperature: 0.7,
23
+ maxTokens: 1000,
24
+ },
25
+ };
26
+
27
+ beforeEach(() => {
28
+ mockChatCompletionsCreate = jasmine.createSpy('chat.completions.create').and.returnValue({
29
+ id: 'chatcmpl-123',
30
+ object: 'chat.completion',
31
+ created: 1234567890,
32
+ model: 'gpt-4o-mini',
33
+ choices: [
34
+ {
35
+ index: 0,
36
+ message: {
37
+ role: 'assistant',
38
+ content: 'Test response',
39
+ },
40
+ finish_reason: 'stop',
41
+ },
42
+ ],
43
+ usage: {
44
+ prompt_tokens: 10,
45
+ completion_tokens: 5,
46
+ total_tokens: 15,
47
+ },
48
+ });
49
+
50
+ mockModelsList = jasmine.createSpy('models.list').and.returnValue({
51
+ data: [],
52
+ });
53
+
54
+ mockOpenAI = {
55
+ chat: {
56
+ completions: {
57
+ create: mockChatCompletionsCreate,
58
+ },
59
+ },
60
+ models: {
61
+ list: mockModelsList,
62
+ },
63
+ };
64
+
65
+ service = new DyNTS_OAI_LLM_ServiceBase(mockSettings);
66
+ (service as any).openai = mockOpenAI;
67
+ });
68
+
69
+ describe('| constructor', () => {
70
+ it('| should initialize with provided settings', () => {
71
+ expect(service.defaultSettings).toEqual(mockSettings.defaultSettings);
72
+ expect(service.aiProvider).toBeDefined();
73
+ expect(service.capabilities).toBeDefined();
74
+ });
75
+
76
+ it('| should initialize with default settings when not provided', () => {
77
+ // Creating service without settings requires OPENAI_API_KEY env var or throws
78
+ // Test that service creation with settings works and uses defaults for missing values
79
+ const serviceWithPartialSettings = new DyNTS_OAI_LLM_ServiceBase({
80
+ config: { apiKey: 'test-key' },
81
+ });
82
+ expect(serviceWithPartialSettings.defaultSettings).toBeDefined();
83
+ });
84
+
85
+ it('| should set defaultModel from defaultSettings', () => {
86
+ expect(service.defaultModel).toBe(DyFM_OAI_Model.gpt4o_mini);
87
+ });
88
+ });
89
+
90
+ describe('| setup', () => {
91
+ it('| should reinitialize OpenAI client with new config', () => {
92
+ const newConfig = {
93
+ apiKey: 'new-api-key',
94
+ organization: 'new-org',
95
+ };
96
+
97
+ service.setup(newConfig);
98
+
99
+ expect((service as any).openai).toBeDefined();
100
+ });
101
+ });
102
+
103
+ describe('| requestYesNo', () => {
104
+ it('| should return true when answer contains YES', async () => {
105
+ mockChatCompletionsCreate.and.returnValue({
106
+ choices: [
107
+ {
108
+ message: {
109
+ content: 'YES',
110
+ },
111
+ },
112
+ ],
113
+ });
114
+
115
+ spyOn(service as any, 'convertAnswerToBoolean').and.returnValue(true);
116
+
117
+ const result = await service.requestYesNo({
118
+ message: 'Is this a test?',
119
+ issuer: 'test-issuer',
120
+ });
121
+
122
+ expect(result).toBe(true);
123
+ expect((service as any).convertAnswerToBoolean).toHaveBeenCalled();
124
+ });
125
+
126
+ it('| should append predefined request to message', async () => {
127
+ spyOn(service, 'requestSimpleMessage').and.returnValue(Promise.resolve('YES'));
128
+
129
+ await service.requestYesNo({
130
+ message: 'Is this a test?',
131
+ issuer: 'test-issuer',
132
+ });
133
+
134
+ expect(service.requestSimpleMessage).toHaveBeenCalled();
135
+ const callArgs = (service.requestSimpleMessage as jasmine.Spy).calls.mostRecent().args[0];
136
+ expect(callArgs.message).toContain('Is this a test?');
137
+ });
138
+ });
139
+
140
+ describe('| requestPercentage', () => {
141
+ it('| should return number from answer', async () => {
142
+ spyOn(service, 'requestSimpleMessage').and.returnValue(Promise.resolve('75'));
143
+ spyOn(service as any, 'convertAnswerToNumber').and.returnValue(75);
144
+
145
+ const result = await service.requestPercentage({
146
+ message: 'What percentage?',
147
+ issuer: 'test-issuer',
148
+ });
149
+
150
+ expect(result).toBe(75);
151
+ expect((service as any).convertAnswerToNumber).toHaveBeenCalled();
152
+ });
153
+ });
154
+
155
+ describe('| requestSelect', () => {
156
+ it('| should return selected option', async () => {
157
+ const options = ['option1', 'option2', 'option3'];
158
+ spyOn(service, 'requestSimpleMessage').and.returnValue(Promise.resolve('option1'));
159
+ spyOn(service as any, 'stringifySelectOptions').and.returnValue(['option1', 'option2', 'option3']);
160
+ spyOn(service as any, 'getTextListAsText').and.returnValue('option1, option2, option3');
161
+ spyOn(service as any, 'convertAnswerToSelectOption').and.returnValue('option1');
162
+
163
+ const result = await service.requestSelect({
164
+ message: 'Select an option',
165
+ selectFrom: options,
166
+ issuer: 'test-issuer',
167
+ });
168
+
169
+ expect(result).toBe('option1');
170
+ });
171
+ });
172
+
173
+ describe('| requestMultiselect', () => {
174
+ it('| should return multiple selected options', async () => {
175
+ const options = ['option1', 'option2', 'option3'];
176
+ spyOn(service, 'requestSimpleMessage').and.returnValue(Promise.resolve('option1 and option2'));
177
+ spyOn(service as any, 'stringifySelectOptions').and.returnValue(['option1', 'option2', 'option3']);
178
+ spyOn(service as any, 'getTextListAsText').and.returnValue('option1, option2, option3');
179
+ spyOn(service as any, 'convertAnswerToSelectOptions').and.returnValue(['option1', 'option2']);
180
+
181
+ const result = await service.requestMultiselect({
182
+ message: 'Select options',
183
+ options: options,
184
+ issuer: 'test-issuer',
185
+ });
186
+
187
+ expect(result).toEqual(['option1', 'option2']);
188
+ });
189
+ });
190
+
191
+ describe('| requestJSON', () => {
192
+ it('| should return parsed JSON', async () => {
193
+ const jsonString = '{"key": "value"}';
194
+ spyOn(service, 'requestSimpleMessage').and.returnValue(Promise.resolve(jsonString));
195
+ spyOn(service as any, 'convertAnswerToJSON').and.returnValue({ key: 'value' });
196
+
197
+ const result = await service.requestJSON<{ key: string }>({
198
+ message: 'Return JSON',
199
+ issuer: 'test-issuer',
200
+ });
201
+
202
+ expect(result).toEqual({ key: 'value' });
203
+ });
204
+ });
205
+
206
+ describe('| requestList', () => {
207
+ it('| should return list of items', async () => {
208
+ spyOn(service, 'requestSimpleMessage').and.returnValue(Promise.resolve('item1, item2, item3'));
209
+ spyOn(service as any, 'convertAnswerToList').and.returnValue(['item1', 'item2', 'item3']);
210
+
211
+ const result = await service.requestList<string>({
212
+ message: 'Return a list',
213
+ issuer: 'test-issuer',
214
+ });
215
+
216
+ expect(result).toEqual(['item1', 'item2', 'item3']);
217
+ });
218
+ });
219
+
220
+ describe('| requestSimpleMessage', () => {
221
+ it('| should resolve message and return answer', async () => {
222
+ spyOn(service as any, 'logQuestion');
223
+ spyOn(service as any, 'logAnswer');
224
+ spyOn(service, 'resolveMessage').and.returnValue(Promise.resolve('Test response'));
225
+
226
+ const result = await service.requestSimpleMessage({
227
+ message: 'Test question',
228
+ issuer: 'test-issuer',
229
+ });
230
+
231
+ expect(result).toBe('Test response');
232
+ expect((service as any).logQuestion).toHaveBeenCalled();
233
+ expect((service as any).logAnswer).toHaveBeenCalled();
234
+ });
235
+
236
+ it('| should add user message to conversation', async () => {
237
+ const conversation: DyFM_AI_Message[] = [];
238
+ spyOn(service, 'resolveMessage').and.returnValue(Promise.resolve('Test response'));
239
+
240
+ await service.requestSimpleMessage({
241
+ message: 'Test question',
242
+ conversation: conversation,
243
+ issuer: 'test-issuer',
244
+ });
245
+
246
+ expect(conversation.length).toBe(1);
247
+ expect(conversation[0].role).toBe(DyFM_AI_MessageRole.user);
248
+ expect(conversation[0].content).toBe('Test question');
249
+ });
250
+ });
251
+
252
+ describe('| resolveMessage', () => {
253
+ it('| should return string answer when getFullResponse is false', async () => {
254
+ const conversation: DyFM_AI_Message[] = [
255
+ { role: DyFM_AI_MessageRole.user, content: 'Test question' },
256
+ ];
257
+
258
+ const result = await service.resolveMessage({
259
+ conversation: conversation,
260
+ issuer: 'test-issuer',
261
+ getFullResponse: false,
262
+ });
263
+
264
+ expect(result).toBe('Test response');
265
+ expect(mockChatCompletionsCreate).toHaveBeenCalled();
266
+ });
267
+
268
+ it('| should return full ChatCompletion when getFullResponse is true', async () => {
269
+ const conversation: DyFM_AI_Message[] = [
270
+ { role: DyFM_AI_MessageRole.user, content: 'Test question' },
271
+ ];
272
+
273
+ const fullResponse: ChatCompletion = {
274
+ id: 'chatcmpl-123',
275
+ object: 'chat.completion',
276
+ created: 1234567890,
277
+ model: 'gpt-4o-mini',
278
+ choices: [
279
+ {
280
+ index: 0,
281
+ message: {
282
+ role: 'assistant',
283
+ content: 'Test response',
284
+ refusal: null,
285
+ },
286
+ logprobs: null,
287
+ finish_reason: 'stop',
288
+ },
289
+ ],
290
+ usage: {
291
+ prompt_tokens: 10,
292
+ completion_tokens: 5,
293
+ total_tokens: 15,
294
+ },
295
+ };
296
+
297
+ mockChatCompletionsCreate.and.returnValue(fullResponse);
298
+
299
+ const result = await service.resolveMessage({
300
+ conversation: conversation,
301
+ issuer: 'test-issuer',
302
+ getFullResponse: true,
303
+ });
304
+
305
+ expect(result).toEqual(fullResponse);
306
+ });
307
+
308
+ it('| should add system message to conversation', async () => {
309
+ const conversation: DyFM_AI_Message[] = [
310
+ { role: DyFM_AI_MessageRole.user, content: 'Test question' },
311
+ ];
312
+
313
+ spyOn(service as any, 'getDefaultSystemMessage').and.returnValue({
314
+ role: DyFM_AI_MessageRole.system,
315
+ content: 'System prompt',
316
+ });
317
+
318
+ await service.resolveMessage({
319
+ conversation: conversation,
320
+ issuer: 'test-issuer',
321
+ });
322
+
323
+ expect(conversation[0].role).toBe(DyFM_AI_MessageRole.system);
324
+ });
325
+
326
+ it('| should handle API errors', async () => {
327
+ const conversation: DyFM_AI_Message[] = [
328
+ { role: DyFM_AI_MessageRole.user, content: 'Test question' },
329
+ ];
330
+
331
+ mockChatCompletionsCreate.and.throwError(new Error('API Error'));
332
+
333
+ try {
334
+ await service.resolveMessage({
335
+ conversation: conversation,
336
+ issuer: 'test-issuer',
337
+ });
338
+ fail('Should have thrown an error');
339
+ } catch (error) {
340
+ expect(error).toBeInstanceOf(DyFM_Error);
341
+ expect((error as DyFM_Error)._errorCode).toContain('DyNTS-OLSB-RM0');
342
+ }
343
+ });
344
+ });
345
+
346
+ describe('| FR-002 cost-event emit', () => {
347
+
348
+ it('| should emit cost-event with llm-completion callType for single user message', async () => {
349
+ const received: any[] = [];
350
+ (service as any).onCostEvent = (e: any) => { received.push(e); };
351
+
352
+ await service.resolveMessage({
353
+ conversation: [{ role: DyFM_AI_MessageRole.user, content: 'Q?' }],
354
+ issuer: 'llm-issuer-1',
355
+ });
356
+
357
+ expect(received.length).toBe(1);
358
+ expect(received[0].callType).toBe('llm-completion');
359
+ expect(received[0].provider).toBe('open-ai');
360
+ expect(received[0].model).toBe(DyFM_OAI_Model.gpt4o_mini);
361
+ expect(received[0].tokensUsed.input).toBe(10);
362
+ expect(received[0].tokensUsed.output).toBe(5);
363
+ expect(received[0].tokensUsed.total).toBe(15);
364
+ expect(received[0].issuer).toBe('llm-issuer-1');
365
+ expect(received[0].timestamp).toBeInstanceOf(Date);
366
+ expect(received[0].durationMs).toBeGreaterThanOrEqual(0);
367
+ expect(received[0].estimatedCostUsd).toBeUndefined();
368
+ });
369
+
370
+ it('| should emit cost-event with llm-chat callType for multi-message conversation', async () => {
371
+ const received: any[] = [];
372
+ (service as any).onCostEvent = (e: any) => { received.push(e); };
373
+
374
+ await service.resolveMessage({
375
+ conversation: [
376
+ { role: DyFM_AI_MessageRole.user, content: 'Q1?' },
377
+ { role: DyFM_AI_MessageRole.assistant, content: 'A1.' },
378
+ { role: DyFM_AI_MessageRole.user, content: 'Q2?' },
379
+ ],
380
+ issuer: 'llm-issuer-chat',
381
+ });
382
+
383
+ expect(received.length).toBe(1);
384
+ expect(received[0].callType).toBe('llm-chat');
385
+ });
386
+
387
+ it('| constructor accepts onCostEvent option (non-breaking additive)', () => {
388
+ const cb: any = jasmine.createSpy('onCostEvent');
389
+ const s: DyNTS_OAI_LLM_ServiceBase = new DyNTS_OAI_LLM_ServiceBase({
390
+ ...mockSettings,
391
+ onCostEvent: cb,
392
+ });
393
+
394
+ expect((s as any).onCostEvent).toBe(cb);
395
+ });
396
+ });
397
+
398
+ describe('| getMessageCreateInput', () => {
399
+ it('| should create input with default settings', () => {
400
+ const conversation: DyFM_AI_Message[] = [
401
+ { role: DyFM_AI_MessageRole.user, content: 'Test question' },
402
+ ];
403
+
404
+ const result = (service as any).getMessageCreateInput({
405
+ conversation: conversation,
406
+ issuer: 'test-issuer',
407
+ });
408
+
409
+ expect(result.model).toBe(DyFM_OAI_Model.gpt4o_mini);
410
+ expect(result.messages).toBeDefined();
411
+ expect(result.temperature).toBe(0.7);
412
+ expect(result.max_completion_tokens).toBe(1000);
413
+ });
414
+
415
+ it('| should use provided settings when available', () => {
416
+ const conversation: DyFM_AI_Message[] = [
417
+ { role: DyFM_AI_MessageRole.user, content: 'Test question' },
418
+ ];
419
+
420
+ const customSettings: DyFM_OAI_CallSettings = {
421
+ useModel: DyFM_OAI_Model.gpt4o,
422
+ temperature: 0.9,
423
+ maxTokens: 2000,
424
+ };
425
+
426
+ const result = (service as any).getMessageCreateInput({
427
+ conversation: conversation,
428
+ issuer: 'test-issuer',
429
+ settings: customSettings,
430
+ });
431
+
432
+ expect(result.model).toBe(DyFM_OAI_Model.gpt4o);
433
+ expect(result.temperature).toBe(0.9);
434
+ expect(result.max_completion_tokens).toBe(2000);
435
+ });
436
+
437
+ it('| should handle errors', () => {
438
+ // Test that the method handles edge cases gracefully
439
+ // Passing null conversation may or may not throw depending on implementation
440
+ try {
441
+ const result = (service as any).getMessageCreateInput({
442
+ conversation: null as any,
443
+ issuer: 'test-issuer',
444
+ });
445
+ // If it doesn't throw, verify it returns something
446
+ expect(result).toBeDefined();
447
+ } catch (error) {
448
+ // If it throws, it should be a DyFM_Error
449
+ expect(error).toBeInstanceOf(DyFM_Error);
450
+ }
451
+ });
452
+ });
453
+
454
+ describe('| testConnection', () => {
455
+ it('| should return true when connection is successful', async () => {
456
+ const result = await service.testConnection('test-issuer');
457
+
458
+ expect(result).toBe(true);
459
+ expect(mockModelsList).toHaveBeenCalled();
460
+ });
461
+
462
+ it('| should return false when connection fails', async () => {
463
+ const logSpy = spyOn(DyFM_Log, 'error');
464
+ mockModelsList.and.throwError(new Error('Connection failed'));
465
+
466
+ const result = await service.testConnection('test-issuer');
467
+
468
+ expect(result).toBe(false);
469
+ expect(logSpy).toHaveBeenCalled();
470
+ });
471
+ });
472
+
473
+ describe('| properties', () => {
474
+ it('| should have aiProvider property', () => {
475
+ expect(service.aiProvider).toBeDefined();
476
+ });
477
+
478
+ it('| should have capabilities property', () => {
479
+ expect(service.capabilities).toBeDefined();
480
+ expect(service.capabilities.chat).toBe(true);
481
+ expect(service.capabilities.embeddings).toBe(true);
482
+ });
483
+
484
+ it('| should have predefinedRequests property', () => {
485
+ expect(service.predefinedRequests).toBeDefined();
486
+ });
487
+ });
488
+ });
489
+