@typia/langchain 13.1.1 → 13.1.19
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/internal/LangChainParameterConverter.d.ts +35 -0
- package/lib/internal/LangChainParameterConverter.js +52 -0
- package/lib/internal/LangChainParameterConverter.js.map +1 -0
- package/lib/internal/LangChainParameterConverter.mjs +19 -0
- package/lib/internal/LangChainParameterConverter.mjs.map +1 -0
- package/lib/internal/LangChainToolsRegistrar.js +18 -12
- package/lib/internal/LangChainToolsRegistrar.js.map +1 -1
- package/lib/internal/LangChainToolsRegistrar.mjs +12 -8
- package/lib/internal/LangChainToolsRegistrar.mjs.map +1 -1
- package/package.json +6 -5
- package/src/internal/LangChainParameterConverter.ts +53 -0
- package/src/internal/LangChainToolsRegistrar.ts +31 -12
|
@@ -0,0 +1,35 @@
|
|
|
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: `toJsonSchema(tool.schema)`
|
|
7
|
+
* turns it into the parameters the model is shown, and `StructuredTool.call`
|
|
8
|
+
* validates incoming arguments against it before the tool body runs. Handing
|
|
9
|
+
* over a bare `ILlmSchema.IParameters` fills the first role but forfeits the
|
|
10
|
+
* second — LangChain validates it with `@cfworker/json-schema`, which rejects
|
|
11
|
+
* what typia would have coerced and reports the failure in its own words, so
|
|
12
|
+
* typia's coercion and its correctable feedback both become unreachable.
|
|
13
|
+
*
|
|
14
|
+
* Standard JSON Schema separates the two roles. It carries a model-facing schema
|
|
15
|
+
* under `~standard.jsonSchema` and, by declaring no `~standard.validate`, no
|
|
16
|
+
* validator — which is exactly typia's position: show the model this schema, and
|
|
17
|
+
* leave validation to `LlmJson.validateArguments` in the tool body. LangChain
|
|
18
|
+
* reads the schema back through its own `toJsonSchema`, so the model sees the
|
|
19
|
+
* same parameters document as before, byte for byte.
|
|
20
|
+
*
|
|
21
|
+
* `@typia/vercel` states the same contract to the AI SDK through `jsonSchema()`,
|
|
22
|
+
* whose `validate` is likewise left undefined so that `safeValidateTypes`
|
|
23
|
+
* short-circuits its own validation.
|
|
24
|
+
*
|
|
25
|
+
* Two version floors hold this together, and both are pinned in the package
|
|
26
|
+
* manifest. `toJsonSchema` only reads `~standard.jsonSchema` from
|
|
27
|
+
* `@langchain/core@1.1.30` onwards — older versions return the wrapper itself and
|
|
28
|
+
* would advertise *that* to the model in place of the parameters — and
|
|
29
|
+
* `StandardJSONSchemaV1` only exists from `@standard-schema/spec@1.1.0`.
|
|
30
|
+
*
|
|
31
|
+
* @see https://github.com/standard-schema/standard-schema
|
|
32
|
+
*/
|
|
33
|
+
export declare namespace LangChainParameterConverter {
|
|
34
|
+
const convert: (func: ILlmFunction | IHttpLlmFunction) => StandardJSONSchemaV1;
|
|
35
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
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: `toJsonSchema(tool.schema)`
|
|
8
|
+
* turns it into the parameters the model is shown, and `StructuredTool.call`
|
|
9
|
+
* validates incoming arguments against it before the tool body runs. Handing
|
|
10
|
+
* over a bare `ILlmSchema.IParameters` fills the first role but forfeits the
|
|
11
|
+
* second — LangChain validates it with `@cfworker/json-schema`, which rejects
|
|
12
|
+
* what typia would have coerced and reports the failure in its own words, so
|
|
13
|
+
* typia's coercion and its correctable feedback both become unreachable.
|
|
14
|
+
*
|
|
15
|
+
* Standard JSON Schema separates the two roles. It carries a model-facing schema
|
|
16
|
+
* under `~standard.jsonSchema` and, by declaring no `~standard.validate`, no
|
|
17
|
+
* validator — which is exactly typia's position: show the model this schema, and
|
|
18
|
+
* leave validation to `LlmJson.validateArguments` in the tool body. LangChain
|
|
19
|
+
* reads the schema back through its own `toJsonSchema`, so the model sees the
|
|
20
|
+
* same parameters document as before, byte for byte.
|
|
21
|
+
*
|
|
22
|
+
* `@typia/vercel` states the same contract to the AI SDK through `jsonSchema()`,
|
|
23
|
+
* whose `validate` is likewise left undefined so that `safeValidateTypes`
|
|
24
|
+
* short-circuits its own validation.
|
|
25
|
+
*
|
|
26
|
+
* Two version floors hold this together, and both are pinned in the package
|
|
27
|
+
* manifest. `toJsonSchema` only reads `~standard.jsonSchema` from
|
|
28
|
+
* `@langchain/core@1.1.30` onwards — older versions return the wrapper itself and
|
|
29
|
+
* would advertise *that* to the model in place of the parameters — and
|
|
30
|
+
* `StandardJSONSchemaV1` only exists from `@standard-schema/spec@1.1.0`.
|
|
31
|
+
*
|
|
32
|
+
* @see https://github.com/standard-schema/standard-schema
|
|
33
|
+
*/
|
|
34
|
+
var LangChainParameterConverter;
|
|
35
|
+
(function (LangChainParameterConverter) {
|
|
36
|
+
LangChainParameterConverter.convert = (func) => {
|
|
37
|
+
// Coercion never changes the shape a value must end up in, so the input and
|
|
38
|
+
// output schemas are the same document.
|
|
39
|
+
const jsonSchema = () => func.parameters;
|
|
40
|
+
return {
|
|
41
|
+
"~standard": {
|
|
42
|
+
version: 1,
|
|
43
|
+
vendor: "typia",
|
|
44
|
+
jsonSchema: {
|
|
45
|
+
input: jsonSchema,
|
|
46
|
+
output: jsonSchema,
|
|
47
|
+
},
|
|
48
|
+
},
|
|
49
|
+
};
|
|
50
|
+
};
|
|
51
|
+
})(LangChainParameterConverter || (exports.LangChainParameterConverter = LangChainParameterConverter = {}));
|
|
52
|
+
//# sourceMappingURL=LangChainParameterConverter.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"LangChainParameterConverter.js","sourceRoot":"","sources":["../../src/internal/LangChainParameterConverter.ts"],"names":[],"mappings":";;;AAGA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;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: `toJsonSchema(tool.schema)`\n * turns it into the parameters the model is shown, and `StructuredTool.call`\n * validates incoming arguments against it before the tool body runs. Handing\n * over a bare `ILlmSchema.IParameters` fills the first role but forfeits the\n * second — LangChain validates it with `@cfworker/json-schema`, which rejects\n * what typia would have coerced and reports the failure in its own words, so\n * typia's coercion and its correctable feedback both become unreachable.\n *\n * Standard JSON Schema separates the two roles. It carries a model-facing schema\n * under `~standard.jsonSchema` and, by declaring no `~standard.validate`, no\n * validator — which is exactly typia's position: show the model this schema, and\n * leave validation to `LlmJson.validateArguments` in the tool body. LangChain\n * reads the schema back through its own `toJsonSchema`, so the model sees the\n * same parameters document as before, byte for byte.\n *\n * `@typia/vercel` states the same contract to the AI SDK through `jsonSchema()`,\n * whose `validate` is likewise left undefined so that `safeValidateTypes`\n * 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 and\n * 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":";AAiCO,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 (
|
|
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
|
|
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(`"${
|
|
32
|
+
duplicates.push(`"${toolName}" from ${origin} (conflicts with ${existing})`);
|
|
30
33
|
else
|
|
31
|
-
names.set(
|
|
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}"`);
|
|
@@ -66,9 +67,7 @@ var LangChainToolsRegistrar;
|
|
|
66
67
|
const application = controller.application;
|
|
67
68
|
const connection = controller.connection;
|
|
68
69
|
for (const func of application.functions) {
|
|
69
|
-
const toolName = prefix
|
|
70
|
-
? `${controller.name}_${func.name}`
|
|
71
|
-
: func.name;
|
|
70
|
+
const toolName = getToolName(controller.name, func.name, prefix);
|
|
72
71
|
tools.push(createTool({
|
|
73
72
|
name: toolName,
|
|
74
73
|
function: func,
|
|
@@ -96,7 +95,7 @@ var LangChainToolsRegistrar;
|
|
|
96
95
|
const valid = utils_1.LlmJson.validateArguments(entry.function, args !== null && args !== void 0 ? args : {});
|
|
97
96
|
if (valid.success === false)
|
|
98
97
|
throw new tools_1.ToolInputParsingException(`Type errors in "${entry.name}" arguments:\n\n` +
|
|
99
|
-
|
|
98
|
+
utils_1.LlmJson.stringify(valid), JSON.stringify(args !== null && args !== void 0 ? args : {}));
|
|
100
99
|
try {
|
|
101
100
|
const result = yield entry.execute(valid.data);
|
|
102
101
|
if (result === undefined) {
|
|
@@ -120,7 +119,14 @@ var LangChainToolsRegistrar;
|
|
|
120
119
|
}), {
|
|
121
120
|
name: entry.name,
|
|
122
121
|
description: (_a = entry.function.description) !== null && _a !== void 0 ? _a : "",
|
|
123
|
-
schema
|
|
122
|
+
// Declares the model-facing schema without a validator, so the
|
|
123
|
+
// coerce-and-validate step above is the only one that runs; see
|
|
124
|
+
// `LangChainParameterConverter`. The cast is needed because `tool()`
|
|
125
|
+
// still types `schema` as Zod-or-JSON-Schema, even though the
|
|
126
|
+
// `toJsonSchema` it reads that schema back with accepts Standard JSON
|
|
127
|
+
// Schema by its own public signature.
|
|
128
|
+
schema: LangChainParameterConverter_1.LangChainParameterConverter.convert(entry.function),
|
|
124
129
|
}); };
|
|
130
|
+
const getToolName = (controllerName, functionName, prefix) => (prefix ? `${controllerName}_${functionName}` : functionName);
|
|
125
131
|
})(LangChainToolsRegistrar || (exports.LangChainToolsRegistrar = LangChainToolsRegistrar = {}));
|
|
126
132
|
//# sourceMappingURL=LangChainToolsRegistrar.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"LangChainToolsRegistrar.js","sourceRoot":"","sources":["../../src/internal/LangChainToolsRegistrar.ts"],"names":[],"mappings":";;;;;;;;;;;;AAAA,iDAI+B;
|
|
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 (
|
|
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
|
|
14
|
-
|
|
15
|
-
|
|
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,7 +26,7 @@ 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 =
|
|
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({
|
|
@@ -37,7 +40,7 @@ let LangChainToolsRegistrar;
|
|
|
37
40
|
const application = controller.application;
|
|
38
41
|
const connection = controller.connection;
|
|
39
42
|
for (const func of application.functions) {
|
|
40
|
-
const toolName =
|
|
43
|
+
const toolName = getToolName(controller.name, func.name, prefix);
|
|
41
44
|
tools.push(createTool({
|
|
42
45
|
name: toolName,
|
|
43
46
|
function: func,
|
|
@@ -60,7 +63,7 @@ let LangChainToolsRegistrar;
|
|
|
60
63
|
};
|
|
61
64
|
const createTool = (entry) => tool(async (args) => {
|
|
62
65
|
const valid = LlmJson.validateArguments(entry.function, args ?? {});
|
|
63
|
-
if (valid.success === false) throw new ToolInputParsingException(`Type errors in "${entry.name}" arguments:\n\n
|
|
66
|
+
if (valid.success === false) throw new ToolInputParsingException(`Type errors in "${entry.name}" arguments:\n\n` + LlmJson.stringify(valid), JSON.stringify(args ?? {}));
|
|
64
67
|
try {
|
|
65
68
|
const result = await entry.execute(valid.data);
|
|
66
69
|
if (result === void 0) return entry.function.output === void 0 ? { success: true } : {
|
|
@@ -80,8 +83,9 @@ let LangChainToolsRegistrar;
|
|
|
80
83
|
}, {
|
|
81
84
|
name: entry.name,
|
|
82
85
|
description: entry.function.description ?? "",
|
|
83
|
-
schema: entry.function
|
|
86
|
+
schema: LangChainParameterConverter.convert(entry.function)
|
|
84
87
|
});
|
|
88
|
+
const getToolName = (controllerName, functionName, prefix) => prefix ? `${controllerName}_${functionName}` : functionName;
|
|
85
89
|
})(LangChainToolsRegistrar || (LangChainToolsRegistrar = {}));
|
|
86
90
|
//#endregion
|
|
87
91
|
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 (
|
|
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 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 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 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 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 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 // 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 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,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@typia/langchain",
|
|
3
|
-
"version": "13.1.
|
|
3
|
+
"version": "13.1.19",
|
|
4
4
|
"description": "LangChain.js integration for typia",
|
|
5
5
|
"main": "lib/index.js",
|
|
6
6
|
"exports": {
|
|
@@ -22,18 +22,19 @@
|
|
|
22
22
|
},
|
|
23
23
|
"homepage": "https://typia.io",
|
|
24
24
|
"dependencies": {
|
|
25
|
-
"@
|
|
26
|
-
"@typia/interface": "^13.1.
|
|
25
|
+
"@standard-schema/spec": "^1.1.0",
|
|
26
|
+
"@typia/interface": "^13.1.19",
|
|
27
|
+
"@typia/utils": "^13.1.19"
|
|
27
28
|
},
|
|
28
29
|
"peerDependencies": {
|
|
29
|
-
"@langchain/core": ">=1.
|
|
30
|
+
"@langchain/core": ">=1.1.30"
|
|
30
31
|
},
|
|
31
32
|
"devDependencies": {
|
|
32
33
|
"@langchain/core": "^1.1.31",
|
|
33
34
|
"rimraf": "^6.1.2",
|
|
34
35
|
"rolldown": "^1.1.5",
|
|
35
36
|
"tinyglobby": "^0.2.12",
|
|
36
|
-
"ttsc": "^0.
|
|
37
|
+
"ttsc": "^0.19.2",
|
|
37
38
|
"typescript": "^7.0.2"
|
|
38
39
|
},
|
|
39
40
|
"sideEffects": false,
|
|
@@ -0,0 +1,53 @@
|
|
|
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: `toJsonSchema(tool.schema)`
|
|
8
|
+
* turns it into the parameters the model is shown, and `StructuredTool.call`
|
|
9
|
+
* validates incoming arguments against it before the tool body runs. Handing
|
|
10
|
+
* over a bare `ILlmSchema.IParameters` fills the first role but forfeits the
|
|
11
|
+
* second — LangChain validates it with `@cfworker/json-schema`, which rejects
|
|
12
|
+
* what typia would have coerced and reports the failure in its own words, so
|
|
13
|
+
* typia's coercion and its correctable feedback both become unreachable.
|
|
14
|
+
*
|
|
15
|
+
* Standard JSON Schema separates the two roles. It carries a model-facing schema
|
|
16
|
+
* under `~standard.jsonSchema` and, by declaring no `~standard.validate`, no
|
|
17
|
+
* validator — which is exactly typia's position: show the model this schema, and
|
|
18
|
+
* leave validation to `LlmJson.validateArguments` in the tool body. LangChain
|
|
19
|
+
* reads the schema back through its own `toJsonSchema`, so the model sees the
|
|
20
|
+
* same parameters document as before, byte for byte.
|
|
21
|
+
*
|
|
22
|
+
* `@typia/vercel` states the same contract to the AI SDK through `jsonSchema()`,
|
|
23
|
+
* whose `validate` is likewise left undefined so that `safeValidateTypes`
|
|
24
|
+
* short-circuits its own validation.
|
|
25
|
+
*
|
|
26
|
+
* Two version floors hold this together, and both are pinned in the package
|
|
27
|
+
* manifest. `toJsonSchema` only reads `~standard.jsonSchema` from
|
|
28
|
+
* `@langchain/core@1.1.30` onwards — older versions return the wrapper itself and
|
|
29
|
+
* would advertise *that* to the model in place of the parameters — and
|
|
30
|
+
* `StandardJSONSchemaV1` only exists from `@standard-schema/spec@1.1.0`.
|
|
31
|
+
*
|
|
32
|
+
* @see https://github.com/standard-schema/standard-schema
|
|
33
|
+
*/
|
|
34
|
+
export namespace LangChainParameterConverter {
|
|
35
|
+
export const convert = (
|
|
36
|
+
func: ILlmFunction | IHttpLlmFunction,
|
|
37
|
+
): StandardJSONSchemaV1 => {
|
|
38
|
+
// Coercion never changes the shape a value must end up in, so the input and
|
|
39
|
+
// output schemas are the same document.
|
|
40
|
+
const jsonSchema = (): Record<string, unknown> =>
|
|
41
|
+
func.parameters as unknown as Record<string, unknown>;
|
|
42
|
+
return {
|
|
43
|
+
"~standard": {
|
|
44
|
+
version: 1,
|
|
45
|
+
vendor: "typia",
|
|
46
|
+
jsonSchema: {
|
|
47
|
+
input: jsonSchema,
|
|
48
|
+
output: jsonSchema,
|
|
49
|
+
},
|
|
50
|
+
},
|
|
51
|
+
};
|
|
52
|
+
};
|
|
53
|
+
}
|
|
@@ -3,6 +3,7 @@ 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,
|
|
@@ -12,6 +13,8 @@ import {
|
|
|
12
13
|
} from "@typia/interface";
|
|
13
14
|
import { HttpLlm, LlmJson } from "@typia/utils";
|
|
14
15
|
|
|
16
|
+
import { LangChainParameterConverter } from "./LangChainParameterConverter";
|
|
17
|
+
|
|
15
18
|
export namespace LangChainToolsRegistrar {
|
|
16
19
|
export const convert = (props: {
|
|
17
20
|
controllers: Array<ILlmController | IHttpLlmController>;
|
|
@@ -21,17 +24,23 @@ export namespace LangChainToolsRegistrar {
|
|
|
21
24
|
const tools: DynamicStructuredTool[] = [];
|
|
22
25
|
|
|
23
26
|
// check duplicate tool names
|
|
24
|
-
if (
|
|
27
|
+
if (props.controllers.length >= 2) {
|
|
25
28
|
const names: Map<string, string> = new Map();
|
|
26
29
|
const duplicates: string[] = [];
|
|
27
30
|
for (const controller of props.controllers) {
|
|
28
31
|
for (const func of controller.application.functions) {
|
|
29
|
-
const
|
|
32
|
+
const toolName: string = getToolName(
|
|
33
|
+
controller.name,
|
|
34
|
+
func.name,
|
|
35
|
+
prefix,
|
|
36
|
+
);
|
|
37
|
+
const existing: string | undefined = names.get(toolName);
|
|
38
|
+
const origin: string = `${controller.protocol} controller "${controller.name}" function "${func.name}"`;
|
|
30
39
|
if (existing !== undefined)
|
|
31
40
|
duplicates.push(
|
|
32
|
-
`"${
|
|
41
|
+
`"${toolName}" from ${origin} (conflicts with ${existing})`,
|
|
33
42
|
);
|
|
34
|
-
else names.set(
|
|
43
|
+
else names.set(toolName, origin);
|
|
35
44
|
}
|
|
36
45
|
}
|
|
37
46
|
if (duplicates.length > 0)
|
|
@@ -60,9 +69,7 @@ export namespace LangChainToolsRegistrar {
|
|
|
60
69
|
const execute: Record<string, unknown> = controller.execute;
|
|
61
70
|
|
|
62
71
|
for (const func of controller.application.functions) {
|
|
63
|
-
const toolName: string = prefix
|
|
64
|
-
? `${controller.name}_${func.name}`
|
|
65
|
-
: func.name;
|
|
72
|
+
const toolName: string = getToolName(controller.name, func.name, prefix);
|
|
66
73
|
|
|
67
74
|
const method: unknown = execute[func.name];
|
|
68
75
|
if (typeof method !== "function") {
|
|
@@ -90,9 +97,7 @@ export namespace LangChainToolsRegistrar {
|
|
|
90
97
|
const connection = controller.connection;
|
|
91
98
|
|
|
92
99
|
for (const func of application.functions) {
|
|
93
|
-
const toolName: string = prefix
|
|
94
|
-
? `${controller.name}_${func.name}`
|
|
95
|
-
: func.name;
|
|
100
|
+
const toolName: string = getToolName(controller.name, func.name, prefix);
|
|
96
101
|
|
|
97
102
|
tools.push(
|
|
98
103
|
createTool({
|
|
@@ -134,7 +139,7 @@ export namespace LangChainToolsRegistrar {
|
|
|
134
139
|
if (valid.success === false)
|
|
135
140
|
throw new ToolInputParsingException(
|
|
136
141
|
`Type errors in "${entry.name}" arguments:\n\n` +
|
|
137
|
-
|
|
142
|
+
LlmJson.stringify(valid),
|
|
138
143
|
JSON.stringify(args ?? {}),
|
|
139
144
|
);
|
|
140
145
|
try {
|
|
@@ -161,9 +166,23 @@ export namespace LangChainToolsRegistrar {
|
|
|
161
166
|
{
|
|
162
167
|
name: entry.name,
|
|
163
168
|
description: entry.function.description ?? "",
|
|
164
|
-
schema
|
|
169
|
+
// Declares the model-facing schema without a validator, so the
|
|
170
|
+
// coerce-and-validate step above is the only one that runs; see
|
|
171
|
+
// `LangChainParameterConverter`. The cast is needed because `tool()`
|
|
172
|
+
// still types `schema` as Zod-or-JSON-Schema, even though the
|
|
173
|
+
// `toJsonSchema` it reads that schema back with accepts Standard JSON
|
|
174
|
+
// Schema by its own public signature.
|
|
175
|
+
schema: LangChainParameterConverter.convert(
|
|
176
|
+
entry.function,
|
|
177
|
+
) as JSONSchema,
|
|
165
178
|
},
|
|
166
179
|
);
|
|
180
|
+
|
|
181
|
+
const getToolName = (
|
|
182
|
+
controllerName: string,
|
|
183
|
+
functionName: string,
|
|
184
|
+
prefix: boolean,
|
|
185
|
+
): string => (prefix ? `${controllerName}_${functionName}` : functionName);
|
|
167
186
|
}
|
|
168
187
|
|
|
169
188
|
type ITryResult =
|