Você não pode selecionar mais de 25 tópicos Os tópicos devem começar com uma letra ou um número, podem incluir traços ('-') e podem ter até 35 caracteres.

utils.ts 8.6KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336
  1. import { ArrayType, Type } from './types'
  2. import type { ArrayItems, Field, LLMNodeType } from './types'
  3. import type { Schema, ValidationError } from 'jsonschema'
  4. import { Validator } from 'jsonschema'
  5. import produce from 'immer'
  6. import { z } from 'zod'
  7. export const checkNodeValid = (payload: LLMNodeType) => {
  8. return true
  9. }
  10. export const getFieldType = (field: Field) => {
  11. const { type, items } = field
  12. if (type !== Type.array || !items)
  13. return type
  14. return ArrayType[items.type]
  15. }
  16. export const getHasChildren = (schema: Field) => {
  17. const complexTypes = [Type.object, Type.array]
  18. if (!complexTypes.includes(schema.type))
  19. return false
  20. if (schema.type === Type.object)
  21. return schema.properties && Object.keys(schema.properties).length > 0
  22. if (schema.type === Type.array)
  23. return schema.items && schema.items.type === Type.object && schema.items.properties && Object.keys(schema.items.properties).length > 0
  24. }
  25. export const getTypeOf = (target: any) => {
  26. if (target === null) return 'null'
  27. if (typeof target !== 'object') {
  28. return typeof target
  29. }
  30. else {
  31. return Object.prototype.toString
  32. .call(target)
  33. .slice(8, -1)
  34. .toLocaleLowerCase()
  35. }
  36. }
  37. export const inferType = (value: any): Type => {
  38. const type = getTypeOf(value)
  39. if (type === 'array') return Type.array
  40. // type boolean will be treated as string
  41. if (type === 'boolean') return Type.string
  42. if (type === 'number') return Type.number
  43. if (type === 'string') return Type.string
  44. if (type === 'object') return Type.object
  45. return Type.string
  46. }
  47. export const jsonToSchema = (json: any): Field => {
  48. const schema: Field = {
  49. type: inferType(json),
  50. }
  51. if (schema.type === Type.object) {
  52. schema.properties = {}
  53. schema.required = []
  54. schema.additionalProperties = false
  55. Object.entries(json).forEach(([key, value]) => {
  56. schema.properties![key] = jsonToSchema(value)
  57. schema.required!.push(key)
  58. })
  59. }
  60. else if (schema.type === Type.array) {
  61. schema.items = jsonToSchema(json[0]) as ArrayItems
  62. }
  63. return schema
  64. }
  65. export const checkJsonDepth = (json: any) => {
  66. if (!json || getTypeOf(json) !== 'object')
  67. return 0
  68. let maxDepth = 0
  69. if (getTypeOf(json) === 'array') {
  70. if (json[0] && getTypeOf(json[0]) === 'object')
  71. maxDepth = checkJsonDepth(json[0])
  72. }
  73. else if (getTypeOf(json) === 'object') {
  74. const propertyDepths = Object.values(json).map(value => checkJsonDepth(value))
  75. maxDepth = propertyDepths.length ? Math.max(...propertyDepths) + 1 : 1
  76. }
  77. return maxDepth
  78. }
  79. export const checkJsonSchemaDepth = (schema: Field) => {
  80. if (!schema || getTypeOf(schema) !== 'object')
  81. return 0
  82. let maxDepth = 0
  83. if (schema.type === Type.object && schema.properties) {
  84. const propertyDepths = Object.values(schema.properties).map(value => checkJsonSchemaDepth(value))
  85. maxDepth = propertyDepths.length ? Math.max(...propertyDepths) + 1 : 1
  86. }
  87. else if (schema.type === Type.array && schema.items && schema.items.type === Type.object) {
  88. maxDepth = checkJsonSchemaDepth(schema.items) + 1
  89. }
  90. return maxDepth
  91. }
  92. export const findPropertyWithPath = (target: any, path: string[]) => {
  93. let current = target
  94. for (const key of path)
  95. current = current[key]
  96. return current
  97. }
  98. const draft07MetaSchema = {
  99. $schema: 'http://json-schema.org/draft-07/schema#',
  100. $id: 'http://json-schema.org/draft-07/schema#',
  101. title: 'Core schema meta-schema',
  102. definitions: {
  103. schemaArray: {
  104. type: 'array',
  105. minItems: 1,
  106. items: { $ref: '#' },
  107. },
  108. nonNegativeInteger: {
  109. type: 'integer',
  110. minimum: 0,
  111. },
  112. nonNegativeIntegerDefault0: {
  113. allOf: [
  114. { $ref: '#/definitions/nonNegativeInteger' },
  115. { default: 0 },
  116. ],
  117. },
  118. simpleTypes: {
  119. enum: [
  120. 'array',
  121. 'boolean',
  122. 'integer',
  123. 'null',
  124. 'number',
  125. 'object',
  126. 'string',
  127. ],
  128. },
  129. stringArray: {
  130. type: 'array',
  131. items: { type: 'string' },
  132. uniqueItems: true,
  133. default: [],
  134. },
  135. },
  136. type: ['object', 'boolean'],
  137. properties: {
  138. $id: {
  139. type: 'string',
  140. format: 'uri-reference',
  141. },
  142. $schema: {
  143. type: 'string',
  144. format: 'uri',
  145. },
  146. $ref: {
  147. type: 'string',
  148. format: 'uri-reference',
  149. },
  150. title: {
  151. type: 'string',
  152. },
  153. description: {
  154. type: 'string',
  155. },
  156. default: true,
  157. readOnly: {
  158. type: 'boolean',
  159. default: false,
  160. },
  161. examples: {
  162. type: 'array',
  163. items: true,
  164. },
  165. multipleOf: {
  166. type: 'number',
  167. exclusiveMinimum: 0,
  168. },
  169. maximum: {
  170. type: 'number',
  171. },
  172. exclusiveMaximum: {
  173. type: 'number',
  174. },
  175. minimum: {
  176. type: 'number',
  177. },
  178. exclusiveMinimum: {
  179. type: 'number',
  180. },
  181. maxLength: { $ref: '#/definitions/nonNegativeInteger' },
  182. minLength: { $ref: '#/definitions/nonNegativeIntegerDefault0' },
  183. pattern: {
  184. type: 'string',
  185. format: 'regex',
  186. },
  187. additionalItems: { $ref: '#' },
  188. items: {
  189. anyOf: [
  190. { $ref: '#' },
  191. { $ref: '#/definitions/schemaArray' },
  192. ],
  193. default: true,
  194. },
  195. maxItems: { $ref: '#/definitions/nonNegativeInteger' },
  196. minItems: { $ref: '#/definitions/nonNegativeIntegerDefault0' },
  197. uniqueItems: {
  198. type: 'boolean',
  199. default: false,
  200. },
  201. contains: { $ref: '#' },
  202. maxProperties: { $ref: '#/definitions/nonNegativeInteger' },
  203. minProperties: { $ref: '#/definitions/nonNegativeIntegerDefault0' },
  204. required: { $ref: '#/definitions/stringArray' },
  205. additionalProperties: { $ref: '#' },
  206. definitions: {
  207. type: 'object',
  208. additionalProperties: { $ref: '#' },
  209. default: {},
  210. },
  211. properties: {
  212. type: 'object',
  213. additionalProperties: { $ref: '#' },
  214. default: {},
  215. },
  216. patternProperties: {
  217. type: 'object',
  218. additionalProperties: { $ref: '#' },
  219. propertyNames: { format: 'regex' },
  220. default: {},
  221. },
  222. dependencies: {
  223. type: 'object',
  224. additionalProperties: {
  225. anyOf: [
  226. { $ref: '#' },
  227. { $ref: '#/definitions/stringArray' },
  228. ],
  229. },
  230. },
  231. propertyNames: { $ref: '#' },
  232. const: true,
  233. enum: {
  234. type: 'array',
  235. items: true,
  236. minItems: 1,
  237. uniqueItems: true,
  238. },
  239. type: {
  240. anyOf: [
  241. { $ref: '#/definitions/simpleTypes' },
  242. {
  243. type: 'array',
  244. items: { $ref: '#/definitions/simpleTypes' },
  245. minItems: 1,
  246. uniqueItems: true,
  247. },
  248. ],
  249. },
  250. format: { type: 'string' },
  251. allOf: { $ref: '#/definitions/schemaArray' },
  252. anyOf: { $ref: '#/definitions/schemaArray' },
  253. oneOf: { $ref: '#/definitions/schemaArray' },
  254. not: { $ref: '#' },
  255. },
  256. default: true,
  257. } as unknown as Schema
  258. const validator = new Validator()
  259. export const validateSchemaAgainstDraft7 = (schemaToValidate: any) => {
  260. const schema = produce(schemaToValidate, (draft: any) => {
  261. // Make sure the schema has the $schema property for draft-07
  262. if (!draft.$schema)
  263. draft.$schema = 'http://json-schema.org/draft-07/schema#'
  264. })
  265. const result = validator.validate(schema, draft07MetaSchema, {
  266. nestedErrors: true,
  267. throwError: false,
  268. })
  269. // Access errors from the validation result
  270. const errors = result.valid ? [] : result.errors || []
  271. return errors
  272. }
  273. export const getValidationErrorMessage = (errors: ValidationError[]) => {
  274. const message = errors.map((error) => {
  275. return `Error: ${error.path.join('.')} ${error.message} Details: ${JSON.stringify(error.stack)}`
  276. }).join('; ')
  277. return message
  278. }
  279. export const convertBooleanToString = (schema: any) => {
  280. if (schema.type === Type.boolean)
  281. schema.type = Type.string
  282. if (schema.type === Type.array && schema.items && schema.items.type === Type.boolean)
  283. schema.items.type = Type.string
  284. if (schema.type === Type.object) {
  285. schema.properties = Object.entries(schema.properties).reduce((acc, [key, value]) => {
  286. acc[key] = convertBooleanToString(value)
  287. return acc
  288. }, {} as any)
  289. }
  290. if (schema.type === Type.array && schema.items && schema.items.type === Type.object) {
  291. schema.items.properties = Object.entries(schema.items.properties).reduce((acc, [key, value]) => {
  292. acc[key] = convertBooleanToString(value)
  293. return acc
  294. }, {} as any)
  295. }
  296. return schema
  297. }
  298. const schemaRootObject = z.object({
  299. type: z.literal('object'),
  300. properties: z.record(z.string(), z.any()),
  301. required: z.array(z.string()),
  302. additionalProperties: z.boolean().optional(),
  303. })
  304. export const preValidateSchema = (schema: any) => {
  305. const result = schemaRootObject.safeParse(schema)
  306. return result
  307. }