@typia/langchain 12.0.0-dev.20260310 → 12.0.0-dev.20260312

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.
package/lib/index.d.ts CHANGED
@@ -12,14 +12,13 @@ import { IHttpLlmController, ILlmController } from "@typia/interface";
12
12
  *
13
13
  * @example
14
14
  * ```typescript
15
- * import { ChatOpenAI } from "@langchain/openai";
16
- * import { AgentExecutor, createToolCallingAgent } from "langchain/agents";
15
+ * import { initChatModel } from "langchain/chat_models/universal";
17
16
  * import typia from "typia";
18
17
  * import { toLangChainTools } from "@typia/langchain";
19
18
  *
20
19
  * class Calculator {
21
- * add(input: { a: number; b: number }): number {
22
- * return input.a + input.b;
20
+ * add(input: { a: number; b: number }): { value: number } {
21
+ * return { value: input.a + input.b };
23
22
  * }
24
23
  * }
25
24
  *
@@ -29,10 +28,9 @@ import { IHttpLlmController, ILlmController } from "@typia/interface";
29
28
  * ],
30
29
  * });
31
30
  *
32
- * const llm = new ChatOpenAI({ model: "gpt-4" });
33
- * const agent = createToolCallingAgent({ llm, tools, prompt });
34
- * const executor = new AgentExecutor({ agent, tools });
35
- * await executor.invoke({ input: "What is 10 + 5?" });
31
+ * const llm = await initChatModel();
32
+ * const modelWithTools = llm.bindTools(tools);
33
+ * const result = await modelWithTools.invoke("What is 10 + 5?");
36
34
  * ```;
37
35
  *
38
36
  * @param props Conversion properties
@@ -54,7 +52,7 @@ export declare function toLangChainTools(props: {
54
52
  * If `true`, tool names become `{controllerName}_{methodName}`. If `false`,
55
53
  * tool names are just `{methodName}`.
56
54
  *
57
- * @default true
55
+ * @default false
58
56
  */
59
57
  prefix?: boolean | undefined;
60
58
  }): DynamicStructuredTool[];
package/lib/index.js CHANGED
@@ -14,14 +14,13 @@ const LangChainToolsRegistrar_1 = require("./internal/LangChainToolsRegistrar");
14
14
  *
15
15
  * @example
16
16
  * ```typescript
17
- * import { ChatOpenAI } from "@langchain/openai";
18
- * import { AgentExecutor, createToolCallingAgent } from "langchain/agents";
17
+ * import { initChatModel } from "langchain/chat_models/universal";
19
18
  * import typia from "typia";
20
19
  * import { toLangChainTools } from "@typia/langchain";
21
20
  *
22
21
  * class Calculator {
23
- * add(input: { a: number; b: number }): number {
24
- * return input.a + input.b;
22
+ * add(input: { a: number; b: number }): { value: number } {
23
+ * return { value: input.a + input.b };
25
24
  * }
26
25
  * }
27
26
  *
@@ -31,10 +30,9 @@ const LangChainToolsRegistrar_1 = require("./internal/LangChainToolsRegistrar");
31
30
  * ],
32
31
  * });
33
32
  *
34
- * const llm = new ChatOpenAI({ model: "gpt-4" });
35
- * const agent = createToolCallingAgent({ llm, tools, prompt });
36
- * const executor = new AgentExecutor({ agent, tools });
37
- * await executor.invoke({ input: "What is 10 + 5?" });
33
+ * const llm = await initChatModel();
34
+ * const modelWithTools = llm.bindTools(tools);
35
+ * const result = await modelWithTools.invoke("What is 10 + 5?");
38
36
  * ```;
39
37
  *
40
38
  * @param props Conversion properties
package/lib/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;AA2CA,4CAsBC;AA9DD,gFAA6E;AAE7E;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAqCG;AACH,SAAgB,gBAAgB,CAAC,KAoBhC;IACC,OAAO,iDAAuB,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;AAChD,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;AAyCA,4CAsBC;AA5DD,gFAA6E;AAE7E;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmCG;AACH,SAAgB,gBAAgB,CAAC,KAoBhC;IACC,OAAO,iDAAuB,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC;AAChD,CAAC"}
package/lib/index.mjs CHANGED
@@ -12,14 +12,13 @@ import { LangChainToolsRegistrar } from './internal/LangChainToolsRegistrar.mjs'
12
12
  *
13
13
  * @example
14
14
  * ```typescript
15
- * import { ChatOpenAI } from "@langchain/openai";
16
- * import { AgentExecutor, createToolCallingAgent } from "langchain/agents";
15
+ * import { initChatModel } from "langchain/chat_models/universal";
17
16
  * import typia from "typia";
18
17
  * import { toLangChainTools } from "@typia/langchain";
19
18
  *
20
19
  * class Calculator {
21
- * add(input: { a: number; b: number }): number {
22
- * return input.a + input.b;
20
+ * add(input: { a: number; b: number }): { value: number } {
21
+ * return { value: input.a + input.b };
23
22
  * }
24
23
  * }
25
24
  *
@@ -29,10 +28,9 @@ import { LangChainToolsRegistrar } from './internal/LangChainToolsRegistrar.mjs'
29
28
  * ],
30
29
  * });
31
30
  *
32
- * const llm = new ChatOpenAI({ model: "gpt-4" });
33
- * const agent = createToolCallingAgent({ llm, tools, prompt });
34
- * const executor = new AgentExecutor({ agent, tools });
35
- * await executor.invoke({ input: "What is 10 + 5?" });
31
+ * const llm = await initChatModel();
32
+ * const modelWithTools = llm.bindTools(tools);
33
+ * const result = await modelWithTools.invoke("What is 10 + 5?");
36
34
  * ```;
37
35
  *
38
36
  * @param props Conversion properties
package/lib/index.mjs.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.mjs","sources":["../src/index.ts"],"sourcesContent":[null],"names":[],"mappings":";;AAKA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAqCG;AACG,SAAU,gBAAgB,CAAC,KAoBhC,EAAA;AACC,IAAA,OAAO,uBAAuB,CAAC,OAAO,CAAC,KAAK,CAAC;AAC/C;;;;"}
1
+ {"version":3,"file":"index.mjs","sources":["../src/index.ts"],"sourcesContent":[null],"names":[],"mappings":";;AAKA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAmCG;AACG,SAAU,gBAAgB,CAAC,KAoBhC,EAAA;AACC,IAAA,OAAO,uBAAuB,CAAC,OAAO,CAAC,KAAK,CAAC;AAC/C;;;;"}
@@ -12,13 +12,29 @@ 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 zod_1 = require("zod");
16
15
  var LangChainToolsRegistrar;
17
16
  (function (LangChainToolsRegistrar) {
18
17
  LangChainToolsRegistrar.convert = (props) => {
19
18
  var _a;
20
- const prefix = (_a = props.prefix) !== null && _a !== void 0 ? _a : true;
19
+ const prefix = (_a = props.prefix) !== null && _a !== void 0 ? _a : false;
21
20
  const tools = [];
21
+ // check duplicate tool names
22
+ if (prefix === false && props.controllers.length >= 2) {
23
+ const names = new Map();
24
+ const duplicates = [];
25
+ for (const controller of props.controllers) {
26
+ for (const func of controller.application.functions) {
27
+ const existing = names.get(func.name);
28
+ if (existing !== undefined)
29
+ duplicates.push(`"${func.name}" in "${controller.name}" (conflicts with "${existing}")`);
30
+ else
31
+ names.set(func.name, controller.name);
32
+ }
33
+ }
34
+ if (duplicates.length > 0)
35
+ throw new Error(`Duplicate tool names found:\n - ${duplicates.join("\n - ")}`);
36
+ }
37
+ // convert controllers to tools
22
38
  for (const controller of props.controllers) {
23
39
  if (controller.protocol === "class") {
24
40
  convertClassController(tools, controller, prefix);
@@ -32,13 +48,13 @@ var LangChainToolsRegistrar;
32
48
  const convertClassController = (tools, controller, prefix) => {
33
49
  const execute = controller.execute;
34
50
  for (const func of controller.application.functions) {
51
+ const toolName = prefix
52
+ ? `${controller.name}_${func.name}`
53
+ : func.name;
35
54
  const method = execute[func.name];
36
55
  if (typeof method !== "function") {
37
56
  throw new Error(`Method "${func.name}" not found on controller "${controller.name}"`);
38
57
  }
39
- const toolName = prefix
40
- ? `${controller.name}_${func.name}`
41
- : func.name;
42
58
  tools.push(createTool({
43
59
  name: toolName,
44
60
  function: func,
@@ -76,35 +92,22 @@ var LangChainToolsRegistrar;
76
92
  }));
77
93
  }
78
94
  };
79
- // Schema that accepts any object - bypasses LangChain's validation
80
- // so typia can handle all validation with proper error messages.
81
- // LangChain validates JSON Schema using @cfworker/json-schema which
82
- // throws ToolInputParsingException before reaching our func.
83
- const passthroughSchema = zod_1.z.record(zod_1.z.unknown());
84
95
  const createTool = (entry) => {
85
96
  var _a;
86
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
87
97
  return new tools_1.DynamicStructuredTool({
88
98
  name: entry.name,
89
99
  description: (_a = entry.function.description) !== null && _a !== void 0 ? _a : "",
90
- schema: passthroughSchema,
100
+ schema: entry.function.parameters,
91
101
  func: (args) => __awaiter(this, void 0, void 0, function* () {
92
102
  const coerced = utils_1.LlmJson.coerce(args, entry.function.parameters);
93
- const validation = entry.function.validate(coerced);
94
- if (!validation.success) {
95
- return utils_1.LlmJson.stringify(validation);
96
- }
97
- try {
98
- const result = yield entry.execute(validation.data);
99
- return result === undefined
100
- ? "Success"
101
- : JSON.stringify(result, null, 2);
102
- }
103
- catch (error) {
104
- return error instanceof Error
105
- ? `${error.name}: ${error.message}`
106
- : String(error);
107
- }
103
+ const valid = entry.function.validate(coerced);
104
+ if (valid.success === false)
105
+ throw new tools_1.ToolInputParsingException(`Type errors in "${entry.name}" arguments:\n\n` +
106
+ `\`\`\`json\n${utils_1.LlmJson.stringify(valid)}\n\`\`\``, JSON.stringify(coerced));
107
+ const result = yield entry.execute(valid.data);
108
+ return result === undefined
109
+ ? { success: true }
110
+ : { success: true, data: result };
108
111
  }),
109
112
  });
110
113
  };
@@ -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,12 +1,28 @@
1
- import { DynamicStructuredTool } from '@langchain/core/tools';
1
+ import { DynamicStructuredTool, ToolInputParsingException } from '@langchain/core/tools';
2
2
  import { LlmJson, HttpLlm } from '@typia/utils';
3
- import { z } from 'zod';
4
3
 
5
4
  var LangChainToolsRegistrar;
6
5
  (function (LangChainToolsRegistrar) {
7
6
  LangChainToolsRegistrar.convert = (props) => {
8
- const prefix = props.prefix ?? true;
7
+ const prefix = props.prefix ?? false;
9
8
  const tools = [];
9
+ // check duplicate tool names
10
+ if (prefix === false && props.controllers.length >= 2) {
11
+ const names = new Map();
12
+ const duplicates = [];
13
+ for (const controller of props.controllers) {
14
+ for (const func of controller.application.functions) {
15
+ const existing = names.get(func.name);
16
+ if (existing !== undefined)
17
+ duplicates.push(`"${func.name}" in "${controller.name}" (conflicts with "${existing}")`);
18
+ else
19
+ names.set(func.name, controller.name);
20
+ }
21
+ }
22
+ if (duplicates.length > 0)
23
+ throw new Error(`Duplicate tool names found:\n - ${duplicates.join("\n - ")}`);
24
+ }
25
+ // convert controllers to tools
10
26
  for (const controller of props.controllers) {
11
27
  if (controller.protocol === "class") {
12
28
  convertClassController(tools, controller, prefix);
@@ -20,13 +36,13 @@ var LangChainToolsRegistrar;
20
36
  const convertClassController = (tools, controller, prefix) => {
21
37
  const execute = controller.execute;
22
38
  for (const func of controller.application.functions) {
39
+ const toolName = prefix
40
+ ? `${controller.name}_${func.name}`
41
+ : func.name;
23
42
  const method = execute[func.name];
24
43
  if (typeof method !== "function") {
25
44
  throw new Error(`Method "${func.name}" not found on controller "${controller.name}"`);
26
45
  }
27
- const toolName = prefix
28
- ? `${controller.name}_${func.name}`
29
- : func.name;
30
46
  tools.push(createTool({
31
47
  name: toolName,
32
48
  function: func,
@@ -64,37 +80,22 @@ var LangChainToolsRegistrar;
64
80
  }));
65
81
  }
66
82
  };
67
- // Schema that accepts any object - bypasses LangChain's validation
68
- // so typia can handle all validation with proper error messages.
69
- // LangChain validates JSON Schema using @cfworker/json-schema which
70
- // throws ToolInputParsingException before reaching our func.
71
- const passthroughSchema = z.record(z.unknown());
72
- const createTool = (entry) => {
73
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
74
- return new DynamicStructuredTool({
75
- name: entry.name,
76
- description: entry.function.description ?? "",
77
- schema: passthroughSchema,
78
- func: async (args) => {
79
- const coerced = LlmJson.coerce(args, entry.function.parameters);
80
- const validation = entry.function.validate(coerced);
81
- if (!validation.success) {
82
- return LlmJson.stringify(validation);
83
- }
84
- try {
85
- const result = await entry.execute(validation.data);
86
- return result === undefined
87
- ? "Success"
88
- : JSON.stringify(result, null, 2);
89
- }
90
- catch (error) {
91
- return error instanceof Error
92
- ? `${error.name}: ${error.message}`
93
- : String(error);
94
- }
95
- },
96
- });
97
- };
83
+ const createTool = (entry) => new DynamicStructuredTool({
84
+ name: entry.name,
85
+ description: entry.function.description ?? "",
86
+ schema: entry.function.parameters,
87
+ func: async (args) => {
88
+ const coerced = LlmJson.coerce(args, entry.function.parameters);
89
+ const valid = entry.function.validate(coerced);
90
+ if (valid.success === false)
91
+ throw new ToolInputParsingException(`Type errors in "${entry.name}" arguments:\n\n` +
92
+ `\`\`\`json\n${LlmJson.stringify(valid)}\n\`\`\``, JSON.stringify(coerced));
93
+ const result = await entry.execute(valid.data);
94
+ return result === undefined
95
+ ? { success: true }
96
+ : { success: true, data: result };
97
+ },
98
+ });
98
99
  })(LangChainToolsRegistrar || (LangChainToolsRegistrar = {}));
99
100
 
100
101
  export { LangChainToolsRegistrar };
@@ -1 +1 @@
1
- {"version":3,"file":"LangChainToolsRegistrar.mjs","sources":["../../src/internal/LangChainToolsRegistrar.ts"],"sourcesContent":[null],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"LangChainToolsRegistrar.mjs","sources":["../../src/internal/LangChainToolsRegistrar.ts"],"sourcesContent":[null],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@typia/langchain",
3
- "version": "12.0.0-dev.20260310",
3
+ "version": "12.0.0-dev.20260312",
4
4
  "description": "LangChain.js integration for typia",
5
5
  "main": "lib/index.js",
6
6
  "exports": {
@@ -22,15 +22,14 @@
22
22
  },
23
23
  "homepage": "https://typia.io",
24
24
  "dependencies": {
25
- "zod": "^3.25.0",
26
- "@typia/interface": "^12.0.0-dev.20260310",
27
- "@typia/utils": "^12.0.0-dev.20260310"
25
+ "@typia/interface": "^12.0.0-dev.20260312",
26
+ "@typia/utils": "^12.0.0-dev.20260312"
28
27
  },
29
28
  "peerDependencies": {
30
- "@langchain/core": ">=0.3.0"
29
+ "@langchain/core": ">=1.0.0"
31
30
  },
32
31
  "devDependencies": {
33
- "@langchain/core": "^0.3.44",
32
+ "@langchain/core": "^1.1.31",
34
33
  "@rollup/plugin-commonjs": "^29.0.0",
35
34
  "@rollup/plugin-node-resolve": "^16.0.3",
36
35
  "@rollup/plugin-typescript": "^12.3.0",
package/src/index.ts CHANGED
@@ -1,66 +1,64 @@
1
- import type { DynamicStructuredTool } from "@langchain/core/tools";
2
- import { IHttpLlmController, ILlmController } from "@typia/interface";
3
-
4
- import { LangChainToolsRegistrar } from "./internal/LangChainToolsRegistrar";
5
-
6
- /**
7
- * Convert typia controllers to LangChain tools.
8
- *
9
- * Converts TypeScript class methods via `typia.llm.controller<Class>()` or
10
- * OpenAPI operations via `HttpLlm.controller()` to LangChain tools.
11
- *
12
- * Every tool call is validated by typia. If LLM provides invalid arguments,
13
- * returns validation error formatted by {@link LlmJson.stringify} so that LLM
14
- * can correct them automatically.
15
- *
16
- * @example
17
- * ```typescript
18
- * import { ChatOpenAI } from "@langchain/openai";
19
- * import { AgentExecutor, createToolCallingAgent } from "langchain/agents";
20
- * import typia from "typia";
21
- * import { toLangChainTools } from "@typia/langchain";
22
- *
23
- * class Calculator {
24
- * add(input: { a: number; b: number }): number {
25
- * return input.a + input.b;
26
- * }
27
- * }
28
- *
29
- * const tools = toLangChainTools({
30
- * controllers: [
31
- * typia.llm.controller<Calculator>("calculator", new Calculator()),
32
- * ],
33
- * });
34
- *
35
- * const llm = new ChatOpenAI({ model: "gpt-4" });
36
- * const agent = createToolCallingAgent({ llm, tools, prompt });
37
- * const executor = new AgentExecutor({ agent, tools });
38
- * await executor.invoke({ input: "What is 10 + 5?" });
39
- * ```;
40
- *
41
- * @param props Conversion properties
42
- * @returns Array of LangChain DynamicStructuredTool
43
- */
44
- export function toLangChainTools(props: {
45
- /**
46
- * List of controllers to convert to LangChain tools.
47
- *
48
- * - {@link ILlmController}: from `typia.llm.controller<Class>()`, converts all
49
- * methods of the class to tools
50
- * - {@link IHttpLlmController}: from `HttpLlm.controller()`, converts all
51
- * operations from OpenAPI document to tools
52
- */
53
- controllers: Array<ILlmController | IHttpLlmController>;
54
-
55
- /**
56
- * Whether to add controller name as prefix to tool names.
57
- *
58
- * If `true`, tool names become `{controllerName}_{methodName}`. If `false`,
59
- * tool names are just `{methodName}`.
60
- *
61
- * @default true
62
- */
63
- prefix?: boolean | undefined;
64
- }): DynamicStructuredTool[] {
65
- return LangChainToolsRegistrar.convert(props);
66
- }
1
+ import type { DynamicStructuredTool } from "@langchain/core/tools";
2
+ import { IHttpLlmController, ILlmController } from "@typia/interface";
3
+
4
+ import { LangChainToolsRegistrar } from "./internal/LangChainToolsRegistrar";
5
+
6
+ /**
7
+ * Convert typia controllers to LangChain tools.
8
+ *
9
+ * Converts TypeScript class methods via `typia.llm.controller<Class>()` or
10
+ * OpenAPI operations via `HttpLlm.controller()` to LangChain tools.
11
+ *
12
+ * Every tool call is validated by typia. If LLM provides invalid arguments,
13
+ * returns validation error formatted by {@link LlmJson.stringify} so that LLM
14
+ * can correct them automatically.
15
+ *
16
+ * @example
17
+ * ```typescript
18
+ * import { initChatModel } from "langchain/chat_models/universal";
19
+ * import typia from "typia";
20
+ * import { toLangChainTools } from "@typia/langchain";
21
+ *
22
+ * class Calculator {
23
+ * add(input: { a: number; b: number }): { value: number } {
24
+ * return { value: input.a + input.b };
25
+ * }
26
+ * }
27
+ *
28
+ * const tools = toLangChainTools({
29
+ * controllers: [
30
+ * typia.llm.controller<Calculator>("calculator", new Calculator()),
31
+ * ],
32
+ * });
33
+ *
34
+ * const llm = await initChatModel();
35
+ * const modelWithTools = llm.bindTools(tools);
36
+ * const result = await modelWithTools.invoke("What is 10 + 5?");
37
+ * ```;
38
+ *
39
+ * @param props Conversion properties
40
+ * @returns Array of LangChain DynamicStructuredTool
41
+ */
42
+ export function toLangChainTools(props: {
43
+ /**
44
+ * List of controllers to convert to LangChain tools.
45
+ *
46
+ * - {@link ILlmController}: from `typia.llm.controller<Class>()`, converts all
47
+ * methods of the class to tools
48
+ * - {@link IHttpLlmController}: from `HttpLlm.controller()`, converts all
49
+ * operations from OpenAPI document to tools
50
+ */
51
+ controllers: Array<ILlmController | IHttpLlmController>;
52
+
53
+ /**
54
+ * Whether to add controller name as prefix to tool names.
55
+ *
56
+ * If `true`, tool names become `{controllerName}_{methodName}`. If `false`,
57
+ * tool names are just `{methodName}`.
58
+ *
59
+ * @default false
60
+ */
61
+ prefix?: boolean | undefined;
62
+ }): DynamicStructuredTool[] {
63
+ return LangChainToolsRegistrar.convert(props);
64
+ }
@@ -1,139 +1,149 @@
1
- import { DynamicStructuredTool } from "@langchain/core/tools";
2
- import {
3
- IHttpLlmController,
4
- IHttpLlmFunction,
5
- ILlmController,
6
- ILlmFunction,
7
- IValidation,
8
- } from "@typia/interface";
9
- import { HttpLlm, LlmJson } from "@typia/utils";
10
- import { z } from "zod";
11
-
12
- export namespace LangChainToolsRegistrar {
13
- export const convert = (props: {
14
- controllers: Array<ILlmController | IHttpLlmController>;
15
- prefix?: boolean | undefined;
16
- }): DynamicStructuredTool[] => {
17
- const prefix: boolean = props.prefix ?? true;
18
- const tools: DynamicStructuredTool[] = [];
19
-
20
- for (const controller of props.controllers) {
21
- if (controller.protocol === "class") {
22
- convertClassController(tools, controller, prefix);
23
- } else {
24
- convertHttpController(tools, controller, prefix);
25
- }
26
- }
27
-
28
- return tools;
29
- };
30
-
31
- const convertClassController = (
32
- tools: DynamicStructuredTool[],
33
- controller: ILlmController,
34
- prefix: boolean,
35
- ): void => {
36
- const execute: Record<string, unknown> = controller.execute;
37
-
38
- for (const func of controller.application.functions) {
39
- const method: unknown = execute[func.name];
40
- if (typeof method !== "function") {
41
- throw new Error(
42
- `Method "${func.name}" not found on controller "${controller.name}"`,
43
- );
44
- }
45
-
46
- const toolName: string = prefix
47
- ? `${controller.name}_${func.name}`
48
- : func.name;
49
-
50
- tools.push(
51
- createTool({
52
- name: toolName,
53
- function: func,
54
- execute: async (args: unknown) => method.call(execute, args),
55
- }),
56
- );
57
- }
58
- };
59
-
60
- const convertHttpController = (
61
- tools: DynamicStructuredTool[],
62
- controller: IHttpLlmController,
63
- prefix: boolean,
64
- ): void => {
65
- const application = controller.application;
66
- const connection = controller.connection;
67
-
68
- for (const func of application.functions) {
69
- const toolName: string = prefix
70
- ? `${controller.name}_${func.name}`
71
- : func.name;
72
-
73
- tools.push(
74
- createTool({
75
- name: toolName,
76
- function: func,
77
- execute: async (args: unknown) => {
78
- if (controller.execute !== undefined) {
79
- const response = await controller.execute({
80
- connection,
81
- application,
82
- function: func,
83
- arguments: args as object,
84
- });
85
- return response.body;
86
- }
87
- return HttpLlm.execute({
88
- application,
89
- function: func,
90
- connection,
91
- input: args as object,
92
- });
93
- },
94
- }),
95
- );
96
- }
97
- };
98
-
99
- // Schema that accepts any object - bypasses LangChain's validation
100
- // so typia can handle all validation with proper error messages.
101
- // LangChain validates JSON Schema using @cfworker/json-schema which
102
- // throws ToolInputParsingException before reaching our func.
103
- const passthroughSchema = z.record(z.unknown());
104
-
105
- const createTool = (entry: {
106
- name: string;
107
- function: ILlmFunction | IHttpLlmFunction;
108
- execute: (args: unknown) => Promise<unknown>;
109
- }): DynamicStructuredTool => {
110
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
111
- return new DynamicStructuredTool<any>({
112
- name: entry.name,
113
- description: entry.function.description ?? "",
114
- schema: passthroughSchema,
115
- func: async (args: unknown): Promise<string> => {
116
- const coerced: unknown = LlmJson.coerce(
117
- args,
118
- entry.function.parameters,
119
- );
120
- const validation: IValidation<unknown> =
121
- entry.function.validate(coerced);
122
- if (!validation.success) {
123
- return LlmJson.stringify(validation);
124
- }
125
-
126
- try {
127
- const result: unknown = await entry.execute(validation.data);
128
- return result === undefined
129
- ? "Success"
130
- : JSON.stringify(result, null, 2);
131
- } catch (error) {
132
- return error instanceof Error
133
- ? `${error.name}: ${error.message}`
134
- : String(error);
135
- }
136
- },
137
- });
138
- };
139
- }
1
+ import {
2
+ DynamicStructuredTool,
3
+ ToolInputParsingException,
4
+ } from "@langchain/core/tools";
5
+ import {
6
+ IHttpLlmController,
7
+ IHttpLlmFunction,
8
+ ILlmController,
9
+ ILlmFunction,
10
+ IValidation,
11
+ } from "@typia/interface";
12
+ import { HttpLlm, LlmJson } from "@typia/utils";
13
+
14
+ export namespace LangChainToolsRegistrar {
15
+ export const convert = (props: {
16
+ controllers: Array<ILlmController | IHttpLlmController>;
17
+ prefix?: boolean | undefined;
18
+ }): DynamicStructuredTool[] => {
19
+ const prefix: boolean = props.prefix ?? false;
20
+ const tools: DynamicStructuredTool[] = [];
21
+
22
+ // check duplicate tool names
23
+ if (prefix === false && props.controllers.length >= 2) {
24
+ const names: Map<string, string> = new Map();
25
+ const duplicates: string[] = [];
26
+ for (const controller of props.controllers) {
27
+ for (const func of controller.application.functions) {
28
+ const existing: string | undefined = names.get(func.name);
29
+ if (existing !== undefined)
30
+ duplicates.push(
31
+ `"${func.name}" in "${controller.name}" (conflicts with "${existing}")`,
32
+ );
33
+ else names.set(func.name, controller.name);
34
+ }
35
+ }
36
+ if (duplicates.length > 0)
37
+ throw new Error(
38
+ `Duplicate tool names found:\n - ${duplicates.join("\n - ")}`,
39
+ );
40
+ }
41
+
42
+ // convert controllers to tools
43
+ for (const controller of props.controllers) {
44
+ if (controller.protocol === "class") {
45
+ convertClassController(tools, controller, prefix);
46
+ } else {
47
+ convertHttpController(tools, controller, prefix);
48
+ }
49
+ }
50
+
51
+ return tools;
52
+ };
53
+
54
+ const convertClassController = (
55
+ tools: DynamicStructuredTool[],
56
+ controller: ILlmController,
57
+ prefix: boolean,
58
+ ): void => {
59
+ const execute: Record<string, unknown> = controller.execute;
60
+
61
+ for (const func of controller.application.functions) {
62
+ const toolName: string = prefix
63
+ ? `${controller.name}_${func.name}`
64
+ : func.name;
65
+
66
+ const method: unknown = execute[func.name];
67
+ if (typeof method !== "function") {
68
+ throw new Error(
69
+ `Method "${func.name}" not found on controller "${controller.name}"`,
70
+ );
71
+ }
72
+
73
+ tools.push(
74
+ createTool({
75
+ name: toolName,
76
+ function: func,
77
+ execute: async (args: unknown) => method.call(execute, args),
78
+ }),
79
+ );
80
+ }
81
+ };
82
+
83
+ const convertHttpController = (
84
+ tools: DynamicStructuredTool[],
85
+ controller: IHttpLlmController,
86
+ prefix: boolean,
87
+ ): void => {
88
+ const application = controller.application;
89
+ const connection = controller.connection;
90
+
91
+ for (const func of application.functions) {
92
+ const toolName: string = prefix
93
+ ? `${controller.name}_${func.name}`
94
+ : func.name;
95
+
96
+ tools.push(
97
+ createTool({
98
+ name: toolName,
99
+ function: func,
100
+ execute: async (args: unknown) => {
101
+ if (controller.execute !== undefined) {
102
+ const response = await controller.execute({
103
+ connection,
104
+ application,
105
+ function: func,
106
+ arguments: args as object,
107
+ });
108
+ return response.body;
109
+ }
110
+ return HttpLlm.execute({
111
+ application,
112
+ function: func,
113
+ connection,
114
+ input: args as object,
115
+ });
116
+ },
117
+ }),
118
+ );
119
+ }
120
+ };
121
+
122
+ const createTool = (entry: {
123
+ name: string;
124
+ function: ILlmFunction | IHttpLlmFunction;
125
+ execute: (args: unknown) => Promise<unknown>;
126
+ }): DynamicStructuredTool =>
127
+ new DynamicStructuredTool<any>({
128
+ name: entry.name,
129
+ description: entry.function.description ?? "",
130
+ schema: entry.function.parameters,
131
+ func: async (args: unknown): Promise<unknown> => {
132
+ const coerced: unknown = LlmJson.coerce(
133
+ args,
134
+ entry.function.parameters,
135
+ );
136
+ const valid: IValidation<unknown> = entry.function.validate(coerced);
137
+ if (valid.success === false)
138
+ throw new ToolInputParsingException(
139
+ `Type errors in "${entry.name}" arguments:\n\n` +
140
+ `\`\`\`json\n${LlmJson.stringify(valid)}\n\`\`\``,
141
+ JSON.stringify(coerced),
142
+ );
143
+ const result: unknown = await entry.execute(valid.data);
144
+ return result === undefined
145
+ ? { success: true }
146
+ : { success: true, data: result };
147
+ },
148
+ });
149
+ }