@juspay/neurolink 7.28.0 → 7.29.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 +13 -0
- package/dist/cli/commands/ollama.d.ts +3 -0
- package/dist/cli/commands/ollama.js +288 -0
- package/dist/cli/factories/ollamaCommandFactory.d.ts +4 -0
- package/dist/cli/factories/ollamaCommandFactory.js +86 -93
- package/dist/cli/utils/ollamaUtils.d.ts +24 -0
- package/dist/cli/utils/ollamaUtils.js +161 -0
- package/dist/core/baseProvider.js +4 -2
- package/dist/index.d.ts +2 -3
- package/dist/index.js +1 -2
- package/dist/lib/core/baseProvider.js +4 -2
- package/dist/lib/core/dynamicModels.d.ts +6 -6
- package/dist/lib/index.d.ts +2 -3
- package/dist/lib/index.js +1 -2
- package/dist/lib/middleware/builtin/analytics.js +13 -14
- package/dist/lib/middleware/builtin/guardrails.d.ts +20 -0
- package/dist/lib/middleware/builtin/guardrails.js +87 -0
- package/dist/lib/middleware/factory.d.ts +29 -14
- package/dist/lib/middleware/factory.js +136 -110
- package/dist/lib/middleware/index.d.ts +3 -49
- package/dist/lib/middleware/index.js +4 -58
- package/dist/lib/middleware/registry.d.ts +1 -3
- package/dist/lib/middleware/registry.js +4 -5
- package/dist/lib/middleware/types.d.ts +3 -1
- package/dist/middleware/builtin/analytics.js +13 -14
- package/dist/middleware/builtin/guardrails.d.ts +20 -0
- package/dist/middleware/builtin/guardrails.js +87 -0
- package/dist/middleware/factory.d.ts +29 -14
- package/dist/middleware/factory.js +136 -110
- package/dist/middleware/index.d.ts +3 -49
- package/dist/middleware/index.js +4 -58
- package/dist/middleware/registry.d.ts +1 -3
- package/dist/middleware/registry.js +4 -5
- package/dist/middleware/types.d.ts +3 -1
- package/package.json +1 -1
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
import { spawnSync, spawn, } from "child_process";
|
|
2
|
+
import chalk from "chalk";
|
|
3
|
+
import ora from "ora";
|
|
4
|
+
import { logger } from "../../lib/utils/logger.js";
|
|
5
|
+
/**
|
|
6
|
+
* Shared Ollama utilities for CLI commands
|
|
7
|
+
*/
|
|
8
|
+
export class OllamaUtils {
|
|
9
|
+
/**
|
|
10
|
+
* Secure wrapper around spawnSync to prevent command injection.
|
|
11
|
+
*/
|
|
12
|
+
static safeSpawn(command, args, options = {}) {
|
|
13
|
+
const defaultOptions = {
|
|
14
|
+
...options,
|
|
15
|
+
encoding: "utf8", // Always enforce utf8 encoding
|
|
16
|
+
};
|
|
17
|
+
return spawnSync(command, args, defaultOptions);
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Wait for Ollama service to become ready with exponential backoff
|
|
21
|
+
*/
|
|
22
|
+
static async waitForOllamaReady(maxAttempts = 30, initialDelay = 500) {
|
|
23
|
+
let delay = initialDelay;
|
|
24
|
+
for (let attempt = 1; attempt <= maxAttempts; attempt++) {
|
|
25
|
+
try {
|
|
26
|
+
// Try both command line and HTTP API checks
|
|
27
|
+
const cmdCheck = this.safeSpawn("ollama", ["list"]);
|
|
28
|
+
if (!cmdCheck.error && cmdCheck.status === 0) {
|
|
29
|
+
// Stronger HTTP API probe with response validation
|
|
30
|
+
try {
|
|
31
|
+
const apiCheck = this.safeSpawn("curl", [
|
|
32
|
+
"-s",
|
|
33
|
+
"--max-time",
|
|
34
|
+
"3",
|
|
35
|
+
"--fail", // Fail on HTTP error codes
|
|
36
|
+
"-w",
|
|
37
|
+
"%{http_code}",
|
|
38
|
+
"http://localhost:11434/api/tags",
|
|
39
|
+
]);
|
|
40
|
+
if (!apiCheck.error &&
|
|
41
|
+
apiCheck.status === 0 &&
|
|
42
|
+
apiCheck.stdout.trim()) {
|
|
43
|
+
// Validate that we get a proper HTTP 200 response and JSON structure
|
|
44
|
+
const output = apiCheck.stdout.trim();
|
|
45
|
+
const httpCodeMatch = output.match(/(\d{3})$/);
|
|
46
|
+
if (httpCodeMatch && httpCodeMatch[1] === "200") {
|
|
47
|
+
// Try to parse the JSON response (excluding HTTP code)
|
|
48
|
+
const jsonResponse = output.replace(/\d{3}$/, "");
|
|
49
|
+
try {
|
|
50
|
+
const parsedResponse = JSON.parse(jsonResponse);
|
|
51
|
+
// Verify it has the expected structure
|
|
52
|
+
if (parsedResponse && typeof parsedResponse === "object") {
|
|
53
|
+
return true; // Strong verification passed
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
catch {
|
|
57
|
+
// JSON parsing failed, but HTTP 200 is good enough
|
|
58
|
+
return true;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
catch {
|
|
64
|
+
// If curl fails, fall back to command check only
|
|
65
|
+
return true;
|
|
66
|
+
}
|
|
67
|
+
return true; // Command check passed
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
catch {
|
|
71
|
+
// Service not ready yet
|
|
72
|
+
}
|
|
73
|
+
// Wait before next attempt with exponential backoff (max 4 seconds)
|
|
74
|
+
await new Promise((resolve) => setTimeout(resolve, delay));
|
|
75
|
+
delay = Math.min(delay * 1.5, 4000);
|
|
76
|
+
}
|
|
77
|
+
return false; // Timeout reached
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Check if Ollama service is already running
|
|
81
|
+
*/
|
|
82
|
+
static isOllamaRunning() {
|
|
83
|
+
try {
|
|
84
|
+
const check = this.safeSpawn("ollama", ["list"]);
|
|
85
|
+
return !check.error && check.status === 0;
|
|
86
|
+
}
|
|
87
|
+
catch {
|
|
88
|
+
return false;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Unified Ollama start logic that works across platforms
|
|
93
|
+
*/
|
|
94
|
+
static async startOllamaService() {
|
|
95
|
+
logger.always(chalk.blue("Starting Ollama service..."));
|
|
96
|
+
// Check if already running
|
|
97
|
+
if (this.isOllamaRunning()) {
|
|
98
|
+
logger.always(chalk.yellow("Ollama service is already running!"));
|
|
99
|
+
return;
|
|
100
|
+
}
|
|
101
|
+
try {
|
|
102
|
+
if (process.platform === "darwin") {
|
|
103
|
+
logger.always(chalk.gray("Starting Ollama on macOS..."));
|
|
104
|
+
try {
|
|
105
|
+
this.safeSpawn("open", ["-a", "Ollama"]);
|
|
106
|
+
logger.always(chalk.green("✅ Ollama app started"));
|
|
107
|
+
}
|
|
108
|
+
catch {
|
|
109
|
+
const child = spawn("ollama", ["serve"], {
|
|
110
|
+
stdio: "ignore",
|
|
111
|
+
detached: true,
|
|
112
|
+
});
|
|
113
|
+
child.unref();
|
|
114
|
+
logger.always(chalk.green("✅ Ollama service started"));
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
else if (process.platform === "linux") {
|
|
118
|
+
logger.always(chalk.gray("Starting Ollama service on Linux..."));
|
|
119
|
+
try {
|
|
120
|
+
this.safeSpawn("systemctl", ["start", "ollama"]);
|
|
121
|
+
logger.always(chalk.green("✅ Ollama service started"));
|
|
122
|
+
}
|
|
123
|
+
catch {
|
|
124
|
+
const child = spawn("ollama", ["serve"], {
|
|
125
|
+
stdio: "ignore",
|
|
126
|
+
detached: true,
|
|
127
|
+
});
|
|
128
|
+
child.unref();
|
|
129
|
+
logger.always(chalk.green("✅ Ollama service started"));
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
else {
|
|
133
|
+
logger.always(chalk.gray("Starting Ollama on Windows..."));
|
|
134
|
+
// Security Note: Windows shell=true usage is intentional here for 'start' command.
|
|
135
|
+
// Arguments are controlled internally (no user input) and safeSpawn validates command names.
|
|
136
|
+
// This is safer than alternative Windows process creation methods for this specific use case.
|
|
137
|
+
this.safeSpawn("start", ["ollama", "serve"], {
|
|
138
|
+
stdio: "ignore",
|
|
139
|
+
shell: true,
|
|
140
|
+
});
|
|
141
|
+
logger.always(chalk.green("✅ Ollama service started"));
|
|
142
|
+
}
|
|
143
|
+
// Wait for service to become ready with readiness probe
|
|
144
|
+
const readinessSpinner = ora("Waiting for Ollama service to be ready...").start();
|
|
145
|
+
const isReady = await this.waitForOllamaReady();
|
|
146
|
+
if (isReady) {
|
|
147
|
+
readinessSpinner.succeed("Ollama service is ready!");
|
|
148
|
+
}
|
|
149
|
+
else {
|
|
150
|
+
readinessSpinner.warn("Ollama service may still be starting. Try 'ollama list' to check status.");
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
catch (error) {
|
|
154
|
+
logger.error(chalk.red("Failed to start Ollama service"));
|
|
155
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
156
|
+
logger.error(chalk.red("Error:", errorMessage));
|
|
157
|
+
logger.always(chalk.blue("\nTry starting Ollama manually or check installation"));
|
|
158
|
+
process.exit(1);
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
}
|
|
@@ -413,13 +413,15 @@ export class BaseProvider {
|
|
|
413
413
|
return baseModel;
|
|
414
414
|
}
|
|
415
415
|
try {
|
|
416
|
+
// Create a new factory instance with the specified options
|
|
417
|
+
const factory = new MiddlewareFactory(middlewareOptions);
|
|
416
418
|
// Create middleware context
|
|
417
|
-
const context =
|
|
419
|
+
const context = factory.createContext(this.providerName, this.modelName, options, {
|
|
418
420
|
sessionId: this.sessionId,
|
|
419
421
|
userId: this.userId,
|
|
420
422
|
});
|
|
421
423
|
// Apply middleware to the model
|
|
422
|
-
const wrappedModel =
|
|
424
|
+
const wrappedModel = factory.applyMiddleware(baseModel, context, middlewareOptions);
|
|
423
425
|
logger.debug(`Applied middleware to ${this.providerName} model`, {
|
|
424
426
|
provider: this.providerName,
|
|
425
427
|
model: this.modelName,
|
package/dist/index.d.ts
CHANGED
|
@@ -18,9 +18,8 @@ export { getBestProvider, getAvailableProviders, isValidProvider, } from "./util
|
|
|
18
18
|
export { NeuroLink } from "./neurolink.js";
|
|
19
19
|
export type { ProviderStatus, MCPStatus } from "./neurolink.js";
|
|
20
20
|
export type { MCPServerInfo } from "./types/mcpTypes.js";
|
|
21
|
-
export type { NeuroLinkMiddleware, MiddlewareContext, MiddlewareFactoryOptions, } from "./middleware/types.js";
|
|
22
|
-
export {
|
|
23
|
-
export { createAnalyticsMiddleware } from "./middleware/builtin/analytics.js";
|
|
21
|
+
export type { NeuroLinkMiddleware, MiddlewareContext, MiddlewareFactoryOptions, MiddlewarePreset, MiddlewareConfig, } from "./middleware/types.js";
|
|
22
|
+
export { MiddlewareFactory } from "./middleware/factory.js";
|
|
24
23
|
export declare const VERSION = "1.0.0";
|
|
25
24
|
/**
|
|
26
25
|
* Quick start factory function
|
package/dist/index.js
CHANGED
|
@@ -16,8 +16,7 @@ export { BedrockModels, OpenAIModels, VertexModels, DEFAULT_PROVIDER_CONFIGS, }
|
|
|
16
16
|
export { getBestProvider, getAvailableProviders, isValidProvider, } from "./utils/providerUtils.js";
|
|
17
17
|
// Main NeuroLink wrapper class and diagnostic types
|
|
18
18
|
export { NeuroLink } from "./neurolink.js";
|
|
19
|
-
export {
|
|
20
|
-
export { createAnalyticsMiddleware } from "./middleware/builtin/analytics.js";
|
|
19
|
+
export { MiddlewareFactory } from "./middleware/factory.js";
|
|
21
20
|
// Version
|
|
22
21
|
export const VERSION = "1.0.0";
|
|
23
22
|
/**
|
|
@@ -413,13 +413,15 @@ export class BaseProvider {
|
|
|
413
413
|
return baseModel;
|
|
414
414
|
}
|
|
415
415
|
try {
|
|
416
|
+
// Create a new factory instance with the specified options
|
|
417
|
+
const factory = new MiddlewareFactory(middlewareOptions);
|
|
416
418
|
// Create middleware context
|
|
417
|
-
const context =
|
|
419
|
+
const context = factory.createContext(this.providerName, this.modelName, options, {
|
|
418
420
|
sessionId: this.sessionId,
|
|
419
421
|
userId: this.userId,
|
|
420
422
|
});
|
|
421
423
|
// Apply middleware to the model
|
|
422
|
-
const wrappedModel =
|
|
424
|
+
const wrappedModel = factory.applyMiddleware(baseModel, context, middlewareOptions);
|
|
423
425
|
logger.debug(`Applied middleware to ${this.providerName} model`, {
|
|
424
426
|
provider: this.providerName,
|
|
425
427
|
model: this.modelName,
|
|
@@ -20,8 +20,8 @@ declare const ModelConfigSchema: z.ZodObject<{
|
|
|
20
20
|
contextWindow: z.ZodNumber;
|
|
21
21
|
releaseDate: z.ZodString;
|
|
22
22
|
}, "strip", z.ZodTypeAny, {
|
|
23
|
-
capabilities: string[];
|
|
24
23
|
id: string;
|
|
24
|
+
capabilities: string[];
|
|
25
25
|
displayName: string;
|
|
26
26
|
deprecated: boolean;
|
|
27
27
|
pricing: {
|
|
@@ -31,8 +31,8 @@ declare const ModelConfigSchema: z.ZodObject<{
|
|
|
31
31
|
contextWindow: number;
|
|
32
32
|
releaseDate: string;
|
|
33
33
|
}, {
|
|
34
|
-
capabilities: string[];
|
|
35
34
|
id: string;
|
|
35
|
+
capabilities: string[];
|
|
36
36
|
displayName: string;
|
|
37
37
|
deprecated: boolean;
|
|
38
38
|
pricing: {
|
|
@@ -63,8 +63,8 @@ declare const ModelRegistrySchema: z.ZodObject<{
|
|
|
63
63
|
contextWindow: z.ZodNumber;
|
|
64
64
|
releaseDate: z.ZodString;
|
|
65
65
|
}, "strip", z.ZodTypeAny, {
|
|
66
|
-
capabilities: string[];
|
|
67
66
|
id: string;
|
|
67
|
+
capabilities: string[];
|
|
68
68
|
displayName: string;
|
|
69
69
|
deprecated: boolean;
|
|
70
70
|
pricing: {
|
|
@@ -74,8 +74,8 @@ declare const ModelRegistrySchema: z.ZodObject<{
|
|
|
74
74
|
contextWindow: number;
|
|
75
75
|
releaseDate: string;
|
|
76
76
|
}, {
|
|
77
|
-
capabilities: string[];
|
|
78
77
|
id: string;
|
|
78
|
+
capabilities: string[];
|
|
79
79
|
displayName: string;
|
|
80
80
|
deprecated: boolean;
|
|
81
81
|
pricing: {
|
|
@@ -89,8 +89,8 @@ declare const ModelRegistrySchema: z.ZodObject<{
|
|
|
89
89
|
defaults: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodString>>;
|
|
90
90
|
}, "strip", z.ZodTypeAny, {
|
|
91
91
|
models: Record<string, Record<string, {
|
|
92
|
-
capabilities: string[];
|
|
93
92
|
id: string;
|
|
93
|
+
capabilities: string[];
|
|
94
94
|
displayName: string;
|
|
95
95
|
deprecated: boolean;
|
|
96
96
|
pricing: {
|
|
@@ -106,8 +106,8 @@ declare const ModelRegistrySchema: z.ZodObject<{
|
|
|
106
106
|
aliases?: Record<string, string> | undefined;
|
|
107
107
|
}, {
|
|
108
108
|
models: Record<string, Record<string, {
|
|
109
|
-
capabilities: string[];
|
|
110
109
|
id: string;
|
|
110
|
+
capabilities: string[];
|
|
111
111
|
displayName: string;
|
|
112
112
|
deprecated: boolean;
|
|
113
113
|
pricing: {
|
package/dist/lib/index.d.ts
CHANGED
|
@@ -18,9 +18,8 @@ export { getBestProvider, getAvailableProviders, isValidProvider, } from "./util
|
|
|
18
18
|
export { NeuroLink } from "./neurolink.js";
|
|
19
19
|
export type { ProviderStatus, MCPStatus } from "./neurolink.js";
|
|
20
20
|
export type { MCPServerInfo } from "./types/mcpTypes.js";
|
|
21
|
-
export type { NeuroLinkMiddleware, MiddlewareContext, MiddlewareFactoryOptions, } from "./middleware/types.js";
|
|
22
|
-
export {
|
|
23
|
-
export { createAnalyticsMiddleware } from "./middleware/builtin/analytics.js";
|
|
21
|
+
export type { NeuroLinkMiddleware, MiddlewareContext, MiddlewareFactoryOptions, MiddlewarePreset, MiddlewareConfig, } from "./middleware/types.js";
|
|
22
|
+
export { MiddlewareFactory } from "./middleware/factory.js";
|
|
24
23
|
export declare const VERSION = "1.0.0";
|
|
25
24
|
/**
|
|
26
25
|
* Quick start factory function
|
package/dist/lib/index.js
CHANGED
|
@@ -16,8 +16,7 @@ export { BedrockModels, OpenAIModels, VertexModels, DEFAULT_PROVIDER_CONFIGS, }
|
|
|
16
16
|
export { getBestProvider, getAvailableProviders, isValidProvider, } from "./utils/providerUtils.js";
|
|
17
17
|
// Main NeuroLink wrapper class and diagnostic types
|
|
18
18
|
export { NeuroLink } from "./neurolink.js";
|
|
19
|
-
export {
|
|
20
|
-
export { createAnalyticsMiddleware } from "./middleware/builtin/analytics.js";
|
|
19
|
+
export { MiddlewareFactory } from "./middleware/factory.js";
|
|
21
20
|
// Version
|
|
22
21
|
export const VERSION = "1.0.0";
|
|
23
22
|
/**
|
|
@@ -78,20 +78,19 @@ export function createAnalyticsMiddleware() {
|
|
|
78
78
|
streamingMode: true,
|
|
79
79
|
};
|
|
80
80
|
requestMetrics.set(requestId, streamAnalytics);
|
|
81
|
-
//
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
}
|
|
94
|
-
updatedResult.rawResponse.neurolink.analytics = streamAnalytics;
|
|
81
|
+
// Ensure the result includes all required properties
|
|
82
|
+
const updatedResult = {
|
|
83
|
+
...result,
|
|
84
|
+
stream: result.stream || new ReadableStream(),
|
|
85
|
+
rawCall: result.rawCall || { rawPrompt: null, rawSettings: {} },
|
|
86
|
+
rawResponse: {
|
|
87
|
+
...(result.rawResponse || {}),
|
|
88
|
+
neurolink: {
|
|
89
|
+
...(result.rawResponse?.neurolink ?? {}),
|
|
90
|
+
analytics: streamAnalytics,
|
|
91
|
+
},
|
|
92
|
+
},
|
|
93
|
+
};
|
|
95
94
|
return updatedResult;
|
|
96
95
|
}
|
|
97
96
|
catch (error) {
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { LanguageModelV1 } from "ai";
|
|
2
|
+
import type { NeuroLinkMiddleware } from "../types.js";
|
|
3
|
+
/**
|
|
4
|
+
* Configuration for the Guardrails middleware.
|
|
5
|
+
*/
|
|
6
|
+
export interface GuardrailsMiddlewareConfig {
|
|
7
|
+
badWords?: {
|
|
8
|
+
enabled?: boolean;
|
|
9
|
+
list?: string[];
|
|
10
|
+
};
|
|
11
|
+
modelFilter?: {
|
|
12
|
+
enabled?: boolean;
|
|
13
|
+
filterModel?: LanguageModelV1;
|
|
14
|
+
};
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Create Guardrails AI middleware for content filtering and policy enforcement.
|
|
18
|
+
* @param config - Configuration for the guardrails middleware.
|
|
19
|
+
*/
|
|
20
|
+
export declare function createGuardrailsMiddleware(config?: GuardrailsMiddlewareConfig): NeuroLinkMiddleware;
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import { generateText } from "ai";
|
|
2
|
+
import { logger } from "../../utils/logger.js";
|
|
3
|
+
/**
|
|
4
|
+
* Create Guardrails AI middleware for content filtering and policy enforcement.
|
|
5
|
+
* @param config - Configuration for the guardrails middleware.
|
|
6
|
+
*/
|
|
7
|
+
export function createGuardrailsMiddleware(config = {}) {
|
|
8
|
+
const metadata = {
|
|
9
|
+
id: "guardrails",
|
|
10
|
+
name: "Guardrails AI",
|
|
11
|
+
description: "Provides content filtering and policy enforcement using custom rules and AI models.",
|
|
12
|
+
priority: 90,
|
|
13
|
+
defaultEnabled: true,
|
|
14
|
+
};
|
|
15
|
+
const middleware = {
|
|
16
|
+
wrapGenerate: async ({ doGenerate, params }) => {
|
|
17
|
+
logger.debug(`[GuardrailsMiddleware] Applying to generate call.`, {
|
|
18
|
+
badWordsEnabled: !!config.badWords?.enabled,
|
|
19
|
+
modelFilterEnabled: !!config.modelFilter?.enabled,
|
|
20
|
+
});
|
|
21
|
+
let result = await doGenerate();
|
|
22
|
+
// 1. Bad Word Filtering
|
|
23
|
+
if (config.badWords?.enabled && config.badWords.list) {
|
|
24
|
+
let filteredText = result.text;
|
|
25
|
+
for (const term of config.badWords.list) {
|
|
26
|
+
const regex = new RegExp(term, "gi");
|
|
27
|
+
filteredText = filteredText?.replace(regex, "*".repeat(term.length));
|
|
28
|
+
}
|
|
29
|
+
if (result.text !== filteredText) {
|
|
30
|
+
logger.debug(`[GuardrailsMiddleware] Redacted bad words from text.`);
|
|
31
|
+
result = { ...result, text: filteredText ?? "" };
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
// 2. Model-based Filtering
|
|
35
|
+
if (config.modelFilter?.enabled && config.modelFilter.filterModel) {
|
|
36
|
+
logger.debug(`[GuardrailsMiddleware] Invoking model-based filter.`);
|
|
37
|
+
try {
|
|
38
|
+
const filterPrompt = `Is the following text safe? Respond with only "safe" or "unsafe".\n\nText: "${result.text}"`;
|
|
39
|
+
const { text: filterResponse } = await generateText({
|
|
40
|
+
model: config.modelFilter.filterModel, // Cast as any to satisfy type, will be resolved in integration
|
|
41
|
+
prompt: filterPrompt,
|
|
42
|
+
});
|
|
43
|
+
if (filterResponse.toLowerCase().trim() === "unsafe") {
|
|
44
|
+
logger.warn(`[GuardrailsMiddleware] Model-based filter flagged content as unsafe.`);
|
|
45
|
+
result = { ...result, text: "<REDACTED BY AI GUARDRAIL>" };
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
catch (error) {
|
|
49
|
+
logger.error(`[GuardrailsMiddleware] Model-based filter failed.`, {
|
|
50
|
+
error,
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
return result;
|
|
55
|
+
},
|
|
56
|
+
wrapStream: async ({ doStream, params }) => {
|
|
57
|
+
logger.debug(`[GuardrailsMiddleware] Applying to stream call.`, {
|
|
58
|
+
badWordsEnabled: !!config.badWords?.enabled,
|
|
59
|
+
});
|
|
60
|
+
const { stream, ...rest } = await doStream();
|
|
61
|
+
// Note: Model-based filtering is not applied to streams in this version
|
|
62
|
+
// as it requires the full text for analysis.
|
|
63
|
+
const transformStream = new TransformStream({
|
|
64
|
+
transform(chunk, controller) {
|
|
65
|
+
let filteredChunk = chunk;
|
|
66
|
+
if (config.badWords?.enabled && config.badWords.list) {
|
|
67
|
+
for (const term of config.badWords.list) {
|
|
68
|
+
const regex = new RegExp(term, "gi");
|
|
69
|
+
if (typeof filteredChunk === "string") {
|
|
70
|
+
filteredChunk = filteredChunk.replace(regex, "*".repeat(term.length));
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
controller.enqueue(filteredChunk);
|
|
75
|
+
},
|
|
76
|
+
});
|
|
77
|
+
return {
|
|
78
|
+
stream: stream.pipeThrough(transformStream),
|
|
79
|
+
...rest,
|
|
80
|
+
};
|
|
81
|
+
},
|
|
82
|
+
};
|
|
83
|
+
return {
|
|
84
|
+
...middleware,
|
|
85
|
+
metadata,
|
|
86
|
+
};
|
|
87
|
+
}
|
|
@@ -1,36 +1,51 @@
|
|
|
1
1
|
import type { LanguageModelV1 } from "ai";
|
|
2
|
-
import type { MiddlewareContext, MiddlewareConfig, MiddlewareFactoryOptions, MiddlewareChainStats } from "./types.js";
|
|
2
|
+
import type { MiddlewareContext, MiddlewareConfig, MiddlewareFactoryOptions, MiddlewareChainStats, MiddlewarePreset, NeuroLinkMiddleware, MiddlewareRegistrationOptions } from "./types.js";
|
|
3
|
+
import { MiddlewareRegistry } from "./registry.js";
|
|
3
4
|
/**
|
|
4
|
-
* Middleware factory for creating and applying middleware chains
|
|
5
|
+
* Middleware factory for creating and applying middleware chains.
|
|
6
|
+
* Each factory instance manages its own registry and configuration.
|
|
5
7
|
*/
|
|
6
8
|
export declare class MiddlewareFactory {
|
|
9
|
+
registry: MiddlewareRegistry;
|
|
10
|
+
presets: Map<string, MiddlewarePreset>;
|
|
11
|
+
private options;
|
|
12
|
+
constructor(options?: MiddlewareFactoryOptions);
|
|
13
|
+
/**
|
|
14
|
+
* Initialize the factory with built-in middleware and presets
|
|
15
|
+
*/
|
|
16
|
+
private initialize;
|
|
17
|
+
/**
|
|
18
|
+
* Register a custom preset
|
|
19
|
+
*/
|
|
20
|
+
registerPreset(preset: MiddlewarePreset, replace?: boolean): void;
|
|
21
|
+
/**
|
|
22
|
+
* Register a custom middleware
|
|
23
|
+
*/
|
|
24
|
+
register(middleware: NeuroLinkMiddleware, options?: MiddlewareRegistrationOptions): void;
|
|
7
25
|
/**
|
|
8
26
|
* Apply middleware to a language model
|
|
9
27
|
*/
|
|
10
|
-
|
|
28
|
+
applyMiddleware(model: LanguageModelV1, context: MiddlewareContext, options?: MiddlewareFactoryOptions): LanguageModelV1;
|
|
29
|
+
private getCreator;
|
|
11
30
|
/**
|
|
12
31
|
* Build middleware configuration from factory options
|
|
13
32
|
*/
|
|
14
|
-
private
|
|
33
|
+
private buildMiddlewareConfig;
|
|
15
34
|
/**
|
|
16
35
|
* Get preset configuration
|
|
17
36
|
*/
|
|
18
|
-
private
|
|
19
|
-
/**
|
|
20
|
-
* Get built-in preset configurations
|
|
21
|
-
*/
|
|
22
|
-
private static getBuiltInPresets;
|
|
37
|
+
private getPresetConfig;
|
|
23
38
|
/**
|
|
24
39
|
* Create middleware context from provider and options
|
|
25
40
|
*/
|
|
26
|
-
|
|
41
|
+
createContext(provider: string, model: string, options?: Record<string, unknown>, session?: {
|
|
27
42
|
sessionId?: string;
|
|
28
43
|
userId?: string;
|
|
29
44
|
}): MiddlewareContext;
|
|
30
45
|
/**
|
|
31
46
|
* Validate middleware configuration
|
|
32
47
|
*/
|
|
33
|
-
|
|
48
|
+
validateConfig(config: Record<string, MiddlewareConfig>): {
|
|
34
49
|
isValid: boolean;
|
|
35
50
|
errors: string[];
|
|
36
51
|
warnings: string[];
|
|
@@ -38,7 +53,7 @@ export declare class MiddlewareFactory {
|
|
|
38
53
|
/**
|
|
39
54
|
* Get available presets
|
|
40
55
|
*/
|
|
41
|
-
|
|
56
|
+
getAvailablePresets(): Array<{
|
|
42
57
|
name: string;
|
|
43
58
|
description: string;
|
|
44
59
|
middleware: string[];
|
|
@@ -46,9 +61,9 @@ export declare class MiddlewareFactory {
|
|
|
46
61
|
/**
|
|
47
62
|
* Get middleware chain statistics
|
|
48
63
|
*/
|
|
49
|
-
|
|
64
|
+
getChainStats(context: MiddlewareContext, config: Record<string, MiddlewareConfig>): MiddlewareChainStats;
|
|
50
65
|
/**
|
|
51
66
|
* Create a middleware-enabled model factory function
|
|
52
67
|
*/
|
|
53
|
-
|
|
68
|
+
createModelFactory(baseModelFactory: () => Promise<LanguageModelV1>, defaultOptions?: MiddlewareFactoryOptions): (context: MiddlewareContext, options?: MiddlewareFactoryOptions) => Promise<LanguageModelV1>;
|
|
54
69
|
}
|