Let the placeholder flag alone decide, without requiring a textarea
buildTemplatedRichContentParts asked for x-ui-widget: textarea as well as
the flag, so a one-line field could not have its ${{...}} marked even when
its value really is interpolated. Whether a value is interpolated is a
property of the value, not of the control used to type it.
A no-op for everything that exists today: every field carrying the flag
gets it from LongText, whose widget defaults to textarea. It is what lets
LLMDescriptor.model declare the same thing without becoming a textarea.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
3ba14244c3
commit
a8cad0d3cd
|
|
@ -5,6 +5,7 @@ import {
|
|||
import { parentGroupLabel } from './node-utility';
|
||||
import {
|
||||
buildOrderedSchemaDisplay,
|
||||
buildTemplatedRichContentParts,
|
||||
buildSchemaEditableFieldDefinitions,
|
||||
buildSchemaFieldViewModel,
|
||||
buildSchemaRetrieverContext,
|
||||
|
|
@ -668,3 +669,55 @@ describe('optional groups', () => {
|
|||
});
|
||||
});
|
||||
|
||||
describe('buildTemplatedRichContentParts', () => {
|
||||
/** Splitting a value on its ${{...}} placeholders so the editor can mark them. */
|
||||
const splitParts = (content: string) =>
|
||||
content.split(/(\$\{\{[^}]*\}\})/).filter((part) => part.length > 0)
|
||||
.map((part) => ({ text: part, isDynamicInput: part.startsWith('${{') }));
|
||||
|
||||
function schemaWith(field: Record<string, unknown>) {
|
||||
return { type: 'object', properties: { subject: field } };
|
||||
}
|
||||
|
||||
it('splits a one-line field that says it takes placeholders', () => {
|
||||
// It used to require x-ui-widget: textarea as well, so a field drawn as anything else could
|
||||
// not declare that its value is interpolated - which is what LLMDescriptor.model needs.
|
||||
const schema = schemaWith({ type: 'string', 'x-ui-accept-variable-as-placeholder': true });
|
||||
|
||||
const parts = buildTemplatedRichContentParts(
|
||||
{ subject: 'model is ${{global.modelName}}' }, 'subject', schema, splitParts);
|
||||
|
||||
expect(parts).toEqual([
|
||||
{ text: 'model is ', isDynamicInput: false },
|
||||
{ text: '${{global.modelName}}', isDynamicInput: true }
|
||||
]);
|
||||
});
|
||||
|
||||
it('still splits a textarea, which is every field that declared it until now', () => {
|
||||
const schema = schemaWith({
|
||||
type: 'string',
|
||||
'x-ui-widget': 'textarea',
|
||||
'x-ui-accept-variable-as-placeholder': true
|
||||
});
|
||||
|
||||
const parts = buildTemplatedRichContentParts(
|
||||
{ subject: 'Summarise ${{text}}' }, 'subject', schema, splitParts);
|
||||
|
||||
expect(parts.some((part) => part.isDynamicInput)).toBe(true);
|
||||
});
|
||||
|
||||
it('leaves a field that never claimed to take placeholders as one flat part', () => {
|
||||
const schema = schemaWith({ type: 'string' });
|
||||
|
||||
const parts = buildTemplatedRichContentParts(
|
||||
{ subject: 'literally ${{not a placeholder}}' }, 'subject', schema, splitParts);
|
||||
|
||||
expect(parts).toEqual([{ text: 'literally ${{not a placeholder}}', isDynamicInput: false }]);
|
||||
});
|
||||
|
||||
it('has nothing to split when the value is empty', () => {
|
||||
const schema = schemaWith({ type: 'string', 'x-ui-accept-variable-as-placeholder': true });
|
||||
|
||||
expect(buildTemplatedRichContentParts({ subject: ' ' }, 'subject', schema, splitParts)).toEqual([]);
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -707,7 +707,11 @@ export function buildTemplatedRichContentParts(
|
|||
if (!content) return [];
|
||||
|
||||
const ui = getSchemaPathUiMeta(root, path);
|
||||
if (ui.widget === 'textarea' && ui.acceptVariableAsPlaceholder) {
|
||||
// The flag alone decides: whether a value is interpolated is a property of the value, not of the
|
||||
// control used to type it. Requiring a textarea too meant a one-line field could not say it
|
||||
// takes ${{...}} - and every field that says so today is a textarea anyway, so nothing that
|
||||
// already worked changes.
|
||||
if (ui.acceptVariableAsPlaceholder) {
|
||||
return splitParts(content);
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue