@samanhappy/mcphub 1.0.30 → 1.0.32
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/dist/betterAuth.js +13 -5
- package/dist/betterAuth.js.map +1 -1
- package/dist/cli/commands/cache.js.map +1 -1
- package/dist/cli/commands/call.js +2 -2
- package/dist/cli/commands/call.js.map +1 -1
- package/dist/cli/commands/config.js.map +1 -1
- package/dist/cli/commands/discover.js.map +1 -1
- package/dist/cli/commands/export.js.map +1 -1
- package/dist/cli/commands/groups.js.map +1 -1
- package/dist/cli/commands/install.js +1 -1
- package/dist/cli/commands/install.js.map +1 -1
- package/dist/cli/commands/keys.js.map +1 -1
- package/dist/cli/commands/login.js.map +1 -1
- package/dist/cli/commands/servers.js.map +1 -1
- package/dist/cli/commands/tools.js +2 -2
- package/dist/cli/commands/tools.js.map +1 -1
- package/dist/cli/context.js +1 -1
- package/dist/cli/context.js.map +1 -1
- package/dist/cli/http.js +3 -1
- package/dist/cli/http.js.map +1 -1
- package/dist/clients/openapi.js +75 -7
- package/dist/clients/openapi.js.map +1 -1
- package/dist/config/DaoConfigService.js +3 -2
- package/dist/config/DaoConfigService.js.map +1 -1
- package/dist/config/configManager.js +11 -9
- package/dist/config/configManager.js.map +1 -1
- package/dist/config/index.js +32 -7
- package/dist/config/index.js.map +1 -1
- package/dist/config/jwt.js +2 -1
- package/dist/config/jwt.js.map +1 -1
- package/dist/config/migrationUtils.js +46 -45
- package/dist/config/migrationUtils.js.map +1 -1
- package/dist/constants/oauthServerDefaults.js.map +1 -1
- package/dist/controllers/activityController.js +6 -5
- package/dist/controllers/activityController.js.map +1 -1
- package/dist/controllers/authController.js +5 -4
- package/dist/controllers/authController.js.map +1 -1
- package/dist/controllers/bearerKeyController.js +7 -6
- package/dist/controllers/bearerKeyController.js.map +1 -1
- package/dist/controllers/betterAuthController.js +2 -1
- package/dist/controllers/betterAuthController.js.map +1 -1
- package/dist/controllers/builtinPromptController.js +13 -5
- package/dist/controllers/builtinPromptController.js.map +1 -1
- package/dist/controllers/builtinResourceController.js +14 -6
- package/dist/controllers/builtinResourceController.js.map +1 -1
- package/dist/controllers/changelogController.js +5 -2
- package/dist/controllers/changelogController.js.map +1 -1
- package/dist/controllers/cloudController.js +11 -10
- package/dist/controllers/cloudController.js.map +1 -1
- package/dist/controllers/configController.js +4 -3
- package/dist/controllers/configController.js.map +1 -1
- package/dist/controllers/contextCostController.js +3 -2
- package/dist/controllers/contextCostController.js.map +1 -1
- package/dist/controllers/groupController.js +2 -1
- package/dist/controllers/groupController.js.map +1 -1
- package/dist/controllers/healthController.js +2 -1
- package/dist/controllers/healthController.js.map +1 -1
- package/dist/controllers/hostedInternalController.js +2 -1
- package/dist/controllers/hostedInternalController.js.map +1 -1
- package/dist/controllers/logController.js +5 -4
- package/dist/controllers/logController.js.map +1 -1
- package/dist/controllers/mcpbController.js +17 -7
- package/dist/controllers/mcpbController.js.map +1 -1
- package/dist/controllers/oauthCallbackController.js +28 -27
- package/dist/controllers/oauthCallbackController.js.map +1 -1
- package/dist/controllers/oauthClientController.js +7 -6
- package/dist/controllers/oauthClientController.js.map +1 -1
- package/dist/controllers/oauthDynamicRegistrationController.js +22 -4
- package/dist/controllers/oauthDynamicRegistrationController.js.map +1 -1
- package/dist/controllers/oauthServerController.js +96 -10
- package/dist/controllers/oauthServerController.js.map +1 -1
- package/dist/controllers/openApiController.js +7 -6
- package/dist/controllers/openApiController.js.map +1 -1
- package/dist/controllers/promptController.js +2 -1
- package/dist/controllers/promptController.js.map +1 -1
- package/dist/controllers/registryController.js +4 -3
- package/dist/controllers/registryController.js.map +1 -1
- package/dist/controllers/serverController.js +46 -34
- package/dist/controllers/serverController.js.map +1 -1
- package/dist/controllers/templateController.js +2 -1
- package/dist/controllers/templateController.js.map +1 -1
- package/dist/controllers/toolController.js +2 -1
- package/dist/controllers/toolController.js.map +1 -1
- package/dist/dao/DaoFactory.js +3 -2
- package/dist/dao/DaoFactory.js.map +1 -1
- package/dist/dao/ServerDao.js.map +1 -1
- package/dist/dao/UserConfigDaoDbImpl.js.map +1 -1
- package/dist/dao/UserDao.js.map +1 -1
- package/dist/dao/base/JsonFileBaseDao.js +3 -2
- package/dist/dao/base/JsonFileBaseDao.js.map +1 -1
- package/dist/dao/examples.js +35 -34
- package/dist/dao/examples.js.map +1 -1
- package/dist/db/connection.js +59 -58
- package/dist/db/connection.js.map +1 -1
- package/dist/db/index.js +2 -1
- package/dist/db/index.js.map +1 -1
- package/dist/db/repositories/ServerRepository.js +3 -3
- package/dist/db/repositories/ServerRepository.js.map +1 -1
- package/dist/db/repositories/SystemConfigRepository.js +2 -1
- package/dist/db/repositories/SystemConfigRepository.js.map +1 -1
- package/dist/db/repositories/UserConfigRepository.js.map +1 -1
- package/dist/db/repositories/VectorEmbeddingRepository.js +6 -5
- package/dist/db/repositories/VectorEmbeddingRepository.js.map +1 -1
- package/dist/index.js +28 -19
- package/dist/index.js.map +1 -1
- package/dist/middlewares/auth.js +4 -3
- package/dist/middlewares/auth.js.map +1 -1
- package/dist/middlewares/index.js +2 -1
- package/dist/middlewares/index.js.map +1 -1
- package/dist/middlewares/userContext.js +7 -6
- package/dist/middlewares/userContext.js.map +1 -1
- package/dist/models/OAuth.js +7 -6
- package/dist/models/OAuth.js.map +1 -1
- package/dist/models/User.js +13 -12
- package/dist/models/User.js.map +1 -1
- package/dist/routes/index.js +9 -9
- package/dist/routes/index.js.map +1 -1
- package/dist/scripts/dao-demo.js +28 -27
- package/dist/scripts/dao-demo.js.map +1 -1
- package/dist/server.js +40 -30
- package/dist/server.js.map +1 -1
- package/dist/services/activityLoggingService.js +3 -2
- package/dist/services/activityLoggingService.js.map +1 -1
- package/dist/services/betterAuthConfig.js +1 -4
- package/dist/services/betterAuthConfig.js.map +1 -1
- package/dist/services/betterAuthSession.js +8 -7
- package/dist/services/betterAuthSession.js.map +1 -1
- package/dist/services/changelogService.js +5 -2
- package/dist/services/changelogService.js.map +1 -1
- package/dist/services/cloudService.js +10 -9
- package/dist/services/cloudService.js.map +1 -1
- package/dist/services/contextCostService.js +6 -2
- package/dist/services/contextCostService.js.map +1 -1
- package/dist/services/groupService.js +8 -7
- package/dist/services/groupService.js.map +1 -1
- package/dist/services/hostedAuthService.js +3 -2
- package/dist/services/hostedAuthService.js.map +1 -1
- package/dist/services/hostedControlPlaneClient.js.map +1 -1
- package/dist/services/hostedEventSubscriber.js +9 -8
- package/dist/services/hostedEventSubscriber.js.map +1 -1
- package/dist/services/hostedRuntimeCatalogNames.js +1 -3
- package/dist/services/hostedRuntimeCatalogNames.js.map +1 -1
- package/dist/services/keepAliveService.js +7 -6
- package/dist/services/keepAliveService.js.map +1 -1
- package/dist/services/marketService.js +2 -1
- package/dist/services/marketService.js.map +1 -1
- package/dist/services/mcpOAuthProvider.js +22 -21
- package/dist/services/mcpOAuthProvider.js.map +1 -1
- package/dist/services/mcpService.js +136 -123
- package/dist/services/mcpService.js.map +1 -1
- package/dist/services/oauthClientRegistration.js +30 -29
- package/dist/services/oauthClientRegistration.js.map +1 -1
- package/dist/services/oauthServerService.js +22 -17
- package/dist/services/oauthServerService.js.map +1 -1
- package/dist/services/oauthService.js +16 -15
- package/dist/services/oauthService.js.map +1 -1
- package/dist/services/oauthSettingsStore.js +4 -3
- package/dist/services/oauthSettingsStore.js.map +1 -1
- package/dist/services/openApiGeneratorService.js +1 -3
- package/dist/services/openApiGeneratorService.js.map +1 -1
- package/dist/services/registry.js +2 -1
- package/dist/services/registry.js.map +1 -1
- package/dist/services/requestContextService.js.map +1 -1
- package/dist/services/smartRoutingService.js +6 -5
- package/dist/services/smartRoutingService.js.map +1 -1
- package/dist/services/sseService.js +66 -55
- package/dist/services/sseService.js.map +1 -1
- package/dist/services/templateService.js +57 -12
- package/dist/services/templateService.js.map +1 -1
- package/dist/services/toolResultCompressionService.js +8 -5
- package/dist/services/toolResultCompressionService.js.map +1 -1
- package/dist/services/upstreamOAuthDisconnectService.js +9 -6
- package/dist/services/upstreamOAuthDisconnectService.js.map +1 -1
- package/dist/services/userService.js +5 -4
- package/dist/services/userService.js.map +1 -1
- package/dist/services/vectorSearchService.js +126 -112
- package/dist/services/vectorSearchService.js.map +1 -1
- package/dist/utils/abortIsolatingFetch.js +31 -0
- package/dist/utils/abortIsolatingFetch.js.map +1 -0
- package/dist/utils/basePath.js +13 -0
- package/dist/utils/basePath.js.map +1 -0
- package/dist/utils/cacheUtils.js +5 -4
- package/dist/utils/cacheUtils.js.map +1 -1
- package/dist/utils/corsOrigin.js +42 -0
- package/dist/utils/corsOrigin.js.map +1 -0
- package/dist/utils/dbRetry.js +2 -5
- package/dist/utils/dbRetry.js.map +1 -1
- package/dist/utils/frontendShell.js +89 -0
- package/dist/utils/frontendShell.js.map +1 -0
- package/dist/utils/i18n.js.map +1 -1
- package/dist/utils/logger.js +170 -0
- package/dist/utils/logger.js.map +1 -0
- package/dist/utils/migration.js +51 -50
- package/dist/utils/migration.js.map +1 -1
- package/dist/utils/oauthConsentResource.js +45 -0
- package/dist/utils/oauthConsentResource.js.map +1 -0
- package/dist/utils/parameterConversion.js +2 -1
- package/dist/utils/parameterConversion.js.map +1 -1
- package/dist/utils/path.js +9 -8
- package/dist/utils/path.js.map +1 -1
- package/dist/utils/proxyTrust.js.map +1 -1
- package/dist/utils/rateLimit.js +8 -0
- package/dist/utils/rateLimit.js.map +1 -1
- package/dist/utils/requireAdmin.js +15 -0
- package/dist/utils/requireAdmin.js.map +1 -0
- package/dist/utils/serverConfigPersistence.js.map +1 -1
- package/dist/utils/serverShutdown.js +3 -2
- package/dist/utils/serverShutdown.js.map +1 -1
- package/dist/utils/smartRouting.js +4 -3
- package/dist/utils/smartRouting.js.map +1 -1
- package/dist/utils/ssrf.js +19 -6
- package/dist/utils/ssrf.js.map +1 -1
- package/dist/utils/tokenCost.js.map +1 -1
- package/dist/utils/tokenTruncation.js +9 -4
- package/dist/utils/tokenTruncation.js.map +1 -1
- package/dist/utils/version.js +3 -2
- package/dist/utils/version.js.map +1 -1
- package/frontend/dist/assets/{ActivityPage-U9om5C9X.js → ActivityPage-C6m1hGLq.js} +2 -2
- package/frontend/dist/assets/{ActivityPage-U9om5C9X.js.map → ActivityPage-C6m1hGLq.js.map} +1 -1
- package/frontend/dist/assets/{ConfirmDialog-CYfXEuyU.js → ConfirmDialog-BCMNzi21.js} +2 -2
- package/frontend/dist/assets/{ConfirmDialog-CYfXEuyU.js.map → ConfirmDialog-BCMNzi21.js.map} +1 -1
- package/frontend/dist/assets/{Dashboard-DGqpU4bd.js → Dashboard-DFKfjPff.js} +2 -2
- package/frontend/dist/assets/{Dashboard-DGqpU4bd.js.map → Dashboard-DFKfjPff.js.map} +1 -1
- package/frontend/dist/assets/{DeleteDialog-DNe-zTEl.js → DeleteDialog-D9ycSRvn.js} +2 -2
- package/frontend/dist/assets/{DeleteDialog-DNe-zTEl.js.map → DeleteDialog-D9ycSRvn.js.map} +1 -1
- package/frontend/dist/assets/{EndpointCopy-DuGmhiMq.js → EndpointCopy-CP_Fb1To.js} +2 -2
- package/frontend/dist/assets/{EndpointCopy-DuGmhiMq.js.map → EndpointCopy-CP_Fb1To.js.map} +1 -1
- package/frontend/dist/assets/{GroupsPage-Dhqy02FY.js → GroupsPage-Cwfm5q-a.js} +3 -3
- package/frontend/dist/assets/{GroupsPage-Dhqy02FY.js.map → GroupsPage-Cwfm5q-a.js.map} +1 -1
- package/frontend/dist/assets/{LoginPage-BXRn47pC.js → LoginPage-CnF44h_n.js} +2 -2
- package/frontend/dist/assets/LoginPage-CnF44h_n.js.map +1 -0
- package/frontend/dist/assets/{LogsPage-CiO6LZEJ.js → LogsPage-BFIpICsM.js} +2 -2
- package/frontend/dist/assets/{LogsPage-CiO6LZEJ.js.map → LogsPage-BFIpICsM.js.map} +1 -1
- package/frontend/dist/assets/{MarketPage-BEdWfj0u.js → MarketPage-Du-NqiEl.js} +2 -2
- package/frontend/dist/assets/{MarketPage-BEdWfj0u.js.map → MarketPage-Du-NqiEl.js.map} +1 -1
- package/frontend/dist/assets/OAuthConsentPage-zqXUvKMF.js +2 -0
- package/frontend/dist/assets/OAuthConsentPage-zqXUvKMF.js.map +1 -0
- package/frontend/dist/assets/{Pagination-DLSuQ8hw.js → Pagination-Dwko3g48.js} +2 -2
- package/frontend/dist/assets/{Pagination-DLSuQ8hw.js.map → Pagination-Dwko3g48.js.map} +1 -1
- package/frontend/dist/assets/{PromptsPage-BG6oWgAk.js → PromptsPage-Cm8ykiI4.js} +2 -2
- package/frontend/dist/assets/{PromptsPage-BG6oWgAk.js.map → PromptsPage-Cm8ykiI4.js.map} +1 -1
- package/frontend/dist/assets/{ResourcesPage-BqJUKqnV.js → ResourcesPage-UMyO6mQ4.js} +2 -2
- package/frontend/dist/assets/{ResourcesPage-BqJUKqnV.js.map → ResourcesPage-UMyO6mQ4.js.map} +1 -1
- package/frontend/dist/assets/{ServersPage-CHbXHEcG.js → ServersPage-B0RoM8ez.js} +8 -8
- package/frontend/dist/assets/ServersPage-B0RoM8ez.js.map +1 -0
- package/frontend/dist/assets/{SettingsPage-B6yl-U9T.js → SettingsPage-BTB-36-n.js} +2 -2
- package/frontend/dist/assets/{SettingsPage-B6yl-U9T.js.map → SettingsPage-BTB-36-n.js.map} +1 -1
- package/frontend/dist/assets/{StatusDot-Bz7sBh0t.js → StatusDot-BAMXeT2x.js} +2 -2
- package/frontend/dist/assets/{StatusDot-Bz7sBh0t.js.map → StatusDot-BAMXeT2x.js.map} +1 -1
- package/frontend/dist/assets/{ToggleGroup-60voS_0U.js → ToggleGroup-Cfgt5lJo.js} +2 -2
- package/frontend/dist/assets/{ToggleGroup-60voS_0U.js.map → ToggleGroup-Cfgt5lJo.js.map} +1 -1
- package/frontend/dist/assets/{UsersPage-Dzzo9CC-.js → UsersPage-DlzYf7Su.js} +2 -2
- package/frontend/dist/assets/{UsersPage-Dzzo9CC-.js.map → UsersPage-DlzYf7Su.js.map} +1 -1
- package/frontend/dist/assets/{contextCost-CIOdvJNu.js → contextCost-kp2vZYp7.js} +2 -2
- package/frontend/dist/assets/{contextCost-CIOdvJNu.js.map → contextCost-kp2vZYp7.js.map} +1 -1
- package/frontend/dist/assets/{framework-vendor-I9Y6AgD5.js → framework-vendor-BHAQX05P.js} +3 -3
- package/frontend/dist/assets/{framework-vendor-I9Y6AgD5.js.map → framework-vendor-BHAQX05P.js.map} +1 -1
- package/frontend/dist/assets/{i18n-vendor-y9bLzIeh.js → i18n-vendor-CMcB7XUR.js} +2 -2
- package/frontend/dist/assets/{i18n-vendor-y9bLzIeh.js.map → i18n-vendor-CMcB7XUR.js.map} +1 -1
- package/frontend/dist/assets/{icons-vendor-hw0ScL8G.js → icons-vendor-HZHHWeTa.js} +65 -60
- package/frontend/dist/assets/icons-vendor-HZHHWeTa.js.map +1 -0
- package/frontend/dist/assets/index-CvXw771N.js +3 -0
- package/frontend/dist/assets/index-CvXw771N.js.map +1 -0
- package/frontend/dist/assets/{resourceService-DIIsi_li.js → resourceService-D2QmlFby.js} +2 -2
- package/frontend/dist/assets/{resourceService-DIIsi_li.js.map → resourceService-D2QmlFby.js.map} +1 -1
- package/frontend/dist/assets/useSettingsData-CeCvZ2nU.js +2 -0
- package/frontend/dist/assets/{useSettingsData-C84qj7ge.js.map → useSettingsData-CeCvZ2nU.js.map} +1 -1
- package/frontend/dist/assets/{variableDetection-Y9ymMI80.js → variableDetection-DKPiHkxr.js} +2 -2
- package/frontend/dist/assets/{variableDetection-Y9ymMI80.js.map → variableDetection-DKPiHkxr.js.map} +1 -1
- package/frontend/dist/index.html +4 -4
- package/package.json +2 -2
- package/frontend/dist/assets/LoginPage-BXRn47pC.js.map +0 -1
- package/frontend/dist/assets/ServersPage-CHbXHEcG.js.map +0 -1
- package/frontend/dist/assets/icons-vendor-hw0ScL8G.js.map +0 -1
- package/frontend/dist/assets/index-Ca9HKIrV.js +0 -3
- package/frontend/dist/assets/index-Ca9HKIrV.js.map +0 -1
- package/frontend/dist/assets/useSettingsData-C84qj7ge.js +0 -2
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { getRepositoryFactory } from '../db/index.js';
|
|
2
|
-
import { getAppDataSource, isDatabaseConnected, initializeDatabase, reconnectDatabase } from '../db/connection.js';
|
|
2
|
+
import { getAppDataSource, isDatabaseConnected, initializeDatabase, reconnectDatabase, } from '../db/connection.js';
|
|
3
3
|
import { getServerDao } from '../dao/index.js';
|
|
4
4
|
import { getSmartRoutingConfig } from '../utils/smartRouting.js';
|
|
5
5
|
import { filterModelVisibleTools } from '../utils/mcpApps.js';
|
|
@@ -7,9 +7,13 @@ import { toFloat32Array } from '../utils/base64.js';
|
|
|
7
7
|
import { truncateToTokenLimit, truncateWithHeuristic, getModelDefaultTokenLimit, } from '../utils/tokenTruncation.js';
|
|
8
8
|
import { safeStringify, summarizeErrorForLogging } from '../utils/serialization.js';
|
|
9
9
|
import logService from './logService.js';
|
|
10
|
-
import {
|
|
10
|
+
import { scryptSync } from 'node:crypto';
|
|
11
11
|
import OpenAI from 'openai';
|
|
12
12
|
import axios from 'axios';
|
|
13
|
+
import { logger } from '../utils/logger.js';
|
|
14
|
+
// Stable salt for the toolset cache key derivation. Not secret material — it
|
|
15
|
+
// only domain-separates the derivation so identical inputs remain deterministic.
|
|
16
|
+
const TOOLSET_HASH_KEY = 'mcphub:toolset-embedding-cache:v1';
|
|
13
17
|
const maskApiKey = (apiKey) => {
|
|
14
18
|
if (!apiKey) {
|
|
15
19
|
return 'missing';
|
|
@@ -223,7 +227,9 @@ const extractRetryAfterMs = (error) => {
|
|
|
223
227
|
return undefined;
|
|
224
228
|
};
|
|
225
229
|
const applyConfiguredBasePacingDelay = (basePacingDelayMs) => {
|
|
226
|
-
const normalizedBaseDelay = typeof basePacingDelayMs === 'number' &&
|
|
230
|
+
const normalizedBaseDelay = typeof basePacingDelayMs === 'number' &&
|
|
231
|
+
!Number.isNaN(basePacingDelayMs) &&
|
|
232
|
+
basePacingDelayMs >= 0
|
|
227
233
|
? Math.floor(basePacingDelayMs)
|
|
228
234
|
: SAFE_BASE_PACING_DELAY_MS;
|
|
229
235
|
const previousBaseDelay = configuredBasePacingDelayMs;
|
|
@@ -249,7 +255,7 @@ const increaseAdaptivePacingDelay = (status) => {
|
|
|
249
255
|
adaptivePacingDelayMs = Math.min(SAFE_MAX_PACING_DELAY_MS, adaptivePacingDelayMs + SAFE_PACING_DELAY_STEP_MS);
|
|
250
256
|
lastPacingIncreaseAt = Date.now();
|
|
251
257
|
if (adaptivePacingDelayMs !== previousDelay) {
|
|
252
|
-
|
|
258
|
+
logger.warn(`[Embedding][Throttle] Increased pacing delay to ${adaptivePacingDelayMs}ms after status=${status ?? 'unknown'}`);
|
|
253
259
|
}
|
|
254
260
|
};
|
|
255
261
|
const executeWithRetry = async (operation, context) => {
|
|
@@ -279,15 +285,15 @@ const executeWithRetry = async (operation, context) => {
|
|
|
279
285
|
if (retryAfterMs !== undefined && retryAfterMs > 0) {
|
|
280
286
|
// Server specified an explicit wait — honor it regardless of accumulated time.
|
|
281
287
|
waitMs = retryAfterMs;
|
|
282
|
-
|
|
288
|
+
logger.warn(`[Embedding][Retry] provider=${context.provider}, model=${context.model || 'unknown'}, baseURL=${context.baseURL || 'default'}, status=${status}, attempt=${attempt}, respecting Retry-After header: ${waitMs}ms`);
|
|
283
289
|
}
|
|
284
290
|
else {
|
|
285
291
|
// No Retry-After header: use 63 second cooldown and apply the 5-minute safety budget.
|
|
286
292
|
waitMs = 63 * 1000;
|
|
287
|
-
|
|
293
|
+
logger.warn(`[Embedding][Retry] provider=${context.provider}, model=${context.model || 'unknown'}, baseURL=${context.baseURL || 'default'}, status=${status}, attempt=${attempt}, applying 63s rate-limit cooldown (no Retry-After header found)`);
|
|
288
294
|
if (totalRetryTimeMs + waitMs > MAX_TOTAL_RETRY_TIME_MS) {
|
|
289
295
|
const remainingMs = MAX_TOTAL_RETRY_TIME_MS - totalRetryTimeMs;
|
|
290
|
-
|
|
296
|
+
logger.warn(`[Embedding][Retry] Max retry time limit reached (no Retry-After). totalTime=${totalRetryTimeMs}ms, nextWait=${waitMs}ms, remaining=${remainingMs}ms, limit=${MAX_TOTAL_RETRY_TIME_MS}ms. Throwing error.`);
|
|
291
297
|
throw error;
|
|
292
298
|
}
|
|
293
299
|
}
|
|
@@ -298,14 +304,14 @@ const executeWithRetry = async (operation, context) => {
|
|
|
298
304
|
// Fixed exponential sequence: 4s, 8s, 16s, 30s, 60s, 120s, 240s — exactly 7 attempts.
|
|
299
305
|
// No time budget imposed; all 7 attempts will be made regardless of total elapsed time.
|
|
300
306
|
if (exponentialAttempt >= RETRY_EXPONENTIAL_SEQUENCE_MS.length) {
|
|
301
|
-
|
|
307
|
+
logger.warn(`[Embedding][Retry] Exhausted all ${RETRY_EXPONENTIAL_SEQUENCE_MS.length} exponential backoff attempts. provider=${context.provider}, model=${context.model || 'unknown'}, status=${status}. Throwing error.`);
|
|
302
308
|
throw error;
|
|
303
309
|
}
|
|
304
310
|
waitMs = withJitter(RETRY_EXPONENTIAL_SEQUENCE_MS[exponentialAttempt]);
|
|
305
|
-
|
|
311
|
+
logger.warn(`[Embedding][Retry] provider=${context.provider}, model=${context.model || 'unknown'}, baseURL=${context.baseURL || 'default'}, status=${status}, attempt=${attempt} (exponential ${exponentialAttempt + 1}/${RETRY_EXPONENTIAL_SEQUENCE_MS.length}), waiting=${waitMs}ms`);
|
|
306
312
|
exponentialAttempt++;
|
|
307
313
|
}
|
|
308
|
-
|
|
314
|
+
logger.log(`[Embedding][Retry] Attempt ${attempt} failed. Waiting ${waitMs}ms before retry...`);
|
|
309
315
|
await sleep(waitMs);
|
|
310
316
|
attempt++;
|
|
311
317
|
}
|
|
@@ -322,7 +328,7 @@ const withEmbeddingQueue = (operation) => {
|
|
|
322
328
|
const pacingDelayMs = getAdaptivePacingDelayMs();
|
|
323
329
|
const waitMs = lastEmbeddingCallAt === 0 ? 0 : Math.max(0, pacingDelayMs - elapsed);
|
|
324
330
|
if (waitMs > 0) {
|
|
325
|
-
|
|
331
|
+
logger.log(`[Embedding][Queue] Pacing wait ${waitMs}ms before next API call`);
|
|
326
332
|
await sleep(waitMs);
|
|
327
333
|
}
|
|
328
334
|
lastEmbeddingCallAt = Date.now();
|
|
@@ -334,11 +340,11 @@ const withEmbeddingQueue = (operation) => {
|
|
|
334
340
|
};
|
|
335
341
|
const scheduleFullEmbeddingResync = (reason) => {
|
|
336
342
|
if (fullResyncScheduled || fullResyncInProgress) {
|
|
337
|
-
|
|
343
|
+
logger.log(`[Embedding] Full embeddings resync already in progress/scheduled. reason=${reason}`);
|
|
338
344
|
return;
|
|
339
345
|
}
|
|
340
346
|
fullResyncScheduled = true;
|
|
341
|
-
|
|
347
|
+
logger.warn(`[Embedding] Scheduling full embeddings resync. reason=${reason}`);
|
|
342
348
|
setTimeout(async () => {
|
|
343
349
|
if (fullResyncInProgress) {
|
|
344
350
|
fullResyncScheduled = false;
|
|
@@ -350,7 +356,7 @@ const scheduleFullEmbeddingResync = (reason) => {
|
|
|
350
356
|
await syncAllServerToolsEmbeddings();
|
|
351
357
|
}
|
|
352
358
|
catch (error) {
|
|
353
|
-
|
|
359
|
+
logger.error('[EMBED_SYNC_ERROR] Full embeddings resync failed', { reason, error });
|
|
354
360
|
}
|
|
355
361
|
finally {
|
|
356
362
|
fullResyncInProgress = false;
|
|
@@ -400,7 +406,7 @@ export async function createVectorIndex(dataSource, dimensions, tableName = 'vec
|
|
|
400
406
|
CREATE INDEX ${indexName}
|
|
401
407
|
ON ${tableName} USING hnsw (${columnName} vector_cosine_ops);
|
|
402
408
|
`);
|
|
403
|
-
|
|
409
|
+
logger.log(`Created HNSW index for ${dimensions}-dimensional vectors.`);
|
|
404
410
|
return {
|
|
405
411
|
success: true,
|
|
406
412
|
indexType: 'hnsw',
|
|
@@ -409,14 +415,14 @@ export async function createVectorIndex(dataSource, dimensions, tableName = 'vec
|
|
|
409
415
|
}
|
|
410
416
|
catch (error) {
|
|
411
417
|
const errorMessage = error instanceof Error ? error.message : 'Unknown error';
|
|
412
|
-
|
|
418
|
+
logger.warn(`HNSW index creation failed: ${errorMessage}`);
|
|
413
419
|
// Fallback to IVFFlat if HNSW fails (e.g., older pgvector version)
|
|
414
420
|
try {
|
|
415
421
|
await dataSource.query(`
|
|
416
422
|
CREATE INDEX ${indexName}
|
|
417
423
|
ON ${tableName} USING ivfflat (${columnName} vector_cosine_ops) WITH (lists = 100);
|
|
418
424
|
`);
|
|
419
|
-
|
|
425
|
+
logger.log(`Created IVFFlat index for ${dimensions}-dimensional vectors (fallback).`);
|
|
420
426
|
return {
|
|
421
427
|
success: true,
|
|
422
428
|
indexType: 'ivfflat',
|
|
@@ -425,7 +431,7 @@ export async function createVectorIndex(dataSource, dimensions, tableName = 'vec
|
|
|
425
431
|
}
|
|
426
432
|
catch (ivfError) {
|
|
427
433
|
const ivfErrorMessage = ivfError instanceof Error ? ivfError.message : 'Unknown error';
|
|
428
|
-
|
|
434
|
+
logger.warn(`IVFFlat index creation also failed: ${ivfErrorMessage}`);
|
|
429
435
|
return {
|
|
430
436
|
success: false,
|
|
431
437
|
indexType: null,
|
|
@@ -442,7 +448,7 @@ export async function createVectorIndex(dataSource, dimensions, tableName = 'vec
|
|
|
442
448
|
CREATE INDEX ${indexName}
|
|
443
449
|
ON ${tableName} USING hnsw ((${columnName}::halfvec(${dimensions})) halfvec_cosine_ops);
|
|
444
450
|
`);
|
|
445
|
-
|
|
451
|
+
logger.log(`Created HNSW index with halfvec casting for ${dimensions}-dimensional vectors.`);
|
|
446
452
|
return {
|
|
447
453
|
success: true,
|
|
448
454
|
indexType: 'hnsw-halfvec',
|
|
@@ -455,28 +461,28 @@ export async function createVectorIndex(dataSource, dimensions, tableName = 'vec
|
|
|
455
461
|
errorMessage.includes('type does not exist') ||
|
|
456
462
|
errorMessage.includes('operator class');
|
|
457
463
|
if (isHalfvecNotSupported) {
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
464
|
+
logger.warn('');
|
|
465
|
+
logger.warn('═══════════════════════════════════════════════════════════════════════════');
|
|
466
|
+
logger.warn(' ⚠️ HIGH-DIMENSIONAL EMBEDDING INDEX WARNING');
|
|
467
|
+
logger.warn('═══════════════════════════════════════════════════════════════════════════');
|
|
468
|
+
logger.warn(` Your embeddings have ${dimensions} dimensions, which requires halfvec support.`);
|
|
469
|
+
logger.warn('');
|
|
470
|
+
logger.warn(' pgvector dimension limits:');
|
|
471
|
+
logger.warn(` - vector type: max ${VECTOR_MAX_DIMENSIONS} dimensions`);
|
|
472
|
+
logger.warn(` - halfvec type: max ${HALFVEC_MAX_DIMENSIONS} dimensions (pgvector 0.7.0+)`);
|
|
473
|
+
logger.warn('');
|
|
474
|
+
logger.warn(' RECOMMENDATIONS:');
|
|
475
|
+
logger.warn(' 1. Upgrade pgvector to >= 0.7.0 for halfvec support');
|
|
476
|
+
logger.warn(' 2. Or use a smaller embedding model:');
|
|
477
|
+
logger.warn(' - text-embedding-3-small (1536 dimensions) instead of text-embedding-3-large');
|
|
478
|
+
logger.warn(' - bge-m3 (1024 dimensions)');
|
|
479
|
+
logger.warn('');
|
|
480
|
+
logger.warn(' Vector search will work but may be slower without an optimized index.');
|
|
481
|
+
logger.warn('═══════════════════════════════════════════════════════════════════════════');
|
|
482
|
+
logger.warn('');
|
|
477
483
|
}
|
|
478
484
|
else {
|
|
479
|
-
|
|
485
|
+
logger.warn(`HNSW halfvec index creation failed: ${errorMessage}`);
|
|
480
486
|
}
|
|
481
487
|
return {
|
|
482
488
|
success: false,
|
|
@@ -486,24 +492,24 @@ export async function createVectorIndex(dataSource, dimensions, tableName = 'vec
|
|
|
486
492
|
}
|
|
487
493
|
}
|
|
488
494
|
// Strategy 3: For dimensions > 4000, no index is supported
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
495
|
+
logger.warn('');
|
|
496
|
+
logger.warn('═══════════════════════════════════════════════════════════════════════════');
|
|
497
|
+
logger.warn(' ⚠️ EMBEDDING DIMENSIONS EXCEED INDEX LIMITS');
|
|
498
|
+
logger.warn('═══════════════════════════════════════════════════════════════════════════');
|
|
499
|
+
logger.warn(` Your embeddings have ${dimensions} dimensions, which exceeds all limits:`);
|
|
500
|
+
logger.warn(` - vector type: max ${VECTOR_MAX_DIMENSIONS} dimensions`);
|
|
501
|
+
logger.warn(` - halfvec type: max ${HALFVEC_MAX_DIMENSIONS} dimensions`);
|
|
502
|
+
logger.warn('');
|
|
503
|
+
logger.warn(' RECOMMENDATIONS:');
|
|
504
|
+
logger.warn(' 1. Use a smaller embedding model:');
|
|
505
|
+
logger.warn(' - text-embedding-3-small (1536 dimensions)');
|
|
506
|
+
logger.warn(' - text-embedding-3-large (3072 dimensions) with halfvec');
|
|
507
|
+
logger.warn(' - bge-m3 (1024 dimensions)');
|
|
508
|
+
logger.warn(' 2. Or use dimensionality reduction (PCA) to reduce vector size');
|
|
509
|
+
logger.warn('');
|
|
510
|
+
logger.warn(' Vector search will work but will be slow without an index.');
|
|
511
|
+
logger.warn('═══════════════════════════════════════════════════════════════════════════');
|
|
512
|
+
logger.warn('');
|
|
507
513
|
return {
|
|
508
514
|
success: false,
|
|
509
515
|
indexType: null,
|
|
@@ -568,7 +574,7 @@ async function generateEmbedding(text) {
|
|
|
568
574
|
if (provider === 'azure_openai') {
|
|
569
575
|
const azureConfig = getAzureOpenAIConfig(smartRoutingConfig);
|
|
570
576
|
if (!azureConfig.endpoint || !azureConfig.apiKey) {
|
|
571
|
-
|
|
577
|
+
logger.warn('Azure OpenAI endpoint/key not configured. Using fallback embedding method.');
|
|
572
578
|
return generateFallbackEmbedding(text);
|
|
573
579
|
}
|
|
574
580
|
try {
|
|
@@ -580,7 +586,7 @@ async function generateEmbedding(text) {
|
|
|
580
586
|
}
|
|
581
587
|
catch (error) {
|
|
582
588
|
const status = extractErrorStatus(error);
|
|
583
|
-
|
|
589
|
+
logger.warn('Azure OpenAI embeddings request failed after retries', {
|
|
584
590
|
status: status ?? 'unknown',
|
|
585
591
|
endpoint: azureConfig.endpoint || 'missing',
|
|
586
592
|
deployment: azureConfig.embeddingDeployment || 'missing',
|
|
@@ -594,7 +600,7 @@ async function generateEmbedding(text) {
|
|
|
594
600
|
const openai = await getOpenAIClient();
|
|
595
601
|
// Check if API key is configured
|
|
596
602
|
if (!openai.apiKey) {
|
|
597
|
-
|
|
603
|
+
logger.warn('OpenAI API key is not configured. Using fallback embedding method.');
|
|
598
604
|
return generateFallbackEmbedding(text);
|
|
599
605
|
}
|
|
600
606
|
// Truncate text to the model's token limit using precise tokenization.
|
|
@@ -610,12 +616,12 @@ async function generateEmbedding(text) {
|
|
|
610
616
|
truncatedText = await truncateToTokenLimit(text, maxTokens, config.embeddingModel, config.apiKey);
|
|
611
617
|
}
|
|
612
618
|
catch (truncationError) {
|
|
613
|
-
|
|
619
|
+
logger.warn(`Token truncation failed for model ${config.embeddingModel}: ${truncationError?.message ?? String(truncationError)}. Falling back to character-based truncation.`);
|
|
614
620
|
// As a fallback, use the shared conservative character-based heuristic (~3 chars/token)
|
|
615
621
|
// to prevent oversized text from causing a failure in the embedding API call.
|
|
616
622
|
truncatedText = truncateWithHeuristic(text, maxTokens);
|
|
617
623
|
}
|
|
618
|
-
|
|
624
|
+
logger.debug(`[Embedding] Truncation: ${text.length} → ${truncatedText.length} chars (${Date.now() - _truncateStart}ms, maxTokens=${maxTokens})`);
|
|
619
625
|
// Determine encoding format based on configuration
|
|
620
626
|
const encodingFormatSetting = smartRoutingConfig.embeddingEncodingFormat || 'auto';
|
|
621
627
|
let encodingFormat;
|
|
@@ -633,7 +639,7 @@ async function generateEmbedding(text) {
|
|
|
633
639
|
};
|
|
634
640
|
if (process.env.DEBUG === 'true') {
|
|
635
641
|
const debugCurl = buildEmbeddingsDebugCurl(config.baseURL, embeddingPayload);
|
|
636
|
-
|
|
642
|
+
logger.log(`[Embedding] HTTP request details: ${JSON.stringify({
|
|
637
643
|
url: `${(config.baseURL || 'https://api.openai.com/v1').replace(/\/+$/, '')}/embeddings`,
|
|
638
644
|
method: 'POST',
|
|
639
645
|
headers: {
|
|
@@ -642,9 +648,9 @@ async function generateEmbedding(text) {
|
|
|
642
648
|
},
|
|
643
649
|
payload: embeddingPayload,
|
|
644
650
|
}, null, 2)}`);
|
|
645
|
-
|
|
651
|
+
logger.log(`[Embedding] Reproducible curl (copy/paste and replace <YOUR_API_KEY>):\n${debugCurl}`);
|
|
646
652
|
}
|
|
647
|
-
|
|
653
|
+
logger.debug(`[Embedding] API request → model=${config.embeddingModel}, encoding_format=${encodingFormat}, input_length=${truncatedText.length} chars | input_preview: "${truncatedText.substring(0, 200).replace(/\s+/g, ' ')}"`);
|
|
648
654
|
const _requestStart = Date.now();
|
|
649
655
|
try {
|
|
650
656
|
// Call OpenAI-compatible embeddings API with conservative retry/backoff policy.
|
|
@@ -657,7 +663,7 @@ async function generateEmbedding(text) {
|
|
|
657
663
|
model: config.embeddingModel,
|
|
658
664
|
baseURL: config.baseURL,
|
|
659
665
|
}));
|
|
660
|
-
|
|
666
|
+
logger.debug(`[Embedding] API response OK in ${Date.now() - _requestStart}ms`);
|
|
661
667
|
if (encodingFormat === 'base64' && typeof response.data[0].embedding === 'string') {
|
|
662
668
|
const embeddingBase64Str = response.data[0].embedding;
|
|
663
669
|
return toFloat32Array(embeddingBase64Str);
|
|
@@ -667,7 +673,7 @@ async function generateEmbedding(text) {
|
|
|
667
673
|
}
|
|
668
674
|
catch (error) {
|
|
669
675
|
const status = extractErrorStatus(error);
|
|
670
|
-
|
|
676
|
+
logger.warn('OpenAI-compatible embeddings request failed after retries', {
|
|
671
677
|
status: status ?? 'unknown',
|
|
672
678
|
baseURL: config.baseURL || 'default',
|
|
673
679
|
model: config.embeddingModel || 'default',
|
|
@@ -801,7 +807,15 @@ const buildToolSetHash = (tools) => {
|
|
|
801
807
|
inputSchema: tool.inputSchema || null,
|
|
802
808
|
}))
|
|
803
809
|
.sort((a, b) => a.name.localeCompare(b.name));
|
|
804
|
-
|
|
810
|
+
// Derive the cache key with a memory-hard KDF (scrypt) rather than a bare
|
|
811
|
+
// digest: inputs may embed configuration-derived content, so a deliberately
|
|
812
|
+
// expensive keyed derivation is the appropriate construction. Parameters
|
|
813
|
+
// are tuned down (~2ms) because this runs on tool-set refreshes only.
|
|
814
|
+
return scryptSync(stableHashSerialize(normalized), TOOLSET_HASH_KEY, 32, {
|
|
815
|
+
N: 2048,
|
|
816
|
+
r: 8,
|
|
817
|
+
p: 1,
|
|
818
|
+
}).toString('hex');
|
|
805
819
|
};
|
|
806
820
|
const buildServerSearchableText = (serverName, description) => [serverName, description || ''].filter(Boolean).join(' ');
|
|
807
821
|
const parseEmbeddingMetadata = (metadata) => {
|
|
@@ -813,7 +827,7 @@ const parseEmbeddingMetadata = (metadata) => {
|
|
|
813
827
|
return JSON.parse(metadata);
|
|
814
828
|
}
|
|
815
829
|
catch (error) {
|
|
816
|
-
|
|
830
|
+
logger.error('Error parsing vector embedding metadata string', safeStringify({ error }));
|
|
817
831
|
return null;
|
|
818
832
|
}
|
|
819
833
|
}
|
|
@@ -865,7 +879,7 @@ export const saveToolsAsVectorEmbeddings = async (serverName, tools, options = {
|
|
|
865
879
|
}
|
|
866
880
|
// Ensure database is initialized before starting
|
|
867
881
|
if (!isDatabaseConnected()) {
|
|
868
|
-
|
|
882
|
+
logger.log('Database not initialized, initializing...');
|
|
869
883
|
await initializeDatabase();
|
|
870
884
|
}
|
|
871
885
|
const config = await getOpenAIConfig();
|
|
@@ -891,7 +905,7 @@ export const saveToolsAsVectorEmbeddings = async (serverName, tools, options = {
|
|
|
891
905
|
const skipCheckRepo = getRepositoryFactory('vectorEmbeddings')();
|
|
892
906
|
const existingCount = await skipCheckRepo.countByServerNameAndModel(serverName, persistedEmbeddingModel);
|
|
893
907
|
if (existingCount !== tools.length) {
|
|
894
|
-
|
|
908
|
+
logger.log(`[Embedding] [${serverName}] Generating embeddings: existing=${existingCount}, expected=${tools.length} (model=${persistedEmbeddingModel}, hash=${expectedToolSetHash.substring(0, 12)})`);
|
|
895
909
|
}
|
|
896
910
|
else {
|
|
897
911
|
const existingIdentities = await skipCheckRepo.getToolIdentityByServerNameAndModel(serverName, persistedEmbeddingModel);
|
|
@@ -909,14 +923,14 @@ export const saveToolsAsVectorEmbeddings = async (serverName, tools, options = {
|
|
|
909
923
|
serverEmbStatus.text_content === serverSearchableText &&
|
|
910
924
|
serverEmbStatus.hasEmbedding === true;
|
|
911
925
|
if (hasExactContentIds && hasMatchingToolSetHash && hasCurrentServerEmbedding) {
|
|
912
|
-
|
|
926
|
+
logger.log(`[Embedding] [${serverName}] Skipping — tool set already up-to-date (model=${persistedEmbeddingModel}, hash=${expectedToolSetHash.substring(0, 12)})`);
|
|
913
927
|
return;
|
|
914
928
|
}
|
|
915
|
-
|
|
929
|
+
logger.log(`[Embedding] [${serverName}] Generating embeddings: existing=${existingCount}, expected=${tools.length} (model=${persistedEmbeddingModel}, hash=${expectedToolSetHash.substring(0, 12)})`);
|
|
916
930
|
}
|
|
917
931
|
}
|
|
918
932
|
catch (skipCheckError) {
|
|
919
|
-
|
|
933
|
+
logger.warn(`[Embedding] [${serverName}] Skip check failed, proceeding with full sync: ${skipCheckError?.message ?? skipCheckError}`);
|
|
920
934
|
}
|
|
921
935
|
}
|
|
922
936
|
// ── Phase 1: Generate all embeddings in memory (no DB access) ──────────────
|
|
@@ -946,7 +960,7 @@ export const saveToolsAsVectorEmbeddings = async (serverName, tools, options = {
|
|
|
946
960
|
]
|
|
947
961
|
.filter(Boolean)
|
|
948
962
|
.join(' ');
|
|
949
|
-
|
|
963
|
+
logger.debug(`[Embedding] [${serverName}] Tool ${_toolIdx + 1}/${tools.length}: "${tool.name}" | raw text: ${searchableText.length} chars | preview: "${searchableText.substring(0, 200).replace(/\s+/g, ' ')}"`);
|
|
950
964
|
try {
|
|
951
965
|
const embedding = await generateEmbedding(searchableText);
|
|
952
966
|
toolEmbeddings.push({ tool, searchableText, embedding });
|
|
@@ -954,7 +968,7 @@ export const saveToolsAsVectorEmbeddings = async (serverName, tools, options = {
|
|
|
954
968
|
}
|
|
955
969
|
catch (error) {
|
|
956
970
|
const status = extractErrorStatus(error);
|
|
957
|
-
|
|
971
|
+
logger.warn('[EMBED_SYNC_ERROR] Failed while embedding tool', {
|
|
958
972
|
serverName,
|
|
959
973
|
toolName: tool.name,
|
|
960
974
|
status: status ?? 'unknown',
|
|
@@ -970,7 +984,7 @@ export const saveToolsAsVectorEmbeddings = async (serverName, tools, options = {
|
|
|
970
984
|
}
|
|
971
985
|
catch (error) {
|
|
972
986
|
const status = extractErrorStatus(error);
|
|
973
|
-
|
|
987
|
+
logger.warn('[EMBED_SYNC_ERROR] Failed while embedding server metadata', {
|
|
974
988
|
serverName,
|
|
975
989
|
status: status ?? 'unknown',
|
|
976
990
|
error: summarizeErrorForLogging(error),
|
|
@@ -982,7 +996,7 @@ export const saveToolsAsVectorEmbeddings = async (serverName, tools, options = {
|
|
|
982
996
|
// Probe the connection before writing; reconnect if it went stale during the
|
|
983
997
|
// (potentially long) embedding-generation phase above.
|
|
984
998
|
if (!isDatabaseConnected()) {
|
|
985
|
-
|
|
999
|
+
logger.info('Database connection lost during embedding generation, reinitializing...');
|
|
986
1000
|
await initializeDatabase();
|
|
987
1001
|
}
|
|
988
1002
|
else {
|
|
@@ -990,7 +1004,7 @@ export const saveToolsAsVectorEmbeddings = async (serverName, tools, options = {
|
|
|
990
1004
|
await getAppDataSource().query('SELECT 1');
|
|
991
1005
|
}
|
|
992
1006
|
catch {
|
|
993
|
-
|
|
1007
|
+
logger.warn('DB connection stale after embedding generation for server %s — reconnecting...', serverName);
|
|
994
1008
|
await reconnectDatabase();
|
|
995
1009
|
}
|
|
996
1010
|
}
|
|
@@ -1016,7 +1030,7 @@ export const saveToolsAsVectorEmbeddings = async (serverName, tools, options = {
|
|
|
1016
1030
|
const currentContentIds = toolEmbeddings.map(({ tool }) => `${serverName}:${tool.name}`);
|
|
1017
1031
|
const staleCount = await vectorRepository.deleteStaleToolEmbeddings(serverName, currentContentIds, persistedEmbeddingModel);
|
|
1018
1032
|
if (staleCount > 0) {
|
|
1019
|
-
|
|
1033
|
+
logger.log(`[Embedding] [${serverName}] Removed ${staleCount} stale tool embedding(s)`);
|
|
1020
1034
|
}
|
|
1021
1035
|
}
|
|
1022
1036
|
await vectorRepository.saveEmbedding('server', serverName, serverSearchableText, serverEmbedding, {
|
|
@@ -1027,13 +1041,13 @@ export const saveToolsAsVectorEmbeddings = async (serverName, tools, options = {
|
|
|
1027
1041
|
if (vectorDimensionsReset) {
|
|
1028
1042
|
scheduleFullEmbeddingResync(`Vector dimensions changed while syncing server "${serverName}"`);
|
|
1029
1043
|
}
|
|
1030
|
-
|
|
1044
|
+
logger.log('Saved tool embeddings', safeStringify({ serverName, toolCount: tools.length }));
|
|
1031
1045
|
}
|
|
1032
1046
|
catch (error) {
|
|
1033
1047
|
if (shouldReport && !progressErrorEmitted) {
|
|
1034
1048
|
emitProgress(lastProgressCurrent, 'error');
|
|
1035
1049
|
}
|
|
1036
|
-
|
|
1050
|
+
logger.error('Error saving tool embeddings', safeStringify({ serverName, error }));
|
|
1037
1051
|
throw error;
|
|
1038
1052
|
}
|
|
1039
1053
|
};
|
|
@@ -1114,7 +1128,7 @@ export const searchToolsByVector = async (query, limit = 10, threshold = 0.7, se
|
|
|
1114
1128
|
.sort((a, b) => b.similarity - a.similarity);
|
|
1115
1129
|
}
|
|
1116
1130
|
catch (error) {
|
|
1117
|
-
|
|
1131
|
+
logger.error('Error searching tools by vector', safeStringify({ query, limit, threshold, error }));
|
|
1118
1132
|
return [];
|
|
1119
1133
|
}
|
|
1120
1134
|
};
|
|
@@ -1148,7 +1162,7 @@ export const getAllVectorizedTools = async (serverNames) => {
|
|
|
1148
1162
|
}
|
|
1149
1163
|
}
|
|
1150
1164
|
catch (error) {
|
|
1151
|
-
|
|
1165
|
+
logger.warn('Could not determine vector dimensions from database', {
|
|
1152
1166
|
error: error?.message,
|
|
1153
1167
|
});
|
|
1154
1168
|
}
|
|
@@ -1186,7 +1200,7 @@ export const getAllVectorizedTools = async (serverNames) => {
|
|
|
1186
1200
|
};
|
|
1187
1201
|
}
|
|
1188
1202
|
catch (error) {
|
|
1189
|
-
|
|
1203
|
+
logger.error('Error parsing vector embedding metadata string', safeStringify({ error }));
|
|
1190
1204
|
return {
|
|
1191
1205
|
serverName: 'unknown',
|
|
1192
1206
|
toolName: 'unknown',
|
|
@@ -1204,7 +1218,7 @@ export const getAllVectorizedTools = async (serverNames) => {
|
|
|
1204
1218
|
});
|
|
1205
1219
|
}
|
|
1206
1220
|
catch (error) {
|
|
1207
|
-
|
|
1221
|
+
logger.error('Error getting all vectorized tools', safeStringify({ error, serverNames }));
|
|
1208
1222
|
return [];
|
|
1209
1223
|
}
|
|
1210
1224
|
};
|
|
@@ -1216,22 +1230,22 @@ export const removeServerToolEmbeddings = async (serverName) => {
|
|
|
1216
1230
|
try {
|
|
1217
1231
|
const smartRoutingConfig = await getSmartRoutingConfig();
|
|
1218
1232
|
if (!smartRoutingConfig.dbUrl && !process.env.DB_URL) {
|
|
1219
|
-
|
|
1233
|
+
logger.warn('Skipping embedding cleanup because DB URL is not configured', {
|
|
1220
1234
|
serverName,
|
|
1221
1235
|
});
|
|
1222
1236
|
return;
|
|
1223
1237
|
}
|
|
1224
1238
|
// Ensure database is initialized before using repository
|
|
1225
1239
|
if (!isDatabaseConnected()) {
|
|
1226
|
-
|
|
1240
|
+
logger.info('Database not initialized, initializing...');
|
|
1227
1241
|
await initializeDatabase();
|
|
1228
1242
|
}
|
|
1229
1243
|
const vectorRepository = getRepositoryFactory('vectorEmbeddings')();
|
|
1230
1244
|
const removedCount = await vectorRepository.deleteByServerName(serverName);
|
|
1231
|
-
|
|
1245
|
+
logger.log('Removed server embeddings', safeStringify({ serverName, removedCount }));
|
|
1232
1246
|
}
|
|
1233
1247
|
catch (error) {
|
|
1234
|
-
|
|
1248
|
+
logger.error('Error removing server embeddings', safeStringify({ serverName, error }));
|
|
1235
1249
|
}
|
|
1236
1250
|
};
|
|
1237
1251
|
/**
|
|
@@ -1240,7 +1254,7 @@ export const removeServerToolEmbeddings = async (serverName) => {
|
|
|
1240
1254
|
*/
|
|
1241
1255
|
export const syncAllServerToolsEmbeddings = async () => {
|
|
1242
1256
|
try {
|
|
1243
|
-
|
|
1257
|
+
logger.log('Starting synchronization of all server tool embeddings');
|
|
1244
1258
|
// Import getServersInfo to get all server information
|
|
1245
1259
|
const { getServersInfo } = await import('./mcpService.js');
|
|
1246
1260
|
const servers = await getServersInfo();
|
|
@@ -1249,7 +1263,7 @@ export const syncAllServerToolsEmbeddings = async () => {
|
|
|
1249
1263
|
for (const server of servers) {
|
|
1250
1264
|
if (server.status === 'connected' && server.tools && server.tools.length > 0) {
|
|
1251
1265
|
try {
|
|
1252
|
-
|
|
1266
|
+
logger.log('Syncing tool embeddings for server', {
|
|
1253
1267
|
serverName: server.name,
|
|
1254
1268
|
toolCount: server.tools.length,
|
|
1255
1269
|
});
|
|
@@ -1258,30 +1272,30 @@ export const syncAllServerToolsEmbeddings = async () => {
|
|
|
1258
1272
|
serversSynced++;
|
|
1259
1273
|
}
|
|
1260
1274
|
catch (error) {
|
|
1261
|
-
|
|
1262
|
-
|
|
1275
|
+
logger.warn(`[EMBED_SYNC_ERROR] Failed to sync tool embeddings for server "${server.name}"`);
|
|
1276
|
+
logger.error('Failed to sync tool embeddings for server', safeStringify({
|
|
1263
1277
|
serverName: server.name,
|
|
1264
1278
|
error,
|
|
1265
1279
|
}));
|
|
1266
1280
|
}
|
|
1267
1281
|
}
|
|
1268
1282
|
else if (server.status === 'connected' && (!server.tools || server.tools.length === 0)) {
|
|
1269
|
-
|
|
1283
|
+
logger.log('Connected server has no tools to sync', safeStringify({ serverName: server.name }));
|
|
1270
1284
|
}
|
|
1271
1285
|
else {
|
|
1272
|
-
|
|
1286
|
+
logger.log('Skipping server during tool embedding sync', {
|
|
1273
1287
|
serverName: server.name,
|
|
1274
1288
|
status: server.status,
|
|
1275
1289
|
});
|
|
1276
1290
|
}
|
|
1277
1291
|
}
|
|
1278
|
-
|
|
1292
|
+
logger.log('Completed smart routing tool embedding sync', {
|
|
1279
1293
|
totalToolsSynced,
|
|
1280
1294
|
serversSynced,
|
|
1281
1295
|
});
|
|
1282
1296
|
}
|
|
1283
1297
|
catch (error) {
|
|
1284
|
-
|
|
1298
|
+
logger.error('Error during smart routing tool embedding synchronization', { error });
|
|
1285
1299
|
throw error;
|
|
1286
1300
|
}
|
|
1287
1301
|
};
|
|
@@ -1294,7 +1308,7 @@ async function checkDatabaseVectorDimensions(dimensionsNeeded) {
|
|
|
1294
1308
|
try {
|
|
1295
1309
|
// First check if database is initialized
|
|
1296
1310
|
if (!getAppDataSource().isInitialized) {
|
|
1297
|
-
|
|
1311
|
+
logger.info('Database not initialized, initializing...');
|
|
1298
1312
|
await initializeDatabase();
|
|
1299
1313
|
}
|
|
1300
1314
|
// Check current vector dimension in the database
|
|
@@ -1311,7 +1325,7 @@ async function checkDatabaseVectorDimensions(dimensionsNeeded) {
|
|
|
1311
1325
|
`);
|
|
1312
1326
|
}
|
|
1313
1327
|
catch (error) {
|
|
1314
|
-
|
|
1328
|
+
logger.warn('Could not get vector type info, falling back to atttypmod query');
|
|
1315
1329
|
}
|
|
1316
1330
|
// Fallback to original query
|
|
1317
1331
|
const result = await getAppDataSource().query(`
|
|
@@ -1360,19 +1374,19 @@ async function checkDatabaseVectorDimensions(dimensionsNeeded) {
|
|
|
1360
1374
|
}
|
|
1361
1375
|
}
|
|
1362
1376
|
catch (error) {
|
|
1363
|
-
|
|
1377
|
+
logger.warn('Could not check vector dimensions from actual records', { error });
|
|
1364
1378
|
}
|
|
1365
1379
|
// If no dimensions are set or they don't match what we need, handle the mismatch
|
|
1366
1380
|
if (currentDimensions === 0 || currentDimensions !== dimensionsNeeded) {
|
|
1367
|
-
|
|
1381
|
+
logger.log('Vector dimensions mismatch detected', {
|
|
1368
1382
|
currentDimensions,
|
|
1369
1383
|
dimensionsNeeded,
|
|
1370
1384
|
});
|
|
1371
1385
|
if (currentDimensions === 0) {
|
|
1372
|
-
|
|
1386
|
+
logger.log('Setting up vector dimensions for the first time...');
|
|
1373
1387
|
}
|
|
1374
1388
|
else {
|
|
1375
|
-
|
|
1389
|
+
logger.log('Dimension mismatch detected. Clearing existing incompatible vector data...');
|
|
1376
1390
|
// Clear all existing vector embeddings with mismatched dimensions
|
|
1377
1391
|
await clearMismatchedVectorData(dimensionsNeeded);
|
|
1378
1392
|
}
|
|
@@ -1387,14 +1401,14 @@ async function checkDatabaseVectorDimensions(dimensionsNeeded) {
|
|
|
1387
1401
|
await getAppDataSource().query(`DROP INDEX IF EXISTS idx_vector_embeddings_embedding;`);
|
|
1388
1402
|
}
|
|
1389
1403
|
catch (dropError) {
|
|
1390
|
-
|
|
1404
|
+
logger.warn('Could not drop existing vector index before ALTER', {
|
|
1391
1405
|
error: dropError?.message,
|
|
1392
1406
|
});
|
|
1393
1407
|
}
|
|
1394
1408
|
// Alter the column type with the new dimensions
|
|
1395
1409
|
// Use halfvec for dimensions > 2000, vector otherwise
|
|
1396
1410
|
const vectorType = dimensionsNeeded <= VECTOR_MAX_DIMENSIONS ? 'vector' : 'halfvec';
|
|
1397
|
-
|
|
1411
|
+
logger.log('Using vector storage type for configured dimensions', {
|
|
1398
1412
|
vectorType,
|
|
1399
1413
|
dimensionsNeeded,
|
|
1400
1414
|
});
|
|
@@ -1405,9 +1419,9 @@ async function checkDatabaseVectorDimensions(dimensionsNeeded) {
|
|
|
1405
1419
|
// Create appropriate vector index using the helper function
|
|
1406
1420
|
const result = await createVectorIndex(getAppDataSource(), dimensionsNeeded);
|
|
1407
1421
|
if (!result.success) {
|
|
1408
|
-
|
|
1422
|
+
logger.log('Continuing without optimized vector index...');
|
|
1409
1423
|
}
|
|
1410
|
-
|
|
1424
|
+
logger.log('Successfully configured vector dimensions', { dimensionsNeeded });
|
|
1411
1425
|
// Only signal that a full resync is needed when existing data was cleared due
|
|
1412
1426
|
// to a real dimension change. When currentDimensions === 0 it is a first-time
|
|
1413
1427
|
// setup — there are no stale embeddings to re-generate.
|
|
@@ -1416,7 +1430,7 @@ async function checkDatabaseVectorDimensions(dimensionsNeeded) {
|
|
|
1416
1430
|
return false;
|
|
1417
1431
|
}
|
|
1418
1432
|
catch (error) {
|
|
1419
|
-
|
|
1433
|
+
logger.error('Error checking or updating vector dimensions', { error });
|
|
1420
1434
|
throw new Error(`Vector dimension check failed: ${error?.message || 'Unknown error'}`);
|
|
1421
1435
|
}
|
|
1422
1436
|
}
|
|
@@ -1427,16 +1441,16 @@ async function checkDatabaseVectorDimensions(dimensionsNeeded) {
|
|
|
1427
1441
|
*/
|
|
1428
1442
|
async function clearMismatchedVectorData(expectedDimensions) {
|
|
1429
1443
|
try {
|
|
1430
|
-
|
|
1444
|
+
logger.log(`Clearing vector embeddings with dimensions different from ${expectedDimensions}...`);
|
|
1431
1445
|
// Delete all embeddings that don't match the expected dimensions
|
|
1432
1446
|
await getAppDataSource().query(`
|
|
1433
1447
|
DELETE FROM vector_embeddings
|
|
1434
1448
|
WHERE dimensions != $1
|
|
1435
1449
|
`, [expectedDimensions]);
|
|
1436
|
-
|
|
1450
|
+
logger.log('Successfully cleared mismatched vector embeddings');
|
|
1437
1451
|
}
|
|
1438
1452
|
catch (error) {
|
|
1439
|
-
|
|
1453
|
+
logger.error('Error clearing mismatched vector data:', error);
|
|
1440
1454
|
throw error;
|
|
1441
1455
|
}
|
|
1442
1456
|
}
|