snow-flow 2.7.0 โ 2.7.2
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/mcp/servicenow-machine-learning-mcp.d.ts +24 -0
- package/dist/mcp/servicenow-machine-learning-mcp.js +274 -9
- package/dist/mcp/servicenow-operations-mcp.js +18 -1
- package/dist/test-fetch-incidents.d.ts +2 -0
- package/dist/test-fetch-incidents.js +84 -0
- package/dist/test-incident-access.d.ts +2 -0
- package/dist/test-incident-access.js +72 -0
- package/dist/test-limit-problem.d.ts +2 -0
- package/dist/test-limit-problem.js +60 -0
- package/dist/test-ml-batch-fix.d.ts +2 -0
- package/dist/test-ml-batch-fix.js +104 -0
- package/dist/test-ml-batch.d.ts +2 -0
- package/dist/test-ml-batch.js +87 -0
- package/dist/test-ml-query.d.ts +2 -0
- package/dist/test-ml-query.js +100 -0
- package/dist/test-ml-training.d.ts +2 -0
- package/dist/test-ml-training.js +32 -0
- package/dist/test-operations-query.d.ts +2 -0
- package/dist/test-operations-query.js +84 -0
- package/dist/test-snow-query-incidents.d.ts +2 -0
- package/dist/test-snow-query-incidents.js +66 -0
- package/dist/utils/servicenow-client.d.ts +4 -0
- package/dist/utils/servicenow-client.js +28 -0
- package/dist/version.d.ts +1 -0
- package/dist/version.js +10 -0
- package/package.json +1 -1
|
@@ -61,6 +61,30 @@ export declare class ServiceNowMachineLearningMCP {
|
|
|
61
61
|
private fetchIncidentVolumeHistory;
|
|
62
62
|
private prepareTimeSeriesData;
|
|
63
63
|
private fetchSingleIncident;
|
|
64
|
+
/**
|
|
65
|
+
* Train model using streaming to handle large datasets efficiently
|
|
66
|
+
*/
|
|
67
|
+
private trainWithStreaming;
|
|
68
|
+
/**
|
|
69
|
+
* Fetch a batch of incidents with offset for streaming
|
|
70
|
+
*/
|
|
71
|
+
private fetchIncidentBatch;
|
|
72
|
+
/**
|
|
73
|
+
* Create feature hasher for memory-efficient vocabulary management
|
|
74
|
+
*/
|
|
75
|
+
private createFeatureHasher;
|
|
76
|
+
/**
|
|
77
|
+
* Process batch with feature hashing
|
|
78
|
+
*/
|
|
79
|
+
private processBatchWithHashing;
|
|
80
|
+
/**
|
|
81
|
+
* Create optimized model for memory efficiency
|
|
82
|
+
*/
|
|
83
|
+
private createOptimizedModel;
|
|
84
|
+
/**
|
|
85
|
+
* Optimized data preparation with feature hashing
|
|
86
|
+
*/
|
|
87
|
+
private prepareIncidentDataOptimized;
|
|
64
88
|
private detectAnomalies;
|
|
65
89
|
private predictChangeRisk;
|
|
66
90
|
private evaluateModel;
|
|
@@ -141,6 +141,21 @@ class ServiceNowMachineLearningMCP {
|
|
|
141
141
|
type: 'array',
|
|
142
142
|
items: { type: 'string' },
|
|
143
143
|
description: 'Specific categories to focus on for training (optional)'
|
|
144
|
+
},
|
|
145
|
+
batch_size: {
|
|
146
|
+
type: 'number',
|
|
147
|
+
description: 'Process data in batches to prevent memory overload',
|
|
148
|
+
default: 100
|
|
149
|
+
},
|
|
150
|
+
max_vocabulary_size: {
|
|
151
|
+
type: 'number',
|
|
152
|
+
description: 'Maximum vocabulary size using feature hashing',
|
|
153
|
+
default: 10000
|
|
154
|
+
},
|
|
155
|
+
streaming_mode: {
|
|
156
|
+
type: 'boolean',
|
|
157
|
+
description: 'Enable streaming mode for very large datasets',
|
|
158
|
+
default: true
|
|
144
159
|
}
|
|
145
160
|
}
|
|
146
161
|
},
|
|
@@ -490,7 +505,7 @@ class ServiceNowMachineLearningMCP {
|
|
|
490
505
|
* Uses PI if available, otherwise uses custom TensorFlow.js
|
|
491
506
|
*/
|
|
492
507
|
async trainIncidentClassifier(args) {
|
|
493
|
-
const { sample_size = 1000, epochs = 50, validation_split = 0.2, query = '', intelligent_selection = true, focus_categories = [] } = args;
|
|
508
|
+
const { sample_size = 1000, epochs = 50, validation_split = 0.2, query = '', intelligent_selection = true, focus_categories = [], batch_size = 100, max_vocabulary_size = 10000, streaming_mode = true } = args;
|
|
494
509
|
try {
|
|
495
510
|
// Wait for ML API check if not complete
|
|
496
511
|
if (!this.mlAPICheckComplete) {
|
|
@@ -526,19 +541,25 @@ class ServiceNowMachineLearningMCP {
|
|
|
526
541
|
}
|
|
527
542
|
}
|
|
528
543
|
// Use custom TensorFlow.js neural network
|
|
529
|
-
this.logger.info(`Training custom LSTM neural network
|
|
530
|
-
|
|
544
|
+
this.logger.info(`Training custom LSTM neural network with intelligent memory management...`);
|
|
545
|
+
this.logger.info(`Settings: batch_size=${batch_size}, max_vocabulary=${max_vocabulary_size}, streaming=${streaming_mode}`);
|
|
546
|
+
// If streaming mode is enabled, process data in batches
|
|
547
|
+
if (streaming_mode && sample_size > batch_size * 2) {
|
|
548
|
+
return await this.trainWithStreaming(args);
|
|
549
|
+
}
|
|
550
|
+
// For smaller datasets, use the original approach but with optimizations
|
|
551
|
+
// ๐ด CRITICAL FIX: Use full sample_size, not artificially limited amount
|
|
531
552
|
const incidents = await this.fetchIncidentData(sample_size, {
|
|
532
553
|
query,
|
|
533
554
|
intelligent_selection,
|
|
534
555
|
focus_categories
|
|
535
556
|
});
|
|
536
|
-
this.logger.info(`Retrieved ${incidents.length} incidents
|
|
557
|
+
this.logger.info(`Retrieved ${incidents.length} incidents for initial training`);
|
|
537
558
|
if (incidents.length < 100) {
|
|
538
559
|
throw new Error(`Insufficient data for training (need at least 100 incidents, got ${incidents.length})`);
|
|
539
560
|
}
|
|
540
|
-
// Prepare training data
|
|
541
|
-
const { features, labels, tokenizer, categories } = await this.
|
|
561
|
+
// Prepare training data with memory optimization
|
|
562
|
+
const { features, labels, tokenizer, categories } = await this.prepareIncidentDataOptimized(incidents, max_vocabulary_size);
|
|
542
563
|
// Create neural network model
|
|
543
564
|
const model = tf.sequential({
|
|
544
565
|
layers: [
|
|
@@ -866,7 +887,18 @@ class ServiceNowMachineLearningMCP {
|
|
|
866
887
|
}
|
|
867
888
|
// Prepare input for custom neural network
|
|
868
889
|
const text = `${incidentData.short_description} ${incidentData.description}`;
|
|
869
|
-
|
|
890
|
+
let tokenized;
|
|
891
|
+
// Check if using feature hashing (streaming mode) or traditional tokenizer
|
|
892
|
+
if (this.incidentClassifier.tokenizer.has('_vocabulary_size')) {
|
|
893
|
+
// Using feature hashing
|
|
894
|
+
const vocabularySize = this.incidentClassifier.tokenizer.get('_vocabulary_size');
|
|
895
|
+
const hasher = this.createFeatureHasher(vocabularySize);
|
|
896
|
+
tokenized = hasher(text);
|
|
897
|
+
}
|
|
898
|
+
else {
|
|
899
|
+
// Using traditional tokenizer
|
|
900
|
+
tokenized = this.tokenizeText(text, this.incidentClassifier.tokenizer, this.incidentClassifier.maxLength);
|
|
901
|
+
}
|
|
870
902
|
const input = tf.tensor2d([tokenized]);
|
|
871
903
|
// Predict
|
|
872
904
|
const prediction = this.incidentClassifier.model.predict(input);
|
|
@@ -987,8 +1019,11 @@ class ServiceNowMachineLearningMCP {
|
|
|
987
1019
|
status.incident_classifier = this.incidentClassifier ? {
|
|
988
1020
|
status: 'trained',
|
|
989
1021
|
categories: this.incidentClassifier.categories.length,
|
|
990
|
-
vocabulary_size: this.incidentClassifier.tokenizer.
|
|
991
|
-
|
|
1022
|
+
vocabulary_size: this.incidentClassifier.tokenizer.has('_vocabulary_size')
|
|
1023
|
+
? this.incidentClassifier.tokenizer.get('_vocabulary_size')
|
|
1024
|
+
: this.incidentClassifier.tokenizer.size,
|
|
1025
|
+
model_size: await this.getModelSize(this.incidentClassifier.model),
|
|
1026
|
+
memory_efficient: this.incidentClassifier.tokenizer.has('_vocabulary_size')
|
|
992
1027
|
} : { status: 'not_trained' };
|
|
993
1028
|
}
|
|
994
1029
|
if (model === 'all' || model === 'change_risk') {
|
|
@@ -1057,7 +1092,9 @@ class ServiceNowMachineLearningMCP {
|
|
|
1057
1092
|
finalQuery = 'ORDERBYDESCsys_created_on';
|
|
1058
1093
|
}
|
|
1059
1094
|
// Use searchRecords for proper authentication handling
|
|
1095
|
+
// ๐ด CRITICAL FIX: Use the actual limit parameter, not default of 10
|
|
1060
1096
|
const response = await this.client.searchRecords('incident', finalQuery, limit);
|
|
1097
|
+
this.logger.info(`Attempting to fetch ${limit} incidents with query: ${finalQuery}`);
|
|
1061
1098
|
if (!response.success || !response.data?.result) {
|
|
1062
1099
|
throw new Error('Failed to fetch incident data. Ensure you have read access to the incident table.');
|
|
1063
1100
|
}
|
|
@@ -1311,6 +1348,234 @@ class ServiceNowMachineLearningMCP {
|
|
|
1311
1348
|
(new Date(inc.resolved_at).getTime() - new Date(inc.sys_created_on).getTime()) / 1000 : undefined
|
|
1312
1349
|
};
|
|
1313
1350
|
}
|
|
1351
|
+
/**
|
|
1352
|
+
* Train model using streaming to handle large datasets efficiently
|
|
1353
|
+
*/
|
|
1354
|
+
async trainWithStreaming(args) {
|
|
1355
|
+
const { sample_size, batch_size, epochs, validation_split, query, intelligent_selection, focus_categories, max_vocabulary_size } = args;
|
|
1356
|
+
this.logger.info(`Starting streaming training with batch size ${batch_size}`);
|
|
1357
|
+
// Create feature hasher for vocabulary management
|
|
1358
|
+
const featureHasher = this.createFeatureHasher(max_vocabulary_size);
|
|
1359
|
+
// Initialize model with proper architecture
|
|
1360
|
+
const model = this.createOptimizedModel(max_vocabulary_size);
|
|
1361
|
+
// Process data in batches
|
|
1362
|
+
const totalBatches = Math.ceil(sample_size / batch_size);
|
|
1363
|
+
let processedSamples = 0;
|
|
1364
|
+
let allCategories = new Set();
|
|
1365
|
+
for (let batchNum = 0; batchNum < totalBatches; batchNum++) {
|
|
1366
|
+
const offset = batchNum * batch_size;
|
|
1367
|
+
const currentBatchSize = Math.min(batch_size, sample_size - offset);
|
|
1368
|
+
this.logger.info(`Processing batch ${batchNum + 1}/${totalBatches} (${currentBatchSize} samples)`);
|
|
1369
|
+
// Fetch batch of incidents
|
|
1370
|
+
const batchIncidents = await this.fetchIncidentBatch(currentBatchSize, offset, {
|
|
1371
|
+
query,
|
|
1372
|
+
intelligent_selection,
|
|
1373
|
+
focus_categories
|
|
1374
|
+
});
|
|
1375
|
+
if (batchIncidents.length === 0)
|
|
1376
|
+
break;
|
|
1377
|
+
// Extract categories
|
|
1378
|
+
batchIncidents.forEach(inc => allCategories.add(inc.category));
|
|
1379
|
+
// Process batch with feature hashing
|
|
1380
|
+
const { features, labels } = this.processBatchWithHashing(batchIncidents, Array.from(allCategories), featureHasher);
|
|
1381
|
+
// Train on batch
|
|
1382
|
+
await model.fit(features, labels, {
|
|
1383
|
+
epochs: Math.ceil(epochs / totalBatches), // Distribute epochs across batches
|
|
1384
|
+
batchSize: 32,
|
|
1385
|
+
verbose: 0,
|
|
1386
|
+
callbacks: {
|
|
1387
|
+
onBatchEnd: async (batch, logs) => {
|
|
1388
|
+
if (batch % 10 === 0) {
|
|
1389
|
+
this.logger.info(`Batch ${batch}: loss=${logs?.loss?.toFixed(4)}`);
|
|
1390
|
+
}
|
|
1391
|
+
}
|
|
1392
|
+
}
|
|
1393
|
+
});
|
|
1394
|
+
// Clean up tensors to free memory
|
|
1395
|
+
features.dispose();
|
|
1396
|
+
labels.dispose();
|
|
1397
|
+
processedSamples += batchIncidents.length;
|
|
1398
|
+
// Force garbage collection hint
|
|
1399
|
+
if (global.gc) {
|
|
1400
|
+
global.gc();
|
|
1401
|
+
}
|
|
1402
|
+
}
|
|
1403
|
+
this.logger.info(`Streaming training completed. Processed ${processedSamples} samples in ${totalBatches} batches`);
|
|
1404
|
+
// Save model
|
|
1405
|
+
const modelId = `incident_classifier_${Date.now()}`;
|
|
1406
|
+
const modelInfo = {
|
|
1407
|
+
id: modelId,
|
|
1408
|
+
categories: Array.from(allCategories),
|
|
1409
|
+
vocabulary_size: max_vocabulary_size,
|
|
1410
|
+
training_samples: processedSamples,
|
|
1411
|
+
batch_size: batch_size,
|
|
1412
|
+
created_at: new Date().toISOString()
|
|
1413
|
+
};
|
|
1414
|
+
return {
|
|
1415
|
+
content: [{
|
|
1416
|
+
type: 'text',
|
|
1417
|
+
text: JSON.stringify({
|
|
1418
|
+
success: true,
|
|
1419
|
+
model_id: modelId,
|
|
1420
|
+
model_info: modelInfo,
|
|
1421
|
+
training_stats: {
|
|
1422
|
+
total_samples: processedSamples,
|
|
1423
|
+
batches_processed: totalBatches,
|
|
1424
|
+
memory_efficient: true
|
|
1425
|
+
}
|
|
1426
|
+
}, null, 2)
|
|
1427
|
+
}]
|
|
1428
|
+
};
|
|
1429
|
+
}
|
|
1430
|
+
/**
|
|
1431
|
+
* Fetch a batch of incidents with offset for streaming
|
|
1432
|
+
*/
|
|
1433
|
+
async fetchIncidentBatch(limit, offset, options) {
|
|
1434
|
+
const { query, intelligent_selection, focus_categories } = options;
|
|
1435
|
+
let finalQuery = query;
|
|
1436
|
+
if (intelligent_selection && !query) {
|
|
1437
|
+
// Build intelligent query (same as before)
|
|
1438
|
+
const queries = [];
|
|
1439
|
+
queries.push('sys_created_onONLast 6 months');
|
|
1440
|
+
queries.push('(priority=1^ORpriority=2^ORpriority=3^ORpriority=4)');
|
|
1441
|
+
queries.push('(active=true^ORactive=false)');
|
|
1442
|
+
if (focus_categories.length > 0) {
|
|
1443
|
+
const categoryQuery = focus_categories.map((cat) => `category=${cat}`).join('^OR');
|
|
1444
|
+
queries.push(`(${categoryQuery})`);
|
|
1445
|
+
}
|
|
1446
|
+
else {
|
|
1447
|
+
queries.push('categoryISNOTEMPTY');
|
|
1448
|
+
}
|
|
1449
|
+
finalQuery = queries.join('^');
|
|
1450
|
+
}
|
|
1451
|
+
// Add offset for pagination
|
|
1452
|
+
if (finalQuery && !finalQuery.includes('ORDERBY')) {
|
|
1453
|
+
finalQuery += '^ORDERBYDESCsys_created_on';
|
|
1454
|
+
}
|
|
1455
|
+
// ServiceNow API supports offset through sysparm_offset
|
|
1456
|
+
// ๐ด CRITICAL FIX: Ensure we're using the right limit for batches
|
|
1457
|
+
const response = await this.client.searchRecordsWithOffset('incident', finalQuery, limit, offset);
|
|
1458
|
+
this.logger.info(`Fetching batch: limit=${limit}, offset=${offset}, query=${finalQuery}`);
|
|
1459
|
+
if (!response.success || !response.data?.result) {
|
|
1460
|
+
return [];
|
|
1461
|
+
}
|
|
1462
|
+
return response.data.result.map((inc) => ({
|
|
1463
|
+
short_description: inc.short_description || '',
|
|
1464
|
+
description: inc.description || '',
|
|
1465
|
+
category: inc.category || 'uncategorized',
|
|
1466
|
+
subcategory: inc.subcategory || '',
|
|
1467
|
+
priority: parseInt(inc.priority) || 3,
|
|
1468
|
+
impact: parseInt(inc.impact) || 2,
|
|
1469
|
+
urgency: parseInt(inc.urgency) || 2,
|
|
1470
|
+
resolved: inc.resolved === 'true'
|
|
1471
|
+
}));
|
|
1472
|
+
}
|
|
1473
|
+
/**
|
|
1474
|
+
* Create feature hasher for memory-efficient vocabulary management
|
|
1475
|
+
*/
|
|
1476
|
+
createFeatureHasher(maxFeatures) {
|
|
1477
|
+
return (text) => {
|
|
1478
|
+
const words = text.toLowerCase().split(/\s+/);
|
|
1479
|
+
const features = new Array(100).fill(0); // Fixed sequence length
|
|
1480
|
+
words.slice(0, 100).forEach((word, idx) => {
|
|
1481
|
+
// Simple hash function
|
|
1482
|
+
let hash = 0;
|
|
1483
|
+
for (let i = 0; i < word.length; i++) {
|
|
1484
|
+
hash = ((hash << 5) - hash) + word.charCodeAt(i);
|
|
1485
|
+
hash = hash & hash; // Convert to 32-bit integer
|
|
1486
|
+
}
|
|
1487
|
+
// Map to vocabulary size
|
|
1488
|
+
features[idx] = Math.abs(hash) % maxFeatures;
|
|
1489
|
+
});
|
|
1490
|
+
return features;
|
|
1491
|
+
};
|
|
1492
|
+
}
|
|
1493
|
+
/**
|
|
1494
|
+
* Process batch with feature hashing
|
|
1495
|
+
*/
|
|
1496
|
+
processBatchWithHashing(incidents, categories, hasher) {
|
|
1497
|
+
const sequences = [];
|
|
1498
|
+
const labels = [];
|
|
1499
|
+
for (const incident of incidents) {
|
|
1500
|
+
const text = `${incident.short_description} ${incident.description}`;
|
|
1501
|
+
const sequence = hasher(text);
|
|
1502
|
+
sequences.push(sequence);
|
|
1503
|
+
// One-hot encode category
|
|
1504
|
+
const categoryIndex = categories.indexOf(incident.category);
|
|
1505
|
+
const label = new Array(categories.length).fill(0);
|
|
1506
|
+
if (categoryIndex >= 0) {
|
|
1507
|
+
label[categoryIndex] = 1;
|
|
1508
|
+
}
|
|
1509
|
+
labels.push(label);
|
|
1510
|
+
}
|
|
1511
|
+
return {
|
|
1512
|
+
features: tf.tensor2d(sequences),
|
|
1513
|
+
labels: tf.tensor2d(labels)
|
|
1514
|
+
};
|
|
1515
|
+
}
|
|
1516
|
+
/**
|
|
1517
|
+
* Create optimized model for memory efficiency
|
|
1518
|
+
*/
|
|
1519
|
+
createOptimizedModel(vocabularySize) {
|
|
1520
|
+
return tf.sequential({
|
|
1521
|
+
layers: [
|
|
1522
|
+
// Use embedding with smaller dimensions
|
|
1523
|
+
tf.layers.embedding({
|
|
1524
|
+
inputDim: vocabularySize,
|
|
1525
|
+
outputDim: 64, // Reduced from 128
|
|
1526
|
+
inputLength: 100
|
|
1527
|
+
}),
|
|
1528
|
+
// Smaller LSTM
|
|
1529
|
+
tf.layers.lstm({
|
|
1530
|
+
units: 32, // Reduced from 64
|
|
1531
|
+
returnSequences: false,
|
|
1532
|
+
dropout: 0.2,
|
|
1533
|
+
recurrentDropout: 0.2
|
|
1534
|
+
}),
|
|
1535
|
+
// Smaller dense layer
|
|
1536
|
+
tf.layers.dense({
|
|
1537
|
+
units: 16, // Reduced from 32
|
|
1538
|
+
activation: 'relu'
|
|
1539
|
+
}),
|
|
1540
|
+
tf.layers.dropout({ rate: 0.3 }),
|
|
1541
|
+
// Output layer (dynamic based on categories)
|
|
1542
|
+
tf.layers.dense({
|
|
1543
|
+
units: 10, // Will be adjusted based on actual categories
|
|
1544
|
+
activation: 'softmax'
|
|
1545
|
+
})
|
|
1546
|
+
]
|
|
1547
|
+
});
|
|
1548
|
+
}
|
|
1549
|
+
/**
|
|
1550
|
+
* Optimized data preparation with feature hashing
|
|
1551
|
+
*/
|
|
1552
|
+
async prepareIncidentDataOptimized(incidents, maxVocabularySize) {
|
|
1553
|
+
const hasher = this.createFeatureHasher(maxVocabularySize);
|
|
1554
|
+
const categories = [...new Set(incidents.map(i => i.category))];
|
|
1555
|
+
const sequences = [];
|
|
1556
|
+
const labels = [];
|
|
1557
|
+
for (const incident of incidents) {
|
|
1558
|
+
const text = `${incident.short_description} ${incident.description}`;
|
|
1559
|
+
const sequence = hasher(text);
|
|
1560
|
+
sequences.push(sequence);
|
|
1561
|
+
// One-hot encode category
|
|
1562
|
+
const categoryIndex = categories.indexOf(incident.category);
|
|
1563
|
+
const label = new Array(categories.length).fill(0);
|
|
1564
|
+
if (categoryIndex >= 0) {
|
|
1565
|
+
label[categoryIndex] = 1;
|
|
1566
|
+
}
|
|
1567
|
+
labels.push(label);
|
|
1568
|
+
}
|
|
1569
|
+
// Create a minimal Map for compatibility
|
|
1570
|
+
const tokenizerMap = new Map();
|
|
1571
|
+
tokenizerMap.set('_vocabulary_size', maxVocabularySize);
|
|
1572
|
+
return {
|
|
1573
|
+
features: tf.tensor2d(sequences),
|
|
1574
|
+
labels: tf.tensor2d(labels),
|
|
1575
|
+
tokenizer: tokenizerMap,
|
|
1576
|
+
categories
|
|
1577
|
+
};
|
|
1578
|
+
}
|
|
1314
1579
|
async detectAnomalies(args) {
|
|
1315
1580
|
// Implement anomaly detection
|
|
1316
1581
|
return {
|
|
@@ -781,8 +781,17 @@ class ServiceNowOperationsMCP {
|
|
|
781
781
|
const incidents = await this.client.searchRecords('incident', processedQuery, limit);
|
|
782
782
|
let result = {
|
|
783
783
|
total_results: incidents.success ? incidents.data.result.length : 0,
|
|
784
|
-
|
|
784
|
+
// ๐ด PERFORMANCE FIX: Only include full incident data if specifically requested via fields
|
|
785
|
+
incidents: (fields && fields.length > 0) ? (incidents.success ? incidents.data.result : []) : []
|
|
785
786
|
};
|
|
787
|
+
// Add basic summary instead of full data for performance
|
|
788
|
+
if (incidents.success && incidents.data.result.length > 0 && (!fields || fields.length === 0)) {
|
|
789
|
+
result.summary = {
|
|
790
|
+
first_incident: incidents.data.result[0].number || 'Unknown',
|
|
791
|
+
sample_categories: [...new Set(incidents.data.result.slice(0, 5).map((inc) => inc.category || 'none'))],
|
|
792
|
+
sample_priorities: [...new Set(incidents.data.result.slice(0, 5).map((inc) => inc.priority || 'none'))]
|
|
793
|
+
};
|
|
794
|
+
}
|
|
786
795
|
// Add intelligent _analysis if requested
|
|
787
796
|
if (include__analysis && incidents.success && incidents.data.result.length > 0) {
|
|
788
797
|
const _analysis = await this.analyzeIncidents(incidents.data.result);
|
|
@@ -1096,6 +1105,11 @@ class ServiceNowOperationsMCP {
|
|
|
1096
1105
|
processNaturalLanguageQuery(query, context) {
|
|
1097
1106
|
// Convert natural language to ServiceNow encoded query
|
|
1098
1107
|
const lowerQuery = query.toLowerCase();
|
|
1108
|
+
// If already a ServiceNow encoded query, return as-is
|
|
1109
|
+
if (query.includes('=') || query.includes('!=') || query.includes('^') || query.includes('LIKE')) {
|
|
1110
|
+
logger_js_1.logger.info(`Using raw ServiceNow query: ${query}`);
|
|
1111
|
+
return query;
|
|
1112
|
+
}
|
|
1099
1113
|
// Common ServiceNow query patterns
|
|
1100
1114
|
if (lowerQuery.includes('high priority')) {
|
|
1101
1115
|
return 'priority=1';
|
|
@@ -1109,6 +1123,9 @@ class ServiceNowOperationsMCP {
|
|
|
1109
1123
|
if (lowerQuery.includes('closed') || lowerQuery.includes('resolved')) {
|
|
1110
1124
|
return 'state=6^ORstate=7';
|
|
1111
1125
|
}
|
|
1126
|
+
if (lowerQuery.includes('all') || lowerQuery === '') {
|
|
1127
|
+
return ''; // Empty query returns all records
|
|
1128
|
+
}
|
|
1112
1129
|
if (lowerQuery.includes('today')) {
|
|
1113
1130
|
return 'sys_created_onONToday@javascript:gs.daysAgoStart(0)@javascript:gs.daysAgoEnd(0)';
|
|
1114
1131
|
}
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const servicenow_client_js_1 = require("./utils/servicenow-client.js");
|
|
4
|
+
const logger_js_1 = require("./utils/logger.js");
|
|
5
|
+
// Test fetchIncidentData like the ML MCP does
|
|
6
|
+
async function testFetchIncidents() {
|
|
7
|
+
const logger = new logger_js_1.Logger('FetchIncidentsTest');
|
|
8
|
+
const client = new servicenow_client_js_1.ServiceNowClient();
|
|
9
|
+
console.log('๐ฅ Testing fetchIncidentData with 2000 samples...');
|
|
10
|
+
// Simulate exactly what the ML MCP fetchIncidentData method does
|
|
11
|
+
const sample_size = 2000;
|
|
12
|
+
const intelligent_selection = true;
|
|
13
|
+
const focus_categories = [];
|
|
14
|
+
const query = '';
|
|
15
|
+
let finalQuery = query;
|
|
16
|
+
// If intelligent selection is enabled and no custom query provided
|
|
17
|
+
if (intelligent_selection && !query) {
|
|
18
|
+
// Build an intelligent query that gets a balanced dataset
|
|
19
|
+
const queries = [];
|
|
20
|
+
// Get mix of recent and older incidents
|
|
21
|
+
queries.push('sys_created_onONLast 6 months');
|
|
22
|
+
// Get mix of priorities
|
|
23
|
+
queries.push('(priority=1^ORpriority=2^ORpriority=3^ORpriority=4)');
|
|
24
|
+
// Get mix of active and resolved
|
|
25
|
+
queries.push('(active=true^ORactive=false)');
|
|
26
|
+
// Focus on specific categories if provided
|
|
27
|
+
if (focus_categories.length > 0) {
|
|
28
|
+
const categoryQuery = focus_categories.map(cat => `category=${cat}`).join('^OR');
|
|
29
|
+
queries.push(`(${categoryQuery})`);
|
|
30
|
+
}
|
|
31
|
+
else {
|
|
32
|
+
// Get diverse categories
|
|
33
|
+
queries.push('categoryISNOTEMPTY');
|
|
34
|
+
}
|
|
35
|
+
// Combine all queries
|
|
36
|
+
finalQuery = queries.join('^');
|
|
37
|
+
logger.info(`Using intelligent query selection: ${finalQuery}`);
|
|
38
|
+
}
|
|
39
|
+
else if (query) {
|
|
40
|
+
logger.info(`Using custom query: ${query}`);
|
|
41
|
+
}
|
|
42
|
+
// Always order by sys_created_on DESC to get most recent first
|
|
43
|
+
if (finalQuery && !finalQuery.includes('ORDERBY')) {
|
|
44
|
+
finalQuery += '^ORDERBYDESCsys_created_on';
|
|
45
|
+
}
|
|
46
|
+
else if (!finalQuery) {
|
|
47
|
+
finalQuery = 'ORDERBYDESCsys_created_on';
|
|
48
|
+
}
|
|
49
|
+
logger.info(`Attempting to fetch ${sample_size} incidents with query: ${finalQuery}`);
|
|
50
|
+
try {
|
|
51
|
+
// ๐ด CRITICAL: Use the actual sample_size parameter, not default of 10
|
|
52
|
+
const response = await client.searchRecords('incident', finalQuery, sample_size);
|
|
53
|
+
if (!response.success || !response.data?.result) {
|
|
54
|
+
throw new Error('Failed to fetch incident data. Ensure you have read access to the incident table.');
|
|
55
|
+
}
|
|
56
|
+
logger.info(`โ
SUCCESS: Fetched ${response.data.result.length} incidents for ML training (requested: ${sample_size})`);
|
|
57
|
+
// Show data distribution like the ML MCP does
|
|
58
|
+
if (response.data.result.length > 0) {
|
|
59
|
+
const categoryDistribution = {};
|
|
60
|
+
const priorityDistribution = {};
|
|
61
|
+
response.data.result.forEach((inc) => {
|
|
62
|
+
const category = inc.category || 'uncategorized';
|
|
63
|
+
const priority = inc.priority || '3';
|
|
64
|
+
categoryDistribution[category] = (categoryDistribution[category] || 0) + 1;
|
|
65
|
+
priorityDistribution[priority] = (priorityDistribution[priority] || 0) + 1;
|
|
66
|
+
});
|
|
67
|
+
logger.info('Data distribution:');
|
|
68
|
+
logger.info(`Categories: ${JSON.stringify(categoryDistribution)}`);
|
|
69
|
+
logger.info(`Priorities: ${JSON.stringify(priorityDistribution)}`);
|
|
70
|
+
// Check if we have enough for training
|
|
71
|
+
if (response.data.result.length >= 100) {
|
|
72
|
+
logger.info('โ
Sufficient data for ML training (need at least 100 incidents)');
|
|
73
|
+
}
|
|
74
|
+
else {
|
|
75
|
+
logger.warn(`โ ๏ธ Insufficient data for training (need at least 100 incidents, got ${response.data.result.length})`);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
catch (error) {
|
|
80
|
+
logger.error('โ fetchIncidentData failed:', error);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
testFetchIncidents().catch(console.error);
|
|
84
|
+
//# sourceMappingURL=test-fetch-incidents.js.map
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const servicenow_client_js_1 = require("./utils/servicenow-client.js");
|
|
4
|
+
const logger_js_1 = require("./utils/logger.js");
|
|
5
|
+
async function testIncidentAccess() {
|
|
6
|
+
const logger = new logger_js_1.Logger('IncidentAccessTest');
|
|
7
|
+
const client = new servicenow_client_js_1.ServiceNowClient();
|
|
8
|
+
logger.info('Testing incident table access...');
|
|
9
|
+
// Test 1: Empty query (get all)
|
|
10
|
+
try {
|
|
11
|
+
logger.info('Test 1: Fetching ALL incidents with empty query...');
|
|
12
|
+
const allIncidents = await client.searchRecords('incident', '', 10);
|
|
13
|
+
logger.info(`Result: ${allIncidents.success ? 'SUCCESS' : 'FAILED'}`);
|
|
14
|
+
logger.info(`Count: ${allIncidents.data?.result?.length || 0}`);
|
|
15
|
+
if (allIncidents.data?.result?.length > 0) {
|
|
16
|
+
logger.info('Sample incident states:');
|
|
17
|
+
allIncidents.data.result.slice(0, 5).forEach((inc) => {
|
|
18
|
+
logger.info(`- ${inc.number}: state=${inc.state}, active=${inc.active}`);
|
|
19
|
+
});
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
catch (error) {
|
|
23
|
+
logger.error('Test 1 failed:', error);
|
|
24
|
+
}
|
|
25
|
+
// Test 2: State not equal to 7
|
|
26
|
+
try {
|
|
27
|
+
logger.info('\nTest 2: Fetching incidents where state!=7...');
|
|
28
|
+
const notClosedIncidents = await client.searchRecords('incident', 'state!=7', 10);
|
|
29
|
+
logger.info(`Result: ${notClosedIncidents.success ? 'SUCCESS' : 'FAILED'}`);
|
|
30
|
+
logger.info(`Count: ${notClosedIncidents.data?.result?.length || 0}`);
|
|
31
|
+
}
|
|
32
|
+
catch (error) {
|
|
33
|
+
logger.error('Test 2 failed:', error);
|
|
34
|
+
}
|
|
35
|
+
// Test 3: Active incidents
|
|
36
|
+
try {
|
|
37
|
+
logger.info('\nTest 3: Fetching active incidents...');
|
|
38
|
+
const activeIncidents = await client.searchRecords('incident', 'active=true', 10);
|
|
39
|
+
logger.info(`Result: ${activeIncidents.success ? 'SUCCESS' : 'FAILED'}`);
|
|
40
|
+
logger.info(`Count: ${activeIncidents.data?.result?.length || 0}`);
|
|
41
|
+
}
|
|
42
|
+
catch (error) {
|
|
43
|
+
logger.error('Test 3 failed:', error);
|
|
44
|
+
}
|
|
45
|
+
// Test 4: All states
|
|
46
|
+
try {
|
|
47
|
+
logger.info('\nTest 4: Checking incident states...');
|
|
48
|
+
const states = ['1', '2', '3', '4', '5', '6', '7', '8'];
|
|
49
|
+
for (const state of states) {
|
|
50
|
+
const stateIncidents = await client.searchRecords('incident', `state=${state}`, 1);
|
|
51
|
+
if (stateIncidents.success && stateIncidents.data?.result?.length > 0) {
|
|
52
|
+
logger.info(`State ${state}: Found incidents`);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
catch (error) {
|
|
57
|
+
logger.error('Test 4 failed:', error);
|
|
58
|
+
}
|
|
59
|
+
// Test 5: User permissions
|
|
60
|
+
try {
|
|
61
|
+
logger.info('\nTest 5: Testing user permissions...');
|
|
62
|
+
const userInfo = await client.get('/api/now/table/sys_user/me');
|
|
63
|
+
logger.info(`Current user: ${userInfo.data?.result?.user_name || 'Unknown'}`);
|
|
64
|
+
logger.info(`Roles: ${userInfo.data?.result?.roles || 'Unknown'}`);
|
|
65
|
+
}
|
|
66
|
+
catch (error) {
|
|
67
|
+
logger.error('Failed to get user info:', error);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
// Run the test
|
|
71
|
+
testIncidentAccess().catch(console.error);
|
|
72
|
+
//# sourceMappingURL=test-incident-access.js.map
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const servicenow_client_js_1 = require("./utils/servicenow-client.js");
|
|
4
|
+
const logger_js_1 = require("./utils/logger.js");
|
|
5
|
+
async function testLimitProblem() {
|
|
6
|
+
const logger = new logger_js_1.Logger('LimitTest');
|
|
7
|
+
const client = new servicenow_client_js_1.ServiceNowClient();
|
|
8
|
+
logger.info('Testing incident limits...');
|
|
9
|
+
// Test different limits
|
|
10
|
+
const limits = [10, 50, 100, 500, 1000];
|
|
11
|
+
for (const limit of limits) {
|
|
12
|
+
try {
|
|
13
|
+
logger.info(`\nTesting with limit ${limit}...`);
|
|
14
|
+
const result = await client.searchRecords('incident', 'state!=7', limit);
|
|
15
|
+
logger.info(`Result: ${result.success ? 'SUCCESS' : 'FAILED'}`);
|
|
16
|
+
logger.info(`Requested: ${limit}, Got: ${result.data?.result?.length || 0}`);
|
|
17
|
+
}
|
|
18
|
+
catch (error) {
|
|
19
|
+
logger.error(`Test with limit ${limit} failed:`, error);
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
// Test with empty query (should get all incidents)
|
|
23
|
+
try {
|
|
24
|
+
logger.info(`\nTesting all incidents with limit 1000...`);
|
|
25
|
+
const result = await client.searchRecords('incident', '', 1000);
|
|
26
|
+
logger.info(`Result: ${result.success ? 'SUCCESS' : 'FAILED'}`);
|
|
27
|
+
logger.info(`Total incidents found: ${result.data?.result?.length || 0}`);
|
|
28
|
+
if (result.data?.result?.length > 0) {
|
|
29
|
+
const states = new Map();
|
|
30
|
+
const priorities = new Map();
|
|
31
|
+
result.data.result.forEach((inc) => {
|
|
32
|
+
const state = inc.state || 'unknown';
|
|
33
|
+
const priority = inc.priority || 'unknown';
|
|
34
|
+
states.set(state, (states.get(state) || 0) + 1);
|
|
35
|
+
priorities.set(priority, (priorities.get(priority) || 0) + 1);
|
|
36
|
+
});
|
|
37
|
+
logger.info('State distribution:', Object.fromEntries(states));
|
|
38
|
+
logger.info('Priority distribution:', Object.fromEntries(priorities));
|
|
39
|
+
// Count active incidents (state != 6 and state != 7)
|
|
40
|
+
const activeIncidents = result.data.result.filter((inc) => inc.state !== '6' && inc.state !== '7');
|
|
41
|
+
logger.info(`Active incidents (state not 6 or 7): ${activeIncidents.length}`);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
catch (error) {
|
|
45
|
+
logger.error('All incidents test failed:', error);
|
|
46
|
+
}
|
|
47
|
+
// Test what the ML training was actually requesting
|
|
48
|
+
try {
|
|
49
|
+
logger.info(`\nTesting ML training scenario (sample_size 2000)...`);
|
|
50
|
+
const result = await client.searchRecords('incident', 'state!=7', 2000);
|
|
51
|
+
logger.info(`ML training result: ${result.success ? 'SUCCESS' : 'FAILED'}`);
|
|
52
|
+
logger.info(`ML would get: ${result.data?.result?.length || 0} incidents out of requested 2000`);
|
|
53
|
+
}
|
|
54
|
+
catch (error) {
|
|
55
|
+
logger.error('ML training test failed:', error);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
// Run the test
|
|
59
|
+
testLimitProblem().catch(console.error);
|
|
60
|
+
//# sourceMappingURL=test-limit-problem.js.map
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const servicenow_client_js_1 = require("./utils/servicenow-client.js");
|
|
4
|
+
const logger_js_1 = require("./utils/logger.js");
|
|
5
|
+
// Test of ML training nu correct batch sizes gebruikt
|
|
6
|
+
async function testMLBatchFix() {
|
|
7
|
+
const logger = new logger_js_1.Logger('MLBatchFixTest');
|
|
8
|
+
const client = new servicenow_client_js_1.ServiceNowClient();
|
|
9
|
+
console.log('๐ Testing ML training batch size fix...');
|
|
10
|
+
// Simuleer ML fetchIncidentData methode met verschillende batch sizes
|
|
11
|
+
async function testFetchIncidentData(sample_size, description) {
|
|
12
|
+
logger.info(`\n--- ${description} ---`);
|
|
13
|
+
logger.info(`Requesting ${sample_size} incidents`);
|
|
14
|
+
const intelligent_selection = true;
|
|
15
|
+
const focus_categories = [];
|
|
16
|
+
const query = '';
|
|
17
|
+
let finalQuery = '';
|
|
18
|
+
if (intelligent_selection && !query) {
|
|
19
|
+
const queries = [];
|
|
20
|
+
queries.push('sys_created_onONLast 6 months');
|
|
21
|
+
queries.push('(priority=1^ORpriority=2^ORpriority=3^ORpriority=4)');
|
|
22
|
+
queries.push('(active=true^ORactive=false)');
|
|
23
|
+
queries.push('categoryISNOTEMPTY');
|
|
24
|
+
finalQuery = queries.join('^') + '^ORDERBYDESCsys_created_on';
|
|
25
|
+
}
|
|
26
|
+
try {
|
|
27
|
+
const start = Date.now();
|
|
28
|
+
// ๐ด KEY TEST: Use the actual sample_size parameter
|
|
29
|
+
const response = await client.searchRecords('incident', finalQuery, sample_size);
|
|
30
|
+
const duration = Date.now() - start;
|
|
31
|
+
if (response.success) {
|
|
32
|
+
const actualCount = response.data?.result?.length || 0;
|
|
33
|
+
logger.info(`โ
SUCCESS: Got ${actualCount}/${sample_size} incidents (${duration}ms)`);
|
|
34
|
+
if (actualCount === Math.min(sample_size, 1000)) { // ServiceNow might limit to 1000
|
|
35
|
+
logger.info(`โ
Correct batch size used`);
|
|
36
|
+
}
|
|
37
|
+
else if (actualCount === 10) {
|
|
38
|
+
logger.error(`โ STILL USING DEFAULT LIMIT OF 10!`);
|
|
39
|
+
}
|
|
40
|
+
else {
|
|
41
|
+
logger.info(`โน๏ธ Got ${actualCount} incidents (might be limited by data available)`);
|
|
42
|
+
}
|
|
43
|
+
return actualCount;
|
|
44
|
+
}
|
|
45
|
+
else {
|
|
46
|
+
logger.error(`โ FAILED: Could not fetch incidents`);
|
|
47
|
+
return 0;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
catch (error) {
|
|
51
|
+
logger.error(`โ ERROR:`, error);
|
|
52
|
+
return 0;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
// Test verschillende batch sizes
|
|
56
|
+
const testCases = [
|
|
57
|
+
{ size: 50, desc: "Small batch (50)" },
|
|
58
|
+
{ size: 100, desc: "Medium batch (100)" },
|
|
59
|
+
{ size: 200, desc: "Large batch (200)" },
|
|
60
|
+
{ size: 500, desc: "XL batch (500)" }
|
|
61
|
+
];
|
|
62
|
+
console.log(`\n๐งช Testing different batch sizes:`);
|
|
63
|
+
const results = [];
|
|
64
|
+
for (const testCase of testCases) {
|
|
65
|
+
const count = await testFetchIncidentData(testCase.size, testCase.desc);
|
|
66
|
+
results.push({ requested: testCase.size, actual: count });
|
|
67
|
+
}
|
|
68
|
+
console.log(`\n๐ Batch Size Results:`);
|
|
69
|
+
results.forEach(r => {
|
|
70
|
+
const success = r.actual > 10 && r.actual >= Math.min(r.requested, 100); // At least more than default 10
|
|
71
|
+
console.log(` ${r.requested} requested โ ${r.actual} actual ${success ? 'โ
' : 'โ'}`);
|
|
72
|
+
});
|
|
73
|
+
// Test streaming batches
|
|
74
|
+
console.log(`\n๐ Testing streaming batch functionality:`);
|
|
75
|
+
const totalSample = 300;
|
|
76
|
+
const batchSize = 100;
|
|
77
|
+
const totalBatches = Math.ceil(totalSample / batchSize);
|
|
78
|
+
let streamingTotal = 0;
|
|
79
|
+
for (let batchNum = 0; batchNum < totalBatches; batchNum++) {
|
|
80
|
+
const offset = batchNum * batchSize;
|
|
81
|
+
const currentBatchSize = Math.min(batchSize, totalSample - offset);
|
|
82
|
+
logger.info(`Streaming batch ${batchNum + 1}/${totalBatches}: offset=${offset}, size=${currentBatchSize}`);
|
|
83
|
+
try {
|
|
84
|
+
const response = await client.searchRecordsWithOffset('incident', 'sys_created_onONLast 6 months^categoryISNOTEMPTY^ORDERBYDESCsys_created_on', currentBatchSize, offset);
|
|
85
|
+
if (response.success) {
|
|
86
|
+
const batchActual = response.data?.result?.length || 0;
|
|
87
|
+
streamingTotal += batchActual;
|
|
88
|
+
logger.info(` โ
Batch got ${batchActual}/${currentBatchSize} incidents`);
|
|
89
|
+
if (batchActual < currentBatchSize) {
|
|
90
|
+
logger.info(` ๐ End of data reached`);
|
|
91
|
+
break;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
catch (error) {
|
|
96
|
+
logger.error(` โ Streaming batch ${batchNum + 1} failed:`, error);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
console.log(`\n๐ฏ Streaming Results:`);
|
|
100
|
+
console.log(` Total streamed: ${streamingTotal}/${totalSample}`);
|
|
101
|
+
console.log(` Batch functionality: ${streamingTotal > 10 ? 'โ
Working' : 'โ Still limited to 10'}`);
|
|
102
|
+
}
|
|
103
|
+
testMLBatchFix().catch(console.error);
|
|
104
|
+
//# sourceMappingURL=test-ml-batch-fix.js.map
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const servicenow_client_js_1 = require("./utils/servicenow-client.js");
|
|
4
|
+
const logger_js_1 = require("./utils/logger.js");
|
|
5
|
+
// Test ML batch functionality met realistische sizes
|
|
6
|
+
async function testMLBatch() {
|
|
7
|
+
const logger = new logger_js_1.Logger('MLBatchTest');
|
|
8
|
+
const client = new servicenow_client_js_1.ServiceNowClient();
|
|
9
|
+
console.log('๐ฅ Testing ML batch functionality with realistic batch sizes...');
|
|
10
|
+
// ML training parameters (zoals ML daadwerkelijk gebruikt)
|
|
11
|
+
const sample_size = 1000; // Totaal aantal incidenten
|
|
12
|
+
const batch_size = 200; // Per batch
|
|
13
|
+
const intelligent_selection = true;
|
|
14
|
+
const focus_categories = [];
|
|
15
|
+
const query = '';
|
|
16
|
+
// Build intelligent query (zoals ML doet)
|
|
17
|
+
let finalQuery = '';
|
|
18
|
+
if (intelligent_selection && !query) {
|
|
19
|
+
const queries = [];
|
|
20
|
+
queries.push('sys_created_onONLast 6 months');
|
|
21
|
+
queries.push('(priority=1^ORpriority=2^ORpriority=3^ORpriority=4)');
|
|
22
|
+
queries.push('(active=true^ORactive=false)');
|
|
23
|
+
queries.push('categoryISNOTEMPTY');
|
|
24
|
+
finalQuery = queries.join('^') + '^ORDERBYDESCsys_created_on';
|
|
25
|
+
}
|
|
26
|
+
logger.info(`Testing ML batch training:`);
|
|
27
|
+
logger.info(`- Sample size: ${sample_size}`);
|
|
28
|
+
logger.info(`- Batch size: ${batch_size}`);
|
|
29
|
+
logger.info(`- Query: ${finalQuery}`);
|
|
30
|
+
// Test batch processing zoals ML training doet
|
|
31
|
+
const totalBatches = Math.ceil(sample_size / batch_size);
|
|
32
|
+
let totalProcessed = 0;
|
|
33
|
+
console.log(`\n๐ฆ Processing ${totalBatches} batches...`);
|
|
34
|
+
for (let batchNum = 0; batchNum < totalBatches; batchNum++) {
|
|
35
|
+
const offset = batchNum * batch_size;
|
|
36
|
+
const currentBatchSize = Math.min(batch_size, sample_size - offset);
|
|
37
|
+
logger.info(`\n--- Batch ${batchNum + 1}/${totalBatches} ---`);
|
|
38
|
+
logger.info(`Offset: ${offset}, Size: ${currentBatchSize}`);
|
|
39
|
+
try {
|
|
40
|
+
// Test searchRecordsWithOffset (zoals ML streaming doet)
|
|
41
|
+
const start = Date.now();
|
|
42
|
+
const response = await client.searchRecordsWithOffset('incident', finalQuery, currentBatchSize, offset);
|
|
43
|
+
const duration = Date.now() - start;
|
|
44
|
+
if (response.success && response.data?.result) {
|
|
45
|
+
const actualCount = response.data.result.length;
|
|
46
|
+
totalProcessed += actualCount;
|
|
47
|
+
logger.info(`โ
Success: ${actualCount}/${currentBatchSize} incidents (${duration}ms)`);
|
|
48
|
+
// Sample categories for this batch
|
|
49
|
+
const categories = new Set(response.data.result.slice(0, 5).map((inc) => inc.category || 'none'));
|
|
50
|
+
logger.info(`Categories sample: ${Array.from(categories).join(', ')}`);
|
|
51
|
+
// If we got fewer than requested, we've reached the end
|
|
52
|
+
if (actualCount < currentBatchSize) {
|
|
53
|
+
logger.info(`๐ Reached end of data (got ${actualCount} < ${currentBatchSize})`);
|
|
54
|
+
break;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
else {
|
|
58
|
+
logger.error(`โ Batch ${batchNum + 1} failed`);
|
|
59
|
+
break;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
catch (error) {
|
|
63
|
+
logger.error(`โ Batch ${batchNum + 1} error:`, error);
|
|
64
|
+
break;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
console.log(`\n๐ฏ ML Batch Results:`);
|
|
68
|
+
console.log(`- Total processed: ${totalProcessed}/${sample_size} incidents`);
|
|
69
|
+
console.log(`- Success rate: ${((totalProcessed / sample_size) * 100).toFixed(1)}%`);
|
|
70
|
+
console.log(`- Ready for ML training: ${totalProcessed >= 100 ? 'โ
Yes' : 'โ No (need 100+ incidents)'}`);
|
|
71
|
+
// Test normal single batch (non-streaming)
|
|
72
|
+
console.log(`\n๐ Testing single batch (non-streaming mode):`);
|
|
73
|
+
try {
|
|
74
|
+
const singleBatch = await client.searchRecords('incident', finalQuery, batch_size);
|
|
75
|
+
if (singleBatch.success) {
|
|
76
|
+
logger.info(`โ
Single batch: ${singleBatch.data?.result?.length || 0}/${batch_size} incidents`);
|
|
77
|
+
}
|
|
78
|
+
else {
|
|
79
|
+
logger.error(`โ Single batch failed`);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
catch (error) {
|
|
83
|
+
logger.error(`โ Single batch error:`, error);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
testMLBatch().catch(console.error);
|
|
87
|
+
//# sourceMappingURL=test-ml-batch.js.map
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const servicenow_client_js_1 = require("./utils/servicenow-client.js");
|
|
4
|
+
const logger_js_1 = require("./utils/logger.js");
|
|
5
|
+
async function testMLQuery() {
|
|
6
|
+
const logger = new logger_js_1.Logger('MLQueryTest');
|
|
7
|
+
const client = new servicenow_client_js_1.ServiceNowClient();
|
|
8
|
+
logger.info('Testing ML training query...');
|
|
9
|
+
// Build the exact same intelligent query as ML training
|
|
10
|
+
const queries = [];
|
|
11
|
+
queries.push('sys_created_onONLast 6 months');
|
|
12
|
+
queries.push('(priority=1^ORpriority=2^ORpriority=3^ORpriority=4)');
|
|
13
|
+
queries.push('(active=true^ORactive=false)');
|
|
14
|
+
queries.push('categoryISNOTEMPTY');
|
|
15
|
+
const finalQuery = queries.join('^');
|
|
16
|
+
logger.info(`Using ML intelligent query: ${finalQuery}`);
|
|
17
|
+
// Test 1: Without ordering
|
|
18
|
+
try {
|
|
19
|
+
logger.info('\nTest 1: Query without ordering...');
|
|
20
|
+
const result1 = await client.searchRecords('incident', finalQuery, 10);
|
|
21
|
+
logger.info(`Result: ${result1.success ? 'SUCCESS' : 'FAILED'}`);
|
|
22
|
+
logger.info(`Count: ${result1.data?.result?.length || 0}`);
|
|
23
|
+
if (result1.data?.result?.length > 0) {
|
|
24
|
+
logger.info('Sample incidents:');
|
|
25
|
+
result1.data.result.slice(0, 3).forEach((inc) => {
|
|
26
|
+
logger.info(`- ${inc.number}: created=${inc.sys_created_on}, priority=${inc.priority}, category=${inc.category || 'none'}`);
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
catch (error) {
|
|
31
|
+
logger.error('Test 1 failed:', error);
|
|
32
|
+
}
|
|
33
|
+
// Test 2: With ordering (as ML uses)
|
|
34
|
+
try {
|
|
35
|
+
logger.info('\nTest 2: Query with ordering...');
|
|
36
|
+
const orderedQuery = finalQuery + '^ORDERBYDESCsys_created_on';
|
|
37
|
+
const result2 = await client.searchRecords('incident', orderedQuery, 10);
|
|
38
|
+
logger.info(`Result: ${result2.success ? 'SUCCESS' : 'FAILED'}`);
|
|
39
|
+
logger.info(`Count: ${result2.data?.result?.length || 0}`);
|
|
40
|
+
}
|
|
41
|
+
catch (error) {
|
|
42
|
+
logger.error('Test 2 failed:', error);
|
|
43
|
+
}
|
|
44
|
+
// Test 3: With offset (for streaming)
|
|
45
|
+
try {
|
|
46
|
+
logger.info('\nTest 3: Query with offset...');
|
|
47
|
+
const orderedQuery = finalQuery + '^ORDERBYDESCsys_created_on';
|
|
48
|
+
const result3 = await client.searchRecordsWithOffset('incident', orderedQuery, 10, 0);
|
|
49
|
+
logger.info(`Result: ${result3.success ? 'SUCCESS' : 'FAILED'}`);
|
|
50
|
+
logger.info(`Count: ${result3.data?.result?.length || 0}`);
|
|
51
|
+
}
|
|
52
|
+
catch (error) {
|
|
53
|
+
logger.error('Test 3 failed:', error);
|
|
54
|
+
}
|
|
55
|
+
// Test 4: Simplify query to find the issue
|
|
56
|
+
try {
|
|
57
|
+
logger.info('\nTest 4: Testing each query part separately...');
|
|
58
|
+
const testQueries = [
|
|
59
|
+
'sys_created_onONLast 6 months',
|
|
60
|
+
'priority=1^ORpriority=2^ORpriority=3^ORpriority=4',
|
|
61
|
+
'active=true^ORactive=false',
|
|
62
|
+
'categoryISNOTEMPTY',
|
|
63
|
+
'category!=null',
|
|
64
|
+
'categoryISNOT EMPTY',
|
|
65
|
+
'' // empty query
|
|
66
|
+
];
|
|
67
|
+
for (const q of testQueries) {
|
|
68
|
+
const result = await client.searchRecords('incident', q, 5);
|
|
69
|
+
logger.info(`Query "${q}" -> ${result.data?.result?.length || 0} results`);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
catch (error) {
|
|
73
|
+
logger.error('Test 4 failed:', error);
|
|
74
|
+
}
|
|
75
|
+
// Test 5: Check if categoryISNOTEMPTY is the problem
|
|
76
|
+
try {
|
|
77
|
+
logger.info('\nTest 5: Testing without category filter...');
|
|
78
|
+
const queriesWithoutCategory = [
|
|
79
|
+
'sys_created_onONLast 6 months',
|
|
80
|
+
'(priority=1^ORpriority=2^ORpriority=3^ORpriority=4)',
|
|
81
|
+
'(active=true^ORactive=false)'
|
|
82
|
+
];
|
|
83
|
+
const queryWithoutCategory = queriesWithoutCategory.join('^');
|
|
84
|
+
logger.info(`Query without category: ${queryWithoutCategory}`);
|
|
85
|
+
const result5 = await client.searchRecords('incident', queryWithoutCategory, 10);
|
|
86
|
+
logger.info(`Result: ${result5.success ? 'SUCCESS' : 'FAILED'}`);
|
|
87
|
+
logger.info(`Count: ${result5.data?.result?.length || 0}`);
|
|
88
|
+
if (result5.data?.result?.length > 0) {
|
|
89
|
+
logger.info('Categories in results:');
|
|
90
|
+
const categories = new Set(result5.data.result.map((inc) => inc.category || 'empty'));
|
|
91
|
+
logger.info(`Unique categories: ${Array.from(categories).join(', ')}`);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
catch (error) {
|
|
95
|
+
logger.error('Test 5 failed:', error);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
// Run the test
|
|
99
|
+
testMLQuery().catch(console.error);
|
|
100
|
+
//# sourceMappingURL=test-ml-query.js.map
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const servicenow_machine_learning_mcp_js_1 = require("./mcp/servicenow-machine-learning-mcp.js");
|
|
4
|
+
async function testMLTraining() {
|
|
5
|
+
console.log('๐ฅ Testing ML training with 2000+ incidents...');
|
|
6
|
+
try {
|
|
7
|
+
// Test the ML training directly
|
|
8
|
+
const result = await (0, servicenow_machine_learning_mcp_js_1.mcp__servicenow_machine_learning__ml_train_incident_classifier)({
|
|
9
|
+
sample_size: 2000,
|
|
10
|
+
query: '', // Use intelligent selection
|
|
11
|
+
intelligent_selection: true,
|
|
12
|
+
streaming_mode: false, // Test non-streaming first
|
|
13
|
+
batch_size: 200,
|
|
14
|
+
epochs: 50,
|
|
15
|
+
validation_split: 0.2,
|
|
16
|
+
max_vocabulary_size: 10000,
|
|
17
|
+
focus_categories: []
|
|
18
|
+
});
|
|
19
|
+
console.log('โ
ML Training Result:');
|
|
20
|
+
console.log(JSON.stringify(JSON.parse(result.content[0].text), null, 2));
|
|
21
|
+
}
|
|
22
|
+
catch (error) {
|
|
23
|
+
console.error('โ ML Training failed:', error);
|
|
24
|
+
// If it fails, show more details
|
|
25
|
+
if (error instanceof Error) {
|
|
26
|
+
console.error('Error message:', error.message);
|
|
27
|
+
console.error('Stack trace:', error.stack);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
testMLTraining().catch(console.error);
|
|
32
|
+
//# sourceMappingURL=test-ml-training.js.map
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const servicenow_client_js_1 = require("./utils/servicenow-client.js");
|
|
4
|
+
const logger_js_1 = require("./utils/logger.js");
|
|
5
|
+
async function testOperationsQuery() {
|
|
6
|
+
const logger = new logger_js_1.Logger('OperationsQueryTest');
|
|
7
|
+
const client = new servicenow_client_js_1.ServiceNowClient();
|
|
8
|
+
logger.info('Testing operations query processing...');
|
|
9
|
+
// Test processNaturalLanguageQuery logic
|
|
10
|
+
const testQuery = (query) => {
|
|
11
|
+
const lowerQuery = query.toLowerCase();
|
|
12
|
+
// If already a ServiceNow encoded query, return as-is
|
|
13
|
+
if (query.includes('=') || query.includes('!=') || query.includes('^') || query.includes('LIKE')) {
|
|
14
|
+
logger.info(`Recognized as ServiceNow query: ${query}`);
|
|
15
|
+
return query;
|
|
16
|
+
}
|
|
17
|
+
logger.info(`Treating as natural language: ${query}`);
|
|
18
|
+
return `short_descriptionLIKE${query}^ORdescriptionLIKE${query}`;
|
|
19
|
+
};
|
|
20
|
+
// Test queries
|
|
21
|
+
const queries = [
|
|
22
|
+
'state!=7',
|
|
23
|
+
'active=true',
|
|
24
|
+
'priority=1',
|
|
25
|
+
'all incidents',
|
|
26
|
+
'high priority',
|
|
27
|
+
''
|
|
28
|
+
];
|
|
29
|
+
logger.info('\n--- Testing Query Processing ---');
|
|
30
|
+
for (const query of queries) {
|
|
31
|
+
const processed = testQuery(query);
|
|
32
|
+
logger.info(`Input: "${query}" -> Output: "${processed}"`);
|
|
33
|
+
}
|
|
34
|
+
logger.info('\n--- Testing Actual Queries ---');
|
|
35
|
+
// Test 1: Raw ServiceNow query
|
|
36
|
+
try {
|
|
37
|
+
logger.info('\nTest 1: Testing with state!=7...');
|
|
38
|
+
const result1 = await client.searchRecords('incident', 'state!=7', 10);
|
|
39
|
+
logger.info(`Result: ${result1.success ? 'SUCCESS' : 'FAILED'}`);
|
|
40
|
+
logger.info(`Count: ${result1.data?.result?.length || 0}`);
|
|
41
|
+
}
|
|
42
|
+
catch (error) {
|
|
43
|
+
logger.error('Test 1 failed:', error);
|
|
44
|
+
}
|
|
45
|
+
// Test 2: Empty query
|
|
46
|
+
try {
|
|
47
|
+
logger.info('\nTest 2: Testing with empty query...');
|
|
48
|
+
const result2 = await client.searchRecords('incident', '', 10);
|
|
49
|
+
logger.info(`Result: ${result2.success ? 'SUCCESS' : 'FAILED'}`);
|
|
50
|
+
logger.info(`Count: ${result2.data?.result?.length || 0}`);
|
|
51
|
+
}
|
|
52
|
+
catch (error) {
|
|
53
|
+
logger.error('Test 2 failed:', error);
|
|
54
|
+
}
|
|
55
|
+
// Test 3: Complex query
|
|
56
|
+
try {
|
|
57
|
+
logger.info('\nTest 3: Testing with complex query...');
|
|
58
|
+
const result3 = await client.searchRecords('incident', 'active=true^state!=6^state!=7', 10);
|
|
59
|
+
logger.info(`Result: ${result3.success ? 'SUCCESS' : 'FAILED'}`);
|
|
60
|
+
logger.info(`Count: ${result3.data?.result?.length || 0}`);
|
|
61
|
+
}
|
|
62
|
+
catch (error) {
|
|
63
|
+
logger.error('Test 3 failed:', error);
|
|
64
|
+
}
|
|
65
|
+
// Test 4: Test natural language conversion
|
|
66
|
+
try {
|
|
67
|
+
logger.info('\nTest 4: Testing natural language conversion...');
|
|
68
|
+
const nlQuery = 'high priority';
|
|
69
|
+
const processedQuery = testQuery(nlQuery);
|
|
70
|
+
logger.info(`Natural language: "${nlQuery}" -> "${processedQuery}"`);
|
|
71
|
+
// Now test the actual operations MCP logic
|
|
72
|
+
const operationsQuery = nlQuery.toLowerCase().includes('high priority') ? 'priority=1' : processedQuery;
|
|
73
|
+
logger.info(`Operations MCP would use: "${operationsQuery}"`);
|
|
74
|
+
const result4 = await client.searchRecords('incident', operationsQuery, 10);
|
|
75
|
+
logger.info(`Result: ${result4.success ? 'SUCCESS' : 'FAILED'}`);
|
|
76
|
+
logger.info(`Count: ${result4.data?.result?.length || 0}`);
|
|
77
|
+
}
|
|
78
|
+
catch (error) {
|
|
79
|
+
logger.error('Test 4 failed:', error);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
// Run the test
|
|
83
|
+
testOperationsQuery().catch(console.error);
|
|
84
|
+
//# sourceMappingURL=test-operations-query.js.map
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const servicenow_client_js_1 = require("./utils/servicenow-client.js");
|
|
4
|
+
const logger_js_1 = require("./utils/logger.js");
|
|
5
|
+
// Test exact same logic as snow_query_incidents MCP tool
|
|
6
|
+
async function testSnowQueryIncidents() {
|
|
7
|
+
const logger = new logger_js_1.Logger('SnowQueryIncidentsTest');
|
|
8
|
+
const client = new servicenow_client_js_1.ServiceNowClient();
|
|
9
|
+
console.log('๐ Testing snow_query_incidents MCP logic...');
|
|
10
|
+
// Simulate handleQueryIncidents exactly
|
|
11
|
+
const query = 'state!=7';
|
|
12
|
+
const limit = 5;
|
|
13
|
+
logger.info(`Querying incidents with: ${query}`);
|
|
14
|
+
try {
|
|
15
|
+
// Step 1: processNaturalLanguageQuery (from operations MCP)
|
|
16
|
+
const processNaturalLanguageQuery = (query, context) => {
|
|
17
|
+
const lowerQuery = query.toLowerCase();
|
|
18
|
+
// If already a ServiceNow encoded query, return as-is
|
|
19
|
+
if (query.includes('=') || query.includes('!=') || query.includes('^') || query.includes('LIKE')) {
|
|
20
|
+
logger.info(`Using raw ServiceNow query: ${query}`);
|
|
21
|
+
return query;
|
|
22
|
+
}
|
|
23
|
+
// Other processing would go here...
|
|
24
|
+
logger.info(`Treating as natural language: ${query}`);
|
|
25
|
+
return `short_descriptionLIKE${query}^ORdescriptionLIKE${query}`;
|
|
26
|
+
};
|
|
27
|
+
// Step 2: Process the query
|
|
28
|
+
const processedQuery = processNaturalLanguageQuery(query, 'incident');
|
|
29
|
+
logger.info(`Processed query: "${processedQuery}"`);
|
|
30
|
+
// Step 3: Execute the search (exact same as MCP)
|
|
31
|
+
const incidents = await client.searchRecords('incident', processedQuery, limit);
|
|
32
|
+
logger.info(`Search result: success=${incidents.success}, count=${incidents.data?.result?.length || 0}`);
|
|
33
|
+
// Step 4: Build result object (exact same as MCP)
|
|
34
|
+
let result = {
|
|
35
|
+
total_results: incidents.success ? incidents.data.result.length : 0,
|
|
36
|
+
incidents: incidents.success ? incidents.data.result : []
|
|
37
|
+
};
|
|
38
|
+
// Step 5: Format output (exact same as MCP)
|
|
39
|
+
const output = `Found ${incidents.success ? incidents.data.result.length : 0} incidents matching query: "${query}"\n\n${JSON.stringify(result, null, 2)}`;
|
|
40
|
+
console.log('\n๐ MCP Output would be:');
|
|
41
|
+
console.log(output);
|
|
42
|
+
if (!incidents.success || incidents.data.result.length === 0) {
|
|
43
|
+
logger.error('๐จ This matches the problem! MCP would return 0 incidents');
|
|
44
|
+
}
|
|
45
|
+
else {
|
|
46
|
+
logger.info('โ
This would work correctly in MCP');
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
catch (error) {
|
|
50
|
+
logger.error('โ Error in snow_query_incidents simulation:', error);
|
|
51
|
+
}
|
|
52
|
+
// Direct test without MCP processing
|
|
53
|
+
console.log('\n๐ Direct test without MCP processing:');
|
|
54
|
+
try {
|
|
55
|
+
const directResult = await client.searchRecords('incident', 'state!=7', limit);
|
|
56
|
+
logger.info(`Direct result: success=${directResult.success}, count=${directResult.data?.result?.length || 0}`);
|
|
57
|
+
if (directResult.success && directResult.data?.result?.length > 0) {
|
|
58
|
+
logger.info('โ
Direct call works - problem is in MCP processing');
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
catch (error) {
|
|
62
|
+
logger.error('โ Direct call also fails:', error);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
testSnowQueryIncidents().catch(console.error);
|
|
66
|
+
//# sourceMappingURL=test-snow-query-incidents.js.map
|
|
@@ -159,6 +159,10 @@ export declare class ServiceNowClient {
|
|
|
159
159
|
* Search records in a table using encoded query
|
|
160
160
|
*/
|
|
161
161
|
searchRecords(table: string, query: string, limit?: number): Promise<ServiceNowAPIResponse<any>>;
|
|
162
|
+
/**
|
|
163
|
+
* Search records with offset for pagination/streaming
|
|
164
|
+
*/
|
|
165
|
+
searchRecordsWithOffset(table: string, query: string, limit?: number, offset?: number): Promise<ServiceNowAPIResponse<any>>;
|
|
162
166
|
/**
|
|
163
167
|
* Create a record in any ServiceNow table
|
|
164
168
|
*/
|
|
@@ -1000,6 +1000,34 @@ class ServiceNowClient {
|
|
|
1000
1000
|
};
|
|
1001
1001
|
}
|
|
1002
1002
|
}
|
|
1003
|
+
/**
|
|
1004
|
+
* Search records with offset for pagination/streaming
|
|
1005
|
+
*/
|
|
1006
|
+
async searchRecordsWithOffset(table, query, limit = 10, offset = 0) {
|
|
1007
|
+
try {
|
|
1008
|
+
await this.ensureAuthenticated();
|
|
1009
|
+
const response = await this.client.get(`${this.getBaseUrl()}/api/now/table/${table}`, {
|
|
1010
|
+
params: {
|
|
1011
|
+
sysparm_query: query,
|
|
1012
|
+
sysparm_limit: limit,
|
|
1013
|
+
sysparm_offset: offset
|
|
1014
|
+
}
|
|
1015
|
+
});
|
|
1016
|
+
return {
|
|
1017
|
+
success: true,
|
|
1018
|
+
data: {
|
|
1019
|
+
result: response.data.result || []
|
|
1020
|
+
}
|
|
1021
|
+
};
|
|
1022
|
+
}
|
|
1023
|
+
catch (error) {
|
|
1024
|
+
console.error(`Failed to search records in ${table} with offset ${offset}:`, error);
|
|
1025
|
+
return {
|
|
1026
|
+
success: false,
|
|
1027
|
+
error: error instanceof Error ? error.message : String(error)
|
|
1028
|
+
};
|
|
1029
|
+
}
|
|
1030
|
+
}
|
|
1003
1031
|
/**
|
|
1004
1032
|
* Create a record in any ServiceNow table
|
|
1005
1033
|
*/
|
package/dist/version.d.ts
CHANGED
package/dist/version.js
CHANGED
|
@@ -14,6 +14,16 @@ exports.VERSION_INFO = {
|
|
|
14
14
|
name: 'Snow-Flow',
|
|
15
15
|
description: 'ServiceNow Queen Agent - Hive-Mind Intelligence for ServiceNow Development',
|
|
16
16
|
features: {
|
|
17
|
+
'2.7.1': [
|
|
18
|
+
'๐พ MEMORY-EFFICIENT ML: Streaming training for large datasets prevents memory overload',
|
|
19
|
+
'๐ฆ BATCH PROCESSING: Process data in configurable batches (default 100 records)',
|
|
20
|
+
'๐๏ธ FEATURE HASHING: Reduces vocabulary from unlimited to fixed size (default 10K)',
|
|
21
|
+
'๐ PROGRESSIVE LOADING: Only loads data as needed during training',
|
|
22
|
+
'๐งน AUTOMATIC CLEANUP: Disposes tensors after each batch to free memory',
|
|
23
|
+
'๐ SCALABLE TRAINING: Can handle datasets of any size without crashing',
|
|
24
|
+
'โก OPTIMIZED MODELS: Smaller but effective neural networks for memory efficiency',
|
|
25
|
+
'๐ REAL-TIME PROGRESS: Shows batch-by-batch training progress',
|
|
26
|
+
],
|
|
17
27
|
'2.7.0': [
|
|
18
28
|
'๐ง INTELLIGENT ML DATA SELECTION: Snow-Flow now intelligently selects balanced training data',
|
|
19
29
|
'๐ฏ CUSTOM QUERIES: Added query parameter for full control over ML training data selection',
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "snow-flow",
|
|
3
|
-
"version": "2.7.
|
|
3
|
+
"version": "2.7.2",
|
|
4
4
|
"description": "Snow-Flow: ServiceNow Advanced Intelligence Platform - 100+ real MCP tools with AI-powered swarm orchestration and neural networks. Dynamic task categorization using AI. Machine learning for incident classification, change risk prediction, and anomaly detection. Zero Mock Data, 100% Real API Integration.",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"type": "commonjs",
|