langchain 0.2.6 → 0.2.8

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.
@@ -2,6 +2,7 @@ import type { StructuredToolInterface } from "@langchain/core/tools";
2
2
  import type { BaseChatModel, BaseChatModelCallOptions } from "@langchain/core/language_models/chat_models";
3
3
  import { ChatPromptTemplate } from "@langchain/core/prompts";
4
4
  import { OpenAIClient } from "@langchain/openai";
5
+ import { ToolDefinition } from "@langchain/core/language_models/base";
5
6
  import { OpenAIToolsAgentOutputParser, type ToolsAgentStep } from "./output_parser.js";
6
7
  import { AgentRunnableSequence } from "../agent.js";
7
8
  export { OpenAIToolsAgentOutputParser, type ToolsAgentStep };
@@ -18,7 +19,7 @@ export type CreateOpenAIToolsAgentParams = {
18
19
  tools?: StructuredToolInterface[] | OpenAIClient.ChatCompletionTool[] | any[];
19
20
  }>;
20
21
  /** Tools this agent has access to. */
21
- tools: StructuredToolInterface[];
22
+ tools: StructuredToolInterface[] | ToolDefinition[];
22
23
  /** The prompt to use, must have an input key of `agent_scratchpad`. */
23
24
  prompt: ChatPromptTemplate;
24
25
  /**
@@ -2,8 +2,10 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.createStructuredChatAgent = exports.StructuredChatAgent = void 0;
4
4
  const zod_to_json_schema_1 = require("zod-to-json-schema");
5
+ const base_1 = require("@langchain/core/language_models/base");
5
6
  const runnables_1 = require("@langchain/core/runnables");
6
7
  const prompts_1 = require("@langchain/core/prompts");
8
+ const function_calling_1 = require("@langchain/core/utils/function_calling");
7
9
  const llm_chain_js_1 = require("../../chains/llm_chain.cjs");
8
10
  const agent_js_1 = require("../agent.cjs");
9
11
  const outputParser_js_1 = require("./outputParser.cjs");
@@ -215,7 +217,16 @@ async function createStructuredChatAgent({ llm, tools, prompt, streamRunnable, }
215
217
  if (missingVariables.length > 0) {
216
218
  throw new Error(`Provided prompt is missing required input variables: ${JSON.stringify(missingVariables)}`);
217
219
  }
218
- const toolNames = tools.map((tool) => tool.name);
220
+ let toolNames = [];
221
+ if (tools.every(base_1.isOpenAITool)) {
222
+ toolNames = tools.map((tool) => tool.function.name);
223
+ }
224
+ else if (tools.every(function_calling_1.isStructuredTool)) {
225
+ toolNames = tools.map((tool) => tool.name);
226
+ }
227
+ else {
228
+ throw new Error("All tools must be either OpenAI or Structured tools, not a mix.");
229
+ }
219
230
  const partialedPrompt = await prompt.partial({
220
231
  tools: (0, render_js_1.renderTextDescriptionAndArgs)(tools),
221
232
  tool_names: toolNames.join(", "),
@@ -1,5 +1,5 @@
1
1
  import type { StructuredToolInterface } from "@langchain/core/tools";
2
- import type { BaseLanguageModelInterface } from "@langchain/core/language_models/base";
2
+ import { type BaseLanguageModelInterface, type ToolDefinition } from "@langchain/core/language_models/base";
3
3
  import type { BasePromptTemplate } from "@langchain/core/prompts";
4
4
  import { BaseMessagePromptTemplate, ChatPromptTemplate } from "@langchain/core/prompts";
5
5
  import { AgentStep } from "@langchain/core/agents";
@@ -99,7 +99,7 @@ export type CreateStructuredChatAgentParams = {
99
99
  /** LLM to use as the agent. */
100
100
  llm: BaseLanguageModelInterface;
101
101
  /** Tools this agent has access to. */
102
- tools: StructuredToolInterface[];
102
+ tools: (StructuredToolInterface | ToolDefinition)[];
103
103
  /**
104
104
  * The prompt to use. Must have input keys for
105
105
  * `tools`, `tool_names`, and `agent_scratchpad`.
@@ -1,6 +1,8 @@
1
1
  import { zodToJsonSchema } from "zod-to-json-schema";
2
+ import { isOpenAITool, } from "@langchain/core/language_models/base";
2
3
  import { RunnablePassthrough } from "@langchain/core/runnables";
3
4
  import { ChatPromptTemplate, HumanMessagePromptTemplate, SystemMessagePromptTemplate, PromptTemplate, } from "@langchain/core/prompts";
5
+ import { isStructuredTool } from "@langchain/core/utils/function_calling";
4
6
  import { LLMChain } from "../../chains/llm_chain.js";
5
7
  import { Agent, AgentRunnableSequence, } from "../agent.js";
6
8
  import { StructuredChatOutputParserWithRetries } from "./outputParser.js";
@@ -211,7 +213,16 @@ export async function createStructuredChatAgent({ llm, tools, prompt, streamRunn
211
213
  if (missingVariables.length > 0) {
212
214
  throw new Error(`Provided prompt is missing required input variables: ${JSON.stringify(missingVariables)}`);
213
215
  }
214
- const toolNames = tools.map((tool) => tool.name);
216
+ let toolNames = [];
217
+ if (tools.every(isOpenAITool)) {
218
+ toolNames = tools.map((tool) => tool.function.name);
219
+ }
220
+ else if (tools.every(isStructuredTool)) {
221
+ toolNames = tools.map((tool) => tool.name);
222
+ }
223
+ else {
224
+ throw new Error("All tools must be either OpenAI or Structured tools, not a mix.");
225
+ }
215
226
  const partialedPrompt = await prompt.partial({
216
227
  tools: renderTextDescriptionAndArgs(tools),
217
228
  tool_names: toolNames.join(", "),
@@ -1,6 +1,7 @@
1
1
  import { BaseChatModel } from "@langchain/core/language_models/chat_models";
2
2
  import { ChatPromptTemplate } from "@langchain/core/prompts";
3
3
  import { StructuredToolInterface } from "@langchain/core/tools";
4
+ import { ToolDefinition } from "@langchain/core/language_models/base";
4
5
  import { AgentRunnableSequence } from "../agent.js";
5
6
  import { ToolsAgentStep } from "./output_parser.js";
6
7
  /**
@@ -14,7 +15,7 @@ export type CreateToolCallingAgentParams = {
14
15
  */
15
16
  llm: BaseChatModel;
16
17
  /** Tools this agent has access to. */
17
- tools: StructuredToolInterface[];
18
+ tools: StructuredToolInterface[] | ToolDefinition[];
18
19
  /** The prompt to use, must have an input key of `agent_scratchpad`. */
19
20
  prompt: ChatPromptTemplate;
20
21
  /**
@@ -0,0 +1,98 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.MultiFileLoader = void 0;
4
+ const node_path_1 = require("node:path");
5
+ const promises_1 = require("node:fs/promises");
6
+ const base_js_1 = require("../base.cjs");
7
+ const directory_js_1 = require("./directory.cjs");
8
+ /**
9
+ * A document loader that loads documents from multiple files. It extends the
10
+ * `BaseDocumentLoader` class and implements the `load()` method.
11
+ * @example
12
+ * ```typescript
13
+ *
14
+ * const multiFileLoader = new MultiFileLoader(
15
+ * ["path/to/file1.pdf", "path/to/file2.txt"],
16
+ * {
17
+ * ".pdf": (path: string) => new PDFLoader(path),
18
+ * },
19
+ * );
20
+ *
21
+ * const docs = await multiFileLoader.load();
22
+ * console.log({ docs });
23
+ *
24
+ * ```
25
+ */
26
+ class MultiFileLoader extends base_js_1.BaseDocumentLoader {
27
+ constructor(filePaths, loaders, unknown = directory_js_1.UnknownHandling.Warn) {
28
+ super();
29
+ Object.defineProperty(this, "filePaths", {
30
+ enumerable: true,
31
+ configurable: true,
32
+ writable: true,
33
+ value: filePaths
34
+ });
35
+ Object.defineProperty(this, "loaders", {
36
+ enumerable: true,
37
+ configurable: true,
38
+ writable: true,
39
+ value: loaders
40
+ });
41
+ Object.defineProperty(this, "unknown", {
42
+ enumerable: true,
43
+ configurable: true,
44
+ writable: true,
45
+ value: unknown
46
+ });
47
+ if (Object.keys(loaders).length === 0) {
48
+ throw new Error("Must provide at least one loader");
49
+ }
50
+ for (const extension in loaders) {
51
+ if (Object.hasOwn(loaders, extension)) {
52
+ if (extension[0] !== ".") {
53
+ throw new Error(`Extension must start with a dot: ${extension}`);
54
+ }
55
+ }
56
+ }
57
+ }
58
+ /**
59
+ * Loads the documents from the provided file paths. It checks if the file
60
+ * is a directory and ignores it. If a file is a file, it checks if there
61
+ * is a corresponding loader function for the file extension in the `loaders`
62
+ * mapping. If there is, it loads the documents. If there is no
63
+ * corresponding loader function and `unknown` is set to `Warn`, it logs a
64
+ * warning message. If `unknown` is set to `Error`, it throws an error.
65
+ * @returns A promise that resolves to an array of loaded documents.
66
+ */
67
+ async load() {
68
+ const documents = [];
69
+ for (const filePath of this.filePaths) {
70
+ const fullPath = (0, node_path_1.resolve)(filePath);
71
+ const fileStat = await (0, promises_1.stat)(fullPath);
72
+ if (fileStat.isDirectory()) {
73
+ console.warn(`Ignoring directory: ${fullPath}`);
74
+ continue;
75
+ }
76
+ const loaderFactory = this.loaders[(0, node_path_1.extname)(fullPath)];
77
+ if (loaderFactory) {
78
+ const loader = loaderFactory(fullPath);
79
+ documents.push(...(await loader.load()));
80
+ }
81
+ else {
82
+ switch (this.unknown) {
83
+ case directory_js_1.UnknownHandling.Ignore:
84
+ break;
85
+ case directory_js_1.UnknownHandling.Warn:
86
+ console.warn(`Unknown file type: ${fullPath}`);
87
+ break;
88
+ case directory_js_1.UnknownHandling.Error:
89
+ throw new Error(`Unknown file type: ${fullPath}`);
90
+ default:
91
+ throw new Error(`Unknown unknown handling: ${this.unknown}`);
92
+ }
93
+ }
94
+ }
95
+ return documents;
96
+ }
97
+ }
98
+ exports.MultiFileLoader = MultiFileLoader;
@@ -0,0 +1,37 @@
1
+ import { Document } from "@langchain/core/documents";
2
+ import { BaseDocumentLoader } from "../base.js";
3
+ import { type LoadersMapping, UnknownHandling } from "./directory.js";
4
+ /**
5
+ * A document loader that loads documents from multiple files. It extends the
6
+ * `BaseDocumentLoader` class and implements the `load()` method.
7
+ * @example
8
+ * ```typescript
9
+ *
10
+ * const multiFileLoader = new MultiFileLoader(
11
+ * ["path/to/file1.pdf", "path/to/file2.txt"],
12
+ * {
13
+ * ".pdf": (path: string) => new PDFLoader(path),
14
+ * },
15
+ * );
16
+ *
17
+ * const docs = await multiFileLoader.load();
18
+ * console.log({ docs });
19
+ *
20
+ * ```
21
+ */
22
+ export declare class MultiFileLoader extends BaseDocumentLoader {
23
+ filePaths: string[];
24
+ loaders: LoadersMapping;
25
+ unknown: UnknownHandling;
26
+ constructor(filePaths: string[], loaders: LoadersMapping, unknown?: UnknownHandling);
27
+ /**
28
+ * Loads the documents from the provided file paths. It checks if the file
29
+ * is a directory and ignores it. If a file is a file, it checks if there
30
+ * is a corresponding loader function for the file extension in the `loaders`
31
+ * mapping. If there is, it loads the documents. If there is no
32
+ * corresponding loader function and `unknown` is set to `Warn`, it logs a
33
+ * warning message. If `unknown` is set to `Error`, it throws an error.
34
+ * @returns A promise that resolves to an array of loaded documents.
35
+ */
36
+ load(): Promise<Document[]>;
37
+ }
@@ -0,0 +1,94 @@
1
+ import { extname, resolve } from "node:path";
2
+ import { stat } from "node:fs/promises";
3
+ import { BaseDocumentLoader } from "../base.js";
4
+ import { UnknownHandling } from "./directory.js";
5
+ /**
6
+ * A document loader that loads documents from multiple files. It extends the
7
+ * `BaseDocumentLoader` class and implements the `load()` method.
8
+ * @example
9
+ * ```typescript
10
+ *
11
+ * const multiFileLoader = new MultiFileLoader(
12
+ * ["path/to/file1.pdf", "path/to/file2.txt"],
13
+ * {
14
+ * ".pdf": (path: string) => new PDFLoader(path),
15
+ * },
16
+ * );
17
+ *
18
+ * const docs = await multiFileLoader.load();
19
+ * console.log({ docs });
20
+ *
21
+ * ```
22
+ */
23
+ export class MultiFileLoader extends BaseDocumentLoader {
24
+ constructor(filePaths, loaders, unknown = UnknownHandling.Warn) {
25
+ super();
26
+ Object.defineProperty(this, "filePaths", {
27
+ enumerable: true,
28
+ configurable: true,
29
+ writable: true,
30
+ value: filePaths
31
+ });
32
+ Object.defineProperty(this, "loaders", {
33
+ enumerable: true,
34
+ configurable: true,
35
+ writable: true,
36
+ value: loaders
37
+ });
38
+ Object.defineProperty(this, "unknown", {
39
+ enumerable: true,
40
+ configurable: true,
41
+ writable: true,
42
+ value: unknown
43
+ });
44
+ if (Object.keys(loaders).length === 0) {
45
+ throw new Error("Must provide at least one loader");
46
+ }
47
+ for (const extension in loaders) {
48
+ if (Object.hasOwn(loaders, extension)) {
49
+ if (extension[0] !== ".") {
50
+ throw new Error(`Extension must start with a dot: ${extension}`);
51
+ }
52
+ }
53
+ }
54
+ }
55
+ /**
56
+ * Loads the documents from the provided file paths. It checks if the file
57
+ * is a directory and ignores it. If a file is a file, it checks if there
58
+ * is a corresponding loader function for the file extension in the `loaders`
59
+ * mapping. If there is, it loads the documents. If there is no
60
+ * corresponding loader function and `unknown` is set to `Warn`, it logs a
61
+ * warning message. If `unknown` is set to `Error`, it throws an error.
62
+ * @returns A promise that resolves to an array of loaded documents.
63
+ */
64
+ async load() {
65
+ const documents = [];
66
+ for (const filePath of this.filePaths) {
67
+ const fullPath = resolve(filePath);
68
+ const fileStat = await stat(fullPath);
69
+ if (fileStat.isDirectory()) {
70
+ console.warn(`Ignoring directory: ${fullPath}`);
71
+ continue;
72
+ }
73
+ const loaderFactory = this.loaders[extname(fullPath)];
74
+ if (loaderFactory) {
75
+ const loader = loaderFactory(fullPath);
76
+ documents.push(...(await loader.load()));
77
+ }
78
+ else {
79
+ switch (this.unknown) {
80
+ case UnknownHandling.Ignore:
81
+ break;
82
+ case UnknownHandling.Warn:
83
+ console.warn(`Unknown file type: ${fullPath}`);
84
+ break;
85
+ case UnknownHandling.Error:
86
+ throw new Error(`Unknown file type: ${fullPath}`);
87
+ default:
88
+ throw new Error(`Unknown unknown handling: ${this.unknown}`);
89
+ }
90
+ }
91
+ }
92
+ return documents;
93
+ }
94
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,49 @@
1
+ import * as url from "node:url";
2
+ import * as path from "node:path";
3
+ import { test, expect } from "@jest/globals";
4
+ import { MultiFileLoader } from "../fs/multi_file.js";
5
+ import { CSVLoader } from "../fs/csv.js";
6
+ import { PDFLoader } from "../fs/pdf.js";
7
+ import { TextLoader } from "../fs/text.js";
8
+ import { JSONLoader } from "../fs/json.js";
9
+ import { UnknownHandling } from "../fs/directory.js";
10
+ test("Test MultiFileLoader", async () => {
11
+ const baseDirectory = path.resolve(path.dirname(url.fileURLToPath(import.meta.url)), "./example_data");
12
+ const filePaths = [
13
+ path.resolve(baseDirectory, "1706.03762.pdf"),
14
+ path.resolve(baseDirectory, "Jacob_Lee_Resume_2023.pdf"),
15
+ path.resolve(baseDirectory, "Star_Wars_The_Clone_Wars_S06E07_Crisis_at_the_Heart.csv"),
16
+ path.resolve(baseDirectory, "Star_Wars_The_Clone_Wars_S06E07_Crisis_at_the_Heart.json"),
17
+ path.resolve(baseDirectory, "complex.json"),
18
+ path.resolve(baseDirectory, "example.txt"),
19
+ path.resolve(baseDirectory, "example_separator.csv"),
20
+ ];
21
+ const loader = new MultiFileLoader(filePaths, {
22
+ ".csv": (p) => {
23
+ if (p.includes("separator.csv")) {
24
+ return new CSVLoader(p, { column: "html", separator: "|" });
25
+ }
26
+ return new CSVLoader(p, "html");
27
+ },
28
+ ".pdf": (p) => new PDFLoader(p),
29
+ ".txt": (p) => new TextLoader(p),
30
+ ".json": (p) => new JSONLoader(p),
31
+ }, UnknownHandling.Ignore);
32
+ const docs = await loader.load();
33
+ expect(docs.length).toBe(123);
34
+ const expectedSources = [
35
+ // PDF
36
+ ...Array.from({ length: 15 }, (_) => path.resolve(baseDirectory, "1706.03762.pdf")),
37
+ path.resolve(baseDirectory, "Jacob_Lee_Resume_2023.pdf"),
38
+ // CSV
39
+ ...Array.from({ length: 32 }, (_) => path.resolve(baseDirectory, "Star_Wars_The_Clone_Wars_S06E07_Crisis_at_the_Heart.csv")),
40
+ // JSON
41
+ ...Array.from({ length: 32 }, (_) => path.resolve(baseDirectory, "Star_Wars_The_Clone_Wars_S06E07_Crisis_at_the_Heart.json")),
42
+ ...Array.from({ length: 10 }, (_) => path.resolve(baseDirectory, "complex.json")),
43
+ // TXT
44
+ path.resolve(baseDirectory, "example.txt"),
45
+ // CSV
46
+ ...Array.from({ length: 32 }, (_) => path.resolve(baseDirectory, "example_separator.csv")),
47
+ ];
48
+ expect(docs.map((d) => d.metadata.source).sort()).toEqual(expectedSources);
49
+ });