Skip to content

Commit e9fc4ee

Browse files
committed
Merge branch 'master' of https://github.com/samchon/typia into doc/llm-json
2 parents 2dbc70c + 645a25d commit e9fc4ee

22 files changed

+598
-370
lines changed

packages/core/src/programmers/json/JsonApplicationProgrammer.ts

Lines changed: 24 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
import { IJsDocTagInfo, IJsonSchemaApplication } from "@typia/interface";
2+
import ts from "typescript";
23

4+
import { ITypiaContext } from "../../context/ITypiaContext";
5+
import { LiteralFactory } from "../../factories/LiteralFactory";
36
import { MetadataFactory } from "../../factories/MetadataFactory";
47
import { MetadataFunction } from "../../schemas/metadata/MetadataFunction";
58
import { MetadataObjectType } from "../../schemas/metadata/MetadataObjectType";
@@ -56,7 +59,26 @@ export namespace JsonApplicationProgrammer {
5659
return output;
5760
};
5861

59-
export const write = <Version extends "3.0" | "3.1">(props: {
62+
export interface IWriteProps<Version extends "3.0" | "3.1"> {
63+
context: ITypiaContext;
64+
version: Version;
65+
metadata: MetadataSchema;
66+
filter?: (prop: MetadataProperty) => boolean;
67+
}
68+
69+
export const write = <Version extends "3.0" | "3.1">(
70+
props: IWriteProps<Version>,
71+
): ts.Expression => {
72+
const app: IJsonSchemaApplication<Version> = writeApplication({
73+
version: props.version,
74+
metadata: props.metadata,
75+
filter: props.filter,
76+
});
77+
78+
return LiteralFactory.write(app);
79+
};
80+
81+
export const writeApplication = <Version extends "3.0" | "3.1">(props: {
6082
version: Version;
6183
metadata: MetadataSchema;
6284
filter?: (prop: MetadataProperty) => boolean;
@@ -105,7 +127,7 @@ export namespace JsonApplicationProgrammer {
105127
collect,
106128
}),
107129
);
108-
const { components, schemas } = JsonSchemasProgrammer.write({
130+
const { components, schemas } = JsonSchemasProgrammer.writeSchemas({
109131
version: props.version,
110132
metadatas: definitions,
111133
});

packages/core/src/programmers/json/JsonSchemaProgrammer.ts

Lines changed: 33 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,48 @@
11
import { IJsonSchemaUnit, OpenApi } from "@typia/interface";
2+
import ts from "typescript";
23

4+
import { ITypiaContext } from "../../context/ITypiaContext";
5+
import { LiteralFactory } from "../../factories/LiteralFactory";
36
import { MetadataSchema } from "../../schemas/metadata/MetadataSchema";
47
import { JsonSchemasProgrammer } from "./JsonSchemasProgrammer";
58

69
export namespace JsonSchemaProgrammer {
710
export const validate = (metadata: MetadataSchema): string[] =>
811
JsonSchemasProgrammer.validate(metadata);
912

10-
export const write = <Version extends "3.0" | "3.1">(props: {
13+
export interface IWriteProps<Version extends "3.0" | "3.1"> {
14+
context: ITypiaContext;
15+
version: Version;
16+
metadata: MetadataSchema;
17+
}
18+
19+
export const write = <Version extends "3.0" | "3.1">(
20+
props: IWriteProps<Version>,
21+
): ts.Expression => {
22+
const schema: IJsonSchemaUnit<Version> = writeSchema({
23+
version: props.version,
24+
metadata: props.metadata,
25+
});
26+
27+
return ts.factory.createAsExpression(
28+
LiteralFactory.write(schema),
29+
props.context.importer.type({
30+
file: "typia",
31+
name: "IJsonSchemaUnit",
32+
arguments: [
33+
ts.factory.createLiteralTypeNode(
34+
ts.factory.createStringLiteral(props.version),
35+
),
36+
],
37+
}),
38+
);
39+
};
40+
41+
export const writeSchema = <Version extends "3.0" | "3.1">(props: {
1142
version: Version;
1243
metadata: MetadataSchema;
1344
}): IJsonSchemaUnit<Version> => {
14-
const collection = JsonSchemasProgrammer.write({
45+
const collection = JsonSchemasProgrammer.writeSchemas({
1546
version: props.version,
1647
metadatas: [props.metadata],
1748
});

packages/core/src/programmers/json/JsonSchemasProgrammer.ts

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
11
import { IJsonSchemaCollection, OpenApi, OpenApiV3 } from "@typia/interface";
22
import { OpenApiConverter } from "@typia/utils";
3+
import ts from "typescript";
34

5+
import { ITypiaContext } from "../../context/ITypiaContext";
46
import { TransformerError } from "../../context/TransformerError";
7+
import { LiteralFactory } from "../../factories/LiteralFactory";
58
import { MetadataSchema } from "../../schemas/metadata/MetadataSchema";
69
import { AtomicPredicator } from "../helpers/AtomicPredicator";
710
import { json_schema_station } from "../iterate/json_schema_station";
@@ -36,7 +39,35 @@ export namespace JsonSchemasProgrammer {
3639
return output;
3740
};
3841

39-
export const write = <Version extends "3.0" | "3.1">(props: {
42+
export interface IWriteProps<Version extends "3.0" | "3.1"> {
43+
context: ITypiaContext;
44+
version: Version;
45+
metadatas: Array<MetadataSchema>;
46+
}
47+
48+
export const write = <Version extends "3.0" | "3.1">(
49+
props: IWriteProps<Version>,
50+
): ts.Expression => {
51+
const collection: IJsonSchemaCollection<Version> = writeSchemas({
52+
version: props.version,
53+
metadatas: props.metadatas,
54+
});
55+
56+
return ts.factory.createAsExpression(
57+
LiteralFactory.write(collection),
58+
props.context.importer.type({
59+
file: "typia",
60+
name: "IJsonSchemaCollection",
61+
arguments: [
62+
ts.factory.createLiteralTypeNode(
63+
ts.factory.createStringLiteral(props.version),
64+
),
65+
],
66+
}),
67+
);
68+
};
69+
70+
export const writeSchemas = <Version extends "3.0" | "3.1">(props: {
4071
version: Version;
4172
metadatas: Array<MetadataSchema>;
4273
}): IJsonSchemaCollection<Version> =>

packages/core/src/programmers/llm/LlmApplicationProgrammer.ts

Lines changed: 82 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,9 @@ import {
1111
import { LlmSchemaConverter } from "@typia/utils";
1212
import ts from "typescript";
1313

14+
import { IProgrammerProps } from "../../context/IProgrammerProps";
1415
import { ITypiaContext } from "../../context/ITypiaContext";
16+
import { LiteralFactory } from "../../factories/LiteralFactory";
1517
import { MetadataFactory } from "../../factories/MetadataFactory";
1618
import { TypeFactory } from "../../factories/TypeFactory";
1719
import { MetadataFunction } from "../../schemas/metadata/MetadataFunction";
@@ -32,6 +34,69 @@ import { LlmSchemaProgrammer } from "./LlmSchemaProgrammer";
3234
* @author Jeongho Nam - https://github.com/samchon
3335
*/
3436
export namespace LlmApplicationProgrammer {
37+
export interface IProps extends IProgrammerProps {
38+
config?: Partial<
39+
ILlmSchema.IConfig & {
40+
equals: boolean;
41+
}
42+
>;
43+
}
44+
45+
export interface IWriteProps {
46+
context: ITypiaContext;
47+
modulo: ts.LeftHandSideExpression;
48+
metadata: MetadataSchema;
49+
config?: Partial<
50+
ILlmSchema.IConfig & {
51+
equals: boolean;
52+
}
53+
>;
54+
name?: string;
55+
}
56+
57+
export const write = (props: IWriteProps): ts.CallExpression => {
58+
const application: ILlmApplication.__IPrimitive = writeApplication({
59+
context: props.context,
60+
modulo: props.modulo,
61+
config: props.config,
62+
metadata: props.metadata,
63+
name: props.name,
64+
});
65+
66+
const typeNode: ts.ImportTypeNode = props.context.importer.type({
67+
file: "typia",
68+
name: "ILlmApplication.__IPrimitive",
69+
arguments: props.name
70+
? [ts.factory.createTypeReferenceNode(props.name)]
71+
: undefined,
72+
});
73+
74+
return ts.factory.createCallExpression(
75+
props.context.importer.internal("llmApplicationFinalize"),
76+
props.name ? [ts.factory.createTypeReferenceNode(props.name)] : undefined,
77+
[
78+
ts.factory.createAsExpression(
79+
ts.factory.createSatisfiesExpression(
80+
LiteralFactory.write(application),
81+
typeNode,
82+
),
83+
typeNode,
84+
),
85+
ts.factory.createObjectLiteralExpression(
86+
[
87+
ts.factory.createPropertyAssignment(
88+
"equals",
89+
props.config?.equals === true
90+
? ts.factory.createTrue()
91+
: ts.factory.createFalse(),
92+
),
93+
],
94+
false,
95+
),
96+
],
97+
);
98+
};
99+
35100
export const validate = (props: {
36101
config?: Partial<ILlmSchema.IConfig>;
37102
metadata: MetadataSchema;
@@ -75,7 +140,8 @@ export namespace LlmApplicationProgrammer {
75140
);
76141
let least: boolean = false; // tracks whether at least one function exists
77142
for (const p of object.properties) {
78-
const name: string = JSON.stringify(p.key.getSoleLiteral()!);
143+
const rawName: string = p.key.getSoleLiteral()!;
144+
const name: string = JSON.stringify(rawName);
79145
const value: MetadataSchema = p.value;
80146
if (value.functions.length) {
81147
least ||= true;
@@ -94,6 +160,17 @@ export namespace LlmApplicationProgrammer {
94160
);
95161
}
96162

163+
// validate function name length and pattern
164+
const prefix: string = `LLM application's function (${name})`;
165+
if (/^[0-9]/.test(rawName[0] ?? "") === true)
166+
output.push(`${prefix} name cannot start with a number.`);
167+
if (/^[a-zA-Z0-9_-]+$/.test(rawName) === false)
168+
output.push(
169+
`${prefix} name must contain only alphanumeric characters, underscores, or hyphens.`,
170+
);
171+
if (rawName.length > 64)
172+
output.push(`${prefix} name cannot exceed 64 characters.`);
173+
97174
const description: string | undefined = concatDescription(
98175
JsonApplicationProgrammer.writeDescription({
99176
description:
@@ -179,7 +256,7 @@ export namespace LlmApplicationProgrammer {
179256
return messages;
180257
};
181258

182-
export const write = (props: {
259+
export const writeApplication = (props: {
183260
context: ITypiaContext;
184261
modulo: ts.LeftHandSideExpression;
185262
metadata: MetadataSchema;
@@ -222,14 +299,14 @@ export namespace LlmApplicationProgrammer {
222299
// build JSON Schema application, filtering out @human-tagged parameters
223300
const errorMessages: string[] = [];
224301
const application: IJsonSchemaApplication<"3.1"> =
225-
JsonApplicationProgrammer.write({
302+
JsonApplicationProgrammer.writeApplication({
226303
version: "3.1",
227304
metadata,
228305
filter: (p) =>
229306
p.jsDocTags.some((tag) => tag.name === "human") === false,
230307
});
231308
// convert each JSON Schema function to an LLM function
232-
const functions: Array<Omit<ILlmFunction, "parse"> | null> =
309+
const functions: Array<Omit<ILlmFunction, "parse" | "coerce"> | null> =
233310
application.functions.map((func) =>
234311
writeFunction({
235312
context: props.context,
@@ -262,7 +339,7 @@ export namespace LlmApplicationProgrammer {
262339
}
263340
>
264341
| undefined;
265-
}): Omit<ILlmFunction, "parse"> | null => {
342+
}): Omit<ILlmFunction, "parse" | "coerce"> | null => {
266343
const config: ILlmSchema.IConfig = LlmSchemaConverter.getConfig(
267344
props.config,
268345
);

packages/core/src/programmers/llm/LlmCoerceProgrammer.ts

Lines changed: 18 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,6 @@ import { IdentifierFactory } from "../../factories/IdentifierFactory";
66
import { LiteralFactory } from "../../factories/LiteralFactory";
77
import { MetadataFactory } from "../../factories/MetadataFactory";
88
import { StatementFactory } from "../../factories/StatementFactory";
9-
import { TypeFactory } from "../../factories/TypeFactory";
10-
import { MetadataCollection } from "../../schemas/metadata/MetadataCollection";
119
import { MetadataSchema } from "../../schemas/metadata/MetadataSchema";
1210
import { FunctionProgrammer } from "../helpers/FunctionProgrammer";
1311
import { FeatureProgrammer } from "../internal/FeatureProgrammer";
@@ -23,23 +21,18 @@ export namespace LlmCoerceProgrammer {
2321
config?: Partial<ILlmSchema.IConfig>;
2422
modulo: ts.LeftHandSideExpression;
2523
functor: FunctionProgrammer;
26-
type: ts.Type;
24+
metadata: MetadataSchema;
2725
name: string | undefined;
2826
}): FeatureProgrammer.IDecomposed => {
29-
// Generate LLM schema from type
30-
const schema: ILlmSchema.IParameters = writeSchema({
31-
context: props.context,
32-
config: props.config,
33-
type: props.type,
34-
});
35-
36-
const typeName =
37-
props.name ??
38-
TypeFactory.getFullName({
39-
checker: props.context.checker,
40-
type: props.type,
27+
// Generate LLM schema from metadata
28+
const schema: ILlmSchema.IParameters =
29+
LlmParametersProgrammer.writeParameters({
30+
metadata: props.metadata,
31+
config: props.config,
4132
});
4233

34+
const typeName = props.name ?? "unknown";
35+
4336
return {
4437
functions: {},
4538
statements: [
@@ -78,7 +71,15 @@ export namespace LlmCoerceProgrammer {
7871
};
7972
};
8073

81-
export const write = (props: IProps): ts.CallExpression => {
74+
export interface IWriteProps {
75+
context: IProps["context"];
76+
modulo: ts.LeftHandSideExpression;
77+
metadata: MetadataSchema;
78+
config?: Partial<ILlmSchema.IConfig>;
79+
name?: string;
80+
}
81+
82+
export const write = (props: IWriteProps): ts.CallExpression => {
8283
const functor: FunctionProgrammer = new FunctionProgrammer(
8384
props.modulo.getText(),
8485
);
@@ -87,7 +88,7 @@ export namespace LlmCoerceProgrammer {
8788
config: props.config,
8889
modulo: props.modulo,
8990
functor,
90-
type: props.type,
91+
metadata: props.metadata,
9192
name: props.name,
9293
});
9394
return FeatureProgrammer.writeDecomposed({
@@ -102,30 +103,4 @@ export namespace LlmCoerceProgrammer {
102103
metadata: MetadataSchema;
103104
explore: MetadataFactory.IExplore;
104105
}): string[] => LlmParametersProgrammer.validate(props);
105-
106-
const writeSchema = (props: {
107-
context: IProps["context"];
108-
config?: Partial<ILlmSchema.IConfig>;
109-
type: ts.Type;
110-
}): ILlmSchema.IParameters => {
111-
const result = MetadataFactory.analyze({
112-
checker: props.context.checker,
113-
transformer: props.context.transformer,
114-
options: {
115-
absorb: false,
116-
escape: true,
117-
constant: true,
118-
},
119-
components: new MetadataCollection({
120-
replace: MetadataCollection.replace,
121-
}),
122-
type: props.type,
123-
});
124-
if (result.success === false)
125-
throw new Error("Failed to analyze type for LLM coerce.");
126-
return LlmParametersProgrammer.write({
127-
metadata: result.data,
128-
config: props.config,
129-
});
130-
};
131106
}

0 commit comments

Comments
 (0)