Skip to main content
Glama
capabilities.ts9.46 kB
/** * Resource Capability Discovery & Inference Engine * * PRD #48: Resource Capabilities Discovery & Integration * * This module provides capability inference for Kubernetes resources through * AI-powered analysis of schemas and metadata. */ import { Logger } from './error-handling'; import { AIProvider } from './ai-provider.interface'; import { loadPrompt } from './shared-prompt-loader'; /** * Complete resource capability data structure for Vector DB storage */ export interface ResourceCapability { // Resource identification resourceName: string; // "resourcegroups.azure.upbound.io" apiVersion?: string; // "azure.upbound.io/v1beta1" - full apiVersion from kubectl version?: string; // "v1beta1" - just the version part group?: string; // "azure.upbound.io" - API group // Capability information (AI-inferred) capabilities: string[]; // ["postgresql", "mysql", "database", "multi-cloud"] providers: string[]; // ["azure", "gcp", "aws"] abstractions: string[]; // ["high-availability", "persistent-storage", "backup"] complexity: 'low' | 'medium' | 'high'; // User experience complexity // Metadata for AI understanding description: string; // "Managed database solution supporting multiple engines" useCase: string; // "Simple database deployment without infrastructure complexity" // Vector embedding for semantic search (generated separately) embedding?: number[]; // Generated from capability description // Analysis metadata analyzedAt: string; // ISO timestamp confidence: number; // 0-1 AI confidence score } /** * Generic Capability Inference Engine * * Analyzes any Kubernetes CRD using AI to extract semantic capabilities * for improved AI recommendations and resource matching. */ export class CapabilityInferenceEngine { private logger: Logger; private aiProvider: AIProvider; constructor(aiProvider: AIProvider, logger: Logger) { this.aiProvider = aiProvider; this.logger = logger; } /** * Main entry point: analyze resource to infer complete capabilities * * @param resourceName - Full resource name (e.g., "resourcegroups.azure.upbound.io") * @param resourceDefinition - kubectl explain output for the resource * @param interaction_id - Optional interaction ID for tracing * @param apiVersion - Full apiVersion from kubectl (e.g., "apps/v1", "azure.upbound.io/v1beta1") * @param version - Just the version part (e.g., "v1beta1") * @param group - API group (e.g., "azure.upbound.io") * @throws Error if capability inference fails for any reason */ async inferCapabilities( resourceName: string, resourceDefinition?: string, interaction_id?: string, apiVersion?: string, version?: string, group?: string ): Promise<ResourceCapability> { const requestId = `capability-inference-${Date.now()}`; this.logger.info('Starting capability inference', { requestId, resource: resourceName, hasDefinition: !!resourceDefinition, apiVersion, version, group }); // Use AI to analyze all available information const aiResult = await this.inferWithAI(resourceName, resourceDefinition, requestId, interaction_id); // Convert AI result to final capability structure const finalCapability = this.buildResourceCapability(resourceName, aiResult, apiVersion, version, group); this.logger.info('Capability inference completed', { requestId, resource: resourceName, capabilitiesFound: finalCapability.capabilities.length, providersFound: finalCapability.providers.length, complexity: finalCapability.complexity, confidence: finalCapability.confidence, apiVersion, version }); return finalCapability; } /** * Use AI to infer capabilities from all available resource context * * @throws Error if AI inference fails */ private async inferWithAI( resourceName: string, resourceDefinition?: string, requestId?: string, interaction_id?: string ): Promise<{ capabilities: string[]; providers: string[]; abstractions: string[]; complexity: 'low' | 'medium' | 'high'; description: string; useCase: string; confidence: number; }> { try { const prompt = await this.buildInferencePrompt(resourceName, resourceDefinition); const response = await this.aiProvider.sendMessage(prompt, 'capability-inference', { user_intent: `Analyze capabilities of Kubernetes resource: ${resourceName}`, interaction_id: interaction_id || 'inference' }); return this.parseCapabilitiesFromAI(response.content); } catch (error) { this.logger.error('AI capability inference failed', error as Error, { requestId, resource: resourceName }); throw error; // Re-throw to maintain fail-fast behavior } } /** * Build AI inference prompt using standard prompt loading pattern * * @throws Error if prompt template cannot be loaded */ private async buildInferencePrompt( resourceName: string, resourceDefinition?: string ): Promise<string> { // Load prompt template using shared prompt loader const finalPrompt = loadPrompt('capability-inference', { resourceName, resourceDefinition: resourceDefinition || 'No resource definition provided' }); return finalPrompt; } /** * Parse AI response into structured capability data * * @throws Error if AI response cannot be parsed or is invalid */ private parseCapabilitiesFromAI(response: string): { capabilities: string[]; providers: string[]; abstractions: string[]; complexity: 'low' | 'medium' | 'high'; description: string; useCase: string; confidence: number; } { // Look for JSON in the response using standard pattern const jsonMatch = response.match(/\{[\s\S]*\}/); if (!jsonMatch) { throw new Error(`No JSON found in AI response. Response: ${response.substring(0, 200)}...`); } let parsed: any; try { parsed = JSON.parse(jsonMatch[0]); } catch (parseError) { throw new Error(`Invalid JSON in AI response: ${parseError instanceof Error ? parseError.message : String(parseError)}. JSON: ${jsonMatch[0].substring(0, 200)}...`); } // Validate required fields with detailed error messages if (!Array.isArray(parsed.capabilities)) { throw new Error(`AI response missing or invalid capabilities array. Got: ${typeof parsed.capabilities}`); } if (!Array.isArray(parsed.providers)) { throw new Error(`AI response missing or invalid providers array. Got: ${typeof parsed.providers}`); } if (!Array.isArray(parsed.abstractions)) { throw new Error(`AI response missing or invalid abstractions array. Got: ${typeof parsed.abstractions}`); } if (!['low', 'medium', 'high'].includes(parsed.complexity)) { throw new Error(`AI response invalid complexity: ${parsed.complexity}. Must be low, medium, or high`); } if (typeof parsed.description !== 'string' || parsed.description.trim() === '') { throw new Error(`AI response missing or invalid description. Got: ${typeof parsed.description}`); } if (typeof parsed.useCase !== 'string' || parsed.useCase.trim() === '') { throw new Error(`AI response missing or invalid useCase. Got: ${typeof parsed.useCase}`); } if (typeof parsed.confidence !== 'number' || parsed.confidence < 0 || parsed.confidence > 1) { throw new Error(`AI response invalid confidence score: ${parsed.confidence}. Must be number between 0-1`); } return { capabilities: parsed.capabilities, providers: parsed.providers, abstractions: parsed.abstractions, complexity: parsed.complexity, description: parsed.description.trim(), useCase: parsed.useCase.trim(), confidence: parsed.confidence }; } /** * Build final ResourceCapability from AI analysis result */ private buildResourceCapability( resourceName: string, aiResult: { capabilities: string[]; providers: string[]; abstractions: string[]; complexity: 'low' | 'medium' | 'high'; description: string; useCase: string; confidence: number; }, apiVersion?: string, version?: string, group?: string ): ResourceCapability { return { resourceName, apiVersion, version, group, capabilities: aiResult.capabilities, providers: aiResult.providers, abstractions: aiResult.abstractions, complexity: aiResult.complexity, description: aiResult.description, useCase: aiResult.useCase, confidence: aiResult.confidence, analyzedAt: new Date().toISOString() }; } /** * Generate Vector DB ID for capability storage * Creates deterministic UUID from resource name for Qdrant compatibility */ static generateCapabilityId(resourceName: string): string { // Create deterministic UUID from resource name hash const crypto = require('crypto'); const hash = crypto.createHash('sha256').update(`capability-${resourceName}`).digest('hex'); // Convert to UUID format: 8-4-4-4-12 return `${hash.substring(0,8)}-${hash.substring(8,12)}-${hash.substring(12,16)}-${hash.substring(16,20)}-${hash.substring(20,32)}`; } }

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/vfarcic/dot-ai'

If you have feedback or need assistance with the MCP directory API, please join our Discord server