@futdevpro/nts-dynamo 1.15.119 → 1.15.121
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/.dynamo/logs/cicd-pipeline/output.log +1899 -1878
- package/.dynamo/logs/cicd-pipeline/status.json +37 -37
- package/build/_models/control-models/endpoint-params.control-model.d.ts +5 -0
- package/build/_models/control-models/endpoint-params.control-model.d.ts.map +1 -1
- package/build/_models/control-models/endpoint-params.control-model.js +41 -11
- package/build/_models/control-models/endpoint-params.control-model.js.map +1 -1
- package/build/_modules/local-vector-search/_services/lvs-persistent-vector-pool.control-service.d.ts +6 -1
- package/build/_modules/local-vector-search/_services/lvs-persistent-vector-pool.control-service.d.ts.map +1 -1
- package/build/_modules/local-vector-search/_services/lvs-persistent-vector-pool.control-service.js +40 -10
- package/build/_modules/local-vector-search/_services/lvs-persistent-vector-pool.control-service.js.map +1 -1
- package/build/_modules/local-vector-search/_services/lvs-vector-persist.data-service.d.ts +9 -0
- package/build/_modules/local-vector-search/_services/lvs-vector-persist.data-service.d.ts.map +1 -1
- package/build/_modules/local-vector-search/_services/lvs-vector-persist.data-service.js +12 -0
- package/build/_modules/local-vector-search/_services/lvs-vector-persist.data-service.js.map +1 -1
- package/build/_modules/local-vector-search/_services/lvs-vector-pool.control-service.d.ts +12 -2
- package/build/_modules/local-vector-search/_services/lvs-vector-pool.control-service.d.ts.map +1 -1
- package/build/_modules/local-vector-search/_services/lvs-vector-pool.control-service.js +54 -7
- package/build/_modules/local-vector-search/_services/lvs-vector-pool.control-service.js.map +1 -1
- package/build/_modules/server/server-status/server-status.control-service.d.ts +6 -0
- package/build/_modules/server/server-status/server-status.control-service.d.ts.map +1 -1
- package/build/_modules/server/server-status/server-status.control-service.js +10 -0
- package/build/_modules/server/server-status/server-status.control-service.js.map +1 -1
- package/build/_modules/server/server-status/server-status.controller.d.ts.map +1 -1
- package/build/_modules/server/server-status/server-status.controller.js +26 -0
- package/build/_modules/server/server-status/server-status.controller.js.map +1 -1
- package/build/_modules/socket/_models/socket-server-service-params.control-model.d.ts +11 -1
- package/build/_modules/socket/_models/socket-server-service-params.control-model.d.ts.map +1 -1
- package/build/_modules/socket/_models/socket-server-service-params.control-model.js +12 -1
- package/build/_modules/socket/_models/socket-server-service-params.control-model.js.map +1 -1
- package/build/_modules/socket/_services/socket-server.service.d.ts +2 -0
- package/build/_modules/socket/_services/socket-server.service.d.ts.map +1 -1
- package/build/_modules/socket/_services/socket-server.service.js +2 -0
- package/build/_modules/socket/_services/socket-server.service.js.map +1 -1
- package/build/_modules/socket/app-extended.server.d.ts.map +1 -1
- package/build/_modules/socket/app-extended.server.js +13 -8
- package/build/_modules/socket/app-extended.server.js.map +1 -1
- package/build/_services/base/db.service.d.ts +14 -0
- package/build/_services/base/db.service.d.ts.map +1 -1
- package/build/_services/base/db.service.js +27 -0
- package/build/_services/base/db.service.js.map +1 -1
- package/build/_services/core/api.service.d.ts.map +1 -1
- package/build/_services/core/api.service.js +29 -9
- package/build/_services/core/api.service.js.map +1 -1
- package/build/_services/core/db-stats.service.d.ts +34 -0
- package/build/_services/core/db-stats.service.d.ts.map +1 -0
- package/build/_services/core/db-stats.service.js +65 -0
- package/build/_services/core/db-stats.service.js.map +1 -0
- package/build/_services/core/diagnostics.service.d.ts +43 -0
- package/build/_services/core/diagnostics.service.d.ts.map +1 -0
- package/build/_services/core/diagnostics.service.js +122 -0
- package/build/_services/core/diagnostics.service.js.map +1 -0
- package/build/index.d.ts +2 -0
- package/build/index.d.ts.map +1 -1
- package/build/index.js +2 -0
- package/build/index.js.map +1 -1
- package/package.json +2 -2
- package/src/_models/control-models/endpoint-params.control-model.ts +44 -16
- package/src/_modules/local-vector-search/_services/lvs-persistent-vector-pool.control-service.spec.ts +16 -0
- package/src/_modules/local-vector-search/_services/lvs-persistent-vector-pool.control-service.ts +50 -14
- package/src/_modules/local-vector-search/_services/lvs-vector-persist.data-service.ts +22 -0
- package/src/_modules/local-vector-search/_services/lvs-vector-pool.control-service.spec.ts +5 -3
- package/src/_modules/local-vector-search/_services/lvs-vector-pool.control-service.ts +62 -11
- package/src/_modules/server/server-status/server-status.control-service.ts +12 -0
- package/src/_modules/server/server-status/server-status.controller.ts +28 -0
- package/src/_modules/socket/_models/socket-server-service-params.control-model.spec.ts +23 -0
- package/src/_modules/socket/_models/socket-server-service-params.control-model.ts +12 -1
- package/src/_modules/socket/_services/socket-server.service.ts +2 -0
- package/src/_modules/socket/app-extended.server.ts +15 -9
- package/src/_services/base/db.service.ts +39 -1
- package/src/_services/core/api.service.spec.ts +47 -0
- package/src/_services/core/api.service.ts +32 -12
- package/src/_services/core/db-stats.service.ts +104 -0
- package/src/_services/core/diagnostics.service.spec.ts +44 -0
- package/src/_services/core/diagnostics.service.ts +158 -0
- package/src/index.ts +2 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"diagnostics.service.d.ts","sourceRoot":"","sources":["../../../src/_services/core/diagnostics.service.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,QAAQ,MAAM,UAAU,CAAC;AAQrC;;;;;;;;;;;GAWG;AAEH,MAAM,WAAW,iBAAiB;IAChC,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,IAAI,GAAG,MAAM,GAAG,MAAM,CAAC;IAC/B,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,2FAA2F;IAC3F,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,kBAAkB;IACjC,MAAM,EAAE,IAAI,GAAG,MAAM,GAAG,MAAM,CAAC;IAC/B,MAAM,EAAE,iBAAiB,EAAE,CAAC;IAC5B,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,wEAAwE;AACxE,MAAM,MAAM,mBAAmB,GAAG,MAAM,OAAO,CAAC,iBAAiB,CAAC,CAAC;AAKnE,qBAAa,iBAAiB;IAE5B,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,YAAY,CAA6B;IAEjE,8EAA8E;IAC9E,MAAM,CAAC,aAAa,CAAC,KAAK,EAAE,mBAAmB,GAAG,IAAI;IAItD,iFAAiF;WACpE,SAAS,CAAC,GAAG,EAAE;QAC1B,UAAU,EAAE,QAAQ,CAAC,UAAU,CAAC;QAChC,gBAAgB,CAAC,EAAE,MAAM,CAAC;KAC3B,GAAG,OAAO,CAAC,kBAAkB,CAAC;IAqF/B,8GAA8G;IAC9G,MAAM,CAAC,eAAe,CAAC,MAAM,EAAE,iBAAiB,EAAE,GAAG,IAAI,GAAG,MAAM,GAAG,MAAM;IAM3E,4EAA4E;IAC5E,MAAM,CAAC,YAAY,CAAC,MAAM,EAAE,iBAAiB,EAAE,GAAG,MAAM;CASzD"}
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.DyNTS_Diagnostics = void 0;
|
|
4
|
+
const fsm_dynamo_1 = require("@futdevpro/fsm-dynamo");
|
|
5
|
+
const db_stats_service_1 = require("./db-stats.service");
|
|
6
|
+
const event_loop_diag_service_1 = require("./event-loop-diag.service");
|
|
7
|
+
const memory_guard_service_1 = require("./memory-guard.service");
|
|
8
|
+
/** Collection-méret warn-küszöb (MB) — e fölött a dbStats-check warn-ra vált. */
|
|
9
|
+
const DEFAULT_COLLECTION_WARN_MB = 512;
|
|
10
|
+
class DyNTS_Diagnostics {
|
|
11
|
+
static customChecks = [];
|
|
12
|
+
/** App-specifikus check regisztrálása (pl. env-validáció, provider-probe). */
|
|
13
|
+
static registerCheck(check) {
|
|
14
|
+
DyNTS_Diagnostics.customChecks.push(check);
|
|
15
|
+
}
|
|
16
|
+
/** A teljes doctor-riport. SOSEM dob — a hibás check `fail`-ként jelenik meg. */
|
|
17
|
+
static async runDoctor(set) {
|
|
18
|
+
const checks = [];
|
|
19
|
+
// 1) Mongo-probe (readiness-minta: readyState + admin ping).
|
|
20
|
+
try {
|
|
21
|
+
const readyState = set.connection?.readyState ?? 0;
|
|
22
|
+
let pingOk = false;
|
|
23
|
+
if (readyState === 1) {
|
|
24
|
+
await set.connection.db?.admin().ping();
|
|
25
|
+
pingOk = true;
|
|
26
|
+
}
|
|
27
|
+
checks.push({
|
|
28
|
+
name: 'mongo',
|
|
29
|
+
health: readyState === 1 && pingOk ? 'ok' : 'fail',
|
|
30
|
+
detail: { readyState: readyState, pingOk: pingOk },
|
|
31
|
+
fixHint: readyState === 1 && pingOk ? undefined
|
|
32
|
+
: 'Check MONGO_URL + DB container/network reachability (readyState 1 + ping expected).',
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
catch (error) {
|
|
36
|
+
checks.push({
|
|
37
|
+
name: 'mongo', health: 'fail', detail: String(error),
|
|
38
|
+
fixHint: 'Mongo ping threw — check DB availability/credentials.',
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
// 2) Memória (MemoryGuard pressure).
|
|
42
|
+
try {
|
|
43
|
+
const pressure = memory_guard_service_1.DyNTS_MemoryGuard.getInstance().pressure();
|
|
44
|
+
const level = String(pressure?.level ?? 'ok');
|
|
45
|
+
checks.push({
|
|
46
|
+
name: 'memory',
|
|
47
|
+
health: level === 'critical' ? 'fail' : level === 'warning' ? 'warn' : 'ok',
|
|
48
|
+
detail: pressure,
|
|
49
|
+
fixHint: level === 'ok' ? undefined
|
|
50
|
+
: 'Heap pressure — check collection sizes (/server/db-stats) + recent load; consider retention.',
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
catch (error) {
|
|
54
|
+
checks.push({ name: 'memory', health: 'warn', detail: String(error), fixHint: 'MemoryGuard snapshot failed.' });
|
|
55
|
+
}
|
|
56
|
+
// 3) Event-loop lag.
|
|
57
|
+
try {
|
|
58
|
+
const snapshot = event_loop_diag_service_1.DyNTS_EventLoopDiag.getInstance().getSnapshot();
|
|
59
|
+
checks.push({ name: 'eventLoop', health: 'ok', detail: snapshot });
|
|
60
|
+
}
|
|
61
|
+
catch (error) {
|
|
62
|
+
checks.push({ name: 'eventLoop', health: 'warn', detail: String(error), fixHint: 'EventLoopDiag snapshot failed.' });
|
|
63
|
+
}
|
|
64
|
+
// 4) DB-stats + bloat-küszöb (BFR-OVERSEER-004 early-warning).
|
|
65
|
+
try {
|
|
66
|
+
const stats = await db_stats_service_1.DyNTS_DbStats_Util.collect(set.connection);
|
|
67
|
+
const warnMB = set.collectionWarnMB ?? DEFAULT_COLLECTION_WARN_MB;
|
|
68
|
+
const bloated = stats.collections
|
|
69
|
+
.filter((c) => c.sizeMB + c.indexSizeMB > warnMB)
|
|
70
|
+
.map((c) => `${c.name} (${c.sizeMB}MB + ${c.indexSizeMB}MB idx)`);
|
|
71
|
+
checks.push({
|
|
72
|
+
name: 'dbStats',
|
|
73
|
+
health: bloated.length ? 'warn' : 'ok',
|
|
74
|
+
detail: { totalSizeMB: stats.totalSizeMB, collectionCount: stats.collections.length, bloated: bloated },
|
|
75
|
+
fixHint: bloated.length
|
|
76
|
+
? `Collections over ${warnMB}MB — add dataParams.retention (TTL) or prune: ${bloated.join(', ')}`
|
|
77
|
+
: undefined,
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
catch (error) {
|
|
81
|
+
checks.push({ name: 'dbStats', health: 'warn', detail: String(error), fixHint: 'db-stats collection failed.' });
|
|
82
|
+
}
|
|
83
|
+
// 5) App-specifikus check-ek.
|
|
84
|
+
for (const customCheck of DyNTS_Diagnostics.customChecks) {
|
|
85
|
+
try {
|
|
86
|
+
checks.push(await customCheck());
|
|
87
|
+
}
|
|
88
|
+
catch (error) {
|
|
89
|
+
fsm_dynamo_1.DyFM_Log.warn('(DyNTS_Diagnostics) custom check threw (reported as fail)', error);
|
|
90
|
+
checks.push({ name: 'custom', health: 'fail', detail: String(error), fixHint: 'Custom check threw — see detail.' });
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
return {
|
|
94
|
+
health: DyNTS_Diagnostics.interpretHealth(checks),
|
|
95
|
+
checks: checks,
|
|
96
|
+
summary: DyNTS_Diagnostics.buildSummary(checks),
|
|
97
|
+
uptimeSec: Math.round(process.uptime()),
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
/** Aggregált health: bármely fail → fail; különben bármely warn → warn; különben ok. Pure (unit-tesztelt). */
|
|
101
|
+
static interpretHealth(checks) {
|
|
102
|
+
if (checks.some((c) => c.health === 'fail')) {
|
|
103
|
+
return 'fail';
|
|
104
|
+
}
|
|
105
|
+
if (checks.some((c) => c.health === 'warn')) {
|
|
106
|
+
return 'warn';
|
|
107
|
+
}
|
|
108
|
+
return 'ok';
|
|
109
|
+
}
|
|
110
|
+
/** Ember-olvasható összegzés a nem-ok check-ekből. Pure (unit-tesztelt). */
|
|
111
|
+
static buildSummary(checks) {
|
|
112
|
+
const notOk = checks.filter((c) => c.health !== 'ok');
|
|
113
|
+
if (!notOk.length) {
|
|
114
|
+
return 'all checks ok';
|
|
115
|
+
}
|
|
116
|
+
return notOk
|
|
117
|
+
.map((c) => `${c.name}: ${c.health}${c.fixHint ? ` — ${c.fixHint}` : ''}`)
|
|
118
|
+
.join(' | ');
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
exports.DyNTS_Diagnostics = DyNTS_Diagnostics;
|
|
122
|
+
//# sourceMappingURL=diagnostics.service.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"diagnostics.service.js","sourceRoot":"","sources":["../../../src/_services/core/diagnostics.service.ts"],"names":[],"mappings":"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"}
|
package/build/index.d.ts
CHANGED
|
@@ -24,6 +24,8 @@ export * from './_collections/security-headers.util';
|
|
|
24
24
|
export * from './_collections/client-safe-error.util';
|
|
25
25
|
export * from './_collections/load-shed.util';
|
|
26
26
|
export * from './_services/core/event-loop-diag.service';
|
|
27
|
+
export * from './_services/core/db-stats.service';
|
|
28
|
+
export * from './_services/core/diagnostics.service';
|
|
27
29
|
export * from './_models/control-models/api-call-params.control-model';
|
|
28
30
|
export * from './_models/control-models/app-ext-system-controls.control-model';
|
|
29
31
|
export * from './_models/control-models/app-params.control-model';
|
package/build/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AA2BA,cAAc,6BAA6B,CAAC;AAC5C,cAAc,+CAA+C,CAAC;AAC9D,cAAc,qDAAqD,CAAC;AACpE,cAAc,6CAA6C,CAAC;AAC5D,cAAc,0CAA0C,CAAC;AACzD,cAAc,8CAA8C,CAAC;AAC7D,cAAc,sCAAsC,CAAC;AAIrD,cAAc,+BAA+B,CAAC;AAC9C,cAAc,qCAAqC,CAAC;AACpD,cAAc,qCAAqC,CAAC;AACpD,cAAc,8BAA8B,CAAC;AAK7C,mEAAmE;AACnE,cAAc,uDAAuD,CAAC;AACtE,cAAc,qDAAqD,CAAC;AACpE,cAAc,wDAAwD,CAAC;AACvE,cAAc,gDAAgD,CAAC;AAC/D,cAAc,wDAAwD,CAAC;AACvE,cAAc,uDAAuD,CAAC;AACtE,cAAc,8CAA8C,CAAC;AAC7D,cAAc,0DAA0D,CAAC;AACzE,cAAc,iDAAiD,CAAC;AAChE,cAAc,gDAAgD,CAAC;AAC/D,cAAc,sCAAsC,CAAC;AACrD,cAAc,uCAAuC,CAAC;AACtD,cAAc,+BAA+B,CAAC;AAC9C,cAAc,0CAA0C,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AA2BA,cAAc,6BAA6B,CAAC;AAC5C,cAAc,+CAA+C,CAAC;AAC9D,cAAc,qDAAqD,CAAC;AACpE,cAAc,6CAA6C,CAAC;AAC5D,cAAc,0CAA0C,CAAC;AACzD,cAAc,8CAA8C,CAAC;AAC7D,cAAc,sCAAsC,CAAC;AAIrD,cAAc,+BAA+B,CAAC;AAC9C,cAAc,qCAAqC,CAAC;AACpD,cAAc,qCAAqC,CAAC;AACpD,cAAc,8BAA8B,CAAC;AAK7C,mEAAmE;AACnE,cAAc,uDAAuD,CAAC;AACtE,cAAc,qDAAqD,CAAC;AACpE,cAAc,wDAAwD,CAAC;AACvE,cAAc,gDAAgD,CAAC;AAC/D,cAAc,wDAAwD,CAAC;AACvE,cAAc,uDAAuD,CAAC;AACtE,cAAc,8CAA8C,CAAC;AAC7D,cAAc,0DAA0D,CAAC;AACzE,cAAc,iDAAiD,CAAC;AAChE,cAAc,gDAAgD,CAAC;AAC/D,cAAc,sCAAsC,CAAC;AACrD,cAAc,uCAAuC,CAAC;AACtD,cAAc,+BAA+B,CAAC;AAC9C,cAAc,0CAA0C,CAAC;AACzD,cAAc,mCAAmC,CAAC;AAClD,cAAc,sCAAsC,CAAC;AAGrD,cAAc,wDAAwD,CAAC;AACvE,cAAc,gEAAgE,CAAA;AAC9E,cAAc,mDAAmD,CAAC;AAClE,cAAc,4DAA4D,CAAC;AAC3E,cAAc,wDAAwD,CAAC;AACvE,cAAc,sDAAsD,CAAC;AACrE,cAAc,uDAAuD,CAAC;AAGtE,cAAc,gCAAgC,CAAC;AAG/C,cAAc,mDAAmD,CAAC;AAKlE,cAAc,8BAA8B,CAAC;AAC7C,cAAc,+BAA+B,CAAC;AAE9C,cAAc,gCAAgC,CAAC;AAC/C,cAAc,oDAAoD,CAAC;AACnE,cAAc,iCAAiC,CAAC;AAChD,cAAc,uCAAuC,CAAC;AAEtD,cAAc,6CAA6C,CAAC;AAC5D,cAAc,2CAA2C,CAAC;AAG1D,cAAc,mCAAmC,CAAC;AAClD,cAAc,uCAAuC,CAAC;AACtD,cAAc,+BAA+B,CAAC;AAC9C,cAAc,6BAA6B,CAAC;AAC5C,cAAc,yCAAyC,CAAC;AACxD,cAAc,oCAAoC,CAAC;AAGnD,cAAc,+BAA+B,CAAC;AAG9C,cAAc,sCAAsC,CAAC;AACrD,cAAc,0CAA0C,CAAC;AAGzD,cAAc,mCAAmC,CAAC"}
|
package/build/index.js
CHANGED
|
@@ -53,6 +53,8 @@ tslib_1.__exportStar(require("./_collections/security-headers.util"), exports);
|
|
|
53
53
|
tslib_1.__exportStar(require("./_collections/client-safe-error.util"), exports);
|
|
54
54
|
tslib_1.__exportStar(require("./_collections/load-shed.util"), exports);
|
|
55
55
|
tslib_1.__exportStar(require("./_services/core/event-loop-diag.service"), exports);
|
|
56
|
+
tslib_1.__exportStar(require("./_services/core/db-stats.service"), exports);
|
|
57
|
+
tslib_1.__exportStar(require("./_services/core/diagnostics.service"), exports);
|
|
56
58
|
// models/CONTROL MODELS
|
|
57
59
|
tslib_1.__exportStar(require("./_models/control-models/api-call-params.control-model"), exports);
|
|
58
60
|
tslib_1.__exportStar(require("./_models/control-models/app-ext-system-controls.control-model"), exports);
|
package/build/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAEA;;;;;;;;;;;;;;;;;;;;;;IAsBI;;;AAEJ,cAAc;AACd,sEAA4C;AAC5C,wFAA8D;AAC9D,8FAAoE;AACpE,sFAA4D;AAC5D,mFAAyD;AACzD,uFAA6D;AAC7D,+EAAqD;AAGrD,QAAQ;AACR,wEAA8C;AAC9C,8EAAoD;AACpD,8EAAoD;AACpD,uEAA6C;AAG7C,6CAA6C;AAE7C,mEAAmE;AACnE,gGAAsE;AACtE,8FAAoE;AACpE,iGAAuE;AACvE,yFAA+D;AAC/D,iGAAuE;AACvE,gGAAsE;AACtE,uFAA6D;AAC7D,mGAAyE;AACzE,0FAAgE;AAChE,yFAA+D;AAC/D,+EAAqD;AACrD,gFAAsD;AACtD,wEAA8C;AAC9C,mFAAyD;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAEA;;;;;;;;;;;;;;;;;;;;;;IAsBI;;;AAEJ,cAAc;AACd,sEAA4C;AAC5C,wFAA8D;AAC9D,8FAAoE;AACpE,sFAA4D;AAC5D,mFAAyD;AACzD,uFAA6D;AAC7D,+EAAqD;AAGrD,QAAQ;AACR,wEAA8C;AAC9C,8EAAoD;AACpD,8EAAoD;AACpD,uEAA6C;AAG7C,6CAA6C;AAE7C,mEAAmE;AACnE,gGAAsE;AACtE,8FAAoE;AACpE,iGAAuE;AACvE,yFAA+D;AAC/D,iGAAuE;AACvE,gGAAsE;AACtE,uFAA6D;AAC7D,mGAAyE;AACzE,0FAAgE;AAChE,yFAA+D;AAC/D,+EAAqD;AACrD,gFAAsD;AACtD,wEAA8C;AAC9C,mFAAyD;AACzD,4EAAkD;AAClD,+EAAqD;AAErD,wBAAwB;AACxB,iGAAuE;AACvE,yGAA8E;AAC9E,4FAAkE;AAClE,qGAA2E;AAC3E,iGAAuE;AACvE,+FAAqE;AACrE,gGAAsE;AAEtE,eAAe;AACf,yEAA+C;AAE/C,qBAAqB;AACrB,4FAAkE;AAGlE,WAAW;AACX,gBAAgB;AAChB,uEAA6C;AAC7C,wEAA8C;AAE9C,yEAA+C;AAC/C,6FAAmE;AACnE,0EAAgD;AAChD,gFAAsD;AAEtD,sFAA4D;AAC5D,oFAA0D;AAE1D,gBAAgB;AAChB,4EAAkD;AAClD,gFAAsD;AACtD,wEAA8C;AAC9C,sEAA4C;AAC5C,kFAAwD;AACxD,6EAAmD;AAEnD,kBAAkB;AAClB,wEAA8C;AAE9C,iBAAiB;AACjB,+EAAqD;AACrD,mFAAyD;AAEzD,kBAAkB;AAClB,4EAAkD"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@futdevpro/nts-dynamo",
|
|
3
|
-
"version": "01.15.
|
|
3
|
+
"version": "01.15.121",
|
|
4
4
|
"description": "Dynamic NodeTS (NodeJS-Typescript), MongoDB Backend System Framework by Future Development Program Ltd.",
|
|
5
5
|
"DyBu_settings": {
|
|
6
6
|
"packageType": "server-package",
|
|
@@ -378,7 +378,7 @@
|
|
|
378
378
|
"@anthropic-ai/sdk": "~0.39.0",
|
|
379
379
|
"@modelcontextprotocol/sdk": "~1.29.0",
|
|
380
380
|
"better-sqlite3": "~12.4.1",
|
|
381
|
-
"@futdevpro/fsm-dynamo": "1.16.
|
|
381
|
+
"@futdevpro/fsm-dynamo": "1.16.27",
|
|
382
382
|
"@types/express": "4.17.21",
|
|
383
383
|
"@types/geoip-lite": "~1.4.1",
|
|
384
384
|
"@types/node": "~24.1.0",
|
|
@@ -261,6 +261,18 @@ export class DyNTS_Endpoint_Params<
|
|
|
261
261
|
return;
|
|
262
262
|
}
|
|
263
263
|
|
|
264
|
+
// BFR-OVERSEER-010: a logResponseContent IMPLEMENTÁLVA (a korábbi "not implemented yet"
|
|
265
|
+
// warn-stub minden requestre spam-elt). A res.send-et wrap-eljük a küldött body
|
|
266
|
+
// elkapásához; a task-ok után csonkolt preview-t logolunk (log-flood ellen).
|
|
267
|
+
let capturedResponseBody: unknown;
|
|
268
|
+
if (this.logRequest && this.logResponseContent) {
|
|
269
|
+
const originalSend: Response['send'] = res.send.bind(res);
|
|
270
|
+
res.send = (body?: unknown): Response => {
|
|
271
|
+
capturedResponseBody = body;
|
|
272
|
+
return originalSend(body);
|
|
273
|
+
};
|
|
274
|
+
}
|
|
275
|
+
|
|
264
276
|
await DyFM_Array.asyncForEach(this.tasks,
|
|
265
277
|
async (
|
|
266
278
|
task: (req: Request, res: Response, issuer?: string) => Promise<void>
|
|
@@ -268,13 +280,11 @@ export class DyNTS_Endpoint_Params<
|
|
|
268
280
|
await task(req, res, issuer);
|
|
269
281
|
}
|
|
270
282
|
);
|
|
271
|
-
|
|
283
|
+
|
|
272
284
|
if (this.logRequest) {
|
|
285
|
+
DyFM_Log.success(` <<<===== '${this.name}' result sent.`);
|
|
273
286
|
if (this.logResponseContent) {
|
|
274
|
-
DyFM_Log.
|
|
275
|
-
DyFM_Log.warn('sorry, the logResponseContent is not implemented yet.');
|
|
276
|
-
} else {
|
|
277
|
-
DyFM_Log.success(` <<<===== '${this.name}' result sent.`);
|
|
287
|
+
DyFM_Log.info(` response: ${DyNTS_Endpoint_Params.previewBody(capturedResponseBody)}`);
|
|
278
288
|
}
|
|
279
289
|
}
|
|
280
290
|
} catch (error) {
|
|
@@ -283,6 +293,28 @@ export class DyNTS_Endpoint_Params<
|
|
|
283
293
|
};
|
|
284
294
|
}
|
|
285
295
|
|
|
296
|
+
/**
|
|
297
|
+
* BFR-OVERSEER-010: csonkolt, safe response-preview a logResponseContent-hez —
|
|
298
|
+
* nem-parseolható/circular body-ra nem dob, hosszú body-t levág (log-flood ellen).
|
|
299
|
+
*/
|
|
300
|
+
private static previewBody(body: unknown): string {
|
|
301
|
+
const maxLength: number = 2000;
|
|
302
|
+
let text: string;
|
|
303
|
+
if (body === undefined) {
|
|
304
|
+
return '-no body captured-';
|
|
305
|
+
}
|
|
306
|
+
if (typeof body === 'string') {
|
|
307
|
+
text = body;
|
|
308
|
+
} else {
|
|
309
|
+
try {
|
|
310
|
+
text = JSON.stringify(body);
|
|
311
|
+
} catch {
|
|
312
|
+
text = String(body);
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
return text.length > maxLength ? `${text.slice(0, maxLength)}…(truncated ${text.length} chars)` : text;
|
|
316
|
+
}
|
|
317
|
+
|
|
286
318
|
/**
|
|
287
319
|
*
|
|
288
320
|
* @param res
|
|
@@ -492,20 +524,16 @@ export class DyNTS_Endpoint_Params<
|
|
|
492
524
|
// test/dev környezetben TELJES DyFM-leaket hagyott (__localStack + ___systemVersion + issuer).
|
|
493
525
|
// A szabályok (mindig-strip / prod-only additionalContent / kliens-mezők megtartása) SSOT-ja
|
|
494
526
|
// a DyNTS_ClientSafeError_Util.
|
|
495
|
-
|
|
527
|
+
// BFR-OVERSEER-010: a kiküldött client-safe error megőrzése a logResponseContent-hez.
|
|
528
|
+
const clientSafeError: unknown = DyNTS_ClientSafeError_Util.toClientSafe(error);
|
|
529
|
+
res.send(clientSafeError);
|
|
496
530
|
|
|
497
531
|
if (this.logRequest) {
|
|
532
|
+
DyFM_Log.error(
|
|
533
|
+
` <<<===== '${this.name}' error sent: ${(error as DyFM_Error)?._message ?? ''}`
|
|
534
|
+
);
|
|
498
535
|
if (this.logResponseContent) {
|
|
499
|
-
DyFM_Log.error(
|
|
500
|
-
` <<<===== '${this.name}' error sent: ${(error as DyFM_Error)?._message ?? ''}`
|
|
501
|
-
);
|
|
502
|
-
DyFM_Log.error(
|
|
503
|
-
'sorry, the logResponseContent is not implemented yet.'
|
|
504
|
-
);
|
|
505
|
-
} else {
|
|
506
|
-
DyFM_Log.error(
|
|
507
|
-
` <<<===== '${this.name}' error sent: ${(error as DyFM_Error)?._message ?? ''}`
|
|
508
|
-
);
|
|
536
|
+
DyFM_Log.error(` error response: ${DyNTS_Endpoint_Params.previewBody(clientSafeError)}`);
|
|
509
537
|
}
|
|
510
538
|
}
|
|
511
539
|
|
|
@@ -75,6 +75,22 @@ describe('| DyNTS_LVS_PersistentVectorPool_ControlService', () => {
|
|
|
75
75
|
(collectionKey: string) =>
|
|
76
76
|
Promise.resolve(Array.from(store.get(collectionKey)?.values() ?? [])),
|
|
77
77
|
);
|
|
78
|
+
|
|
79
|
+
// BFR-AM-013 (hydration-paging): a batch-elt lean-scan fake-je — a fake-store tartalmát
|
|
80
|
+
// batch-enként adagolja a handler-nek (a valós cursor-viselkedés memóriában modellezve).
|
|
81
|
+
spyOn(DyNTS_LVS_VectorPersist_DataService.prototype, 'findLeanBatchedByCollectionKey').and.callFake(
|
|
82
|
+
async (
|
|
83
|
+
collectionKey: string,
|
|
84
|
+
set: { batchSize?: number; handler: (batch: Partial<DyNTS_LVS_VectorPersist>[]) => Promise<void> | void },
|
|
85
|
+
): Promise<number> => {
|
|
86
|
+
const all: DyNTS_LVS_VectorPersist[] = Array.from(store.get(collectionKey)?.values() ?? []);
|
|
87
|
+
const batchSize: number = set.batchSize && set.batchSize > 0 ? set.batchSize : 500;
|
|
88
|
+
for (let i: number = 0; i < all.length; i += batchSize) {
|
|
89
|
+
await set.handler(all.slice(i, i + batchSize));
|
|
90
|
+
}
|
|
91
|
+
return all.length;
|
|
92
|
+
},
|
|
93
|
+
);
|
|
78
94
|
});
|
|
79
95
|
|
|
80
96
|
/** Egy egységvektor a tengely-irányba (cosine-self = 1) — determinisztikus keresés-ellenőrzéshez. */
|
package/src/_modules/local-vector-search/_services/lvs-persistent-vector-pool.control-service.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
|
|
2
|
-
import { DyFM_Log } from '@futdevpro/fsm-dynamo';
|
|
2
|
+
import { DyFM_Error, DyFM_Log } from '@futdevpro/fsm-dynamo';
|
|
3
3
|
|
|
4
4
|
import { LVS_Search_Mode } from '../_enums/lvs-search-mode.enum';
|
|
5
5
|
import { LVS_SearchResult } from '../_models/lvs-search-result.interface';
|
|
@@ -97,24 +97,60 @@ export class DyNTS_LVS_PersistentVectorPool_ControlService {
|
|
|
97
97
|
* előbb üríti (idempotens hidratálás). A hiányos (üres `vectorId`/`embedding`) rekordokat átugorja.
|
|
98
98
|
* Visszaadja a betöltött vektorok számát. **NEM** Atlas — saját Mongo + in-memory pool.
|
|
99
99
|
*/
|
|
100
|
-
async hydrateFromMongo(
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
100
|
+
async hydrateFromMongo(set?: {
|
|
101
|
+
/** Cursor-batch méret (default 500) — a heap-tranziens felső korlátja egy batch. */
|
|
102
|
+
batchSize?: number,
|
|
103
|
+
/** Opcionális felső korlát a pool-méretre; elérésekor a hidratálás áll + WARN (no-silent-cap). */
|
|
104
|
+
maxVectors?: number,
|
|
105
|
+
}): Promise<number> {
|
|
106
|
+
// BFR-AM-013 (hydration-paging): a korábbi impl a TELJES collection-t egyetlen
|
|
107
|
+
// `findByCollectionKey`-jel (Mongoose-doc-tömb) töltötte memóriába → 32k×2560-dim vektornál
|
|
108
|
+
// RUNTIME-IGAZOLT 93% heap-spike (OOM-közeli). Mostantól LEAN + projektált (vectorId+embedding,
|
|
109
|
+
// a content-szöveg NÉLKÜL) CURSOR-batch — a tranziens felső korlátja EGY batch.
|
|
104
110
|
this.pool.clearPool();
|
|
105
111
|
|
|
106
112
|
let loaded: number = 0;
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
113
|
+
let skipped: number = 0;
|
|
114
|
+
let truncated: boolean = false;
|
|
115
|
+
|
|
116
|
+
await this.getPersistService().findLeanBatchedByCollectionKey(this.collectionKey, {
|
|
117
|
+
batchSize: set?.batchSize,
|
|
118
|
+
handler: (batch: Partial<DyNTS_LVS_VectorPersist>[]): void => {
|
|
119
|
+
for (const record of batch) {
|
|
120
|
+
if (truncated) {
|
|
121
|
+
return;
|
|
122
|
+
}
|
|
123
|
+
if (set?.maxVectors !== undefined && loaded >= set.maxVectors) {
|
|
124
|
+
truncated = true;
|
|
125
|
+
DyFM_Log.warn(
|
|
126
|
+
`[DyNTS LVS hydrate] '${this.collectionKey}': maxVectors (${set.maxVectors}) elérve — ` +
|
|
127
|
+
'a hidratálás CSONKOLVA (no-silent-cap; a maradék vektor NEM került a poolba).',
|
|
128
|
+
);
|
|
129
|
+
return;
|
|
130
|
+
}
|
|
131
|
+
if (!record.vectorId || !record.embedding?.length) {
|
|
132
|
+
continue;
|
|
133
|
+
}
|
|
134
|
+
try {
|
|
135
|
+
this.pool.addVector(record.vectorId, record.embedding);
|
|
136
|
+
loaded++;
|
|
137
|
+
} catch (error) {
|
|
138
|
+
// BFR-AM-013 (NaN/±Inf-guard a hidratálásban): EGY hibás (nem-véges elemű) perzistált
|
|
139
|
+
// vektor NEM boríthatja a teljes boot-hidratálást — skip + warn (nem néma).
|
|
140
|
+
skipped++;
|
|
141
|
+
DyFM_Log.warn(
|
|
142
|
+
`[DyNTS LVS hydrate] '${this.collectionKey}': vektor kihagyva ("${record.vectorId}") — ` +
|
|
143
|
+
`${DyFM_Error.getErrorMessage(error)}`,
|
|
144
|
+
);
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
},
|
|
148
|
+
});
|
|
115
149
|
|
|
116
150
|
DyFM_Log.log(
|
|
117
|
-
`[DyNTS LVS hydrate] '${this.collectionKey}' pool: ${loaded} vektor betöltve az in-memory pool-ba
|
|
151
|
+
`[DyNTS LVS hydrate] '${this.collectionKey}' pool: ${loaded} vektor betöltve az in-memory pool-ba` +
|
|
152
|
+
(skipped ? ` (${skipped} hibás vektor kihagyva)` : '') +
|
|
153
|
+
(truncated ? ' (CSONKOLT — maxVectors)' : '') + '.',
|
|
118
154
|
);
|
|
119
155
|
|
|
120
156
|
return loaded;
|
|
@@ -48,6 +48,28 @@ export class DyNTS_LVS_VectorPersist_DataService extends DyNTS_DataService<DyNTS
|
|
|
48
48
|
);
|
|
49
49
|
}
|
|
50
50
|
|
|
51
|
+
/**
|
|
52
|
+
* BFR-AM-013 (hydration-paging): LEAN + projektált (CSAK `vectorId` + `embedding` — a content-szöveg
|
|
53
|
+
* és metadata NÉLKÜL) cursor-batch scan a boot-hidratáláshoz. A heap-tranziens felső korlátja EGY batch
|
|
54
|
+
* (a teljes result-set + Mongoose-doc-boxing kimarad). Soft-delete-elt rekordok kihagyva.
|
|
55
|
+
*/
|
|
56
|
+
async findLeanBatchedByCollectionKey(
|
|
57
|
+
collectionKey: string,
|
|
58
|
+
set: {
|
|
59
|
+
batchSize?: number,
|
|
60
|
+
handler: (batch: Partial<DyNTS_LVS_VectorPersist>[]) => Promise<void> | void,
|
|
61
|
+
}
|
|
62
|
+
): Promise<number> {
|
|
63
|
+
return this.dataDBService.findLeanBatched(
|
|
64
|
+
{ collectionKey: collectionKey, _deleted: undefined },
|
|
65
|
+
{
|
|
66
|
+
projection: { vectorId: 1, embedding: 1 },
|
|
67
|
+
batchSize: set.batchSize,
|
|
68
|
+
handler: set.handler,
|
|
69
|
+
}
|
|
70
|
+
);
|
|
71
|
+
}
|
|
72
|
+
|
|
51
73
|
/**
|
|
52
74
|
* Egy vektor ATOMIKUS upsert-je a `(collectionKey, vectorId)` logikai-kulcson. MEGLÉVŐ rekordnál
|
|
53
75
|
* `$set` (a Mixed `metadata` silent-drop ellen — memory: mongoose_mixed_atomic_write); ÚJ rekordnál
|
|
@@ -374,9 +374,11 @@ describe('| LVS_VectorPool_ControlService', () => {
|
|
|
374
374
|
expect(vec1Result!.score).toBeCloseTo(1, 10);
|
|
375
375
|
});
|
|
376
376
|
|
|
377
|
-
it('| should produce cosine scores
|
|
377
|
+
it('| should produce cosine scores EQUIVALENT to static cosineSimilarity (precomputed-normalized + Float32 storage, BFR-AM-013)', () => {
|
|
378
378
|
// Regression-guard: a search optimalizalt (precomputed-normalized + alloc-mentes) cosine-ja
|
|
379
|
-
// viselkedes-
|
|
379
|
+
// viselkedes-EKVIVALENS a regi per-osszehasonlitas `cosineSimilarity(query, vector)`-ral.
|
|
380
|
+
// Tolerancia 6 tizedes (nem 10): a pool BELSŐ tárolása Float32Array (BFR-AM-013 Tier-1,
|
|
381
|
+
// ~2-4× memória) → ~1e-7 kerekítési eltérés — a similarity-RANGSORT nem érinti.
|
|
380
382
|
const query: number[] = [0.3, 0.7, 0.2];
|
|
381
383
|
const results: LVS_SearchResult[] = vectorPool.search(query, 4, LVS_Search_Mode.cosineSimilarity);
|
|
382
384
|
|
|
@@ -386,7 +388,7 @@ describe('| LVS_VectorPool_ControlService', () => {
|
|
|
386
388
|
const original: number[] | undefined = originals.get(result.id);
|
|
387
389
|
expect(original).toBeDefined();
|
|
388
390
|
const expected: number = LVS_VectorPool_ControlService.cosineSimilarity(query, original!);
|
|
389
|
-
expect(result.score).toBeCloseTo(expected,
|
|
391
|
+
expect(result.score).toBeCloseTo(expected, 6);
|
|
390
392
|
}
|
|
391
393
|
});
|
|
392
394
|
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { DyFM_Log } from '@futdevpro/fsm-dynamo';
|
|
2
|
+
|
|
1
3
|
import { LVS_Search_Mode } from '../_enums/lvs-search-mode.enum';
|
|
2
4
|
import { LVS_SearchResult } from '../_models/lvs-search-result.interface';
|
|
3
5
|
|
|
@@ -10,14 +12,19 @@ export class LVS_VectorPool_ControlService {
|
|
|
10
12
|
/**
|
|
11
13
|
* In-memory vektor pool tárolás
|
|
12
14
|
* Key: vektor ID, Value: vektor értékek
|
|
15
|
+
*
|
|
16
|
+
* BFR-AM-013 (Tier-1 memória): a belső tárolás `Float32Array` (4 byte/elem, boxing nélkül) a
|
|
17
|
+
* korábbi `number[]` (8 byte + array-boxing) helyett → ~2–4× kisebb PERSISTENS pool-footprint
|
|
18
|
+
* (2560-dim × 32k vektornál a heap érdemi része). A cosine/l2 index-alapú loopjai változatlanul
|
|
19
|
+
* futnak rajta; a publikus `getAll()` kontraktja (number[]) konverzióval megőrzött.
|
|
13
20
|
*/
|
|
14
|
-
private vectorPool: Map<string,
|
|
21
|
+
private vectorPool: Map<string, Float32Array> = new Map<string, Float32Array>();
|
|
15
22
|
|
|
16
23
|
/**
|
|
17
24
|
* Normalizált vektor pool tárolás (L2 normalizált másolatok)
|
|
18
|
-
* Key: vektor ID, Value: L2 normalizált vektor értékek
|
|
25
|
+
* Key: vektor ID, Value: L2 normalizált vektor értékek (Float32Array — l. fent)
|
|
19
26
|
*/
|
|
20
|
-
private normalizedVectorPool: Map<string,
|
|
27
|
+
private normalizedVectorPool: Map<string, Float32Array> = new Map<string, Float32Array>();
|
|
21
28
|
|
|
22
29
|
/**
|
|
23
30
|
* `normalizedOnly` mód: ha `true`, a pool NEM tárolja a nyers vektorokat (`vectorPool`) — CSAK a
|
|
@@ -52,13 +59,13 @@ export class LVS_VectorPool_ControlService {
|
|
|
52
59
|
}
|
|
53
60
|
|
|
54
61
|
// Tároljuk az eredeti vektort — KIVÉVE normalizedOnly módban (akkor a nyers pool kihagyva, ~50% memória).
|
|
62
|
+
// Float32Array-ként (BFR-AM-013 Tier-1) — a number[]-boxing nélkül.
|
|
55
63
|
if (!this.normalizedOnly) {
|
|
56
|
-
this.vectorPool.set(id, vector);
|
|
64
|
+
this.vectorPool.set(id, Float32Array.from(vector));
|
|
57
65
|
}
|
|
58
66
|
|
|
59
67
|
// L2 normalizált másolatot is tárolunk a cosine similarity számításokhoz
|
|
60
|
-
|
|
61
|
-
this.normalizedVectorPool.set(id, normalized);
|
|
68
|
+
this.normalizedVectorPool.set(id, LVS_VectorPool_ControlService.l2NormalizeFloat32(vector));
|
|
62
69
|
}
|
|
63
70
|
|
|
64
71
|
/**
|
|
@@ -102,7 +109,33 @@ export class LVS_VectorPool_ControlService {
|
|
|
102
109
|
* Debugging és export céljára
|
|
103
110
|
*/
|
|
104
111
|
getAll(): Map<string, number[]> {
|
|
105
|
-
|
|
112
|
+
// A publikus kontrakt (number[]) megőrzése — a belső Float32Array-ből konvertálva.
|
|
113
|
+
const result: Map<string, number[]> = new Map<string, number[]>();
|
|
114
|
+
for (const [ id, vector ] of this.vectorPool.entries()) {
|
|
115
|
+
result.set(id, Array.from(vector));
|
|
116
|
+
}
|
|
117
|
+
return result;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* BFR-AM-013 (Tier-1): L2-normalizálás Float32Array-be, explicit loopokkal (allokáció-minimális) —
|
|
122
|
+
* a pool BELSŐ tárolási formátumához. Nullvektor → csupa-0 Float32Array.
|
|
123
|
+
*/
|
|
124
|
+
private static l2NormalizeFloat32(vector: ArrayLike<number>): Float32Array {
|
|
125
|
+
let sumSquares: number = 0;
|
|
126
|
+
for (let i: number = 0; i < vector.length; i++) {
|
|
127
|
+
sumSquares += vector[i] * vector[i];
|
|
128
|
+
}
|
|
129
|
+
const magnitude: number = Math.sqrt(sumSquares);
|
|
130
|
+
|
|
131
|
+
const normalized: Float32Array = new Float32Array(vector.length);
|
|
132
|
+
if (magnitude === 0) {
|
|
133
|
+
return normalized;
|
|
134
|
+
}
|
|
135
|
+
for (let i: number = 0; i < vector.length; i++) {
|
|
136
|
+
normalized[i] = vector[i] / magnitude;
|
|
137
|
+
}
|
|
138
|
+
return normalized;
|
|
106
139
|
}
|
|
107
140
|
|
|
108
141
|
/**
|
|
@@ -156,7 +189,7 @@ export class LVS_VectorPool_ControlService {
|
|
|
156
189
|
* Eredmény: 0 vagy pozitív érték, ahol 0 a legkisebb távolság
|
|
157
190
|
* Statikus metódus, mivel nincs szüksége instance state-re
|
|
158
191
|
*/
|
|
159
|
-
static l2Distance(a: number
|
|
192
|
+
static l2Distance(a: ArrayLike<number>, b: ArrayLike<number>): number {
|
|
160
193
|
if (a.length !== b.length) {
|
|
161
194
|
throw new Error(
|
|
162
195
|
`Vectors must have the same dimension. Got ${a.length} and ${b.length}`
|
|
@@ -200,6 +233,14 @@ export class LVS_VectorPool_ControlService {
|
|
|
200
233
|
throw new Error('Query vector must be a non-empty array');
|
|
201
234
|
}
|
|
202
235
|
|
|
236
|
+
// BFR-AM-013 (NaN/±Inf-guard): nem-véges query-elem NÉMÁN mérgezné a TELJES rangsort
|
|
237
|
+
// (minden dot-product NaN → a valódi találatok kiesnek, hiba nélkül) → fail-closed throw.
|
|
238
|
+
for (let i: number = 0; i < query.length; i++) {
|
|
239
|
+
if (!Number.isFinite(query[i])) {
|
|
240
|
+
throw new Error(`Query vector must contain only finite numbers (non-finite at index ${i})`);
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
|
|
203
244
|
if (k <= 0) {
|
|
204
245
|
throw new Error('k must be a positive number');
|
|
205
246
|
}
|
|
@@ -217,9 +258,9 @@ export class LVS_VectorPool_ControlService {
|
|
|
217
258
|
|
|
218
259
|
// Validáljuk, hogy minden vektor ugyanolyan dimenziójú-e. A referencia-pool mód-függő: l2 a nyers,
|
|
219
260
|
// cosine a normalizált (azonos dimenzió, de `normalizedOnly`-ban a nyers pool üres).
|
|
220
|
-
const referencePool: Map<string,
|
|
261
|
+
const referencePool: Map<string, Float32Array> =
|
|
221
262
|
mode === LVS_Search_Mode.l2Distance ? this.vectorPool : this.normalizedVectorPool;
|
|
222
|
-
const firstVector:
|
|
263
|
+
const firstVector: Float32Array | undefined = referencePool.values().next().value;
|
|
223
264
|
if (firstVector && firstVector.length !== query.length) {
|
|
224
265
|
throw new Error(
|
|
225
266
|
`Query vector dimension (${query.length}) does not match pool vector ` +
|
|
@@ -239,7 +280,10 @@ export class LVS_VectorPool_ControlService {
|
|
|
239
280
|
// (2) a per-összehasonlítás dot-product ALLOKÁCIÓ-MENTES (a régi `l2Normalize` hívásonként KÉT új
|
|
240
281
|
// tömböt allokált → egy 24k-vektoros keresés ~48k tömb-allokáció + GC-spike-ot okozott).
|
|
241
282
|
// A drágán eltárolt `normalizedVectorPool` korábban SOHA nem volt használva a keresésben.
|
|
242
|
-
const queryNormalized:
|
|
283
|
+
const queryNormalized: Float32Array = LVS_VectorPool_ControlService.l2NormalizeFloat32(query);
|
|
284
|
+
// BFR-AM-013 (NaN-guard, defensive): ha egy tárolt vektor mégis NaN-score-t adna (elvileg az
|
|
285
|
+
// addVector-guard kizárja), a találat KIESIK — nem mérgezi a rangsort — és warn-t adunk (nem néma).
|
|
286
|
+
let nanScoreCount: number = 0;
|
|
243
287
|
for (const [id, normalizedVector] of this.normalizedVectorPool.entries()) {
|
|
244
288
|
if (candidateIds && !candidateIds.has(id)) {
|
|
245
289
|
continue;
|
|
@@ -248,8 +292,15 @@ export class LVS_VectorPool_ControlService {
|
|
|
248
292
|
for (let i: number = 0; i < queryNormalized.length; i++) {
|
|
249
293
|
dotProduct += queryNormalized[i] * normalizedVector[i];
|
|
250
294
|
}
|
|
295
|
+
if (!Number.isFinite(dotProduct)) {
|
|
296
|
+
nanScoreCount++;
|
|
297
|
+
continue;
|
|
298
|
+
}
|
|
251
299
|
results.push({ id, score: dotProduct });
|
|
252
300
|
}
|
|
301
|
+
if (nanScoreCount) {
|
|
302
|
+
DyFM_Log.warn(`[LVS search] ${nanScoreCount} vector(s) produced non-finite scores and were skipped.`);
|
|
303
|
+
}
|
|
253
304
|
// Cosine similarity: csökkenő sorrend (legnagyobb hasonlóság először)
|
|
254
305
|
results.sort((a: LVS_SearchResult, b: LVS_SearchResult) => b.score - a.score);
|
|
255
306
|
} else if (mode === LVS_Search_Mode.l2Distance) {
|
|
@@ -14,6 +14,8 @@ import * as mongoose from 'mongoose';
|
|
|
14
14
|
|
|
15
15
|
import { version as _serverVersion } from '../../../../package.json';
|
|
16
16
|
import { DyNTS_SingletonService } from '../../../_services/base/singleton.service';
|
|
17
|
+
import { DyNTS_DbStats, DyNTS_DbStats_Util } from '../../../_services/core/db-stats.service';
|
|
18
|
+
import { DyNTS_Diagnostics, DyNTS_DoctorReport } from '../../../_services/core/diagnostics.service';
|
|
17
19
|
import { DyNTS_global_settings } from '../../../_collections/global-settings.const';
|
|
18
20
|
import { DyNTS_Errors_ControlService } from '../errors/errors.control-service';
|
|
19
21
|
import { DyNTS_ServerStatusSnapshot_ControlService } from './server-status-snapshot.control-service';
|
|
@@ -221,6 +223,16 @@ export abstract class DyNTS_ServerStatus_ControlService<
|
|
|
221
223
|
* A mongoose default-connection. Külön metódus = teszt-seam (spy-jal felülírható, hogy a
|
|
222
224
|
* {@link checkDbReadiness} DB nélkül is tesztelhető legyen).
|
|
223
225
|
*/
|
|
226
|
+
/** BFR-OVERSEER-004 — stats-only DB-diagnosztika a `/server/db-stats` endpointnak (metaadat-only). */
|
|
227
|
+
async getDbStats(): Promise<DyNTS_DbStats> {
|
|
228
|
+
return DyNTS_DbStats_Util.collect(this.getMongooseConnection());
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
/** BFR-AM-015 — aggregált doctor-riport a `/server/doctor` endpointnak (sosem dob). */
|
|
232
|
+
async runDoctor(): Promise<DyNTS_DoctorReport> {
|
|
233
|
+
return DyNTS_Diagnostics.runDoctor({ connection: this.getMongooseConnection() });
|
|
234
|
+
}
|
|
235
|
+
|
|
224
236
|
protected getMongooseConnection(): mongoose.Connection {
|
|
225
237
|
// FIX (2026-07-22) — a `mongoose.connection` NEM feltetlenul az a handle, amit a data-layer hasznal.
|
|
226
238
|
//
|
|
@@ -252,6 +252,34 @@ export abstract class DyNTS_ServerStatus_Controller<
|
|
|
252
252
|
],
|
|
253
253
|
}),
|
|
254
254
|
|
|
255
|
+
// BFR-OVERSEER-004 — stats-only DB-diagnosztika (per-collection count + méret + index-méret;
|
|
256
|
+
// metaadat-only, SOHA nem dokumentum-tartalom). App-auth-tal gate-elhető (lazyPreProcessesFor).
|
|
257
|
+
new DyNTS_Endpoint_Params({
|
|
258
|
+
name: 'getDbStats',
|
|
259
|
+
type: DyFM_HttpCallType.get,
|
|
260
|
+
endpoint: '/db-stats',
|
|
261
|
+
preProcesses: this.lazyPreProcessesFor('getDbStats'),
|
|
262
|
+
tasks: [
|
|
263
|
+
async (req: Request, res: Response): Promise<void> => {
|
|
264
|
+
res.send(await this.server_CS.getDbStats());
|
|
265
|
+
},
|
|
266
|
+
],
|
|
267
|
+
}),
|
|
268
|
+
|
|
269
|
+
// BFR-AM-015 — aggregált doctor (mongo-probe + memory + event-loop + db-stats + app-checkek;
|
|
270
|
+
// health: ok|warn|fail + per-check fixHint). App-auth-tal gate-elhető.
|
|
271
|
+
new DyNTS_Endpoint_Params({
|
|
272
|
+
name: 'getDoctor',
|
|
273
|
+
type: DyFM_HttpCallType.get,
|
|
274
|
+
endpoint: '/doctor',
|
|
275
|
+
preProcesses: this.lazyPreProcessesFor('getDoctor'),
|
|
276
|
+
tasks: [
|
|
277
|
+
async (req: Request, res: Response): Promise<void> => {
|
|
278
|
+
res.send(await this.server_CS.runDoctor());
|
|
279
|
+
},
|
|
280
|
+
],
|
|
281
|
+
}),
|
|
282
|
+
|
|
255
283
|
// Readiness-probe: a /health-tel (liveness) szemben a DB-elérhetőséget is ellenőrzi.
|
|
256
284
|
// Public (mint a /health) — orchestrátor/LB hívja; 503 ha a DB nem ready → no traffic.
|
|
257
285
|
new DyNTS_Endpoint_Params({
|