@futdevpro/nts-dynamo 1.15.15 → 1.15.17
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/version-bump.config.json +5 -0
- package/.github/workflows/main.yml +426 -393
- package/.husky/pre-commit +1 -0
- package/.vscode/settings.json +10 -10
- package/HOWTO.md +15 -15
- package/LICENSE +21 -21
- package/__documentations/2026-04-28-logs-module.md +49 -0
- package/__documentations/nts-integration-tests-2026-03-17.md +26 -26
- package/_specifications/BACKLOG.md +50 -50
- package/_specifications/TODO.md +15 -15
- package/_specifications/agent.md +138 -138
- package/eslint.config.js +3 -3
- package/nodemon.json +24 -24
- package/package.json +343 -362
- package/pipeline.cicd.config.json +152 -0
- package/pnpm-workspace.yaml +2 -0
- 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 +526 -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 +108 -108
- 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 +240 -240
- package/src/_modules/ai/_modules/open-ai/_services/oai-embedding.control-service.ts +98 -98
- 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 +615 -615
- package/src/_modules/ai/_modules/open-ai/_services/oai-llm.service-base.spec.ts +437 -437
- package/src/_modules/ai/_modules/open-ai/_services/oai-llm.service-base.ts +833 -833
- 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 +332 -332
- package/src/_modules/ai/_services/ai-provider.service-base.spec.ts +79 -79
- package/src/_modules/ai/_services/ai-provider.service-base.ts +29 -29
- 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 +19 -19
- 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 +345 -345
- package/src/_modules/local-vector-search/_services/lvs-local-vector-search.data-service.ts +330 -330
- 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 +230 -230
- package/src/_modules/server/errors/errors.control-service.ts +69 -69
- package/src/_modules/server/errors/errors.controller.spec.ts +165 -165
- package/src/_modules/server/errors/errors.controller.ts +270 -270
- 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 +516 -516
- package/src/_modules/server/server-status/server-status.control-service.ts +336 -336
- package/src/_modules/server/server-status/server-status.controller.spec.ts +156 -156
- 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 +493 -493
- package/src/_services/base/data.service.ts +2525 -2525
- 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 +94 -94
- package/tsconfig.app.json +12 -12
- package/tsconfig.json +42 -42
- package/build/_modules/logs/get-logs-routing-module.util.d.ts +0 -19
- package/build/_modules/logs/get-logs-routing-module.util.d.ts.map +0 -1
- package/build/_modules/logs/get-logs-routing-module.util.js +0 -32
- package/build/_modules/logs/get-logs-routing-module.util.js.map +0 -1
- package/build/_modules/logs/index.d.ts +0 -4
- package/build/_modules/logs/index.d.ts.map +0 -1
- package/build/_modules/logs/index.js +0 -10
- package/build/_modules/logs/index.js.map +0 -1
- package/build/_modules/logs/log-buffer.service.d.ts +0 -38
- package/build/_modules/logs/log-buffer.service.d.ts.map +0 -1
- package/build/_modules/logs/log-buffer.service.js +0 -97
- package/build/_modules/logs/log-buffer.service.js.map +0 -1
- package/build/_modules/logs/logs.controller.d.ts +0 -27
- package/build/_modules/logs/logs.controller.d.ts.map +0 -1
- package/build/_modules/logs/logs.controller.js +0 -90
- package/build/_modules/logs/logs.controller.js.map +0 -1
- package/build/_modules/logs/logs.service.d.ts +0 -40
- package/build/_modules/logs/logs.service.d.ts.map +0 -1
- package/build/_modules/logs/logs.service.js +0 -97
- package/build/_modules/logs/logs.service.js.map +0 -1
- package/src/_modules/logs/get-logs-routing-module.util.ts +0 -36
- package/src/_modules/logs/index.ts +0 -3
- package/src/_modules/logs/log-buffer.service.ts +0 -101
- package/src/_modules/logs/logs.controller.ts +0 -109
- package/src/_modules/logs/logs.service.ts +0 -100
|
@@ -1,462 +1,462 @@
|
|
|
1
|
-
|
|
2
|
-
import { DyNTS_OAI_LLMChat_ServiceBase } from './oai-llm-chat.service-base';
|
|
3
|
-
import { DyFM_OAI_Settings, DyFM_OAI_Model, DyFM_OAI_CallSettings } from '@futdevpro/fsm-dynamo/ai/open-ai';
|
|
4
|
-
import { DyFM_AI_MessageRole, DyFM_AI_Message } from '@futdevpro/fsm-dynamo/ai';
|
|
5
|
-
import { DyFM_Error, DyFM_Log, DyFM_Object, DyFM_Array } from '@futdevpro/fsm-dynamo';
|
|
6
|
-
import { ChatCompletion } from 'openai/resources';
|
|
7
|
-
|
|
8
|
-
describe('| DyNTS_OAI_LLMChat_ServiceBase', () => {
|
|
9
|
-
let service: DyNTS_OAI_LLMChat_ServiceBase;
|
|
10
|
-
let mockOpenAI: any;
|
|
11
|
-
let mockChatCompletionsCreate: jasmine.Spy;
|
|
12
|
-
|
|
13
|
-
const mockSettings: DyFM_OAI_Settings = {
|
|
14
|
-
config: {
|
|
15
|
-
apiKey: 'test-api-key',
|
|
16
|
-
organization: 'test-org',
|
|
17
|
-
},
|
|
18
|
-
defaultSettings: {
|
|
19
|
-
useModel: DyFM_OAI_Model.gpt4o_mini,
|
|
20
|
-
systemPrompt: 'Test system prompt',
|
|
21
|
-
temperature: 0.7,
|
|
22
|
-
maxTokens: 1000,
|
|
23
|
-
},
|
|
24
|
-
};
|
|
25
|
-
|
|
26
|
-
beforeEach(() => {
|
|
27
|
-
mockChatCompletionsCreate = jasmine.createSpy('chat.completions.create').and.returnValue({
|
|
28
|
-
id: 'chatcmpl-123',
|
|
29
|
-
object: 'chat.completion',
|
|
30
|
-
created: 1234567890,
|
|
31
|
-
model: 'gpt-4o-mini',
|
|
32
|
-
choices: [
|
|
33
|
-
{
|
|
34
|
-
index: 0,
|
|
35
|
-
message: {
|
|
36
|
-
role: 'assistant',
|
|
37
|
-
content: 'Test response',
|
|
38
|
-
},
|
|
39
|
-
finish_reason: 'stop',
|
|
40
|
-
},
|
|
41
|
-
],
|
|
42
|
-
usage: {
|
|
43
|
-
prompt_tokens: 10,
|
|
44
|
-
completion_tokens: 5,
|
|
45
|
-
total_tokens: 15,
|
|
46
|
-
},
|
|
47
|
-
});
|
|
48
|
-
|
|
49
|
-
mockOpenAI = {
|
|
50
|
-
chat: {
|
|
51
|
-
completions: {
|
|
52
|
-
create: mockChatCompletionsCreate,
|
|
53
|
-
},
|
|
54
|
-
},
|
|
55
|
-
};
|
|
56
|
-
|
|
57
|
-
service = new DyNTS_OAI_LLMChat_ServiceBase(mockSettings);
|
|
58
|
-
(service as any).openai = mockOpenAI;
|
|
59
|
-
});
|
|
60
|
-
|
|
61
|
-
describe('| requestYesNoInConversation', () => {
|
|
62
|
-
it('| should return boolean from conversation', async () => {
|
|
63
|
-
const conversation: DyFM_AI_Message[] = [];
|
|
64
|
-
spyOn(service, 'requestSimpleMessageInConversation').and.returnValue(Promise.resolve('YES'));
|
|
65
|
-
spyOn(service as any, 'convertAnswerToBoolean').and.returnValue(true);
|
|
66
|
-
|
|
67
|
-
const result = await service.requestYesNoInConversation({
|
|
68
|
-
message: 'Is this a test?',
|
|
69
|
-
conversation: conversation,
|
|
70
|
-
issuer: 'test-issuer',
|
|
71
|
-
});
|
|
72
|
-
|
|
73
|
-
expect(result).toBe(true);
|
|
74
|
-
expect((service as any).convertAnswerToBoolean).toHaveBeenCalled();
|
|
75
|
-
});
|
|
76
|
-
|
|
77
|
-
it('| should append predefined request to message', async () => {
|
|
78
|
-
const conversation: DyFM_AI_Message[] = [];
|
|
79
|
-
spyOn(service, 'requestSimpleMessageInConversation').and.returnValue(Promise.resolve('YES'));
|
|
80
|
-
|
|
81
|
-
await service.requestYesNoInConversation({
|
|
82
|
-
message: 'Is this a test?',
|
|
83
|
-
conversation: conversation,
|
|
84
|
-
issuer: 'test-issuer',
|
|
85
|
-
});
|
|
86
|
-
|
|
87
|
-
expect(service.requestSimpleMessageInConversation).toHaveBeenCalled();
|
|
88
|
-
});
|
|
89
|
-
});
|
|
90
|
-
|
|
91
|
-
describe('| requestPercentageInConversation', () => {
|
|
92
|
-
it('| should return number from conversation', async () => {
|
|
93
|
-
const conversation: DyFM_AI_Message[] = [];
|
|
94
|
-
spyOn(service, 'requestSimpleMessageInConversation').and.returnValue(Promise.resolve('75'));
|
|
95
|
-
spyOn(service as any, 'convertAnswerToNumber').and.returnValue(75);
|
|
96
|
-
|
|
97
|
-
const result = await service.requestPercentageInConversation({
|
|
98
|
-
message: 'What percentage?',
|
|
99
|
-
conversation: conversation,
|
|
100
|
-
issuer: 'test-issuer',
|
|
101
|
-
});
|
|
102
|
-
|
|
103
|
-
expect(result).toBe(75);
|
|
104
|
-
expect((service as any).convertAnswerToNumber).toHaveBeenCalled();
|
|
105
|
-
});
|
|
106
|
-
});
|
|
107
|
-
|
|
108
|
-
describe('| requestSelectInConversation', () => {
|
|
109
|
-
it('| should return selected option from conversation', async () => {
|
|
110
|
-
const conversation: DyFM_AI_Message[] = [];
|
|
111
|
-
const options = ['option1', 'option2', 'option3'];
|
|
112
|
-
spyOn(service, 'requestSimpleMessageInConversation').and.returnValue(Promise.resolve('option1'));
|
|
113
|
-
spyOn(service as any, 'stringifySelectOptions').and.returnValue(['option1', 'option2', 'option3']);
|
|
114
|
-
spyOn(service as any, 'getTextListAsText').and.returnValue('option1, option2, option3');
|
|
115
|
-
spyOn(service as any, 'convertAnswerToSelectOption').and.returnValue('option1');
|
|
116
|
-
|
|
117
|
-
const result = await service.requestSelectInConversation({
|
|
118
|
-
message: 'Select an option',
|
|
119
|
-
conversation: conversation,
|
|
120
|
-
selectFrom: options,
|
|
121
|
-
issuer: 'test-issuer',
|
|
122
|
-
});
|
|
123
|
-
|
|
124
|
-
expect(result).toBe('option1');
|
|
125
|
-
});
|
|
126
|
-
});
|
|
127
|
-
|
|
128
|
-
describe('| requestMultiselectInConversation', () => {
|
|
129
|
-
it('| should return multiple selected options from conversation', async () => {
|
|
130
|
-
const conversation: DyFM_AI_Message[] = [];
|
|
131
|
-
const options = ['option1', 'option2', 'option3'];
|
|
132
|
-
spyOn(service, 'requestSimpleMessageInConversation').and.returnValue(Promise.resolve('option1 and option2'));
|
|
133
|
-
spyOn(service as any, 'stringifySelectOptions').and.returnValue(['option1', 'option2', 'option3']);
|
|
134
|
-
spyOn(service as any, 'getTextListAsText').and.returnValue('option1, option2, option3');
|
|
135
|
-
spyOn(service as any, 'convertAnswerToList').and.returnValue(['option1', 'option2']);
|
|
136
|
-
|
|
137
|
-
const result = await service.requestMultiselectInConversation({
|
|
138
|
-
message: 'Select options',
|
|
139
|
-
conversation: conversation,
|
|
140
|
-
options: options,
|
|
141
|
-
issuer: 'test-issuer',
|
|
142
|
-
});
|
|
143
|
-
|
|
144
|
-
expect(result).toEqual(['option1', 'option2']);
|
|
145
|
-
});
|
|
146
|
-
});
|
|
147
|
-
|
|
148
|
-
describe('| requestJSONInConversation', () => {
|
|
149
|
-
it('| should return parsed JSON from conversation', async () => {
|
|
150
|
-
const conversation: DyFM_AI_Message[] = [];
|
|
151
|
-
const jsonString = '{"key": "value"}';
|
|
152
|
-
spyOn(service, 'requestSimpleMessageInConversation').and.returnValue(Promise.resolve(jsonString));
|
|
153
|
-
spyOn(service as any, 'convertAnswerToJSON').and.returnValue({ key: 'value' });
|
|
154
|
-
|
|
155
|
-
const result = await service.requestJSONInConversation<{ key: string }>({
|
|
156
|
-
message: 'Return JSON',
|
|
157
|
-
conversation: conversation,
|
|
158
|
-
issuer: 'test-issuer',
|
|
159
|
-
});
|
|
160
|
-
|
|
161
|
-
expect(result).toEqual({ key: 'value' });
|
|
162
|
-
});
|
|
163
|
-
});
|
|
164
|
-
|
|
165
|
-
describe('| requestJSONInConversationWithKeysDescription', () => {
|
|
166
|
-
it('| should return parsed JSON with keys description', async () => {
|
|
167
|
-
const conversation: DyFM_AI_Message[] = [];
|
|
168
|
-
const jsonString = '{"key": "value"}';
|
|
169
|
-
spyOn(service, 'requestSimpleMessageInConversation').and.returnValue(Promise.resolve(jsonString));
|
|
170
|
-
spyOn(service as any, 'convertAnswerToJSON').and.returnValue({ key: 'value' });
|
|
171
|
-
|
|
172
|
-
const result = await service.requestJSONInConversationWithKeysDescription<{ key: string }>({
|
|
173
|
-
message: 'Return JSON',
|
|
174
|
-
conversation: conversation,
|
|
175
|
-
keysDescription: 'key: string',
|
|
176
|
-
issuer: 'test-issuer',
|
|
177
|
-
});
|
|
178
|
-
|
|
179
|
-
expect(result).toEqual({ key: 'value' });
|
|
180
|
-
});
|
|
181
|
-
});
|
|
182
|
-
|
|
183
|
-
describe('| requestJSONInConversationWithExactKeys', () => {
|
|
184
|
-
it('| should return parsed JSON with exact keys', async () => {
|
|
185
|
-
const conversation: DyFM_AI_Message[] = [];
|
|
186
|
-
const jsonString = '{"key1": "value1", "key2": "value2"}';
|
|
187
|
-
spyOn(service, 'requestSimpleMessageInConversation').and.returnValue(Promise.resolve(jsonString));
|
|
188
|
-
spyOn(service as any, 'getTextListAsText').and.returnValue('key1, key2');
|
|
189
|
-
spyOn(service as any, 'convertAnswerToJSON').and.returnValue({ key1: 'value1', key2: 'value2' });
|
|
190
|
-
|
|
191
|
-
const result = await service.requestJSONInConversationWithExactKeys<{ key1: string; key2: string }>({
|
|
192
|
-
message: 'Return JSON',
|
|
193
|
-
conversation: conversation,
|
|
194
|
-
keys: ['key1', 'key2'],
|
|
195
|
-
issuer: 'test-issuer',
|
|
196
|
-
});
|
|
197
|
-
|
|
198
|
-
expect(result).toEqual({ key1: 'value1', key2: 'value2' });
|
|
199
|
-
});
|
|
200
|
-
});
|
|
201
|
-
|
|
202
|
-
describe('| requestListInConversation', () => {
|
|
203
|
-
it('| should return list from conversation', async () => {
|
|
204
|
-
const conversation: DyFM_AI_Message[] = [];
|
|
205
|
-
spyOn(service, 'requestSimpleMessageInConversation').and.returnValue(Promise.resolve('item1, item2, item3'));
|
|
206
|
-
spyOn(service as any, 'convertAnswerToList').and.returnValue(['item1', 'item2', 'item3']);
|
|
207
|
-
|
|
208
|
-
const result = await service.requestListInConversation<string>({
|
|
209
|
-
message: 'Return a list',
|
|
210
|
-
conversation: conversation,
|
|
211
|
-
issuer: 'test-issuer',
|
|
212
|
-
});
|
|
213
|
-
|
|
214
|
-
expect(result).toEqual(['item1', 'item2', 'item3']);
|
|
215
|
-
});
|
|
216
|
-
});
|
|
217
|
-
|
|
218
|
-
describe('| requestSimpleMessageInConversation', () => {
|
|
219
|
-
it('| should resolve conversation and return answer', async () => {
|
|
220
|
-
const conversation: DyFM_AI_Message[] = [];
|
|
221
|
-
spyOn(service as any, 'logAnswer');
|
|
222
|
-
spyOn(service, 'resolveConversation').and.returnValue(Promise.resolve('Test response'));
|
|
223
|
-
|
|
224
|
-
const result = await service.requestSimpleMessageInConversation({
|
|
225
|
-
message: 'Test question',
|
|
226
|
-
conversation: conversation,
|
|
227
|
-
issuer: 'test-issuer',
|
|
228
|
-
});
|
|
229
|
-
|
|
230
|
-
expect(result).toBe('Test response');
|
|
231
|
-
expect((service as any).logAnswer).toHaveBeenCalled();
|
|
232
|
-
});
|
|
233
|
-
|
|
234
|
-
it('| should add user message to conversation', async () => {
|
|
235
|
-
const conversation: DyFM_AI_Message[] = [];
|
|
236
|
-
spyOn(service, 'resolveConversation').and.returnValue(Promise.resolve('Test response'));
|
|
237
|
-
|
|
238
|
-
await service.requestSimpleMessageInConversation({
|
|
239
|
-
message: 'Test question',
|
|
240
|
-
conversation: conversation,
|
|
241
|
-
issuer: 'test-issuer',
|
|
242
|
-
});
|
|
243
|
-
|
|
244
|
-
expect(conversation.length).toBe(1);
|
|
245
|
-
expect(conversation[0].role).toBe(DyFM_AI_MessageRole.user);
|
|
246
|
-
expect(conversation[0].content).toBe('Test question');
|
|
247
|
-
});
|
|
248
|
-
});
|
|
249
|
-
|
|
250
|
-
describe('| resolveConversation', () => {
|
|
251
|
-
it('| should return string answer when getFullResponse is false', async () => {
|
|
252
|
-
const conversation: DyFM_AI_Message[] = [
|
|
253
|
-
{ role: DyFM_AI_MessageRole.user, content: 'Test question' },
|
|
254
|
-
];
|
|
255
|
-
|
|
256
|
-
spyOn(service as any, 'getDefaultSystemMessage').and.returnValue({
|
|
257
|
-
role: DyFM_AI_MessageRole.system,
|
|
258
|
-
content: 'System prompt',
|
|
259
|
-
});
|
|
260
|
-
spyOn(service as any, 'shortenConversation').and.returnValue(conversation);
|
|
261
|
-
spyOn(service as any, 'validateConversation');
|
|
262
|
-
spyOn(service as any, 'logConversation');
|
|
263
|
-
spyOn(service as any, 'getMessageCreateInput').and.returnValue({
|
|
264
|
-
model: DyFM_OAI_Model.gpt4o_mini,
|
|
265
|
-
messages: conversation as any,
|
|
266
|
-
});
|
|
267
|
-
|
|
268
|
-
const result = await service.resolveConversation({
|
|
269
|
-
conversation: conversation,
|
|
270
|
-
issuer: 'test-issuer',
|
|
271
|
-
getFullResponse: false,
|
|
272
|
-
});
|
|
273
|
-
|
|
274
|
-
expect(result).toBe('Test response');
|
|
275
|
-
expect(mockChatCompletionsCreate).toHaveBeenCalled();
|
|
276
|
-
});
|
|
277
|
-
|
|
278
|
-
it('| should return full ChatCompletion when getFullResponse is true', async () => {
|
|
279
|
-
const conversation: DyFM_AI_Message[] = [
|
|
280
|
-
{ role: DyFM_AI_MessageRole.user, content: 'Test question' },
|
|
281
|
-
];
|
|
282
|
-
|
|
283
|
-
const fullResponse: ChatCompletion = {
|
|
284
|
-
id: 'chatcmpl-123',
|
|
285
|
-
object: 'chat.completion',
|
|
286
|
-
created: 1234567890,
|
|
287
|
-
model: 'gpt-4o-mini',
|
|
288
|
-
choices: [
|
|
289
|
-
{
|
|
290
|
-
index: 0,
|
|
291
|
-
message: {
|
|
292
|
-
role: 'assistant',
|
|
293
|
-
content: 'Test response',
|
|
294
|
-
refusal: null,
|
|
295
|
-
},
|
|
296
|
-
logprobs: null,
|
|
297
|
-
finish_reason: 'stop',
|
|
298
|
-
},
|
|
299
|
-
],
|
|
300
|
-
usage: {
|
|
301
|
-
prompt_tokens: 10,
|
|
302
|
-
completion_tokens: 5,
|
|
303
|
-
total_tokens: 15,
|
|
304
|
-
},
|
|
305
|
-
};
|
|
306
|
-
|
|
307
|
-
mockChatCompletionsCreate.and.returnValue(fullResponse);
|
|
308
|
-
spyOn(service as any, 'getDefaultSystemMessage').and.returnValue({
|
|
309
|
-
role: DyFM_AI_MessageRole.system,
|
|
310
|
-
content: 'System prompt',
|
|
311
|
-
});
|
|
312
|
-
spyOn(service as any, 'shortenConversation').and.returnValue(conversation);
|
|
313
|
-
spyOn(service as any, 'validateConversation');
|
|
314
|
-
spyOn(service as any, 'logConversation');
|
|
315
|
-
spyOn(service as any, 'getMessageCreateInput').and.returnValue({
|
|
316
|
-
model: DyFM_OAI_Model.gpt4o_mini,
|
|
317
|
-
messages: conversation as any,
|
|
318
|
-
});
|
|
319
|
-
|
|
320
|
-
const result = await service.resolveConversation({
|
|
321
|
-
conversation: conversation,
|
|
322
|
-
issuer: 'test-issuer',
|
|
323
|
-
getFullResponse: true,
|
|
324
|
-
});
|
|
325
|
-
|
|
326
|
-
expect(result).toEqual(fullResponse);
|
|
327
|
-
});
|
|
328
|
-
|
|
329
|
-
it('| should add system message if not present', async () => {
|
|
330
|
-
const conversation: DyFM_AI_Message[] = [
|
|
331
|
-
{ role: DyFM_AI_MessageRole.user, content: 'Test question' },
|
|
332
|
-
];
|
|
333
|
-
|
|
334
|
-
spyOn(service as any, 'getDefaultSystemMessage').and.returnValue({
|
|
335
|
-
role: DyFM_AI_MessageRole.system,
|
|
336
|
-
content: 'System prompt',
|
|
337
|
-
});
|
|
338
|
-
spyOn(service as any, 'shortenConversation').and.returnValue(conversation);
|
|
339
|
-
spyOn(service as any, 'validateConversation');
|
|
340
|
-
spyOn(service as any, 'logConversation');
|
|
341
|
-
spyOn(service as any, 'getMessageCreateInput').and.returnValue({
|
|
342
|
-
model: DyFM_OAI_Model.gpt4o_mini,
|
|
343
|
-
messages: conversation as any,
|
|
344
|
-
});
|
|
345
|
-
|
|
346
|
-
await service.resolveConversation({
|
|
347
|
-
conversation: conversation,
|
|
348
|
-
issuer: 'test-issuer',
|
|
349
|
-
});
|
|
350
|
-
|
|
351
|
-
expect((service as any).getDefaultSystemMessage).toHaveBeenCalled();
|
|
352
|
-
});
|
|
353
|
-
|
|
354
|
-
it('| should handle API errors', async () => {
|
|
355
|
-
const conversation: DyFM_AI_Message[] = [
|
|
356
|
-
{ role: DyFM_AI_MessageRole.user, content: 'Test question' },
|
|
357
|
-
];
|
|
358
|
-
|
|
359
|
-
mockChatCompletionsCreate.and.throwError(new Error('API Error'));
|
|
360
|
-
spyOn(service as any, 'getDefaultSystemMessage').and.returnValue({
|
|
361
|
-
role: DyFM_AI_MessageRole.system,
|
|
362
|
-
content: 'System prompt',
|
|
363
|
-
});
|
|
364
|
-
spyOn(service as any, 'shortenConversation').and.returnValue(conversation);
|
|
365
|
-
spyOn(service as any, 'validateConversation');
|
|
366
|
-
spyOn(service as any, 'logConversation');
|
|
367
|
-
spyOn(service as any, 'getMessageCreateInput').and.returnValue({
|
|
368
|
-
model: DyFM_OAI_Model.gpt4o_mini,
|
|
369
|
-
messages: conversation as any,
|
|
370
|
-
});
|
|
371
|
-
|
|
372
|
-
try {
|
|
373
|
-
await service.resolveConversation({
|
|
374
|
-
conversation: conversation,
|
|
375
|
-
issuer: 'test-issuer',
|
|
376
|
-
});
|
|
377
|
-
fail('Should have thrown an error');
|
|
378
|
-
} catch (error) {
|
|
379
|
-
expect(error).toBeInstanceOf(DyFM_Error);
|
|
380
|
-
expect((error as DyFM_Error)._errorCode).toContain('DyNTS-OAB-RC0');
|
|
381
|
-
}
|
|
382
|
-
});
|
|
383
|
-
});
|
|
384
|
-
|
|
385
|
-
describe('| shortenConversation', () => {
|
|
386
|
-
it('| should return conversation when under limit', () => {
|
|
387
|
-
const conversation: DyFM_AI_Message[] = [
|
|
388
|
-
{ role: DyFM_AI_MessageRole.user, content: 'Short message' },
|
|
389
|
-
];
|
|
390
|
-
|
|
391
|
-
spyOn(DyFM_Object, 'clone').and.returnValue(conversation);
|
|
392
|
-
spyOn(DyFM_Array, 'last').and.returnValue(conversation[0]);
|
|
393
|
-
|
|
394
|
-
const result = (service as any).shortenConversation({
|
|
395
|
-
conversation: conversation,
|
|
396
|
-
issuer: 'test-issuer',
|
|
397
|
-
});
|
|
398
|
-
|
|
399
|
-
expect(result).toBeDefined();
|
|
400
|
-
});
|
|
401
|
-
|
|
402
|
-
it('| should shorten conversation when over limit', () => {
|
|
403
|
-
const longMessage = 'x'.repeat(10000);
|
|
404
|
-
const conversation: DyFM_AI_Message[] = Array(100).fill(null).map((_, i) => ({
|
|
405
|
-
role: DyFM_AI_MessageRole.user,
|
|
406
|
-
content: `Message ${i}: ${longMessage}`,
|
|
407
|
-
}));
|
|
408
|
-
|
|
409
|
-
const clonedConversation = [...conversation];
|
|
410
|
-
spyOn(DyFM_Object, 'clone').and.returnValue(clonedConversation);
|
|
411
|
-
spyOn(DyFM_Array, 'last').and.returnValue(conversation[conversation.length - 1]);
|
|
412
|
-
spyOn(DyFM_Log, 'info');
|
|
413
|
-
|
|
414
|
-
const result = (service as any).shortenConversation({
|
|
415
|
-
conversation: conversation,
|
|
416
|
-
issuer: 'test-issuer',
|
|
417
|
-
});
|
|
418
|
-
|
|
419
|
-
expect(result).toBeDefined();
|
|
420
|
-
expect(result.length).toBeLessThanOrEqual(conversation.length);
|
|
421
|
-
});
|
|
422
|
-
|
|
423
|
-
it('| should preserve system messages', () => {
|
|
424
|
-
const conversation: DyFM_AI_Message[] = [
|
|
425
|
-
{ role: DyFM_AI_MessageRole.system, content: 'System prompt' },
|
|
426
|
-
{ role: DyFM_AI_MessageRole.user, content: 'User message' },
|
|
427
|
-
];
|
|
428
|
-
|
|
429
|
-
const clonedConversation = [...conversation];
|
|
430
|
-
spyOn(DyFM_Object, 'clone').and.returnValue(clonedConversation);
|
|
431
|
-
spyOn(DyFM_Array, 'last').and.returnValue(conversation[1]);
|
|
432
|
-
|
|
433
|
-
const result = (service as any).shortenConversation({
|
|
434
|
-
conversation: conversation,
|
|
435
|
-
issuer: 'test-issuer',
|
|
436
|
-
});
|
|
437
|
-
|
|
438
|
-
const systemMessages = result.filter(m => m.role === DyFM_AI_MessageRole.system);
|
|
439
|
-
expect(systemMessages.length).toBeGreaterThan(0);
|
|
440
|
-
});
|
|
441
|
-
|
|
442
|
-
it('| should throw error when no messages left', () => {
|
|
443
|
-
const conversation: DyFM_AI_Message[] = [];
|
|
444
|
-
|
|
445
|
-
const clonedConversation: DyFM_AI_Message[] = [];
|
|
446
|
-
spyOn(DyFM_Object, 'clone').and.returnValue(clonedConversation);
|
|
447
|
-
spyOn(DyFM_Log, 'error');
|
|
448
|
-
|
|
449
|
-
try {
|
|
450
|
-
(service as any).shortenConversation({
|
|
451
|
-
conversation: conversation,
|
|
452
|
-
issuer: 'test-issuer',
|
|
453
|
-
});
|
|
454
|
-
fail('Should have thrown an error');
|
|
455
|
-
} catch (error) {
|
|
456
|
-
expect(error).toBeInstanceOf(DyFM_Error);
|
|
457
|
-
expect((error as DyFM_Error)._errorCode).toContain('DyNTS-OAB-SC0');
|
|
458
|
-
}
|
|
459
|
-
});
|
|
460
|
-
});
|
|
461
|
-
});
|
|
462
|
-
|
|
1
|
+
|
|
2
|
+
import { DyNTS_OAI_LLMChat_ServiceBase } from './oai-llm-chat.service-base';
|
|
3
|
+
import { DyFM_OAI_Settings, DyFM_OAI_Model, DyFM_OAI_CallSettings } from '@futdevpro/fsm-dynamo/ai/open-ai';
|
|
4
|
+
import { DyFM_AI_MessageRole, DyFM_AI_Message } from '@futdevpro/fsm-dynamo/ai';
|
|
5
|
+
import { DyFM_Error, DyFM_Log, DyFM_Object, DyFM_Array } from '@futdevpro/fsm-dynamo';
|
|
6
|
+
import { ChatCompletion } from 'openai/resources';
|
|
7
|
+
|
|
8
|
+
describe('| DyNTS_OAI_LLMChat_ServiceBase', () => {
|
|
9
|
+
let service: DyNTS_OAI_LLMChat_ServiceBase;
|
|
10
|
+
let mockOpenAI: any;
|
|
11
|
+
let mockChatCompletionsCreate: jasmine.Spy;
|
|
12
|
+
|
|
13
|
+
const mockSettings: DyFM_OAI_Settings = {
|
|
14
|
+
config: {
|
|
15
|
+
apiKey: 'test-api-key',
|
|
16
|
+
organization: 'test-org',
|
|
17
|
+
},
|
|
18
|
+
defaultSettings: {
|
|
19
|
+
useModel: DyFM_OAI_Model.gpt4o_mini,
|
|
20
|
+
systemPrompt: 'Test system prompt',
|
|
21
|
+
temperature: 0.7,
|
|
22
|
+
maxTokens: 1000,
|
|
23
|
+
},
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
beforeEach(() => {
|
|
27
|
+
mockChatCompletionsCreate = jasmine.createSpy('chat.completions.create').and.returnValue({
|
|
28
|
+
id: 'chatcmpl-123',
|
|
29
|
+
object: 'chat.completion',
|
|
30
|
+
created: 1234567890,
|
|
31
|
+
model: 'gpt-4o-mini',
|
|
32
|
+
choices: [
|
|
33
|
+
{
|
|
34
|
+
index: 0,
|
|
35
|
+
message: {
|
|
36
|
+
role: 'assistant',
|
|
37
|
+
content: 'Test response',
|
|
38
|
+
},
|
|
39
|
+
finish_reason: 'stop',
|
|
40
|
+
},
|
|
41
|
+
],
|
|
42
|
+
usage: {
|
|
43
|
+
prompt_tokens: 10,
|
|
44
|
+
completion_tokens: 5,
|
|
45
|
+
total_tokens: 15,
|
|
46
|
+
},
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
mockOpenAI = {
|
|
50
|
+
chat: {
|
|
51
|
+
completions: {
|
|
52
|
+
create: mockChatCompletionsCreate,
|
|
53
|
+
},
|
|
54
|
+
},
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
service = new DyNTS_OAI_LLMChat_ServiceBase(mockSettings);
|
|
58
|
+
(service as any).openai = mockOpenAI;
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
describe('| requestYesNoInConversation', () => {
|
|
62
|
+
it('| should return boolean from conversation', async () => {
|
|
63
|
+
const conversation: DyFM_AI_Message[] = [];
|
|
64
|
+
spyOn(service, 'requestSimpleMessageInConversation').and.returnValue(Promise.resolve('YES'));
|
|
65
|
+
spyOn(service as any, 'convertAnswerToBoolean').and.returnValue(true);
|
|
66
|
+
|
|
67
|
+
const result = await service.requestYesNoInConversation({
|
|
68
|
+
message: 'Is this a test?',
|
|
69
|
+
conversation: conversation,
|
|
70
|
+
issuer: 'test-issuer',
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
expect(result).toBe(true);
|
|
74
|
+
expect((service as any).convertAnswerToBoolean).toHaveBeenCalled();
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
it('| should append predefined request to message', async () => {
|
|
78
|
+
const conversation: DyFM_AI_Message[] = [];
|
|
79
|
+
spyOn(service, 'requestSimpleMessageInConversation').and.returnValue(Promise.resolve('YES'));
|
|
80
|
+
|
|
81
|
+
await service.requestYesNoInConversation({
|
|
82
|
+
message: 'Is this a test?',
|
|
83
|
+
conversation: conversation,
|
|
84
|
+
issuer: 'test-issuer',
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
expect(service.requestSimpleMessageInConversation).toHaveBeenCalled();
|
|
88
|
+
});
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
describe('| requestPercentageInConversation', () => {
|
|
92
|
+
it('| should return number from conversation', async () => {
|
|
93
|
+
const conversation: DyFM_AI_Message[] = [];
|
|
94
|
+
spyOn(service, 'requestSimpleMessageInConversation').and.returnValue(Promise.resolve('75'));
|
|
95
|
+
spyOn(service as any, 'convertAnswerToNumber').and.returnValue(75);
|
|
96
|
+
|
|
97
|
+
const result = await service.requestPercentageInConversation({
|
|
98
|
+
message: 'What percentage?',
|
|
99
|
+
conversation: conversation,
|
|
100
|
+
issuer: 'test-issuer',
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
expect(result).toBe(75);
|
|
104
|
+
expect((service as any).convertAnswerToNumber).toHaveBeenCalled();
|
|
105
|
+
});
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
describe('| requestSelectInConversation', () => {
|
|
109
|
+
it('| should return selected option from conversation', async () => {
|
|
110
|
+
const conversation: DyFM_AI_Message[] = [];
|
|
111
|
+
const options = ['option1', 'option2', 'option3'];
|
|
112
|
+
spyOn(service, 'requestSimpleMessageInConversation').and.returnValue(Promise.resolve('option1'));
|
|
113
|
+
spyOn(service as any, 'stringifySelectOptions').and.returnValue(['option1', 'option2', 'option3']);
|
|
114
|
+
spyOn(service as any, 'getTextListAsText').and.returnValue('option1, option2, option3');
|
|
115
|
+
spyOn(service as any, 'convertAnswerToSelectOption').and.returnValue('option1');
|
|
116
|
+
|
|
117
|
+
const result = await service.requestSelectInConversation({
|
|
118
|
+
message: 'Select an option',
|
|
119
|
+
conversation: conversation,
|
|
120
|
+
selectFrom: options,
|
|
121
|
+
issuer: 'test-issuer',
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
expect(result).toBe('option1');
|
|
125
|
+
});
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
describe('| requestMultiselectInConversation', () => {
|
|
129
|
+
it('| should return multiple selected options from conversation', async () => {
|
|
130
|
+
const conversation: DyFM_AI_Message[] = [];
|
|
131
|
+
const options = ['option1', 'option2', 'option3'];
|
|
132
|
+
spyOn(service, 'requestSimpleMessageInConversation').and.returnValue(Promise.resolve('option1 and option2'));
|
|
133
|
+
spyOn(service as any, 'stringifySelectOptions').and.returnValue(['option1', 'option2', 'option3']);
|
|
134
|
+
spyOn(service as any, 'getTextListAsText').and.returnValue('option1, option2, option3');
|
|
135
|
+
spyOn(service as any, 'convertAnswerToList').and.returnValue(['option1', 'option2']);
|
|
136
|
+
|
|
137
|
+
const result = await service.requestMultiselectInConversation({
|
|
138
|
+
message: 'Select options',
|
|
139
|
+
conversation: conversation,
|
|
140
|
+
options: options,
|
|
141
|
+
issuer: 'test-issuer',
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
expect(result).toEqual(['option1', 'option2']);
|
|
145
|
+
});
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
describe('| requestJSONInConversation', () => {
|
|
149
|
+
it('| should return parsed JSON from conversation', async () => {
|
|
150
|
+
const conversation: DyFM_AI_Message[] = [];
|
|
151
|
+
const jsonString = '{"key": "value"}';
|
|
152
|
+
spyOn(service, 'requestSimpleMessageInConversation').and.returnValue(Promise.resolve(jsonString));
|
|
153
|
+
spyOn(service as any, 'convertAnswerToJSON').and.returnValue({ key: 'value' });
|
|
154
|
+
|
|
155
|
+
const result = await service.requestJSONInConversation<{ key: string }>({
|
|
156
|
+
message: 'Return JSON',
|
|
157
|
+
conversation: conversation,
|
|
158
|
+
issuer: 'test-issuer',
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
expect(result).toEqual({ key: 'value' });
|
|
162
|
+
});
|
|
163
|
+
});
|
|
164
|
+
|
|
165
|
+
describe('| requestJSONInConversationWithKeysDescription', () => {
|
|
166
|
+
it('| should return parsed JSON with keys description', async () => {
|
|
167
|
+
const conversation: DyFM_AI_Message[] = [];
|
|
168
|
+
const jsonString = '{"key": "value"}';
|
|
169
|
+
spyOn(service, 'requestSimpleMessageInConversation').and.returnValue(Promise.resolve(jsonString));
|
|
170
|
+
spyOn(service as any, 'convertAnswerToJSON').and.returnValue({ key: 'value' });
|
|
171
|
+
|
|
172
|
+
const result = await service.requestJSONInConversationWithKeysDescription<{ key: string }>({
|
|
173
|
+
message: 'Return JSON',
|
|
174
|
+
conversation: conversation,
|
|
175
|
+
keysDescription: 'key: string',
|
|
176
|
+
issuer: 'test-issuer',
|
|
177
|
+
});
|
|
178
|
+
|
|
179
|
+
expect(result).toEqual({ key: 'value' });
|
|
180
|
+
});
|
|
181
|
+
});
|
|
182
|
+
|
|
183
|
+
describe('| requestJSONInConversationWithExactKeys', () => {
|
|
184
|
+
it('| should return parsed JSON with exact keys', async () => {
|
|
185
|
+
const conversation: DyFM_AI_Message[] = [];
|
|
186
|
+
const jsonString = '{"key1": "value1", "key2": "value2"}';
|
|
187
|
+
spyOn(service, 'requestSimpleMessageInConversation').and.returnValue(Promise.resolve(jsonString));
|
|
188
|
+
spyOn(service as any, 'getTextListAsText').and.returnValue('key1, key2');
|
|
189
|
+
spyOn(service as any, 'convertAnswerToJSON').and.returnValue({ key1: 'value1', key2: 'value2' });
|
|
190
|
+
|
|
191
|
+
const result = await service.requestJSONInConversationWithExactKeys<{ key1: string; key2: string }>({
|
|
192
|
+
message: 'Return JSON',
|
|
193
|
+
conversation: conversation,
|
|
194
|
+
keys: ['key1', 'key2'],
|
|
195
|
+
issuer: 'test-issuer',
|
|
196
|
+
});
|
|
197
|
+
|
|
198
|
+
expect(result).toEqual({ key1: 'value1', key2: 'value2' });
|
|
199
|
+
});
|
|
200
|
+
});
|
|
201
|
+
|
|
202
|
+
describe('| requestListInConversation', () => {
|
|
203
|
+
it('| should return list from conversation', async () => {
|
|
204
|
+
const conversation: DyFM_AI_Message[] = [];
|
|
205
|
+
spyOn(service, 'requestSimpleMessageInConversation').and.returnValue(Promise.resolve('item1, item2, item3'));
|
|
206
|
+
spyOn(service as any, 'convertAnswerToList').and.returnValue(['item1', 'item2', 'item3']);
|
|
207
|
+
|
|
208
|
+
const result = await service.requestListInConversation<string>({
|
|
209
|
+
message: 'Return a list',
|
|
210
|
+
conversation: conversation,
|
|
211
|
+
issuer: 'test-issuer',
|
|
212
|
+
});
|
|
213
|
+
|
|
214
|
+
expect(result).toEqual(['item1', 'item2', 'item3']);
|
|
215
|
+
});
|
|
216
|
+
});
|
|
217
|
+
|
|
218
|
+
describe('| requestSimpleMessageInConversation', () => {
|
|
219
|
+
it('| should resolve conversation and return answer', async () => {
|
|
220
|
+
const conversation: DyFM_AI_Message[] = [];
|
|
221
|
+
spyOn(service as any, 'logAnswer');
|
|
222
|
+
spyOn(service, 'resolveConversation').and.returnValue(Promise.resolve('Test response'));
|
|
223
|
+
|
|
224
|
+
const result = await service.requestSimpleMessageInConversation({
|
|
225
|
+
message: 'Test question',
|
|
226
|
+
conversation: conversation,
|
|
227
|
+
issuer: 'test-issuer',
|
|
228
|
+
});
|
|
229
|
+
|
|
230
|
+
expect(result).toBe('Test response');
|
|
231
|
+
expect((service as any).logAnswer).toHaveBeenCalled();
|
|
232
|
+
});
|
|
233
|
+
|
|
234
|
+
it('| should add user message to conversation', async () => {
|
|
235
|
+
const conversation: DyFM_AI_Message[] = [];
|
|
236
|
+
spyOn(service, 'resolveConversation').and.returnValue(Promise.resolve('Test response'));
|
|
237
|
+
|
|
238
|
+
await service.requestSimpleMessageInConversation({
|
|
239
|
+
message: 'Test question',
|
|
240
|
+
conversation: conversation,
|
|
241
|
+
issuer: 'test-issuer',
|
|
242
|
+
});
|
|
243
|
+
|
|
244
|
+
expect(conversation.length).toBe(1);
|
|
245
|
+
expect(conversation[0].role).toBe(DyFM_AI_MessageRole.user);
|
|
246
|
+
expect(conversation[0].content).toBe('Test question');
|
|
247
|
+
});
|
|
248
|
+
});
|
|
249
|
+
|
|
250
|
+
describe('| resolveConversation', () => {
|
|
251
|
+
it('| should return string answer when getFullResponse is false', async () => {
|
|
252
|
+
const conversation: DyFM_AI_Message[] = [
|
|
253
|
+
{ role: DyFM_AI_MessageRole.user, content: 'Test question' },
|
|
254
|
+
];
|
|
255
|
+
|
|
256
|
+
spyOn(service as any, 'getDefaultSystemMessage').and.returnValue({
|
|
257
|
+
role: DyFM_AI_MessageRole.system,
|
|
258
|
+
content: 'System prompt',
|
|
259
|
+
});
|
|
260
|
+
spyOn(service as any, 'shortenConversation').and.returnValue(conversation);
|
|
261
|
+
spyOn(service as any, 'validateConversation');
|
|
262
|
+
spyOn(service as any, 'logConversation');
|
|
263
|
+
spyOn(service as any, 'getMessageCreateInput').and.returnValue({
|
|
264
|
+
model: DyFM_OAI_Model.gpt4o_mini,
|
|
265
|
+
messages: conversation as any,
|
|
266
|
+
});
|
|
267
|
+
|
|
268
|
+
const result = await service.resolveConversation({
|
|
269
|
+
conversation: conversation,
|
|
270
|
+
issuer: 'test-issuer',
|
|
271
|
+
getFullResponse: false,
|
|
272
|
+
});
|
|
273
|
+
|
|
274
|
+
expect(result).toBe('Test response');
|
|
275
|
+
expect(mockChatCompletionsCreate).toHaveBeenCalled();
|
|
276
|
+
});
|
|
277
|
+
|
|
278
|
+
it('| should return full ChatCompletion when getFullResponse is true', async () => {
|
|
279
|
+
const conversation: DyFM_AI_Message[] = [
|
|
280
|
+
{ role: DyFM_AI_MessageRole.user, content: 'Test question' },
|
|
281
|
+
];
|
|
282
|
+
|
|
283
|
+
const fullResponse: ChatCompletion = {
|
|
284
|
+
id: 'chatcmpl-123',
|
|
285
|
+
object: 'chat.completion',
|
|
286
|
+
created: 1234567890,
|
|
287
|
+
model: 'gpt-4o-mini',
|
|
288
|
+
choices: [
|
|
289
|
+
{
|
|
290
|
+
index: 0,
|
|
291
|
+
message: {
|
|
292
|
+
role: 'assistant',
|
|
293
|
+
content: 'Test response',
|
|
294
|
+
refusal: null,
|
|
295
|
+
},
|
|
296
|
+
logprobs: null,
|
|
297
|
+
finish_reason: 'stop',
|
|
298
|
+
},
|
|
299
|
+
],
|
|
300
|
+
usage: {
|
|
301
|
+
prompt_tokens: 10,
|
|
302
|
+
completion_tokens: 5,
|
|
303
|
+
total_tokens: 15,
|
|
304
|
+
},
|
|
305
|
+
};
|
|
306
|
+
|
|
307
|
+
mockChatCompletionsCreate.and.returnValue(fullResponse);
|
|
308
|
+
spyOn(service as any, 'getDefaultSystemMessage').and.returnValue({
|
|
309
|
+
role: DyFM_AI_MessageRole.system,
|
|
310
|
+
content: 'System prompt',
|
|
311
|
+
});
|
|
312
|
+
spyOn(service as any, 'shortenConversation').and.returnValue(conversation);
|
|
313
|
+
spyOn(service as any, 'validateConversation');
|
|
314
|
+
spyOn(service as any, 'logConversation');
|
|
315
|
+
spyOn(service as any, 'getMessageCreateInput').and.returnValue({
|
|
316
|
+
model: DyFM_OAI_Model.gpt4o_mini,
|
|
317
|
+
messages: conversation as any,
|
|
318
|
+
});
|
|
319
|
+
|
|
320
|
+
const result = await service.resolveConversation({
|
|
321
|
+
conversation: conversation,
|
|
322
|
+
issuer: 'test-issuer',
|
|
323
|
+
getFullResponse: true,
|
|
324
|
+
});
|
|
325
|
+
|
|
326
|
+
expect(result).toEqual(fullResponse);
|
|
327
|
+
});
|
|
328
|
+
|
|
329
|
+
it('| should add system message if not present', async () => {
|
|
330
|
+
const conversation: DyFM_AI_Message[] = [
|
|
331
|
+
{ role: DyFM_AI_MessageRole.user, content: 'Test question' },
|
|
332
|
+
];
|
|
333
|
+
|
|
334
|
+
spyOn(service as any, 'getDefaultSystemMessage').and.returnValue({
|
|
335
|
+
role: DyFM_AI_MessageRole.system,
|
|
336
|
+
content: 'System prompt',
|
|
337
|
+
});
|
|
338
|
+
spyOn(service as any, 'shortenConversation').and.returnValue(conversation);
|
|
339
|
+
spyOn(service as any, 'validateConversation');
|
|
340
|
+
spyOn(service as any, 'logConversation');
|
|
341
|
+
spyOn(service as any, 'getMessageCreateInput').and.returnValue({
|
|
342
|
+
model: DyFM_OAI_Model.gpt4o_mini,
|
|
343
|
+
messages: conversation as any,
|
|
344
|
+
});
|
|
345
|
+
|
|
346
|
+
await service.resolveConversation({
|
|
347
|
+
conversation: conversation,
|
|
348
|
+
issuer: 'test-issuer',
|
|
349
|
+
});
|
|
350
|
+
|
|
351
|
+
expect((service as any).getDefaultSystemMessage).toHaveBeenCalled();
|
|
352
|
+
});
|
|
353
|
+
|
|
354
|
+
it('| should handle API errors', async () => {
|
|
355
|
+
const conversation: DyFM_AI_Message[] = [
|
|
356
|
+
{ role: DyFM_AI_MessageRole.user, content: 'Test question' },
|
|
357
|
+
];
|
|
358
|
+
|
|
359
|
+
mockChatCompletionsCreate.and.throwError(new Error('API Error'));
|
|
360
|
+
spyOn(service as any, 'getDefaultSystemMessage').and.returnValue({
|
|
361
|
+
role: DyFM_AI_MessageRole.system,
|
|
362
|
+
content: 'System prompt',
|
|
363
|
+
});
|
|
364
|
+
spyOn(service as any, 'shortenConversation').and.returnValue(conversation);
|
|
365
|
+
spyOn(service as any, 'validateConversation');
|
|
366
|
+
spyOn(service as any, 'logConversation');
|
|
367
|
+
spyOn(service as any, 'getMessageCreateInput').and.returnValue({
|
|
368
|
+
model: DyFM_OAI_Model.gpt4o_mini,
|
|
369
|
+
messages: conversation as any,
|
|
370
|
+
});
|
|
371
|
+
|
|
372
|
+
try {
|
|
373
|
+
await service.resolveConversation({
|
|
374
|
+
conversation: conversation,
|
|
375
|
+
issuer: 'test-issuer',
|
|
376
|
+
});
|
|
377
|
+
fail('Should have thrown an error');
|
|
378
|
+
} catch (error) {
|
|
379
|
+
expect(error).toBeInstanceOf(DyFM_Error);
|
|
380
|
+
expect((error as DyFM_Error)._errorCode).toContain('DyNTS-OAB-RC0');
|
|
381
|
+
}
|
|
382
|
+
});
|
|
383
|
+
});
|
|
384
|
+
|
|
385
|
+
describe('| shortenConversation', () => {
|
|
386
|
+
it('| should return conversation when under limit', () => {
|
|
387
|
+
const conversation: DyFM_AI_Message[] = [
|
|
388
|
+
{ role: DyFM_AI_MessageRole.user, content: 'Short message' },
|
|
389
|
+
];
|
|
390
|
+
|
|
391
|
+
spyOn(DyFM_Object, 'clone').and.returnValue(conversation);
|
|
392
|
+
spyOn(DyFM_Array, 'last').and.returnValue(conversation[0]);
|
|
393
|
+
|
|
394
|
+
const result = (service as any).shortenConversation({
|
|
395
|
+
conversation: conversation,
|
|
396
|
+
issuer: 'test-issuer',
|
|
397
|
+
});
|
|
398
|
+
|
|
399
|
+
expect(result).toBeDefined();
|
|
400
|
+
});
|
|
401
|
+
|
|
402
|
+
it('| should shorten conversation when over limit', () => {
|
|
403
|
+
const longMessage = 'x'.repeat(10000);
|
|
404
|
+
const conversation: DyFM_AI_Message[] = Array(100).fill(null).map((_, i) => ({
|
|
405
|
+
role: DyFM_AI_MessageRole.user,
|
|
406
|
+
content: `Message ${i}: ${longMessage}`,
|
|
407
|
+
}));
|
|
408
|
+
|
|
409
|
+
const clonedConversation = [...conversation];
|
|
410
|
+
spyOn(DyFM_Object, 'clone').and.returnValue(clonedConversation);
|
|
411
|
+
spyOn(DyFM_Array, 'last').and.returnValue(conversation[conversation.length - 1]);
|
|
412
|
+
spyOn(DyFM_Log, 'info');
|
|
413
|
+
|
|
414
|
+
const result = (service as any).shortenConversation({
|
|
415
|
+
conversation: conversation,
|
|
416
|
+
issuer: 'test-issuer',
|
|
417
|
+
});
|
|
418
|
+
|
|
419
|
+
expect(result).toBeDefined();
|
|
420
|
+
expect(result.length).toBeLessThanOrEqual(conversation.length);
|
|
421
|
+
});
|
|
422
|
+
|
|
423
|
+
it('| should preserve system messages', () => {
|
|
424
|
+
const conversation: DyFM_AI_Message[] = [
|
|
425
|
+
{ role: DyFM_AI_MessageRole.system, content: 'System prompt' },
|
|
426
|
+
{ role: DyFM_AI_MessageRole.user, content: 'User message' },
|
|
427
|
+
];
|
|
428
|
+
|
|
429
|
+
const clonedConversation = [...conversation];
|
|
430
|
+
spyOn(DyFM_Object, 'clone').and.returnValue(clonedConversation);
|
|
431
|
+
spyOn(DyFM_Array, 'last').and.returnValue(conversation[1]);
|
|
432
|
+
|
|
433
|
+
const result = (service as any).shortenConversation({
|
|
434
|
+
conversation: conversation,
|
|
435
|
+
issuer: 'test-issuer',
|
|
436
|
+
});
|
|
437
|
+
|
|
438
|
+
const systemMessages = result.filter(m => m.role === DyFM_AI_MessageRole.system);
|
|
439
|
+
expect(systemMessages.length).toBeGreaterThan(0);
|
|
440
|
+
});
|
|
441
|
+
|
|
442
|
+
it('| should throw error when no messages left', () => {
|
|
443
|
+
const conversation: DyFM_AI_Message[] = [];
|
|
444
|
+
|
|
445
|
+
const clonedConversation: DyFM_AI_Message[] = [];
|
|
446
|
+
spyOn(DyFM_Object, 'clone').and.returnValue(clonedConversation);
|
|
447
|
+
spyOn(DyFM_Log, 'error');
|
|
448
|
+
|
|
449
|
+
try {
|
|
450
|
+
(service as any).shortenConversation({
|
|
451
|
+
conversation: conversation,
|
|
452
|
+
issuer: 'test-issuer',
|
|
453
|
+
});
|
|
454
|
+
fail('Should have thrown an error');
|
|
455
|
+
} catch (error) {
|
|
456
|
+
expect(error).toBeInstanceOf(DyFM_Error);
|
|
457
|
+
expect((error as DyFM_Error)._errorCode).toContain('DyNTS-OAB-SC0');
|
|
458
|
+
}
|
|
459
|
+
});
|
|
460
|
+
});
|
|
461
|
+
});
|
|
462
|
+
|