@juspay/neurolink 7.48.1 → 7.50.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 +19 -0
- package/README.md +215 -16
- package/dist/agent/directTools.d.ts +55 -0
- package/dist/agent/directTools.js +266 -0
- package/dist/cli/factories/commandFactory.d.ts +6 -0
- package/dist/cli/factories/commandFactory.js +149 -16
- package/dist/cli/index.js +13 -2
- package/dist/cli/loop/conversationSelector.d.ts +45 -0
- package/dist/cli/loop/conversationSelector.js +222 -0
- package/dist/cli/loop/optionsSchema.d.ts +1 -1
- package/dist/cli/loop/session.d.ts +36 -8
- package/dist/cli/loop/session.js +257 -61
- package/dist/core/baseProvider.d.ts +9 -0
- package/dist/core/baseProvider.js +45 -5
- package/dist/core/evaluation.js +5 -2
- package/dist/factories/providerRegistry.js +2 -2
- package/dist/index.d.ts +8 -2
- package/dist/index.js +11 -10
- package/dist/lib/agent/directTools.d.ts +55 -0
- package/dist/lib/agent/directTools.js +266 -0
- package/dist/lib/core/baseProvider.d.ts +9 -0
- package/dist/lib/core/baseProvider.js +45 -5
- package/dist/lib/core/evaluation.js +5 -2
- package/dist/lib/factories/providerRegistry.js +2 -2
- package/dist/lib/index.d.ts +8 -2
- package/dist/lib/index.js +11 -10
- package/dist/lib/mcp/factory.d.ts +2 -157
- package/dist/lib/mcp/flexibleToolValidator.d.ts +1 -5
- package/dist/lib/mcp/index.d.ts +3 -2
- package/dist/lib/mcp/mcpCircuitBreaker.d.ts +1 -75
- package/dist/lib/mcp/mcpClientFactory.d.ts +1 -20
- package/dist/lib/mcp/mcpClientFactory.js +1 -0
- package/dist/lib/mcp/registry.d.ts +3 -10
- package/dist/lib/mcp/servers/agent/directToolsServer.d.ts +1 -1
- package/dist/lib/mcp/servers/aiProviders/aiCoreServer.d.ts +1 -1
- package/dist/lib/mcp/servers/utilities/utilityServer.d.ts +1 -1
- package/dist/lib/mcp/toolDiscoveryService.d.ts +3 -84
- package/dist/lib/mcp/toolRegistry.d.ts +2 -24
- package/dist/lib/middleware/builtin/guardrails.d.ts +5 -16
- package/dist/lib/middleware/builtin/guardrails.js +44 -39
- package/dist/lib/middleware/utils/guardrailsUtils.d.ts +64 -0
- package/dist/lib/middleware/utils/guardrailsUtils.js +387 -0
- package/dist/lib/neurolink.d.ts +36 -7
- package/dist/lib/neurolink.js +141 -0
- package/dist/lib/providers/anthropic.js +47 -3
- package/dist/lib/providers/azureOpenai.js +9 -2
- package/dist/lib/providers/googleAiStudio.js +9 -2
- package/dist/lib/providers/googleVertex.js +12 -2
- package/dist/lib/providers/huggingFace.js +1 -1
- package/dist/lib/providers/litellm.js +1 -1
- package/dist/lib/providers/mistral.js +1 -1
- package/dist/lib/providers/openAI.js +47 -3
- package/dist/lib/services/server/ai/observability/instrumentation.d.ts +57 -0
- package/dist/lib/services/server/ai/observability/instrumentation.js +170 -0
- package/dist/lib/session/globalSessionState.d.ts +26 -0
- package/dist/lib/session/globalSessionState.js +86 -1
- package/dist/lib/telemetry/index.d.ts +1 -0
- package/dist/lib/telemetry/telemetryService.d.ts +2 -0
- package/dist/lib/telemetry/telemetryService.js +7 -7
- package/dist/lib/types/cli.d.ts +28 -0
- package/dist/lib/types/content.d.ts +18 -5
- package/dist/lib/types/contextTypes.d.ts +1 -1
- package/dist/lib/types/conversation.d.ts +57 -4
- package/dist/lib/types/fileTypes.d.ts +65 -0
- package/dist/lib/types/fileTypes.js +4 -0
- package/dist/lib/types/generateTypes.d.ts +12 -0
- package/dist/lib/types/guardrails.d.ts +103 -0
- package/dist/lib/types/guardrails.js +1 -0
- package/dist/lib/types/index.d.ts +4 -2
- package/dist/lib/types/index.js +4 -0
- package/dist/lib/types/mcpTypes.d.ts +407 -14
- package/dist/lib/types/modelTypes.d.ts +6 -6
- package/dist/lib/types/observability.d.ts +49 -0
- package/dist/lib/types/observability.js +6 -0
- package/dist/lib/types/streamTypes.d.ts +7 -0
- package/dist/lib/types/tools.d.ts +132 -35
- package/dist/lib/utils/csvProcessor.d.ts +68 -0
- package/dist/lib/utils/csvProcessor.js +277 -0
- package/dist/lib/utils/fileDetector.d.ts +57 -0
- package/dist/lib/utils/fileDetector.js +457 -0
- package/dist/lib/utils/imageProcessor.d.ts +10 -0
- package/dist/lib/utils/imageProcessor.js +22 -0
- package/dist/lib/utils/loopUtils.d.ts +71 -0
- package/dist/lib/utils/loopUtils.js +262 -0
- package/dist/lib/utils/messageBuilder.d.ts +2 -1
- package/dist/lib/utils/messageBuilder.js +197 -2
- package/dist/lib/utils/optionsUtils.d.ts +1 -1
- package/dist/mcp/factory.d.ts +2 -157
- package/dist/mcp/flexibleToolValidator.d.ts +1 -5
- package/dist/mcp/index.d.ts +3 -2
- package/dist/mcp/mcpCircuitBreaker.d.ts +1 -75
- package/dist/mcp/mcpClientFactory.d.ts +1 -20
- package/dist/mcp/mcpClientFactory.js +1 -0
- package/dist/mcp/registry.d.ts +3 -10
- package/dist/mcp/servers/agent/directToolsServer.d.ts +1 -1
- package/dist/mcp/servers/aiProviders/aiCoreServer.d.ts +1 -1
- package/dist/mcp/servers/utilities/utilityServer.d.ts +1 -1
- package/dist/mcp/toolDiscoveryService.d.ts +3 -84
- package/dist/mcp/toolRegistry.d.ts +2 -24
- package/dist/middleware/builtin/guardrails.d.ts +5 -16
- package/dist/middleware/builtin/guardrails.js +44 -39
- package/dist/middleware/utils/guardrailsUtils.d.ts +64 -0
- package/dist/middleware/utils/guardrailsUtils.js +387 -0
- package/dist/neurolink.d.ts +36 -7
- package/dist/neurolink.js +141 -0
- package/dist/providers/anthropic.js +47 -3
- package/dist/providers/azureOpenai.js +9 -2
- package/dist/providers/googleAiStudio.js +9 -2
- package/dist/providers/googleVertex.js +12 -2
- package/dist/providers/huggingFace.js +1 -1
- package/dist/providers/litellm.js +1 -1
- package/dist/providers/mistral.js +1 -1
- package/dist/providers/openAI.js +47 -3
- package/dist/services/server/ai/observability/instrumentation.d.ts +57 -0
- package/dist/services/server/ai/observability/instrumentation.js +170 -0
- package/dist/session/globalSessionState.d.ts +26 -0
- package/dist/session/globalSessionState.js +86 -1
- package/dist/telemetry/index.d.ts +1 -0
- package/dist/telemetry/telemetryService.d.ts +2 -0
- package/dist/telemetry/telemetryService.js +7 -7
- package/dist/types/cli.d.ts +28 -0
- package/dist/types/content.d.ts +18 -5
- package/dist/types/contextTypes.d.ts +1 -1
- package/dist/types/conversation.d.ts +57 -4
- package/dist/types/fileTypes.d.ts +65 -0
- package/dist/types/fileTypes.js +4 -0
- package/dist/types/generateTypes.d.ts +12 -0
- package/dist/types/guardrails.d.ts +103 -0
- package/dist/types/guardrails.js +1 -0
- package/dist/types/index.d.ts +4 -2
- package/dist/types/index.js +4 -0
- package/dist/types/mcpTypes.d.ts +407 -14
- package/dist/types/modelTypes.d.ts +6 -6
- package/dist/types/observability.d.ts +49 -0
- package/dist/types/observability.js +6 -0
- package/dist/types/streamTypes.d.ts +7 -0
- package/dist/types/tools.d.ts +132 -35
- package/dist/utils/csvProcessor.d.ts +68 -0
- package/dist/utils/csvProcessor.js +277 -0
- package/dist/utils/fileDetector.d.ts +57 -0
- package/dist/utils/fileDetector.js +457 -0
- package/dist/utils/imageProcessor.d.ts +10 -0
- package/dist/utils/imageProcessor.js +22 -0
- package/dist/utils/loopUtils.d.ts +71 -0
- package/dist/utils/loopUtils.js +262 -0
- package/dist/utils/messageBuilder.d.ts +2 -1
- package/dist/utils/messageBuilder.js +197 -2
- package/dist/utils/optionsUtils.d.ts +1 -1
- package/package.json +18 -16
- package/dist/lib/mcp/contracts/mcpContract.d.ts +0 -106
- package/dist/lib/mcp/contracts/mcpContract.js +0 -5
- package/dist/mcp/contracts/mcpContract.d.ts +0 -106
- package/dist/mcp/contracts/mcpContract.js +0 -5
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import type { PrecallEvaluationResult, PrecallEvaluationConfig, EvaluationActionResult, BadWordsConfig } from "../../types/guardrails.js";
|
|
2
|
+
import type { LanguageModelV1CallOptions } from "ai";
|
|
3
|
+
export declare const DEFAULT_EVALUATION_PROMPT = "\nEvaluate the following user input for safety and appropriateness. Respond ONLY with valid JSON:\n\n{\n \"overall\": \"safe|unsafe|suspicious|inappropriate\",\n \"safetyScore\": <1-10>,\n \"appropriatenessScore\": <1-10>,\n \"confidenceLevel\": <1-10>,\n \"issues\": [\n {\n \"category\": \"explicit_content|hate_speech|violence|personal_info|spam|other\",\n \"severity\": \"low|medium|high|critical\",\n \"description\": \"Brief description\"\n }\n ],\n \"suggestedAction\": \"allow|block|sanitize|warn\",\n \"reasoning\": \"Brief explanation\"\n}\n\nUser Input: \"{USER_INPUT}\"\n";
|
|
4
|
+
/**
|
|
5
|
+
* Extract user input from LanguageModelV1CallOptions
|
|
6
|
+
*/
|
|
7
|
+
export declare function extractUserInput(params: LanguageModelV1CallOptions): string;
|
|
8
|
+
export declare function parseEvaluationResponse(rawResponse: string): PrecallEvaluationResult;
|
|
9
|
+
/**
|
|
10
|
+
* Handles the precall guardrails logic, including evaluation and sanitization.
|
|
11
|
+
* @param params - The language model call options.
|
|
12
|
+
* @param config - The precall evaluation configuration.
|
|
13
|
+
* @returns An object indicating if the request should be blocked and the (potentially transformed) params.
|
|
14
|
+
*/
|
|
15
|
+
export declare function handlePrecallGuardrails(params: LanguageModelV1CallOptions, config: PrecallEvaluationConfig): Promise<{
|
|
16
|
+
shouldBlock: boolean;
|
|
17
|
+
transformedParams: LanguageModelV1CallOptions;
|
|
18
|
+
}>;
|
|
19
|
+
/**
|
|
20
|
+
* Perform precall evaluation of user input using AI models
|
|
21
|
+
*/
|
|
22
|
+
export declare function performPrecallEvaluation(config: PrecallEvaluationConfig, userInput: string): Promise<PrecallEvaluationResult>;
|
|
23
|
+
export declare function applyEvaluationActions(evaluation: PrecallEvaluationResult, config: PrecallEvaluationConfig, userInput: string): EvaluationActionResult;
|
|
24
|
+
/**
|
|
25
|
+
* Apply parameter sanitization to request parameters
|
|
26
|
+
*/
|
|
27
|
+
export declare function applySanitization(params: LanguageModelV1CallOptions, sanitizedInput: string): LanguageModelV1CallOptions;
|
|
28
|
+
export declare function escapeRegExp(string: string): string;
|
|
29
|
+
export declare function createBlockedResponse(): {
|
|
30
|
+
text: string;
|
|
31
|
+
usage: {
|
|
32
|
+
promptTokens: number;
|
|
33
|
+
completionTokens: number;
|
|
34
|
+
};
|
|
35
|
+
finishReason: "stop";
|
|
36
|
+
warnings: never[];
|
|
37
|
+
rawCall: {
|
|
38
|
+
rawPrompt: null;
|
|
39
|
+
rawSettings: {};
|
|
40
|
+
};
|
|
41
|
+
};
|
|
42
|
+
export declare function createBlockedStream(): ReadableStream<any>;
|
|
43
|
+
/**
|
|
44
|
+
* Result from content filtering operation
|
|
45
|
+
*/
|
|
46
|
+
export interface ContentFilteringResult {
|
|
47
|
+
filteredText: string;
|
|
48
|
+
hasChanges: boolean;
|
|
49
|
+
appliedFilters: string[];
|
|
50
|
+
filteringStats: {
|
|
51
|
+
regexPatternsApplied: number;
|
|
52
|
+
stringFiltersApplied: number;
|
|
53
|
+
totalMatches: number;
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Apply content filtering using bad words configuration
|
|
58
|
+
* Handles both regex patterns and string lists with proper priority
|
|
59
|
+
* @param text The text to filter
|
|
60
|
+
* @param badWordsConfig Bad words configuration
|
|
61
|
+
* @param context Optional context for logging (e.g., "generate", "stream")
|
|
62
|
+
* @returns Filtering result with filtered text and metadata
|
|
63
|
+
*/
|
|
64
|
+
export declare function applyContentFiltering(text: string, badWordsConfig?: BadWordsConfig, context?: string): ContentFilteringResult;
|
|
@@ -0,0 +1,387 @@
|
|
|
1
|
+
import { AIProviderFactory } from "../../core/factory.js";
|
|
2
|
+
import { logger } from "../../utils/logger.js";
|
|
3
|
+
export const DEFAULT_EVALUATION_PROMPT = `
|
|
4
|
+
Evaluate the following user input for safety and appropriateness. Respond ONLY with valid JSON:
|
|
5
|
+
|
|
6
|
+
{
|
|
7
|
+
"overall": "safe|unsafe|suspicious|inappropriate",
|
|
8
|
+
"safetyScore": <1-10>,
|
|
9
|
+
"appropriatenessScore": <1-10>,
|
|
10
|
+
"confidenceLevel": <1-10>,
|
|
11
|
+
"issues": [
|
|
12
|
+
{
|
|
13
|
+
"category": "explicit_content|hate_speech|violence|personal_info|spam|other",
|
|
14
|
+
"severity": "low|medium|high|critical",
|
|
15
|
+
"description": "Brief description"
|
|
16
|
+
}
|
|
17
|
+
],
|
|
18
|
+
"suggestedAction": "allow|block|sanitize|warn",
|
|
19
|
+
"reasoning": "Brief explanation"
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
User Input: "{USER_INPUT}"
|
|
23
|
+
`;
|
|
24
|
+
/**
|
|
25
|
+
* Extract user input from LanguageModelV1CallOptions
|
|
26
|
+
*/
|
|
27
|
+
export function extractUserInput(params) {
|
|
28
|
+
if (typeof params.prompt === "string") {
|
|
29
|
+
return params.prompt;
|
|
30
|
+
}
|
|
31
|
+
return params.prompt
|
|
32
|
+
.map((msg) => {
|
|
33
|
+
if (msg.role === "user") {
|
|
34
|
+
if (typeof msg.content === "string") {
|
|
35
|
+
return msg.content;
|
|
36
|
+
}
|
|
37
|
+
else if (Array.isArray(msg.content)) {
|
|
38
|
+
return msg.content
|
|
39
|
+
.filter((part) => part.type === "text")
|
|
40
|
+
.map((part) => part.text)
|
|
41
|
+
.join(" ");
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
return "";
|
|
45
|
+
})
|
|
46
|
+
.filter(Boolean)
|
|
47
|
+
.join("\n");
|
|
48
|
+
}
|
|
49
|
+
export function parseEvaluationResponse(rawResponse) {
|
|
50
|
+
try {
|
|
51
|
+
const cleanedResponse = rawResponse.replace(/```json\n|```/g, "").trim();
|
|
52
|
+
const parsed = JSON.parse(cleanedResponse);
|
|
53
|
+
return {
|
|
54
|
+
overall: parsed.overall || "safe",
|
|
55
|
+
safetyScore: Number(parsed.safetyScore) || 10,
|
|
56
|
+
appropriatenessScore: Number(parsed.appropriatenessScore) || 10,
|
|
57
|
+
confidenceLevel: Number(parsed.confidenceLevel) || 10,
|
|
58
|
+
issues: parsed.issues || [],
|
|
59
|
+
suggestedAction: parsed.suggestedAction || "allow",
|
|
60
|
+
reasoning: parsed.reasoning || "No reasoning provided.",
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
catch (error) {
|
|
64
|
+
logger.error("[GuardrailsUtils] Failed to parse evaluation response:", error);
|
|
65
|
+
return {
|
|
66
|
+
overall: "safe",
|
|
67
|
+
safetyScore: 10,
|
|
68
|
+
appropriatenessScore: 10,
|
|
69
|
+
confidenceLevel: 1,
|
|
70
|
+
suggestedAction: "allow",
|
|
71
|
+
reasoning: "Error parsing evaluation response - allowing by default.",
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Handles the precall guardrails logic, including evaluation and sanitization.
|
|
77
|
+
* @param params - The language model call options.
|
|
78
|
+
* @param config - The precall evaluation configuration.
|
|
79
|
+
* @returns An object indicating if the request should be blocked and the (potentially transformed) params.
|
|
80
|
+
*/
|
|
81
|
+
export async function handlePrecallGuardrails(params, config) {
|
|
82
|
+
const userInput = extractUserInput(params);
|
|
83
|
+
let transformedParams = params;
|
|
84
|
+
if (userInput.trim()) {
|
|
85
|
+
logger.debug(`[GuardrailsUtils] Performing precall evaluation on user input.`);
|
|
86
|
+
const evaluation = await performPrecallEvaluation(config, userInput);
|
|
87
|
+
const actionResult = applyEvaluationActions(evaluation, config, userInput);
|
|
88
|
+
if (actionResult.shouldBlock) {
|
|
89
|
+
logger.warn(`[GuardrailsUtils] Blocking request due to precall evaluation.`, {
|
|
90
|
+
evaluation,
|
|
91
|
+
userInput: userInput.substring(0, 100),
|
|
92
|
+
});
|
|
93
|
+
return { shouldBlock: true, transformedParams };
|
|
94
|
+
}
|
|
95
|
+
if (actionResult.sanitizedInput) {
|
|
96
|
+
logger.info(`[GuardrailsUtils] Applying input sanitization.`);
|
|
97
|
+
transformedParams = applySanitization(params, actionResult.sanitizedInput);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
else {
|
|
101
|
+
logger.debug(`[GuardrailsUtils] Skipping precall evaluation - no user content to evaluate.`);
|
|
102
|
+
}
|
|
103
|
+
return { shouldBlock: false, transformedParams };
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* Perform precall evaluation of user input using AI models
|
|
107
|
+
*/
|
|
108
|
+
export async function performPrecallEvaluation(config, userInput) {
|
|
109
|
+
try {
|
|
110
|
+
const provider = await AIProviderFactory.createProvider(config.provider || "google-ai", config.evaluationModel || "gemini-1.5-flash");
|
|
111
|
+
const evaluationPrompt = config.evaluationPrompt || DEFAULT_EVALUATION_PROMPT;
|
|
112
|
+
const prompt = evaluationPrompt.replace("{USER_INPUT}", userInput);
|
|
113
|
+
const result = await provider.generate({
|
|
114
|
+
input: { text: prompt },
|
|
115
|
+
});
|
|
116
|
+
if (!result || !result.content) {
|
|
117
|
+
throw new Error("Evaluation generation failed to return a result.");
|
|
118
|
+
}
|
|
119
|
+
return parseEvaluationResponse(result.content);
|
|
120
|
+
}
|
|
121
|
+
catch (error) {
|
|
122
|
+
logger.error("[GuardrailsUtils] Precall evaluation failed:", error);
|
|
123
|
+
return {
|
|
124
|
+
overall: "safe",
|
|
125
|
+
safetyScore: 10,
|
|
126
|
+
appropriatenessScore: 10,
|
|
127
|
+
confidenceLevel: 1,
|
|
128
|
+
suggestedAction: "allow",
|
|
129
|
+
reasoning: "Evaluation failed - allowing by default.",
|
|
130
|
+
};
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
export function applyEvaluationActions(evaluation, config, userInput) {
|
|
134
|
+
const actions = config.actions || {};
|
|
135
|
+
const thresholds = config.thresholds || {};
|
|
136
|
+
const safetyThreshold = thresholds.safetyScore || 7;
|
|
137
|
+
const appropriatenessThreshold = thresholds.appropriatenessScore || 6;
|
|
138
|
+
let actionToTake;
|
|
139
|
+
if (evaluation.overall === "unsafe" ||
|
|
140
|
+
evaluation.safetyScore < safetyThreshold) {
|
|
141
|
+
actionToTake = actions.onUnsafe || "block";
|
|
142
|
+
}
|
|
143
|
+
else if (evaluation.overall === "inappropriate" ||
|
|
144
|
+
evaluation.appropriatenessScore < appropriatenessThreshold) {
|
|
145
|
+
actionToTake = actions.onInappropriate || "warn";
|
|
146
|
+
}
|
|
147
|
+
else if (evaluation.overall === "suspicious") {
|
|
148
|
+
actionToTake = actions.onSuspicious || "log";
|
|
149
|
+
}
|
|
150
|
+
else {
|
|
151
|
+
actionToTake = "allow";
|
|
152
|
+
}
|
|
153
|
+
logger.info("[GuardrailsUtils] Precall evaluation result:", {
|
|
154
|
+
overall: evaluation.overall,
|
|
155
|
+
safetyScore: evaluation.safetyScore,
|
|
156
|
+
appropriatenessScore: evaluation.appropriatenessScore,
|
|
157
|
+
confidenceLevel: evaluation.confidenceLevel,
|
|
158
|
+
suggestedAction: evaluation.suggestedAction,
|
|
159
|
+
actionTaken: actionToTake,
|
|
160
|
+
reasoning: evaluation.reasoning,
|
|
161
|
+
issues: evaluation.issues,
|
|
162
|
+
});
|
|
163
|
+
switch (actionToTake) {
|
|
164
|
+
case "block":
|
|
165
|
+
return { shouldBlock: true };
|
|
166
|
+
case "sanitize": {
|
|
167
|
+
let sanitized = userInput;
|
|
168
|
+
const patterns = config.sanitizationPatterns || [];
|
|
169
|
+
const replacementText = config.replacementText || "[REDACTED]";
|
|
170
|
+
if (patterns.length > 0) {
|
|
171
|
+
logger.debug(`[GuardrailsUtils] Applying ${patterns.length} sanitization patterns with replacement: "${replacementText}".`);
|
|
172
|
+
patterns.forEach((pattern, index) => {
|
|
173
|
+
try {
|
|
174
|
+
const regex = new RegExp(pattern, "gi");
|
|
175
|
+
const before = sanitized;
|
|
176
|
+
let matchCount = 0;
|
|
177
|
+
sanitized = sanitized.replace(regex, () => {
|
|
178
|
+
matchCount++;
|
|
179
|
+
return replacementText;
|
|
180
|
+
});
|
|
181
|
+
if (before !== sanitized) {
|
|
182
|
+
logger.debug(`[GuardrailsUtils] Pattern ${index + 1} matched ${matchCount} times.`);
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
catch (error) {
|
|
186
|
+
logger.error(`[GuardrailsUtils] Invalid sanitization pattern "${pattern}":`, error);
|
|
187
|
+
}
|
|
188
|
+
});
|
|
189
|
+
if (sanitized !== userInput) {
|
|
190
|
+
logger.info(`[GuardrailsUtils] Input sanitized using ${patterns.length} patterns.`);
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
else {
|
|
194
|
+
logger.warn("[GuardrailsUtils] Sanitize action triggered but no sanitizationPatterns provided in config. Input will not be modified.");
|
|
195
|
+
}
|
|
196
|
+
return { shouldBlock: false, sanitizedInput: sanitized };
|
|
197
|
+
}
|
|
198
|
+
case "warn": {
|
|
199
|
+
logger.warn("[GuardrailsUtils] Potentially inappropriate content detected but allowing:", {
|
|
200
|
+
userInput: userInput.substring(0, 100),
|
|
201
|
+
evaluation,
|
|
202
|
+
});
|
|
203
|
+
return { shouldBlock: false };
|
|
204
|
+
}
|
|
205
|
+
case "log": {
|
|
206
|
+
logger.info("[GuardrailsUtils] Suspicious content detected:", {
|
|
207
|
+
userInput: userInput.substring(0, 100),
|
|
208
|
+
evaluation,
|
|
209
|
+
});
|
|
210
|
+
return { shouldBlock: false };
|
|
211
|
+
}
|
|
212
|
+
default:
|
|
213
|
+
return { shouldBlock: false };
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
/**
|
|
217
|
+
* Apply parameter sanitization to request parameters
|
|
218
|
+
*/
|
|
219
|
+
export function applySanitization(params, sanitizedInput) {
|
|
220
|
+
const sanitizedParams = { ...params };
|
|
221
|
+
if (Array.isArray(params.prompt)) {
|
|
222
|
+
const sanitizedPrompt = params.prompt.map((msg) => {
|
|
223
|
+
if (msg.role === "user") {
|
|
224
|
+
if (typeof msg.content === "string") {
|
|
225
|
+
return {
|
|
226
|
+
...msg,
|
|
227
|
+
content: [{ type: "text", text: sanitizedInput }],
|
|
228
|
+
};
|
|
229
|
+
}
|
|
230
|
+
else if (Array.isArray(msg.content)) {
|
|
231
|
+
const sanitizedContent = msg.content.map((part) => {
|
|
232
|
+
if (part.type === "text") {
|
|
233
|
+
return { ...part, text: sanitizedInput };
|
|
234
|
+
}
|
|
235
|
+
return part;
|
|
236
|
+
});
|
|
237
|
+
return { ...msg, content: sanitizedContent };
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
return msg;
|
|
241
|
+
});
|
|
242
|
+
sanitizedParams.prompt =
|
|
243
|
+
sanitizedPrompt;
|
|
244
|
+
}
|
|
245
|
+
return sanitizedParams;
|
|
246
|
+
}
|
|
247
|
+
export function escapeRegExp(string) {
|
|
248
|
+
return string.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
249
|
+
}
|
|
250
|
+
export function createBlockedResponse() {
|
|
251
|
+
return {
|
|
252
|
+
text: "Request contains inappropriate content and has been blocked.",
|
|
253
|
+
usage: { promptTokens: 0, completionTokens: 0 },
|
|
254
|
+
finishReason: "stop",
|
|
255
|
+
warnings: [],
|
|
256
|
+
rawCall: { rawPrompt: null, rawSettings: {} },
|
|
257
|
+
};
|
|
258
|
+
}
|
|
259
|
+
export function createBlockedStream() {
|
|
260
|
+
return new ReadableStream({
|
|
261
|
+
start(controller) {
|
|
262
|
+
controller.enqueue({
|
|
263
|
+
type: "text-delta",
|
|
264
|
+
textDelta: "Request contains inappropriate content and has been blocked.",
|
|
265
|
+
});
|
|
266
|
+
controller.enqueue({
|
|
267
|
+
type: "finish",
|
|
268
|
+
finishReason: "stop",
|
|
269
|
+
usage: { promptTokens: 0, completionTokens: 0 },
|
|
270
|
+
});
|
|
271
|
+
controller.close();
|
|
272
|
+
},
|
|
273
|
+
});
|
|
274
|
+
}
|
|
275
|
+
/**
|
|
276
|
+
* Apply content filtering using bad words configuration
|
|
277
|
+
* Handles both regex patterns and string lists with proper priority
|
|
278
|
+
* @param text The text to filter
|
|
279
|
+
* @param badWordsConfig Bad words configuration
|
|
280
|
+
* @param context Optional context for logging (e.g., "generate", "stream")
|
|
281
|
+
* @returns Filtering result with filtered text and metadata
|
|
282
|
+
*/
|
|
283
|
+
export function applyContentFiltering(text, badWordsConfig, context = "unknown") {
|
|
284
|
+
// Early return if filtering is disabled or no config
|
|
285
|
+
try {
|
|
286
|
+
if (!badWordsConfig?.enabled || !text) {
|
|
287
|
+
return {
|
|
288
|
+
filteredText: text,
|
|
289
|
+
hasChanges: false,
|
|
290
|
+
appliedFilters: [],
|
|
291
|
+
filteringStats: {
|
|
292
|
+
regexPatternsApplied: 0,
|
|
293
|
+
stringFiltersApplied: 0,
|
|
294
|
+
totalMatches: 0,
|
|
295
|
+
},
|
|
296
|
+
};
|
|
297
|
+
}
|
|
298
|
+
let filteredText = text;
|
|
299
|
+
let hasChanges = false;
|
|
300
|
+
const appliedFilters = [];
|
|
301
|
+
let totalMatches = 0;
|
|
302
|
+
const replacementText = badWordsConfig.replacementText || "[REDACTED]";
|
|
303
|
+
// Priority 1: Use regex patterns if provided
|
|
304
|
+
if (badWordsConfig.regexPatterns &&
|
|
305
|
+
badWordsConfig.regexPatterns.length > 0) {
|
|
306
|
+
if (badWordsConfig.list && badWordsConfig.list.length > 0) {
|
|
307
|
+
logger.warn(`[ContentFiltering:${context}] Both regexPatterns and list provided. Using regexPatterns and ignoring list.`);
|
|
308
|
+
}
|
|
309
|
+
logger.debug(`[ContentFiltering:${context}] Applying regex pattern filtering with ${badWordsConfig.regexPatterns.length} patterns using replacement: "${replacementText}".`);
|
|
310
|
+
for (const pattern of badWordsConfig.regexPatterns) {
|
|
311
|
+
try {
|
|
312
|
+
// TODO: Add blocking for overly complex or long patterns
|
|
313
|
+
if (pattern.length > 1000) {
|
|
314
|
+
logger.warn(`[ContentFiltering:${context}] Regex pattern exceeds max length (1000 chars): "${pattern.substring(0, 50)}..."`);
|
|
315
|
+
}
|
|
316
|
+
const regex = new RegExp(pattern, "gi");
|
|
317
|
+
const testStart = Date.now();
|
|
318
|
+
regex.test("test");
|
|
319
|
+
if (Date.now() - testStart > 100) {
|
|
320
|
+
logger.warn(`[ContentFiltering:${context}] Regex pattern "${pattern}" appears to be too complex (slow test execution).`);
|
|
321
|
+
}
|
|
322
|
+
const before = filteredText;
|
|
323
|
+
let matchCount = 0;
|
|
324
|
+
filteredText = filteredText?.replace(regex, () => {
|
|
325
|
+
matchCount++;
|
|
326
|
+
return replacementText;
|
|
327
|
+
});
|
|
328
|
+
if (before !== filteredText) {
|
|
329
|
+
hasChanges = true;
|
|
330
|
+
totalMatches += matchCount;
|
|
331
|
+
appliedFilters.push(`regex:${pattern}`);
|
|
332
|
+
logger.debug(`[ContentFiltering:${context}] Regex pattern "${pattern}" matched ${matchCount} times and filtered content.`);
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
catch (error) {
|
|
336
|
+
logger.error(`[ContentFiltering:${context}] Invalid regex pattern "${pattern}":`, error);
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
// Priority 2: Use simple string list if no regex patterns
|
|
341
|
+
else if (badWordsConfig.list && badWordsConfig.list.length > 0) {
|
|
342
|
+
logger.debug(`[ContentFiltering:${context}] Applying string list filtering with ${badWordsConfig.list.length} terms using replacement: "${replacementText}".`);
|
|
343
|
+
for (const term of badWordsConfig.list) {
|
|
344
|
+
const regex = new RegExp(escapeRegExp(term), "gi");
|
|
345
|
+
const before = filteredText;
|
|
346
|
+
let matchCount = 0;
|
|
347
|
+
filteredText = filteredText?.replace(regex, () => {
|
|
348
|
+
matchCount++;
|
|
349
|
+
return replacementText;
|
|
350
|
+
});
|
|
351
|
+
if (before !== filteredText) {
|
|
352
|
+
hasChanges = true;
|
|
353
|
+
totalMatches += matchCount;
|
|
354
|
+
appliedFilters.push(`string:${term}`);
|
|
355
|
+
logger.debug(`[ContentFiltering:${context}] String filter "${term}" matched ${matchCount} times.`);
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
const result = {
|
|
360
|
+
filteredText,
|
|
361
|
+
hasChanges,
|
|
362
|
+
appliedFilters,
|
|
363
|
+
filteringStats: {
|
|
364
|
+
regexPatternsApplied: badWordsConfig.regexPatterns?.length || 0,
|
|
365
|
+
stringFiltersApplied: badWordsConfig.list?.length || 0,
|
|
366
|
+
totalMatches,
|
|
367
|
+
},
|
|
368
|
+
};
|
|
369
|
+
if (hasChanges) {
|
|
370
|
+
logger.debug(`[ContentFiltering:${context}] Filtering completed. Applied ${appliedFilters.length} filters with ${totalMatches} total matches.`);
|
|
371
|
+
}
|
|
372
|
+
return result;
|
|
373
|
+
}
|
|
374
|
+
catch (error) {
|
|
375
|
+
logger.error(`[ContentFiltering:${context}] Error during content filtering:`, error);
|
|
376
|
+
return {
|
|
377
|
+
filteredText: text,
|
|
378
|
+
hasChanges: false,
|
|
379
|
+
appliedFilters: [],
|
|
380
|
+
filteringStats: {
|
|
381
|
+
regexPatternsApplied: 0,
|
|
382
|
+
stringFiltersApplied: 0,
|
|
383
|
+
totalMatches: 0,
|
|
384
|
+
},
|
|
385
|
+
};
|
|
386
|
+
}
|
|
387
|
+
}
|
package/dist/neurolink.d.ts
CHANGED
|
@@ -10,7 +10,7 @@ import { MCPToolRegistry } from "./mcp/toolRegistry.js";
|
|
|
10
10
|
import type { GenerateOptions, GenerateResult } from "./types/generateTypes.js";
|
|
11
11
|
import type { StreamOptions, StreamResult } from "./types/streamTypes.js";
|
|
12
12
|
import type { MCPServerInfo, MCPExecutableTool } from "./types/mcpTypes.js";
|
|
13
|
-
import type { ToolInfo } from "./
|
|
13
|
+
import type { ToolInfo } from "./types/tools.js";
|
|
14
14
|
import type { NeuroLinkEvents, TypedEventEmitter, ToolExecutionContext, ToolExecutionSummary } from "./types/common.js";
|
|
15
15
|
import type { JsonObject } from "./types/common.js";
|
|
16
16
|
import type { BatchOperationResult } from "./types/typeAliases.js";
|
|
@@ -19,6 +19,17 @@ import { ConversationMemoryManager } from "./core/conversationMemoryManager.js";
|
|
|
19
19
|
import { RedisConversationMemoryManager } from "./core/redisConversationMemoryManager.js";
|
|
20
20
|
import type { HITLConfig } from "./hitl/types.js";
|
|
21
21
|
import type { ExternalMCPServerInstance, ExternalMCPOperationResult, ExternalMCPToolInfo } from "./types/externalMcp.js";
|
|
22
|
+
import type { ObservabilityConfig } from "./types/observability.js";
|
|
23
|
+
/**
|
|
24
|
+
* Configuration object for NeuroLink constructor.
|
|
25
|
+
*/
|
|
26
|
+
export interface NeurolinkConstructorConfig {
|
|
27
|
+
conversationMemory?: Partial<ConversationMemoryConfig>;
|
|
28
|
+
enableOrchestration?: boolean;
|
|
29
|
+
hitl?: HITLConfig;
|
|
30
|
+
toolRegistry?: MCPToolRegistry;
|
|
31
|
+
observability?: ObservabilityConfig;
|
|
32
|
+
}
|
|
22
33
|
export interface ProviderStatus {
|
|
23
34
|
provider: string;
|
|
24
35
|
status: "working" | "failed" | "not-configured";
|
|
@@ -138,12 +149,8 @@ export declare class NeuroLink {
|
|
|
138
149
|
* @throws {Error} When external server manager initialization fails
|
|
139
150
|
* @throws {Error} When HITL configuration is invalid (if enabled)
|
|
140
151
|
*/
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
enableOrchestration?: boolean;
|
|
144
|
-
hitl?: HITLConfig;
|
|
145
|
-
toolRegistry?: MCPToolRegistry;
|
|
146
|
-
});
|
|
152
|
+
private observabilityConfig?;
|
|
153
|
+
constructor(config?: NeurolinkConstructorConfig);
|
|
147
154
|
/**
|
|
148
155
|
* Initialize provider registry with security settings
|
|
149
156
|
*/
|
|
@@ -170,6 +177,10 @@ export declare class NeuroLink {
|
|
|
170
177
|
* Setup event handlers for external server manager
|
|
171
178
|
*/
|
|
172
179
|
private setupExternalServerEventHandlers;
|
|
180
|
+
/**
|
|
181
|
+
* Initialize Langfuse observability for AI operations tracking
|
|
182
|
+
*/
|
|
183
|
+
private initializeLangfuse;
|
|
173
184
|
/**
|
|
174
185
|
* Log constructor completion with final state summary
|
|
175
186
|
*/
|
|
@@ -279,6 +290,24 @@ export declare class NeuroLink {
|
|
|
279
290
|
* @throws {Error} When all providers fail to generate content
|
|
280
291
|
* @throws {Error} When conversation memory operations fail (if enabled)
|
|
281
292
|
*/
|
|
293
|
+
/**
|
|
294
|
+
* Get observability configuration
|
|
295
|
+
*/
|
|
296
|
+
getObservabilityConfig(): ObservabilityConfig | undefined;
|
|
297
|
+
/**
|
|
298
|
+
* Check if Langfuse telemetry is enabled
|
|
299
|
+
* Centralized utility to avoid duplication across providers
|
|
300
|
+
*/
|
|
301
|
+
isTelemetryEnabled(): boolean;
|
|
302
|
+
/**
|
|
303
|
+
* Public method to initialize Langfuse observability
|
|
304
|
+
* This method can be called externally to ensure Langfuse is properly initialized
|
|
305
|
+
*/
|
|
306
|
+
initializeLangfuseObservability(): Promise<void>;
|
|
307
|
+
/**
|
|
308
|
+
* Gracefully shutdown NeuroLink and all MCP connections
|
|
309
|
+
*/
|
|
310
|
+
shutdown(): Promise<void>;
|
|
282
311
|
generate(optionsOrPrompt: GenerateOptions | string): Promise<GenerateResult>;
|
|
283
312
|
/**
|
|
284
313
|
* BACKWARD COMPATIBILITY: Legacy generateText method
|