@futdevpro/fdp-agent-memory 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (117) hide show
  1. package/README.md +345 -0
  2. package/build/package.json +96 -0
  3. package/build/src/_assets/mcp-client-config/README.md +29 -0
  4. package/build/src/_assets/mcp-client-config/claude_desktop_config.json +15 -0
  5. package/build/src/_assets/mcp-client-config/mcp.json +15 -0
  6. package/build/src/_collections/config-catalog.const.js +180 -0
  7. package/build/src/_collections/config-error-codes.const.js +30 -0
  8. package/build/src/_collections/config-presets.const.js +25 -0
  9. package/build/src/_collections/error-banners.const.js +100 -0
  10. package/build/src/_collections/error-codes.const.js +150 -0
  11. package/build/src/_collections/fam-db-models.const.js +37 -0
  12. package/build/src/_collections/fam-entry-bootstrap.util.js +80 -0
  13. package/build/src/_collections/fam-error-context.util.js +90 -0
  14. package/build/src/_collections/fam-error-factory.util.js +64 -0
  15. package/build/src/_enums/fam-config-level.type-enum.js +15 -0
  16. package/build/src/_enums/fam-table.type-enum.js +20 -0
  17. package/build/src/_integration-tests/_helpers/fam-integration-test-setup.util.js +105 -0
  18. package/build/src/_models/data-models/fam-codebase.data-model.js +51 -0
  19. package/build/src/_models/data-models/fam-coding-patterns.data-model.js +58 -0
  20. package/build/src/_models/data-models/fam-config.data-model.js +68 -0
  21. package/build/src/_models/data-models/fam-documents.data-model.js +53 -0
  22. package/build/src/_models/data-models/fam-entry-base-properties.const.js +43 -0
  23. package/build/src/_models/data-models/fam-entry.data-model.js +81 -0
  24. package/build/src/_models/data-models/fam-error.data-model.js +88 -0
  25. package/build/src/_models/data-models/fam-ingest-run.data-model.js +74 -0
  26. package/build/src/_models/data-models/fam-knowledge.data-model.js +48 -0
  27. package/build/src/_models/data-models/fam-memory.data-model.js +55 -0
  28. package/build/src/_models/data-models/fam-reference.data-model.js +67 -0
  29. package/build/src/_models/data-models/fam-rules.data-model.js +51 -0
  30. package/build/src/_models/data-models/fam-scope.data-model.js +52 -0
  31. package/build/src/_models/interfaces/fam-common.interface.js +23 -0
  32. package/build/src/_models/interfaces/fam-config.interface.js +2 -0
  33. package/build/src/_models/interfaces/fam-error.interface.js +2 -0
  34. package/build/src/_modules/embedding/_collections/fam-embedding-pricing.const.js +22 -0
  35. package/build/src/_modules/embedding/_collections/fam-store-registry.const.js +63 -0
  36. package/build/src/_modules/embedding/_models/interfaces/fam-embedding-cost.interface.js +10 -0
  37. package/build/src/_modules/embedding/_models/interfaces/fam-embedding-provider.interface.js +2 -0
  38. package/build/src/_modules/embedding/_models/interfaces/fam-resolved-provider.interface.js +2 -0
  39. package/build/src/_modules/embedding/_services/fam-embedding-bootstrap.control-service.js +52 -0
  40. package/build/src/_modules/embedding/_services/fam-embedding-cost.control-service.js +175 -0
  41. package/build/src/_modules/embedding/_services/fam-embedding-pipeline.control-service.js +202 -0
  42. package/build/src/_modules/embedding/_services/fam-embedding-preset.control-service.js +66 -0
  43. package/build/src/_modules/embedding/_services/fam-embedding.control-service.js +253 -0
  44. package/build/src/_modules/embedding/_services/fam-entry.data-service.js +64 -0
  45. package/build/src/_modules/embedding/_services/fam-lmstudio-embedding.provider.js +112 -0
  46. package/build/src/_modules/embedding/_services/fam-mock-embedding.provider.js +64 -0
  47. package/build/src/_modules/embedding/_services/fam-openai-embedding.provider.js +64 -0
  48. package/build/src/_modules/embedding/_services/fam-vector-search.control-service.js +244 -0
  49. package/build/src/_modules/embedding/index.js +40 -0
  50. package/build/src/_modules/ingest/_collections/fam-content-hash.util.js +35 -0
  51. package/build/src/_modules/ingest/_collections/fam-file-routing.util.js +95 -0
  52. package/build/src/_modules/ingest/_collections/fam-glob-match.util.js +84 -0
  53. package/build/src/_modules/ingest/_collections/fam-md-chunker.util.js +164 -0
  54. package/build/src/_modules/ingest/_collections/fam-scan-path.util.js +91 -0
  55. package/build/src/_modules/ingest/_collections/fam-secret-exclude.util.js +54 -0
  56. package/build/src/_modules/ingest/_collections/fam-sliding-chunker.util.js +76 -0
  57. package/build/src/_modules/ingest/_collections/fam-ts-chunker.util.js +316 -0
  58. package/build/src/_modules/ingest/_models/interfaces/fam-ingest.interface.js +2 -0
  59. package/build/src/_modules/ingest/_services/fam-chunker.control-service.js +114 -0
  60. package/build/src/_modules/ingest/_services/fam-delta-compare.util.js +74 -0
  61. package/build/src/_modules/ingest/_services/fam-ingest-run.data-service.js +85 -0
  62. package/build/src/_modules/ingest/_services/fam-ingest.control-service.js +384 -0
  63. package/build/src/_modules/ingest/_services/fam-scan.control-service.js +211 -0
  64. package/build/src/_modules/ingest/index.js +46 -0
  65. package/build/src/_modules/mcp/_collections/fam-core-tools.const.js +186 -0
  66. package/build/src/_modules/mcp/_models/interfaces/fam-mcp.interface.js +31 -0
  67. package/build/src/_modules/mcp/_services/fam-capabilities-tool.service.js +111 -0
  68. package/build/src/_modules/mcp/_services/fam-capability-registry.service.js +1180 -0
  69. package/build/src/_modules/mcp/_services/fam-mcp-adapter.service.js +123 -0
  70. package/build/src/_modules/mcp/_services/fam-mcp-server.service.js +69 -0
  71. package/build/src/_modules/mcp/_services/fam-read-tool.service.js +99 -0
  72. package/build/src/_modules/mcp/_services/fam-write-tool.service.js +460 -0
  73. package/build/src/_modules/mcp/index.js +35 -0
  74. package/build/src/_modules/migration/_collections/fam-claude-mem-normalize.util.js +166 -0
  75. package/build/src/_modules/migration/_collections/fam-import-content-hash.util.js +38 -0
  76. package/build/src/_modules/migration/_collections/fam-target-mapping.util.js +90 -0
  77. package/build/src/_modules/migration/_enums/fam-claude-mem-source.type-enum.js +20 -0
  78. package/build/src/_modules/migration/_models/interfaces/fam-claude-mem.interface.js +26 -0
  79. package/build/src/_modules/migration/_services/fam-claude-mem-export-reader.service.js +134 -0
  80. package/build/src/_modules/migration/_services/fam-claude-mem-import.control-service.js +533 -0
  81. package/build/src/_modules/migration/_services/fam-claude-mem-sqlite-reader.service.js +144 -0
  82. package/build/src/_modules/migration/_services/fam-claude-mem-worker-reader.service.js +115 -0
  83. package/build/src/_modules/migration/_services/fam-import-dedup.data-service.js +102 -0
  84. package/build/src/_modules/migration/index.js +38 -0
  85. package/build/src/_modules/retrieval/_models/interfaces/fam-retrieval.interface.js +2 -0
  86. package/build/src/_modules/retrieval/_services/fam-retrieval-candidate.data-service.js +67 -0
  87. package/build/src/_modules/retrieval/_services/fam-retrieval-suggestions.util.js +182 -0
  88. package/build/src/_modules/retrieval/_services/fam-retrieval.control-service.js +282 -0
  89. package/build/src/_modules/retrieval/index.js +22 -0
  90. package/build/src/_modules/scope-reference/_collections/fam-fuzzy-match.util.js +86 -0
  91. package/build/src/_modules/scope-reference/_collections/fam-scope-normalize.util.js +47 -0
  92. package/build/src/_modules/scope-reference/_models/interfaces/fam-reference-resolution.interface.js +2 -0
  93. package/build/src/_modules/scope-reference/_models/interfaces/fam-resolution-trace.interface.js +2 -0
  94. package/build/src/_modules/scope-reference/_services/fam-reference.data-service.js +179 -0
  95. package/build/src/_modules/scope-reference/_services/fam-scope-resolver.control-service.js +473 -0
  96. package/build/src/_modules/scope-reference/_services/fam-scope.data-service.js +215 -0
  97. package/build/src/_modules/scope-reference/index.js +26 -0
  98. package/build/src/_routes/server/api/api.controller.js +400 -0
  99. package/build/src/_routes/server/client-app/client-app.control-service.js +132 -0
  100. package/build/src/_routes/server/client-app/client-app.controller.js +35 -0
  101. package/build/src/_routes/server/config/config.control-service.js +476 -0
  102. package/build/src/_routes/server/config/config.data-service.js +49 -0
  103. package/build/src/_routes/server/errors/errors.control-service.js +123 -0
  104. package/build/src/_routes/server/errors/errors.controller.js +65 -0
  105. package/build/src/_routes/server/errors/errors.data-service.js +80 -0
  106. package/build/src/_routes/server/server-status/server-status.control-service.js +19 -0
  107. package/build/src/_routes/server/server-status/server-status.controller.js +39 -0
  108. package/build/src/app.server.js +122 -0
  109. package/build/src/environments/environment.js +20 -0
  110. package/build/src/index.js +18 -0
  111. package/client-dist/chunk-GHKRM4SM.js +1 -0
  112. package/client-dist/chunk-LMTL7GA3.js +575 -0
  113. package/client-dist/index.html +17 -0
  114. package/client-dist/main-2KWB3QYK.js +2 -0
  115. package/client-dist/polyfills-HGDOEU5L.js +2 -0
  116. package/client-dist/styles-3J7JD5YE.css +1 -0
  117. package/package.json +96 -0
@@ -0,0 +1,253 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.FAM_Embedding_ControlService = void 0;
4
+ const fsm_dynamo_1 = require("@futdevpro/fsm-dynamo");
5
+ const error_codes_const_1 = require("../../../_collections/error-codes.const");
6
+ const fam_error_factory_util_1 = require("../../../_collections/fam-error-factory.util");
7
+ const config_control_service_1 = require("../../../_routes/server/config/config.control-service");
8
+ const fam_embedding_cost_control_service_1 = require("./fam-embedding-cost.control-service");
9
+ const fam_lmstudio_embedding_provider_1 = require("./fam-lmstudio-embedding.provider");
10
+ const fam_mock_embedding_provider_1 = require("./fam-mock-embedding.provider");
11
+ const fam_openai_embedding_provider_1 = require("./fam-openai-embedding.provider");
12
+ /**
13
+ * `FAM_Embedding_ControlService` (SP-2.1, dsgn-006 §2) — az embedding-réteg EGYETLEN belső kapuja.
14
+ * Singleton. A config-precedencián (`scope > table > global > env-default`) feloldja a providert +
15
+ * modellt, dispatch-el a megfelelő `FAM_EmbeddingProvider`-hez (OpenAI reuse / LM Studio adapter /
16
+ * mock), alkalmazza a cost-cap-et (SP-2.4), és aggregálja a költséget (BFR-AM-007).
17
+ *
18
+ * **Provider-csere boundary (dsgn-006 §1):** a hívók CSAK az `embedTexts`/`embedOne`/`resolveProvider`
19
+ * API-t látják — a path mögötti impl (LM Studio HTTP / mock) workaround, a bedrock-csere (MP-15)
20
+ * non-breaking. A provider-példányokat lazy építjük (env/secret a hívás-időpontban érvényes).
21
+ *
22
+ * **FIGYELEM (memory: dynts_dataservice_eager_resolve):** NEM tartunk élő DataService mezőt; a config
23
+ * a `FAM_Config_ControlService.getInstance()` singleton-on át oldódik fel.
24
+ */
25
+ class FAM_Embedding_ControlService {
26
+ static _instance;
27
+ /** Default issuer (a hibák / cost-event issuer-éhez). */
28
+ issuer = 'FAM_Embedding_ControlService';
29
+ static getInstance() {
30
+ if (!FAM_Embedding_ControlService._instance) {
31
+ FAM_Embedding_ControlService._instance = new FAM_Embedding_ControlService();
32
+ }
33
+ return FAM_Embedding_ControlService._instance;
34
+ }
35
+ // =========================================================================
36
+ // RESOLVE PROVIDER (dsgn-006 §2 / SP-2.1 + SP-2.3 override)
37
+ // =========================================================================
38
+ /**
39
+ * A provider-konfiguráció feloldása a config-precedencián (dsgn-007 §3). A `embedding.provider` /
40
+ * `embedding.model` / `embedding.dimensions` / `embedding.batchSize` a `scope > table > global >
41
+ * builtin` precedencián; az env-default (a `seedDefaultsFromEnv` által GLOBAL-ba seedelt érték) így
42
+ * automatikusan a global-szinten szerepel. Az LM Studio `baseUrl` az ENV-ből jön (titok-szerű, nem DB).
43
+ */
44
+ async resolveProvider(context) {
45
+ const config = config_control_service_1.FAM_Config_ControlService.getInstance();
46
+ const resolveCtx = { table: context?.table, scopePath: context?.scopePath };
47
+ const providerValue = (await config.resolve('embedding.provider', resolveCtx)).value;
48
+ const provider = typeof providerValue === 'string' ? providerValue : 'openai';
49
+ const modelValue = (await config.resolve('embedding.model', resolveCtx)).value;
50
+ const modelId = typeof modelValue === 'string' ? modelValue : this.defaultModelForProvider(provider);
51
+ const dimsValue = (await config.resolve('embedding.dimensions', resolveCtx)).value;
52
+ const dims = typeof dimsValue === 'number' ? dimsValue : null;
53
+ const batchValue = (await config.resolve('embedding.batchSize', resolveCtx)).value;
54
+ const batchSize = typeof batchValue === 'number' ? batchValue : 64;
55
+ return {
56
+ provider: provider,
57
+ modelId: modelId,
58
+ dims: dims,
59
+ batchSize: batchSize,
60
+ baseUrl: provider === 'lmstudio' ? this.lmStudioBaseUrl() : undefined,
61
+ };
62
+ }
63
+ // =========================================================================
64
+ // EMBED (dsgn-006 §2 batch / single)
65
+ // =========================================================================
66
+ /**
67
+ * Batch-embedding (CCAP `Rag_Embedding_Client.embedTexts` mintára): a `texts` sorrendjében ad
68
+ * `number[][]`-t. A flow: resolveProvider → cost-cap precheck (block-before-overrun + warn) →
69
+ * batch-elt provider-dispatch (`embedding.batchSize`) → cost-aggregálás. Provider-hiba kanonikus
70
+ * `FAM-EMB-*` hibává alakul (MP-8 factory) — soha nem néma.
71
+ */
72
+ async embedTexts(set) {
73
+ if (!set.texts.length) {
74
+ return [];
75
+ }
76
+ const issuer = set.issuer ?? this.issuer;
77
+ const callType = set.callType ?? 'embed-write';
78
+ const resolved = await this.resolveProvider({ table: set.table, scopePath: set.scopePath });
79
+ // Cost-cap precheck (SP-2.4) — a hívás ELŐTT (block-before-overrun).
80
+ const decision = await fam_embedding_cost_control_service_1.FAM_EmbeddingCost_ControlService.getInstance().precheck({
81
+ callType: callType,
82
+ provider: resolved.provider,
83
+ modelId: resolved.modelId,
84
+ texts: set.texts,
85
+ table: set.table,
86
+ scopePath: set.scopePath,
87
+ });
88
+ if (decision.blocked) {
89
+ throw this.costCapError(decision.reason, issuer);
90
+ }
91
+ if (decision.warn) {
92
+ fsm_dynamo_1.DyFM_Log.warn(`[FAM embedding cost-warn] ${decision.reason}`);
93
+ }
94
+ const provider = this.buildProvider(resolved, issuer);
95
+ // Batch-elt dispatch (token-/rate-limit védelem; dsgn-007 embedding.batchSize).
96
+ const allVectors = [];
97
+ for (let i = 0; i < set.texts.length; i += resolved.batchSize) {
98
+ const batch = set.texts.slice(i, i + resolved.batchSize);
99
+ try {
100
+ const vectors = await provider.embedTexts({ texts: batch, model: resolved.modelId, issuer: issuer });
101
+ this.assertDimensions(vectors, resolved, issuer);
102
+ allVectors.push(...vectors);
103
+ }
104
+ catch (error) {
105
+ throw this.providerError(resolved.provider, resolved.modelId, error, issuer);
106
+ }
107
+ }
108
+ // Cost-aggregálás (SP-2.4, BFR-AM-007): a becsült tokenből (a pontos cost-event a bedrock-szinten).
109
+ fam_embedding_cost_control_service_1.FAM_EmbeddingCost_ControlService.getInstance().recordActual({
110
+ callType: callType,
111
+ provider: resolved.provider,
112
+ modelId: resolved.modelId,
113
+ tokens: fam_embedding_cost_control_service_1.FAM_EmbeddingCost_ControlService.getInstance().estimateTokens(set.texts),
114
+ });
115
+ return allVectors;
116
+ }
117
+ /** Egy-szöveg embedding (a `embedTexts` egyelemű alakja). */
118
+ async embedOne(set) {
119
+ const vectors = await this.embedTexts({ texts: [set.text], ...this.stripText(set) });
120
+ return vectors[0] ?? [];
121
+ }
122
+ // =========================================================================
123
+ // PROVIDER-elérhetőség (test_embedding_provider capability — dsgn-003)
124
+ // =========================================================================
125
+ /**
126
+ * A jelenleg feloldott provider elérhetőség-próbája (a `test_embedding_provider` / `get_embedding_config`
127
+ * capability — dsgn-003 — alapja). `false` / hiba esetén bootstrap-skip jelzés (dsgn-006 §4.1) — nem néma.
128
+ */
129
+ async testProvider(context) {
130
+ const resolved = await this.resolveProvider(context);
131
+ try {
132
+ const provider = this.buildProvider(resolved, this.issuer);
133
+ const available = await provider.isAvailable({ model: resolved.modelId, issuer: this.issuer });
134
+ return { available: available, resolved: resolved };
135
+ }
136
+ catch {
137
+ return { available: false, resolved: resolved };
138
+ }
139
+ }
140
+ // =========================================================================
141
+ // provider-építés + dispatch + hiba-konverzió
142
+ // =========================================================================
143
+ /**
144
+ * A feloldott config alapján a konkrét provider-példány. Lazy (env/secret a hívás-időpontban):
145
+ * - `openai` → `FAM_OpenAI_EmbeddingProvider` (REUSE, cost-event sink-kel),
146
+ * - `lmstudio` → `FAM_LMStudio_EmbeddingProvider` (BFR-AM-002 HTTP; `baseUrl` kötelező),
147
+ * - `mock` → `FAM_Mock_EmbeddingProvider` (BFR-AM-008 determinisztikus, dimenzió a config dims-ből),
148
+ * - ismeretlen → `FAM-EMB-PROVIDER-001` (provider nincs konfigurálva, bootstrap-skip).
149
+ */
150
+ buildProvider(resolved, issuer) {
151
+ switch (resolved.provider) {
152
+ case 'openai':
153
+ return new fam_openai_embedding_provider_1.FAM_OpenAI_EmbeddingProvider((event) => this.onCostEvent(event));
154
+ case 'lmstudio':
155
+ if (!resolved.baseUrl) {
156
+ throw fam_error_factory_util_1.FAM_Error_Util.create({
157
+ errorCode: error_codes_const_1.FAM_ERROR_CODES.embProviderNotConfigured,
158
+ message: 'Az LM Studio provider választva, de a LMSTUDIO_BASE_URL nincs beállítva (env).',
159
+ issuer: issuer,
160
+ context: { operation: 'build-provider', provider: 'lmstudio' },
161
+ });
162
+ }
163
+ return new fam_lmstudio_embedding_provider_1.FAM_LMStudio_EmbeddingProvider(resolved.baseUrl, this.lmStudioApiKey());
164
+ case 'mock':
165
+ return new fam_mock_embedding_provider_1.FAM_Mock_EmbeddingProvider(resolved.dims ?? undefined);
166
+ default:
167
+ throw fam_error_factory_util_1.FAM_Error_Util.create({
168
+ errorCode: error_codes_const_1.FAM_ERROR_CODES.embProviderNotConfigured,
169
+ message: `Ismeretlen embedding-provider: '${resolved.provider}'. `
170
+ + `Elérhető: 'openai' | 'lmstudio' | 'mock'.`,
171
+ issuer: issuer,
172
+ context: { operation: 'build-provider', provider: resolved.provider },
173
+ });
174
+ }
175
+ }
176
+ /**
177
+ * Dimenzió-konzisztencia ellenőrzés (dsgn-006 §6.2 / acceptance #6): ha a config explicit `dims`-et
178
+ * deklarál, a kapott vektoroknak ezzel egyezniük kell — eltérés → `FAM-EMB-PROVIDER-003` (nem néma,
179
+ * NEM kever eltérő dimenziójú vektort egy tár pool-jába). `dims=null` → a modell natív dimenziója (skip).
180
+ */
181
+ assertDimensions(vectors, resolved, issuer) {
182
+ if (resolved.dims === null || !vectors.length) {
183
+ return;
184
+ }
185
+ const actual = vectors[0].length;
186
+ if (actual !== resolved.dims) {
187
+ throw fam_error_factory_util_1.FAM_Error_Util.create({
188
+ errorCode: error_codes_const_1.FAM_ERROR_CODES.embProviderDimensionMismatch,
189
+ message: `Az embedding dimenzió-eltérés: a(z) '${resolved.modelId}' modell ${actual}-dimenziós `
190
+ + `vektort adott, de a config ${resolved.dims}-et deklarál. Re-embed szükséges (dsgn-006 §5/§6.2).`,
191
+ issuer: issuer,
192
+ context: { operation: 'assert-dimensions', provider: resolved.provider },
193
+ });
194
+ }
195
+ }
196
+ /** Provider-hiba kanonikus `FAM-EMB-PROVIDER-002`-vé (elérhetetlen / API-hiba). */
197
+ providerError(provider, modelId, cause, issuer) {
198
+ // Ha már kanonikus FAM error (pl. dimenzió-mismatch a batch-en belül), nem csomagoljuk újra.
199
+ if (cause instanceof fsm_dynamo_1.DyFM_Error) {
200
+ return cause;
201
+ }
202
+ return fam_error_factory_util_1.FAM_Error_Util.create({
203
+ errorCode: error_codes_const_1.FAM_ERROR_CODES.embProviderUnreachable,
204
+ message: `Az embedding-provider ('${provider}', modell '${modelId}') elérhetetlen vagy hibát adott.`,
205
+ issuer: issuer,
206
+ cause: cause,
207
+ context: { operation: 'embed', provider: provider },
208
+ });
209
+ }
210
+ /** Cost-cap blokk → `FAM-EMB-COSTCAP-001` (block-before-overrun; dsgn-006 §7 / dsgn-008). */
211
+ costCapError(reason, issuer) {
212
+ return fam_error_factory_util_1.FAM_Error_Util.create({
213
+ errorCode: error_codes_const_1.FAM_ERROR_CODES.embCostCap,
214
+ message: `Embedding cost-cap: a hívás blokkolva a túllépés előtt. ${reason}`,
215
+ issuer: issuer,
216
+ context: { operation: 'cost-cap' },
217
+ });
218
+ }
219
+ /** A cost-event sink (a provider per-call token-fogyasztását aggregálja — BFR-AM-007). */
220
+ onCostEvent(event) {
221
+ // A pontos token-fogyasztás a cost-event-ből; a call-type a `recordActual`-ban becsült úton is
222
+ // beíródik, ezért itt CSAK telemetria-log (a dupla-számolás elkerülése végett nem aggregálunk).
223
+ fsm_dynamo_1.DyFM_Log.log(`[FAM embedding cost-event] ${event.provider}/${event.model} tokens=${event.tokensUsed.total}`);
224
+ }
225
+ // =========================================================================
226
+ // env + default helpers
227
+ // =========================================================================
228
+ /** A default modell egy providerhez (ha a config nem ad modellt). */
229
+ defaultModelForProvider(provider) {
230
+ if (provider === 'lmstudio') {
231
+ return process.env.LMSTUDIO_EMBEDDING_MODEL || 'nomic-embed-text-v1.5';
232
+ }
233
+ return process.env.OPENAI_EMBEDDING_MODEL || 'text-embedding-3-large';
234
+ }
235
+ /** Az LM Studio base URL az ENV-ből (titok-szerű, nem DB; dsgn-006 §8). */
236
+ lmStudioBaseUrl() {
237
+ return process.env.LMSTUDIO_BASE_URL;
238
+ }
239
+ /** Opcionális LM Studio API-kulcs (default-ban nem kell). */
240
+ lmStudioApiKey() {
241
+ return process.env.LMSTUDIO_API_KEY;
242
+ }
243
+ /** A `embedOne` set-jéből a `text`-et kihagyó opció-objektum (a `embedTexts`-nek átadva). */
244
+ stripText(set) {
245
+ return {
246
+ table: set.table,
247
+ scopePath: set.scopePath,
248
+ callType: set.callType,
249
+ issuer: set.issuer,
250
+ };
251
+ }
252
+ }
253
+ exports.FAM_Embedding_ControlService = FAM_Embedding_ControlService;
@@ -0,0 +1,64 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.FAM_Entry_DataService = void 0;
4
+ const nts_dynamo_1 = require("@futdevpro/nts-dynamo");
5
+ const fam_entry_data_model_1 = require("../../../_models/data-models/fam-entry.data-model");
6
+ /**
7
+ * `FAM_Entry_DataService` (SP-2.2) — egy GENERIKUS, tár-paraméteres entry-CRUD a vektor-rétegnek.
8
+ * A `dataParams`-ot a `FAM_STORE_REGISTRY`-ből kapja (per-tár), így EGY data-service-osztály szolgálja
9
+ * ki mind a 6 fő tárat a vektor-persist + hidratálás során — a közös `FAM_Entry` bázis-mezőkre (a
10
+ * vektor-réteg CSAK a `content`/`contentVector`/`embeddingStatus`/`scopePath`/`tags`/`kind`/`weight`
11
+ * mezőket olvassa).
12
+ *
13
+ * **Atomikus vektor-write (memory: reference_mongoose_mixed_atomic_write):** a `contentVector` egy
14
+ * `number[]` Mixed-szerű mező — a `findOne→mutate→save` SILENT-DROP-ot ad; ezért a vektor-frissítés
15
+ * `updateData` (`$set`) úton megy (lásd `setVector`). NEM `saveData`-val a teljes instance-en.
16
+ *
17
+ * **FIGYELEM (memory: dynts_dataservice_eager_resolve):** a base-ctor eager `getDBService`-t hív, ezért
18
+ * a control-service-ek NEM tartanak élő példányt mezőként — minden művelet előtt lazy `new`.
19
+ */
20
+ class FAM_Entry_DataService extends nts_dynamo_1.DyNTS_DataService {
21
+ constructor(set) {
22
+ super(new fam_entry_data_model_1.FAM_Entry(), set.dataParams, set.issuer);
23
+ }
24
+ /**
25
+ * A move-to-archive soft-delete (dsgn-001 §8) archív-collection service-e: a base `deleteData`
26
+ * (a 6 fő tár `addArchive:true`) ezt hívja, hogy a törölt entry-t a `<tár>_archived` tárba
27
+ * mozgassa. A `DyNTS_DataService` base default-ja DOB (nincs ökoszisztéma-implementáció) — ezért
28
+ * az explicit override (a `DyNTS_ArchiveDataService` a `dataParams.addArchive`-ból oldja az
29
+ * archív-nevet; a per-tár `dataParams` itt is a `this.dataParams`).
30
+ */
31
+ getArchiveDataService() {
32
+ return new nts_dynamo_1.DyNTS_ArchiveDataService(new fam_entry_data_model_1.FAM_Entry(), this.dataParams, this.issuer);
33
+ }
34
+ /**
35
+ * A completed-embeddingű, nem-törölt entry-k egy lapja a hidratáláshoz (SP-2.2 boot-hidratálás).
36
+ * A lapozás `skip`/`limit`-tel (a base `findDataList` egyszerű filter-rel; a `_deleted` undefined =
37
+ * aktív). A `findDataList` a soft-delete-elteket alapból kihagyja (a Dynamo-konvenció szerint).
38
+ */
39
+ async findHydratableList(filter) {
40
+ return this.findDataList(filter);
41
+ }
42
+ /**
43
+ * Egy entry vektor-mezőinek ATOMIKUS frissítése (`$set`) — a Mixed silent-drop ellen. A
44
+ * `contentVector` + `embeddingModel` + `embeddingStatus` + opc. `contentHash` egy lépésben.
45
+ */
46
+ async setVector(set) {
47
+ const setBlock = {
48
+ contentVector: set.contentVector,
49
+ embeddingModel: set.embeddingModel,
50
+ embeddingStatus: set.embeddingStatus,
51
+ };
52
+ if (set.contentHash !== undefined) {
53
+ setBlock.contentHash = set.contentHash;
54
+ }
55
+ const update = { $set: setBlock };
56
+ await this.updateData({ filterBy: { _id: set.id }, update: update });
57
+ }
58
+ /** Csak az `embeddingStatus` atomikus frissítése (pl. `pending` a re-embed elején). */
59
+ async setEmbeddingStatus(id, status) {
60
+ const update = { $set: { embeddingStatus: status } };
61
+ await this.updateData({ filterBy: { _id: id }, update: update });
62
+ }
63
+ }
64
+ exports.FAM_Entry_DataService = FAM_Entry_DataService;
@@ -0,0 +1,112 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.FAM_LMStudio_EmbeddingProvider = void 0;
4
+ /**
5
+ * `FAM_LMStudio_EmbeddingProvider` (SP-2.1) — **BFR-AM-002 projekt-lokális workaround**: OpenAI-
6
+ * kompatibilis lokális embedding-endpoint (`${baseUrl}/embeddings`), a CCAP
7
+ * `Rag_OpenAiCompatible_RagEmbedding_Client` mintájára. `baseUrl` + `modelId` (+ opc. `apiKey`).
8
+ * Lokális futás → nincs USD-költség (dsgn-006 §7, csak telemetria). Node 20 global `fetch`-et használ
9
+ * (függőség-mentes), így a core NEM hoz be provider-specifikus SDK-t (boundary, dsgn-006 §1).
10
+ *
11
+ * **Csere-pont (MP-15):** amikor a bedrock `DyNTS_LMStudio_Embedding_ControlService` landol, ez az
12
+ * osztály lecserélhető anélkül, hogy a `FAM_EmbeddingProvider` interfész-hívók törnének.
13
+ */
14
+ class FAM_LMStudio_EmbeddingProvider {
15
+ baseUrl;
16
+ apiKey;
17
+ providerId = 'lmstudio';
18
+ /**
19
+ * @param baseUrl OpenAI-kompatibilis lokális endpoint (pl. `http://localhost:1234/v1`).
20
+ * @param apiKey opcionális Bearer token (LM Studio default-ban nem kér).
21
+ */
22
+ constructor(baseUrl, apiKey) {
23
+ this.baseUrl = baseUrl;
24
+ this.apiKey = apiKey;
25
+ }
26
+ /**
27
+ * Batch-embedding az OpenAI-kompatibilis `/embeddings` végpontra. A `texts` sorrendje == a
28
+ * visszaadott `number[][]` sorrendje. HTTP-/parse-hiba deskriptív üzenettel dobódik (a
29
+ * `FAM_Embedding_ControlService` `FAM-EMB-PROVIDER-002`-vé alakítja — dsgn-008).
30
+ */
31
+ async embedTexts(set) {
32
+ const url = `${this.trimTrailingSlashes(this.baseUrl)}/embeddings`;
33
+ const headers = { 'Content-Type': 'application/json' };
34
+ if (this.apiKey && this.apiKey.trim().length) {
35
+ headers.Authorization = `Bearer ${this.apiKey.trim()}`;
36
+ }
37
+ const response = await fetch(url, {
38
+ method: 'POST',
39
+ headers: headers,
40
+ body: JSON.stringify({ model: set.model, input: set.texts }),
41
+ });
42
+ const rawText = await response.text();
43
+ if (!response.ok) {
44
+ throw new Error(`LM Studio (OpenAI-compatible) embeddings HTTP ${response.status} | url=${url} | `
45
+ + `model=${set.model} | body=${this.snapshot(rawText)}`);
46
+ }
47
+ let parsed;
48
+ try {
49
+ parsed = JSON.parse(rawText);
50
+ }
51
+ catch {
52
+ throw new Error(`LM Studio embeddings: nem JSON válasz | url=${url} | model=${set.model} | `
53
+ + `body=${this.snapshot(rawText)}`);
54
+ }
55
+ return this.parseEmbeddingDataArray(parsed, set.texts.length, url, set.model);
56
+ }
57
+ /**
58
+ * Provider-elérhetőség: egy minimál embedding-próba (egy üres-szöveg helyett rövid token). Ha a
59
+ * hívás dob (endpoint down / model hiányzik), `false` (a `test_embedding_provider` capability így
60
+ * jelez bootstrap-skip-et, dsgn-006 §4.1). Soha nem propagál hibát (try/catch → false).
61
+ */
62
+ async isAvailable(set) {
63
+ try {
64
+ const vectors = await this.embedTexts({ texts: ['ping'], model: set.model, issuer: set.issuer });
65
+ return vectors.length === 1 && vectors[0].length > 0;
66
+ }
67
+ catch {
68
+ return false;
69
+ }
70
+ }
71
+ /** Trailing `/`-ek levágása (kettős slash elkerülése a `/embeddings` join-nál). */
72
+ trimTrailingSlashes(value) {
73
+ return value.replace(/\/+$/, '');
74
+ }
75
+ /** Rövid, biztonságos válasz-snapshot a hiba-üzenethez (max 300 char). */
76
+ snapshot(value) {
77
+ const trimmed = (value ?? '').slice(0, 300);
78
+ return trimmed.length === 300 ? `${trimmed}…` : trimmed;
79
+ }
80
+ /**
81
+ * A `data[].embedding` típusbiztos kiolvasása (CCAP-minta). Hibás/hiányzó mező → deskriptív hiba
82
+ * a teljes válasz-snapshottal. A darabszám-egyezés ellenőrzött (a `texts` és a vektorok 1:1).
83
+ */
84
+ parseEmbeddingDataArray(json, expectedCount, url, modelId) {
85
+ if (json === null || typeof json !== 'object' || Array.isArray(json)) {
86
+ throw new Error(`LM Studio embeddings: érvénytelen válasz-objektum | url=${url} | model=${modelId}`);
87
+ }
88
+ const dataRaw = Reflect.get(json, 'data');
89
+ if (!Array.isArray(dataRaw)) {
90
+ throw new Error(`LM Studio embeddings: hiányzó data tömb | url=${url} | model=${modelId}`);
91
+ }
92
+ const out = [];
93
+ for (const item of dataRaw) {
94
+ if (item === null || typeof item !== 'object' || Array.isArray(item)) {
95
+ continue;
96
+ }
97
+ const embRaw = Reflect.get(item, 'embedding');
98
+ if (!Array.isArray(embRaw)) {
99
+ continue;
100
+ }
101
+ const vec = embRaw.filter((x) => typeof x === 'number' && Number.isFinite(x));
102
+ if (vec.length) {
103
+ out.push(vec);
104
+ }
105
+ }
106
+ if (out.length !== expectedCount) {
107
+ throw new Error(`LM Studio embeddings: várt ${expectedCount} vektor, kaptunk ${out.length} | url=${url} | model=${modelId}`);
108
+ }
109
+ return out;
110
+ }
111
+ }
112
+ exports.FAM_LMStudio_EmbeddingProvider = FAM_LMStudio_EmbeddingProvider;
@@ -0,0 +1,64 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.FAM_Mock_EmbeddingProvider = exports.FAM_MOCK_EMBEDDING_DIMS = void 0;
4
+ const tslib_1 = require("tslib");
5
+ const crypto = tslib_1.__importStar(require("crypto"));
6
+ /** A mock-provider default dimenziója (kicsi, hogy a teszt-pool gyors legyen, de elég a koszinuszhoz). */
7
+ exports.FAM_MOCK_EMBEDDING_DIMS = 64;
8
+ /**
9
+ * `FAM_Mock_EmbeddingProvider` (SP-2.1) — **BFR-AM-008 projekt-lokális workaround**: determinisztikus,
10
+ * hash-alapú, fix-dimenziós, l2-normalizált pszeudo-vektor a szövegből (dsgn-006 §9). Provider- és
11
+ * hálózat-mentes → a tesztek (MP-13) + a tsc-/offline-smoke valódi kulcs nélkül futnak. Azonos szöveg
12
+ * → azonos vektor (idempotens), és a koszinusz-keresés VALÓDI LVS-pool-on tesztelhető.
13
+ *
14
+ * **Csere-pont (MP-15):** amikor a bedrock `DyNTS_AI_Embedding_MockService` landol, ez lecserélhető.
15
+ */
16
+ class FAM_Mock_EmbeddingProvider {
17
+ dims;
18
+ providerId = 'mock';
19
+ /** @param dims a generált vektor dimenziója (default `FAM_MOCK_EMBEDDING_DIMS`). */
20
+ constructor(dims = exports.FAM_MOCK_EMBEDDING_DIMS) {
21
+ this.dims = dims;
22
+ }
23
+ /**
24
+ * Determinisztikus batch-embedding: minden szöveghez a SHA-256 hash bájtjaiból generál egy fix-
25
+ * dimenziós, l2-normalizált vektort. A `model` paramétert beleszövi a seed-be, hogy modell-váltás
26
+ * más vektort adjon (re-embed teszteléshez, dsgn-006 §5).
27
+ */
28
+ async embedTexts(set) {
29
+ return set.texts.map((text) => this.deterministicVector(`${set.model}::${text}`));
30
+ }
31
+ /** A mock mindig elérhető (nincs külső függőség). */
32
+ async isAvailable(_set) {
33
+ return true;
34
+ }
35
+ /**
36
+ * Egy determinisztikus, l2-normalizált vektor a seed-ből. A hash bájtjait körkörösen használja a
37
+ * `dims` kitöltésére, [-1, 1) tartományra képzi, majd l2-normalizál (egységhossz a koszinuszhoz).
38
+ */
39
+ deterministicVector(seed) {
40
+ const hash = crypto.createHash('sha256').update(seed).digest();
41
+ const vector = [];
42
+ for (let i = 0; i < this.dims; i++) {
43
+ const byte = hash[i % hash.length];
44
+ // Egy második hash-bájttal keverünk a hosszabb dimenziókhoz, hogy ne ismétlődjön a minta.
45
+ const mix = hash[(i * 7 + 3) % hash.length];
46
+ const combined = (byte ^ mix) & 0xff;
47
+ vector.push((combined / 255) * 2 - 1);
48
+ }
49
+ return this.l2Normalize(vector);
50
+ }
51
+ /** L2-normalizálás (egységhossz). Nulla-vektor → változatlan (elkerüli a 0-osztást). */
52
+ l2Normalize(vector) {
53
+ let sumSq = 0;
54
+ for (const value of vector) {
55
+ sumSq += value * value;
56
+ }
57
+ const norm = Math.sqrt(sumSq);
58
+ if (!norm) {
59
+ return vector;
60
+ }
61
+ return vector.map((value) => value / norm);
62
+ }
63
+ }
64
+ exports.FAM_Mock_EmbeddingProvider = FAM_Mock_EmbeddingProvider;
@@ -0,0 +1,64 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.FAM_OpenAI_EmbeddingProvider = void 0;
4
+ const ai_1 = require("@futdevpro/fsm-dynamo/ai");
5
+ const open_ai_1 = require("@futdevpro/nts-dynamo/ai/open-ai");
6
+ /**
7
+ * `FAM_OpenAI_EmbeddingProvider` (SP-2.1) — az OpenAI embedding path. **REUSE ([A], dsgn-006 §2):**
8
+ * a bedrock `DyNTS_OAI_Embedding_ControlService`-t használja (`createEmbeddings` batch). A secret
9
+ * (`OPENAI_API_KEY`) + base URL az OpenAI-client default env-feloldásából jön (`DyFM_OAI_ClientOptions`),
10
+ * a modell a config-precedencián feloldva érkezik (`embedTexts.model`).
11
+ *
12
+ * **LM Studio reuse-pont:** a `DyNTS_OAI_Embedding_ControlService` OpenAI-kliense `baseURL`-lel
13
+ * átirányítható egy OpenAI-kompatibilis lokális endpointra — de a FAM az LM Studio-t a `fetch`-alapú
14
+ * `FAM_LMStudio_EmbeddingProvider`-rel kezeli (explicit, függőség-mentes; BFR-AM-002 workaround),
15
+ * hogy a két path egyértelműen elkülönüljön és a bedrock-csere (MP-15) tiszta legyen.
16
+ *
17
+ * **Cost-event (dsgn-006 §7):** a base `onCostEvent` callback-en kapjuk az emittált token-fogyasztást;
18
+ * a `FAM_Embedding_ControlService` regisztrálja a sink-et és aggregál (BFR-AM-007 workaround).
19
+ */
20
+ class FAM_OpenAI_EmbeddingProvider {
21
+ providerId = 'openai';
22
+ /** A bedrock OpenAI embedding control-service (REUSE). Az `onCostEvent` a constructor-ban köthető. */
23
+ embedding_CS;
24
+ /**
25
+ * @param onCostEvent opcionális cost-event sink (a `FAM_Embedding_ControlService` adja át, hogy a
26
+ * token-fogyasztást aggregálja — BFR-AM-007). Non-breaking: ha undefined, nincs aggregáció.
27
+ */
28
+ constructor(onCostEvent) {
29
+ this.embedding_CS = new open_ai_1.DyNTS_OAI_Embedding_ControlService({ onCostEvent: onCostEvent });
30
+ }
31
+ /**
32
+ * Batch-embedding az OpenAI-n (REUSE). A `fullResponse=false` → tiszta `number[][]`. A model a
33
+ * config-precedencián feloldott azonosító. Dimenzió-eltérés / API-hiba a base-en dobódik; a
34
+ * `FAM_Embedding_ControlService` kanonikus `FAM-EMB-*` hibává alakítja (dsgn-008).
35
+ */
36
+ async embedTexts(set) {
37
+ // `fullResponse=false` → a base `number[][]`-t ad. A return-type union a `CreateEmbeddingResponse`-t
38
+ // is tartalmazza (openai típus), de NEM importáljuk explicit (a csomag `openai` peer-függőség
39
+ // nélkül buildel; skipLibCheck) — strukturálisan szűkítünk.
40
+ const result = await this.embedding_CS.createEmbeddings({
41
+ texts: set.texts,
42
+ model: set.model,
43
+ fullResponse: false,
44
+ issuer: set.issuer,
45
+ });
46
+ if (Array.isArray(result)) {
47
+ return result;
48
+ }
49
+ // Defenzív fallback: ha valamiért a full response érkezne, a `data[].embedding`-et emeljük ki.
50
+ return result.data.map((item) => item.embedding);
51
+ }
52
+ /**
53
+ * Provider-elérhetőség: a base `testConnection` (OpenAI API ping). A `model` itt nem releváns a
54
+ * connection-próbához, de a szerződés egységessége miatt megtartjuk. A `DyFM_AI_Provider.OpenAI`
55
+ * konzisztencia-jelölés a diagnosztikához.
56
+ */
57
+ async isAvailable(set) {
58
+ const _provider = ai_1.DyFM_AI_Provider.OpenAI;
59
+ void _provider;
60
+ void set.model;
61
+ return this.embedding_CS.testConnection(set.issuer);
62
+ }
63
+ }
64
+ exports.FAM_OpenAI_EmbeddingProvider = FAM_OpenAI_EmbeddingProvider;