schema.ts 2.7 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677
  1. #!/usr/bin/env bun
  2. import { Config } from "@/config/config"
  3. import { ConfigV1 } from "@kirincode-ai/core/v1/config/config"
  4. import { TuiConfig } from "@kirincode-ai/tui/config"
  5. import { Schema } from "effect"
  6. type JsonSchema = Record<string, unknown>
  7. const MODEL_REF = "https://models.dev/model-schema.json#/$defs/Model"
  8. function generateEffect(schema: Schema.Top) {
  9. const document = Schema.toJsonSchemaDocument(schema)
  10. const normalized = normalize({
  11. $schema: "https://json-schema.org/draft/2020-12/schema",
  12. ...document.schema,
  13. $defs: document.definitions,
  14. })
  15. if (!isRecord(normalized)) throw new Error("schema generator produced a non-object schema")
  16. const restored = restoreModelRefs(normalized)
  17. if (!isRecord(restored)) throw new Error("schema generator produced a non-object schema")
  18. restored.allowComments = true
  19. restored.allowTrailingCommas = true
  20. return restored
  21. }
  22. function normalize(value: unknown): unknown {
  23. if (Array.isArray(value)) return value.map(normalize)
  24. if (!isRecord(value)) return value
  25. const schema = Object.fromEntries(Object.entries(value).map(([key, item]) => [key, normalize(item)]))
  26. if (Array.isArray(schema.anyOf)) {
  27. const anyOf = schema.anyOf.filter((item) => !isRecord(item) || item.type !== "null")
  28. if (anyOf.length !== schema.anyOf.length) {
  29. const { anyOf: _, ...rest } = schema
  30. if (anyOf.length === 1 && isRecord(anyOf[0])) return normalize({ ...anyOf[0], ...rest })
  31. return { ...rest, anyOf }
  32. }
  33. }
  34. if (Array.isArray(schema.allOf) && schema.allOf.length === 1 && isRecord(schema.allOf[0])) {
  35. const { allOf: _, ...rest } = schema
  36. return normalize({ ...schema.allOf[0], ...rest })
  37. }
  38. if (schema.type === "integer" && schema.maximum === undefined) {
  39. return { ...schema, maximum: Number.MAX_SAFE_INTEGER }
  40. }
  41. return schema
  42. }
  43. function restoreModelRefs(value: unknown, key?: string): unknown {
  44. if (Array.isArray(value)) return value.map((item) => restoreModelRefs(item))
  45. if (!isRecord(value)) return value
  46. const schema = Object.fromEntries(Object.entries(value).map(([name, item]) => [name, restoreModelRefs(item, name)]))
  47. if ((key === "model" || key === "small_model") && schema.type === "string") {
  48. return { ...schema, $ref: MODEL_REF }
  49. }
  50. return schema
  51. }
  52. function isRecord(value: unknown): value is JsonSchema {
  53. return typeof value === "object" && value !== null && !Array.isArray(value)
  54. }
  55. const configFile = process.argv[2]
  56. const tuiFile = process.argv[3]
  57. console.log(configFile)
  58. await Bun.write(configFile, JSON.stringify(generateEffect(ConfigV1.Info), null, 2))
  59. if (tuiFile) {
  60. console.log(tuiFile)
  61. await Bun.write(tuiFile, JSON.stringify(generateEffect(TuiConfig.Info), null, 2))
  62. }