diff --git a/packages/frontend/editor-ui/src/utils/fromAIOverrideUtils.test.ts b/packages/frontend/editor-ui/src/utils/fromAIOverrideUtils.test.ts index 227b4732c3..3fbbb6526e 100644 --- a/packages/frontend/editor-ui/src/utils/fromAIOverrideUtils.test.ts +++ b/packages/frontend/editor-ui/src/utils/fromAIOverrideUtils.test.ts @@ -1,6 +1,7 @@ import type { INodeUi } from '@/Interface'; import type { FromAIOverride, OverrideContext } from './fromAIOverrideUtils'; import { + buildUniqueName, buildValueFromOverride, fromAIExtraProps, isFromAIOverrideValue, @@ -213,3 +214,40 @@ describe('FromAiOverride', () => { ); }); }); + +describe('buildUniqueName', () => { + test.each<[string, string, string]>([ + ['no list segments', 'parameters.someParameter', DISPLAY_NAME], + [ + 'list segments in the path', + 'parameters.someList[0].someParameter', + 'someList0_' + DISPLAY_NAME, + ], + [ + 'multiple list segments in the path', + 'parameters.someList[0].nestedList[1].someParameter', + 'someList0_nestedList1_' + DISPLAY_NAME, + ], + [ + 'paths without parameters', + 'someList[0].nestedList[1]', + 'someList0_nestedList1_' + DISPLAY_NAME, + ], + ['empty paths', '', DISPLAY_NAME], + [ + 'path with multiple lists and segment exceeding 63 characters', + 'parameters.someLoooooongList[0].nestedListWithAVeryLongNameThatExceedsTheLimit[1].someParameter', + `someLoooooongList0_nestedListWithAVeryLongNameThatExceedsTheLimit1_${DISPLAY_NAME}`.slice( + -63, + ), + ], + [ + 'path with multiple long segments and truncation', + 'parameters.someExtremelyLongListNameThatExceedsTheLimit.anotherLongSegmentName.finalParameter', + DISPLAY_NAME, + ], + ])('should build a unique name with %s', (_description, path, expected) => { + const context = makeContext('value', path); + expect(buildUniqueName(context)).toEqual(expected); + }); +}); diff --git a/packages/frontend/editor-ui/src/utils/fromAIOverrideUtils.ts b/packages/frontend/editor-ui/src/utils/fromAIOverrideUtils.ts index 08a0efbcd8..e46ef7c381 100644 --- a/packages/frontend/editor-ui/src/utils/fromAIOverrideUtils.ts +++ b/packages/frontend/editor-ui/src/utils/fromAIOverrideUtils.ts @@ -119,14 +119,38 @@ function getBestQuoteChar(description: string) { return "'"; } +// Note that this is not *technically* unique, as two lists with the +// same list name and the same property display name could theoretically +// exist within one node. However, this is unlikely to happen in practice. +export function buildUniqueName(props: Pick) { + const path = props.path.split('.'); + + // include any list segments in the path (e.g. .myListName[0].) for uniqueness + // but drop brackets to avoid token limits + const filteredPaths = path + .filter((x) => /\[\d+\]/i.test(x)) + .map((x) => x.replaceAll(/[\[\]]/gi, '')); + let result = [...filteredPaths, props.parameter.displayName].join('_'); + + // Langchain requires the name to be <64 characters + if (filteredPaths.length > 1) { + // Prefer clipping the list names over the display name + result = result.slice(-63); + } else { + result = result.slice(0, 63); + } + + return result; +} + export function buildValueFromOverride( override: FromAIOverride, - props: Pick, + props: Pick, includeMarker: boolean, ) { const { extraPropValues, extraProps } = override; const marker = includeMarker ? `${FROM_AI_AUTO_GENERATED_MARKER} ` : ''; - const key = sanitizeFromAiParameterName(props.parameter.displayName); + const key = sanitizeFromAiParameterName(buildUniqueName(props)); const description = extraPropValues?.description?.toString() ?? extraProps.description.initialValue;