@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
package/src/_modules/ai/_modules/open-ai/_services/data-services/oai-vector-data.service.spec.ts
CHANGED
|
@@ -1,550 +1,550 @@
|
|
|
1
|
-
|
|
2
|
-
import { DyNTS_OAI_VectorDataService } from './oai-vector-data.service';
|
|
3
|
-
import { DyFM_Metadata, DyFM_DataModel_Params, DyFM_DataProperty_Params, DyFM_BasicProperty_Type, DyFM_Error, DyFM_Object, DyFM_EnvironmentFlag } from '@futdevpro/fsm-dynamo';
|
|
4
|
-
import { DyFM_OAI_Settings, DyFM_OAI_Model } from '@futdevpro/fsm-dynamo/ai/open-ai';
|
|
5
|
-
import { DyNTS_OAI_Embedding_ControlService } from '../oai-embedding.control-service';
|
|
6
|
-
import { DyNTS_DataService } from '../../../../../../_services/base/data.service';
|
|
7
|
-
import { DyNTS_GlobalService } from '../../../../../../_services/core/global.service';
|
|
8
|
-
import { CreateEmbeddingResponse } from 'openai/resources';
|
|
9
|
-
import { DyNTS_global_settings } from '../../../../../../_collections/global-settings.const';
|
|
10
|
-
|
|
11
|
-
class TestMetadata extends DyFM_Metadata {
|
|
12
|
-
name: string;
|
|
13
|
-
content: string;
|
|
14
|
-
contentVectorized?: number[];
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
const testDataParams = new DyFM_DataModel_Params<TestMetadata>({
|
|
18
|
-
dataName: 'test_data',
|
|
19
|
-
properties: {
|
|
20
|
-
name: {
|
|
21
|
-
key: 'name',
|
|
22
|
-
type: DyFM_BasicProperty_Type.string,
|
|
23
|
-
required: true,
|
|
24
|
-
},
|
|
25
|
-
content: {
|
|
26
|
-
key: 'content',
|
|
27
|
-
type: DyFM_BasicProperty_Type.string,
|
|
28
|
-
},
|
|
29
|
-
contentVectorized: {
|
|
30
|
-
key: 'contentVectorized',
|
|
31
|
-
type: 'number[]', // Must be number[] for vectorized property
|
|
32
|
-
vectorizedFrom: ['content'],
|
|
33
|
-
embeddingModel: DyFM_OAI_Model.textEmbedding_3Small,
|
|
34
|
-
vectorizeUseIndex: 'content_vector_index',
|
|
35
|
-
},
|
|
36
|
-
},
|
|
37
|
-
});
|
|
38
|
-
|
|
39
|
-
describe('| DyNTS_OAI_VectorDataService', () => {
|
|
40
|
-
let service: DyNTS_OAI_VectorDataService<TestMetadata>;
|
|
41
|
-
let mockEmbedding_CS: jasmine.SpyObj<DyNTS_OAI_Embedding_ControlService>;
|
|
42
|
-
let mockDBService: jasmine.SpyObj<any>;
|
|
43
|
-
let testData: TestMetadata;
|
|
44
|
-
|
|
45
|
-
const mockOpenAISettings: DyFM_OAI_Settings = {
|
|
46
|
-
config: {
|
|
47
|
-
apiKey: 'test-api-key',
|
|
48
|
-
organization: 'test-org',
|
|
49
|
-
},
|
|
50
|
-
defaultSettings: {
|
|
51
|
-
useModel: DyFM_OAI_Model.textEmbedding_3Small,
|
|
52
|
-
},
|
|
53
|
-
};
|
|
54
|
-
|
|
55
|
-
beforeEach(() => {
|
|
56
|
-
DyNTS_global_settings.env_settings = {
|
|
57
|
-
environment: DyFM_EnvironmentFlag.local,
|
|
58
|
-
mongoUri: 'mongodb://localhost:27017/test',
|
|
59
|
-
} as typeof DyNTS_global_settings.env_settings;
|
|
60
|
-
|
|
61
|
-
testData = Object.assign(new TestMetadata(), {
|
|
62
|
-
_id: 'test-id-123',
|
|
63
|
-
content: 'Test content',
|
|
64
|
-
});
|
|
65
|
-
|
|
66
|
-
mockDBService = jasmine.createSpyObj('DyNTS_DBService', [
|
|
67
|
-
'getAll', 'getDataById', 'aggregate', 'createData', 'updateOne', 'find', 'findOne',
|
|
68
|
-
'getDataListByIds', 'getDataByDependencyId', 'getDataListByDependencyId',
|
|
69
|
-
'getDataListByDependencyIds', 'modifyData', 'markDeletedById', 'trueDeleteDataById',
|
|
70
|
-
'trueDeleteAllData', 'restoreDeletedById',
|
|
71
|
-
]);
|
|
72
|
-
|
|
73
|
-
mockEmbedding_CS = jasmine.createSpyObj('DyNTS_OAI_Embedding_ControlService', ['createEmbedding']);
|
|
74
|
-
|
|
75
|
-
spyOn(DyNTS_GlobalService, 'getDBService').and.returnValue(mockDBService as never);
|
|
76
|
-
spyOn(DyNTS_GlobalService, 'getDBServiceByKey').and.returnValue(mockDBService as never);
|
|
77
|
-
|
|
78
|
-
spyOn(DyNTS_DataService.prototype, 'constructor' as never).and.callThrough();
|
|
79
|
-
spyOn(DyNTS_DataService.prototype, 'getDataById').and.returnValue(Promise.resolve(testData));
|
|
80
|
-
spyOn(DyNTS_DataService.prototype, 'saveData').and.returnValue(Promise.resolve(testData));
|
|
81
|
-
spyOn(DyNTS_DataService.prototype as unknown as { ensureData: (d?: unknown) => TestMetadata }, 'ensureData').and.returnValue(testData);
|
|
82
|
-
|
|
83
|
-
service = new DyNTS_OAI_VectorDataService<TestMetadata>(
|
|
84
|
-
testData,
|
|
85
|
-
testDataParams,
|
|
86
|
-
mockOpenAISettings,
|
|
87
|
-
'test-issuer'
|
|
88
|
-
);
|
|
89
|
-
|
|
90
|
-
(service as { dataDBService: typeof mockDBService }).dataDBService = mockDBService;
|
|
91
|
-
(service as unknown as { embedding_CS: typeof mockEmbedding_CS }).embedding_CS = mockEmbedding_CS;
|
|
92
|
-
});
|
|
93
|
-
|
|
94
|
-
describe('| constructor', () => {
|
|
95
|
-
it('| should initialize with data, params, and settings', () => {
|
|
96
|
-
expect(service.data).toBe(testData);
|
|
97
|
-
expect(service.dataParams).toBe(testDataParams);
|
|
98
|
-
expect((service as any).openAISettings).toEqual(mockOpenAISettings);
|
|
99
|
-
expect(service.issuer).toBe('test-issuer');
|
|
100
|
-
});
|
|
101
|
-
|
|
102
|
-
it('| should identify vectorized properties', () => {
|
|
103
|
-
expect((service as any).vectorizedProperties).toBeDefined();
|
|
104
|
-
expect((service as any).vectorizedProperties.length).toBeGreaterThan(0);
|
|
105
|
-
});
|
|
106
|
-
|
|
107
|
-
it('| should identify vectorize properties', () => {
|
|
108
|
-
expect((service as any).vectorizeProperties).toBeDefined();
|
|
109
|
-
expect((service as any).vectorizeProperties.length).toBeGreaterThan(0);
|
|
110
|
-
});
|
|
111
|
-
|
|
112
|
-
it('| should throw error if apiKey not provided', () => {
|
|
113
|
-
const invalidSettings: DyFM_OAI_Settings = {
|
|
114
|
-
config: {
|
|
115
|
-
apiKey: '',
|
|
116
|
-
},
|
|
117
|
-
defaultSettings: {
|
|
118
|
-
useModel: DyFM_OAI_Model.textEmbedding_3Small,
|
|
119
|
-
},
|
|
120
|
-
};
|
|
121
|
-
|
|
122
|
-
expect(() => {
|
|
123
|
-
new DyNTS_OAI_VectorDataService<TestMetadata>(
|
|
124
|
-
testData,
|
|
125
|
-
testDataParams,
|
|
126
|
-
invalidSettings,
|
|
127
|
-
'test-issuer'
|
|
128
|
-
);
|
|
129
|
-
}).toThrow();
|
|
130
|
-
});
|
|
131
|
-
|
|
132
|
-
it('| should throw error if vectorizedProperties missing', () => {
|
|
133
|
-
const paramsWithoutVectorized = new DyFM_DataModel_Params<TestMetadata>({
|
|
134
|
-
dataName: 'test_data',
|
|
135
|
-
properties: {
|
|
136
|
-
name: {
|
|
137
|
-
key: 'name',
|
|
138
|
-
type: DyFM_BasicProperty_Type.string,
|
|
139
|
-
},
|
|
140
|
-
content: {
|
|
141
|
-
key: 'content',
|
|
142
|
-
type: DyFM_BasicProperty_Type.string,
|
|
143
|
-
},
|
|
144
|
-
},
|
|
145
|
-
});
|
|
146
|
-
|
|
147
|
-
expect(() => {
|
|
148
|
-
new DyNTS_OAI_VectorDataService<TestMetadata>(
|
|
149
|
-
testData,
|
|
150
|
-
paramsWithoutVectorized,
|
|
151
|
-
mockOpenAISettings,
|
|
152
|
-
'test-issuer'
|
|
153
|
-
);
|
|
154
|
-
}).toThrow();
|
|
155
|
-
});
|
|
156
|
-
});
|
|
157
|
-
|
|
158
|
-
describe('| vectorize', () => {
|
|
159
|
-
it('| should create embedding for input', async () => {
|
|
160
|
-
const mockResponse: CreateEmbeddingResponse = {
|
|
161
|
-
data: [
|
|
162
|
-
{
|
|
163
|
-
embedding: [0.1, 0.2, 0.3],
|
|
164
|
-
index: 0,
|
|
165
|
-
object: 'embedding',
|
|
166
|
-
},
|
|
167
|
-
],
|
|
168
|
-
model: 'text-embedding-3-small',
|
|
169
|
-
object: 'list',
|
|
170
|
-
usage: {
|
|
171
|
-
prompt_tokens: 5,
|
|
172
|
-
total_tokens: 5,
|
|
173
|
-
},
|
|
174
|
-
};
|
|
175
|
-
|
|
176
|
-
mockEmbedding_CS.createEmbedding.and.returnValue(Promise.resolve(mockResponse));
|
|
177
|
-
|
|
178
|
-
const result = await service.vectorize('Test input');
|
|
179
|
-
|
|
180
|
-
expect(result).toEqual([0.1, 0.2, 0.3]);
|
|
181
|
-
expect(mockEmbedding_CS.createEmbedding).toHaveBeenCalledWith({
|
|
182
|
-
text: 'Test input',
|
|
183
|
-
model: DyFM_OAI_Model.textEmbedding_3Small,
|
|
184
|
-
fullResponse: true,
|
|
185
|
-
issuer: 'test-issuer',
|
|
186
|
-
});
|
|
187
|
-
expect((service as any).tokenSpent).toBe(5);
|
|
188
|
-
});
|
|
189
|
-
|
|
190
|
-
it('| should throw error if input is empty', async () => {
|
|
191
|
-
try {
|
|
192
|
-
await service.vectorize('');
|
|
193
|
-
fail('Should have thrown an error');
|
|
194
|
-
} catch (error) {
|
|
195
|
-
expect(error).toBeInstanceOf(DyFM_Error);
|
|
196
|
-
expect((error as DyFM_Error)._errorCode).toContain('DyNTS-VDB-V1');
|
|
197
|
-
}
|
|
198
|
-
});
|
|
199
|
-
|
|
200
|
-
it('| should handle embedding errors', async () => {
|
|
201
|
-
const error = new Error('Embedding failed');
|
|
202
|
-
mockEmbedding_CS.createEmbedding.and.returnValue(Promise.reject(error));
|
|
203
|
-
|
|
204
|
-
try {
|
|
205
|
-
await service.vectorize('Test input');
|
|
206
|
-
fail('Should have thrown an error');
|
|
207
|
-
} catch (err) {
|
|
208
|
-
expect(err).toBeInstanceOf(DyFM_Error);
|
|
209
|
-
expect((err as DyFM_Error)._errorCode).toContain('DyNTS-VDB-V0');
|
|
210
|
-
}
|
|
211
|
-
});
|
|
212
|
-
});
|
|
213
|
-
|
|
214
|
-
describe('| vectorizeDataProperties', () => {
|
|
215
|
-
it('| should vectorize properties when data is new', async () => {
|
|
216
|
-
const newData: TestMetadata = Object.assign(new TestMetadata(), {
|
|
217
|
-
name: 'New Name',
|
|
218
|
-
content: 'New content',
|
|
219
|
-
});
|
|
220
|
-
|
|
221
|
-
(DyNTS_DataService.prototype.getDataById as jasmine.Spy).and.returnValue(Promise.resolve(null));
|
|
222
|
-
spyOn(service, 'vectorize').and.returnValue(Promise.resolve([0.1, 0.2, 0.3]));
|
|
223
|
-
|
|
224
|
-
const result = await service.vectorizeDataProperties(newData);
|
|
225
|
-
|
|
226
|
-
expect(service.vectorize).toHaveBeenCalled();
|
|
227
|
-
expect(result.contentVectorized).toEqual([0.1, 0.2, 0.3]);
|
|
228
|
-
});
|
|
229
|
-
|
|
230
|
-
it('| should not vectorize if data unchanged', async () => {
|
|
231
|
-
const existingData: TestMetadata = Object.assign(new TestMetadata(), {
|
|
232
|
-
_id: 'test-id-123',
|
|
233
|
-
content: 'Test content',
|
|
234
|
-
contentVectorized: [0.1, 0.2, 0.3],
|
|
235
|
-
});
|
|
236
|
-
|
|
237
|
-
const newData: TestMetadata = Object.assign(new TestMetadata(), {
|
|
238
|
-
_id: 'test-id-123',
|
|
239
|
-
content: 'Test content',
|
|
240
|
-
});
|
|
241
|
-
|
|
242
|
-
(DyNTS_DataService.prototype.getDataById as jasmine.Spy).and.returnValue(Promise.resolve(existingData));
|
|
243
|
-
spyOn(service, 'vectorize').and.returnValue(Promise.resolve([0.4, 0.5, 0.6]));
|
|
244
|
-
|
|
245
|
-
const result = await service.vectorizeDataProperties(newData);
|
|
246
|
-
|
|
247
|
-
expect(service.vectorize).not.toHaveBeenCalled();
|
|
248
|
-
expect(result.contentVectorized).toEqual([0.1, 0.2, 0.3]);
|
|
249
|
-
});
|
|
250
|
-
|
|
251
|
-
it('| should vectorize if forceVectorize is true', async () => {
|
|
252
|
-
const existingData: TestMetadata = Object.assign(new TestMetadata(), {
|
|
253
|
-
_id: 'test-id-123',
|
|
254
|
-
content: 'Test content',
|
|
255
|
-
contentVectorized: [0.1, 0.2, 0.3],
|
|
256
|
-
});
|
|
257
|
-
|
|
258
|
-
const newData: TestMetadata = Object.assign(new TestMetadata(), {
|
|
259
|
-
_id: 'test-id-123',
|
|
260
|
-
content: 'Test content',
|
|
261
|
-
});
|
|
262
|
-
|
|
263
|
-
(DyNTS_DataService.prototype.getDataById as jasmine.Spy).and.returnValue(Promise.resolve(existingData));
|
|
264
|
-
spyOn(service, 'vectorize').and.returnValue(Promise.resolve([0.4, 0.5, 0.6]));
|
|
265
|
-
|
|
266
|
-
const result = await service.vectorizeDataProperties(newData, true);
|
|
267
|
-
|
|
268
|
-
expect(service.vectorize).toHaveBeenCalled();
|
|
269
|
-
expect(result.contentVectorized).toEqual([0.4, 0.5, 0.6]);
|
|
270
|
-
});
|
|
271
|
-
|
|
272
|
-
it('| should throw error if embedding model not set', async () => {
|
|
273
|
-
const newData: TestMetadata = Object.assign(new TestMetadata(), {
|
|
274
|
-
name: 'New Name',
|
|
275
|
-
content: 'New content',
|
|
276
|
-
});
|
|
277
|
-
|
|
278
|
-
const paramsWithoutModel = new DyFM_DataModel_Params<TestMetadata>({
|
|
279
|
-
dataName: 'test_data',
|
|
280
|
-
properties: {
|
|
281
|
-
name: {
|
|
282
|
-
key: 'name',
|
|
283
|
-
type: DyFM_BasicProperty_Type.string,
|
|
284
|
-
},
|
|
285
|
-
content: {
|
|
286
|
-
key: 'content',
|
|
287
|
-
type: DyFM_BasicProperty_Type.string,
|
|
288
|
-
},
|
|
289
|
-
contentVectorized: {
|
|
290
|
-
key: 'contentVectorized',
|
|
291
|
-
type: DyFM_BasicProperty_Type.array,
|
|
292
|
-
vectorizedFrom: ['content'],
|
|
293
|
-
// embeddingModel not set
|
|
294
|
-
},
|
|
295
|
-
},
|
|
296
|
-
});
|
|
297
|
-
|
|
298
|
-
const serviceWithoutModel = new DyNTS_OAI_VectorDataService<TestMetadata>(
|
|
299
|
-
testData,
|
|
300
|
-
paramsWithoutModel,
|
|
301
|
-
mockOpenAISettings,
|
|
302
|
-
'test-issuer'
|
|
303
|
-
);
|
|
304
|
-
|
|
305
|
-
(serviceWithoutModel as any).vectorizedProperties[0].embeddingModel = undefined;
|
|
306
|
-
(DyNTS_DataService.prototype.getDataById as jasmine.Spy).and.returnValue(Promise.resolve(null));
|
|
307
|
-
|
|
308
|
-
try {
|
|
309
|
-
await serviceWithoutModel.vectorizeDataProperties(newData);
|
|
310
|
-
fail('Should have thrown an error');
|
|
311
|
-
} catch (error) {
|
|
312
|
-
expect(error).toBeInstanceOf(DyFM_Error);
|
|
313
|
-
expect((error as DyFM_Error)._errorCode).toContain('DyNTS-VDB-VDB1');
|
|
314
|
-
}
|
|
315
|
-
});
|
|
316
|
-
});
|
|
317
|
-
|
|
318
|
-
describe('| vectorSearch', () => {
|
|
319
|
-
it('| should perform vector search', async () => {
|
|
320
|
-
const mockResponse: CreateEmbeddingResponse = {
|
|
321
|
-
data: [
|
|
322
|
-
{
|
|
323
|
-
embedding: [0.1, 0.2, 0.3],
|
|
324
|
-
index: 0,
|
|
325
|
-
object: 'embedding',
|
|
326
|
-
},
|
|
327
|
-
],
|
|
328
|
-
model: 'text-embedding-3-small',
|
|
329
|
-
object: 'list',
|
|
330
|
-
usage: {
|
|
331
|
-
prompt_tokens: 5,
|
|
332
|
-
total_tokens: 5,
|
|
333
|
-
},
|
|
334
|
-
};
|
|
335
|
-
|
|
336
|
-
mockEmbedding_CS.createEmbedding.and.returnValue(Promise.resolve(mockResponse));
|
|
337
|
-
mockDBService.aggregate.and.returnValue(Promise.resolve([testData]));
|
|
338
|
-
|
|
339
|
-
const result = await service.vectorSearch({
|
|
340
|
-
input: 'Test search',
|
|
341
|
-
searchInKey: 'contentVectorized',
|
|
342
|
-
});
|
|
343
|
-
|
|
344
|
-
expect(mockEmbedding_CS.createEmbedding).toHaveBeenCalled();
|
|
345
|
-
expect(mockDBService.aggregate).toHaveBeenCalled();
|
|
346
|
-
expect(result).toEqual([testData]);
|
|
347
|
-
});
|
|
348
|
-
|
|
349
|
-
it('| should use default limit and numberOfCandidates', async () => {
|
|
350
|
-
const mockResponse: CreateEmbeddingResponse = {
|
|
351
|
-
data: [
|
|
352
|
-
{
|
|
353
|
-
embedding: [0.1, 0.2, 0.3],
|
|
354
|
-
index: 0,
|
|
355
|
-
object: 'embedding',
|
|
356
|
-
},
|
|
357
|
-
],
|
|
358
|
-
model: 'text-embedding-3-small',
|
|
359
|
-
object: 'list',
|
|
360
|
-
usage: {
|
|
361
|
-
prompt_tokens: 5,
|
|
362
|
-
total_tokens: 5,
|
|
363
|
-
},
|
|
364
|
-
};
|
|
365
|
-
|
|
366
|
-
mockEmbedding_CS.createEmbedding.and.returnValue(Promise.resolve(mockResponse));
|
|
367
|
-
mockDBService.aggregate.and.returnValue(Promise.resolve([]));
|
|
368
|
-
|
|
369
|
-
await service.vectorSearch({
|
|
370
|
-
input: 'Test search',
|
|
371
|
-
searchInKey: 'contentVectorized',
|
|
372
|
-
});
|
|
373
|
-
|
|
374
|
-
const aggregateCall = mockDBService.aggregate.calls.mostRecent().args[0][0];
|
|
375
|
-
expect(aggregateCall.$vectorSearch.limit).toBe(3);
|
|
376
|
-
expect(aggregateCall.$vectorSearch.numCandidates).toBe(100);
|
|
377
|
-
});
|
|
378
|
-
|
|
379
|
-
it('| should throw error if input is empty', async () => {
|
|
380
|
-
try {
|
|
381
|
-
await service.vectorSearch({
|
|
382
|
-
input: '',
|
|
383
|
-
searchInKey: 'contentVectorized',
|
|
384
|
-
});
|
|
385
|
-
fail('Should have thrown an error');
|
|
386
|
-
} catch (error) {
|
|
387
|
-
expect(error).toBeInstanceOf(DyFM_Error);
|
|
388
|
-
expect((error as DyFM_Error)._errorCode).toContain('DyNTS-VDB-VS1');
|
|
389
|
-
}
|
|
390
|
-
});
|
|
391
|
-
|
|
392
|
-
it('| should throw error if property not found', async () => {
|
|
393
|
-
try {
|
|
394
|
-
await service.vectorSearch({
|
|
395
|
-
input: 'Test search',
|
|
396
|
-
searchInKey: 'non-existent',
|
|
397
|
-
});
|
|
398
|
-
fail('Should have thrown an error');
|
|
399
|
-
} catch (error) {
|
|
400
|
-
expect(error).toBeInstanceOf(DyFM_Error);
|
|
401
|
-
expect((error as DyFM_Error)._errorCode).toContain('DyNTS-VDB-VS2');
|
|
402
|
-
}
|
|
403
|
-
});
|
|
404
|
-
|
|
405
|
-
it('| should throw error if property has no vectorizeUseIndex', async () => {
|
|
406
|
-
const paramsWithoutIndex = new DyFM_DataModel_Params<TestMetadata>({
|
|
407
|
-
dataName: 'test_data',
|
|
408
|
-
properties: {
|
|
409
|
-
name: {
|
|
410
|
-
key: 'name',
|
|
411
|
-
type: DyFM_BasicProperty_Type.string,
|
|
412
|
-
},
|
|
413
|
-
content: {
|
|
414
|
-
key: 'content',
|
|
415
|
-
type: DyFM_BasicProperty_Type.string,
|
|
416
|
-
},
|
|
417
|
-
contentVectorized: {
|
|
418
|
-
key: 'contentVectorized',
|
|
419
|
-
type: DyFM_BasicProperty_Type.array,
|
|
420
|
-
vectorizedFrom: ['content'],
|
|
421
|
-
embeddingModel: DyFM_OAI_Model.textEmbedding_3Small,
|
|
422
|
-
// vectorizeUseIndex not set
|
|
423
|
-
},
|
|
424
|
-
},
|
|
425
|
-
});
|
|
426
|
-
|
|
427
|
-
const serviceWithoutIndex = new DyNTS_OAI_VectorDataService<TestMetadata>(
|
|
428
|
-
testData,
|
|
429
|
-
paramsWithoutIndex,
|
|
430
|
-
mockOpenAISettings,
|
|
431
|
-
'test-issuer'
|
|
432
|
-
);
|
|
433
|
-
|
|
434
|
-
(serviceWithoutIndex as any).dataDBService = mockDBService;
|
|
435
|
-
(serviceWithoutIndex as any).embedding_CS = mockEmbedding_CS;
|
|
436
|
-
|
|
437
|
-
try {
|
|
438
|
-
await serviceWithoutIndex.vectorSearch({
|
|
439
|
-
input: 'Test search',
|
|
440
|
-
searchInKey: 'contentVectorized',
|
|
441
|
-
});
|
|
442
|
-
fail('Should have thrown an error');
|
|
443
|
-
} catch (error) {
|
|
444
|
-
expect(error).toBeInstanceOf(DyFM_Error);
|
|
445
|
-
}
|
|
446
|
-
});
|
|
447
|
-
});
|
|
448
|
-
|
|
449
|
-
describe('| saveData', () => {
|
|
450
|
-
it('| should vectorize properties before saving', async () => {
|
|
451
|
-
const newData: TestMetadata = Object.assign(new TestMetadata(), {
|
|
452
|
-
name: 'New Name',
|
|
453
|
-
content: 'New content',
|
|
454
|
-
});
|
|
455
|
-
|
|
456
|
-
(DyNTS_DataService.prototype as unknown as { ensureData: jasmine.Spy }).ensureData.and.returnValue(newData);
|
|
457
|
-
spyOn(service, 'vectorizeDataProperties').and.returnValue(Promise.resolve(newData));
|
|
458
|
-
(DyNTS_DataService.prototype.saveData as jasmine.Spy).and.returnValue(Promise.resolve(newData));
|
|
459
|
-
|
|
460
|
-
const result = await service.saveData(newData);
|
|
461
|
-
|
|
462
|
-
expect(service.vectorizeDataProperties).toHaveBeenCalledWith(newData, undefined);
|
|
463
|
-
expect(result).toBeDefined();
|
|
464
|
-
});
|
|
465
|
-
|
|
466
|
-
it('| should remove vectorized properties if swallowVectorizedProperties is true', async () => {
|
|
467
|
-
const newData: TestMetadata = Object.assign(new TestMetadata(), {
|
|
468
|
-
name: 'New Name',
|
|
469
|
-
content: 'New content',
|
|
470
|
-
contentVectorized: [0.1, 0.2, 0.3],
|
|
471
|
-
});
|
|
472
|
-
|
|
473
|
-
(service as any).swallowVectorizedProperties = true;
|
|
474
|
-
spyOn(service, 'vectorizeDataProperties').and.returnValue(Promise.resolve(newData));
|
|
475
|
-
(DyNTS_DataService.prototype.saveData as jasmine.Spy).and.returnValue(Promise.resolve(newData));
|
|
476
|
-
spyOn(DyFM_Object, 'cleanObject');
|
|
477
|
-
|
|
478
|
-
const result = await service.saveData(newData);
|
|
479
|
-
|
|
480
|
-
expect(result.contentVectorized).toBeUndefined();
|
|
481
|
-
expect(DyFM_Object.cleanObject).toHaveBeenCalled();
|
|
482
|
-
});
|
|
483
|
-
|
|
484
|
-
it('| should keep vectorized properties if swallowVectorizedProperties is false', async () => {
|
|
485
|
-
const newData: TestMetadata = Object.assign(new TestMetadata(), {
|
|
486
|
-
name: 'New Name',
|
|
487
|
-
content: 'New content',
|
|
488
|
-
contentVectorized: [0.1, 0.2, 0.3],
|
|
489
|
-
});
|
|
490
|
-
|
|
491
|
-
(service as any).swallowVectorizedProperties = false;
|
|
492
|
-
spyOn(service, 'vectorizeDataProperties').and.returnValue(Promise.resolve(newData));
|
|
493
|
-
(DyNTS_DataService.prototype.saveData as jasmine.Spy).and.returnValue(Promise.resolve(newData));
|
|
494
|
-
|
|
495
|
-
const result = await service.saveData(newData);
|
|
496
|
-
|
|
497
|
-
expect(result.contentVectorized).toBeDefined();
|
|
498
|
-
});
|
|
499
|
-
|
|
500
|
-
it('| should handle save errors', async () => {
|
|
501
|
-
const newData: TestMetadata = Object.assign(new TestMetadata(), {
|
|
502
|
-
name: 'New Name',
|
|
503
|
-
content: 'New content',
|
|
504
|
-
});
|
|
505
|
-
|
|
506
|
-
const error = new Error('Save failed');
|
|
507
|
-
spyOn(service, 'vectorizeDataProperties').and.returnValue(Promise.resolve(newData));
|
|
508
|
-
(DyNTS_DataService.prototype.saveData as jasmine.Spy).and.returnValue(Promise.reject(error));
|
|
509
|
-
|
|
510
|
-
try {
|
|
511
|
-
await service.saveData(newData);
|
|
512
|
-
fail('Should have thrown an error');
|
|
513
|
-
} catch (err) {
|
|
514
|
-
expect(err).toBeInstanceOf(DyFM_Error);
|
|
515
|
-
expect((err as DyFM_Error)._errorCode).toContain('DyNTS-VDB-SD0');
|
|
516
|
-
}
|
|
517
|
-
});
|
|
518
|
-
});
|
|
519
|
-
|
|
520
|
-
describe('| properties', () => {
|
|
521
|
-
it('| should have tokenSpent property', () => {
|
|
522
|
-
expect((service as any).tokenSpent).toBe(0);
|
|
523
|
-
});
|
|
524
|
-
|
|
525
|
-
it('| should track token usage', async () => {
|
|
526
|
-
const mockResponse: CreateEmbeddingResponse = {
|
|
527
|
-
data: [
|
|
528
|
-
{
|
|
529
|
-
embedding: [0.1, 0.2, 0.3],
|
|
530
|
-
index: 0,
|
|
531
|
-
object: 'embedding',
|
|
532
|
-
},
|
|
533
|
-
],
|
|
534
|
-
model: 'text-embedding-3-small',
|
|
535
|
-
object: 'list',
|
|
536
|
-
usage: {
|
|
537
|
-
prompt_tokens: 10,
|
|
538
|
-
total_tokens: 10,
|
|
539
|
-
},
|
|
540
|
-
};
|
|
541
|
-
|
|
542
|
-
mockEmbedding_CS.createEmbedding.and.returnValue(Promise.resolve(mockResponse));
|
|
543
|
-
|
|
544
|
-
await service.vectorize('Test input');
|
|
545
|
-
|
|
546
|
-
expect((service as any).tokenSpent).toBe(10);
|
|
547
|
-
});
|
|
548
|
-
});
|
|
549
|
-
});
|
|
550
|
-
|
|
1
|
+
|
|
2
|
+
import { DyNTS_OAI_VectorDataService } from './oai-vector-data.service';
|
|
3
|
+
import { DyFM_Metadata, DyFM_DataModel_Params, DyFM_DataProperty_Params, DyFM_BasicProperty_Type, DyFM_Error, DyFM_Object, DyFM_EnvironmentFlag } from '@futdevpro/fsm-dynamo';
|
|
4
|
+
import { DyFM_OAI_Settings, DyFM_OAI_Model } from '@futdevpro/fsm-dynamo/ai/open-ai';
|
|
5
|
+
import { DyNTS_OAI_Embedding_ControlService } from '../oai-embedding.control-service';
|
|
6
|
+
import { DyNTS_DataService } from '../../../../../../_services/base/data.service';
|
|
7
|
+
import { DyNTS_GlobalService } from '../../../../../../_services/core/global.service';
|
|
8
|
+
import { CreateEmbeddingResponse } from 'openai/resources';
|
|
9
|
+
import { DyNTS_global_settings } from '../../../../../../_collections/global-settings.const';
|
|
10
|
+
|
|
11
|
+
class TestMetadata extends DyFM_Metadata {
|
|
12
|
+
name: string;
|
|
13
|
+
content: string;
|
|
14
|
+
contentVectorized?: number[];
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
const testDataParams = new DyFM_DataModel_Params<TestMetadata>({
|
|
18
|
+
dataName: 'test_data',
|
|
19
|
+
properties: {
|
|
20
|
+
name: {
|
|
21
|
+
key: 'name',
|
|
22
|
+
type: DyFM_BasicProperty_Type.string,
|
|
23
|
+
required: true,
|
|
24
|
+
},
|
|
25
|
+
content: {
|
|
26
|
+
key: 'content',
|
|
27
|
+
type: DyFM_BasicProperty_Type.string,
|
|
28
|
+
},
|
|
29
|
+
contentVectorized: {
|
|
30
|
+
key: 'contentVectorized',
|
|
31
|
+
type: 'number[]', // Must be number[] for vectorized property
|
|
32
|
+
vectorizedFrom: ['content'],
|
|
33
|
+
embeddingModel: DyFM_OAI_Model.textEmbedding_3Small,
|
|
34
|
+
vectorizeUseIndex: 'content_vector_index',
|
|
35
|
+
},
|
|
36
|
+
},
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
describe('| DyNTS_OAI_VectorDataService', () => {
|
|
40
|
+
let service: DyNTS_OAI_VectorDataService<TestMetadata>;
|
|
41
|
+
let mockEmbedding_CS: jasmine.SpyObj<DyNTS_OAI_Embedding_ControlService>;
|
|
42
|
+
let mockDBService: jasmine.SpyObj<any>;
|
|
43
|
+
let testData: TestMetadata;
|
|
44
|
+
|
|
45
|
+
const mockOpenAISettings: DyFM_OAI_Settings = {
|
|
46
|
+
config: {
|
|
47
|
+
apiKey: 'test-api-key',
|
|
48
|
+
organization: 'test-org',
|
|
49
|
+
},
|
|
50
|
+
defaultSettings: {
|
|
51
|
+
useModel: DyFM_OAI_Model.textEmbedding_3Small,
|
|
52
|
+
},
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
beforeEach(() => {
|
|
56
|
+
DyNTS_global_settings.env_settings = {
|
|
57
|
+
environment: DyFM_EnvironmentFlag.local,
|
|
58
|
+
mongoUri: 'mongodb://localhost:27017/test',
|
|
59
|
+
} as typeof DyNTS_global_settings.env_settings;
|
|
60
|
+
|
|
61
|
+
testData = Object.assign(new TestMetadata(), {
|
|
62
|
+
_id: 'test-id-123',
|
|
63
|
+
content: 'Test content',
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
mockDBService = jasmine.createSpyObj('DyNTS_DBService', [
|
|
67
|
+
'getAll', 'getDataById', 'aggregate', 'createData', 'updateOne', 'find', 'findOne',
|
|
68
|
+
'getDataListByIds', 'getDataByDependencyId', 'getDataListByDependencyId',
|
|
69
|
+
'getDataListByDependencyIds', 'modifyData', 'markDeletedById', 'trueDeleteDataById',
|
|
70
|
+
'trueDeleteAllData', 'restoreDeletedById',
|
|
71
|
+
]);
|
|
72
|
+
|
|
73
|
+
mockEmbedding_CS = jasmine.createSpyObj('DyNTS_OAI_Embedding_ControlService', ['createEmbedding']);
|
|
74
|
+
|
|
75
|
+
spyOn(DyNTS_GlobalService, 'getDBService').and.returnValue(mockDBService as never);
|
|
76
|
+
spyOn(DyNTS_GlobalService, 'getDBServiceByKey').and.returnValue(mockDBService as never);
|
|
77
|
+
|
|
78
|
+
spyOn(DyNTS_DataService.prototype, 'constructor' as never).and.callThrough();
|
|
79
|
+
spyOn(DyNTS_DataService.prototype, 'getDataById').and.returnValue(Promise.resolve(testData));
|
|
80
|
+
spyOn(DyNTS_DataService.prototype, 'saveData').and.returnValue(Promise.resolve(testData));
|
|
81
|
+
spyOn(DyNTS_DataService.prototype as unknown as { ensureData: (d?: unknown) => TestMetadata }, 'ensureData').and.returnValue(testData);
|
|
82
|
+
|
|
83
|
+
service = new DyNTS_OAI_VectorDataService<TestMetadata>(
|
|
84
|
+
testData,
|
|
85
|
+
testDataParams,
|
|
86
|
+
mockOpenAISettings,
|
|
87
|
+
'test-issuer'
|
|
88
|
+
);
|
|
89
|
+
|
|
90
|
+
(service as { dataDBService: typeof mockDBService }).dataDBService = mockDBService;
|
|
91
|
+
(service as unknown as { embedding_CS: typeof mockEmbedding_CS }).embedding_CS = mockEmbedding_CS;
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
describe('| constructor', () => {
|
|
95
|
+
it('| should initialize with data, params, and settings', () => {
|
|
96
|
+
expect(service.data).toBe(testData);
|
|
97
|
+
expect(service.dataParams).toBe(testDataParams);
|
|
98
|
+
expect((service as any).openAISettings).toEqual(mockOpenAISettings);
|
|
99
|
+
expect(service.issuer).toBe('test-issuer');
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
it('| should identify vectorized properties', () => {
|
|
103
|
+
expect((service as any).vectorizedProperties).toBeDefined();
|
|
104
|
+
expect((service as any).vectorizedProperties.length).toBeGreaterThan(0);
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
it('| should identify vectorize properties', () => {
|
|
108
|
+
expect((service as any).vectorizeProperties).toBeDefined();
|
|
109
|
+
expect((service as any).vectorizeProperties.length).toBeGreaterThan(0);
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
it('| should throw error if apiKey not provided', () => {
|
|
113
|
+
const invalidSettings: DyFM_OAI_Settings = {
|
|
114
|
+
config: {
|
|
115
|
+
apiKey: '',
|
|
116
|
+
},
|
|
117
|
+
defaultSettings: {
|
|
118
|
+
useModel: DyFM_OAI_Model.textEmbedding_3Small,
|
|
119
|
+
},
|
|
120
|
+
};
|
|
121
|
+
|
|
122
|
+
expect(() => {
|
|
123
|
+
new DyNTS_OAI_VectorDataService<TestMetadata>(
|
|
124
|
+
testData,
|
|
125
|
+
testDataParams,
|
|
126
|
+
invalidSettings,
|
|
127
|
+
'test-issuer'
|
|
128
|
+
);
|
|
129
|
+
}).toThrow();
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
it('| should throw error if vectorizedProperties missing', () => {
|
|
133
|
+
const paramsWithoutVectorized = new DyFM_DataModel_Params<TestMetadata>({
|
|
134
|
+
dataName: 'test_data',
|
|
135
|
+
properties: {
|
|
136
|
+
name: {
|
|
137
|
+
key: 'name',
|
|
138
|
+
type: DyFM_BasicProperty_Type.string,
|
|
139
|
+
},
|
|
140
|
+
content: {
|
|
141
|
+
key: 'content',
|
|
142
|
+
type: DyFM_BasicProperty_Type.string,
|
|
143
|
+
},
|
|
144
|
+
},
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
expect(() => {
|
|
148
|
+
new DyNTS_OAI_VectorDataService<TestMetadata>(
|
|
149
|
+
testData,
|
|
150
|
+
paramsWithoutVectorized,
|
|
151
|
+
mockOpenAISettings,
|
|
152
|
+
'test-issuer'
|
|
153
|
+
);
|
|
154
|
+
}).toThrow();
|
|
155
|
+
});
|
|
156
|
+
});
|
|
157
|
+
|
|
158
|
+
describe('| vectorize', () => {
|
|
159
|
+
it('| should create embedding for input', async () => {
|
|
160
|
+
const mockResponse: CreateEmbeddingResponse = {
|
|
161
|
+
data: [
|
|
162
|
+
{
|
|
163
|
+
embedding: [0.1, 0.2, 0.3],
|
|
164
|
+
index: 0,
|
|
165
|
+
object: 'embedding',
|
|
166
|
+
},
|
|
167
|
+
],
|
|
168
|
+
model: 'text-embedding-3-small',
|
|
169
|
+
object: 'list',
|
|
170
|
+
usage: {
|
|
171
|
+
prompt_tokens: 5,
|
|
172
|
+
total_tokens: 5,
|
|
173
|
+
},
|
|
174
|
+
};
|
|
175
|
+
|
|
176
|
+
mockEmbedding_CS.createEmbedding.and.returnValue(Promise.resolve(mockResponse));
|
|
177
|
+
|
|
178
|
+
const result = await service.vectorize('Test input');
|
|
179
|
+
|
|
180
|
+
expect(result).toEqual([0.1, 0.2, 0.3]);
|
|
181
|
+
expect(mockEmbedding_CS.createEmbedding).toHaveBeenCalledWith({
|
|
182
|
+
text: 'Test input',
|
|
183
|
+
model: DyFM_OAI_Model.textEmbedding_3Small,
|
|
184
|
+
fullResponse: true,
|
|
185
|
+
issuer: 'test-issuer',
|
|
186
|
+
});
|
|
187
|
+
expect((service as any).tokenSpent).toBe(5);
|
|
188
|
+
});
|
|
189
|
+
|
|
190
|
+
it('| should throw error if input is empty', async () => {
|
|
191
|
+
try {
|
|
192
|
+
await service.vectorize('');
|
|
193
|
+
fail('Should have thrown an error');
|
|
194
|
+
} catch (error) {
|
|
195
|
+
expect(error).toBeInstanceOf(DyFM_Error);
|
|
196
|
+
expect((error as DyFM_Error)._errorCode).toContain('DyNTS-VDB-V1');
|
|
197
|
+
}
|
|
198
|
+
});
|
|
199
|
+
|
|
200
|
+
it('| should handle embedding errors', async () => {
|
|
201
|
+
const error = new Error('Embedding failed');
|
|
202
|
+
mockEmbedding_CS.createEmbedding.and.returnValue(Promise.reject(error));
|
|
203
|
+
|
|
204
|
+
try {
|
|
205
|
+
await service.vectorize('Test input');
|
|
206
|
+
fail('Should have thrown an error');
|
|
207
|
+
} catch (err) {
|
|
208
|
+
expect(err).toBeInstanceOf(DyFM_Error);
|
|
209
|
+
expect((err as DyFM_Error)._errorCode).toContain('DyNTS-VDB-V0');
|
|
210
|
+
}
|
|
211
|
+
});
|
|
212
|
+
});
|
|
213
|
+
|
|
214
|
+
describe('| vectorizeDataProperties', () => {
|
|
215
|
+
it('| should vectorize properties when data is new', async () => {
|
|
216
|
+
const newData: TestMetadata = Object.assign(new TestMetadata(), {
|
|
217
|
+
name: 'New Name',
|
|
218
|
+
content: 'New content',
|
|
219
|
+
});
|
|
220
|
+
|
|
221
|
+
(DyNTS_DataService.prototype.getDataById as jasmine.Spy).and.returnValue(Promise.resolve(null));
|
|
222
|
+
spyOn(service, 'vectorize').and.returnValue(Promise.resolve([0.1, 0.2, 0.3]));
|
|
223
|
+
|
|
224
|
+
const result = await service.vectorizeDataProperties(newData);
|
|
225
|
+
|
|
226
|
+
expect(service.vectorize).toHaveBeenCalled();
|
|
227
|
+
expect(result.contentVectorized).toEqual([0.1, 0.2, 0.3]);
|
|
228
|
+
});
|
|
229
|
+
|
|
230
|
+
it('| should not vectorize if data unchanged', async () => {
|
|
231
|
+
const existingData: TestMetadata = Object.assign(new TestMetadata(), {
|
|
232
|
+
_id: 'test-id-123',
|
|
233
|
+
content: 'Test content',
|
|
234
|
+
contentVectorized: [0.1, 0.2, 0.3],
|
|
235
|
+
});
|
|
236
|
+
|
|
237
|
+
const newData: TestMetadata = Object.assign(new TestMetadata(), {
|
|
238
|
+
_id: 'test-id-123',
|
|
239
|
+
content: 'Test content',
|
|
240
|
+
});
|
|
241
|
+
|
|
242
|
+
(DyNTS_DataService.prototype.getDataById as jasmine.Spy).and.returnValue(Promise.resolve(existingData));
|
|
243
|
+
spyOn(service, 'vectorize').and.returnValue(Promise.resolve([0.4, 0.5, 0.6]));
|
|
244
|
+
|
|
245
|
+
const result = await service.vectorizeDataProperties(newData);
|
|
246
|
+
|
|
247
|
+
expect(service.vectorize).not.toHaveBeenCalled();
|
|
248
|
+
expect(result.contentVectorized).toEqual([0.1, 0.2, 0.3]);
|
|
249
|
+
});
|
|
250
|
+
|
|
251
|
+
it('| should vectorize if forceVectorize is true', async () => {
|
|
252
|
+
const existingData: TestMetadata = Object.assign(new TestMetadata(), {
|
|
253
|
+
_id: 'test-id-123',
|
|
254
|
+
content: 'Test content',
|
|
255
|
+
contentVectorized: [0.1, 0.2, 0.3],
|
|
256
|
+
});
|
|
257
|
+
|
|
258
|
+
const newData: TestMetadata = Object.assign(new TestMetadata(), {
|
|
259
|
+
_id: 'test-id-123',
|
|
260
|
+
content: 'Test content',
|
|
261
|
+
});
|
|
262
|
+
|
|
263
|
+
(DyNTS_DataService.prototype.getDataById as jasmine.Spy).and.returnValue(Promise.resolve(existingData));
|
|
264
|
+
spyOn(service, 'vectorize').and.returnValue(Promise.resolve([0.4, 0.5, 0.6]));
|
|
265
|
+
|
|
266
|
+
const result = await service.vectorizeDataProperties(newData, true);
|
|
267
|
+
|
|
268
|
+
expect(service.vectorize).toHaveBeenCalled();
|
|
269
|
+
expect(result.contentVectorized).toEqual([0.4, 0.5, 0.6]);
|
|
270
|
+
});
|
|
271
|
+
|
|
272
|
+
it('| should throw error if embedding model not set', async () => {
|
|
273
|
+
const newData: TestMetadata = Object.assign(new TestMetadata(), {
|
|
274
|
+
name: 'New Name',
|
|
275
|
+
content: 'New content',
|
|
276
|
+
});
|
|
277
|
+
|
|
278
|
+
const paramsWithoutModel = new DyFM_DataModel_Params<TestMetadata>({
|
|
279
|
+
dataName: 'test_data',
|
|
280
|
+
properties: {
|
|
281
|
+
name: {
|
|
282
|
+
key: 'name',
|
|
283
|
+
type: DyFM_BasicProperty_Type.string,
|
|
284
|
+
},
|
|
285
|
+
content: {
|
|
286
|
+
key: 'content',
|
|
287
|
+
type: DyFM_BasicProperty_Type.string,
|
|
288
|
+
},
|
|
289
|
+
contentVectorized: {
|
|
290
|
+
key: 'contentVectorized',
|
|
291
|
+
type: DyFM_BasicProperty_Type.array,
|
|
292
|
+
vectorizedFrom: ['content'],
|
|
293
|
+
// embeddingModel not set
|
|
294
|
+
},
|
|
295
|
+
},
|
|
296
|
+
});
|
|
297
|
+
|
|
298
|
+
const serviceWithoutModel = new DyNTS_OAI_VectorDataService<TestMetadata>(
|
|
299
|
+
testData,
|
|
300
|
+
paramsWithoutModel,
|
|
301
|
+
mockOpenAISettings,
|
|
302
|
+
'test-issuer'
|
|
303
|
+
);
|
|
304
|
+
|
|
305
|
+
(serviceWithoutModel as any).vectorizedProperties[0].embeddingModel = undefined;
|
|
306
|
+
(DyNTS_DataService.prototype.getDataById as jasmine.Spy).and.returnValue(Promise.resolve(null));
|
|
307
|
+
|
|
308
|
+
try {
|
|
309
|
+
await serviceWithoutModel.vectorizeDataProperties(newData);
|
|
310
|
+
fail('Should have thrown an error');
|
|
311
|
+
} catch (error) {
|
|
312
|
+
expect(error).toBeInstanceOf(DyFM_Error);
|
|
313
|
+
expect((error as DyFM_Error)._errorCode).toContain('DyNTS-VDB-VDB1');
|
|
314
|
+
}
|
|
315
|
+
});
|
|
316
|
+
});
|
|
317
|
+
|
|
318
|
+
describe('| vectorSearch', () => {
|
|
319
|
+
it('| should perform vector search', async () => {
|
|
320
|
+
const mockResponse: CreateEmbeddingResponse = {
|
|
321
|
+
data: [
|
|
322
|
+
{
|
|
323
|
+
embedding: [0.1, 0.2, 0.3],
|
|
324
|
+
index: 0,
|
|
325
|
+
object: 'embedding',
|
|
326
|
+
},
|
|
327
|
+
],
|
|
328
|
+
model: 'text-embedding-3-small',
|
|
329
|
+
object: 'list',
|
|
330
|
+
usage: {
|
|
331
|
+
prompt_tokens: 5,
|
|
332
|
+
total_tokens: 5,
|
|
333
|
+
},
|
|
334
|
+
};
|
|
335
|
+
|
|
336
|
+
mockEmbedding_CS.createEmbedding.and.returnValue(Promise.resolve(mockResponse));
|
|
337
|
+
mockDBService.aggregate.and.returnValue(Promise.resolve([testData]));
|
|
338
|
+
|
|
339
|
+
const result = await service.vectorSearch({
|
|
340
|
+
input: 'Test search',
|
|
341
|
+
searchInKey: 'contentVectorized',
|
|
342
|
+
});
|
|
343
|
+
|
|
344
|
+
expect(mockEmbedding_CS.createEmbedding).toHaveBeenCalled();
|
|
345
|
+
expect(mockDBService.aggregate).toHaveBeenCalled();
|
|
346
|
+
expect(result).toEqual([testData]);
|
|
347
|
+
});
|
|
348
|
+
|
|
349
|
+
it('| should use default limit and numberOfCandidates', async () => {
|
|
350
|
+
const mockResponse: CreateEmbeddingResponse = {
|
|
351
|
+
data: [
|
|
352
|
+
{
|
|
353
|
+
embedding: [0.1, 0.2, 0.3],
|
|
354
|
+
index: 0,
|
|
355
|
+
object: 'embedding',
|
|
356
|
+
},
|
|
357
|
+
],
|
|
358
|
+
model: 'text-embedding-3-small',
|
|
359
|
+
object: 'list',
|
|
360
|
+
usage: {
|
|
361
|
+
prompt_tokens: 5,
|
|
362
|
+
total_tokens: 5,
|
|
363
|
+
},
|
|
364
|
+
};
|
|
365
|
+
|
|
366
|
+
mockEmbedding_CS.createEmbedding.and.returnValue(Promise.resolve(mockResponse));
|
|
367
|
+
mockDBService.aggregate.and.returnValue(Promise.resolve([]));
|
|
368
|
+
|
|
369
|
+
await service.vectorSearch({
|
|
370
|
+
input: 'Test search',
|
|
371
|
+
searchInKey: 'contentVectorized',
|
|
372
|
+
});
|
|
373
|
+
|
|
374
|
+
const aggregateCall = mockDBService.aggregate.calls.mostRecent().args[0][0];
|
|
375
|
+
expect(aggregateCall.$vectorSearch.limit).toBe(3);
|
|
376
|
+
expect(aggregateCall.$vectorSearch.numCandidates).toBe(100);
|
|
377
|
+
});
|
|
378
|
+
|
|
379
|
+
it('| should throw error if input is empty', async () => {
|
|
380
|
+
try {
|
|
381
|
+
await service.vectorSearch({
|
|
382
|
+
input: '',
|
|
383
|
+
searchInKey: 'contentVectorized',
|
|
384
|
+
});
|
|
385
|
+
fail('Should have thrown an error');
|
|
386
|
+
} catch (error) {
|
|
387
|
+
expect(error).toBeInstanceOf(DyFM_Error);
|
|
388
|
+
expect((error as DyFM_Error)._errorCode).toContain('DyNTS-VDB-VS1');
|
|
389
|
+
}
|
|
390
|
+
});
|
|
391
|
+
|
|
392
|
+
it('| should throw error if property not found', async () => {
|
|
393
|
+
try {
|
|
394
|
+
await service.vectorSearch({
|
|
395
|
+
input: 'Test search',
|
|
396
|
+
searchInKey: 'non-existent',
|
|
397
|
+
});
|
|
398
|
+
fail('Should have thrown an error');
|
|
399
|
+
} catch (error) {
|
|
400
|
+
expect(error).toBeInstanceOf(DyFM_Error);
|
|
401
|
+
expect((error as DyFM_Error)._errorCode).toContain('DyNTS-VDB-VS2');
|
|
402
|
+
}
|
|
403
|
+
});
|
|
404
|
+
|
|
405
|
+
it('| should throw error if property has no vectorizeUseIndex', async () => {
|
|
406
|
+
const paramsWithoutIndex = new DyFM_DataModel_Params<TestMetadata>({
|
|
407
|
+
dataName: 'test_data',
|
|
408
|
+
properties: {
|
|
409
|
+
name: {
|
|
410
|
+
key: 'name',
|
|
411
|
+
type: DyFM_BasicProperty_Type.string,
|
|
412
|
+
},
|
|
413
|
+
content: {
|
|
414
|
+
key: 'content',
|
|
415
|
+
type: DyFM_BasicProperty_Type.string,
|
|
416
|
+
},
|
|
417
|
+
contentVectorized: {
|
|
418
|
+
key: 'contentVectorized',
|
|
419
|
+
type: DyFM_BasicProperty_Type.array,
|
|
420
|
+
vectorizedFrom: ['content'],
|
|
421
|
+
embeddingModel: DyFM_OAI_Model.textEmbedding_3Small,
|
|
422
|
+
// vectorizeUseIndex not set
|
|
423
|
+
},
|
|
424
|
+
},
|
|
425
|
+
});
|
|
426
|
+
|
|
427
|
+
const serviceWithoutIndex = new DyNTS_OAI_VectorDataService<TestMetadata>(
|
|
428
|
+
testData,
|
|
429
|
+
paramsWithoutIndex,
|
|
430
|
+
mockOpenAISettings,
|
|
431
|
+
'test-issuer'
|
|
432
|
+
);
|
|
433
|
+
|
|
434
|
+
(serviceWithoutIndex as any).dataDBService = mockDBService;
|
|
435
|
+
(serviceWithoutIndex as any).embedding_CS = mockEmbedding_CS;
|
|
436
|
+
|
|
437
|
+
try {
|
|
438
|
+
await serviceWithoutIndex.vectorSearch({
|
|
439
|
+
input: 'Test search',
|
|
440
|
+
searchInKey: 'contentVectorized',
|
|
441
|
+
});
|
|
442
|
+
fail('Should have thrown an error');
|
|
443
|
+
} catch (error) {
|
|
444
|
+
expect(error).toBeInstanceOf(DyFM_Error);
|
|
445
|
+
}
|
|
446
|
+
});
|
|
447
|
+
});
|
|
448
|
+
|
|
449
|
+
describe('| saveData', () => {
|
|
450
|
+
it('| should vectorize properties before saving', async () => {
|
|
451
|
+
const newData: TestMetadata = Object.assign(new TestMetadata(), {
|
|
452
|
+
name: 'New Name',
|
|
453
|
+
content: 'New content',
|
|
454
|
+
});
|
|
455
|
+
|
|
456
|
+
(DyNTS_DataService.prototype as unknown as { ensureData: jasmine.Spy }).ensureData.and.returnValue(newData);
|
|
457
|
+
spyOn(service, 'vectorizeDataProperties').and.returnValue(Promise.resolve(newData));
|
|
458
|
+
(DyNTS_DataService.prototype.saveData as jasmine.Spy).and.returnValue(Promise.resolve(newData));
|
|
459
|
+
|
|
460
|
+
const result = await service.saveData(newData);
|
|
461
|
+
|
|
462
|
+
expect(service.vectorizeDataProperties).toHaveBeenCalledWith(newData, undefined);
|
|
463
|
+
expect(result).toBeDefined();
|
|
464
|
+
});
|
|
465
|
+
|
|
466
|
+
it('| should remove vectorized properties if swallowVectorizedProperties is true', async () => {
|
|
467
|
+
const newData: TestMetadata = Object.assign(new TestMetadata(), {
|
|
468
|
+
name: 'New Name',
|
|
469
|
+
content: 'New content',
|
|
470
|
+
contentVectorized: [0.1, 0.2, 0.3],
|
|
471
|
+
});
|
|
472
|
+
|
|
473
|
+
(service as any).swallowVectorizedProperties = true;
|
|
474
|
+
spyOn(service, 'vectorizeDataProperties').and.returnValue(Promise.resolve(newData));
|
|
475
|
+
(DyNTS_DataService.prototype.saveData as jasmine.Spy).and.returnValue(Promise.resolve(newData));
|
|
476
|
+
spyOn(DyFM_Object, 'cleanObject');
|
|
477
|
+
|
|
478
|
+
const result = await service.saveData(newData);
|
|
479
|
+
|
|
480
|
+
expect(result.contentVectorized).toBeUndefined();
|
|
481
|
+
expect(DyFM_Object.cleanObject).toHaveBeenCalled();
|
|
482
|
+
});
|
|
483
|
+
|
|
484
|
+
it('| should keep vectorized properties if swallowVectorizedProperties is false', async () => {
|
|
485
|
+
const newData: TestMetadata = Object.assign(new TestMetadata(), {
|
|
486
|
+
name: 'New Name',
|
|
487
|
+
content: 'New content',
|
|
488
|
+
contentVectorized: [0.1, 0.2, 0.3],
|
|
489
|
+
});
|
|
490
|
+
|
|
491
|
+
(service as any).swallowVectorizedProperties = false;
|
|
492
|
+
spyOn(service, 'vectorizeDataProperties').and.returnValue(Promise.resolve(newData));
|
|
493
|
+
(DyNTS_DataService.prototype.saveData as jasmine.Spy).and.returnValue(Promise.resolve(newData));
|
|
494
|
+
|
|
495
|
+
const result = await service.saveData(newData);
|
|
496
|
+
|
|
497
|
+
expect(result.contentVectorized).toBeDefined();
|
|
498
|
+
});
|
|
499
|
+
|
|
500
|
+
it('| should handle save errors', async () => {
|
|
501
|
+
const newData: TestMetadata = Object.assign(new TestMetadata(), {
|
|
502
|
+
name: 'New Name',
|
|
503
|
+
content: 'New content',
|
|
504
|
+
});
|
|
505
|
+
|
|
506
|
+
const error = new Error('Save failed');
|
|
507
|
+
spyOn(service, 'vectorizeDataProperties').and.returnValue(Promise.resolve(newData));
|
|
508
|
+
(DyNTS_DataService.prototype.saveData as jasmine.Spy).and.returnValue(Promise.reject(error));
|
|
509
|
+
|
|
510
|
+
try {
|
|
511
|
+
await service.saveData(newData);
|
|
512
|
+
fail('Should have thrown an error');
|
|
513
|
+
} catch (err) {
|
|
514
|
+
expect(err).toBeInstanceOf(DyFM_Error);
|
|
515
|
+
expect((err as DyFM_Error)._errorCode).toContain('DyNTS-VDB-SD0');
|
|
516
|
+
}
|
|
517
|
+
});
|
|
518
|
+
});
|
|
519
|
+
|
|
520
|
+
describe('| properties', () => {
|
|
521
|
+
it('| should have tokenSpent property', () => {
|
|
522
|
+
expect((service as any).tokenSpent).toBe(0);
|
|
523
|
+
});
|
|
524
|
+
|
|
525
|
+
it('| should track token usage', async () => {
|
|
526
|
+
const mockResponse: CreateEmbeddingResponse = {
|
|
527
|
+
data: [
|
|
528
|
+
{
|
|
529
|
+
embedding: [0.1, 0.2, 0.3],
|
|
530
|
+
index: 0,
|
|
531
|
+
object: 'embedding',
|
|
532
|
+
},
|
|
533
|
+
],
|
|
534
|
+
model: 'text-embedding-3-small',
|
|
535
|
+
object: 'list',
|
|
536
|
+
usage: {
|
|
537
|
+
prompt_tokens: 10,
|
|
538
|
+
total_tokens: 10,
|
|
539
|
+
},
|
|
540
|
+
};
|
|
541
|
+
|
|
542
|
+
mockEmbedding_CS.createEmbedding.and.returnValue(Promise.resolve(mockResponse));
|
|
543
|
+
|
|
544
|
+
await service.vectorize('Test input');
|
|
545
|
+
|
|
546
|
+
expect((service as any).tokenSpent).toBe(10);
|
|
547
|
+
});
|
|
548
|
+
});
|
|
549
|
+
});
|
|
550
|
+
|