@typia/langchain 13.1.1 → 13.2.0

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.
@@ -0,0 +1,66 @@
1
+ import type { DynamicStructuredTool } from "@langchain/core/tools";
2
+ import { IHttpLlmController, ILlmController } from "@typia/interface";
3
+ type ILangChainController = ILlmController | IHttpLlmController;
4
+ interface ILangChainToolsOptions {
5
+ /**
6
+ * Whether to add controller name as prefix to tool names.
7
+ *
8
+ * If `true`, tool names become `{controllerName}_{methodName}`. If `false`,
9
+ * tool names are just `{methodName}`.
10
+ *
11
+ * @default false
12
+ */
13
+ prefix?: boolean | undefined;
14
+ }
15
+ interface ILangChainToolsProps extends ILangChainToolsOptions {
16
+ /**
17
+ * List of controllers to convert to LangChain tools.
18
+ *
19
+ * - {@link ILlmController}: from `typia.llm.controller<Class>()`, converts all
20
+ * methods of the class to tools
21
+ * - {@link IHttpLlmController}: from `HttpLlm.controller()`, converts all
22
+ * operations from OpenAPI document to tools
23
+ */
24
+ controllers: ILangChainController[];
25
+ }
26
+ /**
27
+ * Convert typia controllers to LangChain tools.
28
+ *
29
+ * Converts TypeScript class methods via `typia.llm.controller<Class>()` or
30
+ * OpenAPI operations via `HttpLlm.controller()` to LangChain tools.
31
+ *
32
+ * Every tool call is validated by typia. If LLM provides invalid arguments,
33
+ * returns validation error formatted by {@link LlmJson.stringify} so that LLM
34
+ * can correct them automatically.
35
+ *
36
+ * @example
37
+ * ```typescript
38
+ * import { initChatModel } from "langchain/chat_models/universal";
39
+ * import typia from "typia";
40
+ * import { toLangChainTools } from "@typia/langchain";
41
+ *
42
+ * class Calculator {
43
+ * add(input: { a: number; b: number }): { value: number } {
44
+ * return { value: input.a + input.b };
45
+ * }
46
+ * }
47
+ *
48
+ * const controller = typia.llm.controller<Calculator>(
49
+ * "calculator",
50
+ * new Calculator(),
51
+ * );
52
+ * const tools = toLangChainTools(controller);
53
+ *
54
+ * const llm = await initChatModel();
55
+ * const modelWithTools = llm.bindTools(tools);
56
+ * const result = await modelWithTools.invoke("What is 10 + 5?");
57
+ * ```;
58
+ *
59
+ * @param input Controller, controller list, or conversion properties
60
+ * @param options Conversion options when `input` is not a properties object
61
+ * @returns Array of LangChain DynamicStructuredTool
62
+ */
63
+ export declare function toLangChainTools(controller: ILangChainController, options?: ILangChainToolsOptions | undefined): DynamicStructuredTool[];
64
+ export declare function toLangChainTools(controllers: ILangChainController[], options?: ILangChainToolsOptions | undefined): DynamicStructuredTool[];
65
+ export declare function toLangChainTools(props: ILangChainToolsProps): DynamicStructuredTool[];
66
+ export {};
@@ -0,0 +1,66 @@
1
+ import type { DynamicStructuredTool } from "@langchain/core/tools";
2
+ import { IHttpLlmController, ILlmController } from "@typia/interface";
3
+ type ILangChainController = ILlmController | IHttpLlmController;
4
+ interface ILangChainToolsOptions {
5
+ /**
6
+ * Whether to add controller name as prefix to tool names.
7
+ *
8
+ * If `true`, tool names become `{controllerName}_{methodName}`. If `false`,
9
+ * tool names are just `{methodName}`.
10
+ *
11
+ * @default false
12
+ */
13
+ prefix?: boolean | undefined;
14
+ }
15
+ interface ILangChainToolsProps extends ILangChainToolsOptions {
16
+ /**
17
+ * List of controllers to convert to LangChain tools.
18
+ *
19
+ * - {@link ILlmController}: from `typia.llm.controller<Class>()`, converts all
20
+ * methods of the class to tools
21
+ * - {@link IHttpLlmController}: from `HttpLlm.controller()`, converts all
22
+ * operations from OpenAPI document to tools
23
+ */
24
+ controllers: ILangChainController[];
25
+ }
26
+ /**
27
+ * Convert typia controllers to LangChain tools.
28
+ *
29
+ * Converts TypeScript class methods via `typia.llm.controller<Class>()` or
30
+ * OpenAPI operations via `HttpLlm.controller()` to LangChain tools.
31
+ *
32
+ * Every tool call is validated by typia. If LLM provides invalid arguments,
33
+ * returns validation error formatted by {@link LlmJson.stringify} so that LLM
34
+ * can correct them automatically.
35
+ *
36
+ * @example
37
+ * ```typescript
38
+ * import { initChatModel } from "langchain/chat_models/universal";
39
+ * import typia from "typia";
40
+ * import { toLangChainTools } from "@typia/langchain";
41
+ *
42
+ * class Calculator {
43
+ * add(input: { a: number; b: number }): { value: number } {
44
+ * return { value: input.a + input.b };
45
+ * }
46
+ * }
47
+ *
48
+ * const controller = typia.llm.controller<Calculator>(
49
+ * "calculator",
50
+ * new Calculator(),
51
+ * );
52
+ * const tools = toLangChainTools(controller);
53
+ *
54
+ * const llm = await initChatModel();
55
+ * const modelWithTools = llm.bindTools(tools);
56
+ * const result = await modelWithTools.invoke("What is 10 + 5?");
57
+ * ```;
58
+ *
59
+ * @param input Controller, controller list, or conversion properties
60
+ * @param options Conversion options when `input` is not a properties object
61
+ * @returns Array of LangChain DynamicStructuredTool
62
+ */
63
+ export declare function toLangChainTools(controller: ILangChainController, options?: ILangChainToolsOptions | undefined): DynamicStructuredTool[];
64
+ export declare function toLangChainTools(controllers: ILangChainController[], options?: ILangChainToolsOptions | undefined): DynamicStructuredTool[];
65
+ export declare function toLangChainTools(props: ILangChainToolsProps): DynamicStructuredTool[];
66
+ export {};
@@ -0,0 +1,37 @@
1
+ import { StandardJSONSchemaV1 } from "@standard-schema/spec";
2
+ import { IHttpLlmFunction, ILlmFunction } from "@typia/interface";
3
+ /**
4
+ * A Standard JSON Schema view of one typia function's parameters.
5
+ *
6
+ * LangChain's tool `schema` fills two roles at once:
7
+ * `toJsonSchema(tool.schema)` turns it into the parameters the model is shown,
8
+ * and `StructuredTool.call` validates incoming arguments against it before the
9
+ * tool body runs. Handing over a bare `ILlmSchema.IParameters` fills the first
10
+ * role but forfeits the second — LangChain validates it with
11
+ * `@cfworker/json-schema`, which rejects what typia would have coerced and
12
+ * reports the failure in its own words, so typia's coercion and its correctable
13
+ * feedback both become unreachable.
14
+ *
15
+ * Standard JSON Schema separates the two roles. It carries a model-facing
16
+ * schema under `~standard.jsonSchema` and, by declaring no
17
+ * `~standard.validate`, no validator — which is exactly typia's position: show
18
+ * the model this schema, and leave validation to `LlmJson.validateArguments` in
19
+ * the tool body. LangChain reads the schema back through its own
20
+ * `toJsonSchema`, so the model sees the same parameters document as before,
21
+ * byte for byte.
22
+ *
23
+ * `@typia/vercel` states the same contract to the AI SDK through
24
+ * `jsonSchema()`, whose `validate` is likewise left undefined so that
25
+ * `safeValidateTypes` short-circuits its own validation.
26
+ *
27
+ * Two version floors hold this together, and both are pinned in the package
28
+ * manifest. `toJsonSchema` only reads `~standard.jsonSchema` from
29
+ * `@langchain/core@1.1.30` onwards — older versions return the wrapper itself
30
+ * and would advertise _that_ to the model in place of the parameters — and
31
+ * `StandardJSONSchemaV1` only exists from `@standard-schema/spec@1.1.0`.
32
+ *
33
+ * @see https://github.com/standard-schema/standard-schema
34
+ */
35
+ export declare namespace LangChainParameterConverter {
36
+ const convert: (func: ILlmFunction | IHttpLlmFunction) => StandardJSONSchemaV1;
37
+ }
@@ -0,0 +1,54 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.LangChainParameterConverter = void 0;
4
+ /**
5
+ * A Standard JSON Schema view of one typia function's parameters.
6
+ *
7
+ * LangChain's tool `schema` fills two roles at once:
8
+ * `toJsonSchema(tool.schema)` turns it into the parameters the model is shown,
9
+ * and `StructuredTool.call` validates incoming arguments against it before the
10
+ * tool body runs. Handing over a bare `ILlmSchema.IParameters` fills the first
11
+ * role but forfeits the second — LangChain validates it with
12
+ * `@cfworker/json-schema`, which rejects what typia would have coerced and
13
+ * reports the failure in its own words, so typia's coercion and its correctable
14
+ * feedback both become unreachable.
15
+ *
16
+ * Standard JSON Schema separates the two roles. It carries a model-facing
17
+ * schema under `~standard.jsonSchema` and, by declaring no
18
+ * `~standard.validate`, no validator — which is exactly typia's position: show
19
+ * the model this schema, and leave validation to `LlmJson.validateArguments` in
20
+ * the tool body. LangChain reads the schema back through its own
21
+ * `toJsonSchema`, so the model sees the same parameters document as before,
22
+ * byte for byte.
23
+ *
24
+ * `@typia/vercel` states the same contract to the AI SDK through
25
+ * `jsonSchema()`, whose `validate` is likewise left undefined so that
26
+ * `safeValidateTypes` short-circuits its own validation.
27
+ *
28
+ * Two version floors hold this together, and both are pinned in the package
29
+ * manifest. `toJsonSchema` only reads `~standard.jsonSchema` from
30
+ * `@langchain/core@1.1.30` onwards — older versions return the wrapper itself
31
+ * and would advertise _that_ to the model in place of the parameters — and
32
+ * `StandardJSONSchemaV1` only exists from `@standard-schema/spec@1.1.0`.
33
+ *
34
+ * @see https://github.com/standard-schema/standard-schema
35
+ */
36
+ var LangChainParameterConverter;
37
+ (function (LangChainParameterConverter) {
38
+ LangChainParameterConverter.convert = (func) => {
39
+ // Coercion never changes the shape a value must end up in, so the input and
40
+ // output schemas are the same document.
41
+ const jsonSchema = () => func.parameters;
42
+ return {
43
+ "~standard": {
44
+ version: 1,
45
+ vendor: "typia",
46
+ jsonSchema: {
47
+ input: jsonSchema,
48
+ output: jsonSchema,
49
+ },
50
+ },
51
+ };
52
+ };
53
+ })(LangChainParameterConverter || (exports.LangChainParameterConverter = LangChainParameterConverter = {}));
54
+ //# sourceMappingURL=LangChainParameterConverter.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"LangChainParameterConverter.js","sourceRoot":"","sources":["../../src/internal/LangChainParameterConverter.ts"],"names":[],"mappings":";;;AAGA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AACH,IAAiB,2BAA2B,CAmB3C;AAnBD,WAAiB,2BAA2B;IAC7B,mCAAO,GAAG,CACrB,IAAqC,EACf,EAAE;QACxB,4EAA4E;QAC5E,wCAAwC;QACxC,MAAM,UAAU,GAAG,GAA4B,EAAE,CAC/C,IAAI,CAAC,UAAgD,CAAC;QACxD,OAAO;YACL,WAAW,EAAE;gBACX,OAAO,EAAE,CAAC;gBACV,MAAM,EAAE,OAAO;gBACf,UAAU,EAAE;oBACV,KAAK,EAAE,UAAU;oBACjB,MAAM,EAAE,UAAU;iBACnB;aACF;SACF,CAAC;IACJ,CAAC,CAAC;AACJ,CAAC,EAnBgB,2BAA2B,aAA3B,2BAA2B,GAA3B,2BAA2B,QAmB3C"}
@@ -0,0 +1,19 @@
1
+ //#region src/internal/LangChainParameterConverter.ts
2
+ let LangChainParameterConverter;
3
+ (function(_LangChainParameterConverter) {
4
+ _LangChainParameterConverter.convert = (func) => {
5
+ const jsonSchema = () => func.parameters;
6
+ return { "~standard": {
7
+ version: 1,
8
+ vendor: "typia",
9
+ jsonSchema: {
10
+ input: jsonSchema,
11
+ output: jsonSchema
12
+ }
13
+ } };
14
+ };
15
+ })(LangChainParameterConverter || (LangChainParameterConverter = {}));
16
+ //#endregion
17
+ export { LangChainParameterConverter };
18
+
19
+ //# sourceMappingURL=LangChainParameterConverter.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"LangChainParameterConverter.mjs","names":[],"sources":["../../src/internal/LangChainParameterConverter.ts"],"sourcesContent":["import { StandardJSONSchemaV1 } from \"@standard-schema/spec\";\nimport { IHttpLlmFunction, ILlmFunction } from \"@typia/interface\";\n\n/**\n * A Standard JSON Schema view of one typia function's parameters.\n *\n * LangChain's tool `schema` fills two roles at once:\n * `toJsonSchema(tool.schema)` turns it into the parameters the model is shown,\n * and `StructuredTool.call` validates incoming arguments against it before the\n * tool body runs. Handing over a bare `ILlmSchema.IParameters` fills the first\n * role but forfeits the second — LangChain validates it with\n * `@cfworker/json-schema`, which rejects what typia would have coerced and\n * reports the failure in its own words, so typia's coercion and its correctable\n * feedback both become unreachable.\n *\n * Standard JSON Schema separates the two roles. It carries a model-facing\n * schema under `~standard.jsonSchema` and, by declaring no\n * `~standard.validate`, no validator — which is exactly typia's position: show\n * the model this schema, and leave validation to `LlmJson.validateArguments` in\n * the tool body. LangChain reads the schema back through its own\n * `toJsonSchema`, so the model sees the same parameters document as before,\n * byte for byte.\n *\n * `@typia/vercel` states the same contract to the AI SDK through\n * `jsonSchema()`, whose `validate` is likewise left undefined so that\n * `safeValidateTypes` short-circuits its own validation.\n *\n * Two version floors hold this together, and both are pinned in the package\n * manifest. `toJsonSchema` only reads `~standard.jsonSchema` from\n * `@langchain/core@1.1.30` onwards — older versions return the wrapper itself\n * and would advertise _that_ to the model in place of the parameters — and\n * `StandardJSONSchemaV1` only exists from `@standard-schema/spec@1.1.0`.\n *\n * @see https://github.com/standard-schema/standard-schema\n */\nexport namespace LangChainParameterConverter {\n export const convert = (\n func: ILlmFunction | IHttpLlmFunction,\n ): StandardJSONSchemaV1 => {\n // Coercion never changes the shape a value must end up in, so the input and\n // output schemas are the same document.\n const jsonSchema = (): Record<string, unknown> =>\n func.parameters as unknown as Record<string, unknown>;\n return {\n \"~standard\": {\n version: 1,\n vendor: \"typia\",\n jsonSchema: {\n input: jsonSchema,\n output: jsonSchema,\n },\n },\n };\n };\n}\n"],"mappings":";AAmCO,IAAA;;yCAEH,SACyB;EAGzB,MAAM,mBACJ,KAAK;EACP,OAAO,EACL,aAAa;GACX,SAAS;GACT,QAAQ;GACR,YAAY;IACV,OAAO;IACP,QAAQ;GACV;EACF,EACF;CACF;GACD,gCAAA,8BAAA,CAAA,EAAD"}
@@ -12,6 +12,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
12
12
  exports.LangChainToolsRegistrar = void 0;
13
13
  const tools_1 = require("@langchain/core/tools");
14
14
  const utils_1 = require("@typia/utils");
15
+ const LangChainParameterConverter_1 = require("./LangChainParameterConverter");
15
16
  var LangChainToolsRegistrar;
16
17
  (function (LangChainToolsRegistrar) {
17
18
  LangChainToolsRegistrar.convert = (props) => {
@@ -19,16 +20,18 @@ var LangChainToolsRegistrar;
19
20
  const prefix = (_a = props.prefix) !== null && _a !== void 0 ? _a : false;
20
21
  const tools = [];
21
22
  // check duplicate tool names
22
- if (prefix === false && props.controllers.length >= 2) {
23
+ if (props.controllers.length >= 2) {
23
24
  const names = new Map();
24
25
  const duplicates = [];
25
26
  for (const controller of props.controllers) {
26
27
  for (const func of controller.application.functions) {
27
- const existing = names.get(func.name);
28
+ const toolName = getToolName(controller.name, func.name, prefix);
29
+ const existing = names.get(toolName);
30
+ const origin = `${controller.protocol} controller "${controller.name}" function "${func.name}"`;
28
31
  if (existing !== undefined)
29
- duplicates.push(`"${func.name}" in "${controller.name}" (conflicts with "${existing}")`);
32
+ duplicates.push(`"${toolName}" from ${origin} (conflicts with ${existing})`);
30
33
  else
31
- names.set(func.name, controller.name);
34
+ names.set(toolName, origin);
32
35
  }
33
36
  }
34
37
  if (duplicates.length > 0)
@@ -48,9 +51,7 @@ var LangChainToolsRegistrar;
48
51
  const convertClassController = (tools, controller, prefix) => {
49
52
  const execute = controller.execute;
50
53
  for (const func of controller.application.functions) {
51
- const toolName = prefix
52
- ? `${controller.name}_${func.name}`
53
- : func.name;
54
+ const toolName = getToolName(controller.name, func.name, prefix);
54
55
  const method = execute[func.name];
55
56
  if (typeof method !== "function") {
56
57
  throw new Error(`Method "${func.name}" not found on controller "${controller.name}"`);
@@ -58,6 +59,7 @@ var LangChainToolsRegistrar;
58
59
  tools.push(createTool({
59
60
  name: toolName,
60
61
  function: func,
62
+ config: controller.application.config,
61
63
  execute: (args) => __awaiter(this, void 0, void 0, function* () { return method.call(execute, args); }),
62
64
  }));
63
65
  }
@@ -66,12 +68,11 @@ var LangChainToolsRegistrar;
66
68
  const application = controller.application;
67
69
  const connection = controller.connection;
68
70
  for (const func of application.functions) {
69
- const toolName = prefix
70
- ? `${controller.name}_${func.name}`
71
- : func.name;
71
+ const toolName = getToolName(controller.name, func.name, prefix);
72
72
  tools.push(createTool({
73
73
  name: toolName,
74
74
  function: func,
75
+ config: application.config,
75
76
  execute: (args) => __awaiter(this, void 0, void 0, function* () {
76
77
  if (controller.execute !== undefined) {
77
78
  const response = yield controller.execute({
@@ -92,35 +93,68 @@ var LangChainToolsRegistrar;
92
93
  }));
93
94
  }
94
95
  };
95
- const createTool = (entry) => { var _a; return (0, tools_1.tool)((args) => __awaiter(this, void 0, void 0, function* () {
96
- const valid = utils_1.LlmJson.validateArguments(entry.function, args !== null && args !== void 0 ? args : {});
97
- if (valid.success === false)
98
- throw new tools_1.ToolInputParsingException(`Type errors in "${entry.name}" arguments:\n\n` +
99
- `\`\`\`json\n${utils_1.LlmJson.stringify(valid)}\n\`\`\``, JSON.stringify(args !== null && args !== void 0 ? args : {}));
100
- try {
101
- const result = yield entry.execute(valid.data);
102
- if (result === undefined) {
103
- return entry.function.output === undefined
104
- ? { success: true }
105
- : {
106
- success: false,
107
- error: `Function "${entry.name}" returned undefined despite declaring an output schema`,
108
- };
96
+ const createTool = (entry) => {
97
+ var _a;
98
+ // A declared return type is a promise to the model, so a result that breaks
99
+ // it is reported rather than delivered. `@typia/mcp` and `@typia/vercel`
100
+ // already refuse one; this tool consulted `output` for its existence alone,
101
+ // so a method returning nothing was reported while a method returning the
102
+ // wrong shape was not (#2302).
103
+ //
104
+ // The application's own config decides how the schema is read back: a
105
+ // strict application carries its constraints as description tags instead of
106
+ // keywords, and only that config tells the inverter to read them.
107
+ const validateOutput = entry.function.output === undefined
108
+ ? undefined
109
+ : utils_1.LlmJson.validate(entry.function.output, true, entry.config);
110
+ return (0, tools_1.tool)((args) => __awaiter(this, void 0, void 0, function* () {
111
+ const valid = utils_1.LlmJson.validateArguments(entry.function, args !== null && args !== void 0 ? args : {});
112
+ if (valid.success === false)
113
+ throw new tools_1.ToolInputParsingException(`Type errors in "${entry.name}" arguments:\n\n` +
114
+ utils_1.LlmJson.stringify(valid), JSON.stringify(args !== null && args !== void 0 ? args : {}));
115
+ try {
116
+ const result = yield entry.execute(valid.data);
117
+ if (result === undefined) {
118
+ return validateOutput === undefined
119
+ ? { success: true }
120
+ : {
121
+ success: false,
122
+ error: `Function "${entry.name}" returned undefined despite declaring an output schema`,
123
+ };
124
+ }
125
+ if (validateOutput !== undefined) {
126
+ const output = validateOutput(result);
127
+ if (output.success === false)
128
+ return {
129
+ success: false,
130
+ error: `Type errors in "${entry.name}" output:
131
+
132
+ ` + utils_1.LlmJson.stringify(output),
133
+ };
134
+ return { success: true, data: output.data };
135
+ }
136
+ return { success: true, data: result };
109
137
  }
110
- return { success: true, data: result };
111
- }
112
- catch (error) {
113
- return {
114
- success: false,
115
- error: error instanceof Error
116
- ? `${error.name}: ${error.message}`
117
- : String(error),
118
- };
119
- }
120
- }), {
121
- name: entry.name,
122
- description: (_a = entry.function.description) !== null && _a !== void 0 ? _a : "",
123
- schema: entry.function.parameters,
124
- }); };
138
+ catch (error) {
139
+ return {
140
+ success: false,
141
+ error: error instanceof Error
142
+ ? `${error.name}: ${error.message}`
143
+ : String(error),
144
+ };
145
+ }
146
+ }), {
147
+ name: entry.name,
148
+ description: (_a = entry.function.description) !== null && _a !== void 0 ? _a : "",
149
+ // Declares the model-facing schema without a validator, so the
150
+ // coerce-and-validate step above is the only one that runs; see
151
+ // `LangChainParameterConverter`. The cast is needed because `tool()`
152
+ // still types `schema` as Zod-or-JSON-Schema, even though the
153
+ // `toJsonSchema` it reads that schema back with accepts Standard JSON
154
+ // Schema by its own public signature.
155
+ schema: LangChainParameterConverter_1.LangChainParameterConverter.convert(entry.function),
156
+ });
157
+ };
158
+ const getToolName = (controllerName, functionName, prefix) => (prefix ? `${controllerName}_${functionName}` : functionName);
125
159
  })(LangChainToolsRegistrar || (exports.LangChainToolsRegistrar = LangChainToolsRegistrar = {}));
126
160
  //# sourceMappingURL=LangChainToolsRegistrar.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"LangChainToolsRegistrar.js","sourceRoot":"","sources":["../../src/internal/LangChainToolsRegistrar.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"LangChainToolsRegistrar.js","sourceRoot":"","sources":["../../src/internal/LangChainToolsRegistrar.ts"],"names":[],"mappings":"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"}
@@ -1,3 +1,4 @@
1
+ import { LangChainParameterConverter } from "./LangChainParameterConverter.mjs";
1
2
  import { ToolInputParsingException, tool } from "@langchain/core/tools";
2
3
  import { HttpLlm, LlmJson } from "@typia/utils";
3
4
  //#region src/internal/LangChainToolsRegistrar.ts
@@ -6,13 +7,15 @@ let LangChainToolsRegistrar;
6
7
  _LangChainToolsRegistrar.convert = (props) => {
7
8
  const prefix = props.prefix ?? false;
8
9
  const tools = [];
9
- if (prefix === false && props.controllers.length >= 2) {
10
+ if (props.controllers.length >= 2) {
10
11
  const names = /* @__PURE__ */ new Map();
11
12
  const duplicates = [];
12
13
  for (const controller of props.controllers) for (const func of controller.application.functions) {
13
- const existing = names.get(func.name);
14
- if (existing !== void 0) duplicates.push(`"${func.name}" in "${controller.name}" (conflicts with "${existing}")`);
15
- else names.set(func.name, controller.name);
14
+ const toolName = getToolName(controller.name, func.name, prefix);
15
+ const existing = names.get(toolName);
16
+ const origin = `${controller.protocol} controller "${controller.name}" function "${func.name}"`;
17
+ if (existing !== void 0) duplicates.push(`"${toolName}" from ${origin} (conflicts with ${existing})`);
18
+ else names.set(toolName, origin);
16
19
  }
17
20
  if (duplicates.length > 0) throw new Error(`Duplicate tool names found:\n - ${duplicates.join("\n - ")}`);
18
21
  }
@@ -23,12 +26,13 @@ let LangChainToolsRegistrar;
23
26
  const convertClassController = (tools, controller, prefix) => {
24
27
  const execute = controller.execute;
25
28
  for (const func of controller.application.functions) {
26
- const toolName = prefix ? `${controller.name}_${func.name}` : func.name;
29
+ const toolName = getToolName(controller.name, func.name, prefix);
27
30
  const method = execute[func.name];
28
31
  if (typeof method !== "function") throw new Error(`Method "${func.name}" not found on controller "${controller.name}"`);
29
32
  tools.push(createTool({
30
33
  name: toolName,
31
34
  function: func,
35
+ config: controller.application.config,
32
36
  execute: async (args) => method.call(execute, args)
33
37
  }));
34
38
  }
@@ -37,10 +41,11 @@ let LangChainToolsRegistrar;
37
41
  const application = controller.application;
38
42
  const connection = controller.connection;
39
43
  for (const func of application.functions) {
40
- const toolName = prefix ? `${controller.name}_${func.name}` : func.name;
44
+ const toolName = getToolName(controller.name, func.name, prefix);
41
45
  tools.push(createTool({
42
46
  name: toolName,
43
47
  function: func,
48
+ config: application.config,
44
49
  execute: async (args) => {
45
50
  if (controller.execute !== void 0) return (await controller.execute({
46
51
  connection,
@@ -58,30 +63,47 @@ let LangChainToolsRegistrar;
58
63
  }));
59
64
  }
60
65
  };
61
- const createTool = (entry) => tool(async (args) => {
62
- const valid = LlmJson.validateArguments(entry.function, args ?? {});
63
- if (valid.success === false) throw new ToolInputParsingException(`Type errors in "${entry.name}" arguments:\n\n\`\`\`json\n${LlmJson.stringify(valid)}\n\`\`\``, JSON.stringify(args ?? {}));
64
- try {
65
- const result = await entry.execute(valid.data);
66
- if (result === void 0) return entry.function.output === void 0 ? { success: true } : {
67
- success: false,
68
- error: `Function "${entry.name}" returned undefined despite declaring an output schema`
69
- };
70
- return {
71
- success: true,
72
- data: result
73
- };
74
- } catch (error) {
75
- return {
76
- success: false,
77
- error: error instanceof Error ? `${error.name}: ${error.message}` : String(error)
78
- };
79
- }
80
- }, {
81
- name: entry.name,
82
- description: entry.function.description ?? "",
83
- schema: entry.function.parameters
84
- });
66
+ const createTool = (entry) => {
67
+ const validateOutput = entry.function.output === void 0 ? void 0 : LlmJson.validate(entry.function.output, true, entry.config);
68
+ return tool(async (args) => {
69
+ const valid = LlmJson.validateArguments(entry.function, args ?? {});
70
+ if (valid.success === false) throw new ToolInputParsingException(`Type errors in "${entry.name}" arguments:\n\n` + LlmJson.stringify(valid), JSON.stringify(args ?? {}));
71
+ try {
72
+ const result = await entry.execute(valid.data);
73
+ if (result === void 0) return validateOutput === void 0 ? { success: true } : {
74
+ success: false,
75
+ error: `Function "${entry.name}" returned undefined despite declaring an output schema`
76
+ };
77
+ if (validateOutput !== void 0) {
78
+ const output = validateOutput(result);
79
+ if (output.success === false) return {
80
+ success: false,
81
+ error: `Type errors in "${entry.name}" output:
82
+
83
+ ` + LlmJson.stringify(output)
84
+ };
85
+ return {
86
+ success: true,
87
+ data: output.data
88
+ };
89
+ }
90
+ return {
91
+ success: true,
92
+ data: result
93
+ };
94
+ } catch (error) {
95
+ return {
96
+ success: false,
97
+ error: error instanceof Error ? `${error.name}: ${error.message}` : String(error)
98
+ };
99
+ }
100
+ }, {
101
+ name: entry.name,
102
+ description: entry.function.description ?? "",
103
+ schema: LangChainParameterConverter.convert(entry.function)
104
+ });
105
+ };
106
+ const getToolName = (controllerName, functionName, prefix) => prefix ? `${controllerName}_${functionName}` : functionName;
85
107
  })(LangChainToolsRegistrar || (LangChainToolsRegistrar = {}));
86
108
  //#endregion
87
109
  export { LangChainToolsRegistrar };
@@ -1 +1 @@
1
- {"version":3,"file":"LangChainToolsRegistrar.mjs","names":[],"sources":["../../src/internal/LangChainToolsRegistrar.ts"],"sourcesContent":["import {\n DynamicStructuredTool,\n ToolInputParsingException,\n tool,\n} from \"@langchain/core/tools\";\nimport {\n IHttpLlmController,\n IHttpLlmFunction,\n ILlmController,\n ILlmFunction,\n IValidation,\n} from \"@typia/interface\";\nimport { HttpLlm, LlmJson } from \"@typia/utils\";\n\nexport namespace LangChainToolsRegistrar {\n export const convert = (props: {\n controllers: Array<ILlmController | IHttpLlmController>;\n prefix?: boolean | undefined;\n }): DynamicStructuredTool[] => {\n const prefix: boolean = props.prefix ?? false;\n const tools: DynamicStructuredTool[] = [];\n\n // check duplicate tool names\n if (prefix === false && props.controllers.length >= 2) {\n const names: Map<string, string> = new Map();\n const duplicates: string[] = [];\n for (const controller of props.controllers) {\n for (const func of controller.application.functions) {\n const existing: string | undefined = names.get(func.name);\n if (existing !== undefined)\n duplicates.push(\n `\"${func.name}\" in \"${controller.name}\" (conflicts with \"${existing}\")`,\n );\n else names.set(func.name, controller.name);\n }\n }\n if (duplicates.length > 0)\n throw new Error(\n `Duplicate tool names found:\\n - ${duplicates.join(\"\\n - \")}`,\n );\n }\n\n // convert controllers to tools\n for (const controller of props.controllers) {\n if (controller.protocol === \"class\") {\n convertClassController(tools, controller, prefix);\n } else {\n convertHttpController(tools, controller, prefix);\n }\n }\n\n return tools;\n };\n\n const convertClassController = (\n tools: DynamicStructuredTool[],\n controller: ILlmController,\n prefix: boolean,\n ): void => {\n const execute: Record<string, unknown> = controller.execute;\n\n for (const func of controller.application.functions) {\n const toolName: string = prefix\n ? `${controller.name}_${func.name}`\n : func.name;\n\n const method: unknown = execute[func.name];\n if (typeof method !== \"function\") {\n throw new Error(\n `Method \"${func.name}\" not found on controller \"${controller.name}\"`,\n );\n }\n\n tools.push(\n createTool({\n name: toolName,\n function: func,\n execute: async (args: unknown) => method.call(execute, args),\n }),\n );\n }\n };\n\n const convertHttpController = (\n tools: DynamicStructuredTool[],\n controller: IHttpLlmController,\n prefix: boolean,\n ): void => {\n const application = controller.application;\n const connection = controller.connection;\n\n for (const func of application.functions) {\n const toolName: string = prefix\n ? `${controller.name}_${func.name}`\n : func.name;\n\n tools.push(\n createTool({\n name: toolName,\n function: func,\n execute: async (args: unknown) => {\n if (controller.execute !== undefined) {\n const response = await controller.execute({\n connection,\n application,\n function: func,\n arguments: args as object,\n });\n return response.body;\n }\n return HttpLlm.execute({\n application,\n function: func,\n connection,\n input: args as object,\n });\n },\n }),\n );\n }\n };\n\n const createTool = (entry: {\n name: string;\n function: ILlmFunction | IHttpLlmFunction;\n execute: (args: unknown) => Promise<unknown>;\n }): DynamicStructuredTool =>\n tool(\n async (args: unknown): Promise<unknown> => {\n const valid: IValidation<unknown> = LlmJson.validateArguments(\n entry.function,\n args ?? {},\n );\n if (valid.success === false)\n throw new ToolInputParsingException(\n `Type errors in \"${entry.name}\" arguments:\\n\\n` +\n `\\`\\`\\`json\\n${LlmJson.stringify(valid)}\\n\\`\\`\\``,\n JSON.stringify(args ?? {}),\n );\n try {\n const result: unknown = await entry.execute(valid.data);\n if (result === undefined) {\n return entry.function.output === undefined\n ? ({ success: true } satisfies ITryResult)\n : ({\n success: false,\n error: `Function \"${entry.name}\" returned undefined despite declaring an output schema`,\n } satisfies ITryResult);\n }\n return { success: true, data: result } satisfies ITryResult;\n } catch (error) {\n return {\n success: false,\n error:\n error instanceof Error\n ? `${error.name}: ${error.message}`\n : String(error),\n } satisfies ITryResult;\n }\n },\n {\n name: entry.name,\n description: entry.function.description ?? \"\",\n schema: entry.function.parameters,\n },\n );\n}\n\ntype ITryResult =\n | { success: true; data?: unknown | undefined }\n | { success: false; error: string };\n"],"mappings":";;;AAcO,IAAA;;qCACmB,UAGO;EAC7B,MAAM,SAAkB,MAAM,UAAU;EACxC,MAAM,QAAiC,CAAC;EAGxC,IAAI,WAAW,SAAS,MAAM,YAAY,UAAU,GAAG;GACrD,MAAM,wBAA6B,IAAI,IAAI;GAC3C,MAAM,aAAuB,CAAC;GAC9B,KAAK,MAAM,cAAc,MAAM,aAC7B,KAAK,MAAM,QAAQ,WAAW,YAAY,WAAW;IACnD,MAAM,WAA+B,MAAM,IAAI,KAAK,IAAI;IACxD,IAAI,aAAa,KAAA,GACf,WAAW,KACT,IAAI,KAAK,KAAK,QAAQ,WAAW,KAAK,qBAAqB,SAAS,GACtE;SACG,MAAM,IAAI,KAAK,MAAM,WAAW,IAAI;GAC3C;GAEF,IAAI,WAAW,SAAS,GACtB,MAAM,IAAI,MACR,oCAAoC,WAAW,KAAK,QAAQ,GAC9D;EACJ;EAGA,KAAK,MAAM,cAAc,MAAM,aAC7B,IAAI,WAAW,aAAa,SAC1B,uBAAuB,OAAO,YAAY,MAAM;OAEhD,sBAAsB,OAAO,YAAY,MAAM;EAInD,OAAO;CACT;CAEA,MAAM,0BACJ,OACA,YACA,WACS;EACT,MAAM,UAAmC,WAAW;EAEpD,KAAK,MAAM,QAAQ,WAAW,YAAY,WAAW;GACnD,MAAM,WAAmB,SACrB,GAAG,WAAW,KAAK,GAAG,KAAK,SAC3B,KAAK;GAET,MAAM,SAAkB,QAAQ,KAAK;GACrC,IAAI,OAAO,WAAW,YACpB,MAAM,IAAI,MACR,WAAW,KAAK,KAAK,6BAA6B,WAAW,KAAK,EACpE;GAGF,MAAM,KACJ,WAAW;IACT,MAAM;IACN,UAAU;IACV,SAAS,OAAO,SAAkB,OAAO,KAAK,SAAS,IAAI;GAC7D,CAAC,CACH;EACF;CACF;CAEA,MAAM,yBACJ,OACA,YACA,WACS;EACT,MAAM,cAAc,WAAW;EAC/B,MAAM,aAAa,WAAW;EAE9B,KAAK,MAAM,QAAQ,YAAY,WAAW;GACxC,MAAM,WAAmB,SACrB,GAAG,WAAW,KAAK,GAAG,KAAK,SAC3B,KAAK;GAET,MAAM,KACJ,WAAW;IACT,MAAM;IACN,UAAU;IACV,SAAS,OAAO,SAAkB;KAChC,IAAI,WAAW,YAAY,KAAA,GAOzB,QAAO,MANgB,WAAW,QAAQ;MACxC;MACA;MACA,UAAU;MACV,WAAW;KACb,CAAC,EAAA,CACe;KAElB,OAAO,QAAQ,QAAQ;MACrB;MACA,UAAU;MACV;MACA,OAAO;KACT,CAAC;IACH;GACF,CAAC,CACH;EACF;CACF;CAEA,MAAM,cAAc,UAKlB,KACE,OAAO,SAAoC;EACzC,MAAM,QAA8B,QAAQ,kBAC1C,MAAM,UACN,QAAQ,CAAC,CACX;EACA,IAAI,MAAM,YAAY,OACpB,MAAM,IAAI,0BACR,mBAAmB,MAAM,KAAK,8BACb,QAAQ,UAAU,KAAK,EAAE,WAC1C,KAAK,UAAU,QAAQ,CAAC,CAAC,CAC3B;EACF,IAAI;GACF,MAAM,SAAkB,MAAM,MAAM,QAAQ,MAAM,IAAI;GACtD,IAAI,WAAW,KAAA,GACb,OAAO,MAAM,SAAS,WAAW,KAAA,IAC5B,EAAE,SAAS,KAAK,IAChB;IACC,SAAS;IACT,OAAO,aAAa,MAAM,KAAK;GACjC;GAEN,OAAO;IAAE,SAAS;IAAM,MAAM;GAAO;EACvC,SAAS,OAAO;GACd,OAAO;IACL,SAAS;IACT,OACE,iBAAiB,QACb,GAAG,MAAM,KAAK,IAAI,MAAM,YACxB,OAAO,KAAK;GACpB;EACF;CACF,GACA;EACE,MAAM,MAAM;EACZ,aAAa,MAAM,SAAS,eAAe;EAC3C,QAAQ,MAAM,SAAS;CACzB,CACF;GACH,4BAAA,0BAAA,CAAA,EAAD"}
1
+ {"version":3,"file":"LangChainToolsRegistrar.mjs","names":[],"sources":["../../src/internal/LangChainToolsRegistrar.ts"],"sourcesContent":["import {\n DynamicStructuredTool,\n ToolInputParsingException,\n tool,\n} from \"@langchain/core/tools\";\nimport type { JSONSchema } from \"@langchain/core/utils/json_schema\";\nimport {\n IHttpLlmController,\n IHttpLlmFunction,\n ILlmController,\n ILlmFunction,\n ILlmSchema,\n IValidation,\n} from \"@typia/interface\";\nimport { HttpLlm, LlmJson } from \"@typia/utils\";\n\nimport { LangChainParameterConverter } from \"./LangChainParameterConverter\";\n\nexport namespace LangChainToolsRegistrar {\n export const convert = (props: {\n controllers: Array<ILlmController | IHttpLlmController>;\n prefix?: boolean | undefined;\n }): DynamicStructuredTool[] => {\n const prefix: boolean = props.prefix ?? false;\n const tools: DynamicStructuredTool[] = [];\n\n // check duplicate tool names\n if (props.controllers.length >= 2) {\n const names: Map<string, string> = new Map();\n const duplicates: string[] = [];\n for (const controller of props.controllers) {\n for (const func of controller.application.functions) {\n const toolName: string = getToolName(\n controller.name,\n func.name,\n prefix,\n );\n const existing: string | undefined = names.get(toolName);\n const origin: string = `${controller.protocol} controller \"${controller.name}\" function \"${func.name}\"`;\n if (existing !== undefined)\n duplicates.push(\n `\"${toolName}\" from ${origin} (conflicts with ${existing})`,\n );\n else names.set(toolName, origin);\n }\n }\n if (duplicates.length > 0)\n throw new Error(\n `Duplicate tool names found:\\n - ${duplicates.join(\"\\n - \")}`,\n );\n }\n\n // convert controllers to tools\n for (const controller of props.controllers) {\n if (controller.protocol === \"class\") {\n convertClassController(tools, controller, prefix);\n } else {\n convertHttpController(tools, controller, prefix);\n }\n }\n\n return tools;\n };\n\n const convertClassController = (\n tools: DynamicStructuredTool[],\n controller: ILlmController,\n prefix: boolean,\n ): void => {\n const execute: Record<string, unknown> = controller.execute;\n\n for (const func of controller.application.functions) {\n const toolName: string = getToolName(controller.name, func.name, prefix);\n\n const method: unknown = execute[func.name];\n if (typeof method !== \"function\") {\n throw new Error(\n `Method \"${func.name}\" not found on controller \"${controller.name}\"`,\n );\n }\n\n tools.push(\n createTool({\n name: toolName,\n function: func,\n config: controller.application.config,\n execute: async (args: unknown) => method.call(execute, args),\n }),\n );\n }\n };\n\n const convertHttpController = (\n tools: DynamicStructuredTool[],\n controller: IHttpLlmController,\n prefix: boolean,\n ): void => {\n const application = controller.application;\n const connection = controller.connection;\n\n for (const func of application.functions) {\n const toolName: string = getToolName(controller.name, func.name, prefix);\n\n tools.push(\n createTool({\n name: toolName,\n function: func,\n config: application.config,\n execute: async (args: unknown) => {\n if (controller.execute !== undefined) {\n const response = await controller.execute({\n connection,\n application,\n function: func,\n arguments: args as object,\n });\n return response.body;\n }\n return HttpLlm.execute({\n application,\n function: func,\n connection,\n input: args as object,\n });\n },\n }),\n );\n }\n };\n\n const createTool = (entry: {\n name: string;\n function: ILlmFunction | IHttpLlmFunction;\n config: ILlmSchema.IConfig;\n execute: (args: unknown) => Promise<unknown>;\n }): DynamicStructuredTool => {\n // A declared return type is a promise to the model, so a result that breaks\n // it is reported rather than delivered. `@typia/mcp` and `@typia/vercel`\n // already refuse one; this tool consulted `output` for its existence alone,\n // so a method returning nothing was reported while a method returning the\n // wrong shape was not (#2302).\n //\n // The application's own config decides how the schema is read back: a\n // strict application carries its constraints as description tags instead of\n // keywords, and only that config tells the inverter to read them.\n const validateOutput:\n | ((output: unknown) => IValidation<unknown>)\n | undefined =\n entry.function.output === undefined\n ? undefined\n : LlmJson.validate(entry.function.output, true, entry.config);\n return tool(\n async (args: unknown): Promise<unknown> => {\n const valid: IValidation<unknown> = LlmJson.validateArguments(\n entry.function,\n args ?? {},\n );\n if (valid.success === false)\n throw new ToolInputParsingException(\n `Type errors in \"${entry.name}\" arguments:\\n\\n` +\n LlmJson.stringify(valid),\n JSON.stringify(args ?? {}),\n );\n try {\n const result: unknown = await entry.execute(valid.data);\n if (result === undefined) {\n return validateOutput === undefined\n ? ({ success: true } satisfies ITryResult)\n : ({\n success: false,\n error: `Function \"${entry.name}\" returned undefined despite declaring an output schema`,\n } satisfies ITryResult);\n }\n if (validateOutput !== undefined) {\n const output: IValidation<unknown> = validateOutput(result);\n if (output.success === false)\n return {\n success: false,\n error:\n `Type errors in \"${entry.name}\" output:\n\n` + LlmJson.stringify(output),\n } satisfies ITryResult;\n return { success: true, data: output.data } satisfies ITryResult;\n }\n return { success: true, data: result } satisfies ITryResult;\n } catch (error) {\n return {\n success: false,\n error:\n error instanceof Error\n ? `${error.name}: ${error.message}`\n : String(error),\n } satisfies ITryResult;\n }\n },\n {\n name: entry.name,\n description: entry.function.description ?? \"\",\n // Declares the model-facing schema without a validator, so the\n // coerce-and-validate step above is the only one that runs; see\n // `LangChainParameterConverter`. The cast is needed because `tool()`\n // still types `schema` as Zod-or-JSON-Schema, even though the\n // `toJsonSchema` it reads that schema back with accepts Standard JSON\n // Schema by its own public signature.\n schema: LangChainParameterConverter.convert(\n entry.function,\n ) as JSONSchema,\n },\n );\n };\n\n const getToolName = (\n controllerName: string,\n functionName: string,\n prefix: boolean,\n ): string => (prefix ? `${controllerName}_${functionName}` : functionName);\n}\n\ntype ITryResult =\n | { success: true; data?: unknown | undefined }\n | { success: false; error: string };\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,12 +1,19 @@
1
1
  {
2
2
  "name": "@typia/langchain",
3
- "version": "13.1.1",
3
+ "version": "13.2.0",
4
4
  "description": "LangChain.js integration for typia",
5
5
  "main": "lib/index.js",
6
6
  "exports": {
7
7
  ".": {
8
+ "import": {
9
+ "types": "./lib/index.d.mts",
10
+ "default": "./lib/index.mjs"
11
+ },
12
+ "require": {
13
+ "types": "./lib/index.d.cts",
14
+ "default": "./lib/index.js"
15
+ },
8
16
  "types": "./lib/index.d.ts",
9
- "import": "./lib/index.mjs",
10
17
  "default": "./lib/index.js"
11
18
  },
12
19
  "./package.json": "./package.json"
@@ -22,18 +29,19 @@
22
29
  },
23
30
  "homepage": "https://typia.io",
24
31
  "dependencies": {
25
- "@typia/utils": "^13.1.1",
26
- "@typia/interface": "^13.1.1"
32
+ "@standard-schema/spec": "^1.1.0",
33
+ "@typia/interface": "^13.2.0",
34
+ "@typia/utils": "^13.2.0"
27
35
  },
28
36
  "peerDependencies": {
29
- "@langchain/core": ">=1.0.0"
37
+ "@langchain/core": ">=1.1.30"
30
38
  },
31
39
  "devDependencies": {
32
40
  "@langchain/core": "^1.1.31",
33
41
  "rimraf": "^6.1.2",
34
42
  "rolldown": "^1.1.5",
35
43
  "tinyglobby": "^0.2.12",
36
- "ttsc": "^0.18.4",
44
+ "ttsc": "^0.19.2",
37
45
  "typescript": "^7.0.2"
38
46
  },
39
47
  "sideEffects": false,
@@ -60,7 +68,7 @@
60
68
  "access": "public"
61
69
  },
62
70
  "scripts": {
63
- "build": "rimraf lib && ttsc && rolldown -c",
71
+ "build": "rimraf lib && ttsc && node ../../scripts/create-conditional-declarations.cjs lib/index.d.ts && rolldown -c",
64
72
  "dev": "ttsc --watch"
65
73
  },
66
74
  "module": "lib/index.mjs",
@@ -0,0 +1,55 @@
1
+ import { StandardJSONSchemaV1 } from "@standard-schema/spec";
2
+ import { IHttpLlmFunction, ILlmFunction } from "@typia/interface";
3
+
4
+ /**
5
+ * A Standard JSON Schema view of one typia function's parameters.
6
+ *
7
+ * LangChain's tool `schema` fills two roles at once:
8
+ * `toJsonSchema(tool.schema)` turns it into the parameters the model is shown,
9
+ * and `StructuredTool.call` validates incoming arguments against it before the
10
+ * tool body runs. Handing over a bare `ILlmSchema.IParameters` fills the first
11
+ * role but forfeits the second — LangChain validates it with
12
+ * `@cfworker/json-schema`, which rejects what typia would have coerced and
13
+ * reports the failure in its own words, so typia's coercion and its correctable
14
+ * feedback both become unreachable.
15
+ *
16
+ * Standard JSON Schema separates the two roles. It carries a model-facing
17
+ * schema under `~standard.jsonSchema` and, by declaring no
18
+ * `~standard.validate`, no validator — which is exactly typia's position: show
19
+ * the model this schema, and leave validation to `LlmJson.validateArguments` in
20
+ * the tool body. LangChain reads the schema back through its own
21
+ * `toJsonSchema`, so the model sees the same parameters document as before,
22
+ * byte for byte.
23
+ *
24
+ * `@typia/vercel` states the same contract to the AI SDK through
25
+ * `jsonSchema()`, whose `validate` is likewise left undefined so that
26
+ * `safeValidateTypes` short-circuits its own validation.
27
+ *
28
+ * Two version floors hold this together, and both are pinned in the package
29
+ * manifest. `toJsonSchema` only reads `~standard.jsonSchema` from
30
+ * `@langchain/core@1.1.30` onwards — older versions return the wrapper itself
31
+ * and would advertise _that_ to the model in place of the parameters — and
32
+ * `StandardJSONSchemaV1` only exists from `@standard-schema/spec@1.1.0`.
33
+ *
34
+ * @see https://github.com/standard-schema/standard-schema
35
+ */
36
+ export namespace LangChainParameterConverter {
37
+ export const convert = (
38
+ func: ILlmFunction | IHttpLlmFunction,
39
+ ): StandardJSONSchemaV1 => {
40
+ // Coercion never changes the shape a value must end up in, so the input and
41
+ // output schemas are the same document.
42
+ const jsonSchema = (): Record<string, unknown> =>
43
+ func.parameters as unknown as Record<string, unknown>;
44
+ return {
45
+ "~standard": {
46
+ version: 1,
47
+ vendor: "typia",
48
+ jsonSchema: {
49
+ input: jsonSchema,
50
+ output: jsonSchema,
51
+ },
52
+ },
53
+ };
54
+ };
55
+ }
@@ -3,15 +3,19 @@ import {
3
3
  ToolInputParsingException,
4
4
  tool,
5
5
  } from "@langchain/core/tools";
6
+ import type { JSONSchema } from "@langchain/core/utils/json_schema";
6
7
  import {
7
8
  IHttpLlmController,
8
9
  IHttpLlmFunction,
9
10
  ILlmController,
10
11
  ILlmFunction,
12
+ ILlmSchema,
11
13
  IValidation,
12
14
  } from "@typia/interface";
13
15
  import { HttpLlm, LlmJson } from "@typia/utils";
14
16
 
17
+ import { LangChainParameterConverter } from "./LangChainParameterConverter";
18
+
15
19
  export namespace LangChainToolsRegistrar {
16
20
  export const convert = (props: {
17
21
  controllers: Array<ILlmController | IHttpLlmController>;
@@ -21,17 +25,23 @@ export namespace LangChainToolsRegistrar {
21
25
  const tools: DynamicStructuredTool[] = [];
22
26
 
23
27
  // check duplicate tool names
24
- if (prefix === false && props.controllers.length >= 2) {
28
+ if (props.controllers.length >= 2) {
25
29
  const names: Map<string, string> = new Map();
26
30
  const duplicates: string[] = [];
27
31
  for (const controller of props.controllers) {
28
32
  for (const func of controller.application.functions) {
29
- const existing: string | undefined = names.get(func.name);
33
+ const toolName: string = getToolName(
34
+ controller.name,
35
+ func.name,
36
+ prefix,
37
+ );
38
+ const existing: string | undefined = names.get(toolName);
39
+ const origin: string = `${controller.protocol} controller "${controller.name}" function "${func.name}"`;
30
40
  if (existing !== undefined)
31
41
  duplicates.push(
32
- `"${func.name}" in "${controller.name}" (conflicts with "${existing}")`,
42
+ `"${toolName}" from ${origin} (conflicts with ${existing})`,
33
43
  );
34
- else names.set(func.name, controller.name);
44
+ else names.set(toolName, origin);
35
45
  }
36
46
  }
37
47
  if (duplicates.length > 0)
@@ -60,9 +70,7 @@ export namespace LangChainToolsRegistrar {
60
70
  const execute: Record<string, unknown> = controller.execute;
61
71
 
62
72
  for (const func of controller.application.functions) {
63
- const toolName: string = prefix
64
- ? `${controller.name}_${func.name}`
65
- : func.name;
73
+ const toolName: string = getToolName(controller.name, func.name, prefix);
66
74
 
67
75
  const method: unknown = execute[func.name];
68
76
  if (typeof method !== "function") {
@@ -75,6 +83,7 @@ export namespace LangChainToolsRegistrar {
75
83
  createTool({
76
84
  name: toolName,
77
85
  function: func,
86
+ config: controller.application.config,
78
87
  execute: async (args: unknown) => method.call(execute, args),
79
88
  }),
80
89
  );
@@ -90,14 +99,13 @@ export namespace LangChainToolsRegistrar {
90
99
  const connection = controller.connection;
91
100
 
92
101
  for (const func of application.functions) {
93
- const toolName: string = prefix
94
- ? `${controller.name}_${func.name}`
95
- : func.name;
102
+ const toolName: string = getToolName(controller.name, func.name, prefix);
96
103
 
97
104
  tools.push(
98
105
  createTool({
99
106
  name: toolName,
100
107
  function: func,
108
+ config: application.config,
101
109
  execute: async (args: unknown) => {
102
110
  if (controller.execute !== undefined) {
103
111
  const response = await controller.execute({
@@ -123,9 +131,25 @@ export namespace LangChainToolsRegistrar {
123
131
  const createTool = (entry: {
124
132
  name: string;
125
133
  function: ILlmFunction | IHttpLlmFunction;
134
+ config: ILlmSchema.IConfig;
126
135
  execute: (args: unknown) => Promise<unknown>;
127
- }): DynamicStructuredTool =>
128
- tool(
136
+ }): DynamicStructuredTool => {
137
+ // A declared return type is a promise to the model, so a result that breaks
138
+ // it is reported rather than delivered. `@typia/mcp` and `@typia/vercel`
139
+ // already refuse one; this tool consulted `output` for its existence alone,
140
+ // so a method returning nothing was reported while a method returning the
141
+ // wrong shape was not (#2302).
142
+ //
143
+ // The application's own config decides how the schema is read back: a
144
+ // strict application carries its constraints as description tags instead of
145
+ // keywords, and only that config tells the inverter to read them.
146
+ const validateOutput:
147
+ | ((output: unknown) => IValidation<unknown>)
148
+ | undefined =
149
+ entry.function.output === undefined
150
+ ? undefined
151
+ : LlmJson.validate(entry.function.output, true, entry.config);
152
+ return tool(
129
153
  async (args: unknown): Promise<unknown> => {
130
154
  const valid: IValidation<unknown> = LlmJson.validateArguments(
131
155
  entry.function,
@@ -134,19 +158,31 @@ export namespace LangChainToolsRegistrar {
134
158
  if (valid.success === false)
135
159
  throw new ToolInputParsingException(
136
160
  `Type errors in "${entry.name}" arguments:\n\n` +
137
- `\`\`\`json\n${LlmJson.stringify(valid)}\n\`\`\``,
161
+ LlmJson.stringify(valid),
138
162
  JSON.stringify(args ?? {}),
139
163
  );
140
164
  try {
141
165
  const result: unknown = await entry.execute(valid.data);
142
166
  if (result === undefined) {
143
- return entry.function.output === undefined
167
+ return validateOutput === undefined
144
168
  ? ({ success: true } satisfies ITryResult)
145
169
  : ({
146
170
  success: false,
147
171
  error: `Function "${entry.name}" returned undefined despite declaring an output schema`,
148
172
  } satisfies ITryResult);
149
173
  }
174
+ if (validateOutput !== undefined) {
175
+ const output: IValidation<unknown> = validateOutput(result);
176
+ if (output.success === false)
177
+ return {
178
+ success: false,
179
+ error:
180
+ `Type errors in "${entry.name}" output:
181
+
182
+ ` + LlmJson.stringify(output),
183
+ } satisfies ITryResult;
184
+ return { success: true, data: output.data } satisfies ITryResult;
185
+ }
150
186
  return { success: true, data: result } satisfies ITryResult;
151
187
  } catch (error) {
152
188
  return {
@@ -161,9 +197,24 @@ export namespace LangChainToolsRegistrar {
161
197
  {
162
198
  name: entry.name,
163
199
  description: entry.function.description ?? "",
164
- schema: entry.function.parameters,
200
+ // Declares the model-facing schema without a validator, so the
201
+ // coerce-and-validate step above is the only one that runs; see
202
+ // `LangChainParameterConverter`. The cast is needed because `tool()`
203
+ // still types `schema` as Zod-or-JSON-Schema, even though the
204
+ // `toJsonSchema` it reads that schema back with accepts Standard JSON
205
+ // Schema by its own public signature.
206
+ schema: LangChainParameterConverter.convert(
207
+ entry.function,
208
+ ) as JSONSchema,
165
209
  },
166
210
  );
211
+ };
212
+
213
+ const getToolName = (
214
+ controllerName: string,
215
+ functionName: string,
216
+ prefix: boolean,
217
+ ): string => (prefix ? `${controllerName}_${functionName}` : functionName);
167
218
  }
168
219
 
169
220
  type ITryResult =