Support richer schema visibility conditions

This commit is contained in:
Lucio Lelii 2026-03-13 16:51:41 +01:00
parent b24b8177a9
commit 34f400ca40
2 changed files with 46 additions and 9 deletions

View File

@ -100,8 +100,13 @@ describe('node-utility', () => {
field: 'useLlm',
equals: 'true'
});
expect(readUiConditionRule({ field: 'method', in: ['POST', 'PUT'] })).toEqual({
field: 'method',
in: ['POST', 'PUT']
});
expect(readUiConditionRule({ field: '', equals: 'true' })).toBeNull();
expect(readUiConditionRule({ field: 'useLlm', equals: true })).toBeNull();
expect(readUiConditionRule({ field: 'method', in: [] })).toBeNull();
});
it('readUiGroup normalizes logical group labels', () => {
@ -123,6 +128,9 @@ describe('node-utility', () => {
expect(evaluateUiConditionRule({ field: 'useLlm', equals: 'false' }, config, resolveFieldSchema)).toBe(false);
expect(evaluateUiConditionRule({ field: 'retries', equals: '3' }, config, resolveFieldSchema)).toBe(true);
expect(evaluateUiConditionRule({ field: 'provider', equals: 'openai' }, config, resolveFieldSchema)).toBe(true);
expect(evaluateUiConditionRule({ field: 'provider', in: ['openai', 'anthropic'] }, config, resolveFieldSchema)).toBe(true);
expect(evaluateUiConditionRule({ field: 'retries', in: ['1', '3'] }, config, resolveFieldSchema)).toBe(true);
expect(evaluateUiConditionRule({ field: 'useLlm', in: ['false', 'true'] }, config, resolveFieldSchema)).toBe(true);
});
it('splitTemplatedTextParts identifies dynamic placeholders', () => {

View File

@ -1,5 +1,7 @@
export type TemplatedTextPart = { text: string; isDynamicInput: boolean };
export type UiConditionRule = { field: string; equals: string };
export type UiConditionRule =
| { field: string; equals: string }
| { field: string; in: string[] };
export function toStringOrNull(value: unknown): string | null {
if (typeof value === 'string' && value.trim().length > 0) return value;
@ -74,13 +76,25 @@ export function readUiConditionRule(value: unknown): UiConditionRule | null {
const field = (value as Record<string, unknown>)['field'];
const equals = (value as Record<string, unknown>)['equals'];
const includes = (value as Record<string, unknown>)['in'];
if (typeof field !== 'string' || field.trim().length === 0) return null;
if (typeof equals !== 'string') return null;
if (typeof equals === 'string') {
return {
field: field.trim(),
equals
};
}
return {
field: field.trim(),
equals
};
if (Array.isArray(includes)) {
const values = includes.filter((item): item is string => typeof item === 'string');
if (!values.length) return null;
return {
field: field.trim(),
in: values
};
}
return null;
}
export function readUiGroup(value: unknown): string | null {
@ -107,17 +121,32 @@ export function evaluateUiConditionRule(
const schema = resolveFieldSchema?.(rule.field);
const schemaType = schema?.['type'];
const type = typeof schemaType === 'string' ? schemaType : null;
const expectedValues = 'in' in rule ? rule.in : null;
const expectedValue = 'equals' in rule ? rule.equals : null;
if (type === 'boolean' || typeof actualValue === 'boolean') {
return actualValue === parseBooleanCondition(rule.equals);
if (expectedValues) {
return expectedValues.some((value) => actualValue === parseBooleanCondition(value));
}
return expectedValue != null && actualValue === parseBooleanCondition(expectedValue);
}
if (type === 'number' || type === 'integer' || typeof actualValue === 'number') {
const expected = Number(rule.equals);
if (expectedValues) {
return expectedValues
.map((value) => Number(value))
.filter((value) => Number.isFinite(value))
.some((value) => actualValue === value);
}
const expected = Number(expectedValue);
return Number.isFinite(expected) && actualValue === expected;
}
return String(actualValue ?? '') === rule.equals;
if (expectedValues) {
return expectedValues.includes(String(actualValue ?? ''));
}
return expectedValue != null && String(actualValue ?? '') === expectedValue;
}
function parseBooleanCondition(value: string): boolean {