2026-01-20 01:12:03 +00:00
|
|
|
import * as interfaces from './smartagent.interfaces.js';
|
|
|
|
|
import { BaseToolWrapper } from './smartagent.tools.base.js';
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* JsonValidatorTool - Validates and formats JSON data
|
|
|
|
|
* Useful for agents to self-validate their JSON output before completing a task
|
|
|
|
|
*/
|
|
|
|
|
export class JsonValidatorTool extends BaseToolWrapper {
|
|
|
|
|
public name = 'json';
|
|
|
|
|
public description = 'Validate and format JSON data. Use this to verify your JSON output is valid before completing a task.';
|
|
|
|
|
|
|
|
|
|
public actions: interfaces.IToolAction[] = [
|
|
|
|
|
{
|
|
|
|
|
name: 'validate',
|
|
|
|
|
description: 'Validate that a string is valid JSON and optionally check required fields',
|
|
|
|
|
parameters: {
|
|
|
|
|
type: 'object',
|
|
|
|
|
properties: {
|
|
|
|
|
jsonString: {
|
|
|
|
|
type: 'string',
|
|
|
|
|
description: 'The JSON string to validate',
|
|
|
|
|
},
|
|
|
|
|
requiredFields: {
|
|
|
|
|
type: 'array',
|
|
|
|
|
items: { type: 'string' },
|
|
|
|
|
description: 'Optional list of field names that must be present at the root level',
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
required: ['jsonString'],
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
name: 'format',
|
|
|
|
|
description: 'Parse and pretty-print JSON string',
|
|
|
|
|
parameters: {
|
|
|
|
|
type: 'object',
|
|
|
|
|
properties: {
|
|
|
|
|
jsonString: {
|
|
|
|
|
type: 'string',
|
|
|
|
|
description: 'The JSON string to format',
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
required: ['jsonString'],
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
];
|
|
|
|
|
|
|
|
|
|
async initialize(): Promise<void> {
|
|
|
|
|
this.isInitialized = true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async cleanup(): Promise<void> {
|
|
|
|
|
this.isInitialized = false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
async execute(
|
|
|
|
|
action: string,
|
|
|
|
|
params: Record<string, unknown>
|
|
|
|
|
): Promise<interfaces.IToolExecutionResult> {
|
|
|
|
|
this.validateAction(action);
|
|
|
|
|
|
|
|
|
|
switch (action) {
|
|
|
|
|
case 'validate':
|
|
|
|
|
return this.validateJson(params);
|
|
|
|
|
case 'format':
|
|
|
|
|
return this.formatJson(params);
|
|
|
|
|
default:
|
|
|
|
|
return { success: false, error: `Unknown action: ${action}` };
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Validate JSON string and optionally check for required fields
|
|
|
|
|
*/
|
|
|
|
|
private validateJson(params: Record<string, unknown>): interfaces.IToolExecutionResult {
|
|
|
|
|
const jsonString = params.jsonString as string;
|
|
|
|
|
const requiredFields = params.requiredFields as string[] | undefined;
|
|
|
|
|
|
|
|
|
|
if (!jsonString || typeof jsonString !== 'string') {
|
|
|
|
|
return {
|
|
|
|
|
success: false,
|
|
|
|
|
error: 'jsonString parameter is required and must be a string',
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
const parsed = JSON.parse(jsonString);
|
|
|
|
|
|
|
|
|
|
// Check required fields if specified
|
|
|
|
|
if (requiredFields && Array.isArray(requiredFields)) {
|
|
|
|
|
const missingFields = requiredFields.filter((field) => {
|
|
|
|
|
if (typeof parsed !== 'object' || parsed === null) {
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
return !(field in parsed);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
if (missingFields.length > 0) {
|
|
|
|
|
return {
|
|
|
|
|
success: false,
|
|
|
|
|
error: `Missing required fields: ${missingFields.join(', ')}`,
|
|
|
|
|
result: {
|
|
|
|
|
valid: false,
|
|
|
|
|
missingFields,
|
|
|
|
|
presentFields: Object.keys(parsed || {}),
|
|
|
|
|
},
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return {
|
|
|
|
|
success: true,
|
|
|
|
|
result: {
|
|
|
|
|
valid: true,
|
|
|
|
|
parsed,
|
|
|
|
|
type: Array.isArray(parsed) ? 'array' : typeof parsed,
|
|
|
|
|
fieldCount: typeof parsed === 'object' && parsed !== null ? Object.keys(parsed).length : undefined,
|
|
|
|
|
},
|
|
|
|
|
summary: `JSON is valid (${Array.isArray(parsed) ? 'array' : typeof parsed})`,
|
|
|
|
|
};
|
|
|
|
|
} catch (error) {
|
|
|
|
|
const errorMessage = (error as Error).message;
|
|
|
|
|
|
|
|
|
|
// Extract position from error message if available
|
|
|
|
|
const posMatch = errorMessage.match(/position\s*(\d+)/i);
|
|
|
|
|
const position = posMatch ? parseInt(posMatch[1]) : undefined;
|
|
|
|
|
|
|
|
|
|
// Provide context around the error position
|
|
|
|
|
let context: string | undefined;
|
|
|
|
|
if (position !== undefined) {
|
|
|
|
|
const start = Math.max(0, position - 20);
|
|
|
|
|
const end = Math.min(jsonString.length, position + 20);
|
|
|
|
|
context = jsonString.substring(start, end);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return {
|
|
|
|
|
success: false,
|
|
|
|
|
error: `Invalid JSON: ${errorMessage}`,
|
|
|
|
|
result: {
|
|
|
|
|
valid: false,
|
|
|
|
|
errorPosition: position,
|
|
|
|
|
errorContext: context,
|
|
|
|
|
},
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Format/pretty-print JSON string
|
|
|
|
|
*/
|
|
|
|
|
private formatJson(params: Record<string, unknown>): interfaces.IToolExecutionResult {
|
|
|
|
|
const jsonString = params.jsonString as string;
|
|
|
|
|
|
|
|
|
|
if (!jsonString || typeof jsonString !== 'string') {
|
|
|
|
|
return {
|
|
|
|
|
success: false,
|
|
|
|
|
error: 'jsonString parameter is required and must be a string',
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
const parsed = JSON.parse(jsonString);
|
|
|
|
|
const formatted = JSON.stringify(parsed, null, 2);
|
|
|
|
|
|
|
|
|
|
return {
|
|
|
|
|
success: true,
|
|
|
|
|
result: formatted,
|
|
|
|
|
summary: `Formatted JSON (${formatted.length} chars)`,
|
|
|
|
|
};
|
|
|
|
|
} catch (error) {
|
|
|
|
|
return {
|
|
|
|
|
success: false,
|
|
|
|
|
error: `Cannot format invalid JSON: ${(error as Error).message}`,
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-01-20 01:30:03 +00:00
|
|
|
public getToolExplanation(): string {
|
|
|
|
|
return `## Tool: json
|
|
|
|
|
Validate and format JSON data. Use this to verify your JSON output is valid before completing a task.
|
|
|
|
|
|
|
|
|
|
### Actions:
|
|
|
|
|
|
|
|
|
|
**validate** - Validate that a string is valid JSON and optionally check required fields
|
|
|
|
|
Parameters:
|
|
|
|
|
- jsonString (required): The JSON string to validate
|
|
|
|
|
- requiredFields (optional): Array of field names that must be present
|
|
|
|
|
|
|
|
|
|
Example:
|
|
|
|
|
<tool_call>
|
|
|
|
|
<tool>json</tool>
|
|
|
|
|
<action>validate</action>
|
|
|
|
|
<params>{"jsonString": "{\\"invoice_number\\":\\"INV-001\\",\\"total\\":99.99}", "requiredFields": ["invoice_number", "total"]}</params>
|
|
|
|
|
</tool_call>
|
|
|
|
|
|
|
|
|
|
**format** - Parse and pretty-print JSON string
|
|
|
|
|
Parameters:
|
|
|
|
|
- jsonString (required): The JSON string to format
|
|
|
|
|
|
|
|
|
|
Example:
|
|
|
|
|
<tool_call>
|
|
|
|
|
<tool>json</tool>
|
|
|
|
|
<action>format</action>
|
|
|
|
|
<params>{"jsonString": "{\\"name\\":\\"test\\",\\"value\\":123}"}</params>
|
|
|
|
|
</tool_call>
|
|
|
|
|
`;
|
|
|
|
|
}
|
|
|
|
|
|
2026-01-20 01:12:03 +00:00
|
|
|
getCallSummary(action: string, params: Record<string, unknown>): string {
|
|
|
|
|
const jsonStr = (params.jsonString as string) || '';
|
|
|
|
|
const preview = jsonStr.length > 50 ? jsonStr.substring(0, 50) + '...' : jsonStr;
|
|
|
|
|
|
|
|
|
|
switch (action) {
|
|
|
|
|
case 'validate':
|
|
|
|
|
const fields = params.requiredFields as string[] | undefined;
|
|
|
|
|
const fieldInfo = fields ? ` (checking fields: ${fields.join(', ')})` : '';
|
|
|
|
|
return `Validate JSON: ${preview}${fieldInfo}`;
|
|
|
|
|
case 'format':
|
|
|
|
|
return `Format JSON: ${preview}`;
|
|
|
|
|
default:
|
|
|
|
|
return `JSON ${action}: ${preview}`;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|