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