@solidstarters/solid-core 1.2.148 → 1.2.150

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.
Files changed (65) hide show
  1. package/dist/controllers/ai-interaction.controller.d.ts +1 -1
  2. package/dist/controllers/ai-interaction.controller.js +1 -1
  3. package/dist/dtos/create-ai-interaction.dto.d.ts +3 -0
  4. package/dist/dtos/create-ai-interaction.dto.d.ts.map +1 -1
  5. package/dist/dtos/create-ai-interaction.dto.js +20 -1
  6. package/dist/dtos/create-ai-interaction.dto.js.map +1 -1
  7. package/dist/dtos/update-ai-interaction.dto.d.ts +3 -0
  8. package/dist/dtos/update-ai-interaction.dto.d.ts.map +1 -1
  9. package/dist/dtos/update-ai-interaction.dto.js +19 -1
  10. package/dist/dtos/update-ai-interaction.dto.js.map +1 -1
  11. package/dist/entities/ai-interaction.entity.d.ts +3 -0
  12. package/dist/entities/ai-interaction.entity.d.ts.map +1 -1
  13. package/dist/entities/ai-interaction.entity.js +17 -1
  14. package/dist/entities/ai-interaction.entity.js.map +1 -1
  15. package/dist/helpers/environment.helper.d.ts +2 -0
  16. package/dist/helpers/environment.helper.d.ts.map +1 -0
  17. package/dist/helpers/environment.helper.js +11 -0
  18. package/dist/helpers/environment.helper.js.map +1 -0
  19. package/dist/index.d.ts +2 -0
  20. package/dist/index.d.ts.map +1 -1
  21. package/dist/index.js +2 -0
  22. package/dist/index.js.map +1 -1
  23. package/dist/jobs/database/trigger-mcp-client-subscriber-database.service.d.ts.map +1 -1
  24. package/dist/jobs/database/trigger-mcp-client-subscriber-database.service.js +2 -0
  25. package/dist/jobs/database/trigger-mcp-client-subscriber-database.service.js.map +1 -1
  26. package/dist/seeders/seed-data/solid-core-metadata.json +83 -3
  27. package/dist/services/ai-interaction.service.d.ts +1 -1
  28. package/dist/services/ai-interaction.service.d.ts.map +1 -1
  29. package/dist/services/ai-interaction.service.js +5 -1
  30. package/dist/services/ai-interaction.service.js.map +1 -1
  31. package/dist/services/media.service.d.ts.map +1 -1
  32. package/dist/services/media.service.js +2 -2
  33. package/dist/services/media.service.js.map +1 -1
  34. package/dist/services/mediaStorageProviders/file-s3-storage-provider.js +3 -2
  35. package/dist/services/mediaStorageProviders/file-s3-storage-provider.js.map +1 -1
  36. package/dist/services/mediaStorageProviders/file-storage-provider.js +6 -4
  37. package/dist/services/mediaStorageProviders/file-storage-provider.js.map +1 -1
  38. package/dist/services/mq-message.service.d.ts +9 -0
  39. package/dist/services/mq-message.service.d.ts.map +1 -1
  40. package/dist/services/mq-message.service.js +61 -0
  41. package/dist/services/mq-message.service.js.map +1 -1
  42. package/dist/services/poller.service.js +1 -1
  43. package/dist/services/poller.service.js.map +1 -1
  44. package/dist/services/queues/database-subscriber.service.js +1 -1
  45. package/dist/services/queues/database-subscriber.service.js.map +1 -1
  46. package/dist/solid-core.module.d.ts.map +1 -1
  47. package/dist/solid-core.module.js +2 -1
  48. package/dist/solid-core.module.js.map +1 -1
  49. package/dist/tsconfig.tsbuildinfo +1 -1
  50. package/package.json +1 -1
  51. package/src/dtos/create-ai-interaction.dto.ts +16 -5
  52. package/src/dtos/update-ai-interaction.dto.ts +16 -5
  53. package/src/entities/ai-interaction.entity.ts +11 -3
  54. package/src/helpers/environment.helper.ts +7 -0
  55. package/src/index.ts +3 -0
  56. package/src/jobs/database/trigger-mcp-client-subscriber-database.service.ts +2 -0
  57. package/src/seeders/seed-data/solid-core-metadata.json +83 -3
  58. package/src/services/ai-interaction.service.ts +8 -3
  59. package/src/services/media.service.ts +2 -3
  60. package/src/services/mediaStorageProviders/file-s3-storage-provider.ts +2 -2
  61. package/src/services/mediaStorageProviders/file-storage-provider.ts +4 -4
  62. package/src/services/mq-message.service.ts +116 -0
  63. package/src/services/poller.service.ts +1 -1
  64. package/src/services/queues/database-subscriber.service.ts +1 -1
  65. package/src/solid-core.module.ts +2 -1
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@solidstarters/solid-core",
3
- "version": "1.2.148",
3
+ "version": "1.2.150",
4
4
  "description": "This module is a NestJS module containing all the required core providers required by a Solid application",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -48,9 +48,20 @@ export class CreateAiInteractionDto {
48
48
  @IsJSON()
49
49
  @ApiProperty()
50
50
  metadata: any;
51
-
52
- @IsOptional()
53
- @IsBoolean()
54
- @ApiProperty()
55
- isApplied: boolean = false;
51
+ @IsOptional()
52
+ @IsBoolean()
53
+ @ApiProperty()
54
+ isApplied: boolean = false;
55
+ @IsOptional()
56
+ @IsInt()
57
+ @ApiProperty()
58
+ parentInteractionId: number;
59
+ @IsString()
60
+ @IsOptional()
61
+ @ApiProperty()
62
+ parentInteractionUserKey: string;
63
+ @IsOptional()
64
+ @IsBoolean()
65
+ @ApiProperty()
66
+ isAutoApply: boolean = false;
56
67
  }
@@ -52,9 +52,20 @@ export class UpdateAiInteractionDto {
52
52
  @IsJSON()
53
53
  @ApiProperty()
54
54
  metadata: any;
55
-
56
- @IsOptional()
57
- @IsBoolean()
58
- @ApiProperty()
59
- isApplied: boolean;
55
+ @IsOptional()
56
+ @IsBoolean()
57
+ @ApiProperty()
58
+ isApplied: boolean;
59
+ @IsOptional()
60
+ @IsInt()
61
+ @ApiProperty()
62
+ parentInteractionId: number;
63
+ @IsString()
64
+ @IsOptional()
65
+ @ApiProperty()
66
+ parentInteractionUserKey: string;
67
+ @IsOptional()
68
+ @IsBoolean()
69
+ @ApiProperty()
70
+ isAutoApply: boolean;
60
71
  }
@@ -28,7 +28,15 @@ export class AiInteraction extends CommonEntity {
28
28
  responseTimeMs: number;
29
29
  @Column({ type: "jsonb", nullable: true })
30
30
  metadata: any;
31
-
32
- @Column({ type: "boolean", nullable: true, default: false })
33
- isApplied: boolean = false;
31
+ @Column({ type: "boolean", nullable: true, default: false })
32
+ isApplied: boolean = false;
33
+ @Index()
34
+ @ManyToOne(() => AiInteraction, { onDelete: "SET NULL", nullable: true })
35
+ @JoinColumn()
36
+ parentInteraction: AiInteraction;
37
+ @Index({ unique: true })
38
+ @Column({ type: "varchar" })
39
+ externalId: string;
40
+ @Column({ type: "boolean", nullable: true, default: false })
41
+ isAutoApply: boolean = false;
34
42
  }
@@ -0,0 +1,7 @@
1
+ export function parseBooleanEnv(key: string, defaultValue: boolean = false): boolean {
2
+ const value = process.env[key];
3
+ if (value === undefined) {
4
+ return defaultValue;
5
+ }
6
+ return value.toLowerCase() === 'true';
7
+ }
package/src/index.ts CHANGED
@@ -166,6 +166,8 @@ export * from './helpers/field-crud-managers/SelectionDynamicFieldCrudManager' /
166
166
  export * from './helpers/field-crud-managers/SelectionStaticFieldCrudManager' //rename
167
167
  export * from './helpers/field-crud-managers/ShortTextFieldCrudManager' //rename
168
168
  export * from './helpers/field-crud-managers/UUIDFieldCrudManager' //rename
169
+ export * from './helpers/environment.helper'
170
+
169
171
  export * from './services/crud.service'
170
172
  export * from './interceptors/logging.interceptor'
171
173
  export * from './interceptors/wrap-response.interceptor'
@@ -264,6 +266,7 @@ export * from './services/csv.service'
264
266
  export * from './services/queues/publisher-factory.service'
265
267
  export * from './services/queues/database-publisher.service'
266
268
  export * from './services/queues/database-subscriber.service'
269
+ export * from './services/ai-interaction.service'
267
270
 
268
271
  // Factories
269
272
  export * from './factories/mail.factory'
@@ -58,6 +58,7 @@ export class TriggerMcpClientSubscriberDatabase extends DatabaseSubscriber<Trigg
58
58
  await this.aiInteractionService.create({
59
59
  userId: aiInteraction.user.id,
60
60
  threadId: aiInteraction.threadId,
61
+ parentInteractionId: aiInteraction.id,
61
62
  role: 'gen-ai',
62
63
  message: '-',
63
64
  contentType: aiResponse.content_type,
@@ -77,6 +78,7 @@ export class TriggerMcpClientSubscriberDatabase extends DatabaseSubscriber<Trigg
77
78
  await this.aiInteractionService.create({
78
79
  userId: aiInteraction.user.id,
79
80
  threadId: aiInteraction.threadId,
81
+ parentInteractionId: aiInteraction.id,
80
82
  role: 'gen-ai',
81
83
  message: nestedResponse,
82
84
  contentType: aiResponse.content_type,
@@ -4919,7 +4919,7 @@
4919
4919
  "tableName": "ss_ai_interactions",
4920
4920
  "dataSource": "default",
4921
4921
  "dataSourceType": "postgres",
4922
- "userKeyFieldUserKey": "message",
4922
+ "userKeyFieldUserKey": "externalId",
4923
4923
  "isSystem": false,
4924
4924
  "fields": [
4925
4925
  {
@@ -4938,6 +4938,35 @@
4938
4938
  "relationModelModuleName": "solid-core",
4939
4939
  "isSystem": true
4940
4940
  },
4941
+ {
4942
+ "name": "externalId",
4943
+ "displayName": "External ID",
4944
+ "description": "Used to track using a reference number of each ai interaction.",
4945
+ "type": "computed",
4946
+ "ormType": "varchar",
4947
+ "isSystem": false,
4948
+ "computedFieldValueType": "string",
4949
+ "computedFieldTriggerConfig": [
4950
+ {
4951
+ "modelName": "aiInteraction",
4952
+ "moduleName": "solid-core",
4953
+ "operations": [
4954
+ "before-insert"
4955
+ ]
4956
+ }
4957
+ ],
4958
+ "computedFieldValueProvider": "AlphaNumExternalIdComputationProvider",
4959
+ "computedFieldValueProviderCtxt": "{\n \"prefix\": \"AI\",\n \"length\": \"10\"\n}",
4960
+ "required": true,
4961
+ "unique": true,
4962
+ "index": true,
4963
+ "private": false,
4964
+ "encrypt": false,
4965
+ "encryptionType": null,
4966
+ "decryptWhen": null,
4967
+ "columnName": null,
4968
+ "isUserKey": true
4969
+ },
4941
4970
  {
4942
4971
  "name": "threadId",
4943
4972
  "displayName": "Thread ID",
@@ -4950,6 +4979,22 @@
4950
4979
  "private": false,
4951
4980
  "encrypt": false
4952
4981
  },
4982
+ {
4983
+ "name": "parentInteraction",
4984
+ "displayName": "Parent Interaction",
4985
+ "type": "relation",
4986
+ "required": false,
4987
+ "unique": false,
4988
+ "index": true,
4989
+ "private": false,
4990
+ "encrypt": false,
4991
+ "relationType": "many-to-one",
4992
+ "relationCoModelSingularName": "aiInteraction",
4993
+ "relationCreateInverse": false,
4994
+ "relationCascade": "set null",
4995
+ "relationModelModuleName": "solid-core",
4996
+ "isSystem": true
4997
+ },
4953
4998
  {
4954
4999
  "name": "role",
4955
5000
  "displayName": "Role",
@@ -5057,6 +5102,17 @@
5057
5102
  "index": false,
5058
5103
  "private": false,
5059
5104
  "encrypt": false
5105
+ },
5106
+ {
5107
+ "name": "isAutoApply",
5108
+ "displayName": "Is Auto Apply",
5109
+ "type": "boolean",
5110
+ "ormType": "boolean",
5111
+ "required": false,
5112
+ "unique": false,
5113
+ "index": false,
5114
+ "private": false,
5115
+ "encrypt": false
5060
5116
  }
5061
5117
  ]
5062
5118
  }
@@ -11867,7 +11923,7 @@
11867
11923
  }
11868
11924
  },
11869
11925
  {
11870
- "name": "ai-interaction-list-view",
11926
+ "name": "aiInteraction-list-view",
11871
11927
  "displayName": "AI Interaction",
11872
11928
  "type": "list",
11873
11929
  "context": "{}",
@@ -11900,6 +11956,18 @@
11900
11956
  "name": "threadId"
11901
11957
  }
11902
11958
  },
11959
+ {
11960
+ "type": "field",
11961
+ "attrs": {
11962
+ "name": "externalId"
11963
+ }
11964
+ },
11965
+ {
11966
+ "type": "field",
11967
+ "attrs": {
11968
+ "name": "parentInteraction"
11969
+ }
11970
+ },
11903
11971
  {
11904
11972
  "type": "field",
11905
11973
  "attrs": {
@@ -11934,7 +12002,7 @@
11934
12002
  }
11935
12003
  },
11936
12004
  {
11937
- "name": "ai-interaction-form-view",
12005
+ "name": "aiInteraction-form-view",
11938
12006
  "displayName": "AI Interaction",
11939
12007
  "type": "form",
11940
12008
  "context": "{}",
@@ -11987,6 +12055,12 @@
11987
12055
  "name": "user"
11988
12056
  }
11989
12057
  },
12058
+ {
12059
+ "type": "field",
12060
+ "attrs": {
12061
+ "name": "externalId"
12062
+ }
12063
+ },
11990
12064
  {
11991
12065
  "type": "field",
11992
12066
  "attrs": {
@@ -12034,6 +12108,12 @@
12034
12108
  "attrs": {
12035
12109
  "name": "responseTimeMs"
12036
12110
  }
12111
+ },
12112
+ {
12113
+ "type": "field",
12114
+ "attrs": {
12115
+ "name": "parentInteraction"
12116
+ }
12037
12117
  }
12038
12118
  ]
12039
12119
  }
@@ -43,7 +43,7 @@ export class AiInteractionService extends CRUDService<AiInteraction> {
43
43
  super(modelMetadataService, moduleMetadataService, configService, fileService, discoveryService, crudHelperService, entityManager, repo, 'aiInteraction', 'solid-core', moduleRef);
44
44
  }
45
45
 
46
- async triggerMcpClientJob(prompt: string): Promise<string> {
46
+ async triggerMcpClientJob(prompt: string): Promise<any> {
47
47
  const activeUser: ActiveUserData = this.requestContextService.getActiveUser();
48
48
 
49
49
  const aiInteraction = await this.create({
@@ -65,7 +65,12 @@ export class AiInteractionService extends CRUDService<AiInteraction> {
65
65
  parentEntityId: aiInteraction.id,
66
66
  };
67
67
 
68
- return await this.publisherFactory.publish(m, 'TriggerMcpClientPublisher');
68
+ const queueMessageId = await this.publisherFactory.publish(m, 'TriggerMcpClientPublisher');
69
+
70
+ return {
71
+ queueMessageId: queueMessageId,
72
+ aiInteractionId: aiInteraction.id
73
+ }
69
74
  }
70
75
 
71
76
  /**
@@ -206,7 +211,7 @@ export class AiInteractionService extends CRUDService<AiInteraction> {
206
211
 
207
212
  // TODO: This provider to implement an interface - IMcpToolResponseHandler ... apply(aiInteraction: AiInteraction)
208
213
  // throw new Error('Method not implemented.');
209
-
214
+
210
215
  // Mark the interaction as applied
211
216
  await this.update(aiInteraction.id, { isApplied: true }, [], true);
212
217
 
@@ -84,7 +84,6 @@ export class MediaService extends CRUDService<Media> {
84
84
  const savedMedias = [];
85
85
  for (let i = 0; i < files.length; i++) {
86
86
 
87
-
88
87
  createDto['fieldMetadata'] = await this.fieldMetadataRepo.findOne({
89
88
  where: {
90
89
  id: createDto['fieldMetadataId']
@@ -113,9 +112,9 @@ export class MediaService extends CRUDService<Media> {
113
112
  const fileName = this.getFileName(file);
114
113
  let awsFileUrl;
115
114
  if (createDto.mediaStorageProviderMetadata.isPublic === true) {
116
- awsFileUrl = await this.fileService.copyToS3(file.path, file.mimetype, fileName, createDto.mediaStorageProviderMetadata.bucketName,);
117
- } else {
118
115
  awsFileUrl = await this.fileService.copyToS3WithPublic(file.path, file.mimetype, fileName, createDto.mediaStorageProviderMetadata.bucketName,);
116
+ } else {
117
+ awsFileUrl = await this.fileService.copyToS3(file.path, file.mimetype, fileName, createDto.mediaStorageProviderMetadata.bucketName,);
119
118
  }
120
119
  // createDto['relativeUri'] = this.getAwsS3FullFilePath(awsFileUrl, createDto.mediaStorageProviderMetadata.bucketName, createDto.mediaStorageProviderMetadata.region);
121
120
  createDto['relativeUri'] = awsFileUrl
@@ -60,7 +60,7 @@ export class FileS3StorageProvider<T> implements MediaStorageProvider<T> {
60
60
  throw new Error("Entity must be an instance of CommonEntity"); //FIXME This needs to be handled through generics. e.g T extends CommonEntity
61
61
  }
62
62
  const result: Media[] = [];
63
- files.forEach(async (file) => {
63
+ for (const file of files) {
64
64
  const fileName = this.getFileName(file);
65
65
  // Store the file in the configured S3 Bucket
66
66
  let awsFileUrl;
@@ -84,7 +84,7 @@ export class FileS3StorageProvider<T> implements MediaStorageProvider<T> {
84
84
  }) as unknown as Media;
85
85
  result.push(mediaEntity);
86
86
  this.logger.debug(`Stored media with`, mediaEntity);
87
- });
87
+ };
88
88
  return result;
89
89
  }
90
90
 
@@ -38,7 +38,7 @@ export class FileStorageProvider<T> implements MediaStorageProvider<T> {
38
38
  throw new Error("Entity must be an instance of CommonEntity"); //FIXME This needs to be handled through generics. e.g T extends CommonEntity
39
39
  }
40
40
  const result: Media[] = [];
41
- files.forEach(async (file) => {
41
+ for (const file of files) {
42
42
  // Store the file in the configured file storage directory
43
43
  const fileStoragePath = this.getFullFilePath(this.getFileName(file));
44
44
  await this.fileService.copyFile(file.path, fileStoragePath);
@@ -57,7 +57,7 @@ export class FileStorageProvider<T> implements MediaStorageProvider<T> {
57
57
  }) as unknown as Media;
58
58
  result.push(mediaEntity);
59
59
  this.logger.debug(`Stored media with`, mediaEntity);
60
- });
60
+ };
61
61
  return result;
62
62
  }
63
63
 
@@ -66,7 +66,7 @@ export class FileStorageProvider<T> implements MediaStorageProvider<T> {
66
66
  throw new Error("Entity must be an instance of CommonEntity"); //FIXME This needs to be handled through generics. e.g T extends CommonEntity
67
67
  }
68
68
  const result: Media[] = [];
69
- streamPairs.forEach(async (pair) => {
69
+ for (const pair of streamPairs) {
70
70
  const stream = pair[0];
71
71
  const fileName = pair[1];
72
72
  this.fileService.writeStreamToFile(stream, this.getFullFilePath(fileName));
@@ -78,7 +78,7 @@ export class FileStorageProvider<T> implements MediaStorageProvider<T> {
78
78
  fieldMetadataId: mediaFieldMetadata.id
79
79
  }) as unknown as Media;
80
80
  this.logger.debug(`Stored media with`, mediaEntity);
81
- });
81
+ };
82
82
  return result;
83
83
  }
84
84
 
@@ -68,4 +68,120 @@ export class MqMessageService extends CRUDService<MqMessage> {
68
68
  });
69
69
  }
70
70
 
71
+ /**
72
+ * Wait until a queue message reaches a terminal status (succeeded/failed).
73
+ *
74
+ * @param messageId string – the external message id you store in `ss_mq_message.messageId`
75
+ * @param opts.timeoutMs total time to wait before giving up (default 60s)
76
+ * @param opts.intervalMs initial poll interval (default 500ms)
77
+ * @param opts.maxIntervalMs cap for exponential backoff (default 2000ms)
78
+ * @param opts.throwOnFailure if true, throws when stage === 'failed' (default false)
79
+ * @param opts.parseJson try JSON.parse on `output` and `error` (default true)
80
+ * @returns resolves with the final MqMessage row when terminal, rejects on timeout (or failure if throwOnFailure)
81
+ */
82
+ async waitForTerminalStatus(
83
+ messageId: string,
84
+ opts?: {
85
+ timeoutMs?: number;
86
+ intervalMs?: number;
87
+ maxIntervalMs?: number;
88
+ throwOnFailure?: boolean;
89
+ parseJson?: boolean;
90
+ logEveryN?: number;
91
+ },
92
+ ): Promise<MqMessage> {
93
+ const {
94
+ timeoutMs = 60_000,
95
+ intervalMs = 500,
96
+ maxIntervalMs = 2_000,
97
+ throwOnFailure = false,
98
+ parseJson = true,
99
+ logEveryN = 10, // log every N polls to avoid noisy logs
100
+ } = opts || {};
101
+
102
+ const start = Date.now();
103
+ let attempt = 0;
104
+ let delay = intervalMs;
105
+
106
+ // Small helper
107
+ const sleep = (ms: number) => new Promise((r) => setTimeout(r, ms));
108
+
109
+ while (true) {
110
+ attempt++;
111
+
112
+ // Fetch minimal columns needed for quick polling
113
+ const rec = await this.repo.findOne({
114
+ where: { messageId },
115
+ select: {
116
+ id: true,
117
+ messageId: true,
118
+ stage: true,
119
+ finishedAt: true,
120
+ elapsedMillis: true,
121
+ output: true,
122
+ error: true,
123
+ input: true,
124
+ // add other fields if you need to return them
125
+ } as any,
126
+ loadEagerRelations: false,
127
+ });
128
+
129
+ if (attempt % logEveryN === 0) {
130
+ this.logger.debug(
131
+ `waitForTerminalStatus(${messageId}) poll #${attempt} -> ${rec?.stage ?? 'not_found'}`
132
+ );
133
+ }
134
+
135
+ if (!rec) {
136
+ // Not found yet – keep waiting until timeout
137
+ } else if (rec.stage === 'succeeded' || rec.stage === 'failed') {
138
+ // Optionally parse output/error if they contain JSON strings
139
+ if (parseJson) {
140
+ rec.output = this.safeJsonParse(rec.output);
141
+ rec.error = this.safeJsonParse(rec.error);
142
+ }
143
+
144
+ if (rec.stage === 'failed' && throwOnFailure) {
145
+ throw new Error(
146
+ `Queue message ${messageId} failed` +
147
+ (rec.error ? `: ${JSON.stringify(rec.error).slice(0, 500)}` : '')
148
+ );
149
+ }
150
+ return rec;
151
+ }
152
+
153
+ // Timeout?
154
+ const elapsed = Date.now() - start;
155
+ if (elapsed >= timeoutMs) {
156
+ throw new Error(`Timed out after ${timeoutMs}ms waiting for message ${messageId} to reach terminal status`);
157
+ }
158
+
159
+ // Backoff with cap
160
+ await sleep(delay);
161
+ delay = Math.min(Math.floor(delay * 1.5), maxIntervalMs);
162
+ }
163
+ }
164
+
165
+ // /**
166
+ // * Optional wrapper: publish and then wait (if your publisher returns the messageId).
167
+ // */
168
+ // async publishAndWait<T>(
169
+ // publishFn: () => Promise<string>, // returns messageId
170
+ // waitOpts?: Parameters<MqMessageService['waitForTerminalStatus']>[1],
171
+ // ): Promise<MqMessage> {
172
+ // const messageId = await publishFn();
173
+ // return this.waitForTerminalStatus(messageId, waitOpts);
174
+ // }
175
+
176
+ private safeJsonParse(value: unknown): unknown {
177
+ if (value == null) return value;
178
+ if (typeof value !== 'string') return value;
179
+ const s = value.trim();
180
+ if (!s) return s;
181
+ try {
182
+ return JSON.parse(s);
183
+ } catch {
184
+ return value; // leave as-is if not valid JSON
185
+ }
186
+ }
71
187
  }
@@ -43,7 +43,7 @@ export class PollerService implements OnModuleDestroy, BeforeApplicationShutdown
43
43
  const opts: Required<PollOptions> = {
44
44
  baseDelayMs: options.baseDelayMs ?? 1000,
45
45
  maxDelayMs: options.maxDelayMs ?? 30_000,
46
- timeoutPerIterationMs: options.timeoutPerIterationMs ?? 60_000,
46
+ timeoutPerIterationMs: options.timeoutPerIterationMs ?? 5 * 60_000,
47
47
  jitter: options.jitter ?? true,
48
48
  };
49
49
 
@@ -108,7 +108,7 @@ export abstract class DatabaseSubscriber<T> implements OnModuleInit, QueueSubscr
108
108
  this.poller.start(queueName, (q) => this.processNext(q), {
109
109
  baseDelayMs: 1000,
110
110
  maxDelayMs: 30_000,
111
- timeoutPerIterationMs: 60_000,
111
+ timeoutPerIterationMs: 5 * 60_000,
112
112
  jitter: true,
113
113
  });
114
114
 
@@ -604,7 +604,8 @@ import { PollerService } from './services/poller.service';
604
604
  ConfigModule,
605
605
  PublisherFactory,
606
606
  MailFactory,
607
- PollerService
607
+ PollerService,
608
+ AiInteractionService,
608
609
  ],
609
610
  })
610
611
  export class SolidCoreModule { }