@typia/utils 12.0.0-dev.20260306 → 12.0.0-dev.20260307-2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (60) hide show
  1. package/README.md +5 -5
  2. package/lib/converters/LlmSchemaConverter.d.ts +1 -20
  3. package/lib/converters/LlmSchemaConverter.js +0 -189
  4. package/lib/converters/LlmSchemaConverter.js.map +1 -1
  5. package/lib/converters/LlmSchemaConverter.mjs +0 -227
  6. package/lib/converters/LlmSchemaConverter.mjs.map +1 -1
  7. package/lib/http/HttpLlm.d.ts +2 -32
  8. package/lib/http/HttpLlm.js +4 -27
  9. package/lib/http/HttpLlm.js.map +1 -1
  10. package/lib/http/HttpLlm.mjs +1 -24
  11. package/lib/http/HttpLlm.mjs.map +1 -1
  12. package/lib/http/internal/HttpLlmApplicationComposer.js +13 -19
  13. package/lib/http/internal/HttpLlmApplicationComposer.js.map +1 -1
  14. package/lib/http/internal/HttpLlmApplicationComposer.mjs +7 -9
  15. package/lib/http/internal/HttpLlmApplicationComposer.mjs.map +1 -1
  16. package/lib/index.mjs +10 -10
  17. package/lib/utils/LlmJson.d.ts +87 -0
  18. package/lib/utils/LlmJson.js +129 -0
  19. package/lib/utils/LlmJson.js.map +1 -0
  20. package/lib/utils/LlmJson.mjs +136 -0
  21. package/lib/utils/LlmJson.mjs.map +1 -0
  22. package/lib/utils/index.d.ts +1 -1
  23. package/lib/utils/index.js +1 -1
  24. package/lib/utils/index.js.map +1 -1
  25. package/lib/utils/index.mjs +1 -1
  26. package/lib/utils/internal/coerceLlmArguments.d.ts +1 -0
  27. package/lib/utils/internal/coerceLlmArguments.js +233 -0
  28. package/lib/utils/internal/coerceLlmArguments.js.map +1 -0
  29. package/lib/utils/internal/coerceLlmArguments.mjs +232 -0
  30. package/lib/utils/internal/coerceLlmArguments.mjs.map +1 -0
  31. package/lib/utils/internal/parseLenientJson.d.ts +1 -0
  32. package/lib/utils/internal/parseLenientJson.js +639 -0
  33. package/lib/utils/internal/parseLenientJson.js.map +1 -0
  34. package/lib/utils/internal/parseLenientJson.mjs +640 -0
  35. package/lib/utils/internal/parseLenientJson.mjs.map +1 -0
  36. package/lib/utils/internal/stringifyValidationFailure.d.ts +2 -0
  37. package/lib/utils/{stringifyValidationFailure.js → internal/stringifyValidationFailure.js} +55 -63
  38. package/lib/utils/internal/stringifyValidationFailure.js.map +1 -0
  39. package/lib/utils/{stringifyValidationFailure.mjs → internal/stringifyValidationFailure.mjs} +54 -63
  40. package/lib/utils/internal/stringifyValidationFailure.mjs.map +1 -0
  41. package/lib/validators/internal/OpenApiOneOfValidator.mjs +5 -1
  42. package/lib/validators/internal/OpenApiOneOfValidator.mjs.map +1 -1
  43. package/package.json +2 -2
  44. package/src/converters/LlmSchemaConverter.ts +0 -277
  45. package/src/http/HttpLlm.ts +1 -44
  46. package/src/http/internal/HttpLlmApplicationComposer.ts +3 -9
  47. package/src/utils/LlmJson.ts +141 -0
  48. package/src/utils/index.ts +1 -1
  49. package/src/utils/internal/coerceLlmArguments.ts +297 -0
  50. package/src/utils/internal/parseLenientJson.ts +731 -0
  51. package/src/utils/{stringifyValidationFailure.ts → internal/stringifyValidationFailure.ts} +66 -70
  52. package/lib/http/internal/LlmDataMerger.d.ts +0 -48
  53. package/lib/http/internal/LlmDataMerger.js +0 -60
  54. package/lib/http/internal/LlmDataMerger.js.map +0 -1
  55. package/lib/http/internal/LlmDataMerger.mjs +0 -59
  56. package/lib/http/internal/LlmDataMerger.mjs.map +0 -1
  57. package/lib/utils/stringifyValidationFailure.d.ts +0 -25
  58. package/lib/utils/stringifyValidationFailure.js.map +0 -1
  59. package/lib/utils/stringifyValidationFailure.mjs.map +0 -1
  60. package/src/http/internal/LlmDataMerger.ts +0 -73
@@ -1,17 +1,14 @@
1
1
  import {
2
2
  IJsonSchemaAttribute,
3
3
  IJsonSchemaTransformError,
4
- ILlmFunction,
5
4
  ILlmSchema,
6
5
  IResult,
7
6
  OpenApi,
8
7
  } from "@typia/interface";
9
8
 
10
- import { NamingConvention } from "../utils/NamingConvention";
11
9
  import { JsonDescriptor } from "../utils/internal/JsonDescriptor";
12
10
  import { LlmTypeChecker } from "../validators/LlmTypeChecker";
13
11
  import { OpenApiTypeChecker } from "../validators/OpenApiTypeChecker";
14
- import { OpenApiValidator } from "../validators/OpenApiValidator";
15
12
  import { LlmDescriptionInverter } from "./internal/LlmDescriptionInverter";
16
13
  import { LlmParametersFinder } from "./internal/LlmParametersComposer";
17
14
  import { OpenApiConstraintShifter } from "./internal/OpenApiConstraintShifter";
@@ -28,7 +25,6 @@ import { OpenApiConstraintShifter } from "./internal/OpenApiConstraintShifter";
28
25
  *
29
26
  * - {@link parameters}: Convert object schema to {@link ILlmSchema.IParameters}
30
27
  * - {@link schema}: Convert any schema to {@link ILlmSchema}
31
- * - {@link separate}: Split parameters into LLM-fillable vs human-required
32
28
  * - {@link invert}: Extract constraints from description back to schema
33
29
  *
34
30
  * Configuration options ({@link ILlmSchema.IConfig}):
@@ -624,279 +620,6 @@ export namespace LlmSchemaConverter {
624
620
  }),
625
621
  } satisfies OpenApi.IJsonSchema;
626
622
  };
627
-
628
- /* -----------------------------------------------------------
629
- SEPARATORS
630
- ----------------------------------------------------------- */
631
- /**
632
- * Separate parameters into LLM and human parts.
633
- *
634
- * Splits parameters based on predicate (human-side if true). Creates separate
635
- * schemas for LLM-fillable and human-required fields.
636
- *
637
- * @param props.parameters Parameters schema to separate
638
- * @param props.predicate Returns true for human-side properties
639
- * @param props.convention Key naming convention for separated types
640
- * @param props.equals Whether to use strict equality validation
641
- * @returns Separated LLM and human parameter schemas
642
- */
643
- export const separate = (props: {
644
- parameters: ILlmSchema.IParameters;
645
- predicate: (schema: ILlmSchema) => boolean;
646
- convention?: (key: string, type: "llm" | "human") => string;
647
- equals?: boolean;
648
- }): ILlmFunction.ISeparated => {
649
- const convention =
650
- props.convention ??
651
- ((key, type) => `${key}.${NamingConvention.capitalize(type)}`);
652
- const [llm, human] = separateObject({
653
- predicate: props.predicate,
654
- convention,
655
- $defs: props.parameters.$defs,
656
- schema: props.parameters,
657
- });
658
- if (llm === null || human === null)
659
- return {
660
- llm: (llm as ILlmSchema.IParameters | null) ?? {
661
- type: "object",
662
- properties: {} as Record<string, ILlmSchema>,
663
- required: [],
664
- additionalProperties: false,
665
- $defs: {},
666
- },
667
- human: human as ILlmSchema.IParameters | null,
668
- };
669
- const output: ILlmFunction.ISeparated = {
670
- llm: {
671
- ...llm,
672
- $defs: Object.fromEntries(
673
- Object.entries(props.parameters.$defs).filter(([key]) =>
674
- key.endsWith(".Llm"),
675
- ),
676
- ),
677
- additionalProperties: false,
678
- },
679
- human: {
680
- ...human,
681
- $defs: Object.fromEntries(
682
- Object.entries(props.parameters.$defs).filter(([key]) =>
683
- key.endsWith(".Human"),
684
- ),
685
- ),
686
- additionalProperties: false,
687
- },
688
- };
689
- for (const key of Object.keys(props.parameters.$defs))
690
- if (key.endsWith(".Llm") === false && key.endsWith(".Human") === false)
691
- delete props.parameters.$defs[key];
692
- if (Object.keys(output.llm.properties).length !== 0) {
693
- const components: OpenApi.IComponents = {};
694
- output.validate = OpenApiValidator.create({
695
- components,
696
- schema: invert({
697
- components,
698
- schema: output.llm,
699
- $defs: output.llm.$defs,
700
- }),
701
- required: true,
702
- equals: props.equals,
703
- });
704
- }
705
- return output;
706
- };
707
-
708
- const separateStation = (props: {
709
- predicate: (schema: ILlmSchema) => boolean;
710
- convention: (key: string, type: "llm" | "human") => string;
711
- $defs: Record<string, ILlmSchema>;
712
- schema: ILlmSchema;
713
- }): [ILlmSchema | null, ILlmSchema | null] => {
714
- if (props.predicate(props.schema) === true) return [null, props.schema];
715
- else if (
716
- LlmTypeChecker.isUnknown(props.schema) ||
717
- LlmTypeChecker.isAnyOf(props.schema)
718
- )
719
- return [props.schema, null];
720
- else if (LlmTypeChecker.isObject(props.schema))
721
- return separateObject({
722
- predicate: props.predicate,
723
- convention: props.convention,
724
- $defs: props.$defs,
725
- schema: props.schema,
726
- });
727
- else if (LlmTypeChecker.isArray(props.schema))
728
- return separateArray({
729
- predicate: props.predicate,
730
- convention: props.convention,
731
- $defs: props.$defs,
732
- schema: props.schema,
733
- });
734
- else if (LlmTypeChecker.isReference(props.schema))
735
- return separateReference({
736
- predicate: props.predicate,
737
- convention: props.convention,
738
- $defs: props.$defs,
739
- schema: props.schema,
740
- });
741
- return [props.schema, null];
742
- };
743
-
744
- const separateArray = (props: {
745
- predicate: (schema: ILlmSchema) => boolean;
746
- convention: (key: string, type: "llm" | "human") => string;
747
- $defs: Record<string, ILlmSchema>;
748
- schema: ILlmSchema.IArray;
749
- }): [ILlmSchema.IArray | null, ILlmSchema.IArray | null] => {
750
- const [x, y] = separateStation({
751
- predicate: props.predicate,
752
- convention: props.convention,
753
- $defs: props.$defs,
754
- schema: props.schema.items,
755
- });
756
- return [
757
- x !== null
758
- ? {
759
- ...props.schema,
760
- items: x,
761
- }
762
- : null,
763
- y !== null
764
- ? {
765
- ...props.schema,
766
- items: y,
767
- }
768
- : null,
769
- ];
770
- };
771
-
772
- const separateObject = (props: {
773
- $defs: Record<string, ILlmSchema>;
774
- predicate: (schema: ILlmSchema) => boolean;
775
- convention: (key: string, type: "llm" | "human") => string;
776
- schema: ILlmSchema.IObject;
777
- }): [ILlmSchema.IObject | null, ILlmSchema.IObject | null] => {
778
- // EMPTY OBJECT
779
- if (
780
- Object.keys(props.schema.properties ?? {}).length === 0 &&
781
- !!props.schema.additionalProperties === false
782
- )
783
- return [props.schema, null];
784
-
785
- const llm = {
786
- ...props.schema,
787
- properties: {} as Record<string, ILlmSchema>,
788
- additionalProperties: props.schema.additionalProperties,
789
- } satisfies ILlmSchema.IObject;
790
- const human = {
791
- ...props.schema,
792
- properties: {} as Record<string, ILlmSchema>,
793
- } satisfies ILlmSchema.IObject;
794
-
795
- for (const [key, value] of Object.entries(props.schema.properties ?? {})) {
796
- const [x, y] = separateStation({
797
- predicate: props.predicate,
798
- convention: props.convention,
799
- $defs: props.$defs,
800
- schema: value,
801
- });
802
- if (x !== null) llm.properties[key] = x;
803
- if (y !== null) human.properties[key] = y;
804
- }
805
- if (
806
- typeof props.schema.additionalProperties === "object" &&
807
- props.schema.additionalProperties !== null
808
- ) {
809
- const [dx, dy] = separateStation({
810
- predicate: props.predicate,
811
- convention: props.convention,
812
- $defs: props.$defs,
813
- schema: props.schema.additionalProperties,
814
- });
815
- llm.additionalProperties = dx ?? false;
816
- human.additionalProperties = dy ?? false;
817
- }
818
- return [
819
- !!Object.keys(llm.properties).length || !!llm.additionalProperties
820
- ? shrinkRequired(llm)
821
- : null,
822
- !!Object.keys(human.properties).length || human.additionalProperties
823
- ? shrinkRequired(human)
824
- : null,
825
- ];
826
- };
827
-
828
- const separateReference = (props: {
829
- predicate: (schema: ILlmSchema) => boolean;
830
- convention: (key: string, type: "llm" | "human") => string;
831
- $defs: Record<string, ILlmSchema>;
832
- schema: ILlmSchema.IReference;
833
- }): [ILlmSchema.IReference | null, ILlmSchema.IReference | null] => {
834
- const key: string =
835
- props.schema.$ref.split("#/$defs/")[1] ??
836
- props.schema.$ref.split("/").at(-1)!;
837
- const humanKey: string = props.convention(key, "human");
838
- const llmKey: string = props.convention(key, "llm");
839
-
840
- // FIND EXISTING
841
- if (props.$defs?.[humanKey] || props.$defs?.[llmKey])
842
- return [
843
- props.$defs?.[llmKey]
844
- ? {
845
- ...props.schema,
846
- $ref: `#/$defs/${llmKey}`,
847
- }
848
- : null,
849
- props.$defs?.[humanKey]
850
- ? {
851
- ...props.schema,
852
- $ref: `#/$defs/${humanKey}`,
853
- }
854
- : null,
855
- ];
856
-
857
- // PRE-ASSIGNMENT
858
- props.$defs![llmKey] = {};
859
- props.$defs![humanKey] = {};
860
-
861
- // DO COMPOSE
862
- const schema: ILlmSchema = props.$defs?.[key]!;
863
- const [llm, human] = separateStation({
864
- predicate: props.predicate,
865
- convention: props.convention,
866
- $defs: props.$defs,
867
- schema,
868
- });
869
- if (llm !== null) Object.assign(props.$defs[llmKey], llm);
870
- if (human !== null) Object.assign(props.$defs[humanKey], human);
871
-
872
- // ONLY ONE
873
- if (llm === null || human === null) {
874
- delete props.$defs[llmKey];
875
- delete props.$defs[humanKey];
876
- return llm === null ? [null, props.schema] : [props.schema, null];
877
- }
878
-
879
- // BOTH OF THEM
880
- return [
881
- llm !== null
882
- ? {
883
- ...props.schema,
884
- $ref: `#/$defs/${llmKey}`,
885
- }
886
- : null,
887
- human !== null
888
- ? {
889
- ...props.schema,
890
- $ref: `#/$defs/${humanKey}`,
891
- }
892
- : null,
893
- ];
894
- };
895
-
896
- const shrinkRequired = (s: ILlmSchema.IObject): ILlmSchema.IObject => {
897
- s.required = s.required.filter((key) => s.properties?.[key] !== undefined);
898
- return s;
899
- };
900
623
  }
901
624
 
902
625
  const validateStrict = (
@@ -5,7 +5,6 @@ import {
5
5
  IHttpLlmFunction,
6
6
  IHttpMigrateApplication,
7
7
  IHttpResponse,
8
- ILlmFunction,
9
8
  OpenApi,
10
9
  OpenApiV3,
11
10
  OpenApiV3_1,
@@ -15,7 +14,6 @@ import {
15
14
  import { HttpMigration } from "./HttpMigration";
16
15
  import { HttpLlmApplicationComposer } from "./internal/HttpLlmApplicationComposer";
17
16
  import { HttpLlmFunctionFetcher } from "./internal/HttpLlmFunctionFetcher";
18
- import { LlmDataMerger } from "./internal/LlmDataMerger";
19
17
 
20
18
  /**
21
19
  * LLM function calling utilities for OpenAPI documents.
@@ -92,8 +90,7 @@ export namespace HttpLlm {
92
90
  /**
93
91
  * Convert OpenAPI document to LLM function calling application.
94
92
  *
95
- * Converts API operations to LLM-callable functions. Use
96
- * {@link mergeParameters} if `separate` option is configured.
93
+ * Converts API operations to LLM-callable functions.
97
94
  *
98
95
  * @param props Composition properties
99
96
  * @returns LLM function calling application
@@ -118,7 +115,6 @@ export namespace HttpLlm {
118
115
  config: {
119
116
  reference: props.config?.reference ?? true,
120
117
  strict: props.config?.strict ?? false,
121
- separate: props.config?.separate ?? null,
122
118
  maxLength: props.config?.maxLength ?? 64,
123
119
  equals: props.config?.equals ?? false,
124
120
  },
@@ -168,43 +164,4 @@ export namespace HttpLlm {
168
164
  */
169
165
  export const propagate = (props: IFetchProps): Promise<IHttpResponse> =>
170
166
  HttpLlmFunctionFetcher.propagate(props);
171
-
172
- /* -----------------------------------------------------------
173
- MERGERS
174
- ----------------------------------------------------------- */
175
- /** Properties for parameter merging. */
176
- export interface IMergeProps {
177
- /** Target function metadata. */
178
- function: ILlmFunction;
179
-
180
- /** LLM-provided arguments. */
181
- llm: object | null;
182
-
183
- /** Human-provided arguments. */
184
- human: object | null;
185
- }
186
-
187
- /**
188
- * Merge separated parameters.
189
- *
190
- * Combines human and LLM parameters when `separate` option was used. Throws
191
- * error if `separate` was not configured.
192
- *
193
- * @param props Merge properties
194
- * @returns Merged parameters
195
- */
196
- export const mergeParameters = (props: IMergeProps): object =>
197
- LlmDataMerger.parameters(props);
198
-
199
- /**
200
- * Merge two values.
201
- *
202
- * Objects are merged at property level. Primitives return `y ?? x`.
203
- *
204
- * @param x First value
205
- * @param y Second value (preferred)
206
- * @returns Merged value
207
- */
208
- export const mergeValue = (x: unknown, y: unknown): unknown =>
209
- LlmDataMerger.value(x, y);
210
167
  }
@@ -9,7 +9,8 @@ import {
9
9
  OpenApi,
10
10
  } from "@typia/interface";
11
11
 
12
- import { LlmSchemaConverter } from "../../converters";
12
+ import { LlmSchemaConverter } from "../../converters/LlmSchemaConverter";
13
+ import { LlmJson } from "../../utils";
13
14
  import { OpenApiValidator } from "../../validators/OpenApiValidator";
14
15
 
15
16
  /**
@@ -33,7 +34,6 @@ export namespace HttpLlmApplicationComposer {
33
34
  }): IHttpLlmApplication => {
34
35
  // fill in config defaults
35
36
  const config: IHttpLlmApplication.IConfig = {
36
- separate: props.config?.separate ?? null,
37
37
  maxLength: props.config?.maxLength ?? 64,
38
38
  equals: props.config?.equals ?? false,
39
39
  reference: props.config?.reference ?? true,
@@ -256,17 +256,11 @@ export namespace HttpLlmApplicationComposer {
256
256
  path: props.route.path,
257
257
  name,
258
258
  parameters: llmParameters.value,
259
- separated: props.config.separate
260
- ? LlmSchemaConverter.separate({
261
- predicate: props.config.separate,
262
- parameters: llmParameters.value,
263
- equals: props.config.equals ?? false,
264
- })
265
- : undefined,
266
259
  output: output?.value,
267
260
  description,
268
261
  deprecated: operation.deprecated,
269
262
  tags: operation.tags,
263
+ parse: (input: string) => LlmJson.parse(input, llmParameters.value),
270
264
  validate: OpenApiValidator.create({
271
265
  components: props.components,
272
266
  schema: parameters,
@@ -0,0 +1,141 @@
1
+ import {
2
+ IJsonParseResult,
3
+ ILlmSchema,
4
+ IValidation,
5
+ OpenApi,
6
+ } from "@typia/interface";
7
+
8
+ import { LlmSchemaConverter } from "../converters";
9
+ import { OpenApiValidator } from "../validators";
10
+ import { coerceLlmArguments } from "./internal/coerceLlmArguments";
11
+ import { parseLenientJson } from "./internal/parseLenientJson";
12
+ import { stringifyValidationFailure } from "./internal/stringifyValidationFailure";
13
+
14
+ /**
15
+ * JSON utilities for LLM function calling.
16
+ *
17
+ * - {@link LlmJson.parse}: Lenient JSON parser for incomplete/malformed JSON
18
+ * - {@link LlmJson.stringify}: Format validation errors for LLM feedback
19
+ * - {@link LlmJson.validate}: Create a reusable validator from schema
20
+ *
21
+ * @author Jeongho Nam - https://github.com/samchon
22
+ */
23
+ export namespace LlmJson {
24
+ /**
25
+ * Coerce LLM arguments to match expected schema types.
26
+ *
27
+ * LLMs often return values with incorrect types (e.g., numbers as strings).
28
+ * This function recursively coerces values based on the schema:
29
+ *
30
+ * - `"42"` → `42` (when schema expects number)
31
+ * - `"true"` → `true` (when schema expects boolean)
32
+ * - `"null"` → `null` (when schema expects null)
33
+ * - `"{...}"` → `{...}` (when schema expects object)
34
+ * - `"[...]"` → `[...]` (when schema expects array)
35
+ *
36
+ * Use this when SDK provides already-parsed objects but values may have
37
+ * wrong types. For raw JSON strings, use {@link parse} instead.
38
+ *
39
+ * @param input Parsed arguments object from LLM
40
+ * @param parameters LLM function parameters schema for type coercion
41
+ * @returns Coerced arguments with corrected types
42
+ */
43
+ export function coerce<T = unknown>(
44
+ input: T,
45
+ parameters: ILlmSchema.IParameters,
46
+ ): T {
47
+ return coerceLlmArguments(input, parameters);
48
+ }
49
+
50
+ /**
51
+ * Parse lenient JSON with optional schema-based coercion.
52
+ *
53
+ * Handles incomplete/malformed JSON commonly produced by LLMs:
54
+ *
55
+ * - Unclosed brackets, strings, trailing commas
56
+ * - JavaScript-style comments (`//` and multi-line)
57
+ * - Unquoted object keys, incomplete keywords (`tru`, `fal`, `nul`)
58
+ * - Markdown code block extraction, junk prefix skipping
59
+ *
60
+ * When `parameters` schema is provided, also coerces double-stringified
61
+ * values: `"42"` → `42`, `"true"` → `true`, `"{...}"` → `{...}` based on
62
+ * expected types.
63
+ *
64
+ * Type validation is NOT performed - use {@link ILlmFunction.validate}.
65
+ *
66
+ * @param input Raw JSON string (potentially incomplete or malformed)
67
+ * @param parameters Optional LLM parameters schema for type coercion
68
+ * @returns Parse result with data on success, or partial data with errors
69
+ */
70
+ export function parse<T = unknown>(
71
+ input: string,
72
+ parameters?: ILlmSchema.IParameters,
73
+ ): IJsonParseResult<T> {
74
+ const result: IJsonParseResult<T> = parseLenientJson<T>(input);
75
+
76
+ // Apply schema-based coercion if parameters provided and parsing succeeded
77
+ if (parameters !== undefined && result.success) {
78
+ return {
79
+ success: true,
80
+ data: coerceLlmArguments(result.data, parameters) as T,
81
+ };
82
+ }
83
+ return result;
84
+ }
85
+
86
+ /**
87
+ * Format validation failure for LLM auto-correction feedback.
88
+ *
89
+ * When LLM generates invalid function call arguments, this produces annotated
90
+ * JSON with inline `// ❌` error comments at each invalid property. The output
91
+ * is wrapped in a markdown code block so that LLM can understand and correct
92
+ * its mistakes in the next turn.
93
+ *
94
+ * Below is an example of the output format:
95
+ *
96
+ * ```json
97
+ * {
98
+ * "name": "John",
99
+ * "age": "twenty", // ❌ [{"path":"$input.age","expected":"number & Type<\"uint32\">"}]
100
+ * "email": "not-an-email", // ❌ [{"path":"$input.email","expected":"string & Format<\"email\">"}]
101
+ * "hobbies": "reading" // ❌ [{"path":"$input.hobbies","expected":"Array<string>"}]
102
+ * }
103
+ * ```
104
+ *
105
+ * @param failure Validation failure from {@link ILlmFunction.validate}
106
+ */
107
+ export function stringify(failure: IValidation.IFailure): string {
108
+ return stringifyValidationFailure(failure);
109
+ }
110
+
111
+ /**
112
+ * Create a reusable validator from LLM parameters schema.
113
+ *
114
+ * When validation fails, format the failure with {@link stringify} for LLM
115
+ * auto-correction feedback.
116
+ *
117
+ * @param parameters LLM function parameters schema
118
+ * @param equals If `true`, reject extraneous properties not defined in the
119
+ * schema. Otherwise, extra properties are ignored.
120
+ * @returns Validator function that checks data against the schema
121
+ */
122
+ export function validate(
123
+ parameters: ILlmSchema.IParameters,
124
+ equals?: boolean | undefined,
125
+ ) {
126
+ const components: OpenApi.IComponents = {
127
+ schemas: {},
128
+ };
129
+ const schema: OpenApi.IJsonSchema = LlmSchemaConverter.invert({
130
+ components,
131
+ schema: parameters,
132
+ $defs: parameters.$defs,
133
+ });
134
+ return OpenApiValidator.create({
135
+ components,
136
+ schema,
137
+ required: true,
138
+ equals,
139
+ });
140
+ }
141
+ }
@@ -1,8 +1,8 @@
1
1
  export * from "./ArrayUtil";
2
+ export * from "./LlmJson";
2
3
  export * from "./MapUtil";
3
4
  export * from "./NamingConvention";
4
5
  export * from "./Singleton";
5
6
  export * from "./StringUtil";
6
7
 
7
8
  export * from "./dedent";
8
- export * from "./stringifyValidationFailure";