@ainetwork/adk-provider-memory-mongodb 0.2.2 → 0.2.4

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.
@@ -1,9 +1,8 @@
1
+ import { Intent } from '@ainetwork/adk/types/memory';
1
2
  import mongoose, { Document } from 'mongoose';
2
3
 
3
- interface IntentDocument extends Document {
4
- name: string;
5
- description: string;
6
- prompt?: string;
4
+ interface IntentDocument extends Omit<Document, 'id'>, Omit<Intent, 'id'> {
5
+ id: string;
7
6
  }
8
7
  declare const IntentModel: mongoose.Model<IntentDocument, {}, {}, {}, mongoose.Document<unknown, {}, IntentDocument, {}> & IntentDocument & Required<{
9
8
  _id: unknown;
@@ -1,9 +1,8 @@
1
+ import { Intent } from '@ainetwork/adk/types/memory';
1
2
  import mongoose, { Document } from 'mongoose';
2
3
 
3
- interface IntentDocument extends Document {
4
- name: string;
5
- description: string;
6
- prompt?: string;
4
+ interface IntentDocument extends Omit<Document, 'id'>, Omit<Intent, 'id'> {
5
+ id: string;
7
6
  }
8
7
  declare const IntentModel: mongoose.Model<IntentDocument, {}, {}, {}, mongoose.Document<unknown, {}, IntentDocument, {}> & IntentDocument & Required<{
9
8
  _id: unknown;
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  IntentModel
3
- } from "../chunk-722XAZKL.js";
3
+ } from "../chunk-YFW7JXII.js";
4
4
  export {
5
5
  IntentModel
6
6
  };
@@ -2,51 +2,229 @@ import { IMemory } from "node_modules/@ainetwork/adk/dist/esm/modules/memory/bas
2
2
  import mongoose from "mongoose";
3
3
  import { loggers } from "@ainetwork/adk/utils/logger";
4
4
 
5
+ export interface MongoDBMemoryConfig {
6
+ uri: string;
7
+ maxReconnectAttempts?: number;
8
+ reconnectInterval?: number;
9
+ maxPoolSize?: number;
10
+ serverSelectionTimeoutMS?: number;
11
+ socketTimeoutMS?: number;
12
+ connectTimeoutMS?: number;
13
+ operationTimeoutMS?: number; // Timeout for database operations
14
+ }
15
+
5
16
  export class MongoDBMemory implements IMemory {
6
- private _isConnected: boolean = false;
7
- private _uri: string;
17
+ private static instance: MongoDBMemory;
18
+ private uri: string;
19
+ private connected: boolean = false;
20
+ private reconnectAttempts: number = 0;
21
+ private maxReconnectAttempts: number;
22
+ private reconnectInterval: number;
23
+ private reconnecting: boolean = false;
24
+ private connectionConfig: mongoose.ConnectOptions;
25
+ private eventListenersSetup: boolean = false;
26
+ private operationTimeoutMS: number;
27
+
28
+ constructor(config: string | MongoDBMemoryConfig) {
29
+ const cfg = typeof config === 'string' ? { uri: config } : config;
30
+
31
+ this.uri = cfg.uri;
32
+ this.maxReconnectAttempts = cfg.maxReconnectAttempts ?? 5;
33
+ this.reconnectInterval = cfg.reconnectInterval ?? 5000;
34
+ this.operationTimeoutMS = cfg.operationTimeoutMS ?? 10000; // Default 10 seconds
35
+ this.connectionConfig = {
36
+ maxPoolSize: cfg.maxPoolSize ?? 1,
37
+ serverSelectionTimeoutMS: cfg.serverSelectionTimeoutMS ?? 30000,
38
+ socketTimeoutMS: cfg.socketTimeoutMS ?? 45000,
39
+ connectTimeoutMS: cfg.connectTimeoutMS ?? 30000,
40
+ bufferCommands: false,
41
+ };
42
+
43
+ if (!MongoDBMemory.instance) {
44
+ MongoDBMemory.instance = this;
45
+ this.setupMongooseEventListeners();
46
+ } else {
47
+ // Use existing instance's connection state
48
+ this.connected = MongoDBMemory.instance.connected;
49
+ this.operationTimeoutMS = MongoDBMemory.instance.operationTimeoutMS;
50
+ }
51
+ }
52
+
53
+ private setupMongooseEventListeners(): void {
54
+ if (this.eventListenersSetup) return;
55
+
56
+ this.eventListenersSetup = true;
57
+
58
+ mongoose.connection.on("connected", () => {
59
+ this.connected = true;
60
+ this.reconnectAttempts = 0;
61
+ this.reconnecting = false;
62
+ loggers.agent.info("MongoDB connected successfully");
63
+ });
64
+
65
+ mongoose.connection.on("disconnected", () => {
66
+ this.connected = false;
67
+ loggers.agent.warn("MongoDB disconnected");
68
+ this.handleDisconnection();
69
+ });
70
+
71
+ mongoose.connection.on("error", (error) => {
72
+ this.connected = false;
73
+ loggers.agent.error("MongoDB connection error:", error);
74
+ this.handleDisconnection();
75
+ });
76
+
77
+ mongoose.connection.on("reconnected", () => {
78
+ this.connected = true;
79
+ this.reconnectAttempts = 0;
80
+ this.reconnecting = false;
81
+ loggers.agent.info("MongoDB reconnected successfully");
82
+ });
83
+ }
84
+
85
+ private async handleDisconnection(): Promise<void> {
86
+ if (this.reconnecting) {
87
+ return;
88
+ }
89
+
90
+ this.reconnecting = true;
91
+
92
+ while (this.reconnectAttempts < this.maxReconnectAttempts && !this.isConnected) {
93
+ this.reconnectAttempts++;
94
+ loggers.agent.info(
95
+ `Attempting to reconnect to MongoDB (${this.reconnectAttempts}/${this.maxReconnectAttempts})...`
96
+ );
97
+
98
+ try {
99
+ await mongoose.connect(this.uri, this.connectionConfig);
100
+ this.connected = true;
101
+ this.reconnectAttempts = 0;
102
+ this.reconnecting = false;
103
+ loggers.agent.info("MongoDB reconnection successful");
104
+ return;
105
+ } catch (error) {
106
+ loggers.agent.error(
107
+ `Reconnection attempt ${this.reconnectAttempts} failed:`,
108
+ error
109
+ );
8
110
 
9
- constructor(uri: string) {
10
- this._uri = uri;
111
+ if (this.reconnectAttempts < this.maxReconnectAttempts) {
112
+ await new Promise((resolve) =>
113
+ setTimeout(resolve, this.reconnectInterval)
114
+ );
115
+ }
116
+ }
117
+ }
118
+
119
+ this.reconnecting = false;
120
+
121
+ if (!this.isConnected) {
122
+ loggers.agent.error(
123
+ `Failed to reconnect to MongoDB after ${this.maxReconnectAttempts} attempts`
124
+ );
125
+ }
11
126
  }
12
127
 
13
128
  public async connect(): Promise<void> {
14
- if (this._isConnected) {
15
- return;
16
- }
17
-
18
- try {
19
- await mongoose.connect(this._uri, {
20
- maxPoolSize: 1,
21
- serverSelectionTimeoutMS: 30000,
22
- socketTimeoutMS: 45000,
23
- connectTimeoutMS: 30000,
24
- bufferCommands: false,
25
- });
26
- this._isConnected = true;
27
- loggers.agent.info("MongoDB connected successfully");
28
- } catch (error) {
29
- loggers.agent.error("Failed to connect to MongoDB:", error);
30
- throw error;
31
- }
129
+ if (this.connected) {
130
+ return;
131
+ }
132
+
133
+ try {
134
+ await mongoose.connect(this.uri, this.connectionConfig);
135
+ this.connected = true;
136
+ this.reconnectAttempts = 0;
137
+ } catch (error) {
138
+ loggers.agent.error("Failed to connect to MongoDB:", error);
139
+ throw error;
140
+ }
32
141
  }
33
142
 
34
143
  public async disconnect(): Promise<void> {
35
- if (!this.isConnected) {
36
- return;
37
- }
144
+ if (!this.isConnected) {
145
+ return;
146
+ }
38
147
 
39
- try {
40
- await mongoose.disconnect();
41
- this._isConnected = false;
42
- loggers.agent.info("MongoDB disconnected successfully");
43
- } catch (error) {
44
- loggers.agent.error("Failed to disconnect from MongoDB:", error);
45
- throw error;
46
- }
148
+ try {
149
+ await mongoose.disconnect();
150
+ this.connected = false;
151
+ } catch (error) {
152
+ loggers.agent.error("Failed to disconnect from MongoDB:", error);
153
+ throw error;
154
+ }
47
155
  }
48
156
 
49
157
  public isConnected(): boolean {
50
- return this._isConnected;
158
+ return this.connected;
159
+ }
160
+
161
+ private async ensureConnection(): Promise<void> {
162
+ if (!this.isConnected && !this.reconnecting) {
163
+ await this.connect();
164
+ }
165
+
166
+ // Wait for reconnection if in progress
167
+ const maxWaitTime = 30000; // 30 seconds
168
+ const startTime = Date.now();
169
+ while (this.reconnecting && Date.now() - startTime < maxWaitTime) {
170
+ await new Promise((resolve) => setTimeout(resolve, 100));
171
+ }
172
+
173
+ if (!this.isConnected) {
174
+ throw new Error("MongoDB is not connected and reconnection failed");
175
+ }
176
+ }
177
+
178
+ /**
179
+ * Get the operation timeout in milliseconds
180
+ */
181
+ protected getOperationTimeout(): number {
182
+ return this.operationTimeoutMS;
183
+ }
184
+
185
+ /**
186
+ * Execute a database operation with automatic retry on connection errors
187
+ * Note: Use mongoose's maxTimeMS option in queries for timeout control
188
+ */
189
+ protected async executeWithRetry<T>(
190
+ operation: () => Promise<T>,
191
+ operationName: string = "Database operation"
192
+ ): Promise<T> {
193
+ await this.ensureConnection();
194
+
195
+ try {
196
+ return await operation();
197
+ } catch (error: any) {
198
+ // Check if it's a timeout error from MongoDB
199
+ if (error.code === 50 || error.message?.includes("operation exceeded time limit")) {
200
+ loggers.agent.error(`${operationName} exceeded time limit`);
201
+ throw error;
202
+ }
203
+
204
+ // Check if it's a connection-related error
205
+ if (
206
+ error.name === "MongoNetworkError" ||
207
+ error.name === "MongoServerError" ||
208
+ error.message?.includes("connection") ||
209
+ error.message?.includes("disconnect")
210
+ ) {
211
+ loggers.agent.warn(
212
+ `${operationName} failed due to connection issue, attempting reconnection...`
213
+ );
214
+
215
+ await this.ensureConnection();
216
+
217
+ // Retry the operation once after reconnection
218
+ try {
219
+ return await operation();
220
+ } catch (retryError: any) {
221
+ loggers.agent.error(`${operationName} failed after retry:`, retryError);
222
+ throw retryError;
223
+ }
224
+ }
225
+
226
+ // If it's not a connection error, just throw it
227
+ throw error;
228
+ }
51
229
  }
52
- }
230
+ }
@@ -2,62 +2,57 @@ import type { Intent } from "@ainetwork/adk/types/memory";
2
2
  import { IIntentMemory } from "@ainetwork/adk/modules";
3
3
  import { MongoDBMemory } from "./base.memory";
4
4
  import { IntentModel } from "../models/intent.model";
5
- import { Types } from "mongoose";
6
5
 
7
6
  export class MongoDBIntent extends MongoDBMemory implements IIntentMemory {
8
7
  public async getIntent(intentId: string): Promise<Intent | undefined> {
9
- const intent = await IntentModel.findById(new Types.ObjectId(intentId));
10
- if (intent) {
11
- return {
12
- name: intent.name,
13
- description: intent.description,
14
- prompt: intent.prompt,
15
- } as Intent;
16
- }
17
- return undefined;
8
+ return this.executeWithRetry(async () => {
9
+ const timeout = this.getOperationTimeout();
10
+ const intent = await IntentModel.findOne({ id: intentId })
11
+ .maxTimeMS(timeout)
12
+ .lean<Intent>();
13
+ return intent || undefined;
14
+ }, `getIntent(${intentId})`);
18
15
  };
19
16
 
20
- public async getIntentByName(intentName: string): Promise<Intent | undefined> {
21
- const intent = await IntentModel.findOne({ name: intentName });
22
- if (intent) {
23
- return {
24
- name: intent.name,
25
- description: intent.description,
26
- prompt: intent.prompt,
27
- } as Intent;
28
- }
29
- return undefined;
30
- }
17
+ public async getIntentByName(intentName: string): Promise<Intent | undefined> {
18
+ return this.executeWithRetry(async () => {
19
+ const timeout = this.getOperationTimeout();
20
+ const intent = await IntentModel.findOne({ name: intentName })
21
+ .maxTimeMS(timeout)
22
+ .lean<Intent>();
23
+ return intent || undefined;
24
+ }, `getIntentByName(${intentName})`);
25
+ }
31
26
 
32
- public async saveIntent(intent: Intent): Promise<void> {
33
- // ObjectId automatically generated (MongoDB automatically generates)
34
- await IntentModel.create({
35
- name: intent.name,
36
- description: intent.description,
37
- prompt: intent.prompt,
38
- });
27
+ public async saveIntent(intent: Intent): Promise<void> {
28
+ return this.executeWithRetry(async () => {
29
+ await IntentModel.create(intent);
30
+ }, `saveIntent(${intent.id})`);
39
31
  };
40
32
 
41
- public async updateIntent(intentId: string, intent: Intent): Promise<void> {
42
- await IntentModel.updateOne({
43
- _id: new Types.ObjectId(intentId),
44
- },{
45
- name: intent.name,
46
- description: intent.description,
47
- prompt: intent.prompt,
48
- });
33
+ public async updateIntent(intentId: string, intent: Intent): Promise<void> {
34
+ return this.executeWithRetry(async () => {
35
+ const timeout = this.getOperationTimeout();
36
+ await IntentModel.updateOne({
37
+ id: intentId,
38
+ }, intent).maxTimeMS(timeout);
39
+ }, `updateIntent(${intentId})`);
49
40
  };
50
41
 
51
- public async deleteIntent(intentId: string): Promise<void> {
52
- await IntentModel.deleteOne({ _id: new Types.ObjectId(intentId) });
42
+ public async deleteIntent(intentId: string): Promise<void> {
43
+ return this.executeWithRetry(async () => {
44
+ const timeout = this.getOperationTimeout();
45
+ await IntentModel.deleteOne({ id: intentId }).maxTimeMS(timeout);
46
+ }, `deleteIntent(${intentId})`);
53
47
  };
54
48
 
55
- public async listIntents(): Promise<Intent[]> {
56
- const intents = await IntentModel.find();
57
- return intents.map(intent => ({
58
- name: intent.name,
59
- description: intent.description,
60
- prompt: intent.prompt,
61
- } as Intent));
49
+ public async listIntents(): Promise<Intent[]> {
50
+ return this.executeWithRetry(async () => {
51
+ const timeout = this.getOperationTimeout();
52
+ const intents = await IntentModel.find()
53
+ .maxTimeMS(timeout)
54
+ .lean<Intent[]>();
55
+ return intents;
56
+ }, `listIntents()`);
62
57
  };
63
- }
58
+ }
@@ -15,101 +15,114 @@ export class MongoDBThread extends MongoDBMemory implements IThreadMemory {
15
15
  userId: string,
16
16
  threadId: string
17
17
  ): Promise<ThreadObject | undefined> {
18
- const thread = await ThreadModel.findOne({ threadId, userId });
19
- const messages = await MessageModel.find({ threadId, userId }).sort({
20
- timestamp: 1,
21
- });
18
+ return this.executeWithRetry(async () => {
19
+ const timeout = this.getOperationTimeout();
20
+ const thread = await ThreadModel.findOne({ threadId, userId }).maxTimeMS(timeout);
21
+ const messages = await MessageModel.find({ threadId, userId })
22
+ .sort({ timestamp: 1 })
23
+ .maxTimeMS(timeout);
22
24
 
23
- if (!thread) return undefined;
25
+ if (!thread) return undefined;
24
26
 
25
- loggers.agent.debug(`Found ${messages.length} messages for thread ${threadId}`);
27
+ loggers.agent.debug(`Found ${messages.length} messages for thread ${threadId}`);
26
28
 
27
- const threadObject: ThreadObject = {
28
- threadId: thread.threadId,
29
- userId: thread.userId,
30
- type: thread.type as ThreadType,
31
- title: thread.title || "New thread",
32
- messages: []
33
- };
34
- messages.forEach((message: MessageDocument) => {
35
- threadObject.messages.push({
36
- messageId: message.messageId,
37
- role: message.role as MessageRole,
38
- content: message.content,
39
- timestamp: message.timestamp,
40
- metadata: message.metadata,
41
- });
42
- });
29
+ const threadObject: ThreadObject = {
30
+ threadId: thread.threadId,
31
+ userId: thread.userId,
32
+ type: thread.type as ThreadType,
33
+ title: thread.title || "New thread",
34
+ messages: []
35
+ };
36
+ messages.forEach((message: MessageDocument) => {
37
+ threadObject.messages.push({
38
+ messageId: message.messageId,
39
+ role: message.role as MessageRole,
40
+ content: message.content,
41
+ timestamp: message.timestamp,
42
+ metadata: message.metadata,
43
+ });
44
+ });
43
45
 
44
- return threadObject;
46
+ return threadObject;
47
+ }, `getThread(${userId}, ${threadId})`);
45
48
  };
46
49
 
47
- public async createThread(
48
- type: ThreadType,
49
- userId: string,
50
- threadId: string,
51
- title: string,
50
+ public async createThread(
51
+ type: ThreadType,
52
+ userId: string,
53
+ threadId: string,
54
+ title: string,
52
55
  ): Promise<ThreadObject> {
53
- const now = Date.now();
54
- await ThreadModel.create({
55
- type,
56
- userId,
57
- threadId,
58
- title,
59
- updated_at: now,
60
- created_at: now,
61
- });
56
+ return this.executeWithRetry(async () => {
57
+ const now = Date.now();
58
+ await ThreadModel.create({
59
+ type,
60
+ userId,
61
+ threadId,
62
+ title,
63
+ updated_at: now,
64
+ created_at: now,
65
+ });
62
66
 
63
- return { type, userId, threadId, title, messages: []};
67
+ return { type, userId, threadId, title, messages: []};
68
+ }, `createThread(${userId}, ${threadId})`);
64
69
  };
65
70
 
66
- public async addMessagesToThread(
71
+ public async addMessagesToThread(
67
72
  userId: string,
68
73
  threadId: string,
69
74
  messages: MessageObject[]
70
75
  ): Promise<void> {
71
- await ThreadModel.updateOne({ threadId, userId }, {
72
- updated_at: Date.now(),
73
- });
74
- for (const message of messages) {
75
- await MessageModel.create({
76
- threadId,
77
- messageId: message.messageId,
78
- userId,
79
- role: message.role,
80
- content: message.content,
81
- timestamp: message.timestamp,
82
- metadata: message.metadata,
76
+ return this.executeWithRetry(async () => {
77
+ await ThreadModel.updateOne({ threadId, userId }, {
78
+ updated_at: Date.now(),
83
79
  });
84
- }
80
+ for (const message of messages) {
81
+ await MessageModel.create({
82
+ threadId,
83
+ messageId: message.messageId,
84
+ userId,
85
+ role: message.role,
86
+ content: message.content,
87
+ timestamp: message.timestamp,
88
+ metadata: message.metadata,
89
+ });
90
+ }
91
+ }, `addMessagesToThread(${userId}, ${threadId})`);
85
92
  };
86
93
 
87
- public async deleteThread(userId: string, threadId: string): Promise<void> {
88
- const messages = await MessageModel.find({ userId, threadId }).sort({
89
- timestamp: 1,
90
- });
94
+ public async deleteThread(userId: string, threadId: string): Promise<void> {
95
+ return this.executeWithRetry(async () => {
96
+ const timeout = this.getOperationTimeout();
97
+ const messages = await MessageModel.find({ userId, threadId })
98
+ .sort({ timestamp: 1 })
99
+ .maxTimeMS(timeout);
100
+
101
+ messages?.forEach((message: MessageDocument) => {
102
+ message.deleteOne();
103
+ });
91
104
 
92
- messages?.forEach((message: MessageDocument) => {
93
- message.deleteOne();
94
- });
95
-
96
- const thread = await ThreadModel.findOne({ userId, threadId });
97
- thread?.deleteOne();
105
+ const thread = await ThreadModel.findOne({ userId, threadId }).maxTimeMS(timeout);
106
+ thread?.deleteOne();
107
+ }, `deleteThread(${userId}, ${threadId})`);
98
108
  };
99
109
 
100
- public async listThreads(userId: string): Promise<ThreadMetadata[]> {
101
- const threads = await ThreadModel.find({ userId }).sort({
102
- updated_at: -1,
103
- });
104
- const data: ThreadMetadata[] = threads.map((thread: ThreadDocument) => {
105
- return {
106
- type: thread.type,
107
- userId,
108
- threadId: thread.threadId,
109
- title: thread.title,
110
- updatedAt: thread.updated_at
111
- } as ThreadMetadata;
112
- })
113
- return data;
110
+ public async listThreads(userId: string): Promise<ThreadMetadata[]> {
111
+ return this.executeWithRetry(async () => {
112
+ const timeout = this.getOperationTimeout();
113
+ const threads = await ThreadModel.find({ userId })
114
+ .sort({ updated_at: -1 })
115
+ .maxTimeMS(timeout);
116
+ const data: ThreadMetadata[] = threads.map((thread: ThreadDocument) => {
117
+ return {
118
+ type: thread.type,
119
+ userId,
120
+ threadId: thread.threadId,
121
+ title: thread.title,
122
+ updatedAt: thread.updated_at
123
+ } as ThreadMetadata;
124
+ })
125
+ return data;
126
+ }, `listThreads(${userId})`);
114
127
  };
115
- }
128
+ }
package/index.ts CHANGED
@@ -1,2 +1,4 @@
1
1
  export { MongoDBThread } from "./implements/thread.memory";
2
- export { MongoDBIntent } from "./implements/intent.memory";
2
+ export { MongoDBIntent } from "./implements/intent.memory";
3
+ export { MongoDBMemory } from "./implements/base.memory";
4
+ export type { MongoDBMemoryConfig } from "./implements/base.memory";
@@ -1,7 +1,14 @@
1
+ import { Intent } from "@ainetwork/adk/types/memory";
1
2
  import mongoose, { type Document, Schema } from "mongoose";
2
3
 
3
4
  const IntentObjectSchema = new Schema(
4
5
  {
6
+ id: {
7
+ type: String,
8
+ required: true,
9
+ index: true,
10
+ unique: true,
11
+ },
5
12
  name: {
6
13
  type: String,
7
14
  required: true,
@@ -15,13 +22,23 @@ const IntentObjectSchema = new Schema(
15
22
  type: String,
16
23
  required: false,
17
24
  },
18
- }
25
+ status: {
26
+ type: String,
27
+ required: true,
28
+ },
29
+ triggeringSentences: {
30
+ type: [String],
31
+ required: false,
32
+ },
33
+ tags: {
34
+ type: [String],
35
+ required: false,
36
+ },
37
+ },
19
38
  );
20
39
 
21
- export interface IntentDocument extends Document {
22
- name: string;
23
- description: string;
24
- prompt?: string;
40
+ export interface IntentDocument extends Omit<Document, 'id'>, Omit<Intent, 'id'> {
41
+ id: string;
25
42
  }
26
43
 
27
44
  export const IntentModel = mongoose.model<IntentDocument>("Intent", IntentObjectSchema);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ainetwork/adk-provider-memory-mongodb",
3
- "version": "0.2.2",
3
+ "version": "0.2.4",
4
4
  "author": "AI Network (https://ainetwork.ai)",
5
5
  "type": "module",
6
6
  "engines": {
@@ -28,7 +28,7 @@
28
28
  "clean": "rm -rf dist"
29
29
  },
30
30
  "dependencies": {
31
- "@ainetwork/adk": "^0.2.7",
31
+ "@ainetwork/adk": "0.2.9",
32
32
  "mongoose": "^8.16.5"
33
33
  },
34
34
  "devDependencies": {
@@ -38,5 +38,5 @@
38
38
  "publishConfig": {
39
39
  "access": "public"
40
40
  },
41
- "gitHead": "e0021453227b0c9c0cd1d21efa0395e8e1f6740e"
41
+ "gitHead": "f1315510a23dd44492dc20737c4be71188d01e26"
42
42
  }