@futdevpro/nts-dynamo 1.15.144 → 1.15.145

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