mirror of
https://github.com/langgenius/dify.git
synced 2026-02-24 02:01:55 -05:00
feat: Add file type support to LLM node JSON schema editor
This commit is contained in:
@@ -60,10 +60,7 @@ import {
|
||||
import { VAR_REGEX } from '@/config'
|
||||
import { AppModeEnum } from '@/types/app'
|
||||
import { OUTPUT_FILE_SUB_VARIABLES } from '../../../constants'
|
||||
import {
|
||||
|
||||
Type,
|
||||
} from '../../../llm/types'
|
||||
import { FILE_REF_FORMAT, Type } from '../../../llm/types'
|
||||
import { VarType as ToolVarType } from '../../../tool/types'
|
||||
|
||||
export const isSystemVar = (valueSelector: ValueSelector) => {
|
||||
@@ -122,7 +119,16 @@ export const inputVarTypeToVarType = (type: InputVarType): VarType => {
|
||||
)
|
||||
}
|
||||
|
||||
const structTypeToVarType = (type: Type, isArray?: boolean): VarType => {
|
||||
const structTypeToVarType = (
|
||||
type: Type,
|
||||
isArray?: boolean,
|
||||
format?: string,
|
||||
itemsFormat?: string,
|
||||
): VarType => {
|
||||
if (isArray && itemsFormat === FILE_REF_FORMAT)
|
||||
return VarType.arrayFile
|
||||
if (!isArray && format === FILE_REF_FORMAT)
|
||||
return VarType.file
|
||||
if (isArray) {
|
||||
return (
|
||||
(
|
||||
@@ -175,6 +181,7 @@ const findExceptVarInStructuredProperties = (
|
||||
const isObj = item.type === Type.object
|
||||
const isArray = item.type === Type.array
|
||||
const arrayType = item.items?.type
|
||||
const arrayFormat = item.items?.format
|
||||
|
||||
if (
|
||||
!isObj
|
||||
@@ -184,6 +191,8 @@ const findExceptVarInStructuredProperties = (
|
||||
type: structTypeToVarType(
|
||||
isArray ? arrayType! : item.type,
|
||||
isArray,
|
||||
item.format,
|
||||
arrayFormat,
|
||||
),
|
||||
},
|
||||
[key],
|
||||
@@ -215,6 +224,7 @@ const findExceptVarInStructuredOutput = (
|
||||
const isObj = item.type === Type.object
|
||||
const isArray = item.type === Type.array
|
||||
const arrayType = item.items?.type
|
||||
const arrayFormat = item.items?.format
|
||||
if (
|
||||
!isObj
|
||||
&& !filterVar(
|
||||
@@ -223,6 +233,8 @@ const findExceptVarInStructuredOutput = (
|
||||
type: structTypeToVarType(
|
||||
isArray ? arrayType! : item.type,
|
||||
isArray,
|
||||
item.format,
|
||||
arrayFormat,
|
||||
),
|
||||
},
|
||||
[key],
|
||||
@@ -1144,8 +1156,14 @@ export const getVarType = ({
|
||||
return
|
||||
|
||||
currProperties = currProperties.properties[key]
|
||||
if (isLast)
|
||||
type = structTypeToVarType(currProperties?.type)
|
||||
if (isLast) {
|
||||
if (currProperties?.format === FILE_REF_FORMAT)
|
||||
type = VarType.file
|
||||
else if (currProperties?.type === Type.array && currProperties?.items?.format === FILE_REF_FORMAT)
|
||||
type = VarType.arrayFile
|
||||
else
|
||||
type = structTypeToVarType(currProperties?.type)
|
||||
}
|
||||
})
|
||||
return type
|
||||
}
|
||||
@@ -1946,15 +1964,20 @@ const varToValueSelectorList = (
|
||||
Object.keys(
|
||||
(v.children as StructuredOutput)?.schema?.properties || {},
|
||||
).forEach((key) => {
|
||||
const type = (v.children as StructuredOutput)?.schema?.properties[key].type
|
||||
const schemaProperty = (v.children as StructuredOutput)?.schema?.properties[key]
|
||||
const type = schemaProperty?.type
|
||||
const isArray = type === Type.array
|
||||
const arrayType = (v.children as StructuredOutput)?.schema?.properties[
|
||||
key
|
||||
].items?.type
|
||||
const arrayType = schemaProperty?.items?.type
|
||||
const arrayFormat = schemaProperty?.items?.format
|
||||
varToValueSelectorList(
|
||||
{
|
||||
variable: key,
|
||||
type: structTypeToVarType(isArray ? arrayType! : type, isArray),
|
||||
type: structTypeToVarType(
|
||||
isArray ? arrayType! : type,
|
||||
isArray,
|
||||
schemaProperty?.format,
|
||||
arrayFormat,
|
||||
),
|
||||
},
|
||||
[...parentValueSelector, v.variable],
|
||||
res,
|
||||
|
||||
@@ -44,17 +44,21 @@ const TYPE_OPTIONS = [
|
||||
{ value: Type.number, text: 'number' },
|
||||
{ value: Type.boolean, text: 'boolean' },
|
||||
{ value: Type.object, text: 'object' },
|
||||
{ value: Type.file, text: 'file' },
|
||||
{ value: ArrayType.string, text: 'array[string]' },
|
||||
{ value: ArrayType.number, text: 'array[number]' },
|
||||
{ value: ArrayType.object, text: 'array[object]' },
|
||||
{ value: ArrayType.file, text: 'array[file]' },
|
||||
]
|
||||
|
||||
const MAXIMUM_DEPTH_TYPE_OPTIONS = [
|
||||
{ value: Type.string, text: 'string' },
|
||||
{ value: Type.number, text: 'number' },
|
||||
{ value: Type.boolean, text: 'boolean' },
|
||||
{ value: Type.file, text: 'file' },
|
||||
{ value: ArrayType.string, text: 'array[string]' },
|
||||
{ value: ArrayType.number, text: 'array[number]' },
|
||||
{ value: ArrayType.file, text: 'array[file]' },
|
||||
]
|
||||
|
||||
const EditCard: FC<EditCardProps> = ({
|
||||
|
||||
@@ -4,7 +4,7 @@ import type { EditData } from './edit-card'
|
||||
import { noop } from 'es-toolkit/function'
|
||||
import { produce } from 'immer'
|
||||
import Toast from '@/app/components/base/toast'
|
||||
import { ArrayType, Type } from '../../../types'
|
||||
import { ArrayType, FILE_REF_FORMAT, Type } from '../../../types'
|
||||
import { findPropertyWithPath } from '../../../utils'
|
||||
import { useMittContext } from './context'
|
||||
import { useVisualEditorStore } from './store'
|
||||
@@ -133,6 +133,7 @@ export const useSchemaNodeOperations = (props: VisualEditorProps) => {
|
||||
}
|
||||
if (schema.type === Type.array)
|
||||
delete schema.items
|
||||
delete schema.format
|
||||
switch (newType) {
|
||||
case Type.object:
|
||||
schema.type = Type.object
|
||||
@@ -140,6 +141,10 @@ export const useSchemaNodeOperations = (props: VisualEditorProps) => {
|
||||
schema.required = []
|
||||
schema.additionalProperties = false
|
||||
break
|
||||
case Type.file:
|
||||
schema.type = Type.string
|
||||
schema.format = FILE_REF_FORMAT
|
||||
break
|
||||
case ArrayType.string:
|
||||
schema.type = Type.array
|
||||
schema.items = {
|
||||
@@ -167,6 +172,13 @@ export const useSchemaNodeOperations = (props: VisualEditorProps) => {
|
||||
additionalProperties: false,
|
||||
}
|
||||
break
|
||||
case ArrayType.file:
|
||||
schema.type = Type.array
|
||||
schema.items = {
|
||||
type: Type.string,
|
||||
format: FILE_REF_FORMAT,
|
||||
}
|
||||
break
|
||||
default:
|
||||
schema.type = newType as Type
|
||||
}
|
||||
@@ -309,6 +321,7 @@ export const useSchemaNodeOperations = (props: VisualEditorProps) => {
|
||||
}
|
||||
if (schema.type === Type.array)
|
||||
delete schema.items
|
||||
delete schema.format
|
||||
switch (newType) {
|
||||
case Type.object:
|
||||
schema.type = Type.object
|
||||
@@ -316,6 +329,10 @@ export const useSchemaNodeOperations = (props: VisualEditorProps) => {
|
||||
schema.required = []
|
||||
schema.additionalProperties = false
|
||||
break
|
||||
case Type.file:
|
||||
schema.type = Type.string
|
||||
schema.format = FILE_REF_FORMAT
|
||||
break
|
||||
case ArrayType.string:
|
||||
schema.type = Type.array
|
||||
schema.items = {
|
||||
@@ -343,6 +360,13 @@ export const useSchemaNodeOperations = (props: VisualEditorProps) => {
|
||||
additionalProperties: false,
|
||||
}
|
||||
break
|
||||
case ArrayType.file:
|
||||
schema.type = Type.array
|
||||
schema.items = {
|
||||
type: Type.string,
|
||||
format: FILE_REF_FORMAT,
|
||||
}
|
||||
break
|
||||
default:
|
||||
schema.type = newType as Type
|
||||
}
|
||||
@@ -398,6 +422,7 @@ export const useSchemaNodeOperations = (props: VisualEditorProps) => {
|
||||
}
|
||||
if (schema.type === Type.array)
|
||||
delete schema.items
|
||||
delete schema.format
|
||||
switch (newType) {
|
||||
case Type.object:
|
||||
schema.type = Type.object
|
||||
@@ -405,6 +430,10 @@ export const useSchemaNodeOperations = (props: VisualEditorProps) => {
|
||||
schema.required = []
|
||||
schema.additionalProperties = false
|
||||
break
|
||||
case Type.file:
|
||||
schema.type = Type.string
|
||||
schema.format = FILE_REF_FORMAT
|
||||
break
|
||||
case ArrayType.string:
|
||||
schema.type = Type.array
|
||||
schema.items = {
|
||||
@@ -432,6 +461,13 @@ export const useSchemaNodeOperations = (props: VisualEditorProps) => {
|
||||
additionalProperties: false,
|
||||
}
|
||||
break
|
||||
case ArrayType.file:
|
||||
schema.type = Type.array
|
||||
schema.items = {
|
||||
type: Type.string,
|
||||
format: FILE_REF_FORMAT,
|
||||
}
|
||||
break
|
||||
default:
|
||||
schema.type = newType as Type
|
||||
}
|
||||
|
||||
@@ -20,6 +20,8 @@ export type LLMNodeType = CommonNodeType & {
|
||||
reasoning_format?: 'tagged' | 'separated'
|
||||
}
|
||||
|
||||
export const FILE_REF_FORMAT = 'dify-file-ref'
|
||||
|
||||
export enum Type {
|
||||
string = 'string',
|
||||
number = 'number',
|
||||
@@ -38,12 +40,13 @@ export enum ArrayType {
|
||||
number = 'array[number]',
|
||||
boolean = 'array[boolean]',
|
||||
object = 'array[object]',
|
||||
file = 'array[file]',
|
||||
}
|
||||
|
||||
export type TypeWithArray = Type | ArrayType
|
||||
|
||||
type ArrayItemType = Exclude<Type, Type.array>
|
||||
export type ArrayItems = Omit<Field, 'type'> & { type: ArrayItemType }
|
||||
export type ArrayItems = Omit<Field, 'type' | 'format'> & { type: ArrayItemType; format?: string }
|
||||
|
||||
export type SchemaEnumType = string[] | number[]
|
||||
|
||||
@@ -54,6 +57,7 @@ export type Field = {
|
||||
}
|
||||
required?: string[] // Key of required properties in object
|
||||
description?: string
|
||||
format?: string
|
||||
items?: ArrayItems // Array has items. Define the item type
|
||||
enum?: SchemaEnumType // Enum values
|
||||
additionalProperties?: false // Required in object by api. Just set false
|
||||
|
||||
@@ -2,21 +2,24 @@ import type { ValidationError } from 'jsonschema'
|
||||
import type { ArrayItems, Field, LLMNodeType } from './types'
|
||||
import { z } from 'zod'
|
||||
import { draft07Validator, forbidBooleanProperties } from '@/utils/validators'
|
||||
import { ArrayType, Type } from './types'
|
||||
import { ArrayType, FILE_REF_FORMAT, Type } from './types'
|
||||
|
||||
export const checkNodeValid = (_payload: LLMNodeType) => {
|
||||
return true
|
||||
}
|
||||
|
||||
export const getFieldType = (field: Field) => {
|
||||
const { type, items, enum: enums } = field
|
||||
const { type, items, enum: enums, format } = field
|
||||
if (format === FILE_REF_FORMAT)
|
||||
return Type.file
|
||||
if (field.schemaType === 'file')
|
||||
return Type.file
|
||||
if (enums && enums.length > 0)
|
||||
return Type.enumType
|
||||
if (type !== Type.array || !items)
|
||||
return type
|
||||
|
||||
if (items.format === FILE_REF_FORMAT || items.type === Type.file)
|
||||
return ArrayType.file
|
||||
return ArrayType[items.type as keyof typeof ArrayType]
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user