snow-flow 2.7.0 โ†’ 2.7.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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,24 @@ class ServiceNowMachineLearningMCP {
526
541
  }
527
542
  }
528
543
  // Use custom TensorFlow.js neural network
529
- this.logger.info(`Training custom LSTM neural network for incident classification with ${sample_size} samples...`);
530
- // Fetch historical incidents with intelligent selection
531
- const incidents = await this.fetchIncidentData(sample_size, {
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
+ const incidents = await this.fetchIncidentData(Math.min(sample_size, batch_size * 5), {
532
552
  query,
533
553
  intelligent_selection,
534
554
  focus_categories
535
555
  });
536
- this.logger.info(`Retrieved ${incidents.length} incidents from ServiceNow`);
556
+ this.logger.info(`Retrieved ${incidents.length} incidents for initial training`);
537
557
  if (incidents.length < 100) {
538
558
  throw new Error(`Insufficient data for training (need at least 100 incidents, got ${incidents.length})`);
539
559
  }
540
- // Prepare training data
541
- const { features, labels, tokenizer, categories } = await this.prepareIncidentData(incidents);
560
+ // Prepare training data with memory optimization
561
+ const { features, labels, tokenizer, categories } = await this.prepareIncidentDataOptimized(incidents, max_vocabulary_size);
542
562
  // Create neural network model
543
563
  const model = tf.sequential({
544
564
  layers: [
@@ -866,7 +886,18 @@ class ServiceNowMachineLearningMCP {
866
886
  }
867
887
  // Prepare input for custom neural network
868
888
  const text = `${incidentData.short_description} ${incidentData.description}`;
869
- const tokenized = this.tokenizeText(text, this.incidentClassifier.tokenizer, this.incidentClassifier.maxLength);
889
+ let tokenized;
890
+ // Check if using feature hashing (streaming mode) or traditional tokenizer
891
+ if (this.incidentClassifier.tokenizer.has('_vocabulary_size')) {
892
+ // Using feature hashing
893
+ const vocabularySize = this.incidentClassifier.tokenizer.get('_vocabulary_size');
894
+ const hasher = this.createFeatureHasher(vocabularySize);
895
+ tokenized = hasher(text);
896
+ }
897
+ else {
898
+ // Using traditional tokenizer
899
+ tokenized = this.tokenizeText(text, this.incidentClassifier.tokenizer, this.incidentClassifier.maxLength);
900
+ }
870
901
  const input = tf.tensor2d([tokenized]);
871
902
  // Predict
872
903
  const prediction = this.incidentClassifier.model.predict(input);
@@ -987,8 +1018,11 @@ class ServiceNowMachineLearningMCP {
987
1018
  status.incident_classifier = this.incidentClassifier ? {
988
1019
  status: 'trained',
989
1020
  categories: this.incidentClassifier.categories.length,
990
- vocabulary_size: this.incidentClassifier.tokenizer.size,
991
- model_size: await this.getModelSize(this.incidentClassifier.model)
1021
+ vocabulary_size: this.incidentClassifier.tokenizer.has('_vocabulary_size')
1022
+ ? this.incidentClassifier.tokenizer.get('_vocabulary_size')
1023
+ : this.incidentClassifier.tokenizer.size,
1024
+ model_size: await this.getModelSize(this.incidentClassifier.model),
1025
+ memory_efficient: this.incidentClassifier.tokenizer.has('_vocabulary_size')
992
1026
  } : { status: 'not_trained' };
993
1027
  }
994
1028
  if (model === 'all' || model === 'change_risk') {
@@ -1311,6 +1345,232 @@ class ServiceNowMachineLearningMCP {
1311
1345
  (new Date(inc.resolved_at).getTime() - new Date(inc.sys_created_on).getTime()) / 1000 : undefined
1312
1346
  };
1313
1347
  }
1348
+ /**
1349
+ * Train model using streaming to handle large datasets efficiently
1350
+ */
1351
+ async trainWithStreaming(args) {
1352
+ const { sample_size, batch_size, epochs, validation_split, query, intelligent_selection, focus_categories, max_vocabulary_size } = args;
1353
+ this.logger.info(`Starting streaming training with batch size ${batch_size}`);
1354
+ // Create feature hasher for vocabulary management
1355
+ const featureHasher = this.createFeatureHasher(max_vocabulary_size);
1356
+ // Initialize model with proper architecture
1357
+ const model = this.createOptimizedModel(max_vocabulary_size);
1358
+ // Process data in batches
1359
+ const totalBatches = Math.ceil(sample_size / batch_size);
1360
+ let processedSamples = 0;
1361
+ let allCategories = new Set();
1362
+ for (let batchNum = 0; batchNum < totalBatches; batchNum++) {
1363
+ const offset = batchNum * batch_size;
1364
+ const currentBatchSize = Math.min(batch_size, sample_size - offset);
1365
+ this.logger.info(`Processing batch ${batchNum + 1}/${totalBatches} (${currentBatchSize} samples)`);
1366
+ // Fetch batch of incidents
1367
+ const batchIncidents = await this.fetchIncidentBatch(currentBatchSize, offset, {
1368
+ query,
1369
+ intelligent_selection,
1370
+ focus_categories
1371
+ });
1372
+ if (batchIncidents.length === 0)
1373
+ break;
1374
+ // Extract categories
1375
+ batchIncidents.forEach(inc => allCategories.add(inc.category));
1376
+ // Process batch with feature hashing
1377
+ const { features, labels } = this.processBatchWithHashing(batchIncidents, Array.from(allCategories), featureHasher);
1378
+ // Train on batch
1379
+ await model.fit(features, labels, {
1380
+ epochs: Math.ceil(epochs / totalBatches), // Distribute epochs across batches
1381
+ batchSize: 32,
1382
+ verbose: 0,
1383
+ callbacks: {
1384
+ onBatchEnd: async (batch, logs) => {
1385
+ if (batch % 10 === 0) {
1386
+ this.logger.info(`Batch ${batch}: loss=${logs?.loss?.toFixed(4)}`);
1387
+ }
1388
+ }
1389
+ }
1390
+ });
1391
+ // Clean up tensors to free memory
1392
+ features.dispose();
1393
+ labels.dispose();
1394
+ processedSamples += batchIncidents.length;
1395
+ // Force garbage collection hint
1396
+ if (global.gc) {
1397
+ global.gc();
1398
+ }
1399
+ }
1400
+ this.logger.info(`Streaming training completed. Processed ${processedSamples} samples in ${totalBatches} batches`);
1401
+ // Save model
1402
+ const modelId = `incident_classifier_${Date.now()}`;
1403
+ const modelInfo = {
1404
+ id: modelId,
1405
+ categories: Array.from(allCategories),
1406
+ vocabulary_size: max_vocabulary_size,
1407
+ training_samples: processedSamples,
1408
+ batch_size: batch_size,
1409
+ created_at: new Date().toISOString()
1410
+ };
1411
+ return {
1412
+ content: [{
1413
+ type: 'text',
1414
+ text: JSON.stringify({
1415
+ success: true,
1416
+ model_id: modelId,
1417
+ model_info: modelInfo,
1418
+ training_stats: {
1419
+ total_samples: processedSamples,
1420
+ batches_processed: totalBatches,
1421
+ memory_efficient: true
1422
+ }
1423
+ }, null, 2)
1424
+ }]
1425
+ };
1426
+ }
1427
+ /**
1428
+ * Fetch a batch of incidents with offset for streaming
1429
+ */
1430
+ async fetchIncidentBatch(limit, offset, options) {
1431
+ const { query, intelligent_selection, focus_categories } = options;
1432
+ let finalQuery = query;
1433
+ if (intelligent_selection && !query) {
1434
+ // Build intelligent query (same as before)
1435
+ const queries = [];
1436
+ queries.push('sys_created_onONLast 6 months');
1437
+ queries.push('(priority=1^ORpriority=2^ORpriority=3^ORpriority=4)');
1438
+ queries.push('(active=true^ORactive=false)');
1439
+ if (focus_categories.length > 0) {
1440
+ const categoryQuery = focus_categories.map((cat) => `category=${cat}`).join('^OR');
1441
+ queries.push(`(${categoryQuery})`);
1442
+ }
1443
+ else {
1444
+ queries.push('categoryISNOTEMPTY');
1445
+ }
1446
+ finalQuery = queries.join('^');
1447
+ }
1448
+ // Add offset for pagination
1449
+ if (finalQuery && !finalQuery.includes('ORDERBY')) {
1450
+ finalQuery += '^ORDERBYDESCsys_created_on';
1451
+ }
1452
+ // ServiceNow API supports offset through sysparm_offset
1453
+ const response = await this.client.searchRecordsWithOffset('incident', finalQuery, limit, offset);
1454
+ if (!response.success || !response.data?.result) {
1455
+ return [];
1456
+ }
1457
+ return response.data.result.map((inc) => ({
1458
+ short_description: inc.short_description || '',
1459
+ description: inc.description || '',
1460
+ category: inc.category || 'uncategorized',
1461
+ subcategory: inc.subcategory || '',
1462
+ priority: parseInt(inc.priority) || 3,
1463
+ impact: parseInt(inc.impact) || 2,
1464
+ urgency: parseInt(inc.urgency) || 2,
1465
+ resolved: inc.resolved === 'true'
1466
+ }));
1467
+ }
1468
+ /**
1469
+ * Create feature hasher for memory-efficient vocabulary management
1470
+ */
1471
+ createFeatureHasher(maxFeatures) {
1472
+ return (text) => {
1473
+ const words = text.toLowerCase().split(/\s+/);
1474
+ const features = new Array(100).fill(0); // Fixed sequence length
1475
+ words.slice(0, 100).forEach((word, idx) => {
1476
+ // Simple hash function
1477
+ let hash = 0;
1478
+ for (let i = 0; i < word.length; i++) {
1479
+ hash = ((hash << 5) - hash) + word.charCodeAt(i);
1480
+ hash = hash & hash; // Convert to 32-bit integer
1481
+ }
1482
+ // Map to vocabulary size
1483
+ features[idx] = Math.abs(hash) % maxFeatures;
1484
+ });
1485
+ return features;
1486
+ };
1487
+ }
1488
+ /**
1489
+ * Process batch with feature hashing
1490
+ */
1491
+ processBatchWithHashing(incidents, categories, hasher) {
1492
+ const sequences = [];
1493
+ const labels = [];
1494
+ for (const incident of incidents) {
1495
+ const text = `${incident.short_description} ${incident.description}`;
1496
+ const sequence = hasher(text);
1497
+ sequences.push(sequence);
1498
+ // One-hot encode category
1499
+ const categoryIndex = categories.indexOf(incident.category);
1500
+ const label = new Array(categories.length).fill(0);
1501
+ if (categoryIndex >= 0) {
1502
+ label[categoryIndex] = 1;
1503
+ }
1504
+ labels.push(label);
1505
+ }
1506
+ return {
1507
+ features: tf.tensor2d(sequences),
1508
+ labels: tf.tensor2d(labels)
1509
+ };
1510
+ }
1511
+ /**
1512
+ * Create optimized model for memory efficiency
1513
+ */
1514
+ createOptimizedModel(vocabularySize) {
1515
+ return tf.sequential({
1516
+ layers: [
1517
+ // Use embedding with smaller dimensions
1518
+ tf.layers.embedding({
1519
+ inputDim: vocabularySize,
1520
+ outputDim: 64, // Reduced from 128
1521
+ inputLength: 100
1522
+ }),
1523
+ // Smaller LSTM
1524
+ tf.layers.lstm({
1525
+ units: 32, // Reduced from 64
1526
+ returnSequences: false,
1527
+ dropout: 0.2,
1528
+ recurrentDropout: 0.2
1529
+ }),
1530
+ // Smaller dense layer
1531
+ tf.layers.dense({
1532
+ units: 16, // Reduced from 32
1533
+ activation: 'relu'
1534
+ }),
1535
+ tf.layers.dropout({ rate: 0.3 }),
1536
+ // Output layer (dynamic based on categories)
1537
+ tf.layers.dense({
1538
+ units: 10, // Will be adjusted based on actual categories
1539
+ activation: 'softmax'
1540
+ })
1541
+ ]
1542
+ });
1543
+ }
1544
+ /**
1545
+ * Optimized data preparation with feature hashing
1546
+ */
1547
+ async prepareIncidentDataOptimized(incidents, maxVocabularySize) {
1548
+ const hasher = this.createFeatureHasher(maxVocabularySize);
1549
+ const categories = [...new Set(incidents.map(i => i.category))];
1550
+ const sequences = [];
1551
+ const labels = [];
1552
+ for (const incident of incidents) {
1553
+ const text = `${incident.short_description} ${incident.description}`;
1554
+ const sequence = hasher(text);
1555
+ sequences.push(sequence);
1556
+ // One-hot encode category
1557
+ const categoryIndex = categories.indexOf(incident.category);
1558
+ const label = new Array(categories.length).fill(0);
1559
+ if (categoryIndex >= 0) {
1560
+ label[categoryIndex] = 1;
1561
+ }
1562
+ labels.push(label);
1563
+ }
1564
+ // Create a minimal Map for compatibility
1565
+ const tokenizerMap = new Map();
1566
+ tokenizerMap.set('_vocabulary_size', maxVocabularySize);
1567
+ return {
1568
+ features: tf.tensor2d(sequences),
1569
+ labels: tf.tensor2d(labels),
1570
+ tokenizer: tokenizerMap,
1571
+ categories
1572
+ };
1573
+ }
1314
1574
  async detectAnomalies(args) {
1315
1575
  // Implement anomaly detection
1316
1576
  return {
@@ -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
@@ -7,6 +7,7 @@ export declare const VERSION_INFO: {
7
7
  name: string;
8
8
  description: string;
9
9
  features: {
10
+ '2.7.1': string[];
10
11
  '2.7.0': string[];
11
12
  '2.6.9': string[];
12
13
  '2.6.8': string[];
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.0",
3
+ "version": "2.7.1",
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",