@solidstarters/solid-core 1.2.178 → 1.2.180
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/dist/controllers/ai-interaction.controller.js.map +1 -1
- package/dist/controllers/service.controller.d.ts +8 -1
- package/dist/controllers/service.controller.d.ts.map +1 -1
- package/dist/controllers/service.controller.js +39 -2
- package/dist/controllers/service.controller.js.map +1 -1
- package/dist/helpers/date.helper.d.ts +2 -0
- package/dist/helpers/date.helper.d.ts.map +1 -0
- package/dist/helpers/date.helper.js +18 -0
- package/dist/helpers/date.helper.js.map +1 -0
- package/dist/jobs/database/trigger-mcp-client-subscriber-database.service.d.ts.map +1 -1
- package/dist/jobs/database/trigger-mcp-client-subscriber-database.service.js +23 -17
- package/dist/jobs/database/trigger-mcp-client-subscriber-database.service.js.map +1 -1
- package/dist/seeders/seed-data/solid-core-metadata.json +2 -1
- package/dist/services/ai-interaction.service.d.ts.map +1 -1
- package/dist/services/ai-interaction.service.js +11 -3
- package/dist/services/ai-interaction.service.js.map +1 -1
- package/dist/services/excel.service.js +2 -2
- package/dist/services/excel.service.js.map +1 -1
- package/dist/services/genai/ingest-metadata.service.d.ts.map +1 -1
- package/dist/services/genai/ingest-metadata.service.js +9 -8
- package/dist/services/genai/ingest-metadata.service.js.map +1 -1
- package/dist/services/import-transaction.service.d.ts.map +1 -1
- package/dist/services/import-transaction.service.js +37 -10
- package/dist/services/import-transaction.service.js.map +1 -1
- package/dist/services/mcp-tool-response-handlers/solid-create-model-with-fields-mcp-tool-response-handler.service.d.ts.map +1 -1
- package/dist/services/mcp-tool-response-handlers/solid-create-model-with-fields-mcp-tool-response-handler.service.js +2 -2
- package/dist/services/mcp-tool-response-handlers/solid-create-model-with-fields-mcp-tool-response-handler.service.js.map +1 -1
- package/dist/services/mcp-tool-response-handlers/solid-create-module-mcp-tool-response-handler.service.js +2 -2
- package/dist/services/mcp-tool-response-handlers/solid-create-module-mcp-tool-response-handler.service.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +3 -2
- package/src/controllers/ai-interaction.controller.ts +1 -1
- package/src/controllers/service.controller.ts +40 -2
- package/src/helpers/date.helper.ts +13 -0
- package/src/jobs/database/trigger-mcp-client-subscriber-database.service.ts +27 -18
- package/src/seeders/seed-data/solid-core-metadata.json +3 -1
- package/src/services/ai-interaction.service.ts +14 -7
- package/src/services/excel.service.ts +2 -2
- package/src/services/genai/ingest-metadata.service.ts +34 -8
- package/src/services/import-transaction.service.ts +46 -11
- package/src/services/mcp-tool-response-handlers/solid-create-model-with-fields-mcp-tool-response-handler.service.ts +3 -2
- package/src/services/mcp-tool-response-handlers/solid-create-module-mcp-tool-response-handler.service.ts +2 -2
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@solidstarters/solid-core",
|
|
3
|
-
"version": "1.2.
|
|
3
|
+
"version": "1.2.180",
|
|
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",
|
|
@@ -49,6 +49,7 @@
|
|
|
49
49
|
"cache-manager-redis-store": "^3.0.1",
|
|
50
50
|
"class-transformer": "^0.5.1",
|
|
51
51
|
"class-validator": "^0.14.1",
|
|
52
|
+
"dayjs": "^1.11.18",
|
|
52
53
|
"exceljs": "^4.4.0",
|
|
53
54
|
"fast-csv": "^5.0.2",
|
|
54
55
|
"handlebars": "^4.7.8",
|
|
@@ -69,7 +70,7 @@
|
|
|
69
70
|
"pluralize": "^8.0.0",
|
|
70
71
|
"puppeteer": "^23.2.0",
|
|
71
72
|
"r2r-js": "^0.4.43",
|
|
72
|
-
"reflect-metadata": "^0.
|
|
73
|
+
"reflect-metadata": "^0.2.2",
|
|
73
74
|
"rxjs": "^7.8.1",
|
|
74
75
|
"swagger-ui-express": "^5.0.0",
|
|
75
76
|
"twilio": "^5.8.0",
|
|
@@ -95,7 +95,7 @@ export class AiInteractionController {
|
|
|
95
95
|
@ApiBearerAuth("jwt")
|
|
96
96
|
@Post('/trigger-mcp-client-job')
|
|
97
97
|
async triggerMcpClientJob(@Body() dto: InvokeAiPromptDto, @ActiveUser() activeUser: ActiveUserData) {
|
|
98
|
-
return this.service.triggerMcpClientJob(dto,activeUser.sub);
|
|
98
|
+
return this.service.triggerMcpClientJob(dto, activeUser.sub);
|
|
99
99
|
}
|
|
100
100
|
|
|
101
101
|
@ApiBearerAuth("jwt")
|
|
@@ -5,10 +5,14 @@ import { Public } from 'src/decorators/public.decorator';
|
|
|
5
5
|
import { ErrorMapperService } from 'src/helpers/error-mapper.service';
|
|
6
6
|
import { ActiveUserData } from 'src/interfaces/active-user-data.interface';
|
|
7
7
|
import { AiInteractionService } from 'src/services/ai-interaction.service';
|
|
8
|
+
import { IngestMetadataService } from 'src/services/genai/ingest-metadata.service';
|
|
8
9
|
import { MqMessageService } from 'src/services/mq-message.service';
|
|
9
10
|
import { SolidRegistry } from '../helpers/solid-registry';
|
|
10
11
|
|
|
11
|
-
|
|
12
|
+
export interface PostProcessCodeGenConfig {
|
|
13
|
+
runModuleMetadataSeeder?: boolean; // If true, regenerate module metadata
|
|
14
|
+
runSolidIngestion?: boolean; // If true, run solid ingestion command
|
|
15
|
+
}
|
|
12
16
|
@Controller('')
|
|
13
17
|
@ApiTags("Common")
|
|
14
18
|
// @UseGuards(ThrottlerGuard)
|
|
@@ -20,7 +24,9 @@ export class ServiceController {
|
|
|
20
24
|
private readonly solidRegistry: SolidRegistry,
|
|
21
25
|
private readonly aiInteractionService: AiInteractionService,
|
|
22
26
|
private readonly mqMessageService: MqMessageService,
|
|
23
|
-
private readonly errorMapper: ErrorMapperService
|
|
27
|
+
private readonly errorMapper: ErrorMapperService,
|
|
28
|
+
private readonly ingestMetadataService: IngestMetadataService,
|
|
29
|
+
|
|
24
30
|
) { }
|
|
25
31
|
|
|
26
32
|
@Public()
|
|
@@ -103,6 +109,38 @@ export class ServiceController {
|
|
|
103
109
|
return { message: `seed data for ${seedData.seeder}` };
|
|
104
110
|
}
|
|
105
111
|
|
|
112
|
+
@ApiBearerAuth("jwt")
|
|
113
|
+
@Post('code-generation/post-process')
|
|
114
|
+
async postProcessCodeGeneration(@Body() config : PostProcessCodeGenConfig) {
|
|
115
|
+
// Set defaults if not provided
|
|
116
|
+
config.runModuleMetadataSeeder = config.runModuleMetadataSeeder ?? true;
|
|
117
|
+
config.runSolidIngestion = config.runSolidIngestion ?? true;
|
|
118
|
+
|
|
119
|
+
// Run the Module Metadata Seeder Service
|
|
120
|
+
if (config.runModuleMetadataSeeder) {
|
|
121
|
+
this.logger.debug(`Running the Module Metadata Seeder Service as part of post-process code generation`);
|
|
122
|
+
const seeder = this.solidRegistry
|
|
123
|
+
.getSeeders()
|
|
124
|
+
.filter((seeder) => seeder.name === 'ModuleMetadataSeederService')
|
|
125
|
+
.map((seeder) => seeder.instance)
|
|
126
|
+
.pop();
|
|
127
|
+
if (!seeder) {
|
|
128
|
+
this.logger.error(`Seeder service ModuleMetadataSeederService not found. Does your service have a seed() method?`);
|
|
129
|
+
} else {
|
|
130
|
+
await seeder.seed();
|
|
131
|
+
}
|
|
132
|
+
} else {
|
|
133
|
+
this.logger.debug(`Skipping the Module Metadata Seeder Service as part of post-process code generation`);
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
// Run the Solid ingestion command
|
|
137
|
+
if (config.runSolidIngestion) {
|
|
138
|
+
this.logger.debug(`Running the Solid ingestion command as part of post-process code generation`);
|
|
139
|
+
await this.ingestMetadataService.ingest();
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
|
|
106
144
|
// @Public()
|
|
107
145
|
// @Get('play')
|
|
108
146
|
// play() {
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import dayjs from 'dayjs';
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
export function parseFlexibleDate(value: string): Date | null {
|
|
5
|
+
if (!value) return null;
|
|
6
|
+
const formats = ['DD-MM-YYYY', 'YYYY-MM-DD'];
|
|
7
|
+
const parsed = dayjs(value, formats, true); // true = strict parsing
|
|
8
|
+
if (!parsed.isValid()) {
|
|
9
|
+
return null;
|
|
10
|
+
}
|
|
11
|
+
return parsed.toDate();
|
|
12
|
+
}
|
|
13
|
+
|
|
@@ -92,22 +92,26 @@ export class TriggerMcpClientSubscriberDatabase extends DatabaseSubscriber<Trigg
|
|
|
92
92
|
responseTimeMs: 0, // Updated after we receive the response
|
|
93
93
|
metadata: '', // Updated in the tool
|
|
94
94
|
isApplied: false, // Updated after we receive the response
|
|
95
|
-
status: '' // Updated after we receive the response
|
|
95
|
+
status: 'pending' // Updated after we receive the response
|
|
96
96
|
});
|
|
97
97
|
|
|
98
98
|
const finalPrompt = `
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
99
|
+
# User Prompt:
|
|
100
|
+
${prompt}
|
|
101
|
+
|
|
102
|
+
# System Instructions:
|
|
103
|
+
- aiInteractionId: ${genAiInteraction.id}
|
|
104
|
+
- moduleName:${message.payload.moduleName}
|
|
105
|
+
- You will be invoking tools if needed.
|
|
106
|
+
- If a tool is invoked, you must return **exactly** the raw output from the tool, without any json envelopes, additional formatting, commentary, or text.
|
|
107
|
+
- Do not wrap the result in quotes, JSON, or markdown fences.
|
|
108
|
+
- Do not explain what the result means.
|
|
109
|
+
|
|
110
|
+
# Past Interactions:
|
|
111
|
+
This section contains the last 10 interactions done between the human and LLM. These are sorted by oldest first.
|
|
112
|
+
Use these interactions to further identify concerns based on the current User Prompt.
|
|
113
|
+
|
|
114
|
+
`
|
|
111
115
|
|
|
112
116
|
const aiResponse = await this.aiInteractionService.runMcpPrompt(finalPrompt);
|
|
113
117
|
this.triggerMcpClientSubscriberLogger.log(`aiResponse: `);
|
|
@@ -116,8 +120,8 @@ export class TriggerMcpClientSubscriberDatabase extends DatabaseSubscriber<Trigg
|
|
|
116
120
|
if (!aiResponse.success) {
|
|
117
121
|
this.triggerMcpClientSubscriberLogger.log(`Gen ai has returned with a false status code`);
|
|
118
122
|
|
|
119
|
-
const errorsStr = aiResponse.errors
|
|
120
|
-
const errorTrace = aiResponse.error_trace
|
|
123
|
+
const errorsStr = aiResponse.errors?.join('\n ');
|
|
124
|
+
const errorTrace = aiResponse.error_trace?.join('\n');
|
|
121
125
|
|
|
122
126
|
// await this.aiInteractionService.create({
|
|
123
127
|
// userId: aiInteraction.user.id,
|
|
@@ -148,7 +152,7 @@ export class TriggerMcpClientSubscriberDatabase extends DatabaseSubscriber<Trigg
|
|
|
148
152
|
throw new Error(errorsStr);
|
|
149
153
|
}
|
|
150
154
|
else {
|
|
151
|
-
|
|
155
|
+
let nestedResponse = this.cleanNestedResponse(aiResponse);
|
|
152
156
|
|
|
153
157
|
// const genAiInteraction = await this.aiInteractionService.create({
|
|
154
158
|
// userId: aiInteraction.user.id,
|
|
@@ -166,14 +170,19 @@ export class TriggerMcpClientSubscriberDatabase extends DatabaseSubscriber<Trigg
|
|
|
166
170
|
// });
|
|
167
171
|
|
|
168
172
|
// TODO: Update the previously created genAiInteraction record with the respective success fields and save to DB
|
|
173
|
+
const errorsStr = nestedResponse.status == "error" && nestedResponse.errors.join('\n ');
|
|
174
|
+
|
|
169
175
|
await this.aiInteractionService.update(genAiInteraction.id, {
|
|
176
|
+
errorMessage: nestedResponse.status == "error" ? `${errorsStr}` : "",
|
|
177
|
+
// errorMessage:"",
|
|
170
178
|
// contentType: aiResponse.content_type,
|
|
171
|
-
errorMessage: '',
|
|
172
179
|
// message: nestedResponse,
|
|
173
180
|
modelUsed: aiResponse.model,
|
|
174
181
|
responseTimeMs: aiResponse.duration_ms,
|
|
175
182
|
isApplied: aiInteraction.isApplied,
|
|
176
|
-
|
|
183
|
+
|
|
184
|
+
// status: aiResponse.success ? 'succeeded' : 'failed'
|
|
185
|
+
status: aiResponse.success && nestedResponse.status == "success" ? 'succeeded' : 'failed'
|
|
177
186
|
}, [], true);
|
|
178
187
|
|
|
179
188
|
// If the human interaction was with isAutoApply=true, then we can go ahead and autoApply.
|
|
@@ -12331,7 +12331,9 @@
|
|
|
12331
12331
|
"type": "field",
|
|
12332
12332
|
"attrs": {
|
|
12333
12333
|
"name": "metadata",
|
|
12334
|
-
"height": "80vh"
|
|
12334
|
+
"height": "80vh",
|
|
12335
|
+
"viewWidget":"SolidAiInteractionMetadataFieldFormWidget"
|
|
12336
|
+
|
|
12335
12337
|
}
|
|
12336
12338
|
}
|
|
12337
12339
|
]
|
|
@@ -62,7 +62,7 @@ export class AiInteractionService extends CRUDService<AiInteraction> {
|
|
|
62
62
|
const m = {
|
|
63
63
|
payload: {
|
|
64
64
|
aiInteractionId: aiInteraction.id,
|
|
65
|
-
moduleName:dto.moduleName
|
|
65
|
+
moduleName: dto.moduleName
|
|
66
66
|
},
|
|
67
67
|
parentEntity: 'aiInteraction',
|
|
68
68
|
parentEntityId: aiInteraction.id,
|
|
@@ -149,6 +149,7 @@ export class AiInteractionService extends CRUDService<AiInteraction> {
|
|
|
149
149
|
}
|
|
150
150
|
catch (ex) {
|
|
151
151
|
this.logger.warn(`Attempting to parse mcp client response assuming it is JSON, however it is not: ${parsedResponse}`);
|
|
152
|
+
// raw.success = false
|
|
152
153
|
}
|
|
153
154
|
// Parse the response string into an object
|
|
154
155
|
// const parsedResponse = JSON.parse(raw.response);
|
|
@@ -203,16 +204,22 @@ export class AiInteractionService extends CRUDService<AiInteraction> {
|
|
|
203
204
|
}
|
|
204
205
|
|
|
205
206
|
// TODO: Validation: Check if JSON.parse(metadata).tools_invoked starts with solid_
|
|
206
|
-
let metadata = {};
|
|
207
|
+
let metadata: any = {};
|
|
207
208
|
try {
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
209
|
+
if (typeof aiInteraction.metadata === "string") {
|
|
210
|
+
metadata = JSON.parse(aiInteraction.metadata);
|
|
211
|
+
} else if (typeof aiInteraction.metadata === "object" && aiInteraction.metadata !== null) {
|
|
212
|
+
metadata = aiInteraction.metadata;
|
|
213
|
+
} else {
|
|
214
|
+
// optional fallback
|
|
215
|
+
metadata = {};
|
|
216
|
+
}
|
|
217
|
+
} catch (e) {
|
|
211
218
|
// TODO: RESPONSE SHAPE ALERT Check if we want to control the shape of the response....
|
|
212
|
-
throw new Error(e);
|
|
219
|
+
throw new Error(`Invalid metadata JSON: ${e}`);
|
|
213
220
|
}
|
|
214
221
|
|
|
215
|
-
const toolsInvoked = metadata['
|
|
222
|
+
const toolsInvoked = metadata['toolsInvoked'];
|
|
216
223
|
if (!toolsInvoked) {
|
|
217
224
|
// TODO: RESPONSE SHAPE ALERT Check if we want to control the shape of the response....
|
|
218
225
|
throw new Error(ERROR_MESSAGES.UNABLE_TO_RESOLVE_SOLID_COMMAND);
|
|
@@ -59,7 +59,7 @@ export class ExcelService {
|
|
|
59
59
|
// headers.reduce((acc, header) => ({ ...acc, [header]: '' }), {})
|
|
60
60
|
// ).commit(); // Write a dummy record with headers
|
|
61
61
|
|
|
62
|
-
|
|
62
|
+
workbook.commit();
|
|
63
63
|
return passThrough;
|
|
64
64
|
}
|
|
65
65
|
|
|
@@ -86,7 +86,7 @@ export class ExcelService {
|
|
|
86
86
|
this.logger.debug(`✅ Chunk ${chunkIndex} written to Excel`);
|
|
87
87
|
}
|
|
88
88
|
|
|
89
|
-
|
|
89
|
+
workbook.commit();
|
|
90
90
|
// passThrough.end(); // ✅ Properly close the stream
|
|
91
91
|
} catch (error) {
|
|
92
92
|
this.logger.error(`❌ Error writing Excel: ${error.message}`);
|
|
@@ -121,7 +121,7 @@ export class IngestMetadataService {
|
|
|
121
121
|
ingestionInfo.collectionId = collectionId;
|
|
122
122
|
|
|
123
123
|
// Delete and re-insert a document representing the full json...
|
|
124
|
-
await this.deleteInsertRagDocumentForModuleMetadataJsonFile(ingestionInfo, fullPath, fileName)
|
|
124
|
+
// await this.deleteInsertRagDocumentForModuleMetadataJsonFile(ingestionInfo, fullPath, fileName)
|
|
125
125
|
|
|
126
126
|
// Delete and re-insert a chunk representing the module.
|
|
127
127
|
await this.deleteInsertRagChunkForModule(ingestionInfo, moduleMetadata);
|
|
@@ -129,11 +129,29 @@ export class IngestMetadataService {
|
|
|
129
129
|
// Delete and re-insert chunks representing each model.
|
|
130
130
|
for (const model of moduleMetadata.models) {
|
|
131
131
|
await this.deleteInsertRagChunkForModel(ingestionInfo, enabledModule, model);
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
132
|
+
|
|
133
|
+
// Disabling this for now...
|
|
134
|
+
// for (const field of model.fields) {
|
|
135
|
+
// await this.deleteInsertRagChunkForField(ingestionInfo, enabledModule, model.singularName, field);
|
|
136
|
+
// }
|
|
135
137
|
}
|
|
136
138
|
|
|
139
|
+
// TODO: Delete and re-insert chunks representing roles
|
|
140
|
+
|
|
141
|
+
// TODO: Delete and re-insert chunks representing menus
|
|
142
|
+
|
|
143
|
+
// TODO: Delete and re-insert chunks representing actions
|
|
144
|
+
|
|
145
|
+
// TODO: Delete and re-insert chunks representing list views
|
|
146
|
+
|
|
147
|
+
// TODO: Delete and re-insert chunks representing kanban views
|
|
148
|
+
|
|
149
|
+
// TODO: Delete and re-insert chunks representing form views
|
|
150
|
+
|
|
151
|
+
// TODO: Delete and re-insert chunks representing security rules
|
|
152
|
+
|
|
153
|
+
// TODO: Delete and re-insert chunks representing scheduled jobs
|
|
154
|
+
|
|
137
155
|
// Save ingestion info to disk...
|
|
138
156
|
fs.writeFileSync(enabledModulIngestionInfoFullPath, JSON.stringify({ ...ingestionInfo }, null, 2), 'utf8');
|
|
139
157
|
}
|
|
@@ -242,7 +260,7 @@ export class IngestMetadataService {
|
|
|
242
260
|
description: m?.description ?? null,
|
|
243
261
|
|
|
244
262
|
// Include field names to detect field-level changes at module granularity - maybe remove this later?
|
|
245
|
-
fields: Array.isArray(m?.fields) ? m.fields.map((f: any) => f?.name ?? null) : [],
|
|
263
|
+
// fields: Array.isArray(m?.fields) ? m.fields.map((f: any) => f?.name ?? null) : [],
|
|
246
264
|
})),
|
|
247
265
|
});
|
|
248
266
|
|
|
@@ -270,6 +288,7 @@ Usage: Use this chunk to choose the correct model/field chunks for code generati
|
|
|
270
288
|
|
|
271
289
|
// metadata has to be concise and queryable
|
|
272
290
|
const metadata = {
|
|
291
|
+
collectionName: `${moduleName}-rag-collection`,
|
|
273
292
|
kind: 'solidx-metadata',
|
|
274
293
|
type: 'module',
|
|
275
294
|
moduleName,
|
|
@@ -288,7 +307,8 @@ Usage: Use this chunk to choose the correct model/field chunks for code generati
|
|
|
288
307
|
}
|
|
289
308
|
|
|
290
309
|
const r = await this.ragClient.documents.create({
|
|
291
|
-
|
|
310
|
+
chunks: [text],
|
|
311
|
+
// raw_text: text,
|
|
292
312
|
metadata: metadata,
|
|
293
313
|
collectionIds: [ingestionInfo.collectionId],
|
|
294
314
|
});
|
|
@@ -364,10 +384,15 @@ Fields (${fields.length}) [name:type|flags]:
|
|
|
364
384
|
${fieldSummaryLines.join('\n')}
|
|
365
385
|
|
|
366
386
|
Usage: Use this chunk to generate DTOs, subscribers, custom service methods, and CRUD handlers for ${modelName}.
|
|
367
|
-
|
|
387
|
+
|
|
388
|
+
Full model metadata json:
|
|
389
|
+
${JSON.stringify(model)}
|
|
390
|
+
|
|
391
|
+
`;
|
|
368
392
|
|
|
369
393
|
// 4) Metadata (concise & queryable)
|
|
370
394
|
const metadata = {
|
|
395
|
+
collectionName: `${moduleName}-rag-collection`,
|
|
371
396
|
kind: 'solidx-metadata',
|
|
372
397
|
type: 'model',
|
|
373
398
|
moduleName,
|
|
@@ -392,7 +417,7 @@ For exact constraints (enum/min/max/regex/default), consult the individual field
|
|
|
392
417
|
|
|
393
418
|
// 6) Create new document (R2R auto-generates the ID)
|
|
394
419
|
const r = await this.ragClient.documents.create({
|
|
395
|
-
|
|
420
|
+
chunks: [text],
|
|
396
421
|
metadata,
|
|
397
422
|
collectionIds: [ingestionInfo.collectionId],
|
|
398
423
|
});
|
|
@@ -661,6 +686,7 @@ For exact constraints (enum/min/max/regex/default), consult the individual field
|
|
|
661
686
|
|
|
662
687
|
// 4) Build text + metadata tailored to FieldMetadata
|
|
663
688
|
const { text, metadata } = this._buildFieldTextAndMetadata(moduleName, modelName, field);
|
|
689
|
+
|
|
664
690
|
// also keep the hash in metadata for audit/debug
|
|
665
691
|
(metadata as any).schemaHash = schemaHash;
|
|
666
692
|
|
|
@@ -26,6 +26,7 @@ import { CsvService } from './csv.service';
|
|
|
26
26
|
import { ExcelService } from './excel.service';
|
|
27
27
|
import { SolidIntrospectService } from './solid-introspect.service';
|
|
28
28
|
import { ERROR_MESSAGES } from 'src/constants/error-messages';
|
|
29
|
+
import { parseFlexibleDate } from 'src/helpers/date.helper';
|
|
29
30
|
|
|
30
31
|
interface ImportTemplateFileInfo {
|
|
31
32
|
stream: NodeJS.ReadableStream;
|
|
@@ -566,10 +567,11 @@ export class ImportTransactionService extends CRUDService<ImportTransaction> {
|
|
|
566
567
|
// TODO Move this logic to field crud managers i.e add a parse method to the field crud manager interface
|
|
567
568
|
switch (fieldType) {
|
|
568
569
|
case SolidFieldType.relation: {
|
|
569
|
-
return await this.populateDtoForRelations(fieldMetadata, record, key, dtoRecord);
|
|
570
|
+
return await this.populateDtoForRelations(fieldMetadata, record, key, dtoRecord);
|
|
570
571
|
}
|
|
571
572
|
case SolidFieldType.date:
|
|
572
|
-
case SolidFieldType.datetime:
|
|
573
|
+
case SolidFieldType.datetime:
|
|
574
|
+
return this.populateDtoForDate(record, key, fieldMetadata, dtoRecord);
|
|
573
575
|
case SolidFieldType.int:
|
|
574
576
|
case SolidFieldType.bigint:
|
|
575
577
|
case SolidFieldType.decimal:
|
|
@@ -579,16 +581,24 @@ export class ImportTransactionService extends CRUDService<ImportTransaction> {
|
|
|
579
581
|
case SolidFieldType.selectionStatic:
|
|
580
582
|
case SolidFieldType.selectionDynamic:
|
|
581
583
|
return this.populateDtoForSelectionValues(dtoRecord, fieldMetadata, record, key);
|
|
582
|
-
|
|
584
|
+
case SolidFieldType.email:
|
|
585
|
+
case SolidFieldType.shortText:
|
|
586
|
+
case SolidFieldType.longtext:
|
|
587
|
+
case SolidFieldType.richText: {
|
|
588
|
+
dtoRecord[fieldMetadata.name] = record[key] ? String(record[key]).trim() : null; // Trim text fields and set to null if empty
|
|
589
|
+
return dtoRecord;
|
|
590
|
+
}
|
|
591
|
+
default: {
|
|
583
592
|
dtoRecord[fieldMetadata.name] = record[key];
|
|
584
593
|
return dtoRecord;
|
|
594
|
+
}
|
|
585
595
|
}
|
|
586
596
|
}
|
|
587
597
|
|
|
588
598
|
private populateDtoForSelectionValues(dtoRecord: Record<string, any>, fieldMetadata: FieldMetadata, record: Record<string, any>, key: string) {
|
|
589
599
|
const rawValue = record[key];
|
|
590
600
|
|
|
591
|
-
if (rawValue == null) {
|
|
601
|
+
if (rawValue == null || rawValue === '' || (typeof rawValue === 'string' && rawValue.trim() === '')) {
|
|
592
602
|
dtoRecord[fieldMetadata.name] = null;
|
|
593
603
|
return dtoRecord;
|
|
594
604
|
}
|
|
@@ -611,16 +621,28 @@ export class ImportTransactionService extends CRUDService<ImportTransaction> {
|
|
|
611
621
|
|
|
612
622
|
|
|
613
623
|
private populateDtoForBoolean(dtoRecord: Record<string, any>, fieldMetadata: FieldMetadata, record: Record<string, any>, key: string) {
|
|
614
|
-
const
|
|
615
|
-
if (
|
|
616
|
-
|
|
624
|
+
const cellValue = record[key];
|
|
625
|
+
if (cellValue === null || cellValue === undefined || cellValue === '') {
|
|
626
|
+
dtoRecord[fieldMetadata.name] = null; // If the cell is empty, set the field to null
|
|
627
|
+
return dtoRecord;
|
|
628
|
+
}
|
|
629
|
+
|
|
630
|
+
// If the cell contains values other than 'true', '1', 'yes', 'false', '0', 'no', throw an error
|
|
631
|
+
const booleanValue = (String(cellValue).toLowerCase() === 'true' || String(cellValue) === '1' || String(cellValue).toLowerCase() === 'yes') ? true : (String(cellValue).toLowerCase() === 'false' || String(cellValue) === '0' || String(cellValue).toLowerCase() === 'no') ? false : null;
|
|
632
|
+
if (booleanValue === null) {
|
|
633
|
+
throw new Error(`Invalid boolean value for cell ${key} with value ${cellValue}`);
|
|
617
634
|
}
|
|
618
635
|
dtoRecord[fieldMetadata.name] = booleanValue;
|
|
619
636
|
return dtoRecord;
|
|
620
637
|
}
|
|
621
638
|
|
|
622
639
|
private populateDtoForNumber(dtoRecord: Record<string, any>, fieldMetadata: FieldMetadata, record: Record<string, any>, key: string) {
|
|
623
|
-
const
|
|
640
|
+
const cellValue = record[key];
|
|
641
|
+
if (cellValue === null || cellValue === undefined || cellValue === '') {
|
|
642
|
+
dtoRecord[fieldMetadata.name] = null; // If the cell is empty, set the field to null
|
|
643
|
+
return dtoRecord;
|
|
644
|
+
}
|
|
645
|
+
const numberValue = Number(cellValue);
|
|
624
646
|
if (isNaN(numberValue)) {
|
|
625
647
|
throw new Error(`Invalid number value for cell ${key} with value ${record[key]}`);
|
|
626
648
|
}
|
|
@@ -630,10 +652,23 @@ export class ImportTransactionService extends CRUDService<ImportTransaction> {
|
|
|
630
652
|
|
|
631
653
|
private populateDtoForDate(record: Record<string, any>, key: string, fieldMetadata: FieldMetadata, dtoRecord: Record<string, any>) {
|
|
632
654
|
{
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
655
|
+
// Get the cell value
|
|
656
|
+
const cellValue = record[key];
|
|
657
|
+
if (!cellValue) { //If cell is a falsy value (empty)
|
|
658
|
+
dtoRecord[fieldMetadata.name] = null; // If the cell is empty, set the field to null
|
|
659
|
+
return dtoRecord;
|
|
636
660
|
}
|
|
661
|
+
// Use flexible date parser
|
|
662
|
+
console.log(cellValue,'cellValue');
|
|
663
|
+
|
|
664
|
+
const dateValue = parseFlexibleDate(cellValue);
|
|
665
|
+
console.log(dateValue,'dateValue');
|
|
666
|
+
|
|
667
|
+
if (!dateValue) {
|
|
668
|
+
throw new Error(
|
|
669
|
+
`Invalid date value for cell ${key} with value ${cellValue}`
|
|
670
|
+
);
|
|
671
|
+
}
|
|
637
672
|
dtoRecord[fieldMetadata.name] = dateValue;
|
|
638
673
|
return dtoRecord;
|
|
639
674
|
}
|
|
@@ -5,6 +5,7 @@ import { SolidRegistry } from "src/helpers/solid-registry";
|
|
|
5
5
|
import { ModelMetadataService } from "../model-metadata.service";
|
|
6
6
|
import { CreateModelMetadataDto } from "src/dtos/create-model-metadata.dto";
|
|
7
7
|
import { ModuleMetadataService } from "../module-metadata.service";
|
|
8
|
+
import { model } from "mongoose";
|
|
8
9
|
|
|
9
10
|
@Injectable()
|
|
10
11
|
export class SolidCreateModelWithFieldsMcpToolResponseHandler implements IMcpToolResponseHandler {
|
|
@@ -19,9 +20,9 @@ export class SolidCreateModelWithFieldsMcpToolResponseHandler implements IMcpToo
|
|
|
19
20
|
async apply(aiInteraction: AiInteraction) {
|
|
20
21
|
// const aiResponse = JSON.parse(aiInteraction.message);
|
|
21
22
|
const escapedMessage = aiInteraction.message.replace(/\\'/g, "'");
|
|
22
|
-
const
|
|
23
|
+
const aiResponseMessage = JSON.parse(escapedMessage);
|
|
23
24
|
|
|
24
|
-
const { moduleUserKey, modelSchema } =
|
|
25
|
+
const { moduleUserKey, ...modelSchema } = aiResponseMessage;
|
|
25
26
|
const moduleMetadata = await this.moduleMetadataService.findOneByUserKey(moduleUserKey);
|
|
26
27
|
if (!moduleMetadata) {
|
|
27
28
|
throw new Error(`Module with user key ${moduleUserKey} not found.`);
|
|
@@ -16,9 +16,9 @@ export class SolidCreateModuleMcpToolResponseHandler implements IMcpToolResponse
|
|
|
16
16
|
}
|
|
17
17
|
|
|
18
18
|
async apply(aiInteraction: AiInteraction) {
|
|
19
|
-
const
|
|
19
|
+
const aiResponseMessage = JSON.parse(aiInteraction.message);
|
|
20
20
|
|
|
21
|
-
const moduleMetadata =
|
|
21
|
+
const moduleMetadata = aiResponseMessage?.moduleMetadata ?? {};
|
|
22
22
|
|
|
23
23
|
// TODO: Validate if another module with same name exists, if it does then raise an error...
|
|
24
24
|
|