noteconnection 1.7.0 → 1.8.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.
- package/LICENSE +674 -21
- package/README.md +242 -62
- package/dist/src/agent_workspace.contract.parity.test.js +475 -0
- package/dist/src/agent_workspace.frontend.test.js +9989 -0
- package/dist/src/agent_workspace.locale.contract.test.js +95 -0
- package/dist/src/agent_workspace.runtime.behavior.test.js +5072 -0
- package/dist/src/copy.assets.contract.test.js +130 -0
- package/dist/src/core/PathBridge.js +28 -3
- package/dist/src/export/WorkspaceExportBundle.js +1130 -0
- package/dist/src/export/WorkspaceExportBundle.test.js +1480 -0
- package/dist/src/export/index.js +18 -0
- package/dist/src/export/types.js +2 -0
- package/dist/src/fixrisk.issue.verifier.contract.test.js +3 -1
- package/dist/src/foundation.ann.runtime.contract.test.js +81 -0
- package/dist/src/foundation.release.evidence.contract.test.js +406 -0
- package/dist/src/foundation.sqlite.runtime.contract.test.js +82 -0
- package/dist/src/frontend/README.md +242 -62
- package/dist/src/frontend/agent_workspace.js +5353 -0
- package/dist/src/frontend/agent_workspace_runtime.js +4434 -0
- package/dist/src/frontend/analysis.js +31 -4
- package/dist/src/frontend/app.js +2178 -62
- package/dist/src/frontend/focus_mode_interactions.js +33 -0
- package/dist/src/frontend/godot_future_path_renderer.js +540 -0
- package/dist/src/frontend/godot_tree_interactions.js +178 -0
- package/dist/src/frontend/graph_state.mjs +105 -0
- package/dist/src/frontend/hosted_future_path_runtime.js +157 -0
- package/dist/src/frontend/i18n.mjs +186 -0
- package/dist/src/frontend/index.html +641 -266
- package/dist/src/frontend/layout_gpu.js +12 -4
- package/dist/src/frontend/locales/en.json +721 -5
- package/dist/src/frontend/locales/zh.json +721 -5
- package/dist/src/frontend/main.mjs +60 -0
- package/dist/src/frontend/markdown_runtime.js +827 -0
- package/dist/src/frontend/notemd.css +49 -0
- package/dist/src/frontend/notemd.html +7 -1
- package/dist/src/frontend/notemd.js +64 -0
- package/dist/src/frontend/path.html +107 -0
- package/dist/src/frontend/path_app.js +2108 -130
- package/dist/src/frontend/path_layout.mjs +143 -0
- package/dist/src/frontend/path_mermaid_utils.mjs +108 -0
- package/dist/src/frontend/path_modules_bridge.js +486 -0
- package/dist/src/frontend/path_state.mjs +118 -0
- package/dist/src/frontend/path_styles.css +146 -0
- package/dist/src/frontend/path_worker_bridge.mjs +85 -0
- package/dist/src/frontend/reader.js +393 -33
- package/dist/src/frontend/runtime_bridge.js +67 -54
- package/dist/src/frontend/runtime_bridge.mjs +279 -0
- package/dist/src/frontend/settings.js +130 -12
- package/dist/src/frontend/source_manager.js +190 -21
- package/dist/src/frontend/styles.css +2720 -72
- package/dist/src/frontend/workbench_state.mjs +101 -0
- package/dist/src/frontend/workspace_panes.js +10168 -0
- package/dist/src/frontend.locale.contract.test.js +62 -0
- package/dist/src/godot.sidecar.bootstrap.contract.test.js +244 -0
- package/dist/src/indexing/IndexLifecycle.js +195 -0
- package/dist/src/indexing/IndexLifecycle.test.js +49 -0
- package/dist/src/indexing/SegmentBuilder.js +64 -0
- package/dist/src/indexing/UnitBuilder.js +48 -0
- package/dist/src/indexing/types.js +2 -0
- package/dist/src/knowledge.api.contract.test.js +170 -0
- package/dist/src/learning/KnowledgeLearningPlatform.js +10386 -0
- package/dist/src/learning/KnowledgeLearningPlatform.persistence.test.js +327 -0
- package/dist/src/learning/KnowledgeLearningPlatform.program-f.test.js +99 -0
- package/dist/src/learning/KnowledgeLearningPlatform.test.js +2971 -0
- package/dist/src/learning/KnowledgeWorkspaceConversationRegression.js +2974 -0
- package/dist/src/learning/KnowledgeWorkspaceConversationRegression.test.js +3928 -0
- package/dist/src/learning/answerReleaseReview.js +4319 -0
- package/dist/src/learning/answerReleaseReview.test.js +2888 -0
- package/dist/src/learning/api.js +2 -0
- package/dist/src/learning/conversationComposer.js +1480 -0
- package/dist/src/learning/conversationComposer.test.js +1817 -0
- package/dist/src/learning/domains/ConversationManager.js +53 -0
- package/dist/src/learning/domains/KnowledgeIngestor.js +238 -0
- package/dist/src/learning/domains/KnowledgeQuerier.js +187 -0
- package/dist/src/learning/domains/MasteryEngine.js +387 -0
- package/dist/src/learning/domains/MemoryPolicyManager.js +408 -0
- package/dist/src/learning/domains/QualityEvaluator.js +307 -0
- package/dist/src/learning/domains/TutorRouter.js +313 -0
- package/dist/src/learning/domains/index.js +33 -0
- package/dist/src/learning/domains/types.js +7 -0
- package/dist/src/learning/errors.js +29 -0
- package/dist/src/learning/evidenceContextAssembler.js +1176 -0
- package/dist/src/learning/evidenceContextAssembler.test.js +6332 -0
- package/dist/src/learning/graphContextAssembler.js +870 -0
- package/dist/src/learning/graphContextAssembler.test.js +1033 -0
- package/dist/src/learning/index.js +28 -0
- package/dist/src/learning/queryBackend.js +1898 -0
- package/dist/src/learning/queryBackend.test.js +955 -0
- package/dist/src/learning/ragContextPack.js +257 -0
- package/dist/src/learning/ragContextPack.test.js +160 -0
- package/dist/src/learning/ragPublicText.js +38 -0
- package/dist/src/learning/ragSufficiencyJudge.js +161 -0
- package/dist/src/learning/ragSufficiencyJudge.test.js +177 -0
- package/dist/src/learning/ragSufficiencyProviderJudge.js +227 -0
- package/dist/src/learning/ragSufficiencyProviderJudge.test.js +156 -0
- package/dist/src/learning/requestNormalization.js +198 -0
- package/dist/src/learning/runtimeCapability.js +4677 -0
- package/dist/src/learning/runtimeCapability.test.js +3635 -0
- package/dist/src/learning/store.js +1240 -0
- package/dist/src/learning/store.test.js +1126 -0
- package/dist/src/learning/tutorAdapter.js +2 -0
- package/dist/src/learning/types.js +2 -0
- package/dist/src/learning/vectorAccelerationAdapter.js +942 -0
- package/dist/src/learning/vectorAccelerationAdapter.test.js +382 -0
- package/dist/src/lfs.asset.policy.contract.test.js +153 -0
- package/dist/src/license.policy.contract.test.js +66 -0
- package/dist/src/memory/MemoryGovernance.js +74 -0
- package/dist/src/memory/MemoryGovernance.test.js +46 -0
- package/dist/src/memory/types.js +2 -0
- package/dist/src/mermaid.frontend.guard.contract.test.js +77 -0
- package/dist/src/middleware/auth.js +17 -0
- package/dist/src/middleware/body-parser.js +45 -0
- package/dist/src/middleware/cors.js +44 -0
- package/dist/src/middleware/index.js +21 -0
- package/dist/src/middleware/request-trace.js +96 -0
- package/dist/src/notemd/AppConfigToml.js +6 -4
- package/dist/src/notemd/MermaidProcessor.js +400 -50
- package/dist/src/notemd/NotemdService.js +498 -13
- package/dist/src/notemd/PromptManager.js +15 -0
- package/dist/src/notemd/cli/commands.js +357 -0
- package/dist/src/notemd/cli/dispatcher.js +225 -0
- package/dist/src/notemd/cli/index.js +169 -0
- package/dist/src/notemd/cli/parser.js +68 -0
- package/dist/src/notemd/cli/types.js +2 -0
- package/dist/src/notemd/constants.js +43 -0
- package/dist/src/notemd/diagram/diagramGenerationService.js +78 -0
- package/dist/src/notemd/diagram/diagramSpec.js +79 -0
- package/dist/src/notemd/diagram/diagramSpecResponseParser.js +131 -0
- package/dist/src/notemd/diagram/intent.js +95 -0
- package/dist/src/notemd/diagram/planner.js +71 -0
- package/dist/src/notemd/diagram/prompts/diagramSpecPrompt.js +42 -0
- package/dist/src/notemd/diagram/types.js +18 -0
- package/dist/src/notemd/index.js +26 -0
- package/dist/src/notemd/operations/capabilityManifest.js +23 -0
- package/dist/src/notemd/operations/cliContracts.js +17 -0
- package/dist/src/notemd/operations/configProfileCommands.js +85 -0
- package/dist/src/notemd/operations/registry.contract.test.js +95 -0
- package/dist/src/notemd/operations/registry.js +991 -0
- package/dist/src/notemd/operations/types.js +2 -0
- package/dist/src/notemd/providerDiagnostics.js +220 -0
- package/dist/src/notemd/providerProfiles.js +42 -0
- package/dist/src/notemd/providerTemplates.js +231 -0
- package/dist/src/notemd/search/DuckDuckGoProvider.js +39 -0
- package/dist/src/notemd/search/SearchManager.js +13 -0
- package/dist/src/notemd/search/SearchProvider.js +2 -0
- package/dist/src/notemd/search/TavilyProvider.js +44 -0
- package/dist/src/notemd.agent.manifest.test.js +85 -0
- package/dist/src/notemd.api.contract.test.js +14 -1
- package/dist/src/notemd.app_config_toml.test.js +2 -1
- package/dist/src/notemd.batch.workflow.test.js +117 -0
- package/dist/src/notemd.cli.e2e.test.js +136 -0
- package/dist/src/notemd.core.test.js +51 -0
- package/dist/src/notemd.diagram.pipeline.test.js +233 -0
- package/dist/src/notemd.providerTemplates.test.js +34 -0
- package/dist/src/notemd.server.integration.test.js +143 -35
- package/dist/src/notemd.workflow.pipeline.test.js +162 -0
- package/dist/src/pathbridge.handshake.contract.test.js +16 -2
- package/dist/src/pathmode.background.contract.test.js +69 -0
- package/dist/src/pathmode.settings.api.contract.test.js +9 -0
- package/dist/src/pkg.sidecar.contract.test.js +9 -3
- package/dist/src/platform/ExportProfile.js +58 -0
- package/dist/src/platform/PlatformCapabilities.js +45 -0
- package/dist/src/platform/PlatformCapabilities.test.js +30 -0
- package/dist/src/platform/RenderMaterializer.js +33 -0
- package/dist/src/platform/RenderMaterializer.test.js +32 -0
- package/dist/src/query_backend.external_http.integration.test.js +410 -0
- package/dist/src/reader_renderer.js +404 -3
- package/dist/src/reader_renderer.test.js +87 -0
- package/dist/src/release.godot.mirror.contract.test.js +73 -0
- package/dist/src/resources/ResourceRegistry.js +223 -0
- package/dist/src/resources/ResourceRegistry.test.js +61 -0
- package/dist/src/resources/types.js +2 -0
- package/dist/src/routes/agentWorkspaceDiagnostics.js +173 -0
- package/dist/src/routes/data.js +267 -0
- package/dist/src/routes/diagnostics.js +51 -0
- package/dist/src/routes/index.js +23 -0
- package/dist/src/routes/knowledge.js +968 -0
- package/dist/src/routes/markdown.js +287 -0
- package/dist/src/routes/notemd.js +565 -0
- package/dist/src/routes/registry.contract.test.js +130 -0
- package/dist/src/routes/render.js +285 -0
- package/dist/src/routes/runtimeRunbookRouteOps.js +149 -0
- package/dist/src/routes/runtimeRunbookRouteOps.test.js +194 -0
- package/dist/src/routes/settings.js +6 -0
- package/dist/src/routes/staticFiles.js +94 -0
- package/dist/src/routes/types.js +2 -0
- package/dist/src/runtime.transport.adapter.contract.test.js +81 -0
- package/dist/src/server.js +11090 -1549
- package/dist/src/server.migration.test.js +193 -21
- package/dist/src/server.port.fallback.contract.test.js +63 -0
- package/dist/src/session/SessionStateStore.js +81 -0
- package/dist/src/session/SessionStateStore.test.js +58 -0
- package/dist/src/session/types.js +2 -0
- package/dist/src/settings.runtime.contract.test.js +50 -0
- package/dist/src/shared/types.contract.test.js +107 -0
- package/dist/src/shared/types.js +22 -0
- package/dist/src/sidecar.replacement.boundary.contract.test.js +128 -0
- package/dist/src/sidecar.supply.readiness.contract.test.js +144 -0
- package/dist/src/source_manager.loadflow.test.js +46 -0
- package/dist/src/startup.layout.snapshot.contract.test.js +57 -0
- package/dist/src/tauri.frontend.build.contract.test.js +60 -0
- package/dist/src/tauri.sidecar.cleanup.contract.test.js +21 -0
- package/dist/src/utils/RuntimePaths.js +4 -13
- package/dist/src/utils/platform.js +153 -0
- package/dist/src/workflows/WorkflowArtifactStore.js +96 -0
- package/dist/src/workflows/WorkflowArtifactStore.test.js +80 -0
- package/dist/src/workflows/types.js +2 -0
- package/dist/src/workspace/WorkspaceRegistry.js +122 -0
- package/dist/src/workspace/WorkspaceRegistry.test.js +29 -0
- package/dist/src/workspace/types.js +2 -0
- package/package.json +53 -10
|
@@ -0,0 +1,942 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.normalizeVectorAccelerationAdapterProvider = normalizeVectorAccelerationAdapterProvider;
|
|
4
|
+
exports.createVectorAccelerationAdapter = createVectorAccelerationAdapter;
|
|
5
|
+
const EXTERNAL_STUB_ADAPTER_ID = 'external-stub-vector-acceleration-v1';
|
|
6
|
+
const EXTERNAL_HTTP_ADAPTER_ID = 'external-http-vector-acceleration-v1';
|
|
7
|
+
const EXTERNAL_STUB_TOP_TOKENS = 6;
|
|
8
|
+
const EXTERNAL_STUB_MIN_ATOMS = 48;
|
|
9
|
+
const EXTERNAL_STUB_CANDIDATE_MULTIPLIER = 20;
|
|
10
|
+
const EXTERNAL_STUB_CANDIDATE_MIN_FLOOR = 32;
|
|
11
|
+
const EXTERNAL_HTTP_TIMEOUT_MS_DEFAULT = 1200;
|
|
12
|
+
const EXTERNAL_HTTP_TIMEOUT_MS_MIN = 100;
|
|
13
|
+
const EXTERNAL_HTTP_TIMEOUT_MS_MAX = 10000;
|
|
14
|
+
const EXTERNAL_HTTP_MAX_RETRIES_DEFAULT = 1;
|
|
15
|
+
const EXTERNAL_HTTP_MAX_RETRIES_MIN = 0;
|
|
16
|
+
const EXTERNAL_HTTP_MAX_RETRIES_MAX = 5;
|
|
17
|
+
const EXTERNAL_HTTP_RETRY_DELAY_MS_DEFAULT = 120;
|
|
18
|
+
const EXTERNAL_HTTP_RETRY_DELAY_MS_MIN = 0;
|
|
19
|
+
const EXTERNAL_HTTP_RETRY_DELAY_MS_MAX = 2000;
|
|
20
|
+
const EXTERNAL_HTTP_RETRY_AFTER_MS_MAX = 60000;
|
|
21
|
+
const EXTERNAL_HTTP_RETRYABLE_STATUSES = new Set([408, 425, 429, 500, 502, 503, 504]);
|
|
22
|
+
const EXTERNAL_HTTP_CIRCUIT_FAILURE_THRESHOLD_DEFAULT = 3;
|
|
23
|
+
const EXTERNAL_HTTP_CIRCUIT_FAILURE_THRESHOLD_MIN = 1;
|
|
24
|
+
const EXTERNAL_HTTP_CIRCUIT_FAILURE_THRESHOLD_MAX = 20;
|
|
25
|
+
const EXTERNAL_HTTP_CIRCUIT_COOLDOWN_MS_DEFAULT = 8000;
|
|
26
|
+
const EXTERNAL_HTTP_CIRCUIT_COOLDOWN_MS_MIN = 100;
|
|
27
|
+
const EXTERNAL_HTTP_CIRCUIT_COOLDOWN_MS_MAX = 600000;
|
|
28
|
+
const EXTERNAL_HTTP_CANDIDATE_MULTIPLIER = 32;
|
|
29
|
+
const EXTERNAL_HTTP_CANDIDATE_MIN_FLOOR = 64;
|
|
30
|
+
const EXTERNAL_HTTP_CANDIDATE_MAX_CAP = 4096;
|
|
31
|
+
const EXTERNAL_STUB_REPRESENTATION_VERSION = 'external-stub-vector-acceleration-v1';
|
|
32
|
+
const EXTERNAL_STUB_EMBEDDING_MODEL_ID = 'external-stub-token-prefilter-v1';
|
|
33
|
+
const EXTERNAL_HTTP_REQUEST_ID_PREFIX = 'nc-vector-accel';
|
|
34
|
+
let EXTERNAL_HTTP_REQUEST_ID_SEQUENCE = 0;
|
|
35
|
+
function uniqueTokens(tokens) {
|
|
36
|
+
const seen = new Set();
|
|
37
|
+
const unique = [];
|
|
38
|
+
tokens.forEach((token) => {
|
|
39
|
+
const normalized = String(token || '').trim().toLowerCase();
|
|
40
|
+
if (!normalized || seen.has(normalized)) {
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
seen.add(normalized);
|
|
44
|
+
unique.push(normalized);
|
|
45
|
+
});
|
|
46
|
+
return unique;
|
|
47
|
+
}
|
|
48
|
+
function buildKnownAtomIdSet(input) {
|
|
49
|
+
const knownAtomIds = new Set();
|
|
50
|
+
for (const atomIds of input.tokenToAtomIds.values()) {
|
|
51
|
+
for (const atomId of atomIds) {
|
|
52
|
+
const normalized = String(atomId || '').trim();
|
|
53
|
+
if (normalized)
|
|
54
|
+
knownAtomIds.add(normalized);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
for (const atomIds of input.signatureBuckets.values()) {
|
|
58
|
+
for (const atomId of atomIds) {
|
|
59
|
+
const normalized = String(atomId || '').trim();
|
|
60
|
+
if (normalized)
|
|
61
|
+
knownAtomIds.add(normalized);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
return knownAtomIds;
|
|
65
|
+
}
|
|
66
|
+
function normalizeExternalHttpCandidateIds(rawCandidateAtomIds, input) {
|
|
67
|
+
const knownAtomIds = buildKnownAtomIdSet(input);
|
|
68
|
+
const totalAtomsInScope = Math.max(1, knownAtomIds.size > 0 ? knownAtomIds.size : Math.max(0, Math.floor(Number(input.atomCount || 0))));
|
|
69
|
+
const rawCandidateIds = Array.isArray(rawCandidateAtomIds)
|
|
70
|
+
? rawCandidateAtomIds
|
|
71
|
+
.map((item) => String(item || '').trim())
|
|
72
|
+
.filter(Boolean)
|
|
73
|
+
: [];
|
|
74
|
+
const seen = new Set();
|
|
75
|
+
const normalizedCandidates = [];
|
|
76
|
+
for (const candidateId of rawCandidateIds) {
|
|
77
|
+
if (knownAtomIds.size > 0 && !knownAtomIds.has(candidateId)) {
|
|
78
|
+
continue;
|
|
79
|
+
}
|
|
80
|
+
if (seen.has(candidateId)) {
|
|
81
|
+
continue;
|
|
82
|
+
}
|
|
83
|
+
seen.add(candidateId);
|
|
84
|
+
normalizedCandidates.push(candidateId);
|
|
85
|
+
}
|
|
86
|
+
const maxCandidateCount = Math.max(EXTERNAL_HTTP_CANDIDATE_MIN_FLOOR, Math.min(EXTERNAL_HTTP_CANDIDATE_MAX_CAP, Math.max(1, Math.floor(Number(input.topK || 0))) * EXTERNAL_HTTP_CANDIDATE_MULTIPLIER));
|
|
87
|
+
const candidateAtomIds = normalizedCandidates.slice(0, maxCandidateCount);
|
|
88
|
+
const rejectedCount = Math.max(0, rawCandidateIds.length - candidateAtomIds.length);
|
|
89
|
+
const prefilterRatio = candidateAtomIds.length / totalAtomsInScope;
|
|
90
|
+
return {
|
|
91
|
+
candidateAtomIds,
|
|
92
|
+
rejectedCount,
|
|
93
|
+
totalAtomsInScope,
|
|
94
|
+
prefilterRatio,
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
function normalizeVectorAccelerationAdapterProvider(rawValue) {
|
|
98
|
+
const normalized = String(rawValue || '').trim().toLowerCase();
|
|
99
|
+
if (normalized === 'external_stub' || normalized === 'external-stub' || normalized === 'stub') {
|
|
100
|
+
return 'external_stub';
|
|
101
|
+
}
|
|
102
|
+
if (normalized === 'external_http' || normalized === 'external-http' || normalized === 'http') {
|
|
103
|
+
return 'external_http';
|
|
104
|
+
}
|
|
105
|
+
return 'local';
|
|
106
|
+
}
|
|
107
|
+
function normalizeEndpoint(rawValue) {
|
|
108
|
+
return String(rawValue || '').trim().replace(/\/+$/g, '');
|
|
109
|
+
}
|
|
110
|
+
function normalizeTimeoutMs(rawValue) {
|
|
111
|
+
const numeric = Number(rawValue);
|
|
112
|
+
if (!Number.isFinite(numeric)) {
|
|
113
|
+
return EXTERNAL_HTTP_TIMEOUT_MS_DEFAULT;
|
|
114
|
+
}
|
|
115
|
+
const floored = Math.floor(numeric);
|
|
116
|
+
if (floored < EXTERNAL_HTTP_TIMEOUT_MS_MIN) {
|
|
117
|
+
return EXTERNAL_HTTP_TIMEOUT_MS_MIN;
|
|
118
|
+
}
|
|
119
|
+
if (floored > EXTERNAL_HTTP_TIMEOUT_MS_MAX) {
|
|
120
|
+
return EXTERNAL_HTTP_TIMEOUT_MS_MAX;
|
|
121
|
+
}
|
|
122
|
+
return floored;
|
|
123
|
+
}
|
|
124
|
+
function normalizeMaxRetries(rawValue) {
|
|
125
|
+
const numeric = Number(rawValue);
|
|
126
|
+
if (!Number.isFinite(numeric)) {
|
|
127
|
+
return EXTERNAL_HTTP_MAX_RETRIES_DEFAULT;
|
|
128
|
+
}
|
|
129
|
+
const floored = Math.floor(numeric);
|
|
130
|
+
if (floored < EXTERNAL_HTTP_MAX_RETRIES_MIN) {
|
|
131
|
+
return EXTERNAL_HTTP_MAX_RETRIES_MIN;
|
|
132
|
+
}
|
|
133
|
+
if (floored > EXTERNAL_HTTP_MAX_RETRIES_MAX) {
|
|
134
|
+
return EXTERNAL_HTTP_MAX_RETRIES_MAX;
|
|
135
|
+
}
|
|
136
|
+
return floored;
|
|
137
|
+
}
|
|
138
|
+
function normalizeRetryDelayMs(rawValue) {
|
|
139
|
+
const numeric = Number(rawValue);
|
|
140
|
+
if (!Number.isFinite(numeric)) {
|
|
141
|
+
return EXTERNAL_HTTP_RETRY_DELAY_MS_DEFAULT;
|
|
142
|
+
}
|
|
143
|
+
const floored = Math.floor(numeric);
|
|
144
|
+
if (floored < EXTERNAL_HTTP_RETRY_DELAY_MS_MIN) {
|
|
145
|
+
return EXTERNAL_HTTP_RETRY_DELAY_MS_MIN;
|
|
146
|
+
}
|
|
147
|
+
if (floored > EXTERNAL_HTTP_RETRY_DELAY_MS_MAX) {
|
|
148
|
+
return EXTERNAL_HTTP_RETRY_DELAY_MS_MAX;
|
|
149
|
+
}
|
|
150
|
+
return floored;
|
|
151
|
+
}
|
|
152
|
+
function normalizeCircuitFailureThreshold(rawValue) {
|
|
153
|
+
const numeric = Number(rawValue);
|
|
154
|
+
if (!Number.isFinite(numeric)) {
|
|
155
|
+
return EXTERNAL_HTTP_CIRCUIT_FAILURE_THRESHOLD_DEFAULT;
|
|
156
|
+
}
|
|
157
|
+
const floored = Math.floor(numeric);
|
|
158
|
+
if (floored < EXTERNAL_HTTP_CIRCUIT_FAILURE_THRESHOLD_MIN) {
|
|
159
|
+
return EXTERNAL_HTTP_CIRCUIT_FAILURE_THRESHOLD_MIN;
|
|
160
|
+
}
|
|
161
|
+
if (floored > EXTERNAL_HTTP_CIRCUIT_FAILURE_THRESHOLD_MAX) {
|
|
162
|
+
return EXTERNAL_HTTP_CIRCUIT_FAILURE_THRESHOLD_MAX;
|
|
163
|
+
}
|
|
164
|
+
return floored;
|
|
165
|
+
}
|
|
166
|
+
function normalizeCircuitCooldownMs(rawValue) {
|
|
167
|
+
const numeric = Number(rawValue);
|
|
168
|
+
if (!Number.isFinite(numeric)) {
|
|
169
|
+
return EXTERNAL_HTTP_CIRCUIT_COOLDOWN_MS_DEFAULT;
|
|
170
|
+
}
|
|
171
|
+
const floored = Math.floor(numeric);
|
|
172
|
+
if (floored < EXTERNAL_HTTP_CIRCUIT_COOLDOWN_MS_MIN) {
|
|
173
|
+
return EXTERNAL_HTTP_CIRCUIT_COOLDOWN_MS_MIN;
|
|
174
|
+
}
|
|
175
|
+
if (floored > EXTERNAL_HTTP_CIRCUIT_COOLDOWN_MS_MAX) {
|
|
176
|
+
return EXTERNAL_HTTP_CIRCUIT_COOLDOWN_MS_MAX;
|
|
177
|
+
}
|
|
178
|
+
return floored;
|
|
179
|
+
}
|
|
180
|
+
function normalizeRepresentationVersion(rawValue) {
|
|
181
|
+
return String(rawValue || '')
|
|
182
|
+
.trim()
|
|
183
|
+
.replace(/\s+/g, '_')
|
|
184
|
+
.slice(0, 160);
|
|
185
|
+
}
|
|
186
|
+
function normalizeEmbeddingModelId(rawValue) {
|
|
187
|
+
return String(rawValue || '')
|
|
188
|
+
.trim()
|
|
189
|
+
.replace(/\s+/g, '_')
|
|
190
|
+
.slice(0, 160);
|
|
191
|
+
}
|
|
192
|
+
function normalizeEmbeddingDimension(rawValue) {
|
|
193
|
+
const numeric = Number(rawValue);
|
|
194
|
+
if (!Number.isFinite(numeric)) {
|
|
195
|
+
return 0;
|
|
196
|
+
}
|
|
197
|
+
return Math.max(0, Math.floor(numeric));
|
|
198
|
+
}
|
|
199
|
+
function normalizeIndexSignature(rawValue) {
|
|
200
|
+
return String(rawValue || '')
|
|
201
|
+
.trim()
|
|
202
|
+
.replace(/[^a-zA-Z0-9:_-]+/g, '')
|
|
203
|
+
.slice(0, 200);
|
|
204
|
+
}
|
|
205
|
+
function normalizeRepresentationStatus(rawValue) {
|
|
206
|
+
const normalized = String(rawValue || '').trim().toLowerCase();
|
|
207
|
+
if (normalized === 'aligned' || normalized === 'mismatch' || normalized === 'unknown') {
|
|
208
|
+
return normalized;
|
|
209
|
+
}
|
|
210
|
+
return 'unknown';
|
|
211
|
+
}
|
|
212
|
+
function parseExternalHttpStatusCode(error) {
|
|
213
|
+
const message = String(error?.message || error || '').trim().toLowerCase();
|
|
214
|
+
const matched = message.match(/external_http_status_(\d{3})/);
|
|
215
|
+
if (!matched) {
|
|
216
|
+
return 0;
|
|
217
|
+
}
|
|
218
|
+
const numeric = Number(matched[1]);
|
|
219
|
+
return Number.isFinite(numeric) ? Math.floor(numeric) : 0;
|
|
220
|
+
}
|
|
221
|
+
function isExternalHttpTransientFailure(error) {
|
|
222
|
+
const name = String(error?.name || '').trim().toLowerCase();
|
|
223
|
+
if (name === 'aborterror' || name === 'timeout') {
|
|
224
|
+
return true;
|
|
225
|
+
}
|
|
226
|
+
const statusCode = parseExternalHttpStatusCode(error);
|
|
227
|
+
if (statusCode > 0) {
|
|
228
|
+
return EXTERNAL_HTTP_RETRYABLE_STATUSES.has(statusCode);
|
|
229
|
+
}
|
|
230
|
+
const message = String(error?.message || error || '').trim().toLowerCase();
|
|
231
|
+
return [
|
|
232
|
+
'fetch failed',
|
|
233
|
+
'networkerror',
|
|
234
|
+
'socket_hang_up',
|
|
235
|
+
'econnreset',
|
|
236
|
+
'econnrefused',
|
|
237
|
+
'etimedout',
|
|
238
|
+
'timed out',
|
|
239
|
+
'timeout',
|
|
240
|
+
'eai_again',
|
|
241
|
+
].some((token) => message.includes(token));
|
|
242
|
+
}
|
|
243
|
+
function truncateMessage(rawValue, maxLength = 200) {
|
|
244
|
+
return String(rawValue || '').trim().slice(0, maxLength);
|
|
245
|
+
}
|
|
246
|
+
function normalizeExternalHttpErrorCode(rawValue, fallback = '') {
|
|
247
|
+
const normalized = String(rawValue || '')
|
|
248
|
+
.trim()
|
|
249
|
+
.toLowerCase()
|
|
250
|
+
.replace(/[^a-z0-9:_-]+/g, '_')
|
|
251
|
+
.replace(/^_+|_+$/g, '')
|
|
252
|
+
.slice(0, 96);
|
|
253
|
+
return normalized || fallback;
|
|
254
|
+
}
|
|
255
|
+
function normalizeExternalHttpRequestId(rawValue, fallback = '') {
|
|
256
|
+
const normalized = String(rawValue || '')
|
|
257
|
+
.trim()
|
|
258
|
+
.replace(/[^a-zA-Z0-9._:-]+/g, '')
|
|
259
|
+
.slice(0, 96);
|
|
260
|
+
return normalized || fallback;
|
|
261
|
+
}
|
|
262
|
+
function nextExternalHttpRequestId(nowMs = Date.now()) {
|
|
263
|
+
EXTERNAL_HTTP_REQUEST_ID_SEQUENCE = (EXTERNAL_HTTP_REQUEST_ID_SEQUENCE + 1) % 1000000;
|
|
264
|
+
const timestampToken = Math.max(0, Math.floor(nowMs)).toString(36);
|
|
265
|
+
const sequenceToken = EXTERNAL_HTTP_REQUEST_ID_SEQUENCE.toString(36).padStart(4, '0');
|
|
266
|
+
return `${EXTERNAL_HTTP_REQUEST_ID_PREFIX}-${timestampToken}-${sequenceToken}`;
|
|
267
|
+
}
|
|
268
|
+
function parseExternalHttpRetryAfterMs(rawValue, nowMs = Date.now()) {
|
|
269
|
+
const value = String(rawValue || '').trim();
|
|
270
|
+
if (!value) {
|
|
271
|
+
return 0;
|
|
272
|
+
}
|
|
273
|
+
const numericSeconds = Number(value);
|
|
274
|
+
if (Number.isFinite(numericSeconds)) {
|
|
275
|
+
const numericMs = Math.round(Math.max(0, numericSeconds) * 1000);
|
|
276
|
+
return Math.min(EXTERNAL_HTTP_RETRY_AFTER_MS_MAX, numericMs);
|
|
277
|
+
}
|
|
278
|
+
const parsedAtMs = Date.parse(value);
|
|
279
|
+
if (!Number.isFinite(parsedAtMs)) {
|
|
280
|
+
return 0;
|
|
281
|
+
}
|
|
282
|
+
const deltaMs = parsedAtMs - nowMs;
|
|
283
|
+
if (!Number.isFinite(deltaMs) || deltaMs <= 0) {
|
|
284
|
+
return 0;
|
|
285
|
+
}
|
|
286
|
+
return Math.min(EXTERNAL_HTTP_RETRY_AFTER_MS_MAX, Math.floor(deltaMs));
|
|
287
|
+
}
|
|
288
|
+
async function waitForRetryDelay(ms) {
|
|
289
|
+
if (!Number.isFinite(ms) || ms <= 0) {
|
|
290
|
+
return;
|
|
291
|
+
}
|
|
292
|
+
await new Promise((resolve) => {
|
|
293
|
+
setTimeout(resolve, ms);
|
|
294
|
+
});
|
|
295
|
+
}
|
|
296
|
+
function selectCandidatesFromExternalStub(input) {
|
|
297
|
+
if (!input.annPrefilterEnabled || input.atomCount < EXTERNAL_STUB_MIN_ATOMS) {
|
|
298
|
+
return {
|
|
299
|
+
used: false,
|
|
300
|
+
candidateAtomIds: [],
|
|
301
|
+
mode: 'full_scan',
|
|
302
|
+
};
|
|
303
|
+
}
|
|
304
|
+
const prioritizedTokens = uniqueTokens([
|
|
305
|
+
...Array.from(input.queryWeights.entries())
|
|
306
|
+
.filter((entry) => Number(entry[1]) > 0)
|
|
307
|
+
.sort((left, right) => Number(right[1]) - Number(left[1]))
|
|
308
|
+
.map((entry) => String(entry[0] || '')),
|
|
309
|
+
...input.queryTokens,
|
|
310
|
+
]).slice(0, EXTERNAL_STUB_TOP_TOKENS);
|
|
311
|
+
if (prioritizedTokens.length <= 0) {
|
|
312
|
+
return {
|
|
313
|
+
used: false,
|
|
314
|
+
candidateAtomIds: [],
|
|
315
|
+
mode: 'full_scan',
|
|
316
|
+
};
|
|
317
|
+
}
|
|
318
|
+
const targetCandidateCount = Math.min(input.atomCount, Math.max(EXTERNAL_STUB_CANDIDATE_MIN_FLOOR, Math.max(1, input.topK) * EXTERNAL_STUB_CANDIDATE_MULTIPLIER));
|
|
319
|
+
const minimumCandidateCount = Math.min(input.atomCount, Math.max(1, input.topK) * 2);
|
|
320
|
+
const candidateSet = new Set();
|
|
321
|
+
let tokenMatched = false;
|
|
322
|
+
prioritizedTokens.forEach((token) => {
|
|
323
|
+
if (candidateSet.size >= targetCandidateCount) {
|
|
324
|
+
return;
|
|
325
|
+
}
|
|
326
|
+
const postingList = input.tokenToAtomIds.get(token);
|
|
327
|
+
if (!postingList || postingList.length <= 0) {
|
|
328
|
+
return;
|
|
329
|
+
}
|
|
330
|
+
tokenMatched = true;
|
|
331
|
+
postingList.forEach((atomId) => {
|
|
332
|
+
if (candidateSet.size >= targetCandidateCount) {
|
|
333
|
+
return;
|
|
334
|
+
}
|
|
335
|
+
const normalizedId = String(atomId || '').trim();
|
|
336
|
+
if (normalizedId) {
|
|
337
|
+
candidateSet.add(normalizedId);
|
|
338
|
+
}
|
|
339
|
+
});
|
|
340
|
+
});
|
|
341
|
+
if (!tokenMatched || candidateSet.size < minimumCandidateCount) {
|
|
342
|
+
return {
|
|
343
|
+
used: false,
|
|
344
|
+
candidateAtomIds: [],
|
|
345
|
+
mode: 'full_scan',
|
|
346
|
+
};
|
|
347
|
+
}
|
|
348
|
+
return {
|
|
349
|
+
used: true,
|
|
350
|
+
candidateAtomIds: Array.from(candidateSet),
|
|
351
|
+
mode: 'token_prefilter',
|
|
352
|
+
};
|
|
353
|
+
}
|
|
354
|
+
function createVectorAccelerationAdapter(provider, options = {}) {
|
|
355
|
+
if (provider === 'external_stub') {
|
|
356
|
+
return {
|
|
357
|
+
id: EXTERNAL_STUB_ADAPTER_ID,
|
|
358
|
+
syncIndex: (input) => ({
|
|
359
|
+
synced: true,
|
|
360
|
+
atomCount: input.atomCount,
|
|
361
|
+
indexSignature: String(input.indexSignature || '').trim() || undefined,
|
|
362
|
+
representation: {
|
|
363
|
+
version: String(input.representationVersion || '').trim() || undefined,
|
|
364
|
+
embeddingModelId: String(input.embeddingModelId || '').trim() || undefined,
|
|
365
|
+
embeddingDimension: Number.isFinite(Number(input.embeddingDimension))
|
|
366
|
+
? Math.max(0, Math.floor(Number(input.embeddingDimension || 0)))
|
|
367
|
+
: undefined,
|
|
368
|
+
indexSignature: String(input.indexSignature || '').trim() || undefined,
|
|
369
|
+
validated: true,
|
|
370
|
+
},
|
|
371
|
+
}),
|
|
372
|
+
selectCandidates: selectCandidatesFromExternalStub,
|
|
373
|
+
getHealth: () => ({
|
|
374
|
+
status: 'ready',
|
|
375
|
+
message: 'external_stub_adapter_ready',
|
|
376
|
+
checkedAt: new Date().toISOString(),
|
|
377
|
+
indexSyncStatus: 'ready',
|
|
378
|
+
indexSyncMessage: 'external_stub_index_sync_not_required',
|
|
379
|
+
representationVersion: EXTERNAL_STUB_REPRESENTATION_VERSION,
|
|
380
|
+
embeddingModelId: EXTERNAL_STUB_EMBEDDING_MODEL_ID,
|
|
381
|
+
representationStatus: 'unknown',
|
|
382
|
+
representationStatusReason: 'external_stub_representation_unverified',
|
|
383
|
+
}),
|
|
384
|
+
};
|
|
385
|
+
}
|
|
386
|
+
if (provider !== 'external_http') {
|
|
387
|
+
return undefined;
|
|
388
|
+
}
|
|
389
|
+
const endpoint = normalizeEndpoint(options.externalHttp?.endpoint);
|
|
390
|
+
const timeoutMs = normalizeTimeoutMs(options.externalHttp?.timeoutMs);
|
|
391
|
+
const maxRetries = normalizeMaxRetries(options.externalHttp?.maxRetries);
|
|
392
|
+
const retryDelayMs = normalizeRetryDelayMs(options.externalHttp?.retryDelayMs);
|
|
393
|
+
const circuitFailureThreshold = normalizeCircuitFailureThreshold(options.externalHttp?.circuitFailureThreshold);
|
|
394
|
+
const circuitCooldownMs = normalizeCircuitCooldownMs(options.externalHttp?.circuitCooldownMs);
|
|
395
|
+
let circuitState = 'closed';
|
|
396
|
+
let circuitOpenedAtMs = 0;
|
|
397
|
+
let circuitOpenedAt = '';
|
|
398
|
+
let consecutiveFailures = 0;
|
|
399
|
+
let selectionRequestCount = 0;
|
|
400
|
+
let retryCount = 0;
|
|
401
|
+
let shortCircuitCount = 0;
|
|
402
|
+
let successCount = 0;
|
|
403
|
+
let failureCount = 0;
|
|
404
|
+
let halfOpenProbeSuccessCount = 0;
|
|
405
|
+
let halfOpenProbeFailureCount = 0;
|
|
406
|
+
let lastSuccessAt = '';
|
|
407
|
+
let lastFailureAt = '';
|
|
408
|
+
let lastRequestId = '';
|
|
409
|
+
let lastErrorCode = '';
|
|
410
|
+
let lastRetryAfterMs = 0;
|
|
411
|
+
let indexSyncStatus = endpoint ? 'unknown' : 'unavailable';
|
|
412
|
+
let indexSyncMessage = endpoint ? 'external_http_index_sync_pending' : 'external_http_endpoint_missing';
|
|
413
|
+
let lastSyncAt = '';
|
|
414
|
+
let syncRequestCount = 0;
|
|
415
|
+
let syncSuccessCount = 0;
|
|
416
|
+
let syncFailureCount = 0;
|
|
417
|
+
let syncedIndexSignature = '';
|
|
418
|
+
let syncedAtomCount = 0;
|
|
419
|
+
let representationVersion = '';
|
|
420
|
+
let embeddingModelId = '';
|
|
421
|
+
let embeddingDimension = 0;
|
|
422
|
+
let indexSignature = '';
|
|
423
|
+
let representationStatus = 'unknown';
|
|
424
|
+
let representationStatusReason = '';
|
|
425
|
+
/** M10.6: Prefilter effectiveness tracking. */
|
|
426
|
+
let prefilterEffectivenessRatio = 1;
|
|
427
|
+
let lastTotalAtomsInScope = 0;
|
|
428
|
+
const buildHealthTelemetryFields = () => ({
|
|
429
|
+
indexSyncStatus,
|
|
430
|
+
indexSyncMessage: indexSyncMessage || undefined,
|
|
431
|
+
lastSyncAt: lastSyncAt || undefined,
|
|
432
|
+
syncRequestCount,
|
|
433
|
+
syncSuccessCount,
|
|
434
|
+
syncFailureCount,
|
|
435
|
+
syncedIndexSignature: syncedIndexSignature || undefined,
|
|
436
|
+
syncedAtomCount: syncedAtomCount > 0 ? syncedAtomCount : undefined,
|
|
437
|
+
circuitState,
|
|
438
|
+
consecutiveFailures,
|
|
439
|
+
requestCount: selectionRequestCount,
|
|
440
|
+
retryCount,
|
|
441
|
+
shortCircuitCount,
|
|
442
|
+
successCount,
|
|
443
|
+
failureCount,
|
|
444
|
+
halfOpenProbeSuccessCount,
|
|
445
|
+
halfOpenProbeFailureCount,
|
|
446
|
+
lastSuccessAt: lastSuccessAt || undefined,
|
|
447
|
+
lastFailureAt: lastFailureAt || undefined,
|
|
448
|
+
circuitOpenedAt: circuitOpenedAt || undefined,
|
|
449
|
+
lastRequestId: lastRequestId || undefined,
|
|
450
|
+
lastErrorCode: lastErrorCode || undefined,
|
|
451
|
+
lastRetryAfterMs: lastRetryAfterMs > 0 ? lastRetryAfterMs : undefined,
|
|
452
|
+
representationVersion: representationVersion || undefined,
|
|
453
|
+
embeddingModelId: embeddingModelId || undefined,
|
|
454
|
+
embeddingDimension: embeddingDimension > 0 ? embeddingDimension : undefined,
|
|
455
|
+
indexSignature: indexSignature || undefined,
|
|
456
|
+
representationStatus,
|
|
457
|
+
representationStatusReason: representationStatusReason || undefined,
|
|
458
|
+
prefilterEffectivenessRatio: prefilterEffectivenessRatio || undefined,
|
|
459
|
+
lastTotalAtomsInScope: lastTotalAtomsInScope > 0 ? lastTotalAtomsInScope : undefined,
|
|
460
|
+
});
|
|
461
|
+
let lastHealth = endpoint
|
|
462
|
+
? {
|
|
463
|
+
status: 'degraded',
|
|
464
|
+
message: (`external_http_endpoint_configured:${endpoint}:retries=${maxRetries}:`
|
|
465
|
+
+ `breaker=${circuitFailureThreshold}/${circuitCooldownMs}`),
|
|
466
|
+
checkedAt: new Date().toISOString(),
|
|
467
|
+
...buildHealthTelemetryFields(),
|
|
468
|
+
}
|
|
469
|
+
: {
|
|
470
|
+
status: 'unavailable',
|
|
471
|
+
message: 'external_http_endpoint_missing',
|
|
472
|
+
checkedAt: new Date().toISOString(),
|
|
473
|
+
...buildHealthTelemetryFields(),
|
|
474
|
+
};
|
|
475
|
+
const updateHealth = (next) => {
|
|
476
|
+
lastHealth = {
|
|
477
|
+
...buildHealthTelemetryFields(),
|
|
478
|
+
status: next.status,
|
|
479
|
+
message: String(next.message || '').trim() || undefined,
|
|
480
|
+
checkedAt: String(next.checkedAt || '').trim() || new Date().toISOString(),
|
|
481
|
+
};
|
|
482
|
+
};
|
|
483
|
+
return {
|
|
484
|
+
id: EXTERNAL_HTTP_ADAPTER_ID,
|
|
485
|
+
async syncIndex(input) {
|
|
486
|
+
const requestedRepresentationVersion = normalizeRepresentationVersion(input.representationVersion);
|
|
487
|
+
const requestedEmbeddingModelId = normalizeEmbeddingModelId(input.embeddingModelId);
|
|
488
|
+
const requestedEmbeddingDimension = normalizeEmbeddingDimension(input.embeddingDimension);
|
|
489
|
+
const requestedIndexSignature = normalizeIndexSignature(input.indexSignature);
|
|
490
|
+
const requestedAtomCount = Math.max(0, Math.floor(Number(input.atomCount || 0)));
|
|
491
|
+
if (requestedRepresentationVersion) {
|
|
492
|
+
representationVersion = requestedRepresentationVersion;
|
|
493
|
+
}
|
|
494
|
+
if (requestedEmbeddingModelId) {
|
|
495
|
+
embeddingModelId = requestedEmbeddingModelId;
|
|
496
|
+
}
|
|
497
|
+
if (requestedEmbeddingDimension > 0) {
|
|
498
|
+
embeddingDimension = requestedEmbeddingDimension;
|
|
499
|
+
}
|
|
500
|
+
if (requestedIndexSignature) {
|
|
501
|
+
indexSignature = requestedIndexSignature;
|
|
502
|
+
}
|
|
503
|
+
if (requestedIndexSignature && requestedIndexSignature === syncedIndexSignature && requestedAtomCount === syncedAtomCount) {
|
|
504
|
+
indexSyncStatus = 'ready';
|
|
505
|
+
indexSyncMessage = `external_http_index_sync_cached:${requestedIndexSignature}`;
|
|
506
|
+
return {
|
|
507
|
+
synced: true,
|
|
508
|
+
atomCount: syncedAtomCount,
|
|
509
|
+
indexSignature: syncedIndexSignature || undefined,
|
|
510
|
+
representation: {
|
|
511
|
+
version: representationVersion || undefined,
|
|
512
|
+
embeddingModelId: embeddingModelId || undefined,
|
|
513
|
+
embeddingDimension: embeddingDimension > 0 ? embeddingDimension : undefined,
|
|
514
|
+
indexSignature: syncedIndexSignature || undefined,
|
|
515
|
+
validated: representationStatus !== 'mismatch',
|
|
516
|
+
},
|
|
517
|
+
};
|
|
518
|
+
}
|
|
519
|
+
if (!endpoint) {
|
|
520
|
+
indexSyncStatus = 'unavailable';
|
|
521
|
+
indexSyncMessage = 'external_http_endpoint_missing';
|
|
522
|
+
syncFailureCount += 1;
|
|
523
|
+
throw new Error('external_http_endpoint_missing');
|
|
524
|
+
}
|
|
525
|
+
syncRequestCount += 1;
|
|
526
|
+
const clientRequestId = nextExternalHttpRequestId();
|
|
527
|
+
const controller = new AbortController();
|
|
528
|
+
const timer = setTimeout(() => controller.abort(), timeoutMs);
|
|
529
|
+
try {
|
|
530
|
+
const response = await fetch(`${endpoint}/sync-index`, {
|
|
531
|
+
method: 'POST',
|
|
532
|
+
headers: {
|
|
533
|
+
'content-type': 'application/json',
|
|
534
|
+
'x-request-id': clientRequestId,
|
|
535
|
+
'x-correlation-id': clientRequestId,
|
|
536
|
+
},
|
|
537
|
+
body: JSON.stringify({
|
|
538
|
+
atomCount: requestedAtomCount,
|
|
539
|
+
indexSignature: requestedIndexSignature || undefined,
|
|
540
|
+
representationVersion: requestedRepresentationVersion || undefined,
|
|
541
|
+
embeddingModelId: requestedEmbeddingModelId || undefined,
|
|
542
|
+
embeddingDimension: requestedEmbeddingDimension > 0 ? requestedEmbeddingDimension : undefined,
|
|
543
|
+
tokenToAtomIds: Array.from(input.tokenToAtomIds.entries()),
|
|
544
|
+
signatureBuckets: Array.from(input.signatureBuckets.entries()),
|
|
545
|
+
}),
|
|
546
|
+
signal: controller.signal,
|
|
547
|
+
});
|
|
548
|
+
const connectorRequestId = normalizeExternalHttpRequestId(response?.headers?.get?.('x-request-id'), clientRequestId);
|
|
549
|
+
if (connectorRequestId) {
|
|
550
|
+
lastRequestId = connectorRequestId;
|
|
551
|
+
}
|
|
552
|
+
if (!response.ok) {
|
|
553
|
+
const statusCode = Math.max(0, Math.floor(Number(response.status || 0)));
|
|
554
|
+
const connectorErrorCode = normalizeExternalHttpErrorCode(response?.headers?.get?.('x-error-code'), statusCode > 0 ? `http_${statusCode}` : 'http_error');
|
|
555
|
+
lastErrorCode = connectorErrorCode;
|
|
556
|
+
syncFailureCount += 1;
|
|
557
|
+
indexSyncStatus = 'degraded';
|
|
558
|
+
indexSyncMessage = `external_http_sync_status_${statusCode}:errorCode=${connectorErrorCode}`;
|
|
559
|
+
throw new Error(indexSyncMessage);
|
|
560
|
+
}
|
|
561
|
+
const payload = await response.json();
|
|
562
|
+
const responseRepresentationVersion = normalizeRepresentationVersion(payload?.representationVersion);
|
|
563
|
+
const responseEmbeddingModelId = normalizeEmbeddingModelId(payload?.embeddingModelId);
|
|
564
|
+
const responseEmbeddingDimension = normalizeEmbeddingDimension(payload?.embeddingDimension);
|
|
565
|
+
const responseIndexSignature = normalizeIndexSignature(payload?.indexSignature);
|
|
566
|
+
if (responseRepresentationVersion) {
|
|
567
|
+
representationVersion = responseRepresentationVersion;
|
|
568
|
+
}
|
|
569
|
+
if (responseEmbeddingModelId) {
|
|
570
|
+
embeddingModelId = responseEmbeddingModelId;
|
|
571
|
+
}
|
|
572
|
+
if (responseEmbeddingDimension > 0) {
|
|
573
|
+
embeddingDimension = responseEmbeddingDimension;
|
|
574
|
+
}
|
|
575
|
+
if (responseIndexSignature) {
|
|
576
|
+
indexSignature = responseIndexSignature;
|
|
577
|
+
}
|
|
578
|
+
const responseRepresentationStatus = normalizeRepresentationStatus(payload?.representationStatus);
|
|
579
|
+
const responseRepresentationStatusReason = truncateMessage(payload?.representationStatusReason, 240);
|
|
580
|
+
if (responseRepresentationStatus !== 'unknown') {
|
|
581
|
+
representationStatus = responseRepresentationStatus;
|
|
582
|
+
representationStatusReason = responseRepresentationStatusReason
|
|
583
|
+
|| (responseRepresentationStatus === 'aligned'
|
|
584
|
+
? 'external_http_representation_aligned'
|
|
585
|
+
: 'external_http_representation_mismatch');
|
|
586
|
+
}
|
|
587
|
+
syncedIndexSignature = requestedIndexSignature || responseIndexSignature || indexSignature;
|
|
588
|
+
syncedAtomCount = requestedAtomCount;
|
|
589
|
+
lastSyncAt = new Date().toISOString();
|
|
590
|
+
syncSuccessCount += 1;
|
|
591
|
+
indexSyncStatus = 'ready';
|
|
592
|
+
indexSyncMessage = (`external_http_index_synced:${syncedIndexSignature || 'unknown'}:atoms=${syncedAtomCount}`);
|
|
593
|
+
updateHealth({
|
|
594
|
+
status: circuitState === 'open' ? 'degraded' : 'ready',
|
|
595
|
+
message: 'external_http_adapter_ready',
|
|
596
|
+
checkedAt: new Date().toISOString(),
|
|
597
|
+
});
|
|
598
|
+
return {
|
|
599
|
+
synced: true,
|
|
600
|
+
atomCount: syncedAtomCount,
|
|
601
|
+
indexSignature: syncedIndexSignature || undefined,
|
|
602
|
+
representation: {
|
|
603
|
+
version: representationVersion || undefined,
|
|
604
|
+
embeddingModelId: embeddingModelId || undefined,
|
|
605
|
+
embeddingDimension: embeddingDimension > 0 ? embeddingDimension : undefined,
|
|
606
|
+
indexSignature: syncedIndexSignature || undefined,
|
|
607
|
+
validated: representationStatus !== 'mismatch',
|
|
608
|
+
},
|
|
609
|
+
};
|
|
610
|
+
}
|
|
611
|
+
catch (error) {
|
|
612
|
+
syncFailureCount += 1;
|
|
613
|
+
indexSyncStatus = endpoint ? 'degraded' : 'unavailable';
|
|
614
|
+
indexSyncMessage = truncateMessage(error?.message || error, 240) || 'external_http_index_sync_failed';
|
|
615
|
+
updateHealth({
|
|
616
|
+
status: endpoint ? 'degraded' : 'unavailable',
|
|
617
|
+
message: String(lastHealth.message || '').trim() || indexSyncMessage,
|
|
618
|
+
checkedAt: new Date().toISOString(),
|
|
619
|
+
});
|
|
620
|
+
throw error;
|
|
621
|
+
}
|
|
622
|
+
finally {
|
|
623
|
+
clearTimeout(timer);
|
|
624
|
+
}
|
|
625
|
+
},
|
|
626
|
+
async selectCandidates(input) {
|
|
627
|
+
selectionRequestCount += 1;
|
|
628
|
+
const requestedRepresentationVersion = normalizeRepresentationVersion(input.representationVersion);
|
|
629
|
+
const requestedEmbeddingModelId = normalizeEmbeddingModelId(input.embeddingModelId);
|
|
630
|
+
const requestedEmbeddingDimension = normalizeEmbeddingDimension(input.embeddingDimension);
|
|
631
|
+
const requestedIndexSignature = normalizeIndexSignature(input.indexSignature);
|
|
632
|
+
if (requestedRepresentationVersion) {
|
|
633
|
+
representationVersion = requestedRepresentationVersion;
|
|
634
|
+
}
|
|
635
|
+
if (requestedEmbeddingModelId) {
|
|
636
|
+
embeddingModelId = requestedEmbeddingModelId;
|
|
637
|
+
}
|
|
638
|
+
if (requestedEmbeddingDimension > 0) {
|
|
639
|
+
embeddingDimension = requestedEmbeddingDimension;
|
|
640
|
+
}
|
|
641
|
+
if (requestedIndexSignature) {
|
|
642
|
+
indexSignature = requestedIndexSignature;
|
|
643
|
+
}
|
|
644
|
+
if (!endpoint) {
|
|
645
|
+
const endpointMissingError = new Error('external_http_endpoint_missing');
|
|
646
|
+
lastErrorCode = 'endpoint_missing';
|
|
647
|
+
lastRetryAfterMs = 0;
|
|
648
|
+
consecutiveFailures += 1;
|
|
649
|
+
failureCount += 1;
|
|
650
|
+
lastFailureAt = new Date().toISOString();
|
|
651
|
+
representationStatus = 'unknown';
|
|
652
|
+
representationStatusReason = 'external_http_endpoint_missing';
|
|
653
|
+
updateHealth({
|
|
654
|
+
status: 'unavailable',
|
|
655
|
+
message: 'external_http_endpoint_missing',
|
|
656
|
+
checkedAt: new Date().toISOString(),
|
|
657
|
+
});
|
|
658
|
+
throw endpointMissingError;
|
|
659
|
+
}
|
|
660
|
+
const nowMs = Date.now();
|
|
661
|
+
if (circuitState === 'open') {
|
|
662
|
+
const elapsedMs = Math.max(0, nowMs - circuitOpenedAtMs);
|
|
663
|
+
if (elapsedMs < circuitCooldownMs) {
|
|
664
|
+
const remainingMs = Math.max(0, circuitCooldownMs - elapsedMs);
|
|
665
|
+
const shortCircuitError = new Error(`external_http_circuit_open:${remainingMs}`);
|
|
666
|
+
lastErrorCode = 'circuit_open';
|
|
667
|
+
lastRetryAfterMs = remainingMs;
|
|
668
|
+
shortCircuitCount += 1;
|
|
669
|
+
failureCount += 1;
|
|
670
|
+
lastFailureAt = new Date().toISOString();
|
|
671
|
+
updateHealth({
|
|
672
|
+
status: 'unavailable',
|
|
673
|
+
message: `external_http_circuit_open:${remainingMs}`,
|
|
674
|
+
checkedAt: new Date().toISOString(),
|
|
675
|
+
});
|
|
676
|
+
throw shortCircuitError;
|
|
677
|
+
}
|
|
678
|
+
circuitState = 'half_open';
|
|
679
|
+
updateHealth({
|
|
680
|
+
status: 'degraded',
|
|
681
|
+
message: 'external_http_circuit_half_open',
|
|
682
|
+
checkedAt: new Date().toISOString(),
|
|
683
|
+
});
|
|
684
|
+
}
|
|
685
|
+
const halfOpenProbeActive = circuitState === 'half_open';
|
|
686
|
+
const totalAttempts = Math.max(1, maxRetries + 1);
|
|
687
|
+
let lastError = null;
|
|
688
|
+
for (let attemptIndex = 0; attemptIndex < totalAttempts; attemptIndex += 1) {
|
|
689
|
+
const currentAttempt = attemptIndex + 1;
|
|
690
|
+
const clientRequestId = nextExternalHttpRequestId();
|
|
691
|
+
let connectorRequestId = '';
|
|
692
|
+
let connectorErrorCode = '';
|
|
693
|
+
let responseRetryAfterMs = 0;
|
|
694
|
+
const controller = new AbortController();
|
|
695
|
+
const timer = setTimeout(() => controller.abort(), timeoutMs);
|
|
696
|
+
try {
|
|
697
|
+
const response = await fetch(`${endpoint}/select-candidates`, {
|
|
698
|
+
method: 'POST',
|
|
699
|
+
headers: {
|
|
700
|
+
'content-type': 'application/json',
|
|
701
|
+
'x-request-id': clientRequestId,
|
|
702
|
+
'x-correlation-id': clientRequestId,
|
|
703
|
+
},
|
|
704
|
+
body: JSON.stringify({
|
|
705
|
+
atomCount: input.atomCount,
|
|
706
|
+
queryTokens: input.queryTokens,
|
|
707
|
+
queryWeights: Array.from(input.queryWeights.entries()),
|
|
708
|
+
topK: input.topK,
|
|
709
|
+
annPrefilterEnabled: input.annPrefilterEnabled,
|
|
710
|
+
representationVersion: requestedRepresentationVersion || undefined,
|
|
711
|
+
embeddingModelId: requestedEmbeddingModelId || undefined,
|
|
712
|
+
embeddingDimension: requestedEmbeddingDimension > 0
|
|
713
|
+
? requestedEmbeddingDimension
|
|
714
|
+
: undefined,
|
|
715
|
+
indexSignature: requestedIndexSignature || undefined,
|
|
716
|
+
}),
|
|
717
|
+
signal: controller.signal,
|
|
718
|
+
});
|
|
719
|
+
connectorRequestId = normalizeExternalHttpRequestId(response?.headers?.get?.('x-request-id'), clientRequestId);
|
|
720
|
+
if (connectorRequestId) {
|
|
721
|
+
lastRequestId = connectorRequestId;
|
|
722
|
+
}
|
|
723
|
+
connectorErrorCode = normalizeExternalHttpErrorCode(response?.headers?.get?.('x-error-code'), '');
|
|
724
|
+
if (connectorErrorCode) {
|
|
725
|
+
lastErrorCode = connectorErrorCode;
|
|
726
|
+
}
|
|
727
|
+
responseRetryAfterMs = parseExternalHttpRetryAfterMs(response?.headers?.get?.('retry-after'));
|
|
728
|
+
if (responseRetryAfterMs > 0) {
|
|
729
|
+
lastRetryAfterMs = responseRetryAfterMs;
|
|
730
|
+
}
|
|
731
|
+
if (!response.ok) {
|
|
732
|
+
const statusCode = Math.max(0, Math.floor(Number(response.status || 0)));
|
|
733
|
+
const statusErrorCode = normalizeExternalHttpErrorCode(connectorErrorCode, statusCode > 0 ? `http_${statusCode}` : 'http_error');
|
|
734
|
+
connectorErrorCode = statusErrorCode;
|
|
735
|
+
lastErrorCode = statusErrorCode;
|
|
736
|
+
const retryAfterSegment = responseRetryAfterMs > 0
|
|
737
|
+
? `:retryAfterMs=${responseRetryAfterMs}`
|
|
738
|
+
: '';
|
|
739
|
+
throw new Error(`external_http_status_${statusCode}:errorCode=${statusErrorCode}${retryAfterSegment}:requestId=${connectorRequestId || clientRequestId}`);
|
|
740
|
+
}
|
|
741
|
+
const payload = await response.json();
|
|
742
|
+
const mode = String(payload?.mode || '').trim().toLowerCase();
|
|
743
|
+
const normalizedMode = (mode === 'token_prefilter'
|
|
744
|
+
|| mode === 'token_signature_prefilter') ? mode : 'full_scan';
|
|
745
|
+
const responseRepresentationVersion = normalizeRepresentationVersion(payload?.representationVersion);
|
|
746
|
+
const responseEmbeddingModelId = normalizeEmbeddingModelId(payload?.embeddingModelId);
|
|
747
|
+
const responseEmbeddingDimension = normalizeEmbeddingDimension(payload?.embeddingDimension);
|
|
748
|
+
const responseIndexSignature = normalizeIndexSignature(payload?.indexSignature);
|
|
749
|
+
if (responseRepresentationVersion) {
|
|
750
|
+
representationVersion = responseRepresentationVersion;
|
|
751
|
+
}
|
|
752
|
+
if (responseEmbeddingModelId) {
|
|
753
|
+
embeddingModelId = responseEmbeddingModelId;
|
|
754
|
+
}
|
|
755
|
+
if (responseEmbeddingDimension > 0) {
|
|
756
|
+
embeddingDimension = responseEmbeddingDimension;
|
|
757
|
+
}
|
|
758
|
+
if (responseIndexSignature) {
|
|
759
|
+
indexSignature = responseIndexSignature;
|
|
760
|
+
}
|
|
761
|
+
const responseRepresentationStatus = normalizeRepresentationStatus(payload?.representationStatus);
|
|
762
|
+
const responseRepresentationStatusReason = truncateMessage(payload?.representationStatusReason, 240);
|
|
763
|
+
const representationMismatchReasons = [];
|
|
764
|
+
if (responseRepresentationVersion && requestedRepresentationVersion
|
|
765
|
+
&& responseRepresentationVersion !== requestedRepresentationVersion) {
|
|
766
|
+
representationMismatchReasons.push('representation_version');
|
|
767
|
+
}
|
|
768
|
+
if (responseEmbeddingModelId && requestedEmbeddingModelId
|
|
769
|
+
&& responseEmbeddingModelId !== requestedEmbeddingModelId) {
|
|
770
|
+
representationMismatchReasons.push('embedding_model_id');
|
|
771
|
+
}
|
|
772
|
+
if (responseEmbeddingDimension > 0 && requestedEmbeddingDimension > 0
|
|
773
|
+
&& responseEmbeddingDimension !== requestedEmbeddingDimension) {
|
|
774
|
+
representationMismatchReasons.push('embedding_dimension');
|
|
775
|
+
}
|
|
776
|
+
if (responseIndexSignature && requestedIndexSignature
|
|
777
|
+
&& responseIndexSignature !== requestedIndexSignature) {
|
|
778
|
+
representationMismatchReasons.push('index_signature');
|
|
779
|
+
}
|
|
780
|
+
const responseRepresentationMetadataProvided = (Boolean(responseRepresentationVersion)
|
|
781
|
+
|| Boolean(responseEmbeddingModelId)
|
|
782
|
+
|| responseEmbeddingDimension > 0
|
|
783
|
+
|| Boolean(responseIndexSignature));
|
|
784
|
+
const requestRepresentationMetadataProvided = (Boolean(requestedRepresentationVersion)
|
|
785
|
+
|| Boolean(requestedEmbeddingModelId)
|
|
786
|
+
|| requestedEmbeddingDimension > 0
|
|
787
|
+
|| Boolean(requestedIndexSignature));
|
|
788
|
+
if (responseRepresentationStatus !== 'unknown') {
|
|
789
|
+
representationStatus = responseRepresentationStatus;
|
|
790
|
+
representationStatusReason = responseRepresentationStatusReason
|
|
791
|
+
|| (responseRepresentationStatus === 'aligned'
|
|
792
|
+
? 'external_http_representation_aligned'
|
|
793
|
+
: 'external_http_representation_mismatch');
|
|
794
|
+
}
|
|
795
|
+
else if (representationMismatchReasons.length > 0) {
|
|
796
|
+
representationStatus = 'mismatch';
|
|
797
|
+
representationStatusReason = `mismatch:${representationMismatchReasons.join(',')}`;
|
|
798
|
+
}
|
|
799
|
+
else if (responseRepresentationMetadataProvided) {
|
|
800
|
+
representationStatus = 'aligned';
|
|
801
|
+
representationStatusReason = responseRepresentationStatusReason
|
|
802
|
+
|| 'external_http_representation_metadata_aligned';
|
|
803
|
+
}
|
|
804
|
+
else if (requestRepresentationMetadataProvided) {
|
|
805
|
+
representationStatus = 'unknown';
|
|
806
|
+
representationStatusReason = 'external_http_response_representation_metadata_missing';
|
|
807
|
+
}
|
|
808
|
+
else {
|
|
809
|
+
representationStatus = 'unknown';
|
|
810
|
+
representationStatusReason = 'representation_metadata_unavailable';
|
|
811
|
+
}
|
|
812
|
+
const candidateNormalization = normalizeExternalHttpCandidateIds(payload?.candidateAtomIds, input);
|
|
813
|
+
const candidateAtomIds = candidateNormalization.candidateAtomIds;
|
|
814
|
+
if (candidateNormalization.rejectedCount > 0) {
|
|
815
|
+
const rejectedReason = `candidate_ids_out_of_scope:${candidateNormalization.rejectedCount}`;
|
|
816
|
+
representationStatusReason = representationStatusReason
|
|
817
|
+
? `${representationStatusReason}|${rejectedReason}`
|
|
818
|
+
: rejectedReason;
|
|
819
|
+
}
|
|
820
|
+
const used = payload?.used === true
|
|
821
|
+
&& normalizedMode !== 'full_scan'
|
|
822
|
+
&& candidateAtomIds.length > 0;
|
|
823
|
+
prefilterEffectivenessRatio = used ? candidateNormalization.prefilterRatio : 1;
|
|
824
|
+
lastTotalAtomsInScope = used
|
|
825
|
+
? candidateNormalization.totalAtomsInScope
|
|
826
|
+
: Math.max(1, Math.floor(Number(input.atomCount || 0)));
|
|
827
|
+
consecutiveFailures = 0;
|
|
828
|
+
circuitState = 'closed';
|
|
829
|
+
lastErrorCode = '';
|
|
830
|
+
successCount += 1;
|
|
831
|
+
lastSuccessAt = new Date().toISOString();
|
|
832
|
+
if (halfOpenProbeActive) {
|
|
833
|
+
halfOpenProbeSuccessCount += 1;
|
|
834
|
+
}
|
|
835
|
+
updateHealth({
|
|
836
|
+
status: 'ready',
|
|
837
|
+
message: (`external_http_ok:${endpoint}:attempt=${currentAttempt}:`
|
|
838
|
+
+ `requestId=${connectorRequestId || clientRequestId}:circuit=${circuitState}`),
|
|
839
|
+
checkedAt: new Date().toISOString(),
|
|
840
|
+
});
|
|
841
|
+
return {
|
|
842
|
+
used,
|
|
843
|
+
candidateAtomIds: used ? candidateAtomIds : [],
|
|
844
|
+
mode: used ? normalizedMode : 'full_scan',
|
|
845
|
+
representation: {
|
|
846
|
+
version: representationVersion || undefined,
|
|
847
|
+
embeddingModelId: embeddingModelId || undefined,
|
|
848
|
+
embeddingDimension: embeddingDimension > 0 ? embeddingDimension : undefined,
|
|
849
|
+
indexSignature: indexSignature || undefined,
|
|
850
|
+
validated: representationStatus !== 'mismatch',
|
|
851
|
+
},
|
|
852
|
+
prefilterMetrics: {
|
|
853
|
+
candidatesReturned: used ? candidateAtomIds.length : 0,
|
|
854
|
+
totalAtomsInScope: candidateNormalization.totalAtomsInScope,
|
|
855
|
+
prefilterRatio: used ? candidateNormalization.prefilterRatio : 1,
|
|
856
|
+
signatureMatch: !representationMismatchReasons.includes('index_signature'),
|
|
857
|
+
},
|
|
858
|
+
};
|
|
859
|
+
}
|
|
860
|
+
catch (error) {
|
|
861
|
+
lastError = error;
|
|
862
|
+
const transient = isExternalHttpTransientFailure(error);
|
|
863
|
+
const finalAttempt = currentAttempt >= totalAttempts;
|
|
864
|
+
if (!connectorRequestId) {
|
|
865
|
+
const requestIdMatch = String(error?.message || '').match(/requestId=([A-Za-z0-9._:-]+)/i);
|
|
866
|
+
connectorRequestId = normalizeExternalHttpRequestId(requestIdMatch?.[1], clientRequestId);
|
|
867
|
+
}
|
|
868
|
+
if (connectorRequestId) {
|
|
869
|
+
lastRequestId = connectorRequestId;
|
|
870
|
+
}
|
|
871
|
+
if (!connectorErrorCode) {
|
|
872
|
+
const errorCodeMatch = String(error?.message || '').match(/errorCode=([a-z0-9:_-]+)/i);
|
|
873
|
+
connectorErrorCode = normalizeExternalHttpErrorCode(errorCodeMatch?.[1], '');
|
|
874
|
+
}
|
|
875
|
+
if (connectorErrorCode) {
|
|
876
|
+
lastErrorCode = connectorErrorCode;
|
|
877
|
+
}
|
|
878
|
+
if (responseRetryAfterMs <= 0) {
|
|
879
|
+
const retryAfterMatch = String(error?.message || '').match(/retryAfterMs=(\d+)/i);
|
|
880
|
+
const retryAfterCandidate = Number(retryAfterMatch?.[1] || 0);
|
|
881
|
+
if (Number.isFinite(retryAfterCandidate) && retryAfterCandidate > 0) {
|
|
882
|
+
responseRetryAfterMs = Math.min(EXTERNAL_HTTP_RETRY_AFTER_MS_MAX, Math.floor(retryAfterCandidate));
|
|
883
|
+
}
|
|
884
|
+
}
|
|
885
|
+
if (responseRetryAfterMs > 0) {
|
|
886
|
+
lastRetryAfterMs = responseRetryAfterMs;
|
|
887
|
+
}
|
|
888
|
+
const errorMessage = truncateMessage(error?.message || error, 200);
|
|
889
|
+
if (!finalAttempt && transient) {
|
|
890
|
+
retryCount += 1;
|
|
891
|
+
const defaultBackoffDelay = Math.min(EXTERNAL_HTTP_RETRY_AFTER_MS_MAX, retryDelayMs * Math.max(1, 2 ** attemptIndex));
|
|
892
|
+
const effectiveBackoffDelay = Math.max(defaultBackoffDelay, responseRetryAfterMs);
|
|
893
|
+
updateHealth({
|
|
894
|
+
status: 'degraded',
|
|
895
|
+
message: (`external_http_retrying:${currentAttempt}/${totalAttempts}:${errorMessage}:`
|
|
896
|
+
+ `backoffMs=${effectiveBackoffDelay}:retryAfterMs=${responseRetryAfterMs}:`
|
|
897
|
+
+ `requestId=${connectorRequestId || clientRequestId}`),
|
|
898
|
+
checkedAt: new Date().toISOString(),
|
|
899
|
+
});
|
|
900
|
+
await waitForRetryDelay(effectiveBackoffDelay);
|
|
901
|
+
continue;
|
|
902
|
+
}
|
|
903
|
+
consecutiveFailures += 1;
|
|
904
|
+
if (consecutiveFailures >= circuitFailureThreshold) {
|
|
905
|
+
circuitState = 'open';
|
|
906
|
+
circuitOpenedAtMs = Date.now();
|
|
907
|
+
circuitOpenedAt = new Date().toISOString();
|
|
908
|
+
}
|
|
909
|
+
else if (circuitState === 'half_open') {
|
|
910
|
+
circuitState = 'open';
|
|
911
|
+
circuitOpenedAtMs = Date.now();
|
|
912
|
+
circuitOpenedAt = new Date().toISOString();
|
|
913
|
+
}
|
|
914
|
+
if (halfOpenProbeActive) {
|
|
915
|
+
halfOpenProbeFailureCount += 1;
|
|
916
|
+
}
|
|
917
|
+
failureCount += 1;
|
|
918
|
+
lastFailureAt = new Date().toISOString();
|
|
919
|
+
updateHealth({
|
|
920
|
+
status: circuitState === 'open' ? 'unavailable' : (transient ? 'degraded' : 'unavailable'),
|
|
921
|
+
message: (`external_http_failed:${currentAttempt}/${totalAttempts}:${errorMessage}:`
|
|
922
|
+
+ `failures=${consecutiveFailures}:circuit=${circuitState}:`
|
|
923
|
+
+ `errorCode=${connectorErrorCode || 'none'}:retryAfterMs=${responseRetryAfterMs}:`
|
|
924
|
+
+ `requestId=${connectorRequestId || clientRequestId}`),
|
|
925
|
+
checkedAt: new Date().toISOString(),
|
|
926
|
+
});
|
|
927
|
+
throw error;
|
|
928
|
+
}
|
|
929
|
+
finally {
|
|
930
|
+
clearTimeout(timer);
|
|
931
|
+
}
|
|
932
|
+
}
|
|
933
|
+
throw (lastError || new Error('external_http_unknown_failure'));
|
|
934
|
+
},
|
|
935
|
+
getHealth() {
|
|
936
|
+
return {
|
|
937
|
+
...lastHealth,
|
|
938
|
+
checkedAt: String(lastHealth.checkedAt || '').trim() || new Date().toISOString(),
|
|
939
|
+
};
|
|
940
|
+
},
|
|
941
|
+
};
|
|
942
|
+
}
|