@probelabs/probe 0.6.0-rc131 → 0.6.0-rc133
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/build/agent/ProbeAgent.d.ts +1 -1
- package/build/agent/ProbeAgent.js +188 -93
- package/build/agent/index.js +454 -90
- package/build/agent/schemaUtils.js +282 -33
- package/cjs/agent/ProbeAgent.cjs +780 -302
- package/cjs/index.cjs +780 -302
- package/package.json +1 -1
- package/src/agent/ProbeAgent.d.ts +1 -1
- package/src/agent/ProbeAgent.js +188 -93
- package/src/agent/schemaUtils.js +282 -33
package/src/agent/schemaUtils.js
CHANGED
|
@@ -106,28 +106,28 @@ export function cleanSchemaResponse(response) {
|
|
|
106
106
|
}
|
|
107
107
|
}
|
|
108
108
|
|
|
109
|
-
// Fallback:
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
)
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
109
|
+
// Fallback: Check if response is JSON after removing code block markers and whitespace
|
|
110
|
+
// First, remove common code block markers from top and bottom
|
|
111
|
+
let cleaned = trimmed;
|
|
112
|
+
|
|
113
|
+
// Remove opening code block markers (```json, ```, etc.)
|
|
114
|
+
cleaned = cleaned.replace(/^```(?:json)?\s*\n?/i, '');
|
|
115
|
+
|
|
116
|
+
// Remove closing code block markers
|
|
117
|
+
cleaned = cleaned.replace(/\n?```\s*$/, '');
|
|
118
|
+
|
|
119
|
+
// Trim whitespace and newlines
|
|
120
|
+
cleaned = cleaned.trim();
|
|
121
|
+
|
|
122
|
+
// Now check if first and last characters are valid JSON boundaries
|
|
123
|
+
const firstChar = cleaned[0];
|
|
124
|
+
const lastChar = cleaned[cleaned.length - 1];
|
|
125
|
+
|
|
126
|
+
const isJsonObject = firstChar === '{' && lastChar === '}';
|
|
127
|
+
const isJsonArray = firstChar === '[' && lastChar === ']';
|
|
128
|
+
|
|
129
|
+
if (isJsonObject || isJsonArray) {
|
|
130
|
+
return cleaned;
|
|
131
131
|
}
|
|
132
132
|
|
|
133
133
|
return response; // Return original if no extractable JSON found
|
|
@@ -142,29 +142,87 @@ export function cleanSchemaResponse(response) {
|
|
|
142
142
|
*/
|
|
143
143
|
export function validateJsonResponse(response, options = {}) {
|
|
144
144
|
const { debug = false } = options;
|
|
145
|
-
|
|
145
|
+
|
|
146
146
|
if (debug) {
|
|
147
147
|
console.log(`[DEBUG] JSON validation: Starting validation for response (${response.length} chars)`);
|
|
148
148
|
const preview = createMessagePreview(response);
|
|
149
149
|
console.log(`[DEBUG] JSON validation: Preview: ${preview}`);
|
|
150
150
|
}
|
|
151
|
-
|
|
151
|
+
|
|
152
152
|
try {
|
|
153
153
|
const parseStart = Date.now();
|
|
154
154
|
const parsed = JSON.parse(response);
|
|
155
155
|
const parseTime = Date.now() - parseStart;
|
|
156
|
-
|
|
156
|
+
|
|
157
157
|
if (debug) {
|
|
158
158
|
console.log(`[DEBUG] JSON validation: Successfully parsed in ${parseTime}ms`);
|
|
159
159
|
console.log(`[DEBUG] JSON validation: Object type: ${typeof parsed}, keys: ${Object.keys(parsed || {}).length}`);
|
|
160
160
|
}
|
|
161
|
-
|
|
161
|
+
|
|
162
162
|
return { isValid: true, parsed };
|
|
163
163
|
} catch (error) {
|
|
164
|
+
// Extract error position from error message if available
|
|
165
|
+
// Old format: "Unexpected token < in JSON at position 0"
|
|
166
|
+
// New format: "Unexpected token '<', \"...\" is not valid JSON"
|
|
167
|
+
const positionMatch = error.message.match(/position (\d+)/);
|
|
168
|
+
let errorPosition = positionMatch ? parseInt(positionMatch[1], 10) : null;
|
|
169
|
+
|
|
170
|
+
// If position not found in old format, try to extract from new format
|
|
171
|
+
if (errorPosition === null) {
|
|
172
|
+
// Try to find the problematic token in the new error format
|
|
173
|
+
const tokenMatch = error.message.match(/Unexpected token '(.)', /);
|
|
174
|
+
if (tokenMatch && tokenMatch[1]) {
|
|
175
|
+
const problematicToken = tokenMatch[1];
|
|
176
|
+
// Find first occurrence of this token in the response
|
|
177
|
+
errorPosition = response.indexOf(problematicToken);
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
// Create enhanced error message with context snippet
|
|
182
|
+
let enhancedError = error.message;
|
|
183
|
+
let errorContext = null;
|
|
184
|
+
|
|
185
|
+
if (errorPosition !== null && errorPosition >= 0 && response && response.length > 0) {
|
|
186
|
+
// Calculate context window (50 chars before and after)
|
|
187
|
+
const contextRadius = 50;
|
|
188
|
+
const startPos = Math.max(0, errorPosition - contextRadius);
|
|
189
|
+
const endPos = Math.min(response.length, errorPosition + contextRadius);
|
|
190
|
+
|
|
191
|
+
// Extract context snippet
|
|
192
|
+
const beforeError = response.substring(startPos, errorPosition);
|
|
193
|
+
const atError = response[errorPosition] || '';
|
|
194
|
+
const afterError = response.substring(errorPosition + 1, endPos);
|
|
195
|
+
|
|
196
|
+
// Build error context with visual pointer
|
|
197
|
+
const snippet = beforeError + atError + afterError;
|
|
198
|
+
const pointerOffset = beforeError.length;
|
|
199
|
+
const pointer = ' '.repeat(pointerOffset) + '^';
|
|
200
|
+
|
|
201
|
+
errorContext = {
|
|
202
|
+
position: errorPosition,
|
|
203
|
+
snippet: snippet,
|
|
204
|
+
pointer: pointer,
|
|
205
|
+
beforeError: beforeError,
|
|
206
|
+
atError: atError,
|
|
207
|
+
afterError: afterError
|
|
208
|
+
};
|
|
209
|
+
|
|
210
|
+
// Create human-readable error context for display
|
|
211
|
+
enhancedError = `${error.message}
|
|
212
|
+
|
|
213
|
+
Error location (position ${errorPosition}):
|
|
214
|
+
${snippet}
|
|
215
|
+
${pointer} here`;
|
|
216
|
+
}
|
|
217
|
+
|
|
164
218
|
if (debug) {
|
|
165
219
|
console.log(`[DEBUG] JSON validation: Parse failed with error: ${error.message}`);
|
|
166
|
-
console.log(`[DEBUG] JSON validation: Error at position: ${
|
|
167
|
-
|
|
220
|
+
console.log(`[DEBUG] JSON validation: Error at position: ${errorPosition !== null ? errorPosition : 'unknown'}`);
|
|
221
|
+
|
|
222
|
+
if (errorContext) {
|
|
223
|
+
console.log(`[DEBUG] JSON validation: Error context:\n${errorContext.snippet}\n${errorContext.pointer}`);
|
|
224
|
+
}
|
|
225
|
+
|
|
168
226
|
// Try to identify common JSON issues
|
|
169
227
|
if (error.message.includes('Unexpected token')) {
|
|
170
228
|
console.log(`[DEBUG] JSON validation: Likely syntax error - unexpected character`);
|
|
@@ -174,8 +232,13 @@ export function validateJsonResponse(response, options = {}) {
|
|
|
174
232
|
console.log(`[DEBUG] JSON validation: Likely unquoted property names`);
|
|
175
233
|
}
|
|
176
234
|
}
|
|
177
|
-
|
|
178
|
-
return {
|
|
235
|
+
|
|
236
|
+
return {
|
|
237
|
+
isValid: false,
|
|
238
|
+
error: error.message,
|
|
239
|
+
enhancedError: enhancedError,
|
|
240
|
+
errorContext: errorContext
|
|
241
|
+
};
|
|
179
242
|
}
|
|
180
243
|
}
|
|
181
244
|
|
|
@@ -357,11 +420,25 @@ export function isJsonSchemaDefinition(jsonString, options = {}) {
|
|
|
357
420
|
* Create a correction prompt for invalid JSON
|
|
358
421
|
* @param {string} invalidResponse - The invalid JSON response
|
|
359
422
|
* @param {string} schema - The original schema
|
|
360
|
-
* @param {string}
|
|
423
|
+
* @param {string|Object} errorOrValidation - The JSON parsing error string or validation result object
|
|
361
424
|
* @param {number} [retryCount=0] - The current retry attempt (0-based)
|
|
362
425
|
* @returns {string} - Correction prompt for the AI
|
|
363
426
|
*/
|
|
364
|
-
export function createJsonCorrectionPrompt(invalidResponse, schema,
|
|
427
|
+
export function createJsonCorrectionPrompt(invalidResponse, schema, errorOrValidation, retryCount = 0) {
|
|
428
|
+
// Extract error information from validation result or string
|
|
429
|
+
let errorMessage;
|
|
430
|
+
let enhancedError;
|
|
431
|
+
|
|
432
|
+
if (typeof errorOrValidation === 'object' && errorOrValidation !== null) {
|
|
433
|
+
// It's a validation result object
|
|
434
|
+
errorMessage = errorOrValidation.error;
|
|
435
|
+
enhancedError = errorOrValidation.enhancedError || errorMessage;
|
|
436
|
+
} else {
|
|
437
|
+
// It's a plain error string (backwards compatibility)
|
|
438
|
+
errorMessage = errorOrValidation;
|
|
439
|
+
enhancedError = errorMessage;
|
|
440
|
+
}
|
|
441
|
+
|
|
365
442
|
// Create increasingly stronger prompts based on retry attempt
|
|
366
443
|
const strengthLevels = [
|
|
367
444
|
{
|
|
@@ -388,7 +465,7 @@ export function createJsonCorrectionPrompt(invalidResponse, schema, error, retry
|
|
|
388
465
|
|
|
389
466
|
${invalidResponse.substring(0, 500)}${invalidResponse.length > 500 ? '...' : ''}
|
|
390
467
|
|
|
391
|
-
Error: ${
|
|
468
|
+
Error: ${enhancedError}
|
|
392
469
|
|
|
393
470
|
${currentLevel.instruction}
|
|
394
471
|
|
|
@@ -688,6 +765,178 @@ Ensure all Mermaid diagrams are properly formatted within \`\`\`mermaid code blo
|
|
|
688
765
|
return prompt;
|
|
689
766
|
}
|
|
690
767
|
|
|
768
|
+
/**
|
|
769
|
+
* Specialized JSON fixing agent
|
|
770
|
+
* Uses a separate ProbeAgent instance optimized for JSON syntax correction
|
|
771
|
+
*/
|
|
772
|
+
export class JsonFixingAgent {
|
|
773
|
+
constructor(options = {}) {
|
|
774
|
+
// Import ProbeAgent dynamically to avoid circular dependencies
|
|
775
|
+
this.ProbeAgent = null;
|
|
776
|
+
this.options = {
|
|
777
|
+
sessionId: options.sessionId || `json-fixer-${Date.now()}`,
|
|
778
|
+
path: options.path || process.cwd(),
|
|
779
|
+
provider: options.provider,
|
|
780
|
+
model: options.model,
|
|
781
|
+
debug: options.debug,
|
|
782
|
+
tracer: options.tracer,
|
|
783
|
+
// Set to false since we're only fixing JSON syntax, not implementing code
|
|
784
|
+
allowEdit: false
|
|
785
|
+
};
|
|
786
|
+
}
|
|
787
|
+
|
|
788
|
+
/**
|
|
789
|
+
* Get the specialized prompt for JSON fixing
|
|
790
|
+
*/
|
|
791
|
+
getJsonFixingPrompt() {
|
|
792
|
+
return `You are a world-class JSON syntax correction specialist. Your expertise lies in analyzing and fixing JSON syntax errors while preserving the original data structure and intent.
|
|
793
|
+
|
|
794
|
+
CORE RESPONSIBILITIES:
|
|
795
|
+
- Analyze JSON for syntax errors and structural issues
|
|
796
|
+
- Fix syntax errors while maintaining the original data's semantic meaning
|
|
797
|
+
- Ensure JSON follows proper RFC 8259 specification
|
|
798
|
+
- Handle all JSON structures: objects, arrays, primitives, nested structures
|
|
799
|
+
|
|
800
|
+
JSON SYNTAX RULES:
|
|
801
|
+
1. **Property names**: Must be enclosed in double quotes
|
|
802
|
+
2. **String values**: Must use double quotes (not single quotes)
|
|
803
|
+
3. **Numbers**: Can be integers or decimals, no quotes needed
|
|
804
|
+
4. **Booleans**: true or false (lowercase, no quotes)
|
|
805
|
+
5. **Null**: null (lowercase, no quotes)
|
|
806
|
+
6. **Arrays**: Comma-separated values in square brackets [...]
|
|
807
|
+
7. **Objects**: Comma-separated key-value pairs in curly braces {...}
|
|
808
|
+
8. **No trailing commas**: Last item in array/object must not have a trailing comma
|
|
809
|
+
9. **Escape sequences**: Special characters must be escaped (\\n, \\t, \\", \\\\, etc.)
|
|
810
|
+
|
|
811
|
+
COMMON ERRORS TO FIX:
|
|
812
|
+
1. **Unquoted property names**: {name: "value"} → {"name": "value"}
|
|
813
|
+
2. **Single quotes**: {'key': 'value'} → {"key": "value"}
|
|
814
|
+
3. **Trailing commas**: {"a": 1,} → {"a": 1}
|
|
815
|
+
4. **Unquoted strings**: {key: value} → {"key": "value"}
|
|
816
|
+
5. **Missing commas**: {"a": 1 "b": 2} → {"a": 1, "b": 2}
|
|
817
|
+
6. **Extra commas**: {"a": 1,, "b": 2} → {"a": 1, "b": 2}
|
|
818
|
+
7. **Unclosed brackets/braces**: {"key": "value" → {"key": "value"}
|
|
819
|
+
8. **Invalid escape sequences**: Fix or remove
|
|
820
|
+
9. **Comments**: Remove // or /* */ comments (not allowed in JSON)
|
|
821
|
+
10. **Undefined values**: Replace undefined with null
|
|
822
|
+
|
|
823
|
+
FIXING METHODOLOGY:
|
|
824
|
+
1. **Identify the error location** from the error message
|
|
825
|
+
2. **Analyze the context** around the error
|
|
826
|
+
3. **Apply the appropriate fix** based on JSON syntax rules
|
|
827
|
+
4. **Preserve data intent** - never change the meaning of the data
|
|
828
|
+
5. **Validate the result** - ensure it's parseable JSON
|
|
829
|
+
|
|
830
|
+
CRITICAL RULES:
|
|
831
|
+
- ALWAYS output only the corrected JSON
|
|
832
|
+
- NEVER add explanations, comments, or additional text
|
|
833
|
+
- NEVER wrap in markdown code blocks (no \`\`\`json)
|
|
834
|
+
- PRESERVE the original data structure and values
|
|
835
|
+
- FIX only syntax errors, don't modify the data itself
|
|
836
|
+
- ENSURE the output is valid, parseable JSON
|
|
837
|
+
|
|
838
|
+
When presented with broken JSON, analyze it thoroughly and provide the corrected version that maintains the original intent while fixing all syntax issues.`;
|
|
839
|
+
}
|
|
840
|
+
|
|
841
|
+
/**
|
|
842
|
+
* Initialize the ProbeAgent if not already done
|
|
843
|
+
*/
|
|
844
|
+
async initializeAgent() {
|
|
845
|
+
if (!this.ProbeAgent) {
|
|
846
|
+
// Dynamic import to avoid circular dependency
|
|
847
|
+
const { ProbeAgent } = await import('./ProbeAgent.js');
|
|
848
|
+
this.ProbeAgent = ProbeAgent;
|
|
849
|
+
}
|
|
850
|
+
|
|
851
|
+
if (!this.agent) {
|
|
852
|
+
this.agent = new this.ProbeAgent({
|
|
853
|
+
sessionId: this.options.sessionId,
|
|
854
|
+
customPrompt: this.getJsonFixingPrompt(),
|
|
855
|
+
path: this.options.path,
|
|
856
|
+
provider: this.options.provider,
|
|
857
|
+
model: this.options.model,
|
|
858
|
+
debug: this.options.debug,
|
|
859
|
+
tracer: this.options.tracer,
|
|
860
|
+
allowEdit: this.options.allowEdit,
|
|
861
|
+
maxIterations: 5, // Allow multiple iterations for JSON fixing
|
|
862
|
+
disableJsonValidation: true // CRITICAL: Disable JSON validation in nested agent to prevent infinite recursion
|
|
863
|
+
});
|
|
864
|
+
}
|
|
865
|
+
|
|
866
|
+
return this.agent;
|
|
867
|
+
}
|
|
868
|
+
|
|
869
|
+
/**
|
|
870
|
+
* Fix invalid JSON using the specialized agent
|
|
871
|
+
* @param {string} invalidJson - The broken JSON string
|
|
872
|
+
* @param {string} schema - The original schema for context
|
|
873
|
+
* @param {Object} validationResult - Validation result with error details
|
|
874
|
+
* @param {number} attemptNumber - Current attempt number (for logging)
|
|
875
|
+
* @returns {Promise<string>} - The corrected JSON
|
|
876
|
+
*/
|
|
877
|
+
async fixJson(invalidJson, schema, validationResult, attemptNumber = 1) {
|
|
878
|
+
await this.initializeAgent();
|
|
879
|
+
|
|
880
|
+
// Build error context from validation result
|
|
881
|
+
let errorContext = validationResult.error;
|
|
882
|
+
if (validationResult.enhancedError) {
|
|
883
|
+
errorContext = validationResult.enhancedError;
|
|
884
|
+
}
|
|
885
|
+
|
|
886
|
+
const prompt = `Fix the following invalid JSON.
|
|
887
|
+
|
|
888
|
+
Error: ${errorContext}
|
|
889
|
+
|
|
890
|
+
Invalid JSON:
|
|
891
|
+
${invalidJson}
|
|
892
|
+
|
|
893
|
+
Expected schema structure:
|
|
894
|
+
${schema}
|
|
895
|
+
|
|
896
|
+
Provide only the corrected JSON without any markdown formatting or explanations.`;
|
|
897
|
+
|
|
898
|
+
try {
|
|
899
|
+
if (this.options.debug) {
|
|
900
|
+
console.log(`[DEBUG] JSON fixing: Attempt ${attemptNumber} to fix JSON with separate agent`);
|
|
901
|
+
}
|
|
902
|
+
|
|
903
|
+
// Call the specialized JSON fixing agent
|
|
904
|
+
const result = await this.agent.answer(prompt, []);
|
|
905
|
+
|
|
906
|
+
// Clean the result (in case AI added markdown despite instructions)
|
|
907
|
+
const cleaned = cleanSchemaResponse(result);
|
|
908
|
+
|
|
909
|
+
if (this.options.debug) {
|
|
910
|
+
console.log(`[DEBUG] JSON fixing: Agent returned ${cleaned.length} chars`);
|
|
911
|
+
}
|
|
912
|
+
|
|
913
|
+
return cleaned;
|
|
914
|
+
} catch (error) {
|
|
915
|
+
if (this.options.debug) {
|
|
916
|
+
console.error(`[DEBUG] JSON fixing failed: ${error.message}`);
|
|
917
|
+
}
|
|
918
|
+
throw new Error(`Failed to fix JSON: ${error.message}`);
|
|
919
|
+
}
|
|
920
|
+
}
|
|
921
|
+
|
|
922
|
+
/**
|
|
923
|
+
* Get token usage information from the specialized agent
|
|
924
|
+
* @returns {Object} - Token usage statistics
|
|
925
|
+
*/
|
|
926
|
+
getTokenUsage() {
|
|
927
|
+
return this.agent ? this.agent.getTokenUsage() : null;
|
|
928
|
+
}
|
|
929
|
+
|
|
930
|
+
/**
|
|
931
|
+
* Cancel any ongoing operations
|
|
932
|
+
*/
|
|
933
|
+
cancel() {
|
|
934
|
+
if (this.agent) {
|
|
935
|
+
this.agent.cancel();
|
|
936
|
+
}
|
|
937
|
+
}
|
|
938
|
+
}
|
|
939
|
+
|
|
691
940
|
/**
|
|
692
941
|
* Use maid to attempt auto-fixing of mermaid diagrams
|
|
693
942
|
* @param {string} diagramContent - The diagram content to fix
|