UNPKG

@hashgraphonline/conversational-agent

Version:

Hashgraph Online conversational AI agent implementing HCS-10 communication, HCS-2 registries, and content inscription on Hedera. https://hol.org

326 lines (325 loc) 9.23 kB
import { z } from "zod"; import { Logger } from "@hashgraphonline/standards-sdk"; import { wrapToolWithFormValidation } from "./index33.js"; import { FormGenerator } from "./index11.js"; import { fieldGuidanceRegistry } from "./index14.js"; import { isFormValidatable } from "@hashgraphonline/standards-agent-kit"; class ToolRegistry { constructor(logger) { this.tools = /* @__PURE__ */ new Map(); this.formGenerator = new FormGenerator(); this.logger = logger || new Logger({ module: "ToolRegistry" }); } /** * Register a tool with the registry */ registerTool(tool, options = {}) { const capabilities = this.analyzeToolCapabilities(tool); const metadata = { name: tool.name, version: "1.0.0", category: options.metadata?.category || "core", description: tool.description, capabilities, dependencies: [], schema: tool.schema, ...options.metadata }; try { if (!metadata.entityResolutionPreferences) { const schemaRecord = tool.schema; const rawPrefs = schemaRecord && typeof schemaRecord === "object" && schemaRecord["_entityResolutionPreferences"]; if (rawPrefs && typeof rawPrefs === "object") { metadata.entityResolutionPreferences = rawPrefs; } } } catch { } try { const schemaRecord = tool.schema; const schemaDef = schemaRecord && schemaRecord._def; if (schemaDef?.typeName === "ZodObject") { const shape = typeof schemaDef.shape === "function" ? schemaDef.shape?.() || {} : schemaDef.shape || {}; const metadataField = shape["metadata"]; const isStringArray = !!metadataField && metadataField._def?.typeName === "ZodArray" && metadataField._def?.type?._def?.typeName === "ZodString"; if (isStringArray && typeof tool.description === "string") { if (!metadata.entityResolutionPreferences) { metadata.entityResolutionPreferences = { inscription: "hrl" }; } const note = " NOTE: When referencing inscriptions or media, provide canonical Hashlink Resource Locators (e.g., hcs://<standard>/<topicId>) rather than external URLs or embedded JSON."; if (!tool.description.includes("Hashlink Resource Locators")) { tool.description = `${tool.description}${note}`; } } } } catch { } let finalTool = tool; let wrapper; if (this.shouldWrapTool(tool, capabilities, options)) { wrapper = wrapToolWithFormValidation( tool, this.formGenerator, { requireAllFields: false, skipFields: ["metaOptions"], ...options.wrapperConfig } ); finalTool = wrapper; } try { if (metadata.entityResolutionPreferences) { finalTool["entityResolutionPreferences"] = metadata.entityResolutionPreferences; } } catch { } const entry = { tool: finalTool, metadata, wrapper, originalTool: tool, options: { priority: capabilities.priority, capability: "basic", // Default capability enabled: true, // All tools are enabled by default namespace: metadata.category } }; this.tools.set(tool.name, entry); try { const metaFG = metadata.fieldGuidance; if (metaFG) { fieldGuidanceRegistry.registerToolConfiguration(metaFG); } const provider = metadata.fieldGuidanceProvider; if (provider) { const pattern = metaFG?.toolPattern ?? tool.name; fieldGuidanceRegistry.registerToolProvider(pattern, provider, { id: `${tool.name}:field-guidance-provider`, priority: 0 }); } } catch { } } /** * Get a tool by name */ getTool(name) { return this.tools.get(name) || null; } /** * Get tools by capability */ getToolsByCapability(capability, value) { const results = []; for (const entry of this.tools.values()) { if (value !== void 0) { if (entry.metadata.capabilities[capability] === value) { results.push(entry); } } else if (entry.metadata.capabilities[capability]) { results.push(entry); } } return results; } /** * Get tools by query */ getToolsByQuery(query) { const results = []; for (const entry of this.tools.values()) { let matches = true; if (query.name && entry.metadata.name !== query.name) { matches = false; } if (query.category && entry.metadata.category !== query.category) { matches = false; } if (query.capabilities) { for (const [key, value] of Object.entries(query.capabilities)) { if (entry.metadata.capabilities[key] !== value) { matches = false; break; } } } if (matches) { results.push(entry); } } return results; } /** * Get all registered tools */ getAllTools() { return Array.from(this.tools.values()).map((entry) => entry.tool); } /** * Get all registry entries */ getAllRegistryEntries() { return Array.from(this.tools.values()); } /** * Get all tool names */ getToolNames() { return Array.from(this.tools.keys()); } /** * Get tools by priority */ getToolsByPriority(priority) { return this.getToolsByCapability("priority", priority); } /** * Get enabled tools (all tools are considered enabled by default) */ getEnabledTools() { return this.getAllRegistryEntries(); } /** * Get tools by namespace/category */ getToolsByNamespace(namespace) { if (!namespace) { return this.getAllRegistryEntries(); } return this.getToolsByQuery({ category: namespace }); } /** * Check if registry has capability */ hasCapability(capability) { for (const entry of this.tools.values()) { if (entry.metadata.capabilities[capability]) { return true; } } return false; } /** * Update tool options (metadata) */ updateToolOptions(name, options) { const entry = this.tools.get(name); if (!entry) { return false; } entry.metadata = { ...entry.metadata, ...options }; return true; } /** * Check if a tool is registered */ hasTool(name) { return this.tools.has(name); } /** * Unregister a tool */ unregisterTool(name) { return this.tools.delete(name); } /** * Clear all tools */ clear() { this.tools.clear(); } /** * Analyze tool capabilities */ analyzeToolCapabilities(tool) { const implementsFormValidatable = isFormValidatable(tool); const hasRenderConfig = this.hasRenderConfig(tool); const isZodObjectLike = this.isZodObjectLike(tool.schema); const supportsFormValidation = implementsFormValidatable || hasRenderConfig; const requiresWrapper = supportsFormValidation && isZodObjectLike; let priority = "medium"; let category = "core"; if (supportsFormValidation && requiresWrapper) { priority = "critical"; } else if (supportsFormValidation) { priority = "high"; } else if (tool.description?.toLowerCase().includes("query") || tool.description?.toLowerCase().includes("search")) { priority = "low"; } const toolAsAny = tool; if (tool.constructor.name.includes("MCP") || toolAsAny.isMCPTool) { category = "mcp"; } else if (toolAsAny.isExtension || tool.constructor.name.includes("Extension")) { category = "extension"; } return { supportsFormValidation, requiresWrapper, priority, category }; } /** * Check if tool has render configuration */ hasRenderConfig(tool) { const schema = tool.schema; return !!(schema && schema._renderConfig); } /** * Determine if tool should be wrapped */ shouldWrapTool(tool, capabilities, options) { if (options.skipWrapper) { return false; } if (options.forceWrapper) { return true; } return capabilities.requiresWrapper; } /** * Check if schema is ZodObject-like */ isZodObjectLike(schema) { if (!schema || typeof schema !== "object") { return false; } const schemaRecord = schema; const schemaDef = schemaRecord._def; return schema instanceof z.ZodObject || schemaDef?.typeName === "ZodObject" || "shape" in schemaRecord && typeof schemaRecord.shape === "object"; } /** * Get statistics about the registry */ getStatistics() { const stats = { totalTools: this.tools.size, wrappedTools: 0, unwrappedTools: 0, categoryCounts: { core: 0, extension: 0, mcp: 0 }, priorityCounts: { low: 0, medium: 0, high: 0, critical: 0 } }; for (const entry of this.tools.values()) { if (entry.wrapper) { stats.wrappedTools++; } else { stats.unwrappedTools++; } stats.categoryCounts[entry.metadata.category]++; stats.priorityCounts[entry.metadata.capabilities.priority]++; } return stats; } } export { ToolRegistry }; //# sourceMappingURL=index46.js.map