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

268 lines (267 loc) 9.18 kB
import { Logger } from "@hashgraphonline/standards-sdk"; class FieldGuidanceRegistry { constructor() { this.configurations = []; this.providers = []; this.registerOrderCounter = 0; this.logger = new Logger({ module: "FieldGuidanceRegistry" }); } /** * Register field guidance for a specific tool */ registerToolConfiguration(config) { this.configurations.push(config); } /** * Register a provider for dynamic field/global guidance */ registerToolProvider(toolPattern, provider, options) { const id = options?.id ?? `provider-${this.providers.length + 1}`; const priority = options?.priority ?? 0; if (this.providers.some((p) => p.id === id)) { this.logger.error("Duplicate provider id", { id }); throw new Error("DUPLICATE_PROVIDER_ID"); } this.providers.push({ id, priority, pattern: toolPattern, provider, order: this.registerOrderCounter++ }); return id; } /** Unregister a provider by id */ unregisterProvider(id) { this.providers = this.providers.filter((p) => p.id !== id); } /** List registered providers */ listProviders() { return this.providers.map(({ id, priority, pattern }) => ({ id, priority, pattern })); } /** * Get field guidance for a specific tool and field */ getFieldGuidance(toolName, fieldName) { if (process.env.CA_FORM_GUIDANCE_ENABLED === "false") { return null; } for (const config of this.configurations) { const matches = typeof config.toolPattern === "string" ? toolName.toLowerCase().includes(config.toolPattern.toLowerCase()) : config.toolPattern.test(toolName); if (matches && config.fields[fieldName]) { const staticGuidance = config.fields[fieldName]; const providers2 = this.pickMatchingProviders(toolName); if (providers2.length === 0) return staticGuidance; let merged = { ...staticGuidance }; for (const p of [...providers2].reverse()) { const fromProvider = this.safeGetFieldGuidance( p, fieldName, toolName ); if (fromProvider) { merged = this.mergeGuidance(merged, fromProvider); } } return merged; } } const providers = this.pickMatchingProviders(toolName); if (providers.length > 0) { let merged = {}; for (const p of [...providers].reverse()) { const g = this.safeGetFieldGuidance(p, fieldName, toolName); if (g) merged = this.mergeGuidance(merged, g); } return Object.keys(merged).length > 0 ? merged : null; } return null; } /** * Get global guidance for a tool */ getGlobalGuidance(toolName) { if (process.env.CA_FORM_GUIDANCE_ENABLED === "false") { return null; } for (const config of this.configurations) { const matches = typeof config.toolPattern === "string" ? toolName.toLowerCase().includes(config.toolPattern.toLowerCase()) : config.toolPattern.test(toolName); if (matches && config.globalGuidance) { const base = config.globalGuidance; const providers2 = this.pickMatchingProviders(toolName); if (providers2.length === 0) return base; let mergedWarnings = base.warnings; let mergedQuality = base.qualityStandards; for (const p of [...providers2].reverse()) { const fromProvider = this.safeGetGlobalGuidance(p, toolName); if (fromProvider) { mergedWarnings = fromProvider.warnings ?? mergedWarnings; mergedQuality = fromProvider.qualityStandards ?? mergedQuality; } } const result = {}; if (mergedWarnings !== void 0) result.warnings = mergedWarnings; if (mergedQuality !== void 0) result.qualityStandards = mergedQuality; return result; } } const providers = this.pickMatchingProviders(toolName); if (providers.length > 0) { let mergedWarnings; let mergedQuality; for (const p of [...providers].reverse()) { const g = this.safeGetGlobalGuidance(p, toolName); if (g) { mergedWarnings = g.warnings ?? mergedWarnings; mergedQuality = g.qualityStandards ?? mergedQuality; } } const result = {}; if (mergedWarnings !== void 0) result.warnings = mergedWarnings; if (mergedQuality !== void 0) result.qualityStandards = mergedQuality; return Object.keys(result).length > 0 ? result : null; } return null; } /** * Validate field value against guidance rules */ validateFieldValue(toolName, fieldName, value) { const guidance = this.getFieldGuidance(toolName, fieldName); const warnings = []; const errors = []; if (!guidance || typeof value !== "string") { return { isValid: true, warnings, errors }; } if (guidance.warnings) { for (const warning of guidance.warnings) { if (warning.pattern.test(value)) { warnings.push(warning.message); } } } if (guidance.validationRules) { const { rejectPatterns, qualityChecks } = guidance.validationRules; if (rejectPatterns) { for (const reject of rejectPatterns) { if (reject.pattern.test(value)) { errors.push(`Rejected: ${reject.reason}`); } } } if (qualityChecks) { if (qualityChecks.forbidTechnicalTerms) { const lowerValue = value.toLowerCase(); for (const term of qualityChecks.forbidTechnicalTerms) { if (lowerValue.includes(term.toLowerCase())) { errors.push( `Avoid technical terms like "${term}" in NFT metadata` ); } } } if (qualityChecks.requireSpecificTerms) { const lowerValue = value.toLowerCase(); const hasRequired = qualityChecks.requireSpecificTerms.some( (term) => lowerValue.includes(term.toLowerCase()) ); if (!hasRequired) { warnings.push( `Consider including terms like: ${qualityChecks.requireSpecificTerms.join( ", " )}` ); } } if (qualityChecks.minNonTechnicalWords) { const words = value.split(/\s+/).filter((word) => word.length > 2); if (words.length < qualityChecks.minNonTechnicalWords) { warnings.push( `Consider providing more descriptive content (at least ${qualityChecks.minNonTechnicalWords} meaningful words)` ); } } } } return { isValid: errors.length === 0, warnings, errors }; } /** * Clear all configurations (useful for testing) */ clear() { this.configurations = []; this.providers = []; this.registerOrderCounter = 0; } /** Choose matching provider by priority then last-in wins */ pickMatchingProviders(toolName) { const matches = this.providers.filter( (p) => typeof p.pattern === "string" ? toolName.toLowerCase().includes(p.pattern.toLowerCase()) : p.pattern.test(toolName) ); const sorted = matches.sort((a, b) => { if (b.priority !== a.priority) return b.priority - a.priority; return b.order - a.order; }); return sorted.map((m) => ({ id: m.id, provider: m.provider, priority: m.priority, order: m.order })); } safeGetFieldGuidance(winner, fieldName, toolName) { try { return winner.provider.getFieldGuidance(fieldName, { toolName }) ?? null; } catch (err) { this.logger.warn("Provider getFieldGuidance failed", { id: winner.id, err }); return null; } } safeGetGlobalGuidance(winner, toolName) { try { return winner.provider.getGlobalGuidance?.(toolName) ?? null; } catch (err) { this.logger.warn("Provider getGlobalGuidance failed", { id: winner.id, err }); return null; } } mergeGuidance(base, over) { const out = {}; const suggestions = over.suggestions ?? base.suggestions; if (suggestions !== void 0) out.suggestions = suggestions; const predefinedOptions = over.predefinedOptions ?? base.predefinedOptions; if (predefinedOptions !== void 0) out.predefinedOptions = predefinedOptions; const warnings = over.warnings ?? base.warnings; if (warnings !== void 0) out.warnings = warnings; const validationRules = over.validationRules ?? base.validationRules; if (validationRules !== void 0) out.validationRules = validationRules; const fieldTypeOverride = over.fieldTypeOverride ?? base.fieldTypeOverride; if (fieldTypeOverride !== void 0) out.fieldTypeOverride = fieldTypeOverride; const contextualHelpText = over.contextualHelpText ?? base.contextualHelpText; if (contextualHelpText !== void 0) out.contextualHelpText = contextualHelpText; return out; } } const fieldGuidanceRegistry = new FieldGuidanceRegistry(); export { fieldGuidanceRegistry }; //# sourceMappingURL=index14.js.map