- Move all plugin files into breznflow/ subdirectory (matches BreznGEO structure) - Add README.md (English) and README.de.md (German) with full documentation - Add GPL-2.0 LICENSE file - Rewrite readme.txt: expanded description, FAQs, external services, changelog Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
166 lines
3.4 KiB
PHP
166 lines
3.4 KiB
PHP
<?php
|
|
/**
|
|
* Categorizes workflow nodes by type and detects AI nodes.
|
|
*
|
|
* @package BreznFlow
|
|
* @since 1.0.0
|
|
*/
|
|
|
|
namespace BreznFlow\Features;
|
|
|
|
if ( ! defined( 'ABSPATH' ) ) {
|
|
exit;
|
|
}
|
|
|
|
/**
|
|
* Categorizes n8n workflow nodes by type (trigger, action, AI, etc.).
|
|
*
|
|
* @since 1.0.0
|
|
*/
|
|
class NodeCategorizer {
|
|
/** Keywords indicating AI-related nodes. */
|
|
const AI_KEYWORDS = array(
|
|
'openai',
|
|
'anthropic',
|
|
'claude',
|
|
'gemini',
|
|
'googleai',
|
|
'huggingface',
|
|
'langchain',
|
|
'agent',
|
|
'vectorstore',
|
|
'gpt',
|
|
'ollama',
|
|
'mistral',
|
|
'cohere',
|
|
'lmchat',
|
|
'chainllm',
|
|
'memorybuffer',
|
|
);
|
|
|
|
/**
|
|
* Categorizes nodes and computes counts.
|
|
*
|
|
* @param array $nodes Array of node objects from workflow data.
|
|
* @return array{counts: array, by_category: array, ai_nodes: array, has_ai: bool, total: int}
|
|
*/
|
|
public static function categorize( array $nodes ): array {
|
|
$counts = array();
|
|
$by_category = array();
|
|
$ai_nodes = array();
|
|
|
|
foreach ( $nodes as $node ) {
|
|
$type = isset( $node['type'] ) ? (string) $node['type'] : '';
|
|
$slug = NodeTypeRegistry::extract_slug( $type );
|
|
$entry = NodeTypeRegistry::lookup( $type );
|
|
$label = $entry['label'] ?? $slug;
|
|
|
|
// Count by display label.
|
|
if ( ! isset( $counts[ $label ] ) ) {
|
|
$counts[ $label ] = 0;
|
|
}
|
|
++$counts[ $label ];
|
|
|
|
// Check for AI nodes.
|
|
$slug_lower = strtolower( $slug );
|
|
foreach ( self::AI_KEYWORDS as $keyword ) {
|
|
if ( str_contains( $slug_lower, $keyword ) ) {
|
|
if ( ! in_array( $type, $ai_nodes, true ) ) {
|
|
$ai_nodes[] = $type;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Group by category.
|
|
$category = self::get_category( $slug );
|
|
if ( ! isset( $by_category[ $category ] ) ) {
|
|
$by_category[ $category ] = array();
|
|
}
|
|
$by_category[ $category ][] = $label;
|
|
}
|
|
|
|
arsort( $counts );
|
|
|
|
return array(
|
|
'counts' => $counts,
|
|
'by_category' => $by_category,
|
|
'ai_nodes' => $ai_nodes,
|
|
'has_ai' => ! empty( $ai_nodes ),
|
|
'total' => count( $nodes ),
|
|
);
|
|
}
|
|
|
|
/**
|
|
* Determines the category for a node slug.
|
|
*
|
|
* @since 1.0.0
|
|
* @param string $slug Node type slug.
|
|
* @return string Category name.
|
|
*/
|
|
private static function get_category( string $slug ): string {
|
|
$slug_lower = strtolower( $slug );
|
|
|
|
// AI check first.
|
|
foreach ( self::AI_KEYWORDS as $keyword ) {
|
|
if ( str_contains( $slug_lower, $keyword ) ) {
|
|
return 'ai';
|
|
}
|
|
}
|
|
|
|
$trigger_slugs = array( 'scheduletrigger', 'webhook', 'manualtrigger', 'formtrigger', 'emailreadimap', 'rssfeadread' );
|
|
foreach ( $trigger_slugs as $t ) {
|
|
if ( str_contains( $slug_lower, 'trigger' ) || str_contains( $slug_lower, 'webhook' ) ) {
|
|
return 'trigger';
|
|
}
|
|
}
|
|
|
|
$logic_slugs = array(
|
|
'if',
|
|
'switch',
|
|
'filter',
|
|
'merge',
|
|
'splitinbatches',
|
|
'splitout',
|
|
'sort',
|
|
'limit',
|
|
'removeduplicates',
|
|
'aggregate',
|
|
'comparedatasets',
|
|
);
|
|
foreach ( $logic_slugs as $l ) {
|
|
if ( $slug_lower === $l ) {
|
|
return 'logic';
|
|
}
|
|
}
|
|
|
|
$code_slugs = array(
|
|
'code',
|
|
'function',
|
|
'executeworkflow',
|
|
'set',
|
|
'editfields',
|
|
'html',
|
|
'xml',
|
|
'markdown',
|
|
'crypto',
|
|
'tofile',
|
|
'converttofile',
|
|
'extractfromfile',
|
|
);
|
|
foreach ( $code_slugs as $c ) {
|
|
if ( $slug_lower === $c ) {
|
|
return 'transform';
|
|
}
|
|
}
|
|
|
|
$db_slugs = array( 'mysql', 'postgres', 'redis', 'mongodb', 'sqlite', 'microsoftsql', 'supabase' );
|
|
foreach ( $db_slugs as $d ) {
|
|
if ( $slug_lower === $d ) {
|
|
return 'database';
|
|
}
|
|
}
|
|
|
|
return 'action';
|
|
}
|
|
}
|