Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 | 98x 98x 98x 1x 98x 98x 98x 98x 98x 98x 98x 98x 98x 98x 98x 98x 98x 98x 98x 98x 98x 98x 98x 98x 98x 98x 98x 98x 98x 98x 4x 4x 98x 98x 98x 98x 98x 98x 98x 98x 98x 98x 98x 98x 98x 98x 98x 98x 98x 302x 302x 302x 302x 98x 98x 98x 98x 98x 98x 3x 3x 3x 3x 3x 3x 3x 3x 3x 3x 3x 10x 10x 10x 10x 10x 8x 10x 10x 10x 3x 8x 2x 8x 3x 3x 3x 3x 1x 1x 1x 1x 3x 3x 98x 10x 10x 10x 10x 10x 10x 10x 10x 10x 10x 98x 24x 24x 24x 24x 24x 24x 24x 24x 24x 24x 107x 107x 107x 24x 24x 24x 24x 24x 24x 24x 24x 24x 24x 24x 98x 98x 98x 98x 98x 98x 98x 98x 98x 98x 145x 145x 143x 143x 143x 98x 98x 98x 98x 98x 102x 98x 98x 98x 98x 98x 98x 107x 98x 98x 98x 98x 98x 98x 98x 98x 98x 98x 98x 98x 98x 98x 98x 98x | import { FormData, NodeConfig, ACTION_GROUPS, Features } from '../types';
import { CallLLM, Node } from '../../store/flow-definition';
import { generateUUID, createMultiCategoryRouter } from '../../utils';
import { html } from 'lit';
import { validateWith } from '../utils';
import { LLMModel, hasLLMRole } from '../flow-utils';
export const split_by_llm_categorize: NodeConfig = {
type: 'split_by_llm_categorize',
name: 'Split by AI',
group: ACTION_GROUPS.services,
flowTypes: [],
features: [Features.AI],
form: {
llm: {
type: 'select',
label: 'LLM',
helpText: 'Select the LLM to use for categorization',
required: true,
endpoint: '/api/internal/llms.json',
valueKey: 'uuid',
nameKey: 'name',
placeholder: 'Select an LLM...',
shouldExclude: (option: LLMModel) => !hasLLMRole(option, 'engine')
},
input: {
type: 'text',
label: 'Input',
helpText: 'The input to categorize (usually @input)',
required: true,
evaluated: true,
placeholder: '@input'
},
categories: {
type: 'array',
helpText: 'Define the categories for classification',
required: true,
sortable: true,
itemLabel: 'Category',
minItems: 1,
maxItems: 10,
isEmptyItem: (item: any) => {
return !item.name || item.name.trim() === '';
},
itemConfig: {
name: {
type: 'text',
placeholder: 'Category name',
required: true
}
}
}
},
layout: ['llm', 'input', 'categories'],
validate: validateWith((formData, errors) => {
if (!formData.categories || !Array.isArray(formData.categories)) return;
const categories = formData.categories.filter(
(item: any) => item?.name && item.name.trim() !== ''
);
const duplicateCategories: string[] = [];
const lowerCaseMap = new Map<string, string[]>();
categories.forEach((category) => {
const lowerName = category.name.trim().toLowerCase();
if (!lowerCaseMap.has(lowerName)) {
lowerCaseMap.set(lowerName, []);
}
lowerCaseMap.get(lowerName).push(category.name.trim());
});
lowerCaseMap.forEach((originalNames) => {
if (originalNames.length > 1) {
duplicateCategories.push(...originalNames);
}
});
if (duplicateCategories.length > 0) {
const uniqueDuplicates = [...new Set(duplicateCategories)];
errors.categories = `Duplicate category names found: ${uniqueDuplicates.join(
', '
)}`;
}
}),
render: (node: Node) => {
const callLlmAction = node.actions?.find(
(action) => action.type === 'call_llm'
) as CallLLM;
return html`
<div class="body">Categorize with ${callLlmAction.llm.name}</div>
`;
},
toFormData: (node: Node) => {
// Extract data from the existing node structure
const callLlmAction = node.actions?.find(
(action) => action.type === 'call_llm'
) as any;
const categories =
node.router?.categories
?.filter((cat) => cat.name !== 'Other' && cat.name !== 'Failure')
.map((cat) => ({ name: cat.name })) || [];
return {
uuid: node.uuid,
llm: callLlmAction?.llm ? [callLlmAction.llm] : [],
input: callLlmAction?.input || '@input',
categories: categories
};
},
fromFormData: (formData: FormData, originalNode: Node): Node => {
// Get LLM selection
const llmSelection =
Array.isArray(formData.llm) && formData.llm.length > 0
? formData.llm[0]
: null;
// Get user categories
const userCategories = (formData.categories || [])
.filter((item: any) => item?.name?.trim())
.map((item: any) => item.name.trim());
// Find existing call_llm action to preserve its UUID
const existingCallLlmAction = originalNode.actions?.find(
(action) => action.type === 'call_llm'
);
const callLlmUuid = existingCallLlmAction?.uuid || generateUUID();
// Create call_llm action (using any type to match the example format)
const callLlmAction: CallLLM = {
type: 'call_llm',
uuid: callLlmUuid,
llm: llmSelection
? {
uuid: llmSelection.uuid || llmSelection.value,
name: llmSelection.name
}
: { uuid: '', name: '' },
instructions: `@(prompt("categorize", slice(node.categories, 0, -2)))`,
input: formData.input || '@input',
output_local: '_llm_output'
};
// Create categories and exits
const existingCategories = originalNode.router?.categories || [];
const existingExits = originalNode.exits || [];
const existingCases = originalNode.router?.cases || [];
const { router, exits } = createMultiCategoryRouter(
'@locals._llm_output',
userCategories,
(categoryName) => ({
type: 'has_only_text',
arguments: [categoryName]
}),
existingCategories,
existingExits,
existingCases
);
// Return the complete node
return {
uuid: originalNode.uuid,
actions: [callLlmAction],
router: router,
exits: exits
};
},
// Localization support for categories
localizable: 'categories',
nonTranslatableCategories: ['Failure']
};
|