Skip to content

Commit 5ee1136

Browse files
authored
fix(utils): enhance LlmJson.{parse|stringify} logics. (#1768)
* enhance logics * `parseLenientJson` completed. * enhanced * complete parseLenientJson * documentation * fix type * fix import path * fix typo * Rename from `ILlmJsonParseResult` to `IJsonParseResult` * publish next version * more example case
1 parent adec008 commit 5ee1136

File tree

75 files changed

+1773
-298
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

75 files changed

+1773
-298
lines changed

benchmark/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"private": true,
33
"name": "@typia/benchmark",
4-
"version": "12.0.0-dev.20260306",
4+
"version": "12.0.0-dev.20260307",
55
"description": "Benchmark program of typia performance",
66
"main": "bin/index.js",
77
"scripts": {

config/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"private": true,
33
"name": "@typia/config",
4-
"version": "12.0.0-dev.20260306",
4+
"version": "12.0.0-dev.20260307",
55
"description": "Shared build configuration for typia packages",
66
"devDependencies": {
77
"@rollup/plugin-commonjs": "catalog:rollup",

examples/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"private": true,
33
"name": "@typia/examples",
4-
"version": "12.0.0-dev.20260306",
4+
"version": "12.0.0-dev.20260307",
55
"description": "Example codes for typia website",
66
"scripts": {
77
"build": "rimraf bin && cross-env NODE_OPTIONS=\"--no-experimental-strip-types -r ts-node/register\" tsc && prettier --write bin/**/*.js",

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"private": true,
33
"name": "@typia/station",
4-
"version": "12.0.0-dev.20260306",
4+
"version": "12.0.0-dev.20260307",
55
"description": "Typia Station",
66
"scripts": {
77
"build": "pnpm --filter=./packages/* -r build",

packages/core/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@typia/core",
3-
"version": "12.0.0-dev.20260306",
3+
"version": "12.0.0-dev.20260307",
44
"description": "Superfast runtime validators with only one line",
55
"main": "src/index.ts",
66
"exports": {

packages/interface/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@typia/interface",
3-
"version": "12.0.0-dev.20260306",
3+
"version": "12.0.0-dev.20260307",
44
"description": "Superfast runtime validators with only one line",
55
"main": "src/index.ts",
66
"exports": {

packages/interface/src/http/IHttpLlmFunction.ts

Lines changed: 8 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -1,92 +1,31 @@
11
import { OpenApi } from "../openapi/OpenApi";
2-
import { ILlmSchema } from "../schema/ILlmSchema";
3-
import { IValidation } from "../schema/IValidation";
2+
import { ILlmFunction } from "../schema/ILlmFunction";
43
import { IHttpMigrateRoute } from "./IHttpMigrateRoute";
54

65
/**
76
* LLM function calling schema from OpenAPI operation.
87
*
9-
* `IHttpLlmFunction` represents a single HTTP endpoint converted to LLM
10-
* function calling format. Generated from {@link OpenApi.IOperation} as part of
11-
* {@link IHttpLlmApplication}.
8+
* Extends {@link ILlmFunction} with HTTP-specific properties. Generated from
9+
* {@link OpenApi.IOperation} as part of {@link IHttpLlmApplication}.
1210
*
13-
* Key properties:
11+
* - {@link method}, {@link path}: HTTP endpoint info
12+
* - {@link operation}: Source OpenAPI operation
13+
* - {@link route}: Source migration route
1414
*
15-
* - {@link name}: Function name (max 64 chars for OpenAI compatibility)
16-
* - {@link parameters}: Input schema with path/query/body merged
17-
* - {@link output}: Response schema (undefined if void)
18-
* - {@link description}: Critical for LLM function selection
19-
* - {@link validate}: Built-in argument validator for error feedback
20-
*
21-
* The {@link validate} function is essential: LLMs make frequent type errors
22-
* (e.g., `"123"` instead of `123`). Validate and retry improves success rate
23-
* from ~50% to 99%.
15+
* Inherits {@link parse} and {@link validate} from {@link ILlmFunction}.
2416
*
2517
* @author Jeongho Nam - https://github.com/samchon
2618
*/
27-
export interface IHttpLlmFunction {
19+
export interface IHttpLlmFunction extends ILlmFunction {
2820
/** HTTP method of the endpoint. */
2921
method: "get" | "post" | "patch" | "put" | "delete";
3022

3123
/** Path of the endpoint. */
3224
path: string;
3325

34-
/**
35-
* Function name composed from {@link IHttpMigrateRoute.accessor}.
36-
*
37-
* @maxLength 64
38-
*/
39-
name: string;
40-
41-
/**
42-
* Parameter schema.
43-
*
44-
* With keyword mode: single object with pathParameters, query, body merged.
45-
* Without keyword mode: array of [pathParameters..., query?, body?].
46-
*/
47-
parameters: ILlmSchema.IParameters;
48-
49-
/**
50-
* Return type as an object parameters schema.
51-
*
52-
* Wraps the return type in an {@link ILlmSchema.IParameters} object with
53-
* `$defs` for shared type definitions and `properties` for the structured
54-
* output. `undefined` if the endpoint returns void.
55-
*/
56-
output?: ILlmSchema.IParameters | undefined;
57-
58-
/** Function description for LLM context. Critical for function selection. */
59-
description?: string | undefined;
60-
61-
/** Whether the function is deprecated. */
62-
deprecated?: boolean | undefined;
63-
6426
/** Category tags from {@link OpenApi.IOperation.tags}. */
6527
tags?: string[];
6628

67-
/**
68-
* Lenient JSON parser with schema-based coercion.
69-
*
70-
* Parses incomplete/malformed JSON (unclosed brackets, trailing commas,
71-
* unclosed strings) and coerces double-stringified values using the
72-
* function's own {@link parameters} schema.
73-
*
74-
* This does NOT perform type validation — use {@link validate} after
75-
* parsing to check the result.
76-
*
77-
* @param str Raw JSON string from LLM output
78-
* @returns Validation result with parsed data or syntax errors
79-
*/
80-
parse: (str: string) => IValidation<unknown>;
81-
82-
/**
83-
* Validates LLM-composed arguments.
84-
*
85-
* LLMs frequently make type errors. Use this to provide validation feedback
86-
* and retry. Success rate improves from ~50% to 99% on retry.
87-
*/
88-
validate: (args: unknown) => IValidation<unknown>;
89-
9029
/** Returns the source {@link OpenApi.IOperation}. */
9130
operation: () => OpenApi.IOperation;
9231

Lines changed: 136 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,136 @@
1+
import { DeepPartial } from "../typings/DeepPartial";
2+
3+
/**
4+
* Result of lenient JSON parsing.
5+
*
6+
* `IJsonParseResult<T>` represents the result of parsing JSON that may be
7+
* incomplete, malformed, or contain non-standard syntax (e.g., unquoted keys,
8+
* trailing commas, missing quotes).
9+
*
10+
* Unlike standard JSON parsing which fails on any syntax error, lenient parsing
11+
* attempts to recover as much data as possible while reporting issues.
12+
*
13+
* Check the {@link IJsonParseResult.success} discriminator:
14+
*
15+
* - `true` → {@link IJsonParseResult.ISuccess} with parsed
16+
* {@link IJsonParseResult.ISuccess.data}
17+
* - `false` → {@link IJsonParseResult.IFailure} with partial
18+
* {@link IJsonParseResult.IFailure.data} and
19+
* {@link IJsonParseResult.IFailure.errors}
20+
*
21+
* @author Jeongho Nam - https://github.com/samchon
22+
* @template T The expected type after successful parsing
23+
*/
24+
export type IJsonParseResult<T = unknown> =
25+
| IJsonParseResult.ISuccess<T>
26+
| IJsonParseResult.IFailure<T>;
27+
28+
export namespace IJsonParseResult {
29+
/**
30+
* Successful parsing result.
31+
*
32+
* Indicates the JSON was parsed without any errors. The data may still have
33+
* been recovered from non-standard syntax (e.g., unquoted keys, trailing
34+
* commas), but no information was lost.
35+
*
36+
* @template T The parsed type
37+
*/
38+
export interface ISuccess<T = unknown> {
39+
/**
40+
* Success discriminator.
41+
*
42+
* Always `true` for successful parsing.
43+
*/
44+
success: true;
45+
46+
/** The parsed data with correct type. */
47+
data: T;
48+
}
49+
50+
/**
51+
* Failed parsing result with partial data and errors.
52+
*
53+
* Indicates the JSON had syntax errors that could not be fully recovered. The
54+
* {@link data} contains whatever could be parsed, and {@link errors} describes
55+
* what went wrong.
56+
*
57+
* @template T The expected type (data may be partial)
58+
*/
59+
export interface IFailure<T = unknown> {
60+
/**
61+
* Success discriminator.
62+
*
63+
* Always `false` for failed parsing.
64+
*/
65+
success: false;
66+
67+
/**
68+
* Partially parsed data.
69+
*
70+
* Contains whatever could be recovered from the malformed JSON. May be
71+
* incomplete or have missing properties.
72+
*/
73+
data: DeepPartial<T>;
74+
75+
/**
76+
* The original input string that was parsed.
77+
*
78+
* Preserved for debugging or error correction purposes.
79+
*/
80+
input: string;
81+
82+
/**
83+
* Array of parsing errors encountered.
84+
*
85+
* Each error describes a specific issue found during parsing, with location
86+
* and suggested fix.
87+
*/
88+
errors: IError[];
89+
}
90+
91+
/**
92+
* Detailed information about a parsing error.
93+
*/
94+
export interface IError {
95+
/**
96+
* Property path to the error location.
97+
*
98+
* A dot-notation path from the root to the error location. Uses `$input` as
99+
* the root.
100+
*
101+
* @example
102+
* $input.user.email;
103+
*
104+
* @example
105+
* $input.items[0].price;
106+
*/
107+
path: string;
108+
109+
/**
110+
* What was expected at this location.
111+
*
112+
* @example
113+
* JSON value (string, number, boolean, null, object, or array)
114+
*
115+
* @example
116+
* quoted string
117+
*
118+
* @example
119+
* ":";
120+
*/
121+
expected: string;
122+
123+
/**
124+
* Description of what was actually found.
125+
*
126+
* Human/AI-readable message explaining the issue.
127+
*
128+
* @example
129+
* unquoted string 'abc' - did you forget quotes?
130+
*
131+
* @example
132+
* missing opening quote for 'hello'
133+
*/
134+
value: unknown;
135+
}
136+
}

packages/interface/src/schema/ILlmFunction.ts

Lines changed: 21 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,18 @@
1+
import { IJsonParseResult } from "./IJsonParseResult";
12
import { ILlmSchema } from "./ILlmSchema";
23
import { IValidation } from "./IValidation";
34

45
/**
5-
* LLM function calling metadata.
6+
* LLM function calling schema for TypeScript functions.
67
*
7-
* `ILlmFunction` describes a single callable function for LLM agents. Generated
8-
* as part of {@link ILlmApplication} by `typia.llm.application<App>()`.
8+
* Generated by `typia.llm.application<App>()` as part of
9+
* {@link ILlmApplication}.
910
*
10-
* Contains the function {@link name} (max 64 chars for OpenAI),
11-
* {@link parameters} schema for input types, optional {@link output} schema for
12-
* return type, and {@link description} for LLM to understand the function's
13-
* purpose.
14-
*
15-
* The built-in {@link validate} function checks LLM-generated arguments against
16-
* the schema, enabling auto-correction when the LLM makes type errors (e.g.,
17-
* returning `"123"` instead of `123`).
11+
* - {@link name}: Function identifier (max 64 chars for OpenAI)
12+
* - {@link parameters}: Input schema, {@link output}: Return schema
13+
* - {@link description}: Guides LLM function selection
14+
* - {@link parse}: Lenient JSON parser with type coercion
15+
* - {@link validate}: Argument validator for LLM error correction
1816
*
1917
* @author Jeongho Nam - https://github.com/samchon
2018
*/
@@ -79,17 +77,22 @@ export interface ILlmFunction {
7977
/**
8078
* Lenient JSON parser with schema-based coercion.
8179
*
82-
* Parses incomplete/malformed JSON (unclosed brackets, trailing commas,
83-
* unclosed strings) and coerces double-stringified values using the
84-
* function's own {@link parameters} schema.
80+
* Handles incomplete/malformed JSON commonly produced by LLMs:
81+
*
82+
* - Unclosed brackets, strings, trailing commas
83+
* - JavaScript-style comments (`//` and multi-line)
84+
* - Unquoted object keys, incomplete keywords (`tru`, `fal`, `nul`)
85+
* - Markdown code block extraction, junk prefix skipping
86+
*
87+
* Also coerces double-stringified values (`"42"` → `42`) using the
88+
* {@link parameters} schema.
8589
*
86-
* This does NOT perform type validation — use {@link validate} after
87-
* parsing to check the result.
90+
* Type validation is NOT performed — use {@link validate} after parsing.
8891
*
8992
* @param str Raw JSON string from LLM output
90-
* @returns Validation result with parsed data or syntax errors
93+
* @returns Parse result with data on success, or partial data with errors
9194
*/
92-
parse: (str: string) => IValidation<unknown>;
95+
parse: (str: string) => IJsonParseResult<unknown>;
9396

9497
/**
9598
* Validates LLM-generated arguments against the schema.

packages/interface/src/schema/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,3 +11,4 @@ export * from "./ILlmController";
1111
export * from "./ILlmApplication";
1212
export * from "./ILlmFunction";
1313
export * from "./ILlmSchema";
14+
export * from "./IJsonParseResult";

0 commit comments

Comments
 (0)