mirror of
https://github.com/langgenius/dify.git
synced 2026-11-13 23:06:40 +08:00
679 lines
18 KiB
TypeScript
679 lines
18 KiB
TypeScript
import type { PromptVariable } from '@/models/debug'
|
|
import type { UserInputFormItem } from '@/types/app'
|
|
import { SupportUploadFileTypes } from '@/app/components/workflow/types'
|
|
import {
|
|
formatBooleanInputs,
|
|
promptVariablesToUserInputsForm,
|
|
userInputsFormToPromptVariables,
|
|
} from './model-config'
|
|
|
|
const getTextInput = (item: UserInputFormItem | undefined) => {
|
|
if (!item || !('text-input' in item)) throw new Error('Expected text-input user input form item')
|
|
|
|
return item['text-input']
|
|
}
|
|
|
|
describe('Model Config Utilities', () => {
|
|
describe('userInputsFormToPromptVariables', () => {
|
|
it('should return empty array for null input', () => {
|
|
const result = userInputsFormToPromptVariables(null)
|
|
expect(result).toEqual([])
|
|
})
|
|
|
|
it('should convert text-input to string prompt variable', () => {
|
|
const userInputs: UserInputFormItem[] = [
|
|
{
|
|
'text-input': {
|
|
label: 'User Name',
|
|
variable: 'user_name',
|
|
required: true,
|
|
max_length: 100,
|
|
default: '',
|
|
hide: false,
|
|
},
|
|
},
|
|
]
|
|
|
|
const result = userInputsFormToPromptVariables(userInputs)
|
|
|
|
expect(result).toHaveLength(1)
|
|
expect(result[0]).toEqual({
|
|
key: 'user_name',
|
|
name: 'User Name',
|
|
required: true,
|
|
type: 'string',
|
|
max_length: 100,
|
|
options: [],
|
|
is_context_var: false,
|
|
hide: false,
|
|
default: '',
|
|
})
|
|
})
|
|
|
|
it('should convert paragraph to paragraph prompt variable', () => {
|
|
const userInputs: UserInputFormItem[] = [
|
|
{
|
|
paragraph: {
|
|
label: 'Description',
|
|
variable: 'description',
|
|
required: false,
|
|
max_length: 500,
|
|
default: '',
|
|
hide: false,
|
|
},
|
|
},
|
|
]
|
|
|
|
const result = userInputsFormToPromptVariables(userInputs)
|
|
|
|
expect(result[0]).toEqual({
|
|
key: 'description',
|
|
name: 'Description',
|
|
required: false,
|
|
type: 'paragraph',
|
|
max_length: 500,
|
|
options: [],
|
|
is_context_var: false,
|
|
hide: false,
|
|
default: '',
|
|
})
|
|
})
|
|
|
|
it('should convert number input to number prompt variable', () => {
|
|
const userInputs: UserInputFormItem[] = [
|
|
{
|
|
number: {
|
|
label: 'Age',
|
|
variable: 'age',
|
|
required: true,
|
|
default: '',
|
|
hide: false,
|
|
},
|
|
},
|
|
]
|
|
|
|
const result = userInputsFormToPromptVariables(userInputs)
|
|
|
|
expect(result[0]).toEqual({
|
|
key: 'age',
|
|
name: 'Age',
|
|
required: true,
|
|
type: 'number',
|
|
options: [],
|
|
hide: false,
|
|
default: '',
|
|
})
|
|
})
|
|
|
|
it('should convert checkbox to checkbox prompt variable', () => {
|
|
const userInputs: UserInputFormItem[] = [
|
|
{
|
|
checkbox: {
|
|
label: 'Accept Terms',
|
|
variable: 'accept_terms',
|
|
required: true,
|
|
default: '',
|
|
hide: false,
|
|
},
|
|
},
|
|
]
|
|
|
|
const result = userInputsFormToPromptVariables(userInputs)
|
|
|
|
expect(result[0]).toEqual({
|
|
key: 'accept_terms',
|
|
name: 'Accept Terms',
|
|
required: true,
|
|
type: 'checkbox',
|
|
options: [],
|
|
hide: false,
|
|
default: '',
|
|
})
|
|
})
|
|
|
|
it('should convert select input to select prompt variable', () => {
|
|
const userInputs: UserInputFormItem[] = [
|
|
{
|
|
select: {
|
|
label: 'Country',
|
|
variable: 'country',
|
|
required: true,
|
|
options: ['USA', 'Canada', 'Mexico'],
|
|
default: 'USA',
|
|
hide: false,
|
|
},
|
|
},
|
|
]
|
|
|
|
const result = userInputsFormToPromptVariables(userInputs)
|
|
|
|
expect(result[0]).toEqual({
|
|
key: 'country',
|
|
name: 'Country',
|
|
required: true,
|
|
type: 'select',
|
|
options: ['USA', 'Canada', 'Mexico'],
|
|
is_context_var: false,
|
|
hide: false,
|
|
default: 'USA',
|
|
})
|
|
})
|
|
|
|
it('should convert file input to file prompt variable', () => {
|
|
const userInputs: UserInputFormItem[] = [
|
|
{
|
|
file: {
|
|
label: 'Profile Picture',
|
|
variable: 'profile_pic',
|
|
required: false,
|
|
allowed_file_types: [SupportUploadFileTypes.image],
|
|
allowed_file_extensions: ['.jpg', '.png'],
|
|
allowed_file_upload_methods: ['local_file', 'remote_url'],
|
|
default: '',
|
|
hide: false,
|
|
},
|
|
},
|
|
]
|
|
|
|
const result = userInputsFormToPromptVariables(userInputs)
|
|
|
|
expect(result[0]).toEqual({
|
|
key: 'profile_pic',
|
|
name: 'Profile Picture',
|
|
required: false,
|
|
type: 'file',
|
|
config: {
|
|
allowed_file_types: ['image'],
|
|
allowed_file_extensions: ['.jpg', '.png'],
|
|
allowed_file_upload_methods: ['local_file', 'remote_url'],
|
|
number_limits: 1,
|
|
},
|
|
hide: false,
|
|
default: '',
|
|
})
|
|
})
|
|
|
|
it('should convert file-list input to file-list prompt variable', () => {
|
|
const userInputs: UserInputFormItem[] = [
|
|
{
|
|
'file-list': {
|
|
label: 'Documents',
|
|
variable: 'documents',
|
|
required: true,
|
|
allowed_file_types: [SupportUploadFileTypes.document],
|
|
allowed_file_extensions: ['.pdf', '.docx'],
|
|
allowed_file_upload_methods: ['local_file'],
|
|
max_length: 5,
|
|
default: '',
|
|
hide: false,
|
|
},
|
|
},
|
|
]
|
|
|
|
const result = userInputsFormToPromptVariables(userInputs)
|
|
|
|
expect(result[0]).toEqual({
|
|
key: 'documents',
|
|
name: 'Documents',
|
|
required: true,
|
|
type: 'file-list',
|
|
config: {
|
|
allowed_file_types: ['document'],
|
|
allowed_file_extensions: ['.pdf', '.docx'],
|
|
allowed_file_upload_methods: ['local_file'],
|
|
number_limits: 5,
|
|
},
|
|
hide: false,
|
|
default: '',
|
|
})
|
|
})
|
|
|
|
it.each([
|
|
[
|
|
{ file: { default: '', label: 'Attachment', variable: 'attachment' } },
|
|
{ number_limits: 1 },
|
|
],
|
|
[{ 'file-list': { label: 'Attachments', variable: 'attachments' } }, {}],
|
|
])('should allow publishing a loaded file input with omitted restrictions', (input, config) => {
|
|
const variables = userInputsFormToPromptVariables([input])
|
|
const form = promptVariablesToUserInputsForm(variables)
|
|
|
|
expect(variables[0]?.config).toEqual(config)
|
|
expect(Object.keys(form[0]!)).toEqual(Object.keys(input))
|
|
expect(userInputsFormToPromptVariables(form)[0]?.config).toEqual(config)
|
|
})
|
|
|
|
it('should convert external_data_tool to prompt variable', () => {
|
|
const userInputs: UserInputFormItem[] = [
|
|
{
|
|
external_data_tool: {
|
|
label: 'API Data',
|
|
variable: 'api_data',
|
|
type: 'api',
|
|
enabled: true,
|
|
required: false,
|
|
config: { endpoint: 'https://api.example.com' },
|
|
icon: 'api-icon',
|
|
icon_background: '#FF5733',
|
|
hide: false,
|
|
},
|
|
},
|
|
]
|
|
|
|
const result = userInputsFormToPromptVariables(userInputs)
|
|
|
|
expect(result[0]).toEqual({
|
|
key: 'api_data',
|
|
name: 'API Data',
|
|
required: false,
|
|
type: 'api',
|
|
enabled: true,
|
|
config: { endpoint: 'https://api.example.com' },
|
|
icon: 'api-icon',
|
|
icon_background: '#FF5733',
|
|
is_context_var: false,
|
|
hide: false,
|
|
})
|
|
})
|
|
|
|
it('should mark variable as context var when matching dataset_query_variable', () => {
|
|
const userInputs: UserInputFormItem[] = [
|
|
{
|
|
'text-input': {
|
|
label: 'Query',
|
|
variable: 'query',
|
|
required: true,
|
|
max_length: 200,
|
|
default: '',
|
|
hide: false,
|
|
},
|
|
},
|
|
]
|
|
|
|
const result = userInputsFormToPromptVariables(userInputs, 'query')
|
|
|
|
expect(result[0]!.is_context_var).toBe(true)
|
|
})
|
|
})
|
|
|
|
describe('promptVariablesToUserInputsForm', () => {
|
|
it('should convert string prompt variable to text-input', () => {
|
|
const promptVariables: PromptVariable[] = [
|
|
{
|
|
key: 'user_name',
|
|
name: 'User Name',
|
|
required: true,
|
|
type: 'string',
|
|
max_length: 100,
|
|
options: [],
|
|
},
|
|
]
|
|
|
|
const result = promptVariablesToUserInputsForm(promptVariables)
|
|
|
|
expect(result).toHaveLength(1)
|
|
expect(result[0]).toEqual({
|
|
'text-input': {
|
|
label: 'User Name',
|
|
variable: 'user_name',
|
|
required: true,
|
|
max_length: 100,
|
|
default: '',
|
|
hide: undefined,
|
|
},
|
|
})
|
|
})
|
|
|
|
it('should convert paragraph prompt variable to paragraph input', () => {
|
|
const promptVariables: PromptVariable[] = [
|
|
{
|
|
key: 'description',
|
|
name: 'Description',
|
|
required: false,
|
|
type: 'paragraph',
|
|
max_length: 500,
|
|
options: [],
|
|
},
|
|
]
|
|
|
|
const result = promptVariablesToUserInputsForm(promptVariables)
|
|
|
|
expect(result[0]).toEqual({
|
|
paragraph: {
|
|
label: 'Description',
|
|
variable: 'description',
|
|
required: false,
|
|
max_length: 500,
|
|
default: '',
|
|
hide: undefined,
|
|
},
|
|
})
|
|
})
|
|
|
|
it('should convert number prompt variable to number input', () => {
|
|
const promptVariables: PromptVariable[] = [
|
|
{
|
|
key: 'age',
|
|
name: 'Age',
|
|
required: true,
|
|
type: 'number',
|
|
options: [],
|
|
},
|
|
]
|
|
|
|
const result = promptVariablesToUserInputsForm(promptVariables)
|
|
|
|
expect(result[0]).toEqual({
|
|
number: {
|
|
label: 'Age',
|
|
variable: 'age',
|
|
required: true,
|
|
default: '',
|
|
hide: undefined,
|
|
},
|
|
})
|
|
})
|
|
|
|
it('should convert checkbox prompt variable to checkbox input', () => {
|
|
const promptVariables: PromptVariable[] = [
|
|
{
|
|
key: 'accept_terms',
|
|
name: 'Accept Terms',
|
|
required: true,
|
|
type: 'checkbox',
|
|
options: [],
|
|
},
|
|
]
|
|
|
|
const result = promptVariablesToUserInputsForm(promptVariables)
|
|
|
|
expect(result[0]).toEqual({
|
|
checkbox: {
|
|
label: 'Accept Terms',
|
|
variable: 'accept_terms',
|
|
required: true,
|
|
default: '',
|
|
hide: undefined,
|
|
},
|
|
})
|
|
})
|
|
|
|
it('should convert select prompt variable to select input', () => {
|
|
const promptVariables: PromptVariable[] = [
|
|
{
|
|
key: 'country',
|
|
name: 'Country',
|
|
required: true,
|
|
type: 'select',
|
|
options: ['USA', 'Canada', 'Mexico'],
|
|
default: 'USA',
|
|
},
|
|
]
|
|
|
|
const result = promptVariablesToUserInputsForm(promptVariables)
|
|
|
|
expect(result[0]).toEqual({
|
|
select: {
|
|
label: 'Country',
|
|
variable: 'country',
|
|
required: true,
|
|
options: ['USA', 'Canada', 'Mexico'],
|
|
default: 'USA',
|
|
hide: undefined,
|
|
},
|
|
})
|
|
})
|
|
|
|
it('should filter out variables with empty key or name', () => {
|
|
const promptVariables: PromptVariable[] = [
|
|
{
|
|
key: '',
|
|
name: 'Empty Key',
|
|
required: true,
|
|
type: 'string',
|
|
options: [],
|
|
},
|
|
{
|
|
key: 'valid',
|
|
name: '',
|
|
required: true,
|
|
type: 'string',
|
|
options: [],
|
|
},
|
|
{
|
|
key: ' ',
|
|
name: 'Whitespace Key',
|
|
required: true,
|
|
type: 'string',
|
|
options: [],
|
|
},
|
|
{
|
|
key: 'valid_key',
|
|
name: 'Valid Name',
|
|
required: true,
|
|
type: 'string',
|
|
options: [],
|
|
},
|
|
]
|
|
|
|
const result = promptVariablesToUserInputsForm(promptVariables)
|
|
|
|
expect(result).toHaveLength(1)
|
|
expect(getTextInput(result[0]).variable).toBe('valid_key')
|
|
})
|
|
|
|
it('should convert external data tool prompt variable', () => {
|
|
const promptVariables: PromptVariable[] = [
|
|
{
|
|
key: 'api_data',
|
|
name: 'API Data',
|
|
required: false,
|
|
type: 'api',
|
|
enabled: true,
|
|
config: { endpoint: 'https://api.example.com' },
|
|
icon: 'api-icon',
|
|
icon_background: '#FF5733',
|
|
},
|
|
]
|
|
|
|
const result = promptVariablesToUserInputsForm(promptVariables)
|
|
|
|
expect(result[0]).toEqual({
|
|
external_data_tool: {
|
|
label: 'API Data',
|
|
variable: 'api_data',
|
|
enabled: true,
|
|
type: 'api',
|
|
config: { endpoint: 'https://api.example.com' },
|
|
required: false,
|
|
icon: 'api-icon',
|
|
icon_background: '#FF5733',
|
|
hide: undefined,
|
|
},
|
|
})
|
|
})
|
|
|
|
it('should default required to true when not false', () => {
|
|
const promptVariables: PromptVariable[] = [
|
|
{
|
|
key: 'test1',
|
|
name: 'Test 1',
|
|
required: undefined,
|
|
type: 'string',
|
|
options: [],
|
|
},
|
|
{
|
|
key: 'test2',
|
|
name: 'Test 2',
|
|
required: false,
|
|
type: 'string',
|
|
options: [],
|
|
},
|
|
]
|
|
|
|
const result = promptVariablesToUserInputsForm(promptVariables)
|
|
|
|
expect(getTextInput(result[0]).required).toBe(true)
|
|
expect(getTextInput(result[1]).required).toBe(false)
|
|
})
|
|
})
|
|
|
|
describe('formatBooleanInputs', () => {
|
|
it('should return inputs unchanged when useInputs is null', () => {
|
|
const inputs = { key1: 'value1', key2: 'value2' }
|
|
const result = formatBooleanInputs(null, inputs)
|
|
expect(result).toEqual(inputs)
|
|
})
|
|
|
|
it('should convert boolean inputs to boolean type', () => {
|
|
const useInputs: PromptVariable[] = [
|
|
{
|
|
key: 'accept_terms',
|
|
name: 'Accept Terms',
|
|
required: true,
|
|
type: 'checkbox',
|
|
options: [],
|
|
},
|
|
{
|
|
key: 'subscribe',
|
|
name: 'Subscribe',
|
|
required: false,
|
|
type: 'checkbox',
|
|
options: [],
|
|
},
|
|
]
|
|
|
|
const inputs = {
|
|
accept_terms: 'true',
|
|
subscribe: '',
|
|
other_field: 'value',
|
|
}
|
|
|
|
const original = { ...inputs }
|
|
const result = formatBooleanInputs(useInputs, inputs)
|
|
expect(inputs).toEqual(original)
|
|
|
|
expect(result).toEqual({
|
|
accept_terms: true,
|
|
subscribe: false,
|
|
other_field: 'value',
|
|
})
|
|
})
|
|
|
|
it('should handle various truthy and falsy values', () => {
|
|
const useInputs: PromptVariable[] = [
|
|
{
|
|
key: 'bool1',
|
|
name: 'Bool 1',
|
|
required: true,
|
|
type: 'checkbox',
|
|
options: [],
|
|
},
|
|
{
|
|
key: 'bool2',
|
|
name: 'Bool 2',
|
|
required: true,
|
|
type: 'checkbox',
|
|
options: [],
|
|
},
|
|
{
|
|
key: 'bool3',
|
|
name: 'Bool 3',
|
|
required: true,
|
|
type: 'checkbox',
|
|
options: [],
|
|
},
|
|
{
|
|
key: 'bool4',
|
|
name: 'Bool 4',
|
|
required: true,
|
|
type: 'checkbox',
|
|
options: [],
|
|
},
|
|
]
|
|
|
|
const inputs = {
|
|
bool1: 1,
|
|
bool2: 0,
|
|
bool3: 'yes',
|
|
bool4: null,
|
|
}
|
|
|
|
const result = formatBooleanInputs(useInputs, inputs)
|
|
|
|
expect(result?.bool1).toBe(true)
|
|
expect(result?.bool2).toBe(false)
|
|
expect(result?.bool3).toBe(true)
|
|
expect(result?.bool4).toBe(false)
|
|
})
|
|
})
|
|
|
|
describe('publishing loaded form fields', () => {
|
|
it.each([
|
|
{
|
|
'text-input': {
|
|
label: 'Name',
|
|
variable: 'name',
|
|
required: true,
|
|
max_length: 50,
|
|
default: 'Alice',
|
|
hide: true,
|
|
},
|
|
},
|
|
{
|
|
paragraph: { label: 'Notes', variable: 'notes', required: false, default: 'Initial notes' },
|
|
},
|
|
{ number: { label: 'Count', variable: 'count', required: false, default: 0 } },
|
|
{ checkbox: { label: 'Enabled', variable: 'enabled', required: true, default: false } },
|
|
{
|
|
select: {
|
|
label: 'Choice',
|
|
variable: 'choice',
|
|
required: false,
|
|
options: ['A', 'B'],
|
|
default: 'B',
|
|
},
|
|
},
|
|
{
|
|
file: {
|
|
default: '',
|
|
label: 'Attachment',
|
|
variable: 'attachment',
|
|
required: true,
|
|
allowed_file_types: [SupportUploadFileTypes.image],
|
|
allowed_file_extensions: ['.png'],
|
|
allowed_file_upload_methods: ['local_file'],
|
|
},
|
|
},
|
|
{
|
|
'file-list': {
|
|
default: '',
|
|
label: 'Documents',
|
|
variable: 'documents',
|
|
required: false,
|
|
max_length: 5,
|
|
allowed_file_types: [SupportUploadFileTypes.document],
|
|
allowed_file_upload_methods: ['local_file', 'remote_url'],
|
|
},
|
|
},
|
|
{
|
|
json_object: {
|
|
label: 'JSON',
|
|
variable: 'json',
|
|
required: false,
|
|
default: '{"name":"Alice"}',
|
|
json_schema: { type: 'object', properties: { name: { type: 'string' } } },
|
|
},
|
|
},
|
|
] satisfies UserInputFormItem[])(
|
|
'preserves the field type, defaults, and restrictions for %j',
|
|
(input) => {
|
|
const variables = userInputsFormToPromptVariables([input])
|
|
const restored = promptVariablesToUserInputsForm(variables)
|
|
expect(restored).toMatchObject([input])
|
|
expect(userInputsFormToPromptVariables(restored)).toEqual(variables)
|
|
},
|
|
)
|
|
})
|
|
})
|