mirror of
https://git.mirrors.martin98.com/https://github.com/langgenius/dify.git
synced 2025-08-14 05:45:55 +08:00
feat: add obj and array type support
This commit is contained in:
parent
dded6001e2
commit
e42f84f723
@ -19,6 +19,8 @@ export enum FormTypeEnum {
|
|||||||
toolSelector = 'tool-selector',
|
toolSelector = 'tool-selector',
|
||||||
multiToolSelector = 'array[tools]',
|
multiToolSelector = 'array[tools]',
|
||||||
appSelector = 'app-selector',
|
appSelector = 'app-selector',
|
||||||
|
object = 'object',
|
||||||
|
array = 'array',
|
||||||
}
|
}
|
||||||
|
|
||||||
export type FormOption = {
|
export type FormOption = {
|
||||||
|
@ -3,6 +3,7 @@ import { useTranslation } from 'react-i18next'
|
|||||||
import produce from 'immer'
|
import produce from 'immer'
|
||||||
import {
|
import {
|
||||||
RiArrowRightUpLine,
|
RiArrowRightUpLine,
|
||||||
|
RiBracesLine,
|
||||||
} from '@remixicon/react'
|
} from '@remixicon/react'
|
||||||
import Tooltip from '@/app/components/base/tooltip'
|
import Tooltip from '@/app/components/base/tooltip'
|
||||||
import Switch from '@/app/components/base/switch'
|
import Switch from '@/app/components/base/switch'
|
||||||
@ -22,6 +23,8 @@ import type { ToolVarInputs } from '@/app/components/workflow/nodes/tool/types'
|
|||||||
import { VarType as VarKindType } from '@/app/components/workflow/nodes/tool/types'
|
import { VarType as VarKindType } from '@/app/components/workflow/nodes/tool/types'
|
||||||
import { VarType } from '@/app/components/workflow/types'
|
import { VarType } from '@/app/components/workflow/types'
|
||||||
import cn from '@/utils/classnames'
|
import cn from '@/utils/classnames'
|
||||||
|
import { useBoolean } from 'ahooks'
|
||||||
|
import SchemaModal from './schema-modal'
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
value: Record<string, any>
|
value: Record<string, any>
|
||||||
@ -133,7 +136,12 @@ const ReasoningConfigForm: React.FC<Props> = ({
|
|||||||
}
|
}
|
||||||
}, [onChange, value])
|
}, [onChange, value])
|
||||||
|
|
||||||
const renderField = (schema: any) => {
|
const [isShowSchema, {
|
||||||
|
setTrue: showSchema,
|
||||||
|
setFalse: hideSchema,
|
||||||
|
}] = useBoolean(false)
|
||||||
|
|
||||||
|
const renderField = (schema: any, showSchema: () => void) => {
|
||||||
const {
|
const {
|
||||||
variable,
|
variable,
|
||||||
label,
|
label,
|
||||||
@ -149,26 +157,56 @@ const ReasoningConfigForm: React.FC<Props> = ({
|
|||||||
popupContent={<div className='w-[200px]'>
|
popupContent={<div className='w-[200px]'>
|
||||||
{tooltip[language] || tooltip.en_US}
|
{tooltip[language] || tooltip.en_US}
|
||||||
</div>}
|
</div>}
|
||||||
triggerClassName='ml-1 w-4 h-4'
|
triggerClassName='ml-0.5 w-4 h-4'
|
||||||
asChild={false} />
|
asChild={false} />
|
||||||
))
|
))
|
||||||
const varInput = value[variable].value
|
const varInput = value[variable].value
|
||||||
const isNumber = type === FormTypeEnum.textNumber
|
const isNumber = type === FormTypeEnum.textNumber
|
||||||
const isSelect = type === FormTypeEnum.select
|
const isSelect = type === FormTypeEnum.select
|
||||||
const isFile = type === FormTypeEnum.file || type === FormTypeEnum.files
|
const isFile = type === FormTypeEnum.file || type === FormTypeEnum.files
|
||||||
|
const isObject = type === FormTypeEnum.object
|
||||||
|
const isArray = type === FormTypeEnum.array
|
||||||
|
const isShowSchemaTooltip = isObject || isArray
|
||||||
const isAppSelector = type === FormTypeEnum.appSelector
|
const isAppSelector = type === FormTypeEnum.appSelector
|
||||||
const isModelSelector = type === FormTypeEnum.modelSelector
|
const isModelSelector = type === FormTypeEnum.modelSelector
|
||||||
// const isToolSelector = type === FormTypeEnum.toolSelector
|
// const isToolSelector = type === FormTypeEnum.toolSelector
|
||||||
const isString = !isNumber && !isSelect && !isFile && !isAppSelector && !isModelSelector
|
const isString = !isNumber && !isSelect && !isFile && !isAppSelector && !isModelSelector && !isObject && !isArray
|
||||||
|
const valueType = (() => {
|
||||||
|
if (isNumber) return VarType.number
|
||||||
|
if (isSelect) return VarType.string
|
||||||
|
if (isFile) return VarType.file
|
||||||
|
if (isObject) return VarType.object
|
||||||
|
if (isArray) return VarType.array
|
||||||
|
|
||||||
|
return VarType.string
|
||||||
|
})()
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div key={variable} className='space-y-1'>
|
<div key={variable} className='space-y-1'>
|
||||||
<div className='system-sm-semibold flex items-center justify-between py-2 text-text-secondary'>
|
<div className='system-sm-semibold flex items-center justify-between py-2 text-text-secondary'>
|
||||||
<div className='flex items-center space-x-2'>
|
<div className='flex items-center'>
|
||||||
<span className={cn('code-sm-semibold text-text-secondary')}>{label[language] || label.en_US}</span>
|
<span className={cn('code-sm-semibold text-text-secondary')}>{label[language] || label.en_US}</span>
|
||||||
{required && (
|
{required && (
|
||||||
<span className='ml-1 text-red-500'>*</span>
|
<span className='ml-1 text-red-500'>*</span>
|
||||||
)}
|
)}
|
||||||
{tooltipContent}
|
{tooltipContent}
|
||||||
|
<span className='system-xs-regular mx-1 text-text-quaternary'>·</span>
|
||||||
|
<span className='system-xs-regular text-text-tertiary'>{valueType}</span>
|
||||||
|
{!isShowSchemaTooltip && (
|
||||||
|
<Tooltip
|
||||||
|
popupContent={<div className='system-xs-medium w-[200px] text-text-secondary'>
|
||||||
|
Click to view parameter schema
|
||||||
|
</div>}
|
||||||
|
asChild={false}>
|
||||||
|
<div
|
||||||
|
className='ml-0.5 cursor-pointer rounded-[4px] p-px text-text-tertiary hover:bg-state-base-hover hover:text-text-secondary'
|
||||||
|
onClick={showSchema}
|
||||||
|
>
|
||||||
|
<RiBracesLine className='size-3.5'/>
|
||||||
|
</div>
|
||||||
|
</Tooltip>
|
||||||
|
)}
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
<div className='flex cursor-pointer items-center gap-1 rounded-[6px] border border-divider-subtle bg-background-default-lighter px-2 py-1 hover:bg-state-base-hover' onClick={() => handleAutomatic(variable, !auto)}>
|
<div className='flex cursor-pointer items-center gap-1 rounded-[6px] border border-divider-subtle bg-background-default-lighter px-2 py-1 hover:bg-state-base-hover' onClick={() => handleAutomatic(variable, !auto)}>
|
||||||
<span className='system-xs-medium text-text-secondary'>{t('plugin.detailPanel.toolSelector.auto')}</span>
|
<span className='system-xs-medium text-text-secondary'>{t('plugin.detailPanel.toolSelector.auto')}</span>
|
||||||
@ -220,7 +258,7 @@ const ReasoningConfigForm: React.FC<Props> = ({
|
|||||||
schema={schema}
|
schema={schema}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
{isFile && (
|
{(isFile || isObject || isArray) && (
|
||||||
<VarReferencePicker
|
<VarReferencePicker
|
||||||
zIndex={1001}
|
zIndex={1001}
|
||||||
readonly={false}
|
readonly={false}
|
||||||
@ -229,7 +267,15 @@ const ReasoningConfigForm: React.FC<Props> = ({
|
|||||||
value={varInput?.value || []}
|
value={varInput?.value || []}
|
||||||
onChange={handleFileChange(variable)}
|
onChange={handleFileChange(variable)}
|
||||||
defaultVarKindType={VarKindType.variable}
|
defaultVarKindType={VarKindType.variable}
|
||||||
filterVar={(varPayload: Var) => varPayload.type === VarType.file || varPayload.type === VarType.arrayFile}
|
filterVar={(varPayload: Var) => {
|
||||||
|
if(isFile)
|
||||||
|
return varPayload.type === VarType.file || varPayload.type === VarType.arrayFile
|
||||||
|
if(isObject)
|
||||||
|
return varPayload.type === VarType.object
|
||||||
|
if(isArray)
|
||||||
|
return [VarType.array, VarType.arrayNumber, VarType.arrayString, VarType.arrayObject, VarType.arrayFile].includes(varPayload.type)
|
||||||
|
return true
|
||||||
|
}}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
{isAppSelector && (
|
{isAppSelector && (
|
||||||
@ -267,7 +313,13 @@ const ReasoningConfigForm: React.FC<Props> = ({
|
|||||||
}
|
}
|
||||||
return (
|
return (
|
||||||
<div className='space-y-3 px-4 py-2'>
|
<div className='space-y-3 px-4 py-2'>
|
||||||
{schemas.map(schema => renderField(schema))}
|
{!isShowSchema && schemas.map(schema => renderField(schema, showSchema))}
|
||||||
|
{isShowSchema && (
|
||||||
|
<SchemaModal
|
||||||
|
isShow={isShowSchema}
|
||||||
|
onClose={hideSchema}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,127 @@
|
|||||||
|
'use client'
|
||||||
|
import type { FC } from 'react'
|
||||||
|
import React from 'react'
|
||||||
|
import Modal from '@/app/components/base/modal'
|
||||||
|
import VisualEditor from '@/app/components/workflow/nodes/llm/components/json-schema-config-modal/visual-editor'
|
||||||
|
import type { SchemaRoot } from '@/app/components/workflow/nodes/llm/types'
|
||||||
|
import { Type } from '@/app/components/workflow/nodes/llm/types'
|
||||||
|
import { MittProvider, VisualEditorContextProvider } from '@/app/components/workflow/nodes/llm/components/json-schema-config-modal/visual-editor/context'
|
||||||
|
import { useTranslation } from 'react-i18next'
|
||||||
|
import { RiCloseLine } from '@remixicon/react'
|
||||||
|
|
||||||
|
const testSchema: SchemaRoot = {
|
||||||
|
type: Type.object,
|
||||||
|
properties: {
|
||||||
|
after: {
|
||||||
|
type: Type.string,
|
||||||
|
description: 'The ID of the existing block that the new block should be appended after. If not provided, content will be appended at the end of the page.',
|
||||||
|
},
|
||||||
|
content_block: {
|
||||||
|
type: Type.object,
|
||||||
|
properties: {
|
||||||
|
block_property: {
|
||||||
|
type: Type.string,
|
||||||
|
description: 'The block property of the block to be added. Possible property are `paragraph`,`heading_1`,`heading_2`,`heading_3`,`callout`,`todo`,`toggle`,`quote`, `bulleted_list_item`, `numbered_list_item`, other properties possible are `file`,`image`,`video` (link required).',
|
||||||
|
},
|
||||||
|
bold: {
|
||||||
|
type: Type.boolean,
|
||||||
|
description: 'Indicates if the text is bold.',
|
||||||
|
},
|
||||||
|
code: {
|
||||||
|
type: Type.boolean,
|
||||||
|
description: 'Indicates if the text is formatted as code.',
|
||||||
|
},
|
||||||
|
color: {
|
||||||
|
type: Type.string,
|
||||||
|
description: 'The color of the text background or text itself.',
|
||||||
|
},
|
||||||
|
content: {
|
||||||
|
anyOf: [
|
||||||
|
{
|
||||||
|
type: Type.string,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
enum: [
|
||||||
|
'null',
|
||||||
|
],
|
||||||
|
nullable: true,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
description: 'The textual content of the rich text object. Required for paragraph, heading_1, heading_2, heading_3, callout, todo, toggle, quote.',
|
||||||
|
},
|
||||||
|
italic: {
|
||||||
|
type: Type.boolean,
|
||||||
|
description: 'Indicates if the text is italic.',
|
||||||
|
},
|
||||||
|
link: {
|
||||||
|
type: Type.string,
|
||||||
|
description: 'The URL of the rich text object or the file to be uploaded or image/video link',
|
||||||
|
},
|
||||||
|
strikethrough: {
|
||||||
|
type: Type.boolean,
|
||||||
|
description: 'Indicates if the text has strikethrough.',
|
||||||
|
},
|
||||||
|
underline: {
|
||||||
|
type: Type.boolean,
|
||||||
|
description: 'Indicates if the text is underlined.',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
additionalProperties: false,
|
||||||
|
description: 'Child content to append to a page.',
|
||||||
|
},
|
||||||
|
parent_block_id: {
|
||||||
|
type: Type.string,
|
||||||
|
description: 'The ID of the page which the children will be added.',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
required: [
|
||||||
|
'content_block',
|
||||||
|
'parent_block_id',
|
||||||
|
],
|
||||||
|
additionalProperties: false,
|
||||||
|
}
|
||||||
|
type Props = {
|
||||||
|
isShow: boolean
|
||||||
|
onClose: () => void
|
||||||
|
}
|
||||||
|
|
||||||
|
const SchemaModal: FC<Props> = ({
|
||||||
|
isShow,
|
||||||
|
onClose,
|
||||||
|
}) => {
|
||||||
|
const { t } = useTranslation()
|
||||||
|
return (
|
||||||
|
<Modal
|
||||||
|
isShow={isShow}
|
||||||
|
onClose={onClose}
|
||||||
|
className='max-w-[960px] p-0'
|
||||||
|
wrapperClassName='z-[9999]'
|
||||||
|
>
|
||||||
|
<div className='pb-6'>
|
||||||
|
{/* Header */}
|
||||||
|
<div className='relative flex p-6 pb-3 pr-14'>
|
||||||
|
<div className='title-2xl-semi-bold grow truncate text-text-primary'>
|
||||||
|
{t('workflow.nodes.llm.jsonSchema.title')}
|
||||||
|
</div>
|
||||||
|
<div className='absolute right-5 top-5 flex h-8 w-8 items-center justify-center p-1.5' onClick={onClose}>
|
||||||
|
<RiCloseLine className='h-[18px] w-[18px] text-text-tertiary' />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{/* Content */}
|
||||||
|
<div className='flex max-h-[700px] overflow-y-auto px-6 py-2'>
|
||||||
|
<MittProvider>
|
||||||
|
<VisualEditorContextProvider>
|
||||||
|
<VisualEditor
|
||||||
|
schema={testSchema}
|
||||||
|
onChange={(schema: SchemaRoot) => {
|
||||||
|
console.log('Schema changed:', schema)
|
||||||
|
}}
|
||||||
|
></VisualEditor>
|
||||||
|
</VisualEditorContextProvider>
|
||||||
|
</MittProvider>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</Modal>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
export default React.memo(SchemaModal)
|
Loading…
x
Reference in New Issue
Block a user