mirror of
https://github.com/langgenius/dify.git
synced 2026-01-26 21:52:20 +08:00
Signed-off-by: -LAN- <laipz8200@outlook.com> Signed-off-by: kenwoodjw <blackxin55+@gmail.com> Signed-off-by: Yongtao Huang <yongtaoh2022@gmail.com> Signed-off-by: yihong0618 <zouzou0208@gmail.com> Signed-off-by: zhanluxianshen <zhanluxianshen@163.com> Co-authored-by: -LAN- <laipz8200@outlook.com> Co-authored-by: GuanMu <ballmanjq@gmail.com> Co-authored-by: Davide Delbianco <davide.delbianco@outlook.com> Co-authored-by: NeatGuyCoding <15627489+NeatGuyCoding@users.noreply.github.com> Co-authored-by: kenwoodjw <blackxin55+@gmail.com> Co-authored-by: Yongtao Huang <yongtaoh2022@gmail.com> Co-authored-by: Yongtao Huang <99629139+hyongtao-db@users.noreply.github.com> Co-authored-by: Qiang Lee <18018968632@163.com> Co-authored-by: 李强04 <liqiang04@gaotu.cn> Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com> Co-authored-by: Asuka Minato <i@asukaminato.eu.org> Co-authored-by: Matri Qi <matrixdom@126.com> Co-authored-by: huayaoyue6 <huayaoyue@163.com> Co-authored-by: Bowen Liang <liangbowen@gf.com.cn> Co-authored-by: znn <jubinkumarsoni@gmail.com> Co-authored-by: crazywoola <427733928@qq.com> Co-authored-by: crazywoola <100913391+crazywoola@users.noreply.github.com> Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> Co-authored-by: yihong <zouzou0208@gmail.com> Co-authored-by: Muke Wang <shaodwaaron@gmail.com> Co-authored-by: wangmuke <wangmuke@kingsware.cn> Co-authored-by: Wu Tianwei <30284043+WTW0313@users.noreply.github.com> Co-authored-by: quicksand <quicksandzn@gmail.com> Co-authored-by: 非法操作 <hjlarry@163.com> Co-authored-by: zxhlyh <jasonapring2015@outlook.com> Co-authored-by: Eric Guo <eric.guocz@gmail.com> Co-authored-by: Zhedong Cen <cenzhedong2@126.com> Co-authored-by: jiangbo721 <jiangbo721@163.com> Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: hjlarry <25834719+hjlarry@users.noreply.github.com> Co-authored-by: lxsummer <35754229+lxjustdoit@users.noreply.github.com> Co-authored-by: 湛露先生 <zhanluxianshen@163.com> Co-authored-by: Guangdong Liu <liugddx@gmail.com> Co-authored-by: QuantumGhost <obelisk.reg+git@gmail.com> Co-authored-by: Claude <noreply@anthropic.com> Co-authored-by: Yessenia-d <yessenia.contact@gmail.com> Co-authored-by: huangzhuo1949 <167434202+huangzhuo1949@users.noreply.github.com> Co-authored-by: huangzhuo <huangzhuo1@xiaomi.com> Co-authored-by: 17hz <0x149527@gmail.com> Co-authored-by: Amy <1530140574@qq.com> Co-authored-by: Joel <iamjoel007@gmail.com> Co-authored-by: Nite Knite <nkCoding@gmail.com> Co-authored-by: Yeuoly <45712896+Yeuoly@users.noreply.github.com> Co-authored-by: Petrus Han <petrus.hanks@gmail.com> Co-authored-by: iamjoel <2120155+iamjoel@users.noreply.github.com> Co-authored-by: Kalo Chin <frog.beepers.0n@icloud.com> Co-authored-by: Ujjwal Maurya <ujjwalsbx@gmail.com> Co-authored-by: Maries <xh001x@hotmail.com>
338 lines
8.7 KiB
TypeScript
338 lines
8.7 KiB
TypeScript
import { ArrayType, Type } from './types'
|
|
import type { ArrayItems, Field, LLMNodeType } from './types'
|
|
import type { Schema, ValidationError } from 'jsonschema'
|
|
import { Validator } from 'jsonschema'
|
|
import produce from 'immer'
|
|
import { z } from 'zod'
|
|
|
|
export const checkNodeValid = (_payload: LLMNodeType) => {
|
|
return true
|
|
}
|
|
|
|
export const getFieldType = (field: Field) => {
|
|
const { type, items } = field
|
|
if (type !== Type.array || !items)
|
|
return type
|
|
|
|
return ArrayType[items.type]
|
|
}
|
|
|
|
export const getHasChildren = (schema: Field) => {
|
|
const complexTypes = [Type.object, Type.array]
|
|
if (!complexTypes.includes(schema.type))
|
|
return false
|
|
if (schema.type === Type.object)
|
|
return schema.properties && Object.keys(schema.properties).length > 0
|
|
if (schema.type === Type.array)
|
|
return schema.items && schema.items.type === Type.object && schema.items.properties && Object.keys(schema.items.properties).length > 0
|
|
}
|
|
|
|
export const getTypeOf = (target: any) => {
|
|
if (target === null) return 'null'
|
|
if (typeof target !== 'object') {
|
|
return typeof target
|
|
}
|
|
else {
|
|
return Object.prototype.toString
|
|
.call(target)
|
|
.slice(8, -1)
|
|
.toLocaleLowerCase()
|
|
}
|
|
}
|
|
|
|
export const inferType = (value: any): Type => {
|
|
const type = getTypeOf(value)
|
|
if (type === 'array') return Type.array
|
|
// type boolean will be treated as string
|
|
if (type === 'boolean') return Type.string
|
|
if (type === 'number') return Type.number
|
|
if (type === 'string') return Type.string
|
|
if (type === 'object') return Type.object
|
|
return Type.string
|
|
}
|
|
|
|
export const jsonToSchema = (json: any): Field => {
|
|
const schema: Field = {
|
|
type: inferType(json),
|
|
}
|
|
|
|
if (schema.type === Type.object) {
|
|
schema.properties = {}
|
|
schema.required = []
|
|
schema.additionalProperties = false
|
|
|
|
Object.entries(json).forEach(([key, value]) => {
|
|
schema.properties![key] = jsonToSchema(value)
|
|
schema.required!.push(key)
|
|
})
|
|
}
|
|
else if (schema.type === Type.array) {
|
|
schema.items = jsonToSchema(json[0]) as ArrayItems
|
|
}
|
|
|
|
return schema
|
|
}
|
|
|
|
export const checkJsonDepth = (json: any) => {
|
|
if (!json || getTypeOf(json) !== 'object')
|
|
return 0
|
|
|
|
let maxDepth = 0
|
|
|
|
if (getTypeOf(json) === 'array') {
|
|
if (json[0] && getTypeOf(json[0]) === 'object')
|
|
maxDepth = checkJsonDepth(json[0])
|
|
}
|
|
else if (getTypeOf(json) === 'object') {
|
|
const propertyDepths = Object.values(json).map(value => checkJsonDepth(value))
|
|
maxDepth = propertyDepths.length ? Math.max(...propertyDepths) + 1 : 1
|
|
}
|
|
|
|
return maxDepth
|
|
}
|
|
|
|
export const checkJsonSchemaDepth = (schema: Field) => {
|
|
if (!schema || getTypeOf(schema) !== 'object')
|
|
return 0
|
|
|
|
let maxDepth = 0
|
|
|
|
if (schema.type === Type.object && schema.properties) {
|
|
const propertyDepths = Object.values(schema.properties).map(value => checkJsonSchemaDepth(value))
|
|
maxDepth = propertyDepths.length ? Math.max(...propertyDepths) + 1 : 1
|
|
}
|
|
else if (schema.type === Type.array && schema.items && schema.items.type === Type.object) {
|
|
maxDepth = checkJsonSchemaDepth(schema.items) + 1
|
|
}
|
|
|
|
return maxDepth
|
|
}
|
|
|
|
export const findPropertyWithPath = (target: any, path: string[]) => {
|
|
let current = target
|
|
for (const key of path)
|
|
current = current[key]
|
|
return current
|
|
}
|
|
|
|
const draft07MetaSchema = {
|
|
$schema: 'http://json-schema.org/draft-07/schema#',
|
|
$id: 'http://json-schema.org/draft-07/schema#',
|
|
title: 'Core schema meta-schema',
|
|
definitions: {
|
|
schemaArray: {
|
|
type: 'array',
|
|
minItems: 1,
|
|
items: { $ref: '#' },
|
|
},
|
|
nonNegativeInteger: {
|
|
type: 'integer',
|
|
minimum: 0,
|
|
},
|
|
nonNegativeIntegerDefault0: {
|
|
allOf: [
|
|
{ $ref: '#/definitions/nonNegativeInteger' },
|
|
{ default: 0 },
|
|
],
|
|
},
|
|
simpleTypes: {
|
|
enum: [
|
|
'array',
|
|
'boolean',
|
|
'integer',
|
|
'null',
|
|
'number',
|
|
'object',
|
|
'string',
|
|
],
|
|
},
|
|
stringArray: {
|
|
type: 'array',
|
|
items: { type: 'string' },
|
|
uniqueItems: true,
|
|
default: [],
|
|
},
|
|
},
|
|
type: ['object', 'boolean'],
|
|
properties: {
|
|
$id: {
|
|
type: 'string',
|
|
format: 'uri-reference',
|
|
},
|
|
$schema: {
|
|
type: 'string',
|
|
format: 'uri',
|
|
},
|
|
$ref: {
|
|
type: 'string',
|
|
format: 'uri-reference',
|
|
},
|
|
title: {
|
|
type: 'string',
|
|
},
|
|
description: {
|
|
type: 'string',
|
|
},
|
|
default: true,
|
|
readOnly: {
|
|
type: 'boolean',
|
|
default: false,
|
|
},
|
|
examples: {
|
|
type: 'array',
|
|
items: true,
|
|
},
|
|
multipleOf: {
|
|
type: 'number',
|
|
exclusiveMinimum: 0,
|
|
},
|
|
maximum: {
|
|
type: 'number',
|
|
},
|
|
exclusiveMaximum: {
|
|
type: 'number',
|
|
},
|
|
minimum: {
|
|
type: 'number',
|
|
},
|
|
exclusiveMinimum: {
|
|
type: 'number',
|
|
},
|
|
maxLength: { $ref: '#/definitions/nonNegativeInteger' },
|
|
minLength: { $ref: '#/definitions/nonNegativeIntegerDefault0' },
|
|
pattern: {
|
|
type: 'string',
|
|
format: 'regex',
|
|
},
|
|
additionalItems: { $ref: '#' },
|
|
items: {
|
|
anyOf: [
|
|
{ $ref: '#' },
|
|
{ $ref: '#/definitions/schemaArray' },
|
|
],
|
|
default: true,
|
|
},
|
|
maxItems: { $ref: '#/definitions/nonNegativeInteger' },
|
|
minItems: { $ref: '#/definitions/nonNegativeIntegerDefault0' },
|
|
uniqueItems: {
|
|
type: 'boolean',
|
|
default: false,
|
|
},
|
|
contains: { $ref: '#' },
|
|
maxProperties: { $ref: '#/definitions/nonNegativeInteger' },
|
|
minProperties: { $ref: '#/definitions/nonNegativeIntegerDefault0' },
|
|
required: { $ref: '#/definitions/stringArray' },
|
|
additionalProperties: { $ref: '#' },
|
|
definitions: {
|
|
type: 'object',
|
|
additionalProperties: { $ref: '#' },
|
|
default: {},
|
|
},
|
|
properties: {
|
|
type: 'object',
|
|
additionalProperties: { $ref: '#' },
|
|
default: {},
|
|
},
|
|
patternProperties: {
|
|
type: 'object',
|
|
additionalProperties: { $ref: '#' },
|
|
propertyNames: { format: 'regex' },
|
|
default: {},
|
|
},
|
|
dependencies: {
|
|
type: 'object',
|
|
additionalProperties: {
|
|
anyOf: [
|
|
{ $ref: '#' },
|
|
{ $ref: '#/definitions/stringArray' },
|
|
],
|
|
},
|
|
},
|
|
propertyNames: { $ref: '#' },
|
|
const: true,
|
|
enum: {
|
|
type: 'array',
|
|
items: true,
|
|
minItems: 1,
|
|
uniqueItems: true,
|
|
},
|
|
type: {
|
|
anyOf: [
|
|
{ $ref: '#/definitions/simpleTypes' },
|
|
{
|
|
type: 'array',
|
|
items: { $ref: '#/definitions/simpleTypes' },
|
|
minItems: 1,
|
|
uniqueItems: true,
|
|
},
|
|
],
|
|
},
|
|
format: { type: 'string' },
|
|
allOf: { $ref: '#/definitions/schemaArray' },
|
|
anyOf: { $ref: '#/definitions/schemaArray' },
|
|
oneOf: { $ref: '#/definitions/schemaArray' },
|
|
not: { $ref: '#' },
|
|
},
|
|
default: true,
|
|
} as unknown as Schema
|
|
|
|
const validator = new Validator()
|
|
|
|
export const validateSchemaAgainstDraft7 = (schemaToValidate: any) => {
|
|
const schema = produce(schemaToValidate, (draft: any) => {
|
|
// Make sure the schema has the $schema property for draft-07
|
|
if (!draft.$schema)
|
|
draft.$schema = 'http://json-schema.org/draft-07/schema#'
|
|
})
|
|
|
|
const result = validator.validate(schema, draft07MetaSchema, {
|
|
nestedErrors: true,
|
|
throwError: false,
|
|
})
|
|
|
|
// Access errors from the validation result
|
|
const errors = result.valid ? [] : result.errors || []
|
|
|
|
return errors
|
|
}
|
|
|
|
export const getValidationErrorMessage = (errors: ValidationError[]) => {
|
|
const message = errors.map((error) => {
|
|
return `Error: ${error.path.join('.')} ${error.message} Details: ${JSON.stringify(error.stack)}`
|
|
}).join('; ')
|
|
return message
|
|
}
|
|
|
|
// Previous Not support boolean type, so transform boolean to string when paste it into schema editor
|
|
export const convertBooleanToString = (schema: any) => {
|
|
if (schema.type === Type.boolean)
|
|
schema.type = Type.string
|
|
if (schema.type === Type.array && schema.items && schema.items.type === Type.boolean)
|
|
schema.items.type = Type.string
|
|
if (schema.type === Type.object) {
|
|
schema.properties = Object.entries(schema.properties).reduce((acc, [key, value]) => {
|
|
acc[key] = convertBooleanToString(value)
|
|
return acc
|
|
}, {} as any)
|
|
}
|
|
if (schema.type === Type.array && schema.items && schema.items.type === Type.object) {
|
|
schema.items.properties = Object.entries(schema.items.properties).reduce((acc, [key, value]) => {
|
|
acc[key] = convertBooleanToString(value)
|
|
return acc
|
|
}, {} as any)
|
|
}
|
|
return schema
|
|
}
|
|
|
|
const schemaRootObject = z.object({
|
|
type: z.literal('object'),
|
|
properties: z.record(z.string(), z.any()),
|
|
required: z.array(z.string()),
|
|
additionalProperties: z.boolean().optional(),
|
|
})
|
|
|
|
export const preValidateSchema = (schema: any) => {
|
|
const result = schemaRootObject.safeParse(schema)
|
|
return result
|
|
}
|