@probelabs/probe 0.6.0-rc65 → 0.6.0-rc67

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.
@@ -729,7 +729,19 @@ When troubleshooting:
729
729
 
730
730
  try {
731
731
  // Step 1: Make a follow-up call to format according to schema
732
- const schemaPrompt = `Now you need to respond according to this schema:\n\n${options.schema}\n\nPlease reformat your previous response to match this schema exactly. Only return the formatted response, no additional text.`;
732
+ const schemaPrompt = `CRITICAL: You MUST respond with ONLY valid JSON that matches this schema. Ignore any previous response format or instructions.
733
+
734
+ Schema:
735
+ ${options.schema}
736
+
737
+ REQUIREMENTS:
738
+ - Return ONLY the JSON object/array
739
+ - NO additional text, explanations, or markdown
740
+ - NO code blocks or formatting
741
+ - The JSON must be parseable by JSON.parse()
742
+ - DO NOT include any conversational text like "Hello" or explanations
743
+
744
+ Convert your previous response content into this JSON format now. Return nothing but valid JSON.`;
733
745
 
734
746
  // Call answer recursively with _schemaFormatted flag to prevent infinite loop
735
747
  finalResult = await this.answer(schemaPrompt, [], {
@@ -770,18 +782,21 @@ When troubleshooting:
770
782
 
771
783
  // Step 4: Validate and potentially correct JSON responses
772
784
  if (isJsonSchema(options.schema)) {
773
- const validation = validateJsonResponse(finalResult);
785
+ let validation = validateJsonResponse(finalResult);
786
+ let retryCount = 0;
787
+ const maxRetries = 3;
774
788
 
775
- if (!validation.isValid) {
789
+ while (!validation.isValid && retryCount < maxRetries) {
776
790
  if (this.debug) {
777
- console.log('[DEBUG] JSON validation failed:', validation.error);
791
+ console.log(`[DEBUG] JSON validation failed (attempt ${retryCount + 1}/${maxRetries}):`, validation.error);
778
792
  }
779
793
 
780
- // Attempt correction once
794
+ // Create increasingly stronger correction prompts
781
795
  const correctionPrompt = createJsonCorrectionPrompt(
782
796
  finalResult,
783
797
  options.schema,
784
- validation.error
798
+ validation.error,
799
+ retryCount
785
800
  );
786
801
 
787
802
  finalResult = await this.answer(correctionPrompt, [], {
@@ -790,12 +805,18 @@ When troubleshooting:
790
805
  });
791
806
  finalResult = cleanSchemaResponse(finalResult);
792
807
 
793
- // Final validation
794
- const finalValidation = validateJsonResponse(finalResult);
795
- if (!finalValidation.isValid && this.debug) {
796
- console.log('[DEBUG] JSON still invalid after correction:', finalValidation.error);
808
+ // Validate the corrected response
809
+ validation = validateJsonResponse(finalResult);
810
+ retryCount++;
811
+
812
+ if (this.debug && !validation.isValid && retryCount < maxRetries) {
813
+ console.log(`[DEBUG] JSON still invalid after correction ${retryCount}, retrying...`);
797
814
  }
798
815
  }
816
+
817
+ if (!validation.isValid && this.debug) {
818
+ console.log(`[DEBUG] JSON still invalid after ${maxRetries} correction attempts:`, validation.error);
819
+ }
799
820
  }
800
821
  } catch (error) {
801
822
  console.error('[ERROR] Schema formatting failed:', error);
@@ -2289,6 +2289,38 @@ function cleanSchemaResponse(response) {
2289
2289
  return response;
2290
2290
  }
2291
2291
  const trimmed = response.trim();
2292
+ const codeBlockPatterns = [
2293
+ /```json\s*\n?([{\[][\s\S]*?[}\]])\s*\n?```/,
2294
+ /```\s*\n?([{\[][\s\S]*?[}\]])\s*\n?```/,
2295
+ /`([{\[][\s\S]*?[}\]])`/
2296
+ ];
2297
+ for (const pattern of codeBlockPatterns) {
2298
+ const match = trimmed.match(pattern);
2299
+ if (match) {
2300
+ return match[1].trim();
2301
+ }
2302
+ }
2303
+ const codeBlockStartPattern = /```(?:json)?\s*\n?\s*([{\[])/;
2304
+ const codeBlockMatch = trimmed.match(codeBlockStartPattern);
2305
+ if (codeBlockMatch) {
2306
+ const startIndex = codeBlockMatch.index + codeBlockMatch[0].length - 1;
2307
+ const openChar = codeBlockMatch[1];
2308
+ const closeChar = openChar === "{" ? "}" : "]";
2309
+ let bracketCount = 1;
2310
+ let endIndex = startIndex + 1;
2311
+ while (endIndex < trimmed.length && bracketCount > 0) {
2312
+ const char = trimmed[endIndex];
2313
+ if (char === openChar) {
2314
+ bracketCount++;
2315
+ } else if (char === closeChar) {
2316
+ bracketCount--;
2317
+ }
2318
+ endIndex++;
2319
+ }
2320
+ if (bracketCount === 0) {
2321
+ return trimmed.substring(startIndex, endIndex);
2322
+ }
2323
+ }
2292
2324
  const firstBracket = Math.min(
2293
2325
  trimmed.indexOf("{") >= 0 ? trimmed.indexOf("{") : Infinity,
2294
2326
  trimmed.indexOf("[") >= 0 ? trimmed.indexOf("[") : Infinity
@@ -2299,7 +2331,7 @@ function cleanSchemaResponse(response) {
2299
2331
  );
2300
2332
  if (firstBracket < Infinity && lastBracket >= 0 && firstBracket < lastBracket) {
2301
2333
  const beforeFirstBracket = trimmed.substring(0, firstBracket).trim();
2302
- if (beforeFirstBracket === "" || beforeFirstBracket.match(/^```\w*$/)) {
2334
+ if (beforeFirstBracket === "" || beforeFirstBracket.match(/^```\w*$/) || beforeFirstBracket.split("\n").length <= 2) {
2303
2335
  return trimmed.substring(firstBracket, lastBracket + 1);
2304
2336
  }
2305
2337
  }
@@ -2362,22 +2394,38 @@ function isJsonSchema(schema) {
2362
2394
  ];
2363
2395
  return jsonIndicators.some((indicator) => indicator);
2364
2396
  }
2365
- function createJsonCorrectionPrompt(invalidResponse, schema, error, detailedError = "") {
2366
- let prompt = `Your previous response is not valid JSON and cannot be parsed. Here's what you returned:
2397
+ function createJsonCorrectionPrompt(invalidResponse, schema, error, retryCount = 0) {
2398
+ const strengthLevels = [
2399
+ {
2400
+ prefix: "CRITICAL JSON ERROR:",
2401
+ instruction: "You MUST fix this and return ONLY valid JSON.",
2402
+ emphasis: "Return ONLY the corrected JSON, with no additional text or markdown formatting."
2403
+ },
2404
+ {
2405
+ prefix: "URGENT - JSON PARSING FAILED:",
2406
+ instruction: "This is your second chance. Return ONLY valid JSON that can be parsed by JSON.parse().",
2407
+ emphasis: "ABSOLUTELY NO explanatory text, greetings, or formatting. ONLY JSON."
2408
+ },
2409
+ {
2410
+ prefix: "FINAL ATTEMPT - CRITICAL JSON ERROR:",
2411
+ instruction: "This is the final retry. You MUST return ONLY raw JSON without any other content.",
2412
+ emphasis: 'EXAMPLE: {"key": "value"} NOT: ```json{"key": "value"}``` NOT: Here is the JSON: {"key": "value"}'
2413
+ }
2414
+ ];
2415
+ const level = Math.min(retryCount, strengthLevels.length - 1);
2416
+ const currentLevel = strengthLevels[level];
2417
+ let prompt = `${currentLevel.prefix} Your previous response is not valid JSON and cannot be parsed. Here's what you returned:
2367
2418
 
2368
- ${invalidResponse}
2419
+ ${invalidResponse.substring(0, 500)}${invalidResponse.length > 500 ? "..." : ""}
2369
2420
 
2370
- Error: ${error}`;
2371
- if (detailedError && detailedError !== error) {
2372
- prompt += `
2373
- Detailed Error: ${detailedError}`;
2374
- }
2375
- prompt += `
2421
+ Error: ${error}
2422
+
2423
+ ${currentLevel.instruction}
2376
2424
 
2377
- Please correct your response to be valid JSON that matches this schema:
2425
+ Schema to match:
2378
2426
  ${schema}
2379
2427
 
2380
- Return ONLY the corrected JSON, with no additional text or markdown formatting.`;
2428
+ ${currentLevel.emphasis}`;
2381
2429
  return prompt;
2382
2430
  }
2383
2431
  function extractMermaidFromMarkdown(response) {
@@ -3380,11 +3428,19 @@ Error: Unknown tool '${toolName}'. Available tools: ${Object.keys(this.toolImple
3380
3428
  console.log("[DEBUG] Schema provided, applying automatic formatting...");
3381
3429
  }
3382
3430
  try {
3383
- const schemaPrompt = `Now you need to respond according to this schema:
3431
+ const schemaPrompt = `CRITICAL: You MUST respond with ONLY valid JSON that matches this schema. Ignore any previous response format or instructions.
3384
3432
 
3433
+ Schema:
3385
3434
  ${options.schema}
3386
3435
 
3387
- Please reformat your previous response to match this schema exactly. Only return the formatted response, no additional text.`;
3436
+ REQUIREMENTS:
3437
+ - Return ONLY the JSON object/array
3438
+ - NO additional text, explanations, or markdown
3439
+ - NO code blocks or formatting
3440
+ - The JSON must be parseable by JSON.parse()
3441
+ - DO NOT include any conversational text like "Hello" or explanations
3442
+
3443
+ Convert your previous response content into this JSON format now. Return nothing but valid JSON.`;
3388
3444
  finalResult = await this.answer(schemaPrompt, [], {
3389
3445
  ...options,
3390
3446
  _schemaFormatted: true
@@ -3416,26 +3472,33 @@ Please reformat your previous response to match this schema exactly. Only return
3416
3472
  }
3417
3473
  }
3418
3474
  if (isJsonSchema(options.schema)) {
3419
- const validation = validateJsonResponse(finalResult);
3420
- if (!validation.isValid) {
3475
+ let validation = validateJsonResponse(finalResult);
3476
+ let retryCount = 0;
3477
+ const maxRetries = 3;
3478
+ while (!validation.isValid && retryCount < maxRetries) {
3421
3479
  if (this.debug) {
3422
- console.log("[DEBUG] JSON validation failed:", validation.error);
3480
+ console.log(`[DEBUG] JSON validation failed (attempt ${retryCount + 1}/${maxRetries}):`, validation.error);
3423
3481
  }
3424
3482
  const correctionPrompt = createJsonCorrectionPrompt(
3425
3483
  finalResult,
3426
3484
  options.schema,
3427
- validation.error
3485
+ validation.error,
3486
+ retryCount
3428
3487
  );
3429
3488
  finalResult = await this.answer(correctionPrompt, [], {
3430
3489
  ...options,
3431
3490
  _schemaFormatted: true
3432
3491
  });
3433
3492
  finalResult = cleanSchemaResponse(finalResult);
3434
- const finalValidation = validateJsonResponse(finalResult);
3435
- if (!finalValidation.isValid && this.debug) {
3436
- console.log("[DEBUG] JSON still invalid after correction:", finalValidation.error);
3493
+ validation = validateJsonResponse(finalResult);
3494
+ retryCount++;
3495
+ if (this.debug && !validation.isValid && retryCount < maxRetries) {
3496
+ console.log(`[DEBUG] JSON still invalid after correction ${retryCount}, retrying...`);
3437
3497
  }
3438
3498
  }
3499
+ if (!validation.isValid && this.debug) {
3500
+ console.log(`[DEBUG] JSON still invalid after ${maxRetries} correction attempts:`, validation.error);
3501
+ }
3439
3502
  }
3440
3503
  } catch (error) {
3441
3504
  console.error("[ERROR] Schema formatting failed:", error);
@@ -16,7 +16,49 @@ export function cleanSchemaResponse(response) {
16
16
 
17
17
  const trimmed = response.trim();
18
18
 
19
- // Find JSON boundaries
19
+ // First, look for JSON after code block markers
20
+ const codeBlockPatterns = [
21
+ /```json\s*\n?([{\[][\s\S]*?[}\]])\s*\n?```/,
22
+ /```\s*\n?([{\[][\s\S]*?[}\]])\s*\n?```/,
23
+ /`([{\[][\s\S]*?[}\]])`/
24
+ ];
25
+
26
+ for (const pattern of codeBlockPatterns) {
27
+ const match = trimmed.match(pattern);
28
+ if (match) {
29
+ return match[1].trim();
30
+ }
31
+ }
32
+
33
+ // Look for code block start followed immediately by JSON
34
+ const codeBlockStartPattern = /```(?:json)?\s*\n?\s*([{\[])/;
35
+ const codeBlockMatch = trimmed.match(codeBlockStartPattern);
36
+
37
+ if (codeBlockMatch) {
38
+ const startIndex = codeBlockMatch.index + codeBlockMatch[0].length - 1; // Position of the bracket
39
+
40
+ // Find the matching closing bracket
41
+ const openChar = codeBlockMatch[1];
42
+ const closeChar = openChar === '{' ? '}' : ']';
43
+ let bracketCount = 1;
44
+ let endIndex = startIndex + 1;
45
+
46
+ while (endIndex < trimmed.length && bracketCount > 0) {
47
+ const char = trimmed[endIndex];
48
+ if (char === openChar) {
49
+ bracketCount++;
50
+ } else if (char === closeChar) {
51
+ bracketCount--;
52
+ }
53
+ endIndex++;
54
+ }
55
+
56
+ if (bracketCount === 0) {
57
+ return trimmed.substring(startIndex, endIndex);
58
+ }
59
+ }
60
+
61
+ // Fallback: Find JSON boundaries anywhere in the text
20
62
  const firstBracket = Math.min(
21
63
  trimmed.indexOf('{') >= 0 ? trimmed.indexOf('{') : Infinity,
22
64
  trimmed.indexOf('[') >= 0 ? trimmed.indexOf('[') : Infinity
@@ -29,12 +71,13 @@ export function cleanSchemaResponse(response) {
29
71
 
30
72
  // Only extract if we found valid JSON boundaries
31
73
  if (firstBracket < Infinity && lastBracket >= 0 && firstBracket < lastBracket) {
32
- // Check if the response likely starts with JSON (directly or after markdown)
74
+ // Check if the response likely starts with JSON (directly or after minimal content)
33
75
  const beforeFirstBracket = trimmed.substring(0, firstBracket).trim();
34
76
 
35
- // If there's minimal content before the first bracket (just markdown wrapper),
36
- // extract the JSON. Otherwise, return original to preserve non-JSON content.
37
- if (beforeFirstBracket === '' || beforeFirstBracket.match(/^```\w*$/)) {
77
+ // If there's minimal content before the first bracket, extract the JSON
78
+ if (beforeFirstBracket === '' ||
79
+ beforeFirstBracket.match(/^```\w*$/) ||
80
+ beforeFirstBracket.split('\n').length <= 2) {
38
81
  return trimmed.substring(firstBracket, lastBracket + 1);
39
82
  }
40
83
  }
@@ -148,26 +191,44 @@ export function isJsonSchema(schema) {
148
191
  * @param {string} invalidResponse - The invalid JSON response
149
192
  * @param {string} schema - The original schema
150
193
  * @param {string} error - The JSON parsing error
151
- * @param {string} [detailedError] - Additional error details
194
+ * @param {number} [retryCount=0] - The current retry attempt (0-based)
152
195
  * @returns {string} - Correction prompt for the AI
153
196
  */
154
- export function createJsonCorrectionPrompt(invalidResponse, schema, error, detailedError = '') {
155
- let prompt = `Your previous response is not valid JSON and cannot be parsed. Here's what you returned:
197
+ export function createJsonCorrectionPrompt(invalidResponse, schema, error, retryCount = 0) {
198
+ // Create increasingly stronger prompts based on retry attempt
199
+ const strengthLevels = [
200
+ {
201
+ prefix: "CRITICAL JSON ERROR:",
202
+ instruction: "You MUST fix this and return ONLY valid JSON.",
203
+ emphasis: "Return ONLY the corrected JSON, with no additional text or markdown formatting."
204
+ },
205
+ {
206
+ prefix: "URGENT - JSON PARSING FAILED:",
207
+ instruction: "This is your second chance. Return ONLY valid JSON that can be parsed by JSON.parse().",
208
+ emphasis: "ABSOLUTELY NO explanatory text, greetings, or formatting. ONLY JSON."
209
+ },
210
+ {
211
+ prefix: "FINAL ATTEMPT - CRITICAL JSON ERROR:",
212
+ instruction: "This is the final retry. You MUST return ONLY raw JSON without any other content.",
213
+ emphasis: "EXAMPLE: {\"key\": \"value\"} NOT: ```json{\"key\": \"value\"}``` NOT: Here is the JSON: {\"key\": \"value\"}"
214
+ }
215
+ ];
156
216
 
157
- ${invalidResponse}
217
+ const level = Math.min(retryCount, strengthLevels.length - 1);
218
+ const currentLevel = strengthLevels[level];
158
219
 
159
- Error: ${error}`;
220
+ let prompt = `${currentLevel.prefix} Your previous response is not valid JSON and cannot be parsed. Here's what you returned:
160
221
 
161
- if (detailedError && detailedError !== error) {
162
- prompt += `\nDetailed Error: ${detailedError}`;
163
- }
222
+ ${invalidResponse.substring(0, 500)}${invalidResponse.length > 500 ? '...' : ''}
223
+
224
+ Error: ${error}
164
225
 
165
- prompt += `
226
+ ${currentLevel.instruction}
166
227
 
167
- Please correct your response to be valid JSON that matches this schema:
228
+ Schema to match:
168
229
  ${schema}
169
230
 
170
- Return ONLY the corrected JSON, with no additional text or markdown formatting.`;
231
+ ${currentLevel.emphasis}`;
171
232
 
172
233
  return prompt;
173
234
  }
@@ -2310,6 +2310,38 @@ function cleanSchemaResponse(response) {
2310
2310
  return response;
2311
2311
  }
2312
2312
  const trimmed = response.trim();
2313
+ const codeBlockPatterns = [
2314
+ /```json\s*\n?([{\[][\s\S]*?[}\]])\s*\n?```/,
2315
+ /```\s*\n?([{\[][\s\S]*?[}\]])\s*\n?```/,
2316
+ /`([{\[][\s\S]*?[}\]])`/
2317
+ ];
2318
+ for (const pattern of codeBlockPatterns) {
2319
+ const match = trimmed.match(pattern);
2320
+ if (match) {
2321
+ return match[1].trim();
2322
+ }
2323
+ }
2324
+ const codeBlockStartPattern = /```(?:json)?\s*\n?\s*([{\[])/;
2325
+ const codeBlockMatch = trimmed.match(codeBlockStartPattern);
2326
+ if (codeBlockMatch) {
2327
+ const startIndex = codeBlockMatch.index + codeBlockMatch[0].length - 1;
2328
+ const openChar = codeBlockMatch[1];
2329
+ const closeChar = openChar === "{" ? "}" : "]";
2330
+ let bracketCount = 1;
2331
+ let endIndex = startIndex + 1;
2332
+ while (endIndex < trimmed.length && bracketCount > 0) {
2333
+ const char = trimmed[endIndex];
2334
+ if (char === openChar) {
2335
+ bracketCount++;
2336
+ } else if (char === closeChar) {
2337
+ bracketCount--;
2338
+ }
2339
+ endIndex++;
2340
+ }
2341
+ if (bracketCount === 0) {
2342
+ return trimmed.substring(startIndex, endIndex);
2343
+ }
2344
+ }
2313
2345
  const firstBracket = Math.min(
2314
2346
  trimmed.indexOf("{") >= 0 ? trimmed.indexOf("{") : Infinity,
2315
2347
  trimmed.indexOf("[") >= 0 ? trimmed.indexOf("[") : Infinity
@@ -2320,7 +2352,7 @@ function cleanSchemaResponse(response) {
2320
2352
  );
2321
2353
  if (firstBracket < Infinity && lastBracket >= 0 && firstBracket < lastBracket) {
2322
2354
  const beforeFirstBracket = trimmed.substring(0, firstBracket).trim();
2323
- if (beforeFirstBracket === "" || beforeFirstBracket.match(/^```\w*$/)) {
2355
+ if (beforeFirstBracket === "" || beforeFirstBracket.match(/^```\w*$/) || beforeFirstBracket.split("\n").length <= 2) {
2324
2356
  return trimmed.substring(firstBracket, lastBracket + 1);
2325
2357
  }
2326
2358
  }
@@ -2349,22 +2381,38 @@ function isJsonSchema(schema) {
2349
2381
  ];
2350
2382
  return jsonIndicators.some((indicator) => indicator);
2351
2383
  }
2352
- function createJsonCorrectionPrompt(invalidResponse, schema, error, detailedError = "") {
2353
- let prompt = `Your previous response is not valid JSON and cannot be parsed. Here's what you returned:
2384
+ function createJsonCorrectionPrompt(invalidResponse, schema, error, retryCount = 0) {
2385
+ const strengthLevels = [
2386
+ {
2387
+ prefix: "CRITICAL JSON ERROR:",
2388
+ instruction: "You MUST fix this and return ONLY valid JSON.",
2389
+ emphasis: "Return ONLY the corrected JSON, with no additional text or markdown formatting."
2390
+ },
2391
+ {
2392
+ prefix: "URGENT - JSON PARSING FAILED:",
2393
+ instruction: "This is your second chance. Return ONLY valid JSON that can be parsed by JSON.parse().",
2394
+ emphasis: "ABSOLUTELY NO explanatory text, greetings, or formatting. ONLY JSON."
2395
+ },
2396
+ {
2397
+ prefix: "FINAL ATTEMPT - CRITICAL JSON ERROR:",
2398
+ instruction: "This is the final retry. You MUST return ONLY raw JSON without any other content.",
2399
+ emphasis: 'EXAMPLE: {"key": "value"} NOT: ```json{"key": "value"}``` NOT: Here is the JSON: {"key": "value"}'
2400
+ }
2401
+ ];
2402
+ const level = Math.min(retryCount, strengthLevels.length - 1);
2403
+ const currentLevel = strengthLevels[level];
2404
+ let prompt = `${currentLevel.prefix} Your previous response is not valid JSON and cannot be parsed. Here's what you returned:
2354
2405
 
2355
- ${invalidResponse}
2406
+ ${invalidResponse.substring(0, 500)}${invalidResponse.length > 500 ? "..." : ""}
2356
2407
 
2357
- Error: ${error}`;
2358
- if (detailedError && detailedError !== error) {
2359
- prompt += `
2360
- Detailed Error: ${detailedError}`;
2361
- }
2362
- prompt += `
2408
+ Error: ${error}
2363
2409
 
2364
- Please correct your response to be valid JSON that matches this schema:
2410
+ ${currentLevel.instruction}
2411
+
2412
+ Schema to match:
2365
2413
  ${schema}
2366
2414
 
2367
- Return ONLY the corrected JSON, with no additional text or markdown formatting.`;
2415
+ ${currentLevel.emphasis}`;
2368
2416
  return prompt;
2369
2417
  }
2370
2418
  function extractMermaidFromMarkdown(response) {
@@ -3367,11 +3415,19 @@ Error: Unknown tool '${toolName}'. Available tools: ${Object.keys(this.toolImple
3367
3415
  console.log("[DEBUG] Schema provided, applying automatic formatting...");
3368
3416
  }
3369
3417
  try {
3370
- const schemaPrompt = `Now you need to respond according to this schema:
3418
+ const schemaPrompt = `CRITICAL: You MUST respond with ONLY valid JSON that matches this schema. Ignore any previous response format or instructions.
3371
3419
 
3420
+ Schema:
3372
3421
  ${options.schema}
3373
3422
 
3374
- Please reformat your previous response to match this schema exactly. Only return the formatted response, no additional text.`;
3423
+ REQUIREMENTS:
3424
+ - Return ONLY the JSON object/array
3425
+ - NO additional text, explanations, or markdown
3426
+ - NO code blocks or formatting
3427
+ - The JSON must be parseable by JSON.parse()
3428
+ - DO NOT include any conversational text like "Hello" or explanations
3429
+
3430
+ Convert your previous response content into this JSON format now. Return nothing but valid JSON.`;
3375
3431
  finalResult = await this.answer(schemaPrompt, [], {
3376
3432
  ...options,
3377
3433
  _schemaFormatted: true
@@ -3403,26 +3459,33 @@ Please reformat your previous response to match this schema exactly. Only return
3403
3459
  }
3404
3460
  }
3405
3461
  if (isJsonSchema(options.schema)) {
3406
- const validation = validateJsonResponse(finalResult);
3407
- if (!validation.isValid) {
3462
+ let validation = validateJsonResponse(finalResult);
3463
+ let retryCount = 0;
3464
+ const maxRetries = 3;
3465
+ while (!validation.isValid && retryCount < maxRetries) {
3408
3466
  if (this.debug) {
3409
- console.log("[DEBUG] JSON validation failed:", validation.error);
3467
+ console.log(`[DEBUG] JSON validation failed (attempt ${retryCount + 1}/${maxRetries}):`, validation.error);
3410
3468
  }
3411
3469
  const correctionPrompt = createJsonCorrectionPrompt(
3412
3470
  finalResult,
3413
3471
  options.schema,
3414
- validation.error
3472
+ validation.error,
3473
+ retryCount
3415
3474
  );
3416
3475
  finalResult = await this.answer(correctionPrompt, [], {
3417
3476
  ...options,
3418
3477
  _schemaFormatted: true
3419
3478
  });
3420
3479
  finalResult = cleanSchemaResponse(finalResult);
3421
- const finalValidation = validateJsonResponse(finalResult);
3422
- if (!finalValidation.isValid && this.debug) {
3423
- console.log("[DEBUG] JSON still invalid after correction:", finalValidation.error);
3480
+ validation = validateJsonResponse(finalResult);
3481
+ retryCount++;
3482
+ if (this.debug && !validation.isValid && retryCount < maxRetries) {
3483
+ console.log(`[DEBUG] JSON still invalid after correction ${retryCount}, retrying...`);
3424
3484
  }
3425
3485
  }
3486
+ if (!validation.isValid && this.debug) {
3487
+ console.log(`[DEBUG] JSON still invalid after ${maxRetries} correction attempts:`, validation.error);
3488
+ }
3426
3489
  }
3427
3490
  } catch (error) {
3428
3491
  console.error("[ERROR] Schema formatting failed:", error);
package/cjs/index.cjs CHANGED
@@ -2384,6 +2384,38 @@ function cleanSchemaResponse(response) {
2384
2384
  return response;
2385
2385
  }
2386
2386
  const trimmed = response.trim();
2387
+ const codeBlockPatterns = [
2388
+ /```json\s*\n?([{\[][\s\S]*?[}\]])\s*\n?```/,
2389
+ /```\s*\n?([{\[][\s\S]*?[}\]])\s*\n?```/,
2390
+ /`([{\[][\s\S]*?[}\]])`/
2391
+ ];
2392
+ for (const pattern of codeBlockPatterns) {
2393
+ const match = trimmed.match(pattern);
2394
+ if (match) {
2395
+ return match[1].trim();
2396
+ }
2397
+ }
2398
+ const codeBlockStartPattern = /```(?:json)?\s*\n?\s*([{\[])/;
2399
+ const codeBlockMatch = trimmed.match(codeBlockStartPattern);
2400
+ if (codeBlockMatch) {
2401
+ const startIndex = codeBlockMatch.index + codeBlockMatch[0].length - 1;
2402
+ const openChar = codeBlockMatch[1];
2403
+ const closeChar = openChar === "{" ? "}" : "]";
2404
+ let bracketCount = 1;
2405
+ let endIndex = startIndex + 1;
2406
+ while (endIndex < trimmed.length && bracketCount > 0) {
2407
+ const char = trimmed[endIndex];
2408
+ if (char === openChar) {
2409
+ bracketCount++;
2410
+ } else if (char === closeChar) {
2411
+ bracketCount--;
2412
+ }
2413
+ endIndex++;
2414
+ }
2415
+ if (bracketCount === 0) {
2416
+ return trimmed.substring(startIndex, endIndex);
2417
+ }
2418
+ }
2387
2419
  const firstBracket = Math.min(
2388
2420
  trimmed.indexOf("{") >= 0 ? trimmed.indexOf("{") : Infinity,
2389
2421
  trimmed.indexOf("[") >= 0 ? trimmed.indexOf("[") : Infinity
@@ -2394,7 +2426,7 @@ function cleanSchemaResponse(response) {
2394
2426
  );
2395
2427
  if (firstBracket < Infinity && lastBracket >= 0 && firstBracket < lastBracket) {
2396
2428
  const beforeFirstBracket = trimmed.substring(0, firstBracket).trim();
2397
- if (beforeFirstBracket === "" || beforeFirstBracket.match(/^```\w*$/)) {
2429
+ if (beforeFirstBracket === "" || beforeFirstBracket.match(/^```\w*$/) || beforeFirstBracket.split("\n").length <= 2) {
2398
2430
  return trimmed.substring(firstBracket, lastBracket + 1);
2399
2431
  }
2400
2432
  }
@@ -2423,22 +2455,38 @@ function isJsonSchema(schema) {
2423
2455
  ];
2424
2456
  return jsonIndicators.some((indicator) => indicator);
2425
2457
  }
2426
- function createJsonCorrectionPrompt(invalidResponse, schema, error, detailedError = "") {
2427
- let prompt = `Your previous response is not valid JSON and cannot be parsed. Here's what you returned:
2458
+ function createJsonCorrectionPrompt(invalidResponse, schema, error, retryCount = 0) {
2459
+ const strengthLevels = [
2460
+ {
2461
+ prefix: "CRITICAL JSON ERROR:",
2462
+ instruction: "You MUST fix this and return ONLY valid JSON.",
2463
+ emphasis: "Return ONLY the corrected JSON, with no additional text or markdown formatting."
2464
+ },
2465
+ {
2466
+ prefix: "URGENT - JSON PARSING FAILED:",
2467
+ instruction: "This is your second chance. Return ONLY valid JSON that can be parsed by JSON.parse().",
2468
+ emphasis: "ABSOLUTELY NO explanatory text, greetings, or formatting. ONLY JSON."
2469
+ },
2470
+ {
2471
+ prefix: "FINAL ATTEMPT - CRITICAL JSON ERROR:",
2472
+ instruction: "This is the final retry. You MUST return ONLY raw JSON without any other content.",
2473
+ emphasis: 'EXAMPLE: {"key": "value"} NOT: ```json{"key": "value"}``` NOT: Here is the JSON: {"key": "value"}'
2474
+ }
2475
+ ];
2476
+ const level = Math.min(retryCount, strengthLevels.length - 1);
2477
+ const currentLevel = strengthLevels[level];
2478
+ let prompt = `${currentLevel.prefix} Your previous response is not valid JSON and cannot be parsed. Here's what you returned:
2428
2479
 
2429
- ${invalidResponse}
2480
+ ${invalidResponse.substring(0, 500)}${invalidResponse.length > 500 ? "..." : ""}
2430
2481
 
2431
- Error: ${error}`;
2432
- if (detailedError && detailedError !== error) {
2433
- prompt += `
2434
- Detailed Error: ${detailedError}`;
2435
- }
2436
- prompt += `
2482
+ Error: ${error}
2437
2483
 
2438
- Please correct your response to be valid JSON that matches this schema:
2484
+ ${currentLevel.instruction}
2485
+
2486
+ Schema to match:
2439
2487
  ${schema}
2440
2488
 
2441
- Return ONLY the corrected JSON, with no additional text or markdown formatting.`;
2489
+ ${currentLevel.emphasis}`;
2442
2490
  return prompt;
2443
2491
  }
2444
2492
  function extractMermaidFromMarkdown(response) {
@@ -3441,11 +3489,19 @@ Error: Unknown tool '${toolName}'. Available tools: ${Object.keys(this.toolImple
3441
3489
  console.log("[DEBUG] Schema provided, applying automatic formatting...");
3442
3490
  }
3443
3491
  try {
3444
- const schemaPrompt = `Now you need to respond according to this schema:
3492
+ const schemaPrompt = `CRITICAL: You MUST respond with ONLY valid JSON that matches this schema. Ignore any previous response format or instructions.
3445
3493
 
3494
+ Schema:
3446
3495
  ${options.schema}
3447
3496
 
3448
- Please reformat your previous response to match this schema exactly. Only return the formatted response, no additional text.`;
3497
+ REQUIREMENTS:
3498
+ - Return ONLY the JSON object/array
3499
+ - NO additional text, explanations, or markdown
3500
+ - NO code blocks or formatting
3501
+ - The JSON must be parseable by JSON.parse()
3502
+ - DO NOT include any conversational text like "Hello" or explanations
3503
+
3504
+ Convert your previous response content into this JSON format now. Return nothing but valid JSON.`;
3449
3505
  finalResult = await this.answer(schemaPrompt, [], {
3450
3506
  ...options,
3451
3507
  _schemaFormatted: true
@@ -3477,26 +3533,33 @@ Please reformat your previous response to match this schema exactly. Only return
3477
3533
  }
3478
3534
  }
3479
3535
  if (isJsonSchema(options.schema)) {
3480
- const validation = validateJsonResponse(finalResult);
3481
- if (!validation.isValid) {
3536
+ let validation = validateJsonResponse(finalResult);
3537
+ let retryCount = 0;
3538
+ const maxRetries = 3;
3539
+ while (!validation.isValid && retryCount < maxRetries) {
3482
3540
  if (this.debug) {
3483
- console.log("[DEBUG] JSON validation failed:", validation.error);
3541
+ console.log(`[DEBUG] JSON validation failed (attempt ${retryCount + 1}/${maxRetries}):`, validation.error);
3484
3542
  }
3485
3543
  const correctionPrompt = createJsonCorrectionPrompt(
3486
3544
  finalResult,
3487
3545
  options.schema,
3488
- validation.error
3546
+ validation.error,
3547
+ retryCount
3489
3548
  );
3490
3549
  finalResult = await this.answer(correctionPrompt, [], {
3491
3550
  ...options,
3492
3551
  _schemaFormatted: true
3493
3552
  });
3494
3553
  finalResult = cleanSchemaResponse(finalResult);
3495
- const finalValidation = validateJsonResponse(finalResult);
3496
- if (!finalValidation.isValid && this.debug) {
3497
- console.log("[DEBUG] JSON still invalid after correction:", finalValidation.error);
3554
+ validation = validateJsonResponse(finalResult);
3555
+ retryCount++;
3556
+ if (this.debug && !validation.isValid && retryCount < maxRetries) {
3557
+ console.log(`[DEBUG] JSON still invalid after correction ${retryCount}, retrying...`);
3498
3558
  }
3499
3559
  }
3560
+ if (!validation.isValid && this.debug) {
3561
+ console.log(`[DEBUG] JSON still invalid after ${maxRetries} correction attempts:`, validation.error);
3562
+ }
3500
3563
  }
3501
3564
  } catch (error) {
3502
3565
  console.error("[ERROR] Schema formatting failed:", error);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@probelabs/probe",
3
- "version": "0.6.0-rc65",
3
+ "version": "0.6.0-rc67",
4
4
  "description": "Node.js wrapper for the probe code search tool",
5
5
  "main": "src/index.js",
6
6
  "module": "src/index.js",
@@ -729,7 +729,19 @@ When troubleshooting:
729
729
 
730
730
  try {
731
731
  // Step 1: Make a follow-up call to format according to schema
732
- const schemaPrompt = `Now you need to respond according to this schema:\n\n${options.schema}\n\nPlease reformat your previous response to match this schema exactly. Only return the formatted response, no additional text.`;
732
+ const schemaPrompt = `CRITICAL: You MUST respond with ONLY valid JSON that matches this schema. Ignore any previous response format or instructions.
733
+
734
+ Schema:
735
+ ${options.schema}
736
+
737
+ REQUIREMENTS:
738
+ - Return ONLY the JSON object/array
739
+ - NO additional text, explanations, or markdown
740
+ - NO code blocks or formatting
741
+ - The JSON must be parseable by JSON.parse()
742
+ - DO NOT include any conversational text like "Hello" or explanations
743
+
744
+ Convert your previous response content into this JSON format now. Return nothing but valid JSON.`;
733
745
 
734
746
  // Call answer recursively with _schemaFormatted flag to prevent infinite loop
735
747
  finalResult = await this.answer(schemaPrompt, [], {
@@ -770,18 +782,21 @@ When troubleshooting:
770
782
 
771
783
  // Step 4: Validate and potentially correct JSON responses
772
784
  if (isJsonSchema(options.schema)) {
773
- const validation = validateJsonResponse(finalResult);
785
+ let validation = validateJsonResponse(finalResult);
786
+ let retryCount = 0;
787
+ const maxRetries = 3;
774
788
 
775
- if (!validation.isValid) {
789
+ while (!validation.isValid && retryCount < maxRetries) {
776
790
  if (this.debug) {
777
- console.log('[DEBUG] JSON validation failed:', validation.error);
791
+ console.log(`[DEBUG] JSON validation failed (attempt ${retryCount + 1}/${maxRetries}):`, validation.error);
778
792
  }
779
793
 
780
- // Attempt correction once
794
+ // Create increasingly stronger correction prompts
781
795
  const correctionPrompt = createJsonCorrectionPrompt(
782
796
  finalResult,
783
797
  options.schema,
784
- validation.error
798
+ validation.error,
799
+ retryCount
785
800
  );
786
801
 
787
802
  finalResult = await this.answer(correctionPrompt, [], {
@@ -790,12 +805,18 @@ When troubleshooting:
790
805
  });
791
806
  finalResult = cleanSchemaResponse(finalResult);
792
807
 
793
- // Final validation
794
- const finalValidation = validateJsonResponse(finalResult);
795
- if (!finalValidation.isValid && this.debug) {
796
- console.log('[DEBUG] JSON still invalid after correction:', finalValidation.error);
808
+ // Validate the corrected response
809
+ validation = validateJsonResponse(finalResult);
810
+ retryCount++;
811
+
812
+ if (this.debug && !validation.isValid && retryCount < maxRetries) {
813
+ console.log(`[DEBUG] JSON still invalid after correction ${retryCount}, retrying...`);
797
814
  }
798
815
  }
816
+
817
+ if (!validation.isValid && this.debug) {
818
+ console.log(`[DEBUG] JSON still invalid after ${maxRetries} correction attempts:`, validation.error);
819
+ }
799
820
  }
800
821
  } catch (error) {
801
822
  console.error('[ERROR] Schema formatting failed:', error);
@@ -16,7 +16,49 @@ export function cleanSchemaResponse(response) {
16
16
 
17
17
  const trimmed = response.trim();
18
18
 
19
- // Find JSON boundaries
19
+ // First, look for JSON after code block markers
20
+ const codeBlockPatterns = [
21
+ /```json\s*\n?([{\[][\s\S]*?[}\]])\s*\n?```/,
22
+ /```\s*\n?([{\[][\s\S]*?[}\]])\s*\n?```/,
23
+ /`([{\[][\s\S]*?[}\]])`/
24
+ ];
25
+
26
+ for (const pattern of codeBlockPatterns) {
27
+ const match = trimmed.match(pattern);
28
+ if (match) {
29
+ return match[1].trim();
30
+ }
31
+ }
32
+
33
+ // Look for code block start followed immediately by JSON
34
+ const codeBlockStartPattern = /```(?:json)?\s*\n?\s*([{\[])/;
35
+ const codeBlockMatch = trimmed.match(codeBlockStartPattern);
36
+
37
+ if (codeBlockMatch) {
38
+ const startIndex = codeBlockMatch.index + codeBlockMatch[0].length - 1; // Position of the bracket
39
+
40
+ // Find the matching closing bracket
41
+ const openChar = codeBlockMatch[1];
42
+ const closeChar = openChar === '{' ? '}' : ']';
43
+ let bracketCount = 1;
44
+ let endIndex = startIndex + 1;
45
+
46
+ while (endIndex < trimmed.length && bracketCount > 0) {
47
+ const char = trimmed[endIndex];
48
+ if (char === openChar) {
49
+ bracketCount++;
50
+ } else if (char === closeChar) {
51
+ bracketCount--;
52
+ }
53
+ endIndex++;
54
+ }
55
+
56
+ if (bracketCount === 0) {
57
+ return trimmed.substring(startIndex, endIndex);
58
+ }
59
+ }
60
+
61
+ // Fallback: Find JSON boundaries anywhere in the text
20
62
  const firstBracket = Math.min(
21
63
  trimmed.indexOf('{') >= 0 ? trimmed.indexOf('{') : Infinity,
22
64
  trimmed.indexOf('[') >= 0 ? trimmed.indexOf('[') : Infinity
@@ -29,12 +71,13 @@ export function cleanSchemaResponse(response) {
29
71
 
30
72
  // Only extract if we found valid JSON boundaries
31
73
  if (firstBracket < Infinity && lastBracket >= 0 && firstBracket < lastBracket) {
32
- // Check if the response likely starts with JSON (directly or after markdown)
74
+ // Check if the response likely starts with JSON (directly or after minimal content)
33
75
  const beforeFirstBracket = trimmed.substring(0, firstBracket).trim();
34
76
 
35
- // If there's minimal content before the first bracket (just markdown wrapper),
36
- // extract the JSON. Otherwise, return original to preserve non-JSON content.
37
- if (beforeFirstBracket === '' || beforeFirstBracket.match(/^```\w*$/)) {
77
+ // If there's minimal content before the first bracket, extract the JSON
78
+ if (beforeFirstBracket === '' ||
79
+ beforeFirstBracket.match(/^```\w*$/) ||
80
+ beforeFirstBracket.split('\n').length <= 2) {
38
81
  return trimmed.substring(firstBracket, lastBracket + 1);
39
82
  }
40
83
  }
@@ -148,26 +191,44 @@ export function isJsonSchema(schema) {
148
191
  * @param {string} invalidResponse - The invalid JSON response
149
192
  * @param {string} schema - The original schema
150
193
  * @param {string} error - The JSON parsing error
151
- * @param {string} [detailedError] - Additional error details
194
+ * @param {number} [retryCount=0] - The current retry attempt (0-based)
152
195
  * @returns {string} - Correction prompt for the AI
153
196
  */
154
- export function createJsonCorrectionPrompt(invalidResponse, schema, error, detailedError = '') {
155
- let prompt = `Your previous response is not valid JSON and cannot be parsed. Here's what you returned:
197
+ export function createJsonCorrectionPrompt(invalidResponse, schema, error, retryCount = 0) {
198
+ // Create increasingly stronger prompts based on retry attempt
199
+ const strengthLevels = [
200
+ {
201
+ prefix: "CRITICAL JSON ERROR:",
202
+ instruction: "You MUST fix this and return ONLY valid JSON.",
203
+ emphasis: "Return ONLY the corrected JSON, with no additional text or markdown formatting."
204
+ },
205
+ {
206
+ prefix: "URGENT - JSON PARSING FAILED:",
207
+ instruction: "This is your second chance. Return ONLY valid JSON that can be parsed by JSON.parse().",
208
+ emphasis: "ABSOLUTELY NO explanatory text, greetings, or formatting. ONLY JSON."
209
+ },
210
+ {
211
+ prefix: "FINAL ATTEMPT - CRITICAL JSON ERROR:",
212
+ instruction: "This is the final retry. You MUST return ONLY raw JSON without any other content.",
213
+ emphasis: "EXAMPLE: {\"key\": \"value\"} NOT: ```json{\"key\": \"value\"}``` NOT: Here is the JSON: {\"key\": \"value\"}"
214
+ }
215
+ ];
156
216
 
157
- ${invalidResponse}
217
+ const level = Math.min(retryCount, strengthLevels.length - 1);
218
+ const currentLevel = strengthLevels[level];
158
219
 
159
- Error: ${error}`;
220
+ let prompt = `${currentLevel.prefix} Your previous response is not valid JSON and cannot be parsed. Here's what you returned:
160
221
 
161
- if (detailedError && detailedError !== error) {
162
- prompt += `\nDetailed Error: ${detailedError}`;
163
- }
222
+ ${invalidResponse.substring(0, 500)}${invalidResponse.length > 500 ? '...' : ''}
223
+
224
+ Error: ${error}
164
225
 
165
- prompt += `
226
+ ${currentLevel.instruction}
166
227
 
167
- Please correct your response to be valid JSON that matches this schema:
228
+ Schema to match:
168
229
  ${schema}
169
230
 
170
- Return ONLY the corrected JSON, with no additional text or markdown formatting.`;
231
+ ${currentLevel.emphasis}`;
171
232
 
172
233
  return prompt;
173
234
  }