application() function
undefined
export namespace llm {
// LLM FUNCTION CALLING APPLICATION SCHEMA
export function application<
App extends Record<string, any>,
Config extends Partial<ILlmSchema.IConfig> = {},
>(
config?: Partial<Pick<ILlmApplication.IConfig, "separate">>,
): ILlmApplication;
// STRUCTURED OUTPUT
export function parameters<
Parameters extends Record<string, any>,
Config extends Partial<ILlmSchema.IConfig> = {},
>(): ILlmSchema.IParameters;
// TYPE SCHEMA
export function schema<
T,
Config extends Partial<ILlmSchema.IConfig> = {},
>(
$defs: Record<string, ILlmSchema>,
): ILlmSchema;
}undefined
import { ILlmFunction } from "./ILlmFunction";
import { ILlmSchema } from "./ILlmSchema";
import { IValidation } from "./IValidation";
/**
* Application of LLM function calling.
*
* `ILlmApplication` is a data structure representing a collection of
* {@link ILlmFunction LLM function calling schemas}, composed from a native
* TypeScript class (or interface) type by the `typia.llm.application<App>()`
* function.
*
* Also, there can be some parameters (or their nested properties) which must be
* composed by Human, not by LLM. File uploading feature or some sensitive
* information like secret key (password) are the examples. In that case, you
* can separate the function parameters to both LLM and human sides by
* configuring the {@link ILlmApplication.IConfig.separate} property. The
* separated parameters are assigned to the {@link ILlmFunction.separated}
* property.
*
* For reference, when both LLM and Human filled parameter values to call, you
* can merge them by calling the {@link HttpLlm.mergeParameters} function. In
* other words, if you've configured the {@link ILlmApplication.IConfig.separate}
* property, you have to merge the separated parameters before the function call
* execution.
*
* @author Jeongho Nam - https://github.com/samchon
* @reference https://platform.openai.com/docs/guides/function-calling
*/
export interface ILlmApplication<Class extends object = any> {
/**
* List of function metadata.
*
* List of function metadata that can be used for the LLM function call.
*/
functions: ILlmFunction[];
/** Configuration for the application. */
config: ILlmApplication.IConfig<Class>;
/**
* Class type, the source of the LLM application.
*
* This property is just for the generic type inference, and its value is
* always `undefined`.
*/
__class?: Class | undefined;
}
export namespace ILlmApplication {
/** Configuration for application composition. */
export interface IConfig<Class extends object = any>
extends ILlmSchema.IConfig {
/**
* Separator function for the parameters.
*
* When composing parameter arguments through LLM function call, there can
* be a case that some parameters must be composed by human, or LLM cannot
* understand the parameter.
*
* For example, if the parameter type has configured
* {@link ILlmSchema.IString.contentMediaType} which indicates file
* uploading, it must be composed by human, not by LLM (Large Language
* Model).
*
* In that case, if you configure this property with a function that
* predicating whether the schema value must be composed by human or not,
* the parameters would be separated into two parts.
*
* - {@link ILlmFunction.separated.llm}
* - {@link ILlmFunction.separated.human}
*
* When writing the function, note that returning value `true` means to be a
* human composing the value, and `false` means to LLM composing the value.
* Also, when predicating the schema, it would better to utilize the
* {@link LlmTypeChecker} like features.
*
* @default null
* @param schema Schema to be separated.
* @returns Whether the schema value must be composed by human or not.
*/
separate: null | ((schema: ILlmSchema) => boolean);
/**
* Custom validation functions for specific class methods.
*
* The `validate` property allows you to provide custom validation functions
* that will replace the default validation behavior for specific methods
* within the application class. When specified, these custom validators
* take precedence over the standard type validation generated by
* `typia.llm.application()`.
*
* This feature is particularly useful when you need to:
*
* - Implement business logic validation beyond type checking
* - Add custom constraints that cannot be expressed through type annotations
* - Provide more specific error messages for AI agents
* - Validate dynamic conditions based on runtime state
*
* Each validation function receives the same arguments as its corresponding
* method and must return an {@link IValidation} result. On validation
* success, it should return `{ success: true, data }`. On failure, it
* should return `{ success: false, data, errors }` with detailed error
* information that helps AI agents understand and correct their mistakes.
*
* @default null
*/
validate: null | Partial<ILlmApplication.IValidationHook<Class>>;
}
/**
* Type for custom validation function hooks.
*
* `IValidationHook` defines the structure for custom validation functions
* that can be provided for each method in the application class. It creates a
* mapped type where each property corresponds to a method in the class, and
* the value is a validation function for that method's parameters.
*
* The validation hook functions:
*
* - Receive the same argument type as the original method
* - Must return an {@link IValidation} result indicating success or failure
* - Replace the default type validation when specified
* - Enable custom business logic and runtime validation
*
* Type constraints:
*
* - Only methods (functions) from the class can have validation hooks
* - Non-function properties are typed as `never` and cannot be validated
* - The validation function must match the method's parameter signature
*
* @template Class The application class type containing methods to validate
*/
export type IValidationHook<Class extends object> = {
[K in keyof Class]?: Class[K] extends (args: infer Argument) => unknown
? (input: unknown) => IValidation<Argument>
: never;
};
}undefined
import { ILlmSchema } from "./ILlmSchema";
import { IValidation } from "./IValidation";
/**
* LLM function metadata.
*
* `ILlmFunction` is an interface representing a function metadata, which has
* been used for the LLM (Language Large Model) function calling. Also, it's a
* function structure containing the function {@link name}, {@link parameters} and
* {@link output return type}.
*
* If you provide this `ILlmFunction` data to the LLM provider like "OpenAI",
* the "OpenAI" will compose a function arguments by analyzing conversations
* with the user. With the LLM composed arguments, you can execute the function
* and get the result.
*
* By the way, do not ensure that LLM will always provide the correct arguments.
* The LLM of present age is not perfect, so that you would better to validate
* the arguments before executing the function. I recommend you to validate the
* arguments before execution by using
* [`typia`](https://github.com/samchon/typia) library.
*
* @author Jeongho Nam - https://github.com/samchon
* @reference https://platform.openai.com/docs/guides/function-calling
*/
export interface ILlmFunction {
/**
* Representative name of the function.
*
* @maxLength 64
*/
name: string;
/** List of parameter types. */
parameters: ILlmSchema.IParameters;
/**
* Collection of separated parameters.
*
* Filled only when {@link ILlmApplication.IConfig.separate} is configured.
*/
separated?: ILlmFunction.ISeparated;
/**
* Expected return type.
*
* If the function returns nothing (`void`), the `output` value would be
* `undefined`.
*/
output?: ILlmSchema | undefined;
/**
* Description of the function.
*
* For reference, the `description` is a critical property for teaching the
* purpose of the function to LLMs (Large Language Models). LLMs use this
* description to determine which function to call.
*
* Also, when the LLM converses with the user, the `description` explains the
* function to the user. Therefore, the `description` property has the highest
* priority and should be carefully considered.
*/
description?: string | undefined;
/**
* Whether the function is deprecated or not.
*
* If the `deprecated` is `true`, the function is not recommended to use.
*
* LLM (Large Language Model) may not use the deprecated function.
*/
deprecated?: boolean | undefined;
/**
* Category tags for the function.
*
* You can fill this property by the `@tag ${name}` comment tag.
*/
tags?: string[] | undefined;
/**
* Validate function of the arguments.
*
* You know what? LLM (Large Language Model) like OpenAI takes a lot of
* mistakes when composing arguments in function calling. Even though `number`
* like simple type is defined in the {@link parameters} schema, LLM often
* fills it just by a `string` typed value.
*
* In that case, you have to give a validation feedback to the LLM by using
* this `validate` function. The `validate` function will return detailed
* information about every type errors about the arguments.
*
* And in my experience, OpenAI's `gpt-4o-mini` model tends to construct an
* invalid function calling arguments at the first trial about 50% of the
* time. However, if correct it through this `validate` function, the success
* rate soars to 99% at the second trial, and I've never failed at the third
* trial.
*
* > If you've {@link separated} parameters, use the
* > {@link ILlmFunction.ISeparated.validate} function instead when validating
* > the LLM composed arguments.
*
* > In that case, This `validate` function would be meaningful only when you've
* > merged the LLM and human composed arguments by
* > {@link HttpLlm.mergeParameters} function.
*
* @param args Arguments to validate
* @returns Validation result
*/
validate: (args: unknown) => IValidation<unknown>;
}
export namespace ILlmFunction {
/** Collection of separated parameters. */
export interface ISeparated {
/**
* Parameters that would be composed by the LLM.
*
* Even though no property exists in the LLM side, the `llm` property would
* have at least empty object type.
*/
llm: ILlmSchema.IParameters;
/** Parameters that would be composed by the human. */
human: ILlmSchema.IParameters | null;
/**
* Validate function of the separated arguments.
*
* If LLM part of separated parameters has some properties, this `validate`
* function will be filled for the {@link llm} type validation.
*
* > You know what? LLM (Large Language Model) like OpenAI takes a lot of
* > mistakes when composing arguments in function calling. Even though
* > `number` like simple type is defined in the {@link parameters} schema, LLM
* > often fills it just by a `string` typed value.
*
* > In that case, you have to give a validation feedback to the LLM by using
* > this `validate` function. The `validate` function will return detailed
* > information about every type errors about the arguments.
*
* > And in my experience, OpenAI's `gpt-4o-mini` model tends to construct an
* > invalid function calling arguments at the first trial about 50% of the
* > time. However, if correct it through this `validate` function, the
* > success rate soars to 99% at the second trial, and I've never failed at
* > the third trial.
*
* @param args Arguments to validate
* @returns Validate result
*/
validate?: ((args: unknown) => IValidation<unknown>) | undefined;
}
}undefined
import { IJsonSchemaAttribute } from "./IJsonSchemaAttribute";
/**
* Type schema info for LLM (Large Language Model) function calling.
*
* ## Overview
*
* `ILlmSchema` is a type schema info for LLM function calling, designed to be
* compatible with multiple LLM providers while following the JSON schema
* specification.
*
* ## Specification
*
* `ILlmSchema` basically follows the JSON schema definition of the OpenAPI v3.1
* specification; {@link OpenApiV3_1.IJsonSchema}.
*
* However, it deviates from the standard JSON schema specification and omits
* many features to ensure compatibility across different LLM providers and
* their function calling requirements.
*
* ## Differences from OpenAPI v3.1
*
* Here is the list of how `ILlmSchema` is different with the OpenAPI v3.1 JSON
* schema:
*
* - Decompose mixed type: {@link OpenApiV3_1.IJsonSchema.IMixed}
* - Resolve nullable property:
* {@link OpenApiV3_1.IJsonSchema.__ISignificant.nullable}
* - Tuple type is banned: {@link OpenApiV3_1.IJsonSchema.ITuple.prefixItems}
* - Constant type is banned: {@link OpenApiV3_1.IJsonSchema.IConstant}
* - Merge {@link OpenApiV3_1.IJsonSchema.IOneOf} to {@link ILlmSchema.IAnyOf}
* - Merge {@link OpenApiV3_1.IJsonSchema.IAllOf} to {@link ILlmSchema.IObject}
* - Merge {@link OpenApiV3_1.IJsonSchema.IRecursiveReference} to
* {@link ILlmSchema.IReference}
*
* ## Differences from OpenApi.IJsonSchema
*
* Compared to {@link OpenApi.IJsonSchema}, the emended JSON schema
* specification:
*
* - {@link ILlmSchema.IAnyOf} instead of {@link OpenApi.IJsonSchema.IOneOf}
* - {@link ILlmSchema.IParameters.$defs} instead of
* {@link OpenApi.IJsonSchema.IComponents.schemas}
* - Do not support {@link OpenApi.IJsonSchema.ITuple} type
* - {@link ILlmSchema.properties} and {@link ILlmSchema.required} are always
* defined
*
* ## Strict Mode
*
* When {@link ILlmSchema.IConfig.strict} mode is enabled, the schema
* transformation follows OpenAI's structured output requirements:
*
* - Every {@link ILlmSchema.IObject.additionalProperties} is forced to `false`
* - Every property in {@link ILlmSchema.IObject.properties} becomes
* {@link ILlmSchema.IObject.required}
* - All constraint properties are removed from the schema and moved to
* {@link IJsonSchemaAttribute.description} in a JSDoc-like format:
*
* - Numeric constraints: `minimum`, `maximum`, `exclusiveMinimum`,
* `exclusiveMaximum`, `multipleOf`
* - String constraints: `minLength`, `maxLength`, `pattern`, `format`,
* `contentMediaType`
* - Array constraints: `minItems`, `maxItems`, `uniqueItems`
* - Example: `@minimum 0`, `@maximum 100`, `@format uuid`
*
* @author Jeongho Nam - https://github.com/samchon
*/
export type ILlmSchema =
| ILlmSchema.IBoolean
| ILlmSchema.IInteger
| ILlmSchema.INumber
| ILlmSchema.IString
| ILlmSchema.IArray
| ILlmSchema.IObject
| ILlmSchema.IReference
| ILlmSchema.IAnyOf
| ILlmSchema.INull
| ILlmSchema.IUnknown;
export namespace ILlmSchema {
/** Configuration for the LLM schema composition. */
export interface IConfig {
/**
* Whether to allow reference type in everywhere.
*
* If you configure this property to `false`, most of reference types
* represented by {@link ILlmSchema.IReference} would be escaped to a plain
* type unless recursive type comes.
*
* This is because some LLM models do not understand the reference type
* well, and even the modern version of LLM sometimes occur the
* hallucination.
*
* However, the reference type makes the schema size smaller, so that
* reduces the LLM token cost. Therefore, if you're using the modern version
* of LLM, and want to reduce the LLM token cost, you can configure this
* property to `true`.
*
* @default true
*/
reference: boolean;
/**
* Whether to apply the strict mode.
*
* If you configure this property to `true`, the LLM function calling does
* not allow optional properties and dynamic key typed properties in the
* {@link ILlmSchema.IObject} type. In other words, when strict mode is
* enabled, {@link ILlmSchema.IObject.additionalProperties} is fixed to
* `false`, and every property must be {@link ILlmSchema.IObject.required}.
*
* However, the strict mode actually shows lower performance in practice. If
* you utilize the {@link typia.validate} function and give its validation
* feedback to the LLM, the performance is much better than the strict
* mode.
*
* Therefore, I recommend you to just turn off the strict mode and utilize
* the {@link typia.validate} function instead.
*
* @default false
*/
strict: boolean;
}
/**
* Type for function parameters.
*
* `ILlmSchema.IParameters` defines a function's parameters as a keyword
* object type, where each property represents a named parameter.
*
* It can also be used for structured output metadata to define the expected
* format of LLM responses.
*/
export interface IParameters extends Omit<IObject, "additionalProperties"> {
/**
* Collection of the named types.
*
* This record would be filled when {@link IConfig.reference} is `true`, or
* recursive type comes.
*/
$defs: Record<string, ILlmSchema>;
/**
* Additional properties information.
*
* The `additionalProperties` defines the type schema for additional
* properties that are not listed in the {@link properties}.
*
* By the way, it is not allowed at the parameters level.
*/
additionalProperties: false;
}
/** Boolean type info. */
export interface IBoolean extends IJsonSchemaAttribute.IBoolean {
/** Enumeration values. */
enum?: Array<boolean>;
/** Default value. */
default?: boolean;
}
/** Integer type info. */
export interface IInteger extends IJsonSchemaAttribute.IInteger {
/** Enumeration values. */
enum?: Array<number>;
/**
* Default value.
*
* @type int64
*/
default?: number;
/**
* Minimum value restriction.
*
* @type int64
*/
minimum?: number;
/**
* Maximum value restriction.
*
* @type int64
*/
maximum?: number;
/**
* Exclusive minimum value restriction.
*
* @type int64
*/
exclusiveMinimum?: number;
/**
* Exclusive maximum value restriction.
*
* @type int64
*/
exclusiveMaximum?: number;
/**
* Multiple of value restriction.
*
* @type uint64
* @exclusiveMinimum 0
*/
multipleOf?: number;
}
/** Number (double) type info. */
export interface INumber extends IJsonSchemaAttribute.INumber {
/** Enumeration values. */
enum?: Array<number>;
/** Default value. */
default?: number;
/** Minimum value restriction. */
minimum?: number;
/** Maximum value restriction. */
maximum?: number;
/** Exclusive minimum value restriction. */
exclusiveMinimum?: number;
/** Exclusive maximum value restriction. */
exclusiveMaximum?: number;
/**
* Multiple of value restriction.
*
* @exclusiveMinimum 0
*/
multipleOf?: number;
}
/** String type info. */
export interface IString extends IJsonSchemaAttribute.IString {
/** Enumeration values. */
enum?: Array<string>;
/** Default value. */
default?: string;
/** Format restriction. */
format?:
| "binary"
| "byte"
| "password"
| "regex"
| "uuid"
| "email"
| "hostname"
| "idn-email"
| "idn-hostname"
| "iri"
| "iri-reference"
| "ipv4"
| "ipv6"
| "uri"
| "uri-reference"
| "uri-template"
| "url"
| "date-time"
| "date"
| "time"
| "duration"
| "json-pointer"
| "relative-json-pointer"
| (string & {});
/** Pattern restriction. */
pattern?: string;
/** Content media type restriction. */
contentMediaType?: string;
/**
* Minimum length restriction.
*
* @type uint64
*/
minLength?: number;
/**
* Maximum length restriction.
*
* @type uint64
*/
maxLength?: number;
}
/** Array type info. */
export interface IArray extends IJsonSchemaAttribute.IArray {
/**
* Items type info.
*
* The `items` means the type of the array elements. In other words, it is
* the type schema info of the `T` in the TypeScript array type `Array<T>`.
*/
items: ILlmSchema;
/**
* Unique items restriction.
*
* If this property value is `true`, target array must have unique items.
*/
uniqueItems?: boolean;
/**
* Minimum items restriction.
*
* Restriction of minimum number of items in the array.
*
* @type uint64
*/
minItems?: number;
/**
* Maximum items restriction.
*
* Restriction of maximum number of items in the array.
*
* @type uint64
*/
maxItems?: number;
}
/** Object type info. */
export interface IObject extends IJsonSchemaAttribute.IObject {
/**
* Properties of the object.
*
* The `properties` means a list of key-value pairs of the object's regular
* properties. The key is the name of the regular property, and the value is
* the type schema info.
*/
properties: Record<string, ILlmSchema>;
/**
* Additional properties' info.
*
* The `additionalProperties` means the type schema info of the additional
* properties that are not listed in the {@link properties}.
*
* If the value is `true`, it means that the additional properties are not
* restricted. They can be any type. Otherwise, if the value is
* {@link ILlmSchema} type, it means that the additional properties must
* follow the type schema info.
*
* - `true`: `Record<string, any>`
* - `ILlmSchema`: `Record<string, T>`
*
* Note: When {@link IConfig.strict} mode is enabled, this property is always
* fixed to `false`, meaning no additional properties are allowed.
*/
additionalProperties?: ILlmSchema | boolean;
/**
* List of required property keys.
*
* The `required` contains a list of property keys from {@link properties}
* that must be provided. Properties not listed in `required` are optional,
* while those listed must be filled.
*
* Below is an example of {@link properties} and `required`:
*
* ```typescript
* interface SomeObject {
* id: string;
* email: string;
* name?: string;
* }
* ```
*
* As you can see, `id` and `email` {@link properties} are {@link required},
* so they are listed in the `required` array.
*
* ```json
* {
* "type": "object",
* "properties": {
* "id": { "type": "string" },
* "email": { "type": "string" },
* "name": { "type": "string" }
* },
* "required": ["id", "email"]
* }
* ```
*/
required: string[];
}
/**
* Reference type directing to named schema.
*
* If {@link IConfig.strict} mode is enabled, its other properties like
* {@link description} would be disabled. Instead, the description would be
* placed in the parent type. For example, if this reference type is used as a
* property of an object, the description would be placed in the object
* place.
*/
export interface IReference extends IJsonSchemaAttribute {
/**
* Reference to the named schema.
*
* The `$ref` is a reference to a named schema. The format follows the JSON
* Pointer specification. In OpenAPI, the `$ref` starts with `#/$defs/`
* which indicates the type is stored in the
* {@link ILlmSchema.IParameters.$defs} object.
*
* - `#/$defs/SomeObject`
* - `#/$defs/AnotherObject`
*/
$ref: string;
}
/**
* Union type.
*
* `IAnyOf` represents a union type in TypeScript (`A | B | C`).
*
* For reference, even if your Swagger (or OpenAPI) document defines `oneOf`
* instead of `anyOf`, {@link ILlmSchema} forcibly converts it to the
* `anyOf`-based {@link ILlmSchema.IAnyOf} type.
*/
export interface IAnyOf extends IJsonSchemaAttribute {
/** List of the union types. */
anyOf: Exclude<ILlmSchema, ILlmSchema.IAnyOf>[];
/**
* Discriminator info of the union type.
*
* This discriminator is used to determine which type in the union should be
* used based on the value of a specific property.
*/
"x-discriminator"?: IAnyOf.IDiscriminator;
}
export namespace IAnyOf {
/** Discriminator info of the union type. */
export interface IDiscriminator {
/** Property name for the discriminator. */
propertyName: string;
/**
* Mapping of discriminator values to schema names.
*
* This property is valid only for {@link IReference} typed
* {@link IAnyOf.anyOf} elements. Therefore, the `key` of `mapping` is the
* discriminator value, and the `value` of `mapping` is the schema name
* like `#/components/schemas/SomeObject`.
*/
mapping?: Record<string, string>;
}
}
/** Null type. */
export interface INull extends IJsonSchemaAttribute.INull {}
/** Unknown, the `any` type. */
export interface IUnknown extends IJsonSchemaAttribute.IUnknown {}
}LLM function calling application schema from a native TypeScript class or interface type.
typia.llm.application<App>() is a function composing LLM (Large Language Model) calling application schema from a native TypeScript class or interface type. The function returns an ILlmApplication instance, which is a data structure representing a collection of LLM function calling schemas.
If you put LLM function schema instances registered in the ILlmApplication.functions to the LLM provider like OpenAI ChatGPT, the LLM will select a proper function to call with parameter values of the target function in the conversations with the user. This is the “LLM Function Calling”.
Let’s make A.I. Chatbot super-easily with typia.llm.application<App>() function.
LLM Function Calling and Structured Output
LLM selects proper function and fill arguments.
In nowadays, most LLM (Large Language Model) like OpenAI are supporting “function calling” feature. The “LLM function calling” means that LLM automatically selects a proper function and fills parameter values from conversation with the user (may by chatting text).
Structured output is another feature of LLM. The “structured output” means that LLM automatically transforms the output conversation into a structured data format like JSON.
TypeScript Source Code
import { ILlmApplication } from "@samchon/openapi";
import typia from "typia";
import { BbsArticleService } from "./BbsArticleService";
const app: ILlmApplication = typia.llm.application<BbsArticleService>();
console.log(app);Compiled JavaScript
import typia from "typia";
import * as __typia_transform__isFormatUri from "typia/lib/internal/_isFormatUri.js";
import * as __typia_transform__isFormatUuid from "typia/lib/internal/_isFormatUuid.js";
import * as __typia_transform__validateReport from "typia/lib/internal/_validateReport.js";
const app = {
config: {
reference: true,
strict: false,
separate: null,
validate: null,
},
functions: [
{
name: "index",
parameters: {
type: "object",
properties: {},
additionalProperties: false,
required: [],
$defs: {
IBbsArticle: {
description:
"Article entity.\n\n`IBbsArticle` is an entity representing an article in the BBS (Bulletin Board System).",
type: "object",
properties: {
id: {
description: "Primary Key.",
type: "string",
format: "uuid",
},
created_at: {
description: "Creation time of the article.",
type: "string",
format: "date-time",
},
updated_at: {
description: "Last updated time of the article.",
type: "string",
format: "date-time",
},
title: {
description:
"Title of the article.\n\nRepresentative title of the article.",
type: "string",
},
body: {
description:
"Content body.\n\nContent body of the article written in the markdown format.",
type: "string",
},
thumbnail: {
description:
"Thumbnail image URI.\n\nThumbnail image URI which can represent the article.\n\nIf configured as `null`, it means that no thumbnail image in the article.",
anyOf: [
{
type: "null",
},
{
type: "string",
format: "uri",
contentMediaType: "image/*",
},
],
},
},
required: [
"id",
"created_at",
"updated_at",
"title",
"body",
"thumbnail",
],
},
},
},
output: {
description: "List of every articles",
type: "array",
items: {
$ref: "#/$defs/IBbsArticle",
},
},
description:
"Get all articles.\n\nList up every articles archived in the BBS DB.",
validate: (() => {
const __is = (input) => true;
let errors;
let _report;
return (input) => {
if (false === __is(input)) {
errors = [];
_report = __typia_transform__validateReport._validateReport(errors);
((input, _path, _exceptionable = true) => true)(
input,
"$input",
true,
);
const success = 0 === errors.length;
return success
? {
success,
data: input,
}
: {
success,
errors,
data: input,
};
}
return {
success: true,
data: input,
};
};
})(),
},
{
name: "create",
parameters: {
description: " Properties of create function",
type: "object",
properties: {
input: {
description: "Information of the article to create",
$ref: "#/$defs/IBbsArticle.ICreate",
},
},
required: ["input"],
additionalProperties: false,
$defs: {
"IBbsArticle.ICreate": {
description: "Information of the article to create.",
type: "object",
properties: {
title: {
description:
"Title of the article.\n\nRepresentative title of the article.",
type: "string",
},
body: {
description:
"Content body.\n\nContent body of the article written in the markdown format.",
type: "string",
},
thumbnail: {
description:
"Thumbnail image URI.\n\nThumbnail image URI which can represent the article.\n\nIf configured as `null`, it means that no thumbnail image in the article.",
anyOf: [
{
type: "null",
},
{
type: "string",
format: "uri",
contentMediaType: "image/*",
},
],
},
},
required: ["title", "body", "thumbnail"],
},
IBbsArticle: {
description:
"Article entity.\n\n`IBbsArticle` is an entity representing an article in the BBS (Bulletin Board System).",
type: "object",
properties: {
id: {
description: "Primary Key.",
type: "string",
format: "uuid",
},
created_at: {
description: "Creation time of the article.",
type: "string",
format: "date-time",
},
updated_at: {
description: "Last updated time of the article.",
type: "string",
format: "date-time",
},
title: {
description:
"Title of the article.\n\nRepresentative title of the article.",
type: "string",
},
body: {
description:
"Content body.\n\nContent body of the article written in the markdown format.",
type: "string",
},
thumbnail: {
description:
"Thumbnail image URI.\n\nThumbnail image URI which can represent the article.\n\nIf configured as `null`, it means that no thumbnail image in the article.",
anyOf: [
{
type: "null",
},
{
type: "string",
format: "uri",
contentMediaType: "image/*",
},
],
},
},
required: [
"id",
"created_at",
"updated_at",
"title",
"body",
"thumbnail",
],
},
},
},
output: {
$ref: "#/$defs/IBbsArticle",
},
description:
"Create a new article.\n\nWrites a new article and archives it into the DB.",
validate: (() => {
const _io0 = (input) =>
"object" === typeof input.input &&
null !== input.input &&
_io1(input.input);
const _io1 = (input) =>
"string" === typeof input.title &&
"string" === typeof input.body &&
(null === input.thumbnail ||
("string" === typeof input.thumbnail &&
__typia_transform__isFormatUri._isFormatUri(input.thumbnail)));
const _vo0 = (input, _path, _exceptionable = true) =>
[
((("object" === typeof input.input && null !== input.input) ||
_report(_exceptionable, {
path: _path + ".input",
expected: "IBbsArticle.ICreate",
value: input.input,
})) &&
_vo1(input.input, _path + ".input", true && _exceptionable)) ||
_report(_exceptionable, {
path: _path + ".input",
expected: "IBbsArticle.ICreate",
value: input.input,
}),
].every((flag) => flag);
const _vo1 = (input, _path, _exceptionable = true) =>
[
"string" === typeof input.title ||
_report(_exceptionable, {
path: _path + ".title",
expected: "string",
value: input.title,
}),
"string" === typeof input.body ||
_report(_exceptionable, {
path: _path + ".body",
expected: "string",
value: input.body,
}),
null === input.thumbnail ||
("string" === typeof input.thumbnail &&
(__typia_transform__isFormatUri._isFormatUri(input.thumbnail) ||
_report(_exceptionable, {
path: _path + ".thumbnail",
expected: 'string & Format<"uri">',
value: input.thumbnail,
}))) ||
_report(_exceptionable, {
path: _path + ".thumbnail",
expected:
'((string & Format<"uri"> & ContentMediaType<"image/*">) | null)',
value: input.thumbnail,
}),
].every((flag) => flag);
const __is = (input) =>
"object" === typeof input && null !== input && _io0(input);
let errors;
let _report;
return (input) => {
if (false === __is(input)) {
errors = [];
_report = __typia_transform__validateReport._validateReport(errors);
((input, _path, _exceptionable = true) =>
((("object" === typeof input && null !== input) ||
_report(true, {
path: _path + "",
expected: "__type",
value: input,
})) &&
_vo0(input, _path + "", true)) ||
_report(true, {
path: _path + "",
expected: "__type",
value: input,
}))(input, "$input", true);
const success = 0 === errors.length;
return success
? {
success,
data: input,
}
: {
success,
errors,
data: input,
};
}
return {
success: true,
data: input,
};
};
})(),
},
{
name: "update",
parameters: {
description: " Properties of update function",
type: "object",
properties: {
id: {
description: "Target article's {@link IBbsArticle.id}.",
type: "string",
format: "uuid",
},
input: {
description: "New content to update.",
$ref: "#/$defs/PartialIBbsArticle.ICreate",
},
},
required: ["id", "input"],
additionalProperties: false,
$defs: {
"PartialIBbsArticle.ICreate": {
description: "Make all properties in T optional",
type: "object",
properties: {
title: {
description:
"Title of the article.\n\nRepresentative title of the article.",
type: "string",
},
body: {
description:
"Content body.\n\nContent body of the article written in the markdown format.",
type: "string",
},
thumbnail: {
description:
"Thumbnail image URI.\n\nThumbnail image URI which can represent the article.\n\nIf configured as `null`, it means that no thumbnail image in the article.",
anyOf: [
{
type: "null",
},
{
type: "string",
format: "uri",
contentMediaType: "image/*",
},
],
},
},
required: [],
},
},
},
description: "Update an article.\n\nUpdates an article with new content.",
validate: (() => {
const _io0 = (input) =>
"string" === typeof input.id &&
__typia_transform__isFormatUuid._isFormatUuid(input.id) &&
"object" === typeof input.input &&
null !== input.input &&
false === Array.isArray(input.input) &&
_io1(input.input);
const _io1 = (input) =>
(undefined === input.title || "string" === typeof input.title) &&
(undefined === input.body || "string" === typeof input.body) &&
(null === input.thumbnail ||
undefined === input.thumbnail ||
("string" === typeof input.thumbnail &&
__typia_transform__isFormatUri._isFormatUri(input.thumbnail)));
const _vo0 = (input, _path, _exceptionable = true) =>
[
("string" === typeof input.id &&
(__typia_transform__isFormatUuid._isFormatUuid(input.id) ||
_report(_exceptionable, {
path: _path + ".id",
expected: 'string & Format<"uuid">',
value: input.id,
}))) ||
_report(_exceptionable, {
path: _path + ".id",
expected: '(string & Format<"uuid">)',
value: input.id,
}),
((("object" === typeof input.input &&
null !== input.input &&
false === Array.isArray(input.input)) ||
_report(_exceptionable, {
path: _path + ".input",
expected: "Partial<IBbsArticle.ICreate>",
value: input.input,
})) &&
_vo1(input.input, _path + ".input", true && _exceptionable)) ||
_report(_exceptionable, {
path: _path + ".input",
expected: "Partial<IBbsArticle.ICreate>",
value: input.input,
}),
].every((flag) => flag);
const _vo1 = (input, _path, _exceptionable = true) =>
[
undefined === input.title ||
"string" === typeof input.title ||
_report(_exceptionable, {
path: _path + ".title",
expected: "(string | undefined)",
value: input.title,
}),
undefined === input.body ||
"string" === typeof input.body ||
_report(_exceptionable, {
path: _path + ".body",
expected: "(string | undefined)",
value: input.body,
}),
null === input.thumbnail ||
undefined === input.thumbnail ||
("string" === typeof input.thumbnail &&
(__typia_transform__isFormatUri._isFormatUri(input.thumbnail) ||
_report(_exceptionable, {
path: _path + ".thumbnail",
expected: 'string & Format<"uri">',
value: input.thumbnail,
}))) ||
_report(_exceptionable, {
path: _path + ".thumbnail",
expected:
'((string & Format<"uri"> & ContentMediaType<"image/*">) | null | undefined)',
value: input.thumbnail,
}),
].every((flag) => flag);
const __is = (input) =>
"object" === typeof input && null !== input && _io0(input);
let errors;
let _report;
return (input) => {
if (false === __is(input)) {
errors = [];
_report = __typia_transform__validateReport._validateReport(errors);
((input, _path, _exceptionable = true) =>
((("object" === typeof input && null !== input) ||
_report(true, {
path: _path + "",
expected: "__type",
value: input,
})) &&
_vo0(input, _path + "", true)) ||
_report(true, {
path: _path + "",
expected: "__type",
value: input,
}))(input, "$input", true);
const success = 0 === errors.length;
return success
? {
success,
data: input,
}
: {
success,
errors,
data: input,
};
}
return {
success: true,
data: input,
};
};
})(),
},
{
name: "erase",
parameters: {
description: " Properties of erase function",
type: "object",
properties: {
id: {
description: "Target article's {@link IBbsArticle.id}.",
type: "string",
format: "uuid",
},
},
required: ["id"],
additionalProperties: false,
$defs: {},
},
description: "Erase an article.\n\nErases an article from the DB.",
validate: (() => {
const _io0 = (input) =>
"string" === typeof input.id &&
__typia_transform__isFormatUuid._isFormatUuid(input.id);
const _vo0 = (input, _path, _exceptionable = true) =>
[
("string" === typeof input.id &&
(__typia_transform__isFormatUuid._isFormatUuid(input.id) ||
_report(_exceptionable, {
path: _path + ".id",
expected: 'string & Format<"uuid">',
value: input.id,
}))) ||
_report(_exceptionable, {
path: _path + ".id",
expected: '(string & Format<"uuid">)',
value: input.id,
}),
].every((flag) => flag);
const __is = (input) =>
"object" === typeof input && null !== input && _io0(input);
let errors;
let _report;
return (input) => {
if (false === __is(input)) {
errors = [];
_report = __typia_transform__validateReport._validateReport(errors);
((input, _path, _exceptionable = true) =>
((("object" === typeof input && null !== input) ||
_report(true, {
path: _path + "",
expected: "__type",
value: input,
})) &&
_vo0(input, _path + "", true)) ||
_report(true, {
path: _path + "",
expected: "__type",
value: input,
}))(input, "$input", true);
const success = 0 === errors.length;
return success
? {
success,
data: input,
}
: {
success,
errors,
data: input,
};
}
return {
success: true,
data: input,
};
};
})(),
},
],
};
console.log(app);undefined
import { tags } from "typia";
import { v4 } from "uuid";
import { IBbsArticle } from "./IBbsArticle";
export class BbsArticleService {
private readonly articles: IBbsArticle[] = [];
/**
* Get all articles.
*
* List up every articles archived in the BBS DB.
*
* @returns List of every articles
*/
public index(): IBbsArticle[] {
return this.articles;
}
/**
* Create a new article.
*
* Writes a new article and archives it into the DB.
*
* @param props Properties of create function
* @returns Newly created article
*/
public create(props: {
/**
* Information of the article to create
*/
input: IBbsArticle.ICreate;
}): IBbsArticle {
const article: IBbsArticle = {
id: v4(),
title: props.input.title,
body: props.input.body,
thumbnail: props.input.thumbnail,
created_at: new Date().toISOString(),
updated_at: new Date().toISOString(),
};
this.articles.push(article);
return article;
}
/**
* Update an article.
*
* Updates an article with new content.
*
* @param props Properties of update function
* @param input New content to update
*/
public update(props: {
/**
* Target article's {@link IBbsArticle.id}.
*/
id: string & tags.Format<"uuid">;
/**
* New content to update.
*/
input: IBbsArticle.IUpdate;
}): void {
const article: IBbsArticle | undefined = this.articles.find(
(a) => a.id === props.id,
);
if (article === undefined)
throw new Error("Unable to find the matched article.");
if (props.input.title !== undefined) article.title = props.input.title;
if (props.input.body !== undefined) article.body = props.input.body;
if (props.input.thumbnail !== undefined)
article.thumbnail = props.input.thumbnail;
article.updated_at = new Date().toISOString();
}
/**
* Erase an article.
*
* Erases an article from the DB.
*
* @param props Properties of erase function
*/
public erase(props: {
/**
* Target article's {@link IBbsArticle.id}.
*/
id: string & tags.Format<"uuid">;
}): void {
const index: number = this.articles.findIndex((a) => a.id === props.id);
if (index === -1) throw new Error("Unable to find the matched article.");
this.articles.splice(index, 1);
}
}undefined
import { tags } from "typia";
/**
* Article entity.
*
* `IBbsArticle` is an entity representing an article in the BBS (Bulletin Board System).
*/
export interface IBbsArticle extends IBbsArticle.ICreate {
/**
* Primary Key.
*/
id: string & tags.Format<"uuid">;
/**
* Creation time of the article.
*/
created_at: string & tags.Format<"date-time">;
/**
* Last updated time of the article.
*/
updated_at: string & tags.Format<"date-time">;
}
export namespace IBbsArticle {
/**
* Information of the article to create.
*/
export interface ICreate {
/**
* Title of the article.
*
* Representative title of the article.
*/
title: string;
/**
* Content body.
*
* Content body of the article written in the markdown format.
*/
body: string;
/**
* Thumbnail image URI.
*
* Thumbnail image URI which can represent the article.
*
* If configured as `null`, it means that no thumbnail image in the article.
*/
thumbnail:
| null
| (string & tags.Format<"uri"> & tags.ContentMediaType<"image/*">);
}
/**
* Information of the article to update.
*
* Only the filled properties will be updated.
*/
export type IUpdate = Partial<ICreate>;
}Validation Feedback
import { ILlmApplication, ILlmFunction, IValidation } from "@samchon/openapi";
import { FunctionCall } from "pseudo";
export const correctFunctionCall = (props: {
functionCall: FunctionCall;
application: ILlmApplication<"chatgpt">;
retry: (reason: string, errors?: IValidation.IError[]) => Promise<unknown>;
}): Promise<unknown> => {
// FIND FUNCTION
const func: ILlmFunction<"chatgpt"> | undefined =
props.application.functions.find((f) => f.name === call.name);
if (func === undefined) {
// never happened in my experience
return props.retry(
"Unable to find the matched function name. Try it again.",
);
}
// VALIDATE
const result: IValidation<unknown> = func.validate(
props.functionCall.arguments,
);
if (result.success === false) {
// 1st trial: 30% (gpt-4o-mini in shopping mall chatbot)
// 2nd trial with validation feedback: 99%
// 3nd trial with validation feedback again: never have failed
return props.retry(
"Type errors are detected. Correct it through validation errors",
{
errors: result.errors,
},
);
}
return result.data;
};Is LLM Function Calling perfect? No, absolutely not.
LLM (Large Language Model) service vendor like OpenAI takes a lot of type level mistakes when composing the arguments of function calling or structured output. Even though target schema is super simple like Array<string> type, LLM often fills it just by a string typed value.
In my experience, OpenAI gpt-4o-mini (8b parameters) is taking about 70% of type level mistakes when filling the arguments of function calling to Shopping Mall service. To overcome the imperfection of such LLM function calling, typia.llm.application<App>() function embeds typia.validate<T>() function for the validation feedback strategy.
The key concept of validation feedback strategy is, let LLM function calling to construct invalid typed arguments first, and informing detailed type errors to the LLM, so that induce LLM to emend the wrong typed arguments at the next turn. In this way, I could uprise the success rate of function calling from 30% to 99% just by one step validation feedback. Even though the LLM is still occurs type error, it always has been caught at the next turn.
For reference, the embedded typia.validate<T>() function creates validation logic by analyzing TypeScript source codes and types in the compilation level. Therefore, it is accurate and detailed than any other validator libraries. This is exactly what is needed for function calling, and I can confidentelly say that typia is the best library for LLM function calling.
| Components | typia | TypeBox | ajv | io-ts | zod | C.V. |
|---|---|---|---|---|---|---|
| Easy to use | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ |
| Object (simple)Â | âś” | âś” | âś” | âś” | âś” | âś” |
| Object (hierarchical)Â | âś” | âś” | âś” | âś” | âś” | âś” |
| Object (recursive) | ✔ | ❌ | ✔ | ✔ | ✔ | ✔ |
| Object (union, implicit) | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ |
| Object (union, explicit) | ✔ | ✔ | ✔ | ✔ | ✔ | ❌ |
| Object (additional tags)Â | âś” | âś” | âś” | âś” | âś” | âś” |
| Object (template literal types) | ✔ | ✔ | ✔ | ❌ | ❌ | ❌ |
| Object (dynamic properties) | ✔ | ✔ | ✔ | ❌ | ❌ | ❌ |
| Array (rest tuple) | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ |
| Array (hierarchical)Â | âś” | âś” | âś” | âś” | âś” | âś” |
| Array (recursive) | ✔ | ✔ | ✔ | ✔ | ✔ | ❌ |
| Array (recursive, union) | ✔ | ✔ | ❌ | ✔ | ✔ | ❌ |
| Array (R+U, implicit) | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ |
| Array (repeated) | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ |
| Array (repeated, union) | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ |
| Ultimate Union Type | ✅ | ❌ | ❌ | ❌ | ❌ | ❌ |
C.V.meansclass-validator
Additionally, this validation feedback strategy is useful for some LLM providers do not supporting restriction properties of JSON schema. For example, some LLM providers do not support format property of JSON schema, so that cannot understand the UUID like type. Even though typia.llm.application<App>() function is writing the restriction information to the description property of JSON schema, but LLM provider does not reflect it perfectly.
In that case, if you give validation feedback from ILlmFunction.validate() function to the LLM agent, the LLM agent will be able to understand the restriction information exactly and fill the arguments properly.
- Restriction properties of JSON schema
string:minLength,maxLength,pattern,format,contentMediaTypenumber:minimum,maximum,exclusiveMinimum,exclusiveMaximum,multipleOfarray:minItems,maxItems,uniqueItems,items
Parameters’ Separation
Parameter values from both LLM and Human sides.
When composing parameter arguments through the LLM (Large Language Model) function calling, there can be a case that some parameters (or nested properties) must be composed not by LLM, but by Human. File uploading feature, or sensitive information like secret key (password) cases are the representative examples.
In that case, you can configure the LLM function calling schemas to exclude such Human side parameters (or nested properties) by ILlmApplication.options.separate property. Instead, you have to merge both Human and LLM composed parameters into one by calling the HttpLlm.mergeParameters() before the LLM function call execution.
Here is the example separating the parameter schemas.
TypeScript Source Code
import { ILlmApplication, ILlmSchema, LlmTypeChecker } from "@samchon/openapi";
import typia from "typia";
import { BbsArticleService } from "./BbsArticleService";
const app: ILlmApplication = typia.llm.application<BbsArticleService>({
separate: (schema: ILlmSchema) =>
LlmTypeChecker.isString(schema) && schema.contentMediaType !== undefined,
});
console.log(app);Compiled JavaScript
import { LlmTypeChecker } from "@samchon/openapi";
import typia from "typia";
import * as __typia_transform__isFormatUri from "typia/lib/internal/_isFormatUri.js";
import * as __typia_transform__isFormatUuid from "typia/lib/internal/_isFormatUuid.js";
import * as __typia_transform__llmApplicationFinalize from "typia/lib/internal/_llmApplicationFinalize.js";
import * as __typia_transform__validateReport from "typia/lib/internal/_validateReport.js";
const app = (() => {
const application = {
config: {
reference: true,
strict: false,
separate: null,
validate: null,
},
functions: [
{
name: "index",
parameters: {
type: "object",
properties: {},
additionalProperties: false,
required: [],
$defs: {
IBbsArticle: {
description:
"Article entity.\n\n`IBbsArticle` is an entity representing an article in the BBS (Bulletin Board System).",
type: "object",
properties: {
id: {
description: "Primary Key.",
type: "string",
format: "uuid",
},
created_at: {
description: "Creation time of the article.",
type: "string",
format: "date-time",
},
updated_at: {
description: "Last updated time of the article.",
type: "string",
format: "date-time",
},
title: {
description:
"Title of the article.\n\nRepresentative title of the article.",
type: "string",
},
body: {
description:
"Content body.\n\nContent body of the article written in the markdown format.",
type: "string",
},
thumbnail: {
description:
"Thumbnail image URI.\n\nThumbnail image URI which can represent the article.\n\nIf configured as `null`, it means that no thumbnail image in the article.",
anyOf: [
{
type: "null",
},
{
type: "string",
format: "uri",
contentMediaType: "image/*",
},
],
},
},
required: [
"id",
"created_at",
"updated_at",
"title",
"body",
"thumbnail",
],
},
},
},
output: {
description: "List of every articles",
type: "array",
items: {
$ref: "#/$defs/IBbsArticle",
},
},
description:
"Get all articles.\n\nList up every articles archived in the BBS DB.",
validate: (() => {
const __is = (input) => true;
let errors;
let _report;
return (input) => {
if (false === __is(input)) {
errors = [];
_report =
__typia_transform__validateReport._validateReport(errors);
((input, _path, _exceptionable = true) => true)(
input,
"$input",
true,
);
const success = 0 === errors.length;
return success
? {
success,
data: input,
}
: {
success,
errors,
data: input,
};
}
return {
success: true,
data: input,
};
};
})(),
},
{
name: "create",
parameters: {
description: " Properties of create function",
type: "object",
properties: {
input: {
description: "Information of the article to create",
$ref: "#/$defs/IBbsArticle.ICreate",
},
},
required: ["input"],
additionalProperties: false,
$defs: {
"IBbsArticle.ICreate": {
description: "Information of the article to create.",
type: "object",
properties: {
title: {
description:
"Title of the article.\n\nRepresentative title of the article.",
type: "string",
},
body: {
description:
"Content body.\n\nContent body of the article written in the markdown format.",
type: "string",
},
thumbnail: {
description:
"Thumbnail image URI.\n\nThumbnail image URI which can represent the article.\n\nIf configured as `null`, it means that no thumbnail image in the article.",
anyOf: [
{
type: "null",
},
{
type: "string",
format: "uri",
contentMediaType: "image/*",
},
],
},
},
required: ["title", "body", "thumbnail"],
},
IBbsArticle: {
description:
"Article entity.\n\n`IBbsArticle` is an entity representing an article in the BBS (Bulletin Board System).",
type: "object",
properties: {
id: {
description: "Primary Key.",
type: "string",
format: "uuid",
},
created_at: {
description: "Creation time of the article.",
type: "string",
format: "date-time",
},
updated_at: {
description: "Last updated time of the article.",
type: "string",
format: "date-time",
},
title: {
description:
"Title of the article.\n\nRepresentative title of the article.",
type: "string",
},
body: {
description:
"Content body.\n\nContent body of the article written in the markdown format.",
type: "string",
},
thumbnail: {
description:
"Thumbnail image URI.\n\nThumbnail image URI which can represent the article.\n\nIf configured as `null`, it means that no thumbnail image in the article.",
anyOf: [
{
type: "null",
},
{
type: "string",
format: "uri",
contentMediaType: "image/*",
},
],
},
},
required: [
"id",
"created_at",
"updated_at",
"title",
"body",
"thumbnail",
],
},
},
},
output: {
$ref: "#/$defs/IBbsArticle",
},
description:
"Create a new article.\n\nWrites a new article and archives it into the DB.",
validate: (() => {
const _io0 = (input) =>
"object" === typeof input.input &&
null !== input.input &&
_io1(input.input);
const _io1 = (input) =>
"string" === typeof input.title &&
"string" === typeof input.body &&
(null === input.thumbnail ||
("string" === typeof input.thumbnail &&
__typia_transform__isFormatUri._isFormatUri(input.thumbnail)));
const _vo0 = (input, _path, _exceptionable = true) =>
[
((("object" === typeof input.input && null !== input.input) ||
_report(_exceptionable, {
path: _path + ".input",
expected: "IBbsArticle.ICreate",
value: input.input,
})) &&
_vo1(input.input, _path + ".input", true && _exceptionable)) ||
_report(_exceptionable, {
path: _path + ".input",
expected: "IBbsArticle.ICreate",
value: input.input,
}),
].every((flag) => flag);
const _vo1 = (input, _path, _exceptionable = true) =>
[
"string" === typeof input.title ||
_report(_exceptionable, {
path: _path + ".title",
expected: "string",
value: input.title,
}),
"string" === typeof input.body ||
_report(_exceptionable, {
path: _path + ".body",
expected: "string",
value: input.body,
}),
null === input.thumbnail ||
("string" === typeof input.thumbnail &&
(__typia_transform__isFormatUri._isFormatUri(
input.thumbnail,
) ||
_report(_exceptionable, {
path: _path + ".thumbnail",
expected: 'string & Format<"uri">',
value: input.thumbnail,
}))) ||
_report(_exceptionable, {
path: _path + ".thumbnail",
expected:
'((string & Format<"uri"> & ContentMediaType<"image/*">) | null)',
value: input.thumbnail,
}),
].every((flag) => flag);
const __is = (input) =>
"object" === typeof input && null !== input && _io0(input);
let errors;
let _report;
return (input) => {
if (false === __is(input)) {
errors = [];
_report =
__typia_transform__validateReport._validateReport(errors);
((input, _path, _exceptionable = true) =>
((("object" === typeof input && null !== input) ||
_report(true, {
path: _path + "",
expected: "__type",
value: input,
})) &&
_vo0(input, _path + "", true)) ||
_report(true, {
path: _path + "",
expected: "__type",
value: input,
}))(input, "$input", true);
const success = 0 === errors.length;
return success
? {
success,
data: input,
}
: {
success,
errors,
data: input,
};
}
return {
success: true,
data: input,
};
};
})(),
},
{
name: "update",
parameters: {
description: " Properties of update function",
type: "object",
properties: {
id: {
description: "Target article's {@link IBbsArticle.id}.",
type: "string",
format: "uuid",
},
input: {
description: "New content to update.",
$ref: "#/$defs/PartialIBbsArticle.ICreate",
},
},
required: ["id", "input"],
additionalProperties: false,
$defs: {
"PartialIBbsArticle.ICreate": {
description: "Make all properties in T optional",
type: "object",
properties: {
title: {
description:
"Title of the article.\n\nRepresentative title of the article.",
type: "string",
},
body: {
description:
"Content body.\n\nContent body of the article written in the markdown format.",
type: "string",
},
thumbnail: {
description:
"Thumbnail image URI.\n\nThumbnail image URI which can represent the article.\n\nIf configured as `null`, it means that no thumbnail image in the article.",
anyOf: [
{
type: "null",
},
{
type: "string",
format: "uri",
contentMediaType: "image/*",
},
],
},
},
required: [],
},
},
},
description:
"Update an article.\n\nUpdates an article with new content.",
validate: (() => {
const _io0 = (input) =>
"string" === typeof input.id &&
__typia_transform__isFormatUuid._isFormatUuid(input.id) &&
"object" === typeof input.input &&
null !== input.input &&
false === Array.isArray(input.input) &&
_io1(input.input);
const _io1 = (input) =>
(undefined === input.title || "string" === typeof input.title) &&
(undefined === input.body || "string" === typeof input.body) &&
(null === input.thumbnail ||
undefined === input.thumbnail ||
("string" === typeof input.thumbnail &&
__typia_transform__isFormatUri._isFormatUri(input.thumbnail)));
const _vo0 = (input, _path, _exceptionable = true) =>
[
("string" === typeof input.id &&
(__typia_transform__isFormatUuid._isFormatUuid(input.id) ||
_report(_exceptionable, {
path: _path + ".id",
expected: 'string & Format<"uuid">',
value: input.id,
}))) ||
_report(_exceptionable, {
path: _path + ".id",
expected: '(string & Format<"uuid">)',
value: input.id,
}),
((("object" === typeof input.input &&
null !== input.input &&
false === Array.isArray(input.input)) ||
_report(_exceptionable, {
path: _path + ".input",
expected: "Partial<IBbsArticle.ICreate>",
value: input.input,
})) &&
_vo1(input.input, _path + ".input", true && _exceptionable)) ||
_report(_exceptionable, {
path: _path + ".input",
expected: "Partial<IBbsArticle.ICreate>",
value: input.input,
}),
].every((flag) => flag);
const _vo1 = (input, _path, _exceptionable = true) =>
[
undefined === input.title ||
"string" === typeof input.title ||
_report(_exceptionable, {
path: _path + ".title",
expected: "(string | undefined)",
value: input.title,
}),
undefined === input.body ||
"string" === typeof input.body ||
_report(_exceptionable, {
path: _path + ".body",
expected: "(string | undefined)",
value: input.body,
}),
null === input.thumbnail ||
undefined === input.thumbnail ||
("string" === typeof input.thumbnail &&
(__typia_transform__isFormatUri._isFormatUri(
input.thumbnail,
) ||
_report(_exceptionable, {
path: _path + ".thumbnail",
expected: 'string & Format<"uri">',
value: input.thumbnail,
}))) ||
_report(_exceptionable, {
path: _path + ".thumbnail",
expected:
'((string & Format<"uri"> & ContentMediaType<"image/*">) | null | undefined)',
value: input.thumbnail,
}),
].every((flag) => flag);
const __is = (input) =>
"object" === typeof input && null !== input && _io0(input);
let errors;
let _report;
return (input) => {
if (false === __is(input)) {
errors = [];
_report =
__typia_transform__validateReport._validateReport(errors);
((input, _path, _exceptionable = true) =>
((("object" === typeof input && null !== input) ||
_report(true, {
path: _path + "",
expected: "__type",
value: input,
})) &&
_vo0(input, _path + "", true)) ||
_report(true, {
path: _path + "",
expected: "__type",
value: input,
}))(input, "$input", true);
const success = 0 === errors.length;
return success
? {
success,
data: input,
}
: {
success,
errors,
data: input,
};
}
return {
success: true,
data: input,
};
};
})(),
},
{
name: "erase",
parameters: {
description: " Properties of erase function",
type: "object",
properties: {
id: {
description: "Target article's {@link IBbsArticle.id}.",
type: "string",
format: "uuid",
},
},
required: ["id"],
additionalProperties: false,
$defs: {},
},
description: "Erase an article.\n\nErases an article from the DB.",
validate: (() => {
const _io0 = (input) =>
"string" === typeof input.id &&
__typia_transform__isFormatUuid._isFormatUuid(input.id);
const _vo0 = (input, _path, _exceptionable = true) =>
[
("string" === typeof input.id &&
(__typia_transform__isFormatUuid._isFormatUuid(input.id) ||
_report(_exceptionable, {
path: _path + ".id",
expected: 'string & Format<"uuid">',
value: input.id,
}))) ||
_report(_exceptionable, {
path: _path + ".id",
expected: '(string & Format<"uuid">)',
value: input.id,
}),
].every((flag) => flag);
const __is = (input) =>
"object" === typeof input && null !== input && _io0(input);
let errors;
let _report;
return (input) => {
if (false === __is(input)) {
errors = [];
_report =
__typia_transform__validateReport._validateReport(errors);
((input, _path, _exceptionable = true) =>
((("object" === typeof input && null !== input) ||
_report(true, {
path: _path + "",
expected: "__type",
value: input,
})) &&
_vo0(input, _path + "", true)) ||
_report(true, {
path: _path + "",
expected: "__type",
value: input,
}))(input, "$input", true);
const success = 0 === errors.length;
return success
? {
success,
data: input,
}
: {
success,
errors,
data: input,
};
}
return {
success: true,
data: input,
};
};
})(),
},
],
};
__typia_transform__llmApplicationFinalize._llmApplicationFinalize(
application,
{
...{
separate: (schema) =>
LlmTypeChecker.isString(schema) &&
schema.contentMediaType !== undefined,
},
equals: false,
},
);
return application;
})();
console.log(app);undefined
import { tags } from "typia";
import { v4 } from "uuid";
import { IBbsArticle } from "./IBbsArticle";
export class BbsArticleService {
private readonly articles: IBbsArticle[] = [];
/**
* Get all articles.
*
* List up every articles archived in the BBS DB.
*
* @returns List of every articles
*/
public index(): IBbsArticle[] {
return this.articles;
}
/**
* Create a new article.
*
* Writes a new article and archives it into the DB.
*
* @param props Properties of create function
* @returns Newly created article
*/
public create(props: {
/**
* Information of the article to create
*/
input: IBbsArticle.ICreate;
}): IBbsArticle {
const article: IBbsArticle = {
id: v4(),
title: props.input.title,
body: props.input.body,
thumbnail: props.input.thumbnail,
created_at: new Date().toISOString(),
updated_at: new Date().toISOString(),
};
this.articles.push(article);
return article;
}
/**
* Update an article.
*
* Updates an article with new content.
*
* @param props Properties of update function
* @param input New content to update
*/
public update(props: {
/**
* Target article's {@link IBbsArticle.id}.
*/
id: string & tags.Format<"uuid">;
/**
* New content to update.
*/
input: IBbsArticle.IUpdate;
}): void {
const article: IBbsArticle | undefined = this.articles.find(
(a) => a.id === props.id,
);
if (article === undefined)
throw new Error("Unable to find the matched article.");
if (props.input.title !== undefined) article.title = props.input.title;
if (props.input.body !== undefined) article.body = props.input.body;
if (props.input.thumbnail !== undefined)
article.thumbnail = props.input.thumbnail;
article.updated_at = new Date().toISOString();
}
/**
* Erase an article.
*
* Erases an article from the DB.
*
* @param props Properties of erase function
*/
public erase(props: {
/**
* Target article's {@link IBbsArticle.id}.
*/
id: string & tags.Format<"uuid">;
}): void {
const index: number = this.articles.findIndex((a) => a.id === props.id);
if (index === -1) throw new Error("Unable to find the matched article.");
this.articles.splice(index, 1);
}
}undefined
import { tags } from "typia";
/**
* Article entity.
*
* `IBbsArticle` is an entity representing an article in the BBS (Bulletin Board System).
*/
export interface IBbsArticle extends IBbsArticle.ICreate {
/**
* Primary Key.
*/
id: string & tags.Format<"uuid">;
/**
* Creation time of the article.
*/
created_at: string & tags.Format<"date-time">;
/**
* Last updated time of the article.
*/
updated_at: string & tags.Format<"date-time">;
}
export namespace IBbsArticle {
/**
* Information of the article to create.
*/
export interface ICreate {
/**
* Title of the article.
*
* Representative title of the article.
*/
title: string;
/**
* Content body.
*
* Content body of the article written in the markdown format.
*/
body: string;
/**
* Thumbnail image URI.
*
* Thumbnail image URI which can represent the article.
*
* If configured as `null`, it means that no thumbnail image in the article.
*/
thumbnail:
| null
| (string & tags.Format<"uri"> & tags.ContentMediaType<"image/*">);
}
/**
* Information of the article to update.
*
* Only the filled properties will be updated.
*/
export type IUpdate = Partial<ICreate>;
}Restrictions
typia.llm.application<App>() follows the same restrictions of below.
About the function parameters type, it follows the restriction of both typia.llm.parameters<Params>() and typia.llm.schema<T>() functions. Therefore, the parameters must be a keyworded object type with static keys without any dynamic keys. Also, the object type must not be nullable or optional.
About the return value type, it follows the restriction of typia.llm.schema<T>() function. By the way, if the return type is union type with undefined, it would be compilation error, due to OpenAPI (JSON schema) specification does not support the undefindable union type.
TypeScript Source Code
import { ILlmApplication } from "@samchon/openapi";
import typia, { tags } from "typia";
const app: ILlmApplication = typia.llm.application<BbsArticleController>();
console.log(app);
interface BbsArticleController {
/**
* Create a new article.
*
* Writes a new article and archives it into the DB.
*
* @param props Properties of create function
* @returns Newly created article
*/
create(props: {
/**
* Information of the article to create
*/
input: IBbsArticle.ICreate;
}): Promise<IBbsArticle | undefined>;
erase(id: string & tags.Format<"uuid">): Promise<void>;
}Console Output
src/examples/llm.application.violation.ts:4:29 - error TS(typia.llm.application): unsupported type detected
- BbsArticleController.create: unknown
- LLM application's function ("create")'s return type must not be union type with undefined.
- BbsArticleController.erase: unknown
- LLM application's function ("erase")'s parameter must be an object type.
4 const app: ILlmApplication = typia.llm.application<BbsArticleController>();
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Found 1 error in src/examples/llm.application.violation.ts:4