Skip to main content
Glama
flow-processor.ts6.42 kB
import { createLogger } from './logger.js'; import { BlahFlow, BlahFlowNode, BlahFlowEdge } from './validator.js'; // Create a logger for this module const logger = createLogger('flow-processor'); /** * Compiles flows from a BLAH configuration into executable tools * @param flows Array of flow definitions from the configuration * @returns Array of tools created from the flows */ export function compileFlowsToTools(flows?: BlahFlow[]): any[] { if (!flows || !Array.isArray(flows) || flows.length === 0) { return []; } logger.info(`Compiling ${flows.length} flows into tools`); const flowTools: any[] = []; for (const flow of flows) { try { // Skip flows without a valid name if (!flow.name) { logger.warn('Skipping flow without a name'); continue; } // Skip flows without valid nodes/edges if (!Array.isArray(flow.nodes) || flow.nodes.length === 0) { logger.warn(`Flow '${flow.name}' has no nodes, skipping`); continue; } if (!Array.isArray(flow.edges) || flow.edges.length === 0) { logger.warn(`Flow '${flow.name}' has no edges, skipping`); continue; } // Create the flow tool const flowTool = createToolFromFlow(flow); if (flowTool) { flowTools.push(flowTool); logger.info(`Created tool '${flowTool.name}' from flow '${flow.name}'`); } } catch (error) { logger.error(`Error processing flow '${flow.name || 'unnamed'}':`, error); } } logger.info(`Successfully compiled ${flowTools.length} flows into tools`); return flowTools; } /** * Creates a tool definition from a flow * @param flow The flow definition * @returns A tool object created from the flow */ function createToolFromFlow(flow: BlahFlow): any { // Analyze the flow to find start and end nodes const { startNodes, endNodes } = analyzeFlowTopology(flow); if (startNodes.length === 0) { logger.warn(`Flow '${flow.name}' has no start nodes, skipping`); return null; } if (endNodes.length === 0) { logger.warn(`Flow '${flow.name}' has no end nodes, skipping`); return null; } // Create a name for the flow tool const firstNodeName = startNodes[0].name.replace(/[^a-zA-Z0-9_]/g, '_'); const lastNodeName = endNodes[0].name.replace(/[^a-zA-Z0-9_]/g, '_'); const flowToolName = `FLOW_${firstNodeName}_${lastNodeName}`; // Generate a description for the flow tool const description = generateFlowDescription(flow, startNodes, endNodes); // Create input schema based on start node parameters const inputSchema = { type: 'object', properties: { trigger: { type: 'boolean', description: 'Set to true to trigger the flow' }, // Add additional input parameters based on the start node ...generateInputSchemaFromStartNode(startNodes[0]) } }; // Create the tool object return { name: flowToolName, description, fromFlow: flow.name, inputSchema, command: 'node src/utils/flow-executor.js', // This would be replaced with actual executor parameters: { flowName: flow.name } }; } /** * Analyzes a flow to find start and end nodes * @param flow The flow definition * @returns Object containing arrays of start and end nodes */ function analyzeFlowTopology(flow: BlahFlow): { startNodes: BlahFlowNode[], endNodes: BlahFlowNode[] } { const startNodes: BlahFlowNode[] = []; const endNodes: BlahFlowNode[] = []; const incomingEdgesMap = new Map<string, BlahFlowEdge[]>(); const outgoingEdgesMap = new Map<string, BlahFlowEdge[]>(); // Initialize maps for (const node of flow.nodes) { incomingEdgesMap.set(node.name, []); outgoingEdgesMap.set(node.name, []); } // Populate edge maps for (const edge of flow.edges) { const outgoing = outgoingEdgesMap.get(edge.startNodeName) || []; outgoing.push(edge); outgoingEdgesMap.set(edge.startNodeName, outgoing); const incoming = incomingEdgesMap.get(edge.endNodeName) || []; incoming.push(edge); incomingEdgesMap.set(edge.endNodeName, incoming); } // Find start nodes (no incoming edges) for (const node of flow.nodes) { const incoming = incomingEdgesMap.get(node.name) || []; if (incoming.length === 0) { startNodes.push(node); } } // Find end nodes (no outgoing edges) for (const node of flow.nodes) { const outgoing = outgoingEdgesMap.get(node.name) || []; if (outgoing.length === 0) { endNodes.push(node); } } return { startNodes, endNodes }; } /** * Generates input schema properties based on the start node * @param startNode The flow's start node * @returns Object containing input schema properties */ function generateInputSchemaFromStartNode(startNode: BlahFlowNode): Record<string, any> { // This would be expanded based on the specific node type const properties: Record<string, any> = {}; // Add parameters from the start node to the input schema for (const [key, value] of Object.entries(startNode.parameters)) { properties[key] = { type: typeof value === 'number' ? 'number' : 'string', description: `Parameter ${key} for the flow` }; } return properties; } /** * Generates a description for a flow tool * @param flow The flow definition * @param startNodes Array of start nodes * @param endNodes Array of end nodes * @returns A description string */ function generateFlowDescription(flow: BlahFlow, startNodes: BlahFlowNode[], endNodes: BlahFlowNode[]): string { let description = `Tool created from flow '${flow.name}'`; if (flow.description) { description += `: ${flow.description}`; } // Add information about start, steps, and end description += `. Starts at '${startNodes[0].text}'`; // Add information about conditional logic const conditionalEdges = flow.edges.filter(edge => edge.condition); if (conditionalEdges.length > 0) { description += `, includes conditional branching based on results`; } // Add information about end points if (endNodes.length === 1) { description += `, and ends at '${endNodes[0].text}'`; } else if (endNodes.length > 1) { description += `, and has ${endNodes.length} possible end points`; } // Add node count description += `. Contains ${flow.nodes.length} steps total.`; return description; }

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/thomasdavis/blah'

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