feat(smartagent): add JsonValidatorTool and support passing base64-encoded images with task runs (vision-capable models); bump @push.rocks/smartai to ^0.12.0

This commit is contained in:
2026-01-20 01:12:03 +00:00
parent 05e4f03061
commit e7968a31b1
9 changed files with 241 additions and 30 deletions

View File

@@ -3,6 +3,6 @@
*/
export const commitinfo = {
name: '@push.rocks/smartagent',
version: '1.2.7',
version: '1.3.0',
description: 'an agentic framework built on top of @push.rocks/smartai'
}

View File

@@ -16,6 +16,7 @@ export { HttpTool } from './smartagent.tools.http.js';
export { ShellTool } from './smartagent.tools.shell.js';
export { BrowserTool } from './smartagent.tools.browser.js';
export { DenoTool, type TDenoPermission } from './smartagent.tools.deno.js';
export { JsonValidatorTool } from './smartagent.tools.json.js';
// Export all interfaces
export * from './smartagent.interfaces.js';

View File

@@ -67,8 +67,10 @@ export class DriverAgent {
/**
* Initialize a new conversation for a task
* @param task The task description
* @param images Optional base64-encoded images for vision tasks
*/
public async startTask(task: string): Promise<interfaces.IAgentMessage> {
public async startTask(task: string, images?: string[]): Promise<interfaces.IAgentMessage> {
// Reset message history
this.messageHistory = [];
@@ -106,6 +108,7 @@ export class DriverAgent {
systemMessage: fullSystemMessage,
userMessage: userMessage,
messageHistory: [],
images: images,
onToken: this.onToken,
});
} else {
@@ -114,14 +117,16 @@ export class DriverAgent {
systemMessage: fullSystemMessage,
userMessage: userMessage,
messageHistory: [],
images: images,
});
}
// Add assistant response to history
this.messageHistory.push({
// Add assistant response to history (store images if provided)
const historyMessage: plugins.smartai.ChatMessage = {
role: 'assistant',
content: response.message,
});
};
this.messageHistory.push(historyMessage);
return {
role: 'assistant',

View File

@@ -234,8 +234,10 @@ export class DualAgentOrchestrator {
/**
* Run a task through the dual-agent system
* @param task The task description
* @param options Optional task run options (e.g., images for vision tasks)
*/
public async run(task: string): Promise<interfaces.IDualAgentRunResult> {
public async run(task: string, options?: interfaces.ITaskRunOptions): Promise<interfaces.IDualAgentRunResult> {
if (!this.isRunning) {
throw new Error('Orchestrator not started. Call start() first.');
}
@@ -246,14 +248,17 @@ export class DualAgentOrchestrator {
let completed = false;
let finalResult: string | null = null;
// Extract images from options
const images = options?.images;
// Add initial task to history
this.conversationHistory.push({
role: 'user',
content: task,
});
// Start the driver with the task
let driverResponse = await this.driver.startTask(task);
// Start the driver with the task and optional images
let driverResponse = await this.driver.startTask(task, images);
this.conversationHistory.push(driverResponse);
// Emit task started event

View File

@@ -1,5 +1,17 @@
import * as plugins from './plugins.js';
// ================================
// Task Run Options
// ================================
/**
* Options for running a task with the DualAgentOrchestrator
*/
export interface ITaskRunOptions {
/** Base64-encoded images to include with the task (for vision-capable models) */
images?: string[];
}
// ================================
// Agent Configuration Interfaces
// ================================

193
ts/smartagent.tools.json.ts Normal file
View File

@@ -0,0 +1,193 @@
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}`,
};
}
}
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}`;
}
}
}