@juspay/neurolink 7.7.1 → 7.9.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/CHANGELOG.md +25 -2
- package/README.md +34 -2
- package/dist/cli/commands/config.d.ts +3 -3
- package/dist/cli/commands/sagemaker.d.ts +11 -0
- package/dist/cli/commands/sagemaker.js +778 -0
- package/dist/cli/factories/commandFactory.js +7 -2
- package/dist/cli/index.js +3 -0
- package/dist/cli/utils/interactiveSetup.js +28 -0
- package/dist/core/baseProvider.d.ts +2 -2
- package/dist/core/types.d.ts +16 -4
- package/dist/core/types.js +24 -3
- package/dist/factories/providerFactory.js +10 -1
- package/dist/factories/providerRegistry.js +6 -1
- package/dist/lib/core/baseProvider.d.ts +2 -2
- package/dist/lib/core/types.d.ts +16 -4
- package/dist/lib/core/types.js +24 -3
- package/dist/lib/factories/providerFactory.js +10 -1
- package/dist/lib/factories/providerRegistry.js +6 -1
- package/dist/lib/neurolink.d.ts +15 -0
- package/dist/lib/neurolink.js +73 -1
- package/dist/lib/providers/amazonSagemaker.d.ts +67 -0
- package/dist/lib/providers/amazonSagemaker.js +149 -0
- package/dist/lib/providers/googleVertex.d.ts +4 -0
- package/dist/lib/providers/googleVertex.js +44 -3
- package/dist/lib/providers/index.d.ts +4 -0
- package/dist/lib/providers/index.js +4 -0
- package/dist/lib/providers/sagemaker/adaptive-semaphore.d.ts +86 -0
- package/dist/lib/providers/sagemaker/adaptive-semaphore.js +212 -0
- package/dist/lib/providers/sagemaker/client.d.ts +156 -0
- package/dist/lib/providers/sagemaker/client.js +462 -0
- package/dist/lib/providers/sagemaker/config.d.ts +73 -0
- package/dist/lib/providers/sagemaker/config.js +308 -0
- package/dist/lib/providers/sagemaker/detection.d.ts +176 -0
- package/dist/lib/providers/sagemaker/detection.js +596 -0
- package/dist/lib/providers/sagemaker/diagnostics.d.ts +37 -0
- package/dist/lib/providers/sagemaker/diagnostics.js +137 -0
- package/dist/lib/providers/sagemaker/error-constants.d.ts +78 -0
- package/dist/lib/providers/sagemaker/error-constants.js +227 -0
- package/dist/lib/providers/sagemaker/errors.d.ts +83 -0
- package/dist/lib/providers/sagemaker/errors.js +216 -0
- package/dist/lib/providers/sagemaker/index.d.ts +35 -0
- package/dist/lib/providers/sagemaker/index.js +67 -0
- package/dist/lib/providers/sagemaker/language-model.d.ts +182 -0
- package/dist/lib/providers/sagemaker/language-model.js +755 -0
- package/dist/lib/providers/sagemaker/parsers.d.ts +136 -0
- package/dist/lib/providers/sagemaker/parsers.js +625 -0
- package/dist/lib/providers/sagemaker/streaming.d.ts +39 -0
- package/dist/lib/providers/sagemaker/streaming.js +320 -0
- package/dist/lib/providers/sagemaker/structured-parser.d.ts +117 -0
- package/dist/lib/providers/sagemaker/structured-parser.js +625 -0
- package/dist/lib/providers/sagemaker/types.d.ts +456 -0
- package/dist/lib/providers/sagemaker/types.js +7 -0
- package/dist/lib/sdk/toolRegistration.d.ts +1 -1
- package/dist/lib/sdk/toolRegistration.js +13 -5
- package/dist/lib/types/cli.d.ts +36 -1
- package/dist/lib/utils/providerHealth.js +19 -4
- package/dist/neurolink.d.ts +15 -0
- package/dist/neurolink.js +73 -1
- package/dist/providers/amazonSagemaker.d.ts +67 -0
- package/dist/providers/amazonSagemaker.js +149 -0
- package/dist/providers/googleVertex.d.ts +4 -0
- package/dist/providers/googleVertex.js +44 -3
- package/dist/providers/index.d.ts +4 -0
- package/dist/providers/index.js +4 -0
- package/dist/providers/sagemaker/adaptive-semaphore.d.ts +86 -0
- package/dist/providers/sagemaker/adaptive-semaphore.js +212 -0
- package/dist/providers/sagemaker/client.d.ts +156 -0
- package/dist/providers/sagemaker/client.js +462 -0
- package/dist/providers/sagemaker/config.d.ts +73 -0
- package/dist/providers/sagemaker/config.js +308 -0
- package/dist/providers/sagemaker/detection.d.ts +176 -0
- package/dist/providers/sagemaker/detection.js +596 -0
- package/dist/providers/sagemaker/diagnostics.d.ts +37 -0
- package/dist/providers/sagemaker/diagnostics.js +137 -0
- package/dist/providers/sagemaker/error-constants.d.ts +78 -0
- package/dist/providers/sagemaker/error-constants.js +227 -0
- package/dist/providers/sagemaker/errors.d.ts +83 -0
- package/dist/providers/sagemaker/errors.js +216 -0
- package/dist/providers/sagemaker/index.d.ts +35 -0
- package/dist/providers/sagemaker/index.js +67 -0
- package/dist/providers/sagemaker/language-model.d.ts +182 -0
- package/dist/providers/sagemaker/language-model.js +755 -0
- package/dist/providers/sagemaker/parsers.d.ts +136 -0
- package/dist/providers/sagemaker/parsers.js +625 -0
- package/dist/providers/sagemaker/streaming.d.ts +39 -0
- package/dist/providers/sagemaker/streaming.js +320 -0
- package/dist/providers/sagemaker/structured-parser.d.ts +117 -0
- package/dist/providers/sagemaker/structured-parser.js +625 -0
- package/dist/providers/sagemaker/types.d.ts +456 -0
- package/dist/providers/sagemaker/types.js +7 -0
- package/dist/sdk/toolRegistration.d.ts +1 -1
- package/dist/sdk/toolRegistration.js +13 -5
- package/dist/types/cli.d.ts +36 -1
- package/dist/utils/providerHealth.js +19 -4
- package/package.json +8 -2
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Amazon SageMaker Provider Implementation (Simplified)
|
|
3
|
+
*
|
|
4
|
+
* This module provides a simplified SageMaker provider that extends BaseProvider
|
|
5
|
+
* and integrates with the NeuroLink ecosystem using existing patterns.
|
|
6
|
+
*/
|
|
7
|
+
import { BaseProvider } from "../core/baseProvider.js";
|
|
8
|
+
import { logger } from "../utils/logger.js";
|
|
9
|
+
// SageMaker-specific imports
|
|
10
|
+
import { getSageMakerConfig, getSageMakerModelConfig, getDefaultSageMakerEndpoint, getSageMakerModel, } from "./sagemaker/config.js";
|
|
11
|
+
import { handleSageMakerError, SageMakerError } from "./sagemaker/errors.js";
|
|
12
|
+
import { SageMakerLanguageModel } from "./sagemaker/language-model.js";
|
|
13
|
+
/**
|
|
14
|
+
* Amazon SageMaker Provider extending BaseProvider
|
|
15
|
+
*/
|
|
16
|
+
export class AmazonSageMakerProvider extends BaseProvider {
|
|
17
|
+
sagemakerModel;
|
|
18
|
+
sagemakerConfig;
|
|
19
|
+
modelConfig;
|
|
20
|
+
constructor(modelName, endpointName) {
|
|
21
|
+
super(modelName, "sagemaker");
|
|
22
|
+
try {
|
|
23
|
+
// Load and validate configuration
|
|
24
|
+
this.sagemakerConfig = getSageMakerConfig();
|
|
25
|
+
this.modelConfig = getSageMakerModelConfig(endpointName || getDefaultSageMakerEndpoint());
|
|
26
|
+
// Create the proper LanguageModel (v2) implementation
|
|
27
|
+
this.sagemakerModel = new SageMakerLanguageModel(this.modelName, this.sagemakerConfig, this.modelConfig);
|
|
28
|
+
logger.debug("Amazon SageMaker Provider initialized", {
|
|
29
|
+
modelName: this.modelName,
|
|
30
|
+
endpointName: this.modelConfig.endpointName,
|
|
31
|
+
region: this.sagemakerConfig.region,
|
|
32
|
+
provider: this.providerName,
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
catch (error) {
|
|
36
|
+
logger.error("Failed to initialize SageMaker provider", {
|
|
37
|
+
error: error instanceof Error ? error.message : String(error),
|
|
38
|
+
modelName,
|
|
39
|
+
endpointName,
|
|
40
|
+
});
|
|
41
|
+
throw handleSageMakerError(error);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
getProviderName() {
|
|
45
|
+
return "sagemaker";
|
|
46
|
+
}
|
|
47
|
+
getDefaultModel() {
|
|
48
|
+
return getSageMakerModel();
|
|
49
|
+
}
|
|
50
|
+
getAISDKModel() {
|
|
51
|
+
return this.sagemakerModel;
|
|
52
|
+
}
|
|
53
|
+
async executeStream(options, analysisSchema) {
|
|
54
|
+
try {
|
|
55
|
+
// For now, throw an error indicating this is not yet implemented
|
|
56
|
+
throw new SageMakerError("SageMaker streaming not yet fully implemented. Coming in next phase.", "MODEL_ERROR", 501, undefined, this.modelConfig.endpointName);
|
|
57
|
+
}
|
|
58
|
+
catch (error) {
|
|
59
|
+
throw this.handleProviderError(error);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
handleProviderError(error) {
|
|
63
|
+
if (error instanceof SageMakerError) {
|
|
64
|
+
return error;
|
|
65
|
+
}
|
|
66
|
+
if (error instanceof Error && error.name === "TimeoutError") {
|
|
67
|
+
return new SageMakerError(`SageMaker request timed out. Consider increasing timeout.`, "NETWORK_ERROR", 408, error, this.modelConfig.endpointName);
|
|
68
|
+
}
|
|
69
|
+
return handleSageMakerError(error, this.modelConfig.endpointName);
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Get SageMaker-specific provider information
|
|
73
|
+
*/
|
|
74
|
+
getSageMakerInfo() {
|
|
75
|
+
return {
|
|
76
|
+
endpointName: this.modelConfig.endpointName,
|
|
77
|
+
modelType: this.modelConfig.modelType || "custom",
|
|
78
|
+
region: this.sagemakerConfig.region,
|
|
79
|
+
configured: !!(this.sagemakerConfig.accessKeyId && this.sagemakerConfig.secretAccessKey),
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Test basic configuration
|
|
84
|
+
*/
|
|
85
|
+
async testConnection() {
|
|
86
|
+
try {
|
|
87
|
+
// Basic validation test
|
|
88
|
+
if (!this.sagemakerConfig.accessKeyId ||
|
|
89
|
+
!this.sagemakerConfig.secretAccessKey) {
|
|
90
|
+
return {
|
|
91
|
+
connected: false,
|
|
92
|
+
error: "AWS credentials not configured",
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
if (!this.modelConfig.endpointName ||
|
|
96
|
+
this.modelConfig.endpointName === "default-endpoint") {
|
|
97
|
+
return {
|
|
98
|
+
connected: false,
|
|
99
|
+
error: "SageMaker endpoint not configured",
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
// For now, just return that configuration looks valid
|
|
103
|
+
return {
|
|
104
|
+
connected: true,
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
catch (error) {
|
|
108
|
+
return {
|
|
109
|
+
connected: false,
|
|
110
|
+
error: error instanceof Error ? error.message : String(error),
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* Public method to get the AI SDK model for CLI and external usage
|
|
116
|
+
*/
|
|
117
|
+
async getModel() {
|
|
118
|
+
return this.getAISDKModel();
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
121
|
+
* Test connectivity to the SageMaker endpoint
|
|
122
|
+
*/
|
|
123
|
+
async testConnectivity() {
|
|
124
|
+
const model = this.sagemakerModel;
|
|
125
|
+
return model.testConnectivity
|
|
126
|
+
? await model.testConnectivity()
|
|
127
|
+
: { success: false, error: "Test method not available" };
|
|
128
|
+
}
|
|
129
|
+
/**
|
|
130
|
+
* Get model capabilities and information
|
|
131
|
+
*/
|
|
132
|
+
getModelCapabilities() {
|
|
133
|
+
const model = this.sagemakerModel;
|
|
134
|
+
return model.getModelCapabilities
|
|
135
|
+
? model.getModelCapabilities()
|
|
136
|
+
: {
|
|
137
|
+
capabilities: {
|
|
138
|
+
streaming: true,
|
|
139
|
+
toolCalling: true,
|
|
140
|
+
structuredOutput: true,
|
|
141
|
+
batchInference: true,
|
|
142
|
+
supportedResponseFormats: ["text", "json_object"],
|
|
143
|
+
supportedToolTypes: ["function"],
|
|
144
|
+
maxBatchSize: 10,
|
|
145
|
+
},
|
|
146
|
+
};
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
export default AmazonSageMakerProvider;
|
|
@@ -108,6 +108,10 @@ export declare class GoogleVertexProvider extends BaseProvider {
|
|
|
108
108
|
maxTokens: number;
|
|
109
109
|
};
|
|
110
110
|
};
|
|
111
|
+
/**
|
|
112
|
+
* Get model suggestions when a model is not found
|
|
113
|
+
*/
|
|
114
|
+
private getModelSuggestions;
|
|
111
115
|
}
|
|
112
116
|
export default GoogleVertexProvider;
|
|
113
117
|
export { GoogleVertexProvider as GoogleVertexAI };
|
|
@@ -40,9 +40,9 @@ const getVertexLocation = () => {
|
|
|
40
40
|
"us-central1");
|
|
41
41
|
};
|
|
42
42
|
const getDefaultVertexModel = () => {
|
|
43
|
-
// Use gemini-
|
|
43
|
+
// Use gemini-2.5-flash as default - latest and best price-performance model
|
|
44
44
|
// Override with VERTEX_MODEL environment variable if needed
|
|
45
|
-
return process.env.VERTEX_MODEL || "gemini-
|
|
45
|
+
return process.env.VERTEX_MODEL || "gemini-2.5-flash";
|
|
46
46
|
};
|
|
47
47
|
const hasGoogleCredentials = () => {
|
|
48
48
|
return !!(process.env.GOOGLE_APPLICATION_CREDENTIALS ||
|
|
@@ -276,7 +276,8 @@ export class GoogleVertexProvider extends BaseProvider {
|
|
|
276
276
|
return new Error(`❌ Google Vertex AI Permission Denied\n\nYour Google Cloud credentials don't have permission to access Vertex AI.\n\nRequired Steps:\n1. Ensure your service account has Vertex AI User role\n2. Check if Vertex AI API is enabled in your project\n3. Verify your project ID is correct\n4. Confirm your location/region has Vertex AI available`);
|
|
277
277
|
}
|
|
278
278
|
if (message.includes("NOT_FOUND")) {
|
|
279
|
-
|
|
279
|
+
const modelSuggestions = this.getModelSuggestions(this.modelName);
|
|
280
|
+
return new Error(`❌ Google Vertex AI Model Not Found\n\n${message}\n\nModel '${this.modelName}' is not available.\n\nSuggested alternatives:\n${modelSuggestions}\n\nTroubleshooting:\n1. Check model name spelling and format\n2. Verify model is available in your region (${this.location})\n3. Ensure your project has access to the model\n4. For Claude models, enable Anthropic integration in Google Cloud Console`);
|
|
280
281
|
}
|
|
281
282
|
if (message.includes("QUOTA_EXCEEDED")) {
|
|
282
283
|
return new Error(`❌ Google Vertex AI Quota Exceeded\n\n${message}\n\nSolutions:\n1. Check your Vertex AI quotas in Google Cloud Console\n2. Request quota increase if needed\n3. Try a different model or reduce request frequency\n4. Consider using a different region`);
|
|
@@ -520,6 +521,46 @@ export class GoogleVertexProvider extends BaseProvider {
|
|
|
520
521
|
},
|
|
521
522
|
};
|
|
522
523
|
}
|
|
524
|
+
/**
|
|
525
|
+
* Get model suggestions when a model is not found
|
|
526
|
+
*/
|
|
527
|
+
getModelSuggestions(requestedModel) {
|
|
528
|
+
const availableModels = {
|
|
529
|
+
google: [
|
|
530
|
+
"gemini-2.5-pro",
|
|
531
|
+
"gemini-2.5-flash",
|
|
532
|
+
"gemini-2.5-flash-lite",
|
|
533
|
+
"gemini-2.0-flash-001",
|
|
534
|
+
"gemini-1.5-pro",
|
|
535
|
+
"gemini-1.5-flash"
|
|
536
|
+
],
|
|
537
|
+
claude: [
|
|
538
|
+
"claude-sonnet-4@20250514",
|
|
539
|
+
"claude-opus-4@20250514",
|
|
540
|
+
"claude-3-5-sonnet-20241022",
|
|
541
|
+
"claude-3-5-haiku-20241022",
|
|
542
|
+
"claude-3-sonnet-20240229",
|
|
543
|
+
"claude-3-haiku-20240307",
|
|
544
|
+
"claude-3-opus-20240229",
|
|
545
|
+
],
|
|
546
|
+
};
|
|
547
|
+
let suggestions = "\n🤖 Google Models (always available):\n";
|
|
548
|
+
availableModels.google.forEach((model) => {
|
|
549
|
+
suggestions += ` • ${model}\n`;
|
|
550
|
+
});
|
|
551
|
+
suggestions += "\n🧠 Claude Models (requires Anthropic integration):\n";
|
|
552
|
+
availableModels.claude.forEach((model) => {
|
|
553
|
+
suggestions += ` • ${model}\n`;
|
|
554
|
+
});
|
|
555
|
+
// If the requested model looks like a Claude model, provide specific guidance
|
|
556
|
+
if (requestedModel && requestedModel.toLowerCase().includes("claude")) {
|
|
557
|
+
suggestions += `\n💡 Tip: "${requestedModel}" appears to be a Claude model.\n`;
|
|
558
|
+
suggestions +=
|
|
559
|
+
"Ensure Anthropic integration is enabled in your Google Cloud project.\n";
|
|
560
|
+
suggestions += "Try using an available Claude model from the list above.";
|
|
561
|
+
}
|
|
562
|
+
return suggestions;
|
|
563
|
+
}
|
|
523
564
|
}
|
|
524
565
|
export default GoogleVertexProvider;
|
|
525
566
|
// Re-export for compatibility
|
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
*/
|
|
5
5
|
export { GoogleVertexProvider as GoogleVertexAI } from "./googleVertex.js";
|
|
6
6
|
export { AmazonBedrockProvider as AmazonBedrock } from "./amazonBedrock.js";
|
|
7
|
+
export { AmazonSageMakerProvider as AmazonSageMaker } from "./amazonSagemaker.js";
|
|
7
8
|
export { OpenAIProvider as OpenAI } from "./openAI.js";
|
|
8
9
|
export { OpenAICompatibleProvider as OpenAICompatible } from "./openaiCompatible.js";
|
|
9
10
|
export { AnthropicProvider as AnthropicProvider } from "./anthropic.js";
|
|
@@ -12,6 +13,7 @@ export { GoogleAIStudioProvider as GoogleAIStudio } from "./googleAiStudio.js";
|
|
|
12
13
|
export { HuggingFaceProvider as HuggingFace } from "./huggingFace.js";
|
|
13
14
|
export { OllamaProvider as Ollama } from "./ollama.js";
|
|
14
15
|
export { MistralProvider as MistralAI } from "./mistral.js";
|
|
16
|
+
export { LiteLLMProvider as LiteLLM } from "./litellm.js";
|
|
15
17
|
export type { AIProvider } from "../core/types.js";
|
|
16
18
|
/**
|
|
17
19
|
* Provider registry for dynamic provider instantiation
|
|
@@ -19,6 +21,7 @@ export type { AIProvider } from "../core/types.js";
|
|
|
19
21
|
export declare const PROVIDERS: {
|
|
20
22
|
readonly vertex: "GoogleVertexAI";
|
|
21
23
|
readonly bedrock: "AmazonBedrock";
|
|
24
|
+
readonly sagemaker: "AmazonSageMaker";
|
|
22
25
|
readonly openai: "OpenAI";
|
|
23
26
|
readonly "openai-compatible": "OpenAICompatible";
|
|
24
27
|
readonly anthropic: "AnthropicProvider";
|
|
@@ -27,6 +30,7 @@ export declare const PROVIDERS: {
|
|
|
27
30
|
readonly huggingface: "HuggingFace";
|
|
28
31
|
readonly ollama: "Ollama";
|
|
29
32
|
readonly mistral: "MistralAI";
|
|
33
|
+
readonly litellm: "LiteLLM";
|
|
30
34
|
};
|
|
31
35
|
/**
|
|
32
36
|
* Type for valid provider names
|
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
*/
|
|
5
5
|
export { GoogleVertexProvider as GoogleVertexAI } from "./googleVertex.js";
|
|
6
6
|
export { AmazonBedrockProvider as AmazonBedrock } from "./amazonBedrock.js";
|
|
7
|
+
export { AmazonSageMakerProvider as AmazonSageMaker } from "./amazonSagemaker.js";
|
|
7
8
|
export { OpenAIProvider as OpenAI } from "./openAI.js";
|
|
8
9
|
export { OpenAICompatibleProvider as OpenAICompatible } from "./openaiCompatible.js";
|
|
9
10
|
export { AnthropicProvider as AnthropicProvider } from "./anthropic.js";
|
|
@@ -12,12 +13,14 @@ export { GoogleAIStudioProvider as GoogleAIStudio } from "./googleAiStudio.js";
|
|
|
12
13
|
export { HuggingFaceProvider as HuggingFace } from "./huggingFace.js";
|
|
13
14
|
export { OllamaProvider as Ollama } from "./ollama.js";
|
|
14
15
|
export { MistralProvider as MistralAI } from "./mistral.js";
|
|
16
|
+
export { LiteLLMProvider as LiteLLM } from "./litellm.js";
|
|
15
17
|
/**
|
|
16
18
|
* Provider registry for dynamic provider instantiation
|
|
17
19
|
*/
|
|
18
20
|
export const PROVIDERS = {
|
|
19
21
|
vertex: "GoogleVertexAI",
|
|
20
22
|
bedrock: "AmazonBedrock",
|
|
23
|
+
sagemaker: "AmazonSageMaker",
|
|
21
24
|
openai: "OpenAI",
|
|
22
25
|
"openai-compatible": "OpenAICompatible",
|
|
23
26
|
anthropic: "AnthropicProvider",
|
|
@@ -26,6 +29,7 @@ export const PROVIDERS = {
|
|
|
26
29
|
huggingface: "HuggingFace",
|
|
27
30
|
ollama: "Ollama",
|
|
28
31
|
mistral: "MistralAI",
|
|
32
|
+
litellm: "LiteLLM",
|
|
29
33
|
};
|
|
30
34
|
/**
|
|
31
35
|
* List of all available provider names
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Adaptive Semaphore Utility
|
|
3
|
+
*
|
|
4
|
+
* Provides a sophisticated semaphore implementation with dynamic concurrency adjustment
|
|
5
|
+
* for optimal resource utilization and performance tuning based on response times and error rates.
|
|
6
|
+
*/
|
|
7
|
+
export interface AdaptiveSemaphoreConfig {
|
|
8
|
+
initialConcurrency: number;
|
|
9
|
+
maxConcurrency: number;
|
|
10
|
+
minConcurrency: number;
|
|
11
|
+
}
|
|
12
|
+
export interface AdaptiveSemaphoreMetrics {
|
|
13
|
+
activeRequests: number;
|
|
14
|
+
currentConcurrency: number;
|
|
15
|
+
completedCount: number;
|
|
16
|
+
errorCount: number;
|
|
17
|
+
averageResponseTime: number;
|
|
18
|
+
waitingCount: number;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Adaptive semaphore that automatically adjusts concurrency based on performance metrics
|
|
22
|
+
*/
|
|
23
|
+
export declare class AdaptiveSemaphore {
|
|
24
|
+
private count;
|
|
25
|
+
private waiters;
|
|
26
|
+
private currentConcurrency;
|
|
27
|
+
private activeRequests;
|
|
28
|
+
private completedCount;
|
|
29
|
+
private errorCount;
|
|
30
|
+
private responseTimes;
|
|
31
|
+
private readonly maxConcurrency;
|
|
32
|
+
private readonly minConcurrency;
|
|
33
|
+
constructor(config: AdaptiveSemaphoreConfig);
|
|
34
|
+
/**
|
|
35
|
+
* Acquire a semaphore permit, waiting if necessary
|
|
36
|
+
*/
|
|
37
|
+
acquire(): Promise<void>;
|
|
38
|
+
/**
|
|
39
|
+
* Release a semaphore permit and wake up waiting requests
|
|
40
|
+
*/
|
|
41
|
+
release(): void;
|
|
42
|
+
/**
|
|
43
|
+
* Record successful completion with response time for adaptive adjustment
|
|
44
|
+
*/
|
|
45
|
+
recordSuccess(responseTimeMs: number): void;
|
|
46
|
+
/**
|
|
47
|
+
* Record error for adaptive adjustment
|
|
48
|
+
*/
|
|
49
|
+
recordError(responseTimeMs?: number): void;
|
|
50
|
+
/**
|
|
51
|
+
* Manually adjust concurrency level
|
|
52
|
+
*/
|
|
53
|
+
adjustConcurrency(newLimit: number): void;
|
|
54
|
+
/**
|
|
55
|
+
* Get current performance metrics
|
|
56
|
+
*/
|
|
57
|
+
getMetrics(): AdaptiveSemaphoreMetrics;
|
|
58
|
+
/**
|
|
59
|
+
* Reset metrics for new batch or session
|
|
60
|
+
*/
|
|
61
|
+
resetMetrics(): void;
|
|
62
|
+
/**
|
|
63
|
+
* Automatically adjust concurrency based on performance indicators
|
|
64
|
+
*/
|
|
65
|
+
private adjustConcurrencyBasedOnPerformance;
|
|
66
|
+
/**
|
|
67
|
+
* Check if semaphore is idle (no active or waiting requests)
|
|
68
|
+
*/
|
|
69
|
+
isIdle(): boolean;
|
|
70
|
+
/**
|
|
71
|
+
* Get current concurrency limit
|
|
72
|
+
*/
|
|
73
|
+
getCurrentConcurrency(): number;
|
|
74
|
+
/**
|
|
75
|
+
* Get number of active requests
|
|
76
|
+
*/
|
|
77
|
+
getActiveRequestCount(): number;
|
|
78
|
+
/**
|
|
79
|
+
* Get number of waiting requests
|
|
80
|
+
*/
|
|
81
|
+
getWaitingRequestCount(): number;
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Factory function to create an adaptive semaphore with default configuration
|
|
85
|
+
*/
|
|
86
|
+
export declare function createAdaptiveSemaphore(initialConcurrency: number, maxConcurrency?: number, minConcurrency?: number): AdaptiveSemaphore;
|
|
@@ -0,0 +1,212 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Adaptive Semaphore Utility
|
|
3
|
+
*
|
|
4
|
+
* Provides a sophisticated semaphore implementation with dynamic concurrency adjustment
|
|
5
|
+
* for optimal resource utilization and performance tuning based on response times and error rates.
|
|
6
|
+
*/
|
|
7
|
+
import { logger } from "../../utils/logger.js";
|
|
8
|
+
/**
|
|
9
|
+
* Adaptive semaphore that automatically adjusts concurrency based on performance metrics
|
|
10
|
+
*/
|
|
11
|
+
export class AdaptiveSemaphore {
|
|
12
|
+
count;
|
|
13
|
+
waiters = [];
|
|
14
|
+
currentConcurrency;
|
|
15
|
+
activeRequests = 0;
|
|
16
|
+
completedCount = 0;
|
|
17
|
+
errorCount = 0;
|
|
18
|
+
responseTimes = [];
|
|
19
|
+
maxConcurrency;
|
|
20
|
+
minConcurrency;
|
|
21
|
+
constructor(config) {
|
|
22
|
+
this.currentConcurrency = config.initialConcurrency;
|
|
23
|
+
this.count = config.initialConcurrency;
|
|
24
|
+
this.maxConcurrency = config.maxConcurrency;
|
|
25
|
+
this.minConcurrency = config.minConcurrency;
|
|
26
|
+
logger.debug("AdaptiveSemaphore initialized", {
|
|
27
|
+
initialConcurrency: config.initialConcurrency,
|
|
28
|
+
maxConcurrency: config.maxConcurrency,
|
|
29
|
+
minConcurrency: config.minConcurrency,
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Acquire a semaphore permit, waiting if necessary
|
|
34
|
+
*/
|
|
35
|
+
async acquire() {
|
|
36
|
+
return new Promise((resolve) => {
|
|
37
|
+
if (this.count > 0) {
|
|
38
|
+
this.count--;
|
|
39
|
+
this.activeRequests++;
|
|
40
|
+
resolve();
|
|
41
|
+
}
|
|
42
|
+
else {
|
|
43
|
+
this.waiters.push(() => {
|
|
44
|
+
this.count--;
|
|
45
|
+
this.activeRequests++;
|
|
46
|
+
resolve();
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Release a semaphore permit and wake up waiting requests
|
|
53
|
+
*/
|
|
54
|
+
release() {
|
|
55
|
+
this.activeRequests--;
|
|
56
|
+
if (this.waiters.length > 0) {
|
|
57
|
+
const waiter = this.waiters.shift();
|
|
58
|
+
waiter();
|
|
59
|
+
}
|
|
60
|
+
else {
|
|
61
|
+
this.count++;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Record successful completion with response time for adaptive adjustment
|
|
66
|
+
*/
|
|
67
|
+
recordSuccess(responseTimeMs) {
|
|
68
|
+
this.completedCount++;
|
|
69
|
+
this.responseTimes.push(responseTimeMs);
|
|
70
|
+
// Keep only recent response times for calculation (last 10 responses)
|
|
71
|
+
if (this.responseTimes.length > 10) {
|
|
72
|
+
this.responseTimes.shift();
|
|
73
|
+
}
|
|
74
|
+
this.adjustConcurrencyBasedOnPerformance(responseTimeMs, false);
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Record error for adaptive adjustment
|
|
78
|
+
*/
|
|
79
|
+
recordError(responseTimeMs) {
|
|
80
|
+
this.errorCount++;
|
|
81
|
+
if (responseTimeMs) {
|
|
82
|
+
this.responseTimes.push(responseTimeMs);
|
|
83
|
+
if (this.responseTimes.length > 10) {
|
|
84
|
+
this.responseTimes.shift();
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
this.adjustConcurrencyBasedOnPerformance(responseTimeMs || 0, true);
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Manually adjust concurrency level
|
|
91
|
+
*/
|
|
92
|
+
adjustConcurrency(newLimit) {
|
|
93
|
+
const clampedLimit = Math.max(this.minConcurrency, Math.min(this.maxConcurrency, newLimit));
|
|
94
|
+
const diff = clampedLimit - (this.currentConcurrency - this.count);
|
|
95
|
+
this.count += diff;
|
|
96
|
+
this.currentConcurrency = clampedLimit;
|
|
97
|
+
logger.debug("Concurrency adjusted", {
|
|
98
|
+
newConcurrency: clampedLimit,
|
|
99
|
+
previousConcurrency: this.currentConcurrency - diff,
|
|
100
|
+
availableCount: this.count,
|
|
101
|
+
activeRequests: this.activeRequests,
|
|
102
|
+
});
|
|
103
|
+
// Wake up waiting requests if we increased concurrency
|
|
104
|
+
while (this.count > 0 && this.waiters.length > 0) {
|
|
105
|
+
const waiter = this.waiters.shift();
|
|
106
|
+
this.count--;
|
|
107
|
+
this.activeRequests++;
|
|
108
|
+
waiter();
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* Get current performance metrics
|
|
113
|
+
*/
|
|
114
|
+
getMetrics() {
|
|
115
|
+
const averageResponseTime = this.responseTimes.length > 0
|
|
116
|
+
? this.responseTimes.reduce((sum, time) => sum + time, 0) /
|
|
117
|
+
this.responseTimes.length
|
|
118
|
+
: 0;
|
|
119
|
+
return {
|
|
120
|
+
activeRequests: this.activeRequests,
|
|
121
|
+
currentConcurrency: this.currentConcurrency,
|
|
122
|
+
completedCount: this.completedCount,
|
|
123
|
+
errorCount: this.errorCount,
|
|
124
|
+
averageResponseTime,
|
|
125
|
+
waitingCount: this.waiters.length,
|
|
126
|
+
};
|
|
127
|
+
}
|
|
128
|
+
/**
|
|
129
|
+
* Reset metrics for new batch or session
|
|
130
|
+
*/
|
|
131
|
+
resetMetrics() {
|
|
132
|
+
this.completedCount = 0;
|
|
133
|
+
this.errorCount = 0;
|
|
134
|
+
this.responseTimes = [];
|
|
135
|
+
}
|
|
136
|
+
/**
|
|
137
|
+
* Automatically adjust concurrency based on performance indicators
|
|
138
|
+
*/
|
|
139
|
+
adjustConcurrencyBasedOnPerformance(responseTimeMs, isError) {
|
|
140
|
+
const metrics = this.getMetrics();
|
|
141
|
+
if (isError) {
|
|
142
|
+
// On error, reduce concurrency to be more conservative
|
|
143
|
+
if (this.currentConcurrency > this.minConcurrency) {
|
|
144
|
+
this.adjustConcurrency(Math.max(this.minConcurrency, this.currentConcurrency - 1));
|
|
145
|
+
logger.warn("Reduced concurrency due to error", {
|
|
146
|
+
newConcurrency: this.currentConcurrency,
|
|
147
|
+
errorCount: this.errorCount,
|
|
148
|
+
});
|
|
149
|
+
}
|
|
150
|
+
return;
|
|
151
|
+
}
|
|
152
|
+
// Only adjust after we have some data to work with
|
|
153
|
+
if (this.completedCount < 3) {
|
|
154
|
+
return;
|
|
155
|
+
}
|
|
156
|
+
const fastResponseThreshold = 2000; // 2 seconds
|
|
157
|
+
const slowResponseThreshold = 5000; // 5 seconds
|
|
158
|
+
if (responseTimeMs < fastResponseThreshold &&
|
|
159
|
+
metrics.averageResponseTime < fastResponseThreshold &&
|
|
160
|
+
this.currentConcurrency < this.maxConcurrency) {
|
|
161
|
+
// Fast responses and no bottleneck - increase concurrency
|
|
162
|
+
this.adjustConcurrency(Math.min(this.maxConcurrency, this.currentConcurrency + 1));
|
|
163
|
+
logger.debug("Increased concurrency due to fast responses", {
|
|
164
|
+
newConcurrency: this.currentConcurrency,
|
|
165
|
+
averageResponseTime: metrics.averageResponseTime,
|
|
166
|
+
});
|
|
167
|
+
}
|
|
168
|
+
else if (responseTimeMs > slowResponseThreshold &&
|
|
169
|
+
this.currentConcurrency > this.minConcurrency) {
|
|
170
|
+
// Slow responses - decrease concurrency
|
|
171
|
+
this.adjustConcurrency(Math.max(this.minConcurrency, this.currentConcurrency - 1));
|
|
172
|
+
logger.debug("Decreased concurrency due to slow responses", {
|
|
173
|
+
newConcurrency: this.currentConcurrency,
|
|
174
|
+
responseTime: responseTimeMs,
|
|
175
|
+
});
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
/**
|
|
179
|
+
* Check if semaphore is idle (no active or waiting requests)
|
|
180
|
+
*/
|
|
181
|
+
isIdle() {
|
|
182
|
+
return this.activeRequests === 0 && this.waiters.length === 0;
|
|
183
|
+
}
|
|
184
|
+
/**
|
|
185
|
+
* Get current concurrency limit
|
|
186
|
+
*/
|
|
187
|
+
getCurrentConcurrency() {
|
|
188
|
+
return this.currentConcurrency;
|
|
189
|
+
}
|
|
190
|
+
/**
|
|
191
|
+
* Get number of active requests
|
|
192
|
+
*/
|
|
193
|
+
getActiveRequestCount() {
|
|
194
|
+
return this.activeRequests;
|
|
195
|
+
}
|
|
196
|
+
/**
|
|
197
|
+
* Get number of waiting requests
|
|
198
|
+
*/
|
|
199
|
+
getWaitingRequestCount() {
|
|
200
|
+
return this.waiters.length;
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
/**
|
|
204
|
+
* Factory function to create an adaptive semaphore with default configuration
|
|
205
|
+
*/
|
|
206
|
+
export function createAdaptiveSemaphore(initialConcurrency, maxConcurrency = 10, minConcurrency = 1) {
|
|
207
|
+
return new AdaptiveSemaphore({
|
|
208
|
+
initialConcurrency,
|
|
209
|
+
maxConcurrency,
|
|
210
|
+
minConcurrency,
|
|
211
|
+
});
|
|
212
|
+
}
|