@hashgraphonline/conversational-agent
Version:
Hashgraph Online conversational AI agent implementing HCS-10 communication, HCS-2 registries, and content inscription on Hedera. https://hol.org
320 lines (319 loc) • 8.87 kB
JavaScript
import { ZodError } from "zod";
import { Logger } from "@hashgraphonline/standards-sdk";
import { FormGenerator } from "./index11.js";
import { isFormValidatable } from "@hashgraphonline/standards-agent-kit";
class FormEngine {
constructor(logger) {
this.formGenerator = new FormGenerator();
this.logger = logger || new Logger({ module: "FormEngine" });
}
/**
* Generate a form for a tool with the given input
*/
async generateForm(toolName, tool, input, context) {
const fullContext = {
tool,
input,
...context
};
try {
if (isFormValidatable(tool)) {
return await this.generateFormValidatableForm(tool, input, fullContext);
}
if (input instanceof ZodError) {
return await this.generateErrorBasedForm(tool, input, fullContext);
}
if (this.hasRenderConfig(tool)) {
return await this.generateRenderConfigForm(tool, input, fullContext);
}
if (this.isZodObject(tool.schema)) {
return await this.generateSchemaBasedForm(tool, input, fullContext);
}
return null;
} catch (error) {
this.logger.error(`Failed to generate form for tool: ${toolName}`, {
error: error instanceof Error ? error.message : String(error)
});
throw error;
}
}
/**
* Process a form submission
*/
async processSubmission(submission, context) {
this.validateSubmission(submission);
const baseToolInput = this.extractBaseToolInput(context);
const submissionData = this.extractSubmissionData(submission);
return this.mergeInputData(baseToolInput, submissionData);
}
/**
* Check if a tool requires form generation based on input
*/
shouldGenerateForm(tool, input) {
const inputRecord = input;
if (inputRecord?.__fromForm === true || inputRecord?.renderForm === false) {
return false;
}
if (isFormValidatable(tool)) {
try {
const formValidatableTool = tool;
return formValidatableTool.shouldGenerateForm(input);
} catch (error) {
this.logger.error(
`Error calling shouldGenerateForm() on ${tool.name}:`,
error
);
return false;
}
}
const validation = this.validateInput(tool, input);
return !validation.isValid;
}
/**
* Generate form from error context
*/
async generateFormFromError(error, toolName, toolSchema, originalPrompt) {
return this.formGenerator.generateFormFromError(
error,
toolSchema,
toolName,
originalPrompt
);
}
/**
* Generate form for FormValidatable tools
*/
async generateFormValidatableForm(tool, input, _context) {
const { schemaToUse, isFocusedSchema } = this.resolveFormSchema(tool);
const missingFields = this.determineMissingFields(
tool,
input,
schemaToUse,
isFocusedSchema
);
return this.generateFormWithSchema(tool, input, schemaToUse, missingFields);
}
/**
* Generate form based on schema validation
*/
async generateSchemaBasedForm(tool, input, context) {
const schema = tool.schema;
const formMessage = await this.formGenerator.generateFormFromSchema(
schema,
input,
{
toolName: tool.name,
toolDescription: tool.description
},
context.missingFields
);
if (this.isZodObject(schema)) {
try {
const { jsonSchema, uiSchema } = this.formGenerator.generateJsonSchemaForm(
schema,
input,
context.missingFields || /* @__PURE__ */ new Set()
);
formMessage.jsonSchema = jsonSchema;
formMessage.uiSchema = uiSchema;
} catch (error) {
this.logger.warn(
"Failed to generate JSON Schema for schema-based tool:",
error
);
}
}
formMessage.partialInput = input;
return formMessage;
}
/**
* Generate form based on render config
*/
async generateRenderConfigForm(tool, input, context) {
const schema = tool.schema;
const renderConfig = this.extractRenderConfig(tool);
const formMessage = await this.formGenerator.generateFormFromSchema(
schema,
input,
{
toolName: tool.name,
toolDescription: tool.description
},
context.missingFields
);
if (renderConfig) {
formMessage.formConfig.metadata = {
...formMessage.formConfig.metadata,
renderConfig
};
}
formMessage.partialInput = input;
return formMessage;
}
/**
* Generate form from Zod validation error
*/
async generateErrorBasedForm(tool, error, context) {
return this.formGenerator.generateFormFromError(
error,
tool.schema,
tool.name,
context.input ? String(context.input) : ""
);
}
/**
* Validate input against tool schema
*/
validateInput(tool, input) {
try {
const zodSchema = tool.schema;
zodSchema.parse(input);
return { isValid: true };
} catch (error) {
if (error instanceof ZodError) {
const errors = error.errors.map(
(err) => `${err.path.join(".")}: ${err.message}`
);
return { isValid: false, errors };
}
return { isValid: false, errors: ["Validation failed"] };
}
}
/**
* Check if schema is ZodObject
*/
isZodObject(schema) {
if (!schema || typeof schema !== "object") {
return false;
}
const candidate = schema;
return Boolean(candidate._def && candidate._def.typeName === "ZodObject");
}
/**
* Check if tool has render configuration
*/
hasRenderConfig(tool) {
const schema = tool.schema;
return !!(schema && schema._renderConfig);
}
/**
* Extract render configuration from tool
*/
extractRenderConfig(tool) {
const schema = tool.schema;
return schema?._renderConfig;
}
/**
* Resolve form schema for FormValidatable tools
*/
resolveFormSchema(tool) {
const formValidatableTool = tool;
if (formValidatableTool.getFormSchema) {
const focusedSchema = formValidatableTool.getFormSchema();
if (focusedSchema) {
return { schemaToUse: focusedSchema, isFocusedSchema: true };
}
}
return { schemaToUse: tool.schema, isFocusedSchema: false };
}
/**
* Determine missing fields for form generation
*/
determineMissingFields(tool, input, _schema, _isFocusedSchema) {
const missingFields = /* @__PURE__ */ new Set();
if (!input || typeof input !== "object") {
return missingFields;
}
const inputRecord = input;
const formValidatableTool = tool;
if (formValidatableTool.getEssentialFields) {
const essentialFields = formValidatableTool.getEssentialFields();
for (const field of essentialFields) {
if (!(field in inputRecord) || formValidatableTool.isFieldEmpty && formValidatableTool.isFieldEmpty(field, inputRecord[field])) {
missingFields.add(field);
}
}
}
return missingFields;
}
/**
* Generate form with resolved schema
*/
async generateFormWithSchema(tool, input, schema, missingFields) {
const formMessage = await this.formGenerator.generateFormFromSchema(
schema,
input,
{
toolName: tool.name,
toolDescription: tool.description
},
missingFields
);
if (this.isZodObject(schema)) {
try {
const { jsonSchema, uiSchema } = this.formGenerator.generateJsonSchemaForm(
schema,
input,
missingFields
);
formMessage.jsonSchema = jsonSchema;
formMessage.uiSchema = uiSchema;
} catch (error) {
this.logger.warn("Failed to generate JSON Schema:", error);
}
}
formMessage.partialInput = input;
return formMessage;
}
/**
* Validate form submission
*/
validateSubmission(submission) {
if (!submission.toolName) {
throw new Error("Tool name is required in form submission");
}
if (!submission.parameters) {
throw new Error("Parameters are required in form submission");
}
}
/**
* Extract base tool input from context
*/
extractBaseToolInput(context) {
return context?.originalInput || {};
}
/**
* Extract submission data
*/
extractSubmissionData(submission) {
return {
...submission.parameters,
__fromForm: true
};
}
/**
* Merge input data
*/
mergeInputData(baseInput, submissionData) {
return {
...baseInput,
...submissionData
};
}
/**
* Get registered strategies
*/
getRegisteredStrategies() {
return ["FormValidatable", "SchemaBased", "RenderConfig", "ZodErrorBased"];
}
/**
* Get registered middleware
*/
getRegisteredMiddleware() {
return ["FormSubmissionValidator"];
}
}
export {
FormEngine
};
//# sourceMappingURL=index12.js.map