snow-flow 3.3.3 → 3.3.5

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.
@@ -43,7 +43,7 @@ export declare class MLDataFetcher {
43
43
  */
44
44
  private fetchBatch;
45
45
  /**
46
- * Extract data from MCP tool result
46
+ * Extract data from MCP tool result - More robust parsing
47
47
  */
48
48
  private extractDataFromResult;
49
49
  /**
@@ -140,12 +140,13 @@ class MLDataFetcher {
140
140
  */
141
141
  async fetchBatch(table, query, limit, offset, fields, includeContent = true) {
142
142
  try {
143
- // Build query with offset
144
- const offsetQuery = query ? `${query}^ORDERBY${offset}` : `ORDERBY${offset}`;
143
+ // ServiceNow uses sysparm_offset for pagination, not ORDERBY syntax
144
+ // The query remains unchanged, offset is handled by the tool
145
145
  const result = await this.operationsMCP.handleTool('snow_query_table', {
146
146
  table,
147
- query: offsetQuery,
147
+ query: query || '',
148
148
  limit,
149
+ offset, // Pass offset directly - the tool should handle sysparm_offset
149
150
  fields,
150
151
  include_content: includeContent
151
152
  });
@@ -153,11 +154,27 @@ class MLDataFetcher {
153
154
  }
154
155
  catch (error) {
155
156
  logger.error(`Failed to fetch batch (offset: ${offset}, limit: ${limit}):`, error);
157
+ // If offset isn't supported, try without it for first batch
158
+ if (offset === 0) {
159
+ try {
160
+ const fallbackResult = await this.operationsMCP.handleTool('snow_query_table', {
161
+ table,
162
+ query: query || '',
163
+ limit,
164
+ fields,
165
+ include_content: includeContent
166
+ });
167
+ return this.extractDataFromResult(fallbackResult);
168
+ }
169
+ catch (fallbackError) {
170
+ logger.error('Fallback batch fetch also failed:', fallbackError);
171
+ }
172
+ }
156
173
  return [];
157
174
  }
158
175
  }
159
176
  /**
160
- * Extract data from MCP tool result
177
+ * Extract data from MCP tool result - More robust parsing
161
178
  */
162
179
  extractDataFromResult(result) {
163
180
  if (!result?.content?.[0]?.text) {
@@ -165,30 +182,71 @@ class MLDataFetcher {
165
182
  }
166
183
  try {
167
184
  const text = result.content[0].text;
168
- // Try to parse as JSON first
169
- if (text.includes('[') && text.includes(']')) {
170
- const jsonMatch = text.match(/\[[\s\S]*\]/);
171
- if (jsonMatch) {
172
- return JSON.parse(jsonMatch[0]);
185
+ // Method 1: Direct JSON parsing
186
+ try {
187
+ const parsed = JSON.parse(text);
188
+ if (Array.isArray(parsed)) {
189
+ return parsed;
190
+ }
191
+ if (parsed.result && Array.isArray(parsed.result)) {
192
+ return parsed.result;
193
+ }
194
+ if (parsed.data && Array.isArray(parsed.data)) {
195
+ return parsed.data;
196
+ }
197
+ }
198
+ catch (jsonError) {
199
+ // Not pure JSON, try other methods
200
+ }
201
+ // Method 2: Extract JSON array from text
202
+ const jsonArrayMatch = text.match(/\[\s*\{[\s\S]*\}\s*\]/);
203
+ if (jsonArrayMatch) {
204
+ try {
205
+ return JSON.parse(jsonArrayMatch[0]);
206
+ }
207
+ catch (e) {
208
+ // Continue to next method
209
+ }
210
+ }
211
+ // Method 3: Extract individual JSON objects
212
+ const jsonObjects = [];
213
+ const objectMatches = text.matchAll(/\{[^{}]*(?:\{[^{}]*\}[^{}]*)*\}/g);
214
+ for (const match of objectMatches) {
215
+ try {
216
+ const obj = JSON.parse(match[0]);
217
+ if (obj && typeof obj === 'object') {
218
+ jsonObjects.push(obj);
219
+ }
173
220
  }
221
+ catch (e) {
222
+ // Skip invalid JSON
223
+ }
224
+ }
225
+ if (jsonObjects.length > 0) {
226
+ return jsonObjects;
174
227
  }
175
- // Try to extract from formatted output
228
+ // Method 4: Parse formatted text output (fallback)
176
229
  const lines = text.split('\n');
177
230
  const data = [];
178
231
  let currentRecord = null;
179
232
  for (const line of lines) {
180
- if (line.includes('number:') || line.includes('sys_id:')) {
181
- if (currentRecord) {
233
+ // Detect new record
234
+ if (line.match(/^(Record \d+|\d+\.|#{1,3}|-)/) ||
235
+ (line.includes('sys_id:') && currentRecord)) {
236
+ if (currentRecord && Object.keys(currentRecord).length > 0) {
182
237
  data.push(currentRecord);
183
238
  }
184
239
  currentRecord = {};
185
240
  }
186
- if (currentRecord && line.includes(':')) {
187
- const [key, ...valueParts] = line.split(':');
188
- const cleanKey = key.trim().replace(/^[-\s]+/, '');
189
- const value = valueParts.join(':').trim();
190
- if (cleanKey && value) {
191
- currentRecord[cleanKey] = value;
241
+ // Extract key-value pairs
242
+ if (currentRecord !== null) {
243
+ const kvMatch = line.match(/^\s*[-*]?\s*([\w_]+)\s*[:=]\s*(.+)$/);
244
+ if (kvMatch) {
245
+ const key = kvMatch[1].trim();
246
+ const value = kvMatch[2].trim();
247
+ if (key && value && value !== 'null' && value !== 'undefined') {
248
+ currentRecord[key] = value;
249
+ }
192
250
  }
193
251
  }
194
252
  }
@@ -206,16 +264,19 @@ class MLDataFetcher {
206
264
  * Calculate optimal batch size based on data characteristics
207
265
  */
208
266
  calculateOptimalBatchSize(totalRecords, requestedBatchSize, numFields) {
209
- // Estimate tokens per record (rough approximation)
210
- const avgTokensPerField = 10; // Conservative estimate
211
- const tokensPerRecord = numFields * avgTokensPerField;
212
- const maxTokensPerBatch = 20000; // Leave buffer below 25000 limit
267
+ // More conservative token estimation for ServiceNow data
268
+ const avgTokensPerField = 15; // ServiceNow fields can be verbose
269
+ const systemFieldOverhead = 100; // System fields add overhead
270
+ const tokensPerRecord = (numFields * avgTokensPerField) + systemFieldOverhead;
271
+ const maxTokensPerBatch = 15000; // More conservative limit for safety
213
272
  // Calculate max records per batch based on token limit
214
273
  const maxRecordsPerBatch = Math.floor(maxTokensPerBatch / tokensPerRecord);
215
274
  // Use the smaller of requested batch size and calculated max
216
275
  const optimalSize = Math.min(requestedBatchSize, maxRecordsPerBatch);
217
- // Ensure at least 10 records per batch but not more than total
218
- return Math.max(10, Math.min(optimalSize, totalRecords));
276
+ // For ML training, we want reasonable batch sizes (20-100 typically)
277
+ const minBatchSize = Math.min(20, totalRecords);
278
+ const maxBatchSize = Math.min(100, totalRecords);
279
+ return Math.max(minBatchSize, Math.min(optimalSize, maxBatchSize));
219
280
  }
220
281
  /**
221
282
  * Select appropriate fields for ML training
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "snow-flow",
3
- "version": "3.3.3",
4
- "description": "Snow-Flow v3.3.3: ServiceNow development platform with 180+ MCP tools. NEW: snow_update tool for updating existing artifacts (vs snow_deploy for new). Enhanced MCP servers with real-time progress indicators, per-operation token tracking (tokens reset per call), and comprehensive operation logging. Supports ATF Testing, Knowledge Management, Service Catalog, Change Management, Virtual Agent, Performance Analytics, Flow Designer, Agent Workspace, Mobile, CMDB/Discovery, Event Management, HR Service Delivery, Customer Service Management, and DevOps integration. All tools use official ServiceNow REST APIs across 17 specialized MCP servers with full visibility into API operations.",
3
+ "version": "3.3.5",
4
+ "description": "Snow-Flow v3.3.5: CRITICAL ML FIX - Machine Learning MCP now fully functional with TensorFlow.js! Fixed pagination, ServiceNow API integration, data parsing, and training workflow. Real progress tracking, intelligent error messages, and automatic fallback when PA/PI not available. Train incident classifiers, predict change risks, forecast volumes, and detect anomalies - all working with real ServiceNow data. Enhanced MCP servers with real-time progress indicators and comprehensive operation logging. 180+ MCP tools across 17 specialized servers.",
5
5
  "main": "dist/index.js",
6
6
  "type": "commonjs",
7
7
  "bin": {