Lifei/UI parameter input (#5222)

This commit is contained in:
Lifei Zhou
2025-10-20 10:05:54 +11:00
committed by GitHub
parent 6c3e07e9c7
commit 8b53a5696e
27 changed files with 717 additions and 1250 deletions
+2 -2
View File
@@ -158,7 +158,7 @@ function BaseChatContent({
const {
recipe,
recipeId,
recipeParameters,
recipeParameterValues,
filteredParameters,
initialPrompt,
isParameterModalOpen,
@@ -338,7 +338,7 @@ function BaseChatContent({
append={(text: string) => appendWithTracking(text)}
activities={Array.isArray(recipe.activities) ? recipe.activities : null}
title={recipe.title}
parameterValues={recipeParameters || {}}
parameterValues={recipeParameterValues || {}}
/>
</div>
)}
+2 -2
View File
@@ -98,7 +98,7 @@ function BaseChatContent({
messageHistoryIndex: 0,
messages: [],
recipe: null,
recipeParameters: null,
recipeParameterValues: null,
};
setChat(emptyChat);
@@ -111,7 +111,7 @@ function BaseChatContent({
messageHistoryIndex: 0,
messages: conversation,
recipe: null,
recipeParameters: null,
recipeParameterValues: null,
};
setChat(loadedChat);
@@ -7,7 +7,6 @@ import JsonSchemaEditor from './JsonSchemaEditor';
import InstructionsEditor from './InstructionsEditor';
import { Button } from '../../ui/button';
import { RecipeFormApi } from './recipeFormSchema';
import { extractTemplateVariables } from '../../../utils/providerUtils';
// Type for field API to avoid linting issues - use any to bypass complex type constraints
// eslint-disable-next-line @typescript-eslint/no-explicit-any
@@ -25,6 +24,27 @@ interface RecipeFormFieldsProps {
onJsonSchemaChange?: (value: string) => void;
}
export const extractTemplateVariables = (content: string): string[] => {
const templateVarRegex = /\{\{(.*?)\}\}/g;
const variables: string[] = [];
let match;
while ((match = templateVarRegex.exec(content)) !== null) {
const variable = match[1].trim();
if (variable && !variables.includes(variable)) {
// Filter out complex variables that aren't valid parameter names
// This matches the backend logic in filter_complex_variables()
const validVarRegex = /^\s*[a-zA-Z_][a-zA-Z0-9_]*\s*$/;
if (validVarRegex.test(variable)) {
variables.push(variable);
}
}
}
return variables;
};
export function RecipeFormFields({
form,
onTitleChange,
@@ -3,7 +3,7 @@ import { render, screen } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import { useForm } from '@tanstack/react-form';
import { RecipeFormFields } from '../RecipeFormFields';
import { RecipeFormFields, extractTemplateVariables } from '../RecipeFormFields';
import { type RecipeFormData } from '../recipeFormSchema';
describe('RecipeFormFields', () => {
@@ -683,4 +683,132 @@ describe('RecipeFormFields', () => {
}
});
});
describe('extractTemplateVariables', () => {
it('should extract simple template variables', () => {
const content = 'Hello {{name}}, welcome to {{app}}!';
const result = extractTemplateVariables(content);
expect(result).toEqual(['name', 'app']);
});
it('should extract variables with underscores', () => {
const content = 'User: {{user_name}}, ID: {{user_id}}';
const result = extractTemplateVariables(content);
expect(result).toEqual(['user_name', 'user_id']);
});
it('should extract variables that start with underscore', () => {
const content = 'Private: {{_private}}, Internal: {{__internal}}';
const result = extractTemplateVariables(content);
expect(result).toEqual(['_private', '__internal']);
});
it('should handle variables with numbers', () => {
const content = 'Item {{item1}}, Version {{version2_0}}';
const result = extractTemplateVariables(content);
expect(result).toEqual(['item1', 'version2_0']);
});
it('should trim whitespace from variables', () => {
const content = 'Hello {{ name }}, welcome to {{ app }}!';
const result = extractTemplateVariables(content);
expect(result).toEqual(['name', 'app']);
});
it('should ignore invalid variable names with spaces', () => {
const content = 'Invalid: {{user name}}, Valid: {{username}}';
const result = extractTemplateVariables(content);
expect(result).toEqual(['username']);
});
it('should ignore invalid variable names with dots', () => {
const content = 'Invalid: {{user.name}}, Valid: {{user_name}}';
const result = extractTemplateVariables(content);
expect(result).toEqual(['user_name']);
});
it('should ignore invalid variable names with pipes', () => {
const content = 'Invalid: {{name|upper}}, Valid: {{name}}';
const result = extractTemplateVariables(content);
expect(result).toEqual(['name']);
});
it('should ignore invalid variable names with special characters', () => {
const content = 'Invalid: {{user@name}}, {{user-name}}, {{user$name}}, Valid: {{username}}';
const result = extractTemplateVariables(content);
expect(result).toEqual(['username']);
});
it('should ignore variables starting with numbers', () => {
const content = 'Invalid: {{1name}}, {{2user}}, Valid: {{name1}}';
const result = extractTemplateVariables(content);
expect(result).toEqual(['name1']);
});
it('should remove duplicates', () => {
const content = 'Hello {{name}}, goodbye {{name}}, welcome {{app}}, use {{app}}';
const result = extractTemplateVariables(content);
expect(result).toEqual(['name', 'app']);
});
it('should handle empty content', () => {
const content = '';
const result = extractTemplateVariables(content);
expect(result).toEqual([]);
});
it('should handle content with no variables', () => {
const content = 'This is just plain text with no variables.';
const result = extractTemplateVariables(content);
expect(result).toEqual([]);
});
it('should handle single braces (not template variables)', () => {
const content = 'This {is} not a {template} variable but {{this}} is.';
const result = extractTemplateVariables(content);
expect(result).toEqual(['this']);
});
it('should handle malformed template syntax', () => {
const content = 'Malformed: {{{name}}}, {{name}}, {name}}';
const result = extractTemplateVariables(content);
expect(result).toEqual(['name']);
});
it('should handle empty variable names', () => {
const content = 'Empty: {{}}, Valid: {{name}}';
const result = extractTemplateVariables(content);
expect(result).toEqual(['name']);
});
it('should handle variables with only whitespace', () => {
const content = 'Whitespace: {{ }}, Valid: {{name}}';
const result = extractTemplateVariables(content);
expect(result).toEqual(['name']);
});
it('should ignore complex template expressions with dots and pipes', () => {
const content =
'Complex: {{steps.fetch_payment_data.data.payments.totalEdgeCount | number_format}}, Valid: {{simple_param}}';
const result = extractTemplateVariables(content);
expect(result).toEqual(['simple_param']);
});
it('should handle complex mixed content', () => {
const content = `
Welcome {{user_name}}!
Your account details:
- ID: {{user_id}}
- Email: {{email_address}}
- Invalid: {{user.email}}
- Invalid: {{user name}}
- Invalid: {{1invalid}}
Thank you for using {{app_name}}!
`;
const result = extractTemplateVariables(content);
expect(result).toEqual(['user_name', 'user_id', 'email_address', 'app_name']);
});
});
});