@futdevpro/nts-dynamo 1.15.142 → 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 (400) 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 +46 -0
  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/_modules/artifact-compiler/_enums/artifact-compile-outcome.type-enum.d.ts +6 -0
  28. package/build/_modules/artifact-compiler/_enums/artifact-compile-outcome.type-enum.d.ts.map +1 -0
  29. package/build/_modules/artifact-compiler/_enums/artifact-compile-outcome.type-enum.js +10 -0
  30. package/build/_modules/artifact-compiler/_enums/artifact-compile-outcome.type-enum.js.map +1 -0
  31. package/build/_modules/artifact-compiler/_enums/artifact-compile-stage.type-enum.d.ts +9 -0
  32. package/build/_modules/artifact-compiler/_enums/artifact-compile-stage.type-enum.d.ts.map +1 -0
  33. package/build/_modules/artifact-compiler/_enums/artifact-compile-stage.type-enum.js +13 -0
  34. package/build/_modules/artifact-compiler/_enums/artifact-compile-stage.type-enum.js.map +1 -0
  35. package/build/_modules/artifact-compiler/_enums/artifact-retry-class.type-enum.d.ts +7 -0
  36. package/build/_modules/artifact-compiler/_enums/artifact-retry-class.type-enum.d.ts.map +1 -0
  37. package/build/_modules/artifact-compiler/_enums/artifact-retry-class.type-enum.js +11 -0
  38. package/build/_modules/artifact-compiler/_enums/artifact-retry-class.type-enum.js.map +1 -0
  39. package/build/_modules/artifact-compiler/_models/artifact-compile.interface.d.ts +33 -0
  40. package/build/_modules/artifact-compiler/_models/artifact-compile.interface.d.ts.map +1 -0
  41. package/build/_modules/artifact-compiler/_models/artifact-compile.interface.js +3 -0
  42. package/build/_modules/artifact-compiler/_models/artifact-compile.interface.js.map +1 -0
  43. package/build/_modules/artifact-compiler/artifact-compiler.control-service.d.ts +37 -0
  44. package/build/_modules/artifact-compiler/artifact-compiler.control-service.d.ts.map +1 -0
  45. package/build/_modules/artifact-compiler/artifact-compiler.control-service.js +274 -0
  46. package/build/_modules/artifact-compiler/artifact-compiler.control-service.js.map +1 -0
  47. package/build/_modules/artifact-compiler/index.d.ts +6 -0
  48. package/build/_modules/artifact-compiler/index.d.ts.map +1 -0
  49. package/build/_modules/artifact-compiler/index.js +12 -0
  50. package/build/_modules/artifact-compiler/index.js.map +1 -0
  51. package/build/_services/core/collection-growth-monitor.service.d.ts +1 -1
  52. package/build/_services/core/collection-growth-monitor.service.d.ts.map +1 -1
  53. package/build/_services/core/collection-growth-monitor.service.js +24 -5
  54. package/build/_services/core/collection-growth-monitor.service.js.map +1 -1
  55. package/eslint.config.js +3 -3
  56. package/nodemon.json +24 -24
  57. package/package.json +15 -2
  58. package/pnpm-workspace.yaml +6 -5
  59. package/scripts/run-coverage-tests.js +28 -28
  60. package/spec/support/helpers/spec-reporter-loader.js +359 -359
  61. package/spec/support/helpers/ts-node-helper.js +93 -93
  62. package/spec/support/jasmine.coverage.json +24 -24
  63. package/spec/support/jasmine.json +24 -24
  64. package/src/_collections/archive.util.spec.ts +57 -57
  65. package/src/_collections/archive.util.ts +18 -18
  66. package/src/_collections/atlas-default-db-options.const.ts +9 -9
  67. package/src/_collections/default-fallback-cache-max-age.const.spec.ts +11 -11
  68. package/src/_collections/default-fallback-cache-max-age.const.ts +2 -2
  69. package/src/_collections/default-not-found-page.const.spec.ts +19 -19
  70. package/src/_collections/default-not-found-page.const.ts +22 -22
  71. package/src/_collections/default-socket-path.const.spec.ts +12 -12
  72. package/src/_collections/default-socket-path.const.ts +2 -2
  73. package/src/_collections/get-environment-settings.util.spec.ts +210 -210
  74. package/src/_collections/get-environment-settings.util.ts +48 -48
  75. package/src/_collections/global-settings.const.ts +126 -126
  76. package/src/_collections/sample.env +21 -21
  77. package/src/_collections/signed-url.util.spec.ts +198 -198
  78. package/src/_collections/signed-url.util.ts +170 -170
  79. package/src/_collections/star.controller.spec.ts +224 -224
  80. package/src/_collections/star.controller.ts +129 -129
  81. package/src/_enums/data-model-type.enum.ts +14 -14
  82. package/src/_enums/data-service-function.enum.ts +24 -24
  83. package/src/_enums/predefined-data-types.enum.ts +16 -16
  84. package/src/_enums/route-security.enum.ts +12 -12
  85. package/src/_models/control-models/api-call-params.control-model.spec.ts +152 -152
  86. package/src/_models/control-models/api-call-params.control-model.ts +142 -142
  87. package/src/_models/control-models/app-ext-system-controls.control-model.spec.ts +52 -52
  88. package/src/_models/control-models/app-ext-system-controls.control-model.ts +9 -9
  89. package/src/_models/control-models/app-params.control-model.spec.ts +225 -225
  90. package/src/_models/control-models/app-params.control-model.ts +136 -136
  91. package/src/_models/control-models/app-system-controls.control-model.spec.ts +31 -31
  92. package/src/_models/control-models/app-system-controls.control-model.ts +9 -9
  93. package/src/_models/control-models/endpoint-params.control-model.spec.ts +669 -669
  94. package/src/_models/control-models/endpoint-params.control-model.ts +664 -664
  95. package/src/_models/control-models/http-settings.control-model.spec.ts +77 -77
  96. package/src/_models/control-models/http-settings.control-model.ts +37 -37
  97. package/src/_models/control-models/system-control.control-model.spec.ts +27 -27
  98. package/src/_models/control-models/system-control.control-model.ts +12 -12
  99. package/src/_models/interfaces/certification-settings.interface.ts +7 -7
  100. package/src/_models/interfaces/environment-settings.interface.ts +59 -59
  101. package/src/_models/interfaces/global-log-settings.interface.ts +171 -171
  102. package/src/_models/interfaces/global-service-settings.interface.ts +47 -47
  103. package/src/_models/interfaces/global-settings.interface.ts +294 -294
  104. package/src/_models/interfaces/routing-module-settings.interface.ts +21 -21
  105. package/src/_models/interfaces/static-client-settings.interface.spec.ts +29 -29
  106. package/src/_models/interfaces/static-client-settings.interface.ts +36 -36
  107. package/src/_models/types/db-update.type.ts +100 -100
  108. package/src/_modules/ai/_models/ai-input-interfaces.ts +117 -117
  109. package/src/_modules/ai/_models/ai-test-generation-result.interface.ts +16 -16
  110. package/src/_modules/ai/_modules/ai-mock/_services/ai-embedding-mock.service.spec.ts +115 -115
  111. package/src/_modules/ai/_modules/ai-mock/_services/ai-embedding-mock.service.ts +233 -233
  112. package/src/_modules/ai/_modules/anthropic/_services/aai-user-key.control-service.ts +138 -138
  113. package/src/_modules/ai/_modules/anthropic/index.ts +5 -5
  114. package/src/_modules/ai/_modules/document-ai/_collections/dai-chunking.util.spec.ts +242 -242
  115. package/src/_modules/ai/_modules/document-ai/_collections/dai-chunking.util.ts +639 -639
  116. package/src/_modules/ai/_modules/document-ai/_collections/dai-code-chunking.util.spec.ts +295 -295
  117. package/src/_modules/ai/_modules/document-ai/_collections/dai-code-chunking.util.ts +518 -518
  118. package/src/_modules/ai/_modules/document-ai/_collections/dai-document.util.spec.ts +209 -209
  119. package/src/_modules/ai/_modules/document-ai/_collections/dai-document.util.ts +85 -85
  120. package/src/_modules/ai/_modules/document-ai/_enums/dai-compare-result-type.enum.ts +7 -7
  121. package/src/_modules/ai/_modules/document-ai/_models/data-models/dai-doc-chunk.data-model.ts +146 -146
  122. package/src/_modules/ai/_modules/document-ai/_models/data-models/dai-doc-page.data-model.ts +162 -162
  123. package/src/_modules/ai/_modules/document-ai/_models/data-models/dai-document.data-model.ts +99 -99
  124. package/src/_modules/ai/_modules/document-ai/_models/interfaces/dai-code-chunk.interface.ts +68 -68
  125. package/src/_modules/ai/_modules/document-ai/_models/interfaces/dai-doc-chunk-compare-result.interface.ts +18 -18
  126. package/src/_modules/ai/_modules/document-ai/_models/interfaces/dai-doc-page-compare-result.interface.ts +19 -19
  127. package/src/_modules/ai/_modules/document-ai/_models/interfaces/dai-document-compare-result.interface.ts +25 -25
  128. package/src/_modules/ai/_modules/document-ai/index.ts +30 -30
  129. package/src/_modules/ai/_modules/fdp-ai/_services/fdpai-user-key.control-service.ts +189 -189
  130. package/src/_modules/ai/_modules/fdp-ai/index.ts +5 -5
  131. package/src/_modules/ai/_modules/open-ai/_collections/oai-global-settings.const.ts +9 -9
  132. package/src/_modules/ai/_modules/open-ai/_collections/oai-llm-predefined-requests-hu.conts.ts +82 -82
  133. package/src/_modules/ai/_modules/open-ai/_collections/oai-llm-predefined-requests.conts.ts +75 -75
  134. package/src/_modules/ai/_modules/open-ai/_enums/oai-gpt-message-role.enum.ts +45 -45
  135. package/src/_modules/ai/_modules/open-ai/_models/interfaces/oai-global-settings.interface.ts +7 -7
  136. package/src/_modules/ai/_modules/open-ai/_models/interfaces/oai-gpt-message.interface.ts +7 -7
  137. package/src/_modules/ai/_modules/open-ai/_models/interfaces/oai-llm-predefined-requests.interface.ts +57 -57
  138. package/src/_modules/ai/_modules/open-ai/_services/data-services/oai-doc-chunk-data.service.ts +292 -292
  139. package/src/_modules/ai/_modules/open-ai/_services/data-services/oai-document.data-service.spec.ts +342 -342
  140. package/src/_modules/ai/_modules/open-ai/_services/data-services/oai-vector-data.service.spec.ts +550 -550
  141. package/src/_modules/ai/_modules/open-ai/_services/data-services/oai-vector-data.service.ts +630 -630
  142. package/src/_modules/ai/_modules/open-ai/_services/oai-embedding.control-service.spec.ts +332 -332
  143. package/src/_modules/ai/_modules/open-ai/_services/oai-llm-chat.service-base.spec.ts +462 -462
  144. package/src/_modules/ai/_modules/open-ai/_services/oai-llm-chat.service-base.ts +634 -634
  145. package/src/_modules/ai/_modules/open-ai/_services/oai-llm.service-base.spec.ts +489 -489
  146. package/src/_modules/ai/_modules/open-ai/_services/oai-llm.service-base.tools.spec.ts +173 -173
  147. package/src/_modules/ai/_modules/open-ai/_services/oai-llm.service-base.ts +1085 -1085
  148. package/src/_modules/ai/_modules/open-ai/_services/oai-user-key.control-service.ts +157 -157
  149. package/src/_modules/ai/_services/ai-embedding-provider.registry.spec.ts +110 -110
  150. package/src/_modules/ai/_services/ai-embedding-provider.registry.ts +114 -114
  151. package/src/_modules/ai/_services/ai-embedding.service-base.spec.ts +98 -98
  152. package/src/_modules/ai/_services/ai-embedding.service-base.ts +48 -48
  153. package/src/_modules/ai/_services/ai-llm-chat.service-base.spec.ts +229 -229
  154. package/src/_modules/ai/_services/ai-llm-chat.service-base.ts +68 -68
  155. package/src/_modules/ai/_services/ai-llm.service-base.spec.ts +250 -250
  156. package/src/_modules/ai/_services/ai-llm.service-base.ts +599 -599
  157. package/src/_modules/ai/_services/ai-provider.service-base.spec.ts +158 -158
  158. package/src/_modules/ai/_services/ai-user-key.service-base.ts +59 -59
  159. package/src/_modules/ai/_services/lmstudio-embedding.control-service.spec.ts +197 -197
  160. package/src/_modules/ai/_services/lmstudio-embedding.control-service.ts +371 -371
  161. package/src/_modules/ai/index.ts +33 -33
  162. package/src/_modules/artifact-compiler/_enums/artifact-compile-outcome.type-enum.ts +5 -0
  163. package/src/_modules/artifact-compiler/_enums/artifact-compile-stage.type-enum.ts +8 -0
  164. package/src/_modules/artifact-compiler/_enums/artifact-retry-class.type-enum.ts +6 -0
  165. package/src/_modules/artifact-compiler/_models/artifact-compile.interface.ts +35 -0
  166. package/src/_modules/artifact-compiler/artifact-compiler.control-service.spec.ts +151 -0
  167. package/src/_modules/artifact-compiler/artifact-compiler.control-service.ts +356 -0
  168. package/src/_modules/artifact-compiler/index.ts +9 -0
  169. package/src/_modules/assistant/_collections/ass-global-settings.const.ts +13 -13
  170. package/src/_modules/assistant/_collections/ass.util.spec.ts +176 -176
  171. package/src/_modules/assistant/_collections/ass.util.ts +50 -50
  172. package/src/_modules/assistant/_models/ass-global-settings.interface.ts +15 -15
  173. package/src/_modules/assistant/_services/ass-io.control-service.spec.ts +140 -140
  174. package/src/_modules/assistant/_services/ass-main.control-service.spec.ts +192 -192
  175. package/src/_modules/assistant/_services/ass-main.control-service.ts +107 -107
  176. package/src/_modules/bot/_collections/bot-default-commands.const.ts +12 -12
  177. package/src/_modules/bot/_collections/bot-global-settings.const.ts +39 -39
  178. package/src/_modules/bot/_models/bot-channel-wrapper.interface.ts +62 -62
  179. package/src/_modules/bot/_models/bot-command.interface.ts +8 -8
  180. package/src/_modules/bot/_models/bot-global-settings.interface.ts +96 -96
  181. package/src/_modules/bot/_models/bot-last-mention-date.interface.ts +6 -6
  182. package/src/_modules/bot/_models/bot-last-message-date.interface.ts +5 -5
  183. package/src/_modules/bot/_models/bot-user-wrapper.interface.ts +41 -41
  184. package/src/_modules/bot/_modules/discord-bot/_models/dib-platform.types.ts +9 -9
  185. package/src/_modules/bot/_modules/discord-bot/_services/dib-messaging-provider.control-service.spec.ts +431 -431
  186. package/src/_modules/bot/_modules/dynamo-bot/_collections/dyb-operations.util.spec.ts +160 -160
  187. package/src/_modules/bot/_modules/dynamo-bot/_collections/dyb-operations.util.ts +55 -55
  188. package/src/_modules/bot/_modules/dynamo-bot/_models/dyb-platform.types.ts +15 -15
  189. package/src/_modules/bot/_modules/dynamo-bot/_services/dyb-messaging-provider.control-service.spec.ts +374 -374
  190. package/src/_modules/bot/_modules/dynamo-bot/_services/dyb-messaging-provider.control-service.ts +447 -447
  191. package/src/_modules/bot/_modules/dynamo-bot/index.ts +15 -15
  192. package/src/_modules/bot/_modules/slack-bot/_models/slb-platform.types.ts +9 -9
  193. package/src/_modules/bot/_modules/slack-bot/_services/slb-messaging-provider.control-service.spec.ts +344 -344
  194. package/src/_modules/bot/_modules/slack-bot/_services/slb-messaging-provider.control-service.ts +197 -197
  195. package/src/_modules/bot/_modules/teams-bot/_models/teb-platform.types.ts +9 -9
  196. package/src/_modules/bot/_modules/teams-bot/_services/teb-messaging-provider.control-service.spec.ts +345 -345
  197. package/src/_modules/bot/_modules/teams-bot/_services/teb-messaging-provider.control-service.ts +197 -197
  198. package/src/_modules/bot/_services/bot-commands.control-service.spec.ts +116 -116
  199. package/src/_modules/bot/_services/bot-io.control-service.spec.ts +285 -285
  200. package/src/_modules/bot/_services/bot-main.control-service.spec.ts +208 -208
  201. package/src/_modules/bot/_services/bot-messaging-provider.service-base.spec.ts +349 -349
  202. package/src/_modules/bot/_services/bot-routines.control-service.spec.ts +111 -111
  203. package/src/_modules/custom-data/custom-data.controller.spec.ts +49 -49
  204. package/src/_modules/custom-data/custom-data.controller.ts +67 -67
  205. package/src/_modules/custom-data/custom-data.data-service.spec.ts +54 -54
  206. package/src/_modules/custom-data/custom-data.data-service.ts +21 -21
  207. package/src/_modules/custom-data/get-custom-data-routing-module.util.spec.ts +28 -28
  208. package/src/_modules/custom-data/get-custom-data-routing-module.util.ts +24 -24
  209. package/src/_modules/custom-data/index.ts +9 -9
  210. package/src/_modules/data-readers/_collections/dynts-sqlite-reader.util.spec.ts +161 -161
  211. package/src/_modules/data-readers/_collections/dynts-sqlite-reader.util.ts +203 -203
  212. package/src/_modules/data-readers/_models/interfaces/dynts-sqlite-reader.interface.ts +33 -33
  213. package/src/_modules/data-readers/index.ts +11 -11
  214. package/src/_modules/defaults/_collections/default-endpoints.util.ts +487 -487
  215. package/src/_modules/defaults/_models/default-user.data-model.ts +72 -72
  216. package/src/_modules/defaults/_services/default-auth.service.spec.ts +269 -269
  217. package/src/_modules/defaults/_services/default-auth.service.ts +177 -177
  218. package/src/_modules/defaults/_services/default-socket-events.service.spec.ts +42 -42
  219. package/src/_modules/defaults/_services/default-socket-events.service.ts +61 -61
  220. package/src/_modules/defaults/_services/default-user.data-service.spec.ts +187 -187
  221. package/src/_modules/defaults/_services/default-user.data-service.ts +98 -98
  222. package/src/_modules/defaults/index.ts +17 -17
  223. package/src/_modules/discord-assistant/_collections/dias-global-settings.const.ts +19 -19
  224. package/src/_modules/discord-assistant/_collections/dias.util.spec.ts +366 -366
  225. package/src/_modules/discord-assistant/_collections/dias.util.ts +132 -132
  226. package/src/_modules/discord-assistant/_models/dias-global-settings.interface.ts +19 -19
  227. package/src/_modules/discord-assistant/_models/dias-knowledge.data-model.ts +52 -52
  228. package/src/_modules/discord-assistant/_services/dias-chunk.data-service.ts +177 -177
  229. package/src/_modules/discord-assistant/_services/dias-io.control-service.spec.ts +108 -108
  230. package/src/_modules/discord-assistant/_services/dias-io.control-service.ts +69 -69
  231. package/src/_modules/discord-assistant/_services/dias-main.control-service.spec.ts +22 -22
  232. package/src/_modules/discord-assistant/_services/dias-main.control-service.ts +27 -27
  233. package/src/_modules/discord-assistant/_services/dias.service-base.spec.ts +195 -195
  234. package/src/_modules/discord-assistant/_services/dias.service-base.ts +76 -76
  235. package/src/_modules/discord-assistant/index.ts +38 -38
  236. package/src/_modules/discord-assistant-voiced/_services/dias-discord-bot.control-service.spec.ts +34 -34
  237. package/src/_modules/discord-assistant-voiced/_services/dias-discord-bot.control-service.ts +11 -11
  238. package/src/_modules/discord-assistant-voiced/index.ts +36 -36
  239. package/src/_modules/discord-bot/_collections/dibo-default-commands.const.ts +16 -16
  240. package/src/_modules/discord-bot/_collections/dibo-global-settings.conts.ts +55 -55
  241. package/src/_modules/discord-bot/_collections/dibo-operations.util.spec.ts +214 -214
  242. package/src/_modules/discord-bot/_collections/dibo-operations.util.ts +387 -387
  243. package/src/_modules/discord-bot/_models/dibo-command.interface.ts +12 -12
  244. package/src/_modules/discord-bot/_models/dibo-global-settings.interface.ts +98 -98
  245. package/src/_modules/discord-bot/_models/dibo-last-mention-date.inteface.ts +7 -7
  246. package/src/_modules/discord-bot/_models/dibo-last-message-date.interface.ts +6 -6
  247. package/src/_modules/discord-bot/_services/dibo-commands.control-service.spec.ts +154 -154
  248. package/src/_modules/discord-bot/_services/dibo-commands.control-service.ts +153 -153
  249. package/src/_modules/discord-bot/_services/dibo-io.control-service.spec.ts +264 -264
  250. package/src/_modules/discord-bot/_services/dibo-io.control-service.ts +306 -306
  251. package/src/_modules/discord-bot/_services/dibo-main.control-service.spec.ts +408 -408
  252. package/src/_modules/discord-bot/_services/dibo-main.control-service.ts +487 -487
  253. package/src/_modules/discord-bot/_services/dibo-routines.control-service.spec.ts +105 -105
  254. package/src/_modules/discord-bot/index.ts +36 -36
  255. package/src/_modules/local-vector-search/_enums/lvs-search-mode.enum.ts +35 -35
  256. package/src/_modules/local-vector-search/_models/data-models/lvs-vector-persist.data-model.ts +59 -59
  257. package/src/_modules/local-vector-search/_models/lvs-search-result.interface.ts +17 -17
  258. package/src/_modules/local-vector-search/_services/lvs-doc-chunk-data.service.spec.ts +418 -418
  259. package/src/_modules/local-vector-search/_services/lvs-doc-chunk-data.service.ts +276 -276
  260. package/src/_modules/local-vector-search/_services/lvs-local-vector-search.data-service.spec.ts +480 -480
  261. package/src/_modules/local-vector-search/_services/lvs-local-vector-search.data-service.ts +416 -416
  262. package/src/_modules/local-vector-search/_services/lvs-persistent-vector-pool.control-service.spec.ts +214 -214
  263. package/src/_modules/local-vector-search/_services/lvs-persistent-vector-pool.control-service.ts +186 -186
  264. package/src/_modules/local-vector-search/_services/lvs-vector-persist.data-service.spec.ts +167 -167
  265. package/src/_modules/local-vector-search/_services/lvs-vector-persist.data-service.ts +130 -130
  266. package/src/_modules/local-vector-search/_services/lvs-vector-pool.control-service.spec.ts +509 -509
  267. package/src/_modules/local-vector-search/_services/lvs-vector-pool.control-service.ts +323 -323
  268. package/src/_modules/local-vector-search/index.ts +16 -16
  269. package/src/_modules/logs/index.ts +11 -11
  270. package/src/_modules/mcp/_models/interfaces/dynts-mcp.interface.ts +111 -111
  271. package/src/_modules/mcp/_services/dynts-mcp-server.service-base.spec.ts +142 -142
  272. package/src/_modules/mcp/_services/dynts-mcp-server.service-base.ts +120 -120
  273. package/src/_modules/mcp/_services/dynts-mcp.adapter.ts +168 -168
  274. package/src/_modules/mcp/index.ts +13 -13
  275. package/src/_modules/messaging/README.md +354 -354
  276. package/src/_modules/messaging/_collections/get-messaging-routing-module.util.ts +26 -26
  277. package/src/_modules/messaging/_collections/msg-global-settings.const.ts +22 -22
  278. package/src/_modules/messaging/_collections/msg.util.spec.ts +226 -226
  279. package/src/_modules/messaging/_models/msg-global-settings.interface.ts +37 -37
  280. package/src/_modules/messaging/_services/msg-conversation.data-service.ts +146 -146
  281. package/src/_modules/messaging/_services/msg-events.service.spec.ts +219 -219
  282. package/src/_modules/messaging/_services/msg-events.service.ts +267 -267
  283. package/src/_modules/messaging/_services/msg-integration.control-service.ts +179 -179
  284. package/src/_modules/messaging/_services/msg-main.control-service.spec.ts +147 -147
  285. package/src/_modules/messaging/_services/msg-main.control-service.ts +571 -571
  286. package/src/_modules/messaging/_services/msg-message.data-service.ts +129 -129
  287. package/src/_modules/messaging/_services/msg.controller.spec.ts +201 -201
  288. package/src/_modules/messaging/index.ts +30 -30
  289. package/src/_modules/mock/app-extended-server.mock.ts +201 -201
  290. package/src/_modules/mock/app-integration-test.mock.ts +51 -51
  291. package/src/_modules/mock/app-params.mock.spec.ts +21 -21
  292. package/src/_modules/mock/app-params.mock.ts +9 -9
  293. package/src/_modules/mock/app-server.mock.ts +188 -188
  294. package/src/_modules/mock/auth-service.mock.spec.ts +47 -47
  295. package/src/_modules/mock/auth-service.mock.ts +28 -28
  296. package/src/_modules/mock/controller.mock.spec.ts +26 -26
  297. package/src/_modules/mock/controller.mock.ts +16 -16
  298. package/src/_modules/mock/data-model.mock.spec.ts +111 -111
  299. package/src/_modules/mock/data-model.mock.ts +82 -82
  300. package/src/_modules/mock/email-service-collection.mock.spec.ts +24 -24
  301. package/src/_modules/mock/email-service-collection.mock.ts +15 -15
  302. package/src/_modules/mock/email-service.mock.spec.ts +17 -17
  303. package/src/_modules/mock/email-service.mock.ts +20 -20
  304. package/src/_modules/mock/email-template.mock.html +14 -14
  305. package/src/_modules/mock/endpoint.mock.ts +91 -91
  306. package/src/_modules/mock/socket-client.mock.spec.ts +40 -40
  307. package/src/_modules/mock/socket-client.mock.ts +45 -45
  308. package/src/_modules/mock/socket-server.mock.spec.ts +44 -44
  309. package/src/_modules/mock/socket-server.mock.ts +46 -46
  310. package/src/_modules/oauth2/_routes/oauth2.controller.spec.ts +107 -107
  311. package/src/_modules/oauth2/_routes/oauth2.controller.ts +98 -98
  312. package/src/_modules/oauth2/_services/oauth2.auth-service.spec.ts +254 -254
  313. package/src/_modules/oauth2/_services/oauth2.auth-service.ts +232 -232
  314. package/src/_modules/oauth2/_services/oauth2.control-service.spec.ts +585 -585
  315. package/src/_modules/oauth2/_services/oauth2.control-service.ts +653 -653
  316. package/src/_modules/oauth2/index.ts +17 -17
  317. package/src/_modules/scheduler/_models/scheduler-job.interface.ts +35 -35
  318. package/src/_modules/scheduler/get-scheduler-routing-module.util.ts +33 -33
  319. package/src/_modules/scheduler/index.ts +8 -8
  320. package/src/_modules/scheduler/scheduler.controller.spec.ts +42 -42
  321. package/src/_modules/scheduler/scheduler.controller.ts +69 -69
  322. package/src/_modules/scheduler/scheduler.service.spec.ts +141 -141
  323. package/src/_modules/scheduler/scheduler.service.ts +176 -176
  324. package/src/_modules/scoped-config/_enums/dynts-scoped-config-level.enum.ts +22 -22
  325. package/src/_modules/scoped-config/_models/data-models/dynts-scoped-config.data-model.ts +81 -81
  326. package/src/_modules/scoped-config/_models/interfaces/dynts-scoped-config.interface.ts +107 -107
  327. package/src/_modules/scoped-config/_services/dynts-scoped-config.control-service.spec.ts +306 -306
  328. package/src/_modules/scoped-config/_services/dynts-scoped-config.control-service.ts +295 -295
  329. package/src/_modules/scoped-config/_services/dynts-scoped-config.data-service.spec.ts +118 -118
  330. package/src/_modules/scoped-config/_services/dynts-scoped-config.data-service.ts +105 -105
  331. package/src/_modules/scoped-config/index.ts +17 -17
  332. package/src/_modules/server/errors/errors.control-service.spec.ts +238 -238
  333. package/src/_modules/server/errors/errors.control-service.ts +100 -100
  334. package/src/_modules/server/errors/errors.controller.ts +559 -559
  335. package/src/_modules/server/errors/errors.data-service.spec.ts +480 -480
  336. package/src/_modules/server/index.ts +30 -30
  337. package/src/_modules/server/server-status/server-status-snapshot.control-service.spec.ts +70 -70
  338. package/src/_modules/server/server-status/server-status-snapshot.control-service.ts +17 -17
  339. package/src/_modules/server/server-status/server-status-snapshot.data-service.spec.ts +77 -77
  340. package/src/_modules/server/server-status/server-status-snapshot.data-service.ts +37 -37
  341. package/src/_modules/server/server-status/server-status.control-service.spec.ts +672 -672
  342. package/src/_modules/server/server-status/server-status.control-service.ts +490 -490
  343. package/src/_modules/server/server-status/server-status.controller.spec.ts +276 -276
  344. package/src/_modules/server/server-status/server-status.controller.ts +336 -336
  345. package/src/_modules/socket/_enums/socket-security.enum.ts +11 -11
  346. package/src/_modules/socket/_models/socket-client-service-params.control-model.spec.ts +32 -32
  347. package/src/_modules/socket/_models/socket-client-service-params.control-model.ts +22 -22
  348. package/src/_modules/socket/_models/socket-presence.control-model.spec.ts +158 -158
  349. package/src/_modules/socket/_models/socket-presence.control-model.ts +209 -209
  350. package/src/_modules/socket/_models/socket-server-service-params.control-model.spec.ts +69 -69
  351. package/src/_modules/socket/_services/socket-client.service.spec.ts +15 -15
  352. package/src/_modules/socket/_services/socket-client.service.ts +260 -260
  353. package/src/_modules/socket/app-extended.integration.spec.ts +85 -85
  354. package/src/_modules/socket/app-extended.server.ts +648 -648
  355. package/src/_modules/socket/index.ts +42 -42
  356. package/src/_modules/test/get-test-routing-module.util.spec.ts +28 -28
  357. package/src/_modules/test/get-test-routing-module.util.ts +23 -23
  358. package/src/_modules/test/index.ts +11 -11
  359. package/src/_modules/test/test.controller.spec.ts +72 -72
  360. package/src/_modules/test/test.controller.ts +115 -115
  361. package/src/_modules/usage/get-usage-routing-module.util.ts +22 -22
  362. package/src/_modules/usage/index.ts +15 -15
  363. package/src/_modules/usage/usage.controller.spec.ts +106 -106
  364. package/src/_modules/usage/usage.controller.ts +140 -140
  365. package/src/_modules/usage/usage.data-service.spec.ts +332 -332
  366. package/src/_modules/usage/usage.data-service.ts +185 -185
  367. package/src/_services/base/api.service-base.spec.ts +125 -125
  368. package/src/_services/base/api.service-base.ts +74 -74
  369. package/src/_services/base/archive-data.service.spec.ts +209 -209
  370. package/src/_services/base/archive-data.service.ts +223 -223
  371. package/src/_services/base/data.service.spec.ts +784 -784
  372. package/src/_services/base/data.service.ts +2792 -2792
  373. package/src/_services/base/db.service.spec.ts +160 -160
  374. package/src/_services/base/db.service.ts +1698 -1698
  375. package/src/_services/base/singleton.service-base.spec.ts +28 -28
  376. package/src/_services/base/singleton.service-base.ts +24 -24
  377. package/src/_services/base/singleton.service.spec.ts +114 -114
  378. package/src/_services/base/singleton.service.ts +38 -38
  379. package/src/_services/core/api.service.spec.ts +140 -140
  380. package/src/_services/core/auth.service.spec.ts +159 -159
  381. package/src/_services/core/auth.service.ts +174 -174
  382. package/src/_services/core/collection-growth-monitor.service.spec.ts +53 -1
  383. package/src/_services/core/collection-growth-monitor.service.ts +29 -6
  384. package/src/_services/core/email.service.spec.ts +85 -85
  385. package/src/_services/core/email.service.ts +757 -757
  386. package/src/_services/core/global.service.spec.ts +292 -292
  387. package/src/_services/core/global.service.ts +487 -487
  388. package/src/_services/core/memory-guard.service.spec.ts +245 -245
  389. package/src/_services/core/memory-guard.service.ts +481 -481
  390. package/src/_services/core/service-collection.service.spec.ts +46 -46
  391. package/src/_services/core/service-collection.service.ts +6 -6
  392. package/src/_services/route/controller.service.spec.ts +53 -53
  393. package/src/_services/route/controller.service.ts +148 -148
  394. package/src/_services/route/routing-module.service.spec.ts +98 -98
  395. package/src/_services/route/routing-module.service.ts +330 -330
  396. package/src/_services/server/app.server.ts +2012 -2012
  397. package/src/_services/shared.static-service.spec.ts +99 -99
  398. package/src/_services/shared.static-service.ts +78 -78
  399. package/tsconfig.app.json +12 -12
  400. package/tsconfig.json +42 -42
@@ -1,1085 +1,1085 @@
1
- import { OpenAI } from 'openai';
2
-
3
- import { DyFM_OAI_Settings, DyFM_OAI_Model, DyFM_OAI_CallSettings, DyFM_OAI_Models } from '@futdevpro/fsm-dynamo/ai/open-ai';
4
- import { DyFM_AnyError, DyFM_Error, DyFM_Error_Settings, DyFM_Log, DyFM_notNull, DyFM_Object } from '@futdevpro/fsm-dynamo';
5
- import {
6
- DyFM_AI_Message,
7
- DyFM_AI_MessageRole,
8
- DyFM_AI_Provider,
9
- DyFM_AI_ProviderCapabilities,
10
- DyFM_AI_CallSettings,
11
- DyFM_AI_Config,
12
- DyFM_AI_LLM_Response,
13
- DyFM_AI_Tool,
14
- DyFM_AI_ToolCall,
15
- DyFM_AI_ModelInfo,
16
- DyFM_AI_ModelRegistry_Util
17
- } from '@futdevpro/fsm-dynamo/ai';
18
-
19
- import { DyNTS_global_settings } from '../../../../../_collections/global-settings.const';
20
- import { ChatCompletion } from 'openai/resources';
21
- import { ChatCompletionCreateParamsBase, ChatCompletionMessageParam, ChatCompletionTool } from 'openai/resources/chat/completions';
22
- import { DyNTS_AI_CostEventCallback } from '../../../_models/interfaces/dynts-ai-cost-event-callback.interface';
23
- import { DyNTS_OAI_LLM_Predefined_Requests } from '../_models/interfaces/oai-llm-predefined-requests.interface';
24
- import { DyNTS_OAI_global_settings } from '../_collections/oai-global-settings.const';
25
- import { DyNTS_OAI_LLMDefaultPredefined_Requests } from '../_collections/oai-llm-predefined-requests.conts';
26
- import { DyNTS_AI_LLM_ServiceBase } from '../../../_services/ai-llm.service-base';
27
- import {
28
- DyFM_AI_Message_Input,
29
- DyFM_AI_GenericSelect_Input,
30
- DyFM_AI_GenericMultiSelect_Input,
31
- DyFM_AI_JSONKeysDescription_Input,
32
- DyFM_AI_JSONExactKeys_Input,
33
- DyFM_AI_MultiSelect_Input,
34
- DyFM_AI_ConversationBase_Input
35
- } from '../../../_models/ai-input-interfaces';
36
-
37
-
38
- /**
39
- * {
40
- * organization: 'org-XY',
41
- * apiKey: 'sk-XY',
42
- * project: 'proj_XY',
43
- * }
44
- */
45
-
46
- // Di Dictionary DiModel DiEz DiAz
47
-
48
- export class DyNTS_OAI_LLM_ServiceBase extends DyNTS_AI_LLM_ServiceBase {
49
-
50
- /* readonly defaultErrorUserMsg: string =
51
- `We encountered an unhandled Control Service Error, ` +
52
- `\nplease contact the responsible development team.`; */
53
-
54
- openai: OpenAI;
55
-
56
- readonly defaultSettings: DyFM_OAI_CallSettings;
57
-
58
- // Provider properties from DyNTS_AI_Provider_ServiceBase
59
- readonly aiProvider: DyFM_AI_Provider = DyFM_AI_Provider.OpenAI;
60
-
61
- readonly capabilities: DyFM_AI_ProviderCapabilities = {
62
- chat: true,
63
- embeddings: true,
64
- imageGeneration: true,
65
- vision: true,
66
- audioGeneration: true,
67
- audioAnalysis: true,
68
- functionCalling: true,
69
- streaming: true,
70
- batchOperations: true,
71
- maxContextLength: 128000,
72
- supportedModelTypes: ['gpt-4o', 'gpt-4o-mini', 'text-embedding-3-large', 'text-embedding-3-small']
73
- };
74
-
75
- /* get _debugLog(): boolean {
76
- return this.defaultSettings.debugLog;
77
- } */
78
-
79
- /* get defaultSystemPrompt(): string {
80
- return this.defaultSettings.systemPrompt;
81
- } */
82
-
83
- override get defaultModel(): DyFM_OAI_Model | string {
84
- return this.defaultSettings.useModel;
85
- }
86
-
87
- predefinedRequests: DyNTS_OAI_LLM_Predefined_Requests = DyNTS_OAI_LLMDefaultPredefined_Requests;
88
-
89
- constructor(
90
- set?: DyFM_OAI_Settings & { onCostEvent?: DyNTS_AI_CostEventCallback }
91
- ) {
92
- super();
93
-
94
- DyNTS_global_settings.openAi_settings ??= DyNTS_OAI_global_settings;
95
-
96
- this.openai = new OpenAI(
97
- set?.config ??
98
- {
99
- organization: DyNTS_global_settings.env_settings.openAi.organization,
100
- apiKey: DyNTS_global_settings.env_settings.openAi.apiKey,
101
- project: DyNTS_global_settings.env_settings.openAi.project,
102
- }
103
- );
104
-
105
- this.defaultSettings = set?.defaultSettings ??
106
- DyNTS_OAI_global_settings.defaultSettings ??
107
- new DyFM_OAI_CallSettings();
108
-
109
- // FR-002 — opcionális per-call cost-event callback átadás a base-osztálynak
110
- if (set?.onCostEvent) {
111
- this.onCostEvent = set.onCostEvent;
112
- }
113
- }
114
-
115
- /**
116
- * Inicializálja az OpenAI client-et a megadott config-gal
117
- *
118
- * Reinitializes the OpenAI client with the provided config
119
- */
120
- setup(config: DyFM_AI_Config): void {
121
- this.openai = new OpenAI(config);
122
- }
123
-
124
- /* readonly defaultLogReplacer: string = '...long-context...'; */
125
-
126
- //#region Ask Questions
127
-
128
- /**
129
- * Asks the AI to answer a yes/no question
130
- *
131
- * Uses the {@link DyNTS_OAI_LLM_Predefined_Requests.yesNo.request} to ask the question,
132
- * and the {@link DyNTS_OAI_LLM_Predefined_Requests.yesNo.upperCaseYes} to check the answer
133
- */
134
- async requestYesNo(
135
- set: DyFM_AI_Message_Input
136
- ): Promise<boolean> {
137
- set.message += this.predefinedRequests.yesNo.request;
138
-
139
- const answer = await this.requestSimpleMessage(set);
140
-
141
- return this.convertAnswerToBoolean(answer);
142
- }
143
- /** the exact same as {@link requestYesNo} */
144
- //yesNoQuestion = this.askYesNoQuestion;
145
-
146
- /**
147
- * Asks the AI to answer a percentage question
148
- *
149
- * Uses the {@link DyNTS_OAI_LLM_Predefined_Requests.percentageOnly} to ask the question
150
- */
151
- async requestPercentage(
152
- set: DyFM_AI_Message_Input
153
- ): Promise<number> {
154
- set.message += this.predefinedRequests.percentageOnly;
155
-
156
- const answer = await this.requestSimpleMessage(set);
157
-
158
- return this.convertAnswerToNumber(answer, set.message);
159
- }
160
- /** the exact same as {@link requestPercentage} */
161
- //percentageQuestion = this.askPercentageQuestion;
162
-
163
- /**
164
- * Asks the AI to select one of the options from the list
165
- *
166
- * Uses the {@link DyNTS_OAI_LLM_Predefined_Requests.selectRequest} to ask the question
167
- */
168
- /* async askSelectQuestion(
169
- set: DyFM_AI_GenericSelect_Input<string>
170
- ): Promise<string> {
171
- // replace the {{DyNTS_selectOptions}} with the list of options
172
- const selectQuestionAddition = this.predefinedRequests.selectRequest.replace(
173
- '{{DyNTS_selectOptions}}',
174
- this.getTextListAsText(set.selectFrom)
175
- );
176
- set.message += selectQuestionAddition;
177
-
178
- let answer = await this.requestSimpleMessage(set);
179
-
180
- answer = answer.toLocaleUpperCase();
181
-
182
- for (const item of set.selectFrom) {
183
- if (answer.includes(item.toLocaleUpperCase())) return item;
184
- }
185
-
186
- return null;
187
- } */
188
- /** the exact same as {@link askSelectQuestion} */
189
- //selectQuestion = this.askSelectQuestion;
190
-
191
- /**
192
- * Asks the AI to select one of the options from the list
193
- * Similar to {@link askSelectQuestion},
194
- * but with a more generic approach that can be used for objects
195
- *
196
- * Uses the {@link DyNTS_OAI_LLM_Predefined_Requests.selectRequest} to ask the question
197
- */
198
- async requestSelect<T>(
199
- set: DyFM_AI_GenericSelect_Input<T>
200
- ): Promise<T | { unparsableResult: string }> {
201
- const options: string[] = this.stringifySelectOptions(set.selectFrom);
202
- const selectQuestionAddition = this.predefinedRequests.selectRequest.replace(
203
- '{{DyNTS_selectOptions}}',
204
- this.getTextListAsText(options)
205
- );
206
- set.message += selectQuestionAddition;
207
-
208
- const answer = await this.requestSimpleMessage(set);
209
-
210
- return this.convertAnswerToSelectOption<T>(answer, set.message, set.selectFrom);
211
- }
212
- /** the exact same as {@link requestSelect} */
213
- //selectRequest = this.requestSelect;
214
-
215
- /**
216
- * Asks the AI to select one or more of the options from the list
217
- *
218
- * Uses the {@link DyNTS_OAI_LLM_Predefined_Requests.multiselect} to ask the question
219
- */
220
- async requestMultiselect<T>(
221
- set: DyFM_AI_GenericMultiSelect_Input<T, DyFM_OAI_CallSettings>
222
- ): Promise<T[] | { unparsableResult: string }> {
223
- const options: string[] = this.stringifySelectOptions(set.options);
224
- const selectQuestionAddition = this.predefinedRequests.multiselect.replace(
225
- '{{DyNTS_selectOptions}}',
226
- this.getTextListAsText(options)
227
- );
228
- set.message += selectQuestionAddition;
229
-
230
- let answer = await this.requestSimpleMessage(set);
231
-
232
- answer = answer.toLocaleUpperCase();
233
-
234
- return this.convertAnswerToSelectOptions<T>(answer, set.message, set.options);
235
- /* const result: T[] = [];
236
-
237
- for (const item of options) {
238
- if (answer.includes(item.toLocaleUpperCase())) {
239
- const parsedItem: T | { unparsableResult: string } = DyFM_Object.safeParseJSON<T>(item);
240
- if ((parsedItem as { unparsableResult: string }).unparsableResult) {
241
- result.push(item as any);
242
- } else {
243
- result.push(parsedItem as T);
244
- }
245
-
246
- }
247
- }
248
-
249
- if (!result.length && !answer.trim().length) {
250
- return { unparsableResult: answer };
251
- }
252
-
253
- return result; */
254
- }
255
- /** the exact same as {@link askMultipleSelectQuestionWithOptions} */
256
- //multipleSelectQuestion = this.askMultipleSelectQuestionWithOptions;
257
-
258
- /**
259
- * Asks the AI to select one or more of the options from the list
260
- * Similar to {@link askMultipleSelectQuestionWithOptions},
261
- * but with a more generic approach that can be used for objects
262
- *
263
- * Uses the {@link DyNTS_OAI_LLM_Predefined_Requests.multiselect} to ask the question
264
- */
265
- /* async requestMultiselect<T>(
266
- set: DyFM_AI_GenericMultiSelect_Input<T, DyFM_OAI_CallSettings>
267
- ): Promise<T[]> {
268
- const selectQuestionAddition = this.predefinedRequests.multiselect.replace(
269
- '{{DyNTS_selectOptions}}',
270
- this.getTextListAsText(set.options.map(item => `"${JSON.stringify(item)}"`))
271
- );
272
- set.message += selectQuestionAddition;
273
-
274
- const answer = await this.requestSimpleMessage(set);
275
-
276
- return DyFM_Object.safeParseList<T[]>(answer);
277
- } */
278
- /** the exact same as {@link requestMultipleSelect} */
279
- //multipleSelectRequest = this.requestMultipleSelect;
280
-
281
- /**
282
- * Asks the AI to answer a question that must result a JSON object
283
- *
284
- * If the answer is not a valid JSON object, it will return { unparsableResult: answer }
285
- *
286
- * Uses the {@link DyNTS_OAI_LLM_Predefined_Requests.jsonRequest} to ask the question
287
- *
288
- * (uses {@link DyFM_safeParseJSON})
289
- */
290
- async requestJSON<T = any>(
291
- set: DyFM_AI_Message_Input<DyFM_OAI_CallSettings>
292
- ): Promise<T | { unparsableResult: string }> {
293
- set.message += this.predefinedRequests.jsonRequest;
294
-
295
- const answer = await this.requestSimpleMessage(set);
296
-
297
- return this.convertAnswerToJSON<T>(answer, set.message);
298
- }
299
- /** the exact same as {@link askJSONQuestion} */
300
- //jsonQuestion = this.askJSONQuestion;
301
-
302
- /**
303
- * Asks the AI to answer a question that must result a JSON object with specific key descriptions
304
- *
305
- * If the answer is not a valid JSON object, it will return { unparsableResult: answer }
306
- *
307
- * Uses the {@link DyNTS_OAI_LLM_Predefined_Requests.jsonRequestWithKeysDescription} to ask the question
308
- *
309
- * (uses {@link DyFM_safeParseJSON})
310
- */
311
- async requestJSONQuestionWithKeysDescription<T = any>(
312
- set: DyFM_AI_JSONKeysDescription_Input<DyFM_OAI_CallSettings>
313
- ): Promise<T | { unparsableResult: string }> {
314
- const jsonRequestWithKeysDescription = this.predefinedRequests.jsonRequestWithKeysDescription.replace(
315
- '{{DyNTS_jsonKeysDescription}}',
316
- set.keysDescription
317
- );
318
- set.message += jsonRequestWithKeysDescription;
319
-
320
- const answer = await this.requestSimpleMessage(set);
321
-
322
- return this.convertAnswerToJSON<T>(answer, set.message);
323
- }
324
- /** the exact same as {@link requestJSONQuestionWithKeysDescription} */
325
- //jsonQuestionWithKeysDescription = this.askJSONQuestionWithKeysDescription;
326
-
327
- /**
328
- * Asks the AI to answer a question that must result a JSON object with specific keys
329
- *
330
- * If the answer is not a valid JSON object, it will return { unparsableResult: answer }
331
- *
332
- * Uses the {@link DyNTS_OAI_LLM_Predefined_Requests.jsonRequestWithExactKeys} to ask the question
333
- *
334
- * (uses {@link DyFM_safeParseJSON})
335
- */
336
- async requestJSONWithExactKeys<T = any>(
337
- set: DyFM_AI_JSONExactKeys_Input<DyFM_OAI_CallSettings>
338
- ): Promise<T | { unparsableResult: string }> {
339
- const jsonRequestWithExactKeys = this.predefinedRequests.jsonRequestWithExactKeys.replace(
340
- '{{DyNTS_jsonKeys}}',
341
- this.getTextListAsText(set.keys)
342
- );
343
- set.message += jsonRequestWithExactKeys;
344
-
345
- const answer = await this.requestSimpleMessage(set);
346
-
347
- return this.convertAnswerToJSON<T>(answer, set.message);
348
- }
349
- /** the exact same as {@link askJSONQuestionWithExactKeys} */
350
- //jsonQuestionWithExactKeys = this.askJSONQuestionWithExactKeys;
351
-
352
- /**
353
- * Asks the AI to answer a question that must result a list of strings
354
- *
355
- * If the answer is not a valid JSON list, it will return the answer as a single item in an array
356
- *
357
- * Uses the {@link DyNTS_OAI_LLM_Predefined_Requests.listRequest} to ask the question
358
- *
359
- * (uses {@link DyFM_Object.safeParseList})
360
- */
361
- /* async requestList(
362
- set: DyFM_AI_Base_Input<DyFM_OAI_CallSettings>
363
- ): Promise<string[]> {
364
- set.message += this.predefinedRequests.listRequest;
365
-
366
- const answer = await this.requestSimpleMessage(set);
367
-
368
- return this.convertAnswerToList<string>(answer, set.message);
369
- } */
370
- /** the exact same as {@link askListQuestion} */
371
- //listQuestion = this.askListQuestion;
372
- /** the exact same as {@link askListQuestion} */
373
- //listRequest = this.askListQuestion;
374
-
375
- async requestList<T = any>(
376
- set: DyFM_AI_Message_Input<DyFM_OAI_CallSettings>
377
- ): Promise<T[] | { unparsableResult: string }> {
378
- set.message += this.predefinedRequests.listRequest;
379
-
380
- const answer = await this.requestSimpleMessage(set);
381
-
382
- return this.convertAnswerToList<T>(answer, set.message);
383
- }
384
-
385
- /**
386
- * Asks the AI to answer a question
387
- *
388
- * Uses the {@link resolveSimpleUserMessage}
389
- */
390
- async requestSimpleMessage(
391
- set: DyFM_AI_Message_Input<DyFM_OAI_CallSettings>
392
- ): Promise<string> {
393
- this.logQuestion(set);
394
-
395
- set.conversation ??= [];
396
- set.conversation.push({ role: DyFM_AI_MessageRole.user, content: set.message });
397
-
398
- const answer: string = await this.resolveMessage({
399
- ...set,
400
- conversation: set.conversation,
401
- getFullResponse: false,
402
- }) as string;
403
-
404
- this.logAnswer(answer);
405
-
406
- return answer;
407
- }
408
- /** the exact same as {@link requestSimpleMessage} */
409
- //answer = this.askQuestion;
410
- /** the exact same as {@link requestSimpleMessage} */
411
- //getQuestionAnswer = this.askQuestion;
412
- /** the exact same as {@link requestSimpleMessage} */
413
- //simpleQuestion = this.askQuestion;
414
- /** the exact same as {@link requestSimpleMessage} */
415
- //askSimpleQuestion = this.askQuestion;
416
-
417
- /**
418
- * Resolves a simple user message and returns the answer as string
419
- *
420
- * Uses the {@link resolveMessage}
421
- */
422
- /* async resolveSimpleUserMessage(
423
- set: DyFM_AI_Base_Input<DyFM_OAI_CallSettings>
424
- ): Promise<string> {
425
- return this.resolveMessage({
426
- conversation: [{ role: DyFM_AI_MessageRole.user, content: set.message }],
427
- issuer: set.issuer ?? 'unknown',
428
- settings: set.settings ?? this.defaultSettings,
429
- getFullResponse: false,
430
- }) as Promise<string>;
431
- } */
432
- /** the exact same as {@link resolveSimpleUserMessage} */
433
- //simpleUserMessage = this.resolveSimpleUserMessage;
434
-
435
- //#endregion
436
-
437
- /**
438
- * Resolves a conversation
439
- *
440
- * Uses the {@link getMessageCreateInput}
441
- */
442
- async resolveMessage(
443
- set: DyFM_AI_ConversationBase_Input<DyFM_OAI_CallSettings>
444
- /* {
445
- conversation: [{ role: DyFM_AI_MessageRole.user, content: set.message }],
446
- issuer: set.issuer ?? 'unknown',
447
- settings: set.settings ?? this.defaultSettings,
448
- getFullResponse: false,
449
- } */
450
- /* {
451
- conversation: DyFM_AI_Message[],
452
- issuer: string,
453
- settings?: DyFM_OAI_CallSettings,
454
- getFullResponse?: boolean
455
- } */
456
- ): Promise<string | ChatCompletion> {
457
- try {
458
- // FR-002 — callType meghatározás a unshift ELŐTT (a system-message hozzáadás
459
- // után már 2+ message lenne, ami a "chat" kategóriát mindig kiváltja).
460
- // 1 user-message → 'llm-completion'; több → 'llm-chat'. A 'llm-tool-use'
461
- // categoria a tools paraméterek bevezetésekor kerül használatba (jövőbeli FR).
462
- const userMessageCount: number = set.conversation.length;
463
-
464
- set.conversation.unshift(this.getDefaultSystemMessage(set.settings));
465
-
466
- // FR-002 — per-call cost-event timing: a tényleges API-call latency mérve
467
- const input: ChatCompletionCreateParamsBase = this.getMessageCreateInput(set);
468
- const start: number = Date.now();
469
- const result: ChatCompletion = await this.openai.chat.completions.create(
470
- input,
471
- ) as ChatCompletion;
472
- const durationMs: number = Date.now() - start;
473
-
474
- // FR-002 — cost-event emit (safe, no-op ha nincs onCostEvent callback)
475
- this.emitCostEvent({
476
- callType: userMessageCount === 1 ? 'llm-completion' : 'llm-chat',
477
- provider: this.aiProvider,
478
- model: String(input.model ?? this.defaultModel),
479
- tokensUsed: {
480
- input: result.usage?.prompt_tokens ?? 0,
481
- output: result.usage?.completion_tokens ?? 0,
482
- total: result.usage?.total_tokens ?? 0,
483
- },
484
- durationMs: durationMs,
485
- issuer: set.issuer,
486
- timestamp: new Date(),
487
- });
488
-
489
- if (set.getFullResponse) {
490
- return result;
491
- }
492
-
493
- return result.choices[0].message.content;
494
- } catch (error) {
495
- throw new DyFM_Error({
496
- ...this.getDefaultErrorSettings('resolveConversation', error, set.issuer),
497
-
498
- errorCode: `${DyNTS_global_settings.systemShortCodeName}|DyNTS-OLSB-RM0`,
499
- });
500
- }
501
- }
502
-
503
- /**
504
- * Creates a message create input
505
- *
506
- * Uses the {@link getDefaultSystemMessage}
507
- */
508
- protected getMessageCreateInput(
509
- set: {
510
- conversation: DyFM_AI_Message[],
511
- issuer: string,
512
- settings?: DyFM_OAI_CallSettings
513
- }
514
- ): ChatCompletionCreateParamsBase {
515
- try {
516
- const settings = set.settings ?? this.defaultSettings;
517
-
518
- const useModel: string = String(settings?.useModel ?? this.defaultModel);
519
-
520
- const result: ChatCompletionCreateParamsBase = {
521
- model: useModel,
522
- messages: set.conversation as ChatCompletionMessageParam[],
523
- };
524
-
525
- // FR-048 §3: a sampling-parameterek (+ max_completion_tokens) a per-model `settingsSchema` szerint
526
- // kerulnek ra (drop/clamp). KORABBAN ez a NEM-tool ag FELTETEL NELKUL kuldte a sampling-parametereket,
527
- // ezert reasoning modellen (GPT-5.x / o-series) 400-zal elhasalt volna — ezt itt zarjuk le.
528
- this.applySamplingParams({ input: result, useModel: useModel, settings: set.settings });
529
-
530
- return result;
531
- } catch (error) {
532
- throw new DyFM_Error({
533
- ...this.getDefaultErrorSettings('getMessageCreateSettings', error, set.issuer),
534
-
535
- errorCode: `${DyNTS_global_settings.systemShortCodeName}|DyNTS-OLSB-RC0`,
536
- });
537
- }
538
- }
539
-
540
- /**
541
- * Teszteli a kapcsolatot az OpenAI API-val
542
- *
543
- * Tests the connection to OpenAI API
544
- */
545
- async testConnection(issuer: string): Promise<boolean> {
546
- try {
547
- await this.openai.models.list();
548
- return true;
549
- } catch (error) {
550
- DyFM_Log.error('DyNTS_OAI_LLM_ServiceBase', 'testConnection', 'Connection test failed', {
551
- error,
552
- issuer
553
- });
554
- return false;
555
- }
556
- }
557
-
558
- //#region Function calling (tool use) — FR-047
559
-
560
- /** OpenAI tool-kepes modell-registry (a base capability-precheck-jehez). */
561
- protected override getModelRegistry(): DyFM_AI_ModelInfo[] {
562
- return DyFM_OAI_Models;
563
- }
564
-
565
- /**
566
- * Egy OpenAI-kor a tool-loop-ban: felepiti a natív request-et (system + a teljes beszelgetes
567
- * tool-uzenetekkel + tools), meghivja az API-t, es normalizalt DyFM_AI_LLM_Response-t ad vissza
568
- * (toolCalls feltoltve, ha a model tool-t akar hivni). FR-002 cost-event: 'llm-tool-use'.
569
- */
570
- protected override async callModelWithTools(
571
- set: {
572
- conversation: DyFM_AI_Message[];
573
- tools: DyFM_AI_Tool[];
574
- settings?: DyFM_OAI_CallSettings;
575
- issuer: string;
576
- }
577
- ): Promise<DyFM_AI_LLM_Response> {
578
- try {
579
- const settings: DyFM_OAI_CallSettings = set.settings ?? this.defaultSettings;
580
-
581
- const messages: ChatCompletionMessageParam[] = [
582
- this.toOpenAIMessage(this.getDefaultSystemMessage(settings)),
583
- ...set.conversation.map((message: DyFM_AI_Message) => this.toOpenAIMessage(message)),
584
- ];
585
-
586
- const useModel: DyFM_OAI_Model | string = settings?.useModel ?? this.defaultModel;
587
-
588
- const input: ChatCompletionCreateParamsBase = {
589
- model: useModel,
590
- messages: messages,
591
- };
592
-
593
- // FR-048 §3: a sampling-parameterek + `max_completion_tokens` a per-model `settingsSchema` szerint
594
- // kerulnek ra. A korabbi lokalis `isReasoningModel`-drop atmeneti fallback-kent bent maradt a
595
- // helper-ben (l. `applySamplingParams`), amig a schema ki nem kerul a fsm-bol — igy nincs regresszio.
596
- this.applySamplingParams({ input: input, useModel: String(useModel), settings: set.settings });
597
-
598
- // Ures tools-tomb -> 400 ("tools must have minimum length 1"). Csak akkor kuldjuk, ha van tool.
599
- if (set.tools.length) {
600
- input.tools = set.tools.map((tool: DyFM_AI_Tool) => this.toOpenAITool(tool));
601
- input.tool_choice = 'auto';
602
- }
603
-
604
- const start: number = Date.now();
605
- const result: ChatCompletion = await this.openai.chat.completions.create(input) as ChatCompletion;
606
- const durationMs: number = Date.now() - start;
607
-
608
- const message = result.choices[0].message;
609
-
610
- const toolCalls: DyFM_AI_ToolCall[] = (message.tool_calls ?? [])
611
- .filter((toolCall) => toolCall.type === 'function')
612
- .map((toolCall) => ({
613
- id: toolCall.id,
614
- name: toolCall.function.name,
615
- arguments: this.parseToolArguments(toolCall.function.arguments),
616
- }));
617
-
618
- this.emitCostEvent({
619
- callType: 'llm-tool-use',
620
- provider: this.aiProvider,
621
- model: String(input.model ?? this.defaultModel),
622
- tokensUsed: {
623
- input: result.usage?.prompt_tokens ?? 0,
624
- output: result.usage?.completion_tokens ?? 0,
625
- total: result.usage?.total_tokens ?? 0,
626
- },
627
- durationMs: durationMs,
628
- issuer: set.issuer,
629
- timestamp: new Date(),
630
- });
631
-
632
- return {
633
- content: message.content ?? '',
634
- toolCalls: toolCalls.length ? toolCalls : undefined,
635
- usage: result.usage ? {
636
- promptTokens: result.usage.prompt_tokens,
637
- completionTokens: result.usage.completion_tokens,
638
- totalTokens: result.usage.total_tokens,
639
- } : undefined,
640
- model: result.model,
641
- finishReason: result.choices[0].finish_reason,
642
- stopReason: result.choices[0].finish_reason,
643
- rawResponse: result,
644
- };
645
- } catch (error) {
646
- throw new DyFM_Error({
647
- ...this.getDefaultErrorSettings('callModelWithTools', error, set.issuer),
648
- errorCode: `${DyNTS_global_settings.systemShortCodeName}|DyNTS-OLSB-TC0`,
649
- });
650
- }
651
- }
652
-
653
- /** Agnosztikus tool-definicio → OpenAI ChatCompletionTool. */
654
- protected toOpenAITool(tool: DyFM_AI_Tool): ChatCompletionTool {
655
- return {
656
- type: 'function',
657
- function: {
658
- name: tool.name,
659
- description: tool.description,
660
- parameters: tool.parameters as Record<string, unknown>,
661
- },
662
- };
663
- }
664
-
665
- /**
666
- * Agnosztikus DyFM_AI_Message → OpenAI ChatCompletionMessageParam.
667
- * Kezeli a tool-result (role:'tool') es a tool-hivo assistant (tool_calls) uzeneteket is.
668
- */
669
- protected toOpenAIMessage(message: DyFM_AI_Message): ChatCompletionMessageParam {
670
- if (message.role === DyFM_AI_MessageRole.tool) {
671
- return {
672
- role: 'tool',
673
- tool_call_id: message.toolCallId ?? '',
674
- content: message.content,
675
- };
676
- }
677
-
678
- if (message.role === DyFM_AI_MessageRole.assistant && message.toolCalls?.length) {
679
- return {
680
- role: 'assistant',
681
- content: message.content || null,
682
- tool_calls: message.toolCalls.map((toolCall: DyFM_AI_ToolCall) => ({
683
- id: toolCall.id,
684
- type: 'function' as const,
685
- function: {
686
- name: toolCall.name,
687
- arguments: JSON.stringify(toolCall.arguments),
688
- },
689
- })),
690
- };
691
- }
692
-
693
- return {
694
- role: message.role as 'system' | 'user' | 'assistant',
695
- content: message.content,
696
- };
697
- }
698
-
699
- /** Tool-argumentumok parse-olasa (OpenAI JSON-string → object); hibas JSON eseten ures object. */
700
- protected parseToolArguments(raw: string): Record<string, unknown> {
701
- try {
702
- return JSON.parse(raw || '{}');
703
- } catch {
704
- return {};
705
- }
706
- }
707
-
708
- /**
709
- * ⚠️ **TUL TAG HEURISZTIKA — csak FALLBACK, ne ez legyen az igazsag forrasa.**
710
- *
711
- * Eredetileg (FR-047) azt felteteleztuk, hogy MINDEN `gpt-5.x` + o-series modell elutasitja a
712
- * sampling-parametereket. **Live capture 2026-07-21 ezt MEGCAFOLTA:**
713
- * - elutasit: `gpt-5.5`, `o3-mini`, `o4-mini`
714
- * - **ELFOGADJA**: `gpt-5.4-mini`, `gpt-5.4-nano` (HTTP 200, `reasoning_tokens: 0`)
715
- *
716
- * Vagyis erre a ket modellre ez a regex **NEMAN kidobna ervenyes parametereket** — halkan rontva a
717
- * kimenetet. Ezert az `applySamplingParams` a **registry `settingsSchema`-t reszesiti elonyben**, es ezt
718
- * a heurisztikat CSAK akkor hasznalja, ha az adott modellhez nincs schema (regi fsm-verzio / ismeretlen
719
- * modell). Amint a populalt schema mindenhol elerheto, ez a metodus elhagyhato.
720
- *
721
- * Reszletek: `fdp-documentations/plans/ai-mocking-system/CAPTURE-FINDINGS.md` §1.
722
- */
723
- protected isReasoningModel(modelId: string): boolean {
724
- return /^gpt-5/.test(modelId) || /^o[0-9]/.test(modelId);
725
- }
726
-
727
- /**
728
- * FR-048 §3 — feloldja az agnosztikus sampling-settingseket (hivas-settings > default), atengedi a
729
- * per-model reconciler-en (`settingsSchema` szerinti drop/clamp), majd RAKEPZI a natív OpenAI mezokre.
730
- * Csak azok a natív mezok kerulnek az input-ra, amik a reconcile utan MEGMARADTAK.
731
- *
732
- * Ezzel a "melyik modell mit fogad el" tudas a registry `settingsSchema`-jaban (SSoT) el, nem
733
- * provider-translatoronkent duplikalva.
734
- *
735
- * **Atmeneti fallback (szandekos):** ha a registry-bejegyzesnek MEG NINCS `settingsSchema`-ja (regebbi
736
- * fsm-verzio, vagy registry-ben nem szereplo modell), a korabbi lokalis `isReasoningModel`-drop marad
737
- * ervenyben — igy a FR-047 `514153f` fix NEM regresszal, amig a schema ki nem kerul a fsm-bol. Amint a
738
- * schema mindenhol elerheto, ez a fallback (es az `isReasoningModel`) elhagyhato.
739
- */
740
- protected applySamplingParams(
741
- set: {
742
- input: ChatCompletionCreateParamsBase,
743
- useModel: string,
744
- settings?: DyFM_OAI_CallSettings,
745
- }
746
- ): void {
747
- const source: DyFM_OAI_CallSettings = set.settings ?? this.defaultSettings;
748
-
749
- const resolved: DyFM_OAI_CallSettings = {
750
- ...source,
751
- useModel: set.useModel,
752
- temperature: DyFM_notNull(source?.temperature) ? source.temperature : this.defaultSettings.temperature,
753
- maxTokens: DyFM_notNull(source?.maxTokens) ? source.maxTokens : this.defaultSettings.maxTokens,
754
- topP: DyFM_notNull(source?.topP) ? source.topP : this.defaultSettings.topP,
755
- frequencyPenalty: DyFM_notNull(source?.frequencyPenalty) ?
756
- source.frequencyPenalty : this.defaultSettings.frequencyPenalty,
757
- presencePenalty: DyFM_notNull(source?.presencePenalty) ?
758
- source.presencePenalty : this.defaultSettings.presencePenalty,
759
- };
760
-
761
- const final: DyFM_OAI_CallSettings = this.reconcileCallSettings(set.useModel, resolved);
762
-
763
- const modelInfo: DyFM_AI_ModelInfo = DyFM_AI_ModelRegistry_Util.findModelInfo(
764
- this.getModelRegistry(),
765
- set.useModel
766
- );
767
- const dropSampling: boolean = !modelInfo?.settingsSchema && this.isReasoningModel(set.useModel);
768
-
769
- if (DyFM_notNull(final?.maxTokens)) {
770
- set.input.max_completion_tokens = final.maxTokens;
771
- }
772
-
773
- if (dropSampling) {
774
- return;
775
- }
776
-
777
- if (DyFM_notNull(final?.temperature)) {
778
- set.input.temperature = final.temperature;
779
- }
780
- if (DyFM_notNull(final?.topP)) {
781
- set.input.top_p = final.topP;
782
- }
783
- if (DyFM_notNull(final?.frequencyPenalty)) {
784
- set.input.frequency_penalty = final.frequencyPenalty;
785
- }
786
- if (DyFM_notNull(final?.presencePenalty)) {
787
- set.input.presence_penalty = final.presencePenalty;
788
- }
789
- }
790
-
791
- //#endregion
792
-
793
- // async askJSONListQuestion(
794
- // question: string,
795
- // issuer: string,
796
- // settings?: DyFM_OAI_CallSettings,
797
- // debugLog?: boolean,
798
- // /** this is used to readably replace too long contents to eg '...' in logs */
799
- // replaceThisInLog?: string,
800
- // ): Promise<{ key: string, input: string }[]> {
801
- // question += this.predefinedRequests.jsonListRequest;
802
- //
803
- // const answer = await this.askQuestion(question, issuer, settings, debugLog, replaceThisInLog);
804
- //
805
- // return DyFM_Object.safeParseList<{ key: string, input: string }[]>(answer);
806
- // }
807
-
808
- //#region Tools
809
-
810
- /**
811
- * Logs the question if the debugLog is true
812
- */
813
- /* protected logQuestion(
814
- set: DyFM_AI_Base_Input<DyFM_OAI_CallSettings>
815
- ): void {
816
- if (set.settings?.debugLog ?? this._debugLog) {
817
- console.log('\n - ', set.message);
818
- }
819
- } */
820
-
821
- /**
822
- * olvasható mondatszerű-listaszerű formába teszi a listaelemeket
823
- * pl.: ['a', 'b', 'c'] -> '"a", "b" or "c"'
824
- */
825
- /* getTextListAsText(list: string[]): string {
826
- list = list.filter(item => item?.trim()).map(item => `"${item}"`);
827
-
828
- //list = list.map(item => item.toLocaleLowerCase());
829
-
830
- list.push(list.pop() + ' or ' + list.pop());
831
-
832
- return list.join(', ');
833
- } */
834
-
835
- /* protected getDefaultSystemMessage(settings: DyFM_OAI_CallSettings): DyFM_AI_Message {
836
- return {
837
- role: DyFM_AI_MessageRole.system,
838
- content: settings?.systemPrompt || this.defaultSystemPrompt,
839
- };
840
- } */
841
-
842
- /* protected validateConversation(conversation: DyFM_AI_Message[]): void {
843
- conversation.forEach((message: DyFM_AI_Message, index: number) => {
844
- if (!message.role) {
845
- throw new DyFM_Error({
846
- message: `Message has no role at index ${index}`,
847
- additionalContent: {
848
- invalidMessage: message,
849
- conversation: conversation,
850
- }
851
- });
852
- }
853
-
854
- if (!message.content) {
855
- throw new DyFM_Error({
856
- message: `Message has no content at index ${index}`,
857
- additionalContent: {
858
- invalidMessage: message,
859
- conversation: conversation,
860
- }
861
- });
862
- }
863
- });
864
- } */
865
-
866
- /* protected getDefaultErrorSettings(
867
- fnName: string,
868
- error: DyFM_AnyError,
869
- issuer: string
870
- ): DyFM_Error_Settings {
871
- return {
872
- status: (error as DyFM_Error)?.___status ?? 500,
873
- message: (error as Error)?.message ??
874
- (error as DyFM_Error)?._message ??
875
- `${fnName} was UNSUCCESSFUL (${DyNTS_global_settings.systemShortCodeName})`,
876
- addECToUserMsg: !(error as DyFM_Error)?.__userMessage,
877
- userMessage: (error as DyFM_Error)?.__userMessage ?? this.defaultErrorUserMsg,
878
- issuer: issuer,
879
- issuerService: this.constructor?.name,
880
- error: error,
881
- };
882
- } */
883
-
884
- //#endregion
885
-
886
- //#region Provider Methods (from DyNTS_AI_Provider_ServiceBase)
887
-
888
- //#endregion
889
-
890
- //#region Abstract LLM Methods (from DyNTS_AI_LLM_ServiceBase)
891
-
892
- /**
893
- * Meghívja az LLM-et system és user message-szel
894
- *
895
- * Calls the LLM with system and user messages
896
- */
897
- /* async callLLM(
898
- systemMessage: string,
899
- userMessage: string,
900
- settings?: DyFM_AI_CallSettings,
901
- issuer?: string
902
- ): Promise<string> {
903
- const result = await this.resolveMessage({
904
- conversation: [
905
- { role: DyFM_AI_MessageRole.system, content: systemMessage },
906
- { role: DyFM_AI_MessageRole.user, content: userMessage }
907
- ],
908
- issuer: issuer ?? 'system',
909
- settings: this.convertToOAISettings(settings),
910
- getFullResponse: false
911
- });
912
-
913
- if (typeof result === 'string') {
914
- return result;
915
- }
916
- return '';
917
- } */
918
-
919
- /**
920
- * Meghívja az LLM-et üzenet history-val
921
- *
922
- * Calls the LLM with message history
923
- */
924
- /* async callLLMWithHistory(
925
- messages: DyFM_AI_Message[],
926
- settings?: DyFM_AI_CallSettings,
927
- issuer?: string
928
- ): Promise<string> {
929
- const result = await this.resolveMessage({
930
- conversation: messages,
931
- issuer: issuer ?? 'system',
932
- settings: this.convertToOAISettings(settings),
933
- getFullResponse: false
934
- });
935
-
936
- if (typeof result === 'string') {
937
- return result;
938
- }
939
- return '';
940
- } */
941
-
942
- /**
943
- * Meghívja az LLM-et és visszaadja a raw response-t
944
- *
945
- * Calls the LLM and returns raw response
946
- */
947
- /* async callLLMRaw(
948
- messages: DyFM_AI_Message[],
949
- settings?: DyFM_AI_CallSettings,
950
- issuer?: string
951
- ): Promise<DyFM_AI_LLM_Response> {
952
- const result = await this.resolveMessage({
953
- conversation: messages,
954
- issuer: issuer ?? 'system',
955
- settings: this.convertToOAISettings(settings),
956
- getFullResponse: true
957
- }) as ChatCompletion;
958
-
959
- return {
960
- content: result.choices[0].message.content,
961
- usage: result.usage ? {
962
- promptTokens: result.usage.prompt_tokens,
963
- completionTokens: result.usage.completion_tokens,
964
- totalTokens: result.usage.total_tokens
965
- } : undefined,
966
- model: result.model,
967
- finishReason: result.choices[0].finish_reason,
968
- rawResponse: result
969
- };
970
- } */
971
-
972
- /**
973
- * Küld egy egyszerű üzenetet az LLM-nek
974
- *
975
- * Sends a simple message to the LLM
976
- */
977
- /* async sendMessage(set: DyFM_AI_Base_Input<DyFM_OAI_CallSettings>): Promise<string> {
978
- return await this.resolveSimpleUserMessage({
979
- message: set.message,
980
- issuer: set.issuer,
981
- settings: set.settings ? this.convertToOAISettings(set.settings) : undefined
982
- });
983
- } */
984
-
985
- /**
986
- * Kérdez egy kérdést és JSON választ vár exact keys-szel
987
- *
988
- * Asks a question and expects JSON response with exact keys
989
- */
990
- /* async requestJSONWithExactKeys<T = any>(
991
- set: DyFM_AI_JSONExactKeys_Input
992
- ): Promise<T> {
993
- const answer = await this.askJSONQuestionWithExactKeys({
994
- question: set.question,
995
- keys: set.keys,
996
- issuer: set.issuer,
997
- settings: set.settings ? this.convertToOAISettings(set.settings) : undefined,
998
- debugLog: set.debugLog,
999
- replaceThisInLog: set.replaceThisInLog
1000
- });
1001
-
1002
- if ('unparsableResult' in answer) {
1003
- throw new DyFM_Error({
1004
- message: 'Invalid JSON response from AI',
1005
- additionalContent: { answer },
1006
- userMessage: 'Failed to parse AI response'
1007
- });
1008
- }
1009
-
1010
- return answer as T;
1011
- } */
1012
-
1013
- /**
1014
- * Kérdez egy kérdést és több választ vár
1015
- *
1016
- * Asks a question and expects multiple responses
1017
- */
1018
- /* async requestMultiselect<T = any>(
1019
- set: DyFM_AI_GenericMultiSelect_Input<T>
1020
- ): Promise<T[]> {
1021
- const answer = await this.requestMultipleSelect({
1022
- question: set.message,
1023
- options: set.options,
1024
- issuer: set.issuer,
1025
- settings: set.settings ? this.convertToOAISettings(set.settings) : undefined,
1026
- debugLog: set.debugLog,
1027
- replaceThisInLog: set.replaceThisInLog
1028
- });
1029
-
1030
- return answer as T[];
1031
- } */
1032
-
1033
- /**
1034
- * Konvertálja a generic AI settings-et OpenAI specifikus settings-re
1035
- *
1036
- * Converts generic AI settings to OpenAI-specific settings
1037
- */
1038
- /* private convertToOAISettings(settings?: DyFM_AI_CallSettings): DyFM_OAI_CallSettings {
1039
- if (!settings) {
1040
- return undefined;
1041
- }
1042
-
1043
- const oaiSettings = new DyFM_OAI_CallSettings();
1044
-
1045
- if (settings.systemPrompt !== undefined) {
1046
- oaiSettings.systemPrompt = settings.systemPrompt;
1047
- }
1048
-
1049
- if (settings.useModel !== undefined) {
1050
- oaiSettings.useModel = settings.useModel as DyFM_OAI_Model;
1051
- }
1052
-
1053
- if (settings.project !== undefined) {
1054
- oaiSettings.project = settings.project;
1055
- }
1056
-
1057
- if (settings.debugLog !== undefined) {
1058
- oaiSettings.debugLog = settings.debugLog;
1059
- }
1060
-
1061
- if (settings.temperature !== undefined) {
1062
- oaiSettings.temperature = settings.temperature;
1063
- }
1064
-
1065
- if (settings.maxTokens !== undefined) {
1066
- oaiSettings.maxTokens = settings.maxTokens;
1067
- }
1068
-
1069
- if (settings.topP !== undefined) {
1070
- oaiSettings.topP = settings.topP;
1071
- }
1072
-
1073
- if (settings.frequencyPenalty !== undefined) {
1074
- oaiSettings.frequencyPenalty = settings.frequencyPenalty;
1075
- }
1076
-
1077
- if (settings.presencePenalty !== undefined) {
1078
- oaiSettings.presencePenalty = settings.presencePenalty;
1079
- }
1080
-
1081
- return oaiSettings;
1082
- } */
1083
-
1084
- //#endregion
1085
- }
1
+ import { OpenAI } from 'openai';
2
+
3
+ import { DyFM_OAI_Settings, DyFM_OAI_Model, DyFM_OAI_CallSettings, DyFM_OAI_Models } from '@futdevpro/fsm-dynamo/ai/open-ai';
4
+ import { DyFM_AnyError, DyFM_Error, DyFM_Error_Settings, DyFM_Log, DyFM_notNull, DyFM_Object } from '@futdevpro/fsm-dynamo';
5
+ import {
6
+ DyFM_AI_Message,
7
+ DyFM_AI_MessageRole,
8
+ DyFM_AI_Provider,
9
+ DyFM_AI_ProviderCapabilities,
10
+ DyFM_AI_CallSettings,
11
+ DyFM_AI_Config,
12
+ DyFM_AI_LLM_Response,
13
+ DyFM_AI_Tool,
14
+ DyFM_AI_ToolCall,
15
+ DyFM_AI_ModelInfo,
16
+ DyFM_AI_ModelRegistry_Util
17
+ } from '@futdevpro/fsm-dynamo/ai';
18
+
19
+ import { DyNTS_global_settings } from '../../../../../_collections/global-settings.const';
20
+ import { ChatCompletion } from 'openai/resources';
21
+ import { ChatCompletionCreateParamsBase, ChatCompletionMessageParam, ChatCompletionTool } from 'openai/resources/chat/completions';
22
+ import { DyNTS_AI_CostEventCallback } from '../../../_models/interfaces/dynts-ai-cost-event-callback.interface';
23
+ import { DyNTS_OAI_LLM_Predefined_Requests } from '../_models/interfaces/oai-llm-predefined-requests.interface';
24
+ import { DyNTS_OAI_global_settings } from '../_collections/oai-global-settings.const';
25
+ import { DyNTS_OAI_LLMDefaultPredefined_Requests } from '../_collections/oai-llm-predefined-requests.conts';
26
+ import { DyNTS_AI_LLM_ServiceBase } from '../../../_services/ai-llm.service-base';
27
+ import {
28
+ DyFM_AI_Message_Input,
29
+ DyFM_AI_GenericSelect_Input,
30
+ DyFM_AI_GenericMultiSelect_Input,
31
+ DyFM_AI_JSONKeysDescription_Input,
32
+ DyFM_AI_JSONExactKeys_Input,
33
+ DyFM_AI_MultiSelect_Input,
34
+ DyFM_AI_ConversationBase_Input
35
+ } from '../../../_models/ai-input-interfaces';
36
+
37
+
38
+ /**
39
+ * {
40
+ * organization: 'org-XY',
41
+ * apiKey: 'sk-XY',
42
+ * project: 'proj_XY',
43
+ * }
44
+ */
45
+
46
+ // Di Dictionary DiModel DiEz DiAz
47
+
48
+ export class DyNTS_OAI_LLM_ServiceBase extends DyNTS_AI_LLM_ServiceBase {
49
+
50
+ /* readonly defaultErrorUserMsg: string =
51
+ `We encountered an unhandled Control Service Error, ` +
52
+ `\nplease contact the responsible development team.`; */
53
+
54
+ openai: OpenAI;
55
+
56
+ readonly defaultSettings: DyFM_OAI_CallSettings;
57
+
58
+ // Provider properties from DyNTS_AI_Provider_ServiceBase
59
+ readonly aiProvider: DyFM_AI_Provider = DyFM_AI_Provider.OpenAI;
60
+
61
+ readonly capabilities: DyFM_AI_ProviderCapabilities = {
62
+ chat: true,
63
+ embeddings: true,
64
+ imageGeneration: true,
65
+ vision: true,
66
+ audioGeneration: true,
67
+ audioAnalysis: true,
68
+ functionCalling: true,
69
+ streaming: true,
70
+ batchOperations: true,
71
+ maxContextLength: 128000,
72
+ supportedModelTypes: ['gpt-4o', 'gpt-4o-mini', 'text-embedding-3-large', 'text-embedding-3-small']
73
+ };
74
+
75
+ /* get _debugLog(): boolean {
76
+ return this.defaultSettings.debugLog;
77
+ } */
78
+
79
+ /* get defaultSystemPrompt(): string {
80
+ return this.defaultSettings.systemPrompt;
81
+ } */
82
+
83
+ override get defaultModel(): DyFM_OAI_Model | string {
84
+ return this.defaultSettings.useModel;
85
+ }
86
+
87
+ predefinedRequests: DyNTS_OAI_LLM_Predefined_Requests = DyNTS_OAI_LLMDefaultPredefined_Requests;
88
+
89
+ constructor(
90
+ set?: DyFM_OAI_Settings & { onCostEvent?: DyNTS_AI_CostEventCallback }
91
+ ) {
92
+ super();
93
+
94
+ DyNTS_global_settings.openAi_settings ??= DyNTS_OAI_global_settings;
95
+
96
+ this.openai = new OpenAI(
97
+ set?.config ??
98
+ {
99
+ organization: DyNTS_global_settings.env_settings.openAi.organization,
100
+ apiKey: DyNTS_global_settings.env_settings.openAi.apiKey,
101
+ project: DyNTS_global_settings.env_settings.openAi.project,
102
+ }
103
+ );
104
+
105
+ this.defaultSettings = set?.defaultSettings ??
106
+ DyNTS_OAI_global_settings.defaultSettings ??
107
+ new DyFM_OAI_CallSettings();
108
+
109
+ // FR-002 — opcionális per-call cost-event callback átadás a base-osztálynak
110
+ if (set?.onCostEvent) {
111
+ this.onCostEvent = set.onCostEvent;
112
+ }
113
+ }
114
+
115
+ /**
116
+ * Inicializálja az OpenAI client-et a megadott config-gal
117
+ *
118
+ * Reinitializes the OpenAI client with the provided config
119
+ */
120
+ setup(config: DyFM_AI_Config): void {
121
+ this.openai = new OpenAI(config);
122
+ }
123
+
124
+ /* readonly defaultLogReplacer: string = '...long-context...'; */
125
+
126
+ //#region Ask Questions
127
+
128
+ /**
129
+ * Asks the AI to answer a yes/no question
130
+ *
131
+ * Uses the {@link DyNTS_OAI_LLM_Predefined_Requests.yesNo.request} to ask the question,
132
+ * and the {@link DyNTS_OAI_LLM_Predefined_Requests.yesNo.upperCaseYes} to check the answer
133
+ */
134
+ async requestYesNo(
135
+ set: DyFM_AI_Message_Input
136
+ ): Promise<boolean> {
137
+ set.message += this.predefinedRequests.yesNo.request;
138
+
139
+ const answer = await this.requestSimpleMessage(set);
140
+
141
+ return this.convertAnswerToBoolean(answer);
142
+ }
143
+ /** the exact same as {@link requestYesNo} */
144
+ //yesNoQuestion = this.askYesNoQuestion;
145
+
146
+ /**
147
+ * Asks the AI to answer a percentage question
148
+ *
149
+ * Uses the {@link DyNTS_OAI_LLM_Predefined_Requests.percentageOnly} to ask the question
150
+ */
151
+ async requestPercentage(
152
+ set: DyFM_AI_Message_Input
153
+ ): Promise<number> {
154
+ set.message += this.predefinedRequests.percentageOnly;
155
+
156
+ const answer = await this.requestSimpleMessage(set);
157
+
158
+ return this.convertAnswerToNumber(answer, set.message);
159
+ }
160
+ /** the exact same as {@link requestPercentage} */
161
+ //percentageQuestion = this.askPercentageQuestion;
162
+
163
+ /**
164
+ * Asks the AI to select one of the options from the list
165
+ *
166
+ * Uses the {@link DyNTS_OAI_LLM_Predefined_Requests.selectRequest} to ask the question
167
+ */
168
+ /* async askSelectQuestion(
169
+ set: DyFM_AI_GenericSelect_Input<string>
170
+ ): Promise<string> {
171
+ // replace the {{DyNTS_selectOptions}} with the list of options
172
+ const selectQuestionAddition = this.predefinedRequests.selectRequest.replace(
173
+ '{{DyNTS_selectOptions}}',
174
+ this.getTextListAsText(set.selectFrom)
175
+ );
176
+ set.message += selectQuestionAddition;
177
+
178
+ let answer = await this.requestSimpleMessage(set);
179
+
180
+ answer = answer.toLocaleUpperCase();
181
+
182
+ for (const item of set.selectFrom) {
183
+ if (answer.includes(item.toLocaleUpperCase())) return item;
184
+ }
185
+
186
+ return null;
187
+ } */
188
+ /** the exact same as {@link askSelectQuestion} */
189
+ //selectQuestion = this.askSelectQuestion;
190
+
191
+ /**
192
+ * Asks the AI to select one of the options from the list
193
+ * Similar to {@link askSelectQuestion},
194
+ * but with a more generic approach that can be used for objects
195
+ *
196
+ * Uses the {@link DyNTS_OAI_LLM_Predefined_Requests.selectRequest} to ask the question
197
+ */
198
+ async requestSelect<T>(
199
+ set: DyFM_AI_GenericSelect_Input<T>
200
+ ): Promise<T | { unparsableResult: string }> {
201
+ const options: string[] = this.stringifySelectOptions(set.selectFrom);
202
+ const selectQuestionAddition = this.predefinedRequests.selectRequest.replace(
203
+ '{{DyNTS_selectOptions}}',
204
+ this.getTextListAsText(options)
205
+ );
206
+ set.message += selectQuestionAddition;
207
+
208
+ const answer = await this.requestSimpleMessage(set);
209
+
210
+ return this.convertAnswerToSelectOption<T>(answer, set.message, set.selectFrom);
211
+ }
212
+ /** the exact same as {@link requestSelect} */
213
+ //selectRequest = this.requestSelect;
214
+
215
+ /**
216
+ * Asks the AI to select one or more of the options from the list
217
+ *
218
+ * Uses the {@link DyNTS_OAI_LLM_Predefined_Requests.multiselect} to ask the question
219
+ */
220
+ async requestMultiselect<T>(
221
+ set: DyFM_AI_GenericMultiSelect_Input<T, DyFM_OAI_CallSettings>
222
+ ): Promise<T[] | { unparsableResult: string }> {
223
+ const options: string[] = this.stringifySelectOptions(set.options);
224
+ const selectQuestionAddition = this.predefinedRequests.multiselect.replace(
225
+ '{{DyNTS_selectOptions}}',
226
+ this.getTextListAsText(options)
227
+ );
228
+ set.message += selectQuestionAddition;
229
+
230
+ let answer = await this.requestSimpleMessage(set);
231
+
232
+ answer = answer.toLocaleUpperCase();
233
+
234
+ return this.convertAnswerToSelectOptions<T>(answer, set.message, set.options);
235
+ /* const result: T[] = [];
236
+
237
+ for (const item of options) {
238
+ if (answer.includes(item.toLocaleUpperCase())) {
239
+ const parsedItem: T | { unparsableResult: string } = DyFM_Object.safeParseJSON<T>(item);
240
+ if ((parsedItem as { unparsableResult: string }).unparsableResult) {
241
+ result.push(item as any);
242
+ } else {
243
+ result.push(parsedItem as T);
244
+ }
245
+
246
+ }
247
+ }
248
+
249
+ if (!result.length && !answer.trim().length) {
250
+ return { unparsableResult: answer };
251
+ }
252
+
253
+ return result; */
254
+ }
255
+ /** the exact same as {@link askMultipleSelectQuestionWithOptions} */
256
+ //multipleSelectQuestion = this.askMultipleSelectQuestionWithOptions;
257
+
258
+ /**
259
+ * Asks the AI to select one or more of the options from the list
260
+ * Similar to {@link askMultipleSelectQuestionWithOptions},
261
+ * but with a more generic approach that can be used for objects
262
+ *
263
+ * Uses the {@link DyNTS_OAI_LLM_Predefined_Requests.multiselect} to ask the question
264
+ */
265
+ /* async requestMultiselect<T>(
266
+ set: DyFM_AI_GenericMultiSelect_Input<T, DyFM_OAI_CallSettings>
267
+ ): Promise<T[]> {
268
+ const selectQuestionAddition = this.predefinedRequests.multiselect.replace(
269
+ '{{DyNTS_selectOptions}}',
270
+ this.getTextListAsText(set.options.map(item => `"${JSON.stringify(item)}"`))
271
+ );
272
+ set.message += selectQuestionAddition;
273
+
274
+ const answer = await this.requestSimpleMessage(set);
275
+
276
+ return DyFM_Object.safeParseList<T[]>(answer);
277
+ } */
278
+ /** the exact same as {@link requestMultipleSelect} */
279
+ //multipleSelectRequest = this.requestMultipleSelect;
280
+
281
+ /**
282
+ * Asks the AI to answer a question that must result a JSON object
283
+ *
284
+ * If the answer is not a valid JSON object, it will return { unparsableResult: answer }
285
+ *
286
+ * Uses the {@link DyNTS_OAI_LLM_Predefined_Requests.jsonRequest} to ask the question
287
+ *
288
+ * (uses {@link DyFM_safeParseJSON})
289
+ */
290
+ async requestJSON<T = any>(
291
+ set: DyFM_AI_Message_Input<DyFM_OAI_CallSettings>
292
+ ): Promise<T | { unparsableResult: string }> {
293
+ set.message += this.predefinedRequests.jsonRequest;
294
+
295
+ const answer = await this.requestSimpleMessage(set);
296
+
297
+ return this.convertAnswerToJSON<T>(answer, set.message);
298
+ }
299
+ /** the exact same as {@link askJSONQuestion} */
300
+ //jsonQuestion = this.askJSONQuestion;
301
+
302
+ /**
303
+ * Asks the AI to answer a question that must result a JSON object with specific key descriptions
304
+ *
305
+ * If the answer is not a valid JSON object, it will return { unparsableResult: answer }
306
+ *
307
+ * Uses the {@link DyNTS_OAI_LLM_Predefined_Requests.jsonRequestWithKeysDescription} to ask the question
308
+ *
309
+ * (uses {@link DyFM_safeParseJSON})
310
+ */
311
+ async requestJSONQuestionWithKeysDescription<T = any>(
312
+ set: DyFM_AI_JSONKeysDescription_Input<DyFM_OAI_CallSettings>
313
+ ): Promise<T | { unparsableResult: string }> {
314
+ const jsonRequestWithKeysDescription = this.predefinedRequests.jsonRequestWithKeysDescription.replace(
315
+ '{{DyNTS_jsonKeysDescription}}',
316
+ set.keysDescription
317
+ );
318
+ set.message += jsonRequestWithKeysDescription;
319
+
320
+ const answer = await this.requestSimpleMessage(set);
321
+
322
+ return this.convertAnswerToJSON<T>(answer, set.message);
323
+ }
324
+ /** the exact same as {@link requestJSONQuestionWithKeysDescription} */
325
+ //jsonQuestionWithKeysDescription = this.askJSONQuestionWithKeysDescription;
326
+
327
+ /**
328
+ * Asks the AI to answer a question that must result a JSON object with specific keys
329
+ *
330
+ * If the answer is not a valid JSON object, it will return { unparsableResult: answer }
331
+ *
332
+ * Uses the {@link DyNTS_OAI_LLM_Predefined_Requests.jsonRequestWithExactKeys} to ask the question
333
+ *
334
+ * (uses {@link DyFM_safeParseJSON})
335
+ */
336
+ async requestJSONWithExactKeys<T = any>(
337
+ set: DyFM_AI_JSONExactKeys_Input<DyFM_OAI_CallSettings>
338
+ ): Promise<T | { unparsableResult: string }> {
339
+ const jsonRequestWithExactKeys = this.predefinedRequests.jsonRequestWithExactKeys.replace(
340
+ '{{DyNTS_jsonKeys}}',
341
+ this.getTextListAsText(set.keys)
342
+ );
343
+ set.message += jsonRequestWithExactKeys;
344
+
345
+ const answer = await this.requestSimpleMessage(set);
346
+
347
+ return this.convertAnswerToJSON<T>(answer, set.message);
348
+ }
349
+ /** the exact same as {@link askJSONQuestionWithExactKeys} */
350
+ //jsonQuestionWithExactKeys = this.askJSONQuestionWithExactKeys;
351
+
352
+ /**
353
+ * Asks the AI to answer a question that must result a list of strings
354
+ *
355
+ * If the answer is not a valid JSON list, it will return the answer as a single item in an array
356
+ *
357
+ * Uses the {@link DyNTS_OAI_LLM_Predefined_Requests.listRequest} to ask the question
358
+ *
359
+ * (uses {@link DyFM_Object.safeParseList})
360
+ */
361
+ /* async requestList(
362
+ set: DyFM_AI_Base_Input<DyFM_OAI_CallSettings>
363
+ ): Promise<string[]> {
364
+ set.message += this.predefinedRequests.listRequest;
365
+
366
+ const answer = await this.requestSimpleMessage(set);
367
+
368
+ return this.convertAnswerToList<string>(answer, set.message);
369
+ } */
370
+ /** the exact same as {@link askListQuestion} */
371
+ //listQuestion = this.askListQuestion;
372
+ /** the exact same as {@link askListQuestion} */
373
+ //listRequest = this.askListQuestion;
374
+
375
+ async requestList<T = any>(
376
+ set: DyFM_AI_Message_Input<DyFM_OAI_CallSettings>
377
+ ): Promise<T[] | { unparsableResult: string }> {
378
+ set.message += this.predefinedRequests.listRequest;
379
+
380
+ const answer = await this.requestSimpleMessage(set);
381
+
382
+ return this.convertAnswerToList<T>(answer, set.message);
383
+ }
384
+
385
+ /**
386
+ * Asks the AI to answer a question
387
+ *
388
+ * Uses the {@link resolveSimpleUserMessage}
389
+ */
390
+ async requestSimpleMessage(
391
+ set: DyFM_AI_Message_Input<DyFM_OAI_CallSettings>
392
+ ): Promise<string> {
393
+ this.logQuestion(set);
394
+
395
+ set.conversation ??= [];
396
+ set.conversation.push({ role: DyFM_AI_MessageRole.user, content: set.message });
397
+
398
+ const answer: string = await this.resolveMessage({
399
+ ...set,
400
+ conversation: set.conversation,
401
+ getFullResponse: false,
402
+ }) as string;
403
+
404
+ this.logAnswer(answer);
405
+
406
+ return answer;
407
+ }
408
+ /** the exact same as {@link requestSimpleMessage} */
409
+ //answer = this.askQuestion;
410
+ /** the exact same as {@link requestSimpleMessage} */
411
+ //getQuestionAnswer = this.askQuestion;
412
+ /** the exact same as {@link requestSimpleMessage} */
413
+ //simpleQuestion = this.askQuestion;
414
+ /** the exact same as {@link requestSimpleMessage} */
415
+ //askSimpleQuestion = this.askQuestion;
416
+
417
+ /**
418
+ * Resolves a simple user message and returns the answer as string
419
+ *
420
+ * Uses the {@link resolveMessage}
421
+ */
422
+ /* async resolveSimpleUserMessage(
423
+ set: DyFM_AI_Base_Input<DyFM_OAI_CallSettings>
424
+ ): Promise<string> {
425
+ return this.resolveMessage({
426
+ conversation: [{ role: DyFM_AI_MessageRole.user, content: set.message }],
427
+ issuer: set.issuer ?? 'unknown',
428
+ settings: set.settings ?? this.defaultSettings,
429
+ getFullResponse: false,
430
+ }) as Promise<string>;
431
+ } */
432
+ /** the exact same as {@link resolveSimpleUserMessage} */
433
+ //simpleUserMessage = this.resolveSimpleUserMessage;
434
+
435
+ //#endregion
436
+
437
+ /**
438
+ * Resolves a conversation
439
+ *
440
+ * Uses the {@link getMessageCreateInput}
441
+ */
442
+ async resolveMessage(
443
+ set: DyFM_AI_ConversationBase_Input<DyFM_OAI_CallSettings>
444
+ /* {
445
+ conversation: [{ role: DyFM_AI_MessageRole.user, content: set.message }],
446
+ issuer: set.issuer ?? 'unknown',
447
+ settings: set.settings ?? this.defaultSettings,
448
+ getFullResponse: false,
449
+ } */
450
+ /* {
451
+ conversation: DyFM_AI_Message[],
452
+ issuer: string,
453
+ settings?: DyFM_OAI_CallSettings,
454
+ getFullResponse?: boolean
455
+ } */
456
+ ): Promise<string | ChatCompletion> {
457
+ try {
458
+ // FR-002 — callType meghatározás a unshift ELŐTT (a system-message hozzáadás
459
+ // után már 2+ message lenne, ami a "chat" kategóriát mindig kiváltja).
460
+ // 1 user-message → 'llm-completion'; több → 'llm-chat'. A 'llm-tool-use'
461
+ // categoria a tools paraméterek bevezetésekor kerül használatba (jövőbeli FR).
462
+ const userMessageCount: number = set.conversation.length;
463
+
464
+ set.conversation.unshift(this.getDefaultSystemMessage(set.settings));
465
+
466
+ // FR-002 — per-call cost-event timing: a tényleges API-call latency mérve
467
+ const input: ChatCompletionCreateParamsBase = this.getMessageCreateInput(set);
468
+ const start: number = Date.now();
469
+ const result: ChatCompletion = await this.openai.chat.completions.create(
470
+ input,
471
+ ) as ChatCompletion;
472
+ const durationMs: number = Date.now() - start;
473
+
474
+ // FR-002 — cost-event emit (safe, no-op ha nincs onCostEvent callback)
475
+ this.emitCostEvent({
476
+ callType: userMessageCount === 1 ? 'llm-completion' : 'llm-chat',
477
+ provider: this.aiProvider,
478
+ model: String(input.model ?? this.defaultModel),
479
+ tokensUsed: {
480
+ input: result.usage?.prompt_tokens ?? 0,
481
+ output: result.usage?.completion_tokens ?? 0,
482
+ total: result.usage?.total_tokens ?? 0,
483
+ },
484
+ durationMs: durationMs,
485
+ issuer: set.issuer,
486
+ timestamp: new Date(),
487
+ });
488
+
489
+ if (set.getFullResponse) {
490
+ return result;
491
+ }
492
+
493
+ return result.choices[0].message.content;
494
+ } catch (error) {
495
+ throw new DyFM_Error({
496
+ ...this.getDefaultErrorSettings('resolveConversation', error, set.issuer),
497
+
498
+ errorCode: `${DyNTS_global_settings.systemShortCodeName}|DyNTS-OLSB-RM0`,
499
+ });
500
+ }
501
+ }
502
+
503
+ /**
504
+ * Creates a message create input
505
+ *
506
+ * Uses the {@link getDefaultSystemMessage}
507
+ */
508
+ protected getMessageCreateInput(
509
+ set: {
510
+ conversation: DyFM_AI_Message[],
511
+ issuer: string,
512
+ settings?: DyFM_OAI_CallSettings
513
+ }
514
+ ): ChatCompletionCreateParamsBase {
515
+ try {
516
+ const settings = set.settings ?? this.defaultSettings;
517
+
518
+ const useModel: string = String(settings?.useModel ?? this.defaultModel);
519
+
520
+ const result: ChatCompletionCreateParamsBase = {
521
+ model: useModel,
522
+ messages: set.conversation as ChatCompletionMessageParam[],
523
+ };
524
+
525
+ // FR-048 §3: a sampling-parameterek (+ max_completion_tokens) a per-model `settingsSchema` szerint
526
+ // kerulnek ra (drop/clamp). KORABBAN ez a NEM-tool ag FELTETEL NELKUL kuldte a sampling-parametereket,
527
+ // ezert reasoning modellen (GPT-5.x / o-series) 400-zal elhasalt volna — ezt itt zarjuk le.
528
+ this.applySamplingParams({ input: result, useModel: useModel, settings: set.settings });
529
+
530
+ return result;
531
+ } catch (error) {
532
+ throw new DyFM_Error({
533
+ ...this.getDefaultErrorSettings('getMessageCreateSettings', error, set.issuer),
534
+
535
+ errorCode: `${DyNTS_global_settings.systemShortCodeName}|DyNTS-OLSB-RC0`,
536
+ });
537
+ }
538
+ }
539
+
540
+ /**
541
+ * Teszteli a kapcsolatot az OpenAI API-val
542
+ *
543
+ * Tests the connection to OpenAI API
544
+ */
545
+ async testConnection(issuer: string): Promise<boolean> {
546
+ try {
547
+ await this.openai.models.list();
548
+ return true;
549
+ } catch (error) {
550
+ DyFM_Log.error('DyNTS_OAI_LLM_ServiceBase', 'testConnection', 'Connection test failed', {
551
+ error,
552
+ issuer
553
+ });
554
+ return false;
555
+ }
556
+ }
557
+
558
+ //#region Function calling (tool use) — FR-047
559
+
560
+ /** OpenAI tool-kepes modell-registry (a base capability-precheck-jehez). */
561
+ protected override getModelRegistry(): DyFM_AI_ModelInfo[] {
562
+ return DyFM_OAI_Models;
563
+ }
564
+
565
+ /**
566
+ * Egy OpenAI-kor a tool-loop-ban: felepiti a natív request-et (system + a teljes beszelgetes
567
+ * tool-uzenetekkel + tools), meghivja az API-t, es normalizalt DyFM_AI_LLM_Response-t ad vissza
568
+ * (toolCalls feltoltve, ha a model tool-t akar hivni). FR-002 cost-event: 'llm-tool-use'.
569
+ */
570
+ protected override async callModelWithTools(
571
+ set: {
572
+ conversation: DyFM_AI_Message[];
573
+ tools: DyFM_AI_Tool[];
574
+ settings?: DyFM_OAI_CallSettings;
575
+ issuer: string;
576
+ }
577
+ ): Promise<DyFM_AI_LLM_Response> {
578
+ try {
579
+ const settings: DyFM_OAI_CallSettings = set.settings ?? this.defaultSettings;
580
+
581
+ const messages: ChatCompletionMessageParam[] = [
582
+ this.toOpenAIMessage(this.getDefaultSystemMessage(settings)),
583
+ ...set.conversation.map((message: DyFM_AI_Message) => this.toOpenAIMessage(message)),
584
+ ];
585
+
586
+ const useModel: DyFM_OAI_Model | string = settings?.useModel ?? this.defaultModel;
587
+
588
+ const input: ChatCompletionCreateParamsBase = {
589
+ model: useModel,
590
+ messages: messages,
591
+ };
592
+
593
+ // FR-048 §3: a sampling-parameterek + `max_completion_tokens` a per-model `settingsSchema` szerint
594
+ // kerulnek ra. A korabbi lokalis `isReasoningModel`-drop atmeneti fallback-kent bent maradt a
595
+ // helper-ben (l. `applySamplingParams`), amig a schema ki nem kerul a fsm-bol — igy nincs regresszio.
596
+ this.applySamplingParams({ input: input, useModel: String(useModel), settings: set.settings });
597
+
598
+ // Ures tools-tomb -> 400 ("tools must have minimum length 1"). Csak akkor kuldjuk, ha van tool.
599
+ if (set.tools.length) {
600
+ input.tools = set.tools.map((tool: DyFM_AI_Tool) => this.toOpenAITool(tool));
601
+ input.tool_choice = 'auto';
602
+ }
603
+
604
+ const start: number = Date.now();
605
+ const result: ChatCompletion = await this.openai.chat.completions.create(input) as ChatCompletion;
606
+ const durationMs: number = Date.now() - start;
607
+
608
+ const message = result.choices[0].message;
609
+
610
+ const toolCalls: DyFM_AI_ToolCall[] = (message.tool_calls ?? [])
611
+ .filter((toolCall) => toolCall.type === 'function')
612
+ .map((toolCall) => ({
613
+ id: toolCall.id,
614
+ name: toolCall.function.name,
615
+ arguments: this.parseToolArguments(toolCall.function.arguments),
616
+ }));
617
+
618
+ this.emitCostEvent({
619
+ callType: 'llm-tool-use',
620
+ provider: this.aiProvider,
621
+ model: String(input.model ?? this.defaultModel),
622
+ tokensUsed: {
623
+ input: result.usage?.prompt_tokens ?? 0,
624
+ output: result.usage?.completion_tokens ?? 0,
625
+ total: result.usage?.total_tokens ?? 0,
626
+ },
627
+ durationMs: durationMs,
628
+ issuer: set.issuer,
629
+ timestamp: new Date(),
630
+ });
631
+
632
+ return {
633
+ content: message.content ?? '',
634
+ toolCalls: toolCalls.length ? toolCalls : undefined,
635
+ usage: result.usage ? {
636
+ promptTokens: result.usage.prompt_tokens,
637
+ completionTokens: result.usage.completion_tokens,
638
+ totalTokens: result.usage.total_tokens,
639
+ } : undefined,
640
+ model: result.model,
641
+ finishReason: result.choices[0].finish_reason,
642
+ stopReason: result.choices[0].finish_reason,
643
+ rawResponse: result,
644
+ };
645
+ } catch (error) {
646
+ throw new DyFM_Error({
647
+ ...this.getDefaultErrorSettings('callModelWithTools', error, set.issuer),
648
+ errorCode: `${DyNTS_global_settings.systemShortCodeName}|DyNTS-OLSB-TC0`,
649
+ });
650
+ }
651
+ }
652
+
653
+ /** Agnosztikus tool-definicio → OpenAI ChatCompletionTool. */
654
+ protected toOpenAITool(tool: DyFM_AI_Tool): ChatCompletionTool {
655
+ return {
656
+ type: 'function',
657
+ function: {
658
+ name: tool.name,
659
+ description: tool.description,
660
+ parameters: tool.parameters as Record<string, unknown>,
661
+ },
662
+ };
663
+ }
664
+
665
+ /**
666
+ * Agnosztikus DyFM_AI_Message → OpenAI ChatCompletionMessageParam.
667
+ * Kezeli a tool-result (role:'tool') es a tool-hivo assistant (tool_calls) uzeneteket is.
668
+ */
669
+ protected toOpenAIMessage(message: DyFM_AI_Message): ChatCompletionMessageParam {
670
+ if (message.role === DyFM_AI_MessageRole.tool) {
671
+ return {
672
+ role: 'tool',
673
+ tool_call_id: message.toolCallId ?? '',
674
+ content: message.content,
675
+ };
676
+ }
677
+
678
+ if (message.role === DyFM_AI_MessageRole.assistant && message.toolCalls?.length) {
679
+ return {
680
+ role: 'assistant',
681
+ content: message.content || null,
682
+ tool_calls: message.toolCalls.map((toolCall: DyFM_AI_ToolCall) => ({
683
+ id: toolCall.id,
684
+ type: 'function' as const,
685
+ function: {
686
+ name: toolCall.name,
687
+ arguments: JSON.stringify(toolCall.arguments),
688
+ },
689
+ })),
690
+ };
691
+ }
692
+
693
+ return {
694
+ role: message.role as 'system' | 'user' | 'assistant',
695
+ content: message.content,
696
+ };
697
+ }
698
+
699
+ /** Tool-argumentumok parse-olasa (OpenAI JSON-string → object); hibas JSON eseten ures object. */
700
+ protected parseToolArguments(raw: string): Record<string, unknown> {
701
+ try {
702
+ return JSON.parse(raw || '{}');
703
+ } catch {
704
+ return {};
705
+ }
706
+ }
707
+
708
+ /**
709
+ * ⚠️ **TUL TAG HEURISZTIKA — csak FALLBACK, ne ez legyen az igazsag forrasa.**
710
+ *
711
+ * Eredetileg (FR-047) azt felteteleztuk, hogy MINDEN `gpt-5.x` + o-series modell elutasitja a
712
+ * sampling-parametereket. **Live capture 2026-07-21 ezt MEGCAFOLTA:**
713
+ * - elutasit: `gpt-5.5`, `o3-mini`, `o4-mini`
714
+ * - **ELFOGADJA**: `gpt-5.4-mini`, `gpt-5.4-nano` (HTTP 200, `reasoning_tokens: 0`)
715
+ *
716
+ * Vagyis erre a ket modellre ez a regex **NEMAN kidobna ervenyes parametereket** — halkan rontva a
717
+ * kimenetet. Ezert az `applySamplingParams` a **registry `settingsSchema`-t reszesiti elonyben**, es ezt
718
+ * a heurisztikat CSAK akkor hasznalja, ha az adott modellhez nincs schema (regi fsm-verzio / ismeretlen
719
+ * modell). Amint a populalt schema mindenhol elerheto, ez a metodus elhagyhato.
720
+ *
721
+ * Reszletek: `fdp-documentations/plans/ai-mocking-system/CAPTURE-FINDINGS.md` §1.
722
+ */
723
+ protected isReasoningModel(modelId: string): boolean {
724
+ return /^gpt-5/.test(modelId) || /^o[0-9]/.test(modelId);
725
+ }
726
+
727
+ /**
728
+ * FR-048 §3 — feloldja az agnosztikus sampling-settingseket (hivas-settings > default), atengedi a
729
+ * per-model reconciler-en (`settingsSchema` szerinti drop/clamp), majd RAKEPZI a natív OpenAI mezokre.
730
+ * Csak azok a natív mezok kerulnek az input-ra, amik a reconcile utan MEGMARADTAK.
731
+ *
732
+ * Ezzel a "melyik modell mit fogad el" tudas a registry `settingsSchema`-jaban (SSoT) el, nem
733
+ * provider-translatoronkent duplikalva.
734
+ *
735
+ * **Atmeneti fallback (szandekos):** ha a registry-bejegyzesnek MEG NINCS `settingsSchema`-ja (regebbi
736
+ * fsm-verzio, vagy registry-ben nem szereplo modell), a korabbi lokalis `isReasoningModel`-drop marad
737
+ * ervenyben — igy a FR-047 `514153f` fix NEM regresszal, amig a schema ki nem kerul a fsm-bol. Amint a
738
+ * schema mindenhol elerheto, ez a fallback (es az `isReasoningModel`) elhagyhato.
739
+ */
740
+ protected applySamplingParams(
741
+ set: {
742
+ input: ChatCompletionCreateParamsBase,
743
+ useModel: string,
744
+ settings?: DyFM_OAI_CallSettings,
745
+ }
746
+ ): void {
747
+ const source: DyFM_OAI_CallSettings = set.settings ?? this.defaultSettings;
748
+
749
+ const resolved: DyFM_OAI_CallSettings = {
750
+ ...source,
751
+ useModel: set.useModel,
752
+ temperature: DyFM_notNull(source?.temperature) ? source.temperature : this.defaultSettings.temperature,
753
+ maxTokens: DyFM_notNull(source?.maxTokens) ? source.maxTokens : this.defaultSettings.maxTokens,
754
+ topP: DyFM_notNull(source?.topP) ? source.topP : this.defaultSettings.topP,
755
+ frequencyPenalty: DyFM_notNull(source?.frequencyPenalty) ?
756
+ source.frequencyPenalty : this.defaultSettings.frequencyPenalty,
757
+ presencePenalty: DyFM_notNull(source?.presencePenalty) ?
758
+ source.presencePenalty : this.defaultSettings.presencePenalty,
759
+ };
760
+
761
+ const final: DyFM_OAI_CallSettings = this.reconcileCallSettings(set.useModel, resolved);
762
+
763
+ const modelInfo: DyFM_AI_ModelInfo = DyFM_AI_ModelRegistry_Util.findModelInfo(
764
+ this.getModelRegistry(),
765
+ set.useModel
766
+ );
767
+ const dropSampling: boolean = !modelInfo?.settingsSchema && this.isReasoningModel(set.useModel);
768
+
769
+ if (DyFM_notNull(final?.maxTokens)) {
770
+ set.input.max_completion_tokens = final.maxTokens;
771
+ }
772
+
773
+ if (dropSampling) {
774
+ return;
775
+ }
776
+
777
+ if (DyFM_notNull(final?.temperature)) {
778
+ set.input.temperature = final.temperature;
779
+ }
780
+ if (DyFM_notNull(final?.topP)) {
781
+ set.input.top_p = final.topP;
782
+ }
783
+ if (DyFM_notNull(final?.frequencyPenalty)) {
784
+ set.input.frequency_penalty = final.frequencyPenalty;
785
+ }
786
+ if (DyFM_notNull(final?.presencePenalty)) {
787
+ set.input.presence_penalty = final.presencePenalty;
788
+ }
789
+ }
790
+
791
+ //#endregion
792
+
793
+ // async askJSONListQuestion(
794
+ // question: string,
795
+ // issuer: string,
796
+ // settings?: DyFM_OAI_CallSettings,
797
+ // debugLog?: boolean,
798
+ // /** this is used to readably replace too long contents to eg '...' in logs */
799
+ // replaceThisInLog?: string,
800
+ // ): Promise<{ key: string, input: string }[]> {
801
+ // question += this.predefinedRequests.jsonListRequest;
802
+ //
803
+ // const answer = await this.askQuestion(question, issuer, settings, debugLog, replaceThisInLog);
804
+ //
805
+ // return DyFM_Object.safeParseList<{ key: string, input: string }[]>(answer);
806
+ // }
807
+
808
+ //#region Tools
809
+
810
+ /**
811
+ * Logs the question if the debugLog is true
812
+ */
813
+ /* protected logQuestion(
814
+ set: DyFM_AI_Base_Input<DyFM_OAI_CallSettings>
815
+ ): void {
816
+ if (set.settings?.debugLog ?? this._debugLog) {
817
+ console.log('\n - ', set.message);
818
+ }
819
+ } */
820
+
821
+ /**
822
+ * olvasható mondatszerű-listaszerű formába teszi a listaelemeket
823
+ * pl.: ['a', 'b', 'c'] -> '"a", "b" or "c"'
824
+ */
825
+ /* getTextListAsText(list: string[]): string {
826
+ list = list.filter(item => item?.trim()).map(item => `"${item}"`);
827
+
828
+ //list = list.map(item => item.toLocaleLowerCase());
829
+
830
+ list.push(list.pop() + ' or ' + list.pop());
831
+
832
+ return list.join(', ');
833
+ } */
834
+
835
+ /* protected getDefaultSystemMessage(settings: DyFM_OAI_CallSettings): DyFM_AI_Message {
836
+ return {
837
+ role: DyFM_AI_MessageRole.system,
838
+ content: settings?.systemPrompt || this.defaultSystemPrompt,
839
+ };
840
+ } */
841
+
842
+ /* protected validateConversation(conversation: DyFM_AI_Message[]): void {
843
+ conversation.forEach((message: DyFM_AI_Message, index: number) => {
844
+ if (!message.role) {
845
+ throw new DyFM_Error({
846
+ message: `Message has no role at index ${index}`,
847
+ additionalContent: {
848
+ invalidMessage: message,
849
+ conversation: conversation,
850
+ }
851
+ });
852
+ }
853
+
854
+ if (!message.content) {
855
+ throw new DyFM_Error({
856
+ message: `Message has no content at index ${index}`,
857
+ additionalContent: {
858
+ invalidMessage: message,
859
+ conversation: conversation,
860
+ }
861
+ });
862
+ }
863
+ });
864
+ } */
865
+
866
+ /* protected getDefaultErrorSettings(
867
+ fnName: string,
868
+ error: DyFM_AnyError,
869
+ issuer: string
870
+ ): DyFM_Error_Settings {
871
+ return {
872
+ status: (error as DyFM_Error)?.___status ?? 500,
873
+ message: (error as Error)?.message ??
874
+ (error as DyFM_Error)?._message ??
875
+ `${fnName} was UNSUCCESSFUL (${DyNTS_global_settings.systemShortCodeName})`,
876
+ addECToUserMsg: !(error as DyFM_Error)?.__userMessage,
877
+ userMessage: (error as DyFM_Error)?.__userMessage ?? this.defaultErrorUserMsg,
878
+ issuer: issuer,
879
+ issuerService: this.constructor?.name,
880
+ error: error,
881
+ };
882
+ } */
883
+
884
+ //#endregion
885
+
886
+ //#region Provider Methods (from DyNTS_AI_Provider_ServiceBase)
887
+
888
+ //#endregion
889
+
890
+ //#region Abstract LLM Methods (from DyNTS_AI_LLM_ServiceBase)
891
+
892
+ /**
893
+ * Meghívja az LLM-et system és user message-szel
894
+ *
895
+ * Calls the LLM with system and user messages
896
+ */
897
+ /* async callLLM(
898
+ systemMessage: string,
899
+ userMessage: string,
900
+ settings?: DyFM_AI_CallSettings,
901
+ issuer?: string
902
+ ): Promise<string> {
903
+ const result = await this.resolveMessage({
904
+ conversation: [
905
+ { role: DyFM_AI_MessageRole.system, content: systemMessage },
906
+ { role: DyFM_AI_MessageRole.user, content: userMessage }
907
+ ],
908
+ issuer: issuer ?? 'system',
909
+ settings: this.convertToOAISettings(settings),
910
+ getFullResponse: false
911
+ });
912
+
913
+ if (typeof result === 'string') {
914
+ return result;
915
+ }
916
+ return '';
917
+ } */
918
+
919
+ /**
920
+ * Meghívja az LLM-et üzenet history-val
921
+ *
922
+ * Calls the LLM with message history
923
+ */
924
+ /* async callLLMWithHistory(
925
+ messages: DyFM_AI_Message[],
926
+ settings?: DyFM_AI_CallSettings,
927
+ issuer?: string
928
+ ): Promise<string> {
929
+ const result = await this.resolveMessage({
930
+ conversation: messages,
931
+ issuer: issuer ?? 'system',
932
+ settings: this.convertToOAISettings(settings),
933
+ getFullResponse: false
934
+ });
935
+
936
+ if (typeof result === 'string') {
937
+ return result;
938
+ }
939
+ return '';
940
+ } */
941
+
942
+ /**
943
+ * Meghívja az LLM-et és visszaadja a raw response-t
944
+ *
945
+ * Calls the LLM and returns raw response
946
+ */
947
+ /* async callLLMRaw(
948
+ messages: DyFM_AI_Message[],
949
+ settings?: DyFM_AI_CallSettings,
950
+ issuer?: string
951
+ ): Promise<DyFM_AI_LLM_Response> {
952
+ const result = await this.resolveMessage({
953
+ conversation: messages,
954
+ issuer: issuer ?? 'system',
955
+ settings: this.convertToOAISettings(settings),
956
+ getFullResponse: true
957
+ }) as ChatCompletion;
958
+
959
+ return {
960
+ content: result.choices[0].message.content,
961
+ usage: result.usage ? {
962
+ promptTokens: result.usage.prompt_tokens,
963
+ completionTokens: result.usage.completion_tokens,
964
+ totalTokens: result.usage.total_tokens
965
+ } : undefined,
966
+ model: result.model,
967
+ finishReason: result.choices[0].finish_reason,
968
+ rawResponse: result
969
+ };
970
+ } */
971
+
972
+ /**
973
+ * Küld egy egyszerű üzenetet az LLM-nek
974
+ *
975
+ * Sends a simple message to the LLM
976
+ */
977
+ /* async sendMessage(set: DyFM_AI_Base_Input<DyFM_OAI_CallSettings>): Promise<string> {
978
+ return await this.resolveSimpleUserMessage({
979
+ message: set.message,
980
+ issuer: set.issuer,
981
+ settings: set.settings ? this.convertToOAISettings(set.settings) : undefined
982
+ });
983
+ } */
984
+
985
+ /**
986
+ * Kérdez egy kérdést és JSON választ vár exact keys-szel
987
+ *
988
+ * Asks a question and expects JSON response with exact keys
989
+ */
990
+ /* async requestJSONWithExactKeys<T = any>(
991
+ set: DyFM_AI_JSONExactKeys_Input
992
+ ): Promise<T> {
993
+ const answer = await this.askJSONQuestionWithExactKeys({
994
+ question: set.question,
995
+ keys: set.keys,
996
+ issuer: set.issuer,
997
+ settings: set.settings ? this.convertToOAISettings(set.settings) : undefined,
998
+ debugLog: set.debugLog,
999
+ replaceThisInLog: set.replaceThisInLog
1000
+ });
1001
+
1002
+ if ('unparsableResult' in answer) {
1003
+ throw new DyFM_Error({
1004
+ message: 'Invalid JSON response from AI',
1005
+ additionalContent: { answer },
1006
+ userMessage: 'Failed to parse AI response'
1007
+ });
1008
+ }
1009
+
1010
+ return answer as T;
1011
+ } */
1012
+
1013
+ /**
1014
+ * Kérdez egy kérdést és több választ vár
1015
+ *
1016
+ * Asks a question and expects multiple responses
1017
+ */
1018
+ /* async requestMultiselect<T = any>(
1019
+ set: DyFM_AI_GenericMultiSelect_Input<T>
1020
+ ): Promise<T[]> {
1021
+ const answer = await this.requestMultipleSelect({
1022
+ question: set.message,
1023
+ options: set.options,
1024
+ issuer: set.issuer,
1025
+ settings: set.settings ? this.convertToOAISettings(set.settings) : undefined,
1026
+ debugLog: set.debugLog,
1027
+ replaceThisInLog: set.replaceThisInLog
1028
+ });
1029
+
1030
+ return answer as T[];
1031
+ } */
1032
+
1033
+ /**
1034
+ * Konvertálja a generic AI settings-et OpenAI specifikus settings-re
1035
+ *
1036
+ * Converts generic AI settings to OpenAI-specific settings
1037
+ */
1038
+ /* private convertToOAISettings(settings?: DyFM_AI_CallSettings): DyFM_OAI_CallSettings {
1039
+ if (!settings) {
1040
+ return undefined;
1041
+ }
1042
+
1043
+ const oaiSettings = new DyFM_OAI_CallSettings();
1044
+
1045
+ if (settings.systemPrompt !== undefined) {
1046
+ oaiSettings.systemPrompt = settings.systemPrompt;
1047
+ }
1048
+
1049
+ if (settings.useModel !== undefined) {
1050
+ oaiSettings.useModel = settings.useModel as DyFM_OAI_Model;
1051
+ }
1052
+
1053
+ if (settings.project !== undefined) {
1054
+ oaiSettings.project = settings.project;
1055
+ }
1056
+
1057
+ if (settings.debugLog !== undefined) {
1058
+ oaiSettings.debugLog = settings.debugLog;
1059
+ }
1060
+
1061
+ if (settings.temperature !== undefined) {
1062
+ oaiSettings.temperature = settings.temperature;
1063
+ }
1064
+
1065
+ if (settings.maxTokens !== undefined) {
1066
+ oaiSettings.maxTokens = settings.maxTokens;
1067
+ }
1068
+
1069
+ if (settings.topP !== undefined) {
1070
+ oaiSettings.topP = settings.topP;
1071
+ }
1072
+
1073
+ if (settings.frequencyPenalty !== undefined) {
1074
+ oaiSettings.frequencyPenalty = settings.frequencyPenalty;
1075
+ }
1076
+
1077
+ if (settings.presencePenalty !== undefined) {
1078
+ oaiSettings.presencePenalty = settings.presencePenalty;
1079
+ }
1080
+
1081
+ return oaiSettings;
1082
+ } */
1083
+
1084
+ //#endregion
1085
+ }