@juspay/neurolink 7.28.1 → 7.29.1
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 +12 -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/lib/neurolink.d.ts +297 -4
- package/dist/lib/neurolink.js +297 -4
- package/dist/lib/providers/googleVertex.js +13 -4
- 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/dist/neurolink.d.ts +297 -4
- package/dist/neurolink.js +297 -4
- package/dist/providers/googleVertex.js +13 -4
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,15 @@
|
|
|
1
|
+
## [7.29.1](https://github.com/juspay/neurolink/compare/v7.29.0...v7.29.1) (2025-08-28)
|
|
2
|
+
|
|
3
|
+
### Bug Fixes
|
|
4
|
+
|
|
5
|
+
- **(vertex):** restored support for adc ([238666a](https://github.com/juspay/neurolink/commit/238666ab907fc16945d5de6c5f79637be128f4e6))
|
|
6
|
+
|
|
7
|
+
## [7.29.0](https://github.com/juspay/neurolink/compare/v7.28.1...v7.29.0) (2025-08-26)
|
|
8
|
+
|
|
9
|
+
### Features
|
|
10
|
+
|
|
11
|
+
- **(guardrails):** added guardrails as a middleware ([ac60f6b](https://github.com/juspay/neurolink/commit/ac60f6b143a58f86e17481ddb3e067e5307391cf))
|
|
12
|
+
|
|
1
13
|
## [7.28.1](https://github.com/juspay/neurolink/compare/v7.28.0...v7.28.1) (2025-08-26)
|
|
2
14
|
|
|
3
15
|
### Bug Fixes
|
|
@@ -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
|
}
|