@langchain/classic 1.0.29 → 1.0.31
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/CHANGELOG.md +13 -0
- package/dist/agents/mrkl/outputParser.d.ts +1 -1
- package/dist/storage/file_system.cjs +72 -8
- package/dist/storage/file_system.cjs.map +1 -1
- package/dist/storage/file_system.d.cts +4 -0
- package/dist/storage/file_system.d.cts.map +1 -1
- package/dist/storage/file_system.d.ts +4 -0
- package/dist/storage/file_system.d.ts.map +1 -1
- package/dist/storage/file_system.js +72 -8
- package/dist/storage/file_system.js.map +1 -1
- package/package.json +15 -15
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,18 @@
|
|
|
1
1
|
# @langchain/classic
|
|
2
2
|
|
|
3
|
+
## 1.0.31
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- [#9386](https://github.com/langchain-ai/langchainjs/pull/9386) [`71e53f1`](https://github.com/langchain-ai/langchainjs/commit/71e53f1c07bc60e2c2304b10f0edd3c85c62b192) Thanks [@Josh-Engle](https://github.com/Josh-Engle)! - Prevent local file corruption when using `LocalFileStore`
|
|
8
|
+
|
|
9
|
+
## 1.0.30
|
|
10
|
+
|
|
11
|
+
### Patch Changes
|
|
12
|
+
|
|
13
|
+
- Updated dependencies [[`2301260`](https://github.com/langchain-ai/langchainjs/commit/2301260ae90ead5c5f725c8dae1487b6722607e2)]:
|
|
14
|
+
- @langchain/openai@1.4.4
|
|
15
|
+
|
|
3
16
|
## 1.0.29
|
|
4
17
|
|
|
5
18
|
### Patch Changes
|
|
@@ -24,10 +24,10 @@ declare class ZeroShotAgentOutputParser extends AgentActionOutputParser {
|
|
|
24
24
|
tool?: undefined;
|
|
25
25
|
toolInput?: undefined;
|
|
26
26
|
} | {
|
|
27
|
+
returnValues?: undefined;
|
|
27
28
|
tool: string;
|
|
28
29
|
toolInput: string;
|
|
29
30
|
log: string;
|
|
30
|
-
returnValues?: undefined;
|
|
31
31
|
}>;
|
|
32
32
|
/**
|
|
33
33
|
* Returns the format instructions for parsing the output of an agent
|
|
@@ -40,6 +40,7 @@ var file_system_exports = /* @__PURE__ */ require_runtime.__exportAll({ LocalFil
|
|
|
40
40
|
var LocalFileStore = class extends _langchain_core_stores.BaseStore {
|
|
41
41
|
lc_namespace = ["langchain", "storage"];
|
|
42
42
|
rootPath;
|
|
43
|
+
keyLocks = /* @__PURE__ */ new Map();
|
|
43
44
|
constructor(fields) {
|
|
44
45
|
super(fields);
|
|
45
46
|
this.rootPath = fields.rootPath;
|
|
@@ -65,11 +66,14 @@ var LocalFileStore = class extends _langchain_core_stores.BaseStore {
|
|
|
65
66
|
* @param fileContent An object with the key-value pairs to be written to the file.
|
|
66
67
|
*/
|
|
67
68
|
async setFileContent(content, key) {
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
69
|
+
await this.withKeyLock(key, async () => {
|
|
70
|
+
const fullPath = this.getFullPath(key);
|
|
71
|
+
try {
|
|
72
|
+
await this.writeFileAtomically(content, fullPath);
|
|
73
|
+
} catch (error) {
|
|
74
|
+
throw new Error(`Error writing file at path: ${fullPath}.\nError: ${JSON.stringify(error)}`);
|
|
75
|
+
}
|
|
76
|
+
});
|
|
73
77
|
}
|
|
74
78
|
/**
|
|
75
79
|
* Returns the full path of the file where the value of the given key is stored.
|
|
@@ -102,11 +106,14 @@ var LocalFileStore = class extends _langchain_core_stores.BaseStore {
|
|
|
102
106
|
}
|
|
103
107
|
/**
|
|
104
108
|
* Sets the values for the given keys in the store.
|
|
109
|
+
* The last value for duplicate keys will be used.
|
|
105
110
|
* @param keyValuePairs Array of key-value pairs to set in the store.
|
|
106
111
|
* @returns Promise that resolves when all key-value pairs have been set.
|
|
107
112
|
*/
|
|
108
113
|
async mset(keyValuePairs) {
|
|
109
|
-
|
|
114
|
+
const deduped = /* @__PURE__ */ new Map();
|
|
115
|
+
for (const [key, value] of keyValuePairs) deduped.set(key, value);
|
|
116
|
+
await Promise.all(Array.from(deduped.entries(), ([key, value]) => this.setFileContent(value, key)));
|
|
110
117
|
}
|
|
111
118
|
/**
|
|
112
119
|
* Deletes the given keys and their associated values from the store.
|
|
@@ -114,7 +121,13 @@ var LocalFileStore = class extends _langchain_core_stores.BaseStore {
|
|
|
114
121
|
* @returns Promise that resolves when all keys have been deleted.
|
|
115
122
|
*/
|
|
116
123
|
async mdelete(keys) {
|
|
117
|
-
await Promise.all(keys.map((key) =>
|
|
124
|
+
await Promise.all(keys.map((key) => this.withKeyLock(key, async () => {
|
|
125
|
+
try {
|
|
126
|
+
await node_fs_promises.unlink(this.getFullPath(key));
|
|
127
|
+
} catch (error) {
|
|
128
|
+
if (!error || error.code !== "ENOENT") throw error;
|
|
129
|
+
}
|
|
130
|
+
})));
|
|
118
131
|
}
|
|
119
132
|
/**
|
|
120
133
|
* Asynchronous generator that yields keys from the store. If a prefix is
|
|
@@ -123,7 +136,7 @@ var LocalFileStore = class extends _langchain_core_stores.BaseStore {
|
|
|
123
136
|
* @returns AsyncGenerator that yields keys from the store.
|
|
124
137
|
*/
|
|
125
138
|
async *yieldKeys(prefix) {
|
|
126
|
-
const allKeys = (await node_fs_promises.readdir(this.rootPath)).
|
|
139
|
+
const allKeys = (await node_fs_promises.readdir(this.rootPath)).filter((file) => file.endsWith(".txt")).map((file) => file.replace(/\.txt$/, ""));
|
|
127
140
|
for (const key of allKeys) if (prefix === void 0 || key.startsWith(prefix)) yield key;
|
|
128
141
|
}
|
|
129
142
|
/**
|
|
@@ -142,8 +155,59 @@ var LocalFileStore = class extends _langchain_core_stores.BaseStore {
|
|
|
142
155
|
throw new Error(`An error occurred creating directory at: ${rootPath}.\nError: ${JSON.stringify(error)}`);
|
|
143
156
|
}
|
|
144
157
|
}
|
|
158
|
+
try {
|
|
159
|
+
const entries = await node_fs_promises.readdir(rootPath);
|
|
160
|
+
await Promise.all(entries.filter((file) => file.endsWith(".tmp")).map((tempFile) => node_fs_promises.unlink(node_path.join(rootPath, tempFile)).catch(() => {})));
|
|
161
|
+
} catch {}
|
|
145
162
|
return new this({ rootPath });
|
|
146
163
|
}
|
|
164
|
+
/**
|
|
165
|
+
* Ensures calls for the same key run sequentially by chaining promises.
|
|
166
|
+
* @param key Key to serialize operations for.
|
|
167
|
+
* @param fn Async work to execute while the lock is held.
|
|
168
|
+
* @returns Promise resolving with the callback result once the lock releases.
|
|
169
|
+
*/
|
|
170
|
+
async withKeyLock(key, fn) {
|
|
171
|
+
const waitForPrevious = (this.keyLocks.get(key) ?? Promise.resolve()).catch(() => {});
|
|
172
|
+
let resolveCurrent;
|
|
173
|
+
const current = new Promise((resolve) => {
|
|
174
|
+
resolveCurrent = resolve;
|
|
175
|
+
});
|
|
176
|
+
const tail = waitForPrevious.then(() => current);
|
|
177
|
+
this.keyLocks.set(key, tail);
|
|
178
|
+
await waitForPrevious;
|
|
179
|
+
try {
|
|
180
|
+
return await fn();
|
|
181
|
+
} finally {
|
|
182
|
+
resolveCurrent?.();
|
|
183
|
+
if (this.keyLocks.get(key) === tail) this.keyLocks.delete(key);
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
/**
|
|
187
|
+
* Writes data to a temporary file before atomically renaming it into place.
|
|
188
|
+
* @param content Serialized value to persist.
|
|
189
|
+
* @param fullPath Destination path for the stored key.
|
|
190
|
+
*/
|
|
191
|
+
async writeFileAtomically(content, fullPath) {
|
|
192
|
+
const directory = node_path.dirname(fullPath);
|
|
193
|
+
await node_fs_promises.mkdir(directory, { recursive: true });
|
|
194
|
+
const tempPath = `${fullPath}.${Date.now()}-${Math.random().toString(16).slice(2)}.tmp`;
|
|
195
|
+
try {
|
|
196
|
+
await node_fs_promises.writeFile(tempPath, content);
|
|
197
|
+
try {
|
|
198
|
+
await node_fs_promises.rename(tempPath, fullPath);
|
|
199
|
+
} catch (renameError) {
|
|
200
|
+
const code = renameError.code;
|
|
201
|
+
if (renameError && (code === "EPERM" || code === "EACCES")) {
|
|
202
|
+
await node_fs_promises.writeFile(fullPath, content);
|
|
203
|
+
await node_fs_promises.unlink(tempPath).catch(() => {});
|
|
204
|
+
} else throw renameError;
|
|
205
|
+
}
|
|
206
|
+
} catch (error) {
|
|
207
|
+
await node_fs_promises.unlink(tempPath).catch(() => {});
|
|
208
|
+
throw error;
|
|
209
|
+
}
|
|
210
|
+
}
|
|
147
211
|
};
|
|
148
212
|
//#endregion
|
|
149
213
|
exports.LocalFileStore = LocalFileStore;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"file_system.cjs","names":["BaseStore","fs","path"],"sources":["../../src/storage/file_system.ts"],"sourcesContent":["import * as fs from \"node:fs/promises\";\nimport * as path from \"node:path\";\nimport { BaseStore } from \"@langchain/core/stores\";\n\n/**\n * File system implementation of the BaseStore using a dictionary. Used for\n * storing key-value pairs in the file system.\n * @example\n * ```typescript\n * const store = await LocalFileStore.fromPath(\"./messages\");\n * await store.mset(\n * Array.from({ length: 5 }).map((_, index) => [\n * `message:id:${index}`,\n * new TextEncoder().encode(\n * JSON.stringify(\n * index % 2 === 0\n * ? new AIMessage(\"ai stuff...\")\n * : new HumanMessage(\"human stuff...\"),\n * ),\n * ),\n * ]),\n * );\n * const retrievedMessages = await store.mget([\"message:id:0\", \"message:id:1\"]);\n * console.log(retrievedMessages.map((v) => new TextDecoder().decode(v)));\n * for await (const key of store.yieldKeys(\"message:id:\")) {\n * await store.mdelete([key]);\n * }\n * ```\n *\n * @security **Security Notice** This file store\n * can alter any text file in the provided directory and any subfolders.\n * Make sure that the path you specify when initializing the store is free\n * of other files.\n */\nexport class LocalFileStore extends BaseStore<string, Uint8Array> {\n lc_namespace = [\"langchain\", \"storage\"];\n\n rootPath: string;\n\n constructor(fields: { rootPath: string }) {\n super(fields);\n this.rootPath = fields.rootPath;\n }\n\n /**\n * Read and parse the file at the given path.\n * @param key The key to read the file for.\n * @returns Promise that resolves to the parsed file content.\n */\n private async getParsedFile(key: string): Promise<Uint8Array | undefined> {\n // Validate the key to prevent path traversal\n if (!/^[a-zA-Z0-9_\\-:.]+$/.test(key)) {\n throw new Error(\n \"Invalid key. Only alphanumeric characters, underscores, hyphens, colons, and periods are allowed.\"\n );\n }\n try {\n const fileContent = await fs.readFile(this.getFullPath(key));\n if (!fileContent) {\n return undefined;\n }\n return fileContent;\n // oxlint-disable-next-line @typescript-eslint/no-explicit-any\n } catch (e: any) {\n // File does not exist yet.\n if (\"code\" in e && e.code === \"ENOENT\") {\n return undefined;\n }\n throw new Error(\n `Error reading and parsing file at path: ${\n this.rootPath\n }.\\nError: ${JSON.stringify(e)}`\n );\n }\n }\n\n /**\n * Writes the given key-value pairs to the file at the given path.\n * @param fileContent An object with the key-value pairs to be written to the file.\n */\n private async setFileContent(content: Uint8Array, key: string) {\n try {\n await fs.writeFile(this.getFullPath(key), content);\n } catch (error) {\n throw new Error(\n `Error writing file at path: ${this.getFullPath(\n key\n )}.\\nError: ${JSON.stringify(error)}`\n );\n }\n }\n\n /**\n * Returns the full path of the file where the value of the given key is stored.\n * @param key the key to get the full path for\n */\n private getFullPath(key: string): string {\n try {\n const keyAsTxtFile = `${key}.txt`;\n\n // Validate the key to prevent path traversal\n if (!/^[a-zA-Z0-9_.\\-/]+$/.test(key)) {\n throw new Error(`Invalid characters in key: ${key}`);\n }\n\n const fullPath = path.resolve(this.rootPath, keyAsTxtFile);\n const commonPath = path.resolve(this.rootPath);\n\n if (!fullPath.startsWith(commonPath)) {\n throw new Error(\n `Invalid key: ${key}. Key should be relative to the root path. ` +\n `Root path: ${this.rootPath}, Full path: ${fullPath}`\n );\n }\n\n return fullPath;\n } catch (e) {\n throw new Error(\n `Error getting full path for key: ${key}.\\nError: ${String(e)}`\n );\n }\n }\n\n /**\n * Retrieves the values associated with the given keys from the store.\n * @param keys Keys to retrieve values for.\n * @returns Array of values associated with the given keys.\n */\n async mget(keys: string[]) {\n const values: (Uint8Array | undefined)[] = [];\n for (const key of keys) {\n const fileContent = await this.getParsedFile(key);\n values.push(fileContent);\n }\n return values;\n }\n\n /**\n * Sets the values for the given keys in the store.\n * @param keyValuePairs Array of key-value pairs to set in the store.\n * @returns Promise that resolves when all key-value pairs have been set.\n */\n async mset(keyValuePairs: [string, Uint8Array][]): Promise<void> {\n await Promise.all(\n keyValuePairs.map(([key, value]) => this.setFileContent(value, key))\n );\n }\n\n /**\n * Deletes the given keys and their associated values from the store.\n * @param keys Keys to delete from the store.\n * @returns Promise that resolves when all keys have been deleted.\n */\n async mdelete(keys: string[]): Promise<void> {\n await Promise.all(keys.map((key) => fs.unlink(this.getFullPath(key))));\n }\n\n /**\n * Asynchronous generator that yields keys from the store. If a prefix is\n * provided, it only yields keys that start with the prefix.\n * @param prefix Optional prefix to filter keys.\n * @returns AsyncGenerator that yields keys from the store.\n */\n async *yieldKeys(prefix?: string): AsyncGenerator<string> {\n const allFiles = await fs.readdir(this.rootPath);\n const allKeys = allFiles.map((file) => file.replace(\".txt\", \"\"));\n for (const key of allKeys) {\n if (prefix === undefined || key.startsWith(prefix)) {\n yield key;\n }\n }\n }\n\n /**\n * Static method for initializing the class.\n * Preforms a check to see if the directory exists, and if not, creates it.\n * @param path Path to the directory.\n * @returns Promise that resolves to an instance of the class.\n */\n static async fromPath(rootPath: string): Promise<LocalFileStore> {\n try {\n // Verifies the directory exists at the provided path, and that it is readable and writable.\n await fs.access(rootPath, fs.constants.R_OK | fs.constants.W_OK);\n } catch {\n try {\n // Directory does not exist, create it.\n await fs.mkdir(rootPath, { recursive: true });\n } catch (error) {\n throw new Error(\n `An error occurred creating directory at: ${rootPath}.\\nError: ${JSON.stringify(\n error\n )}`\n );\n }\n }\n\n return new this({ rootPath });\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAkCA,IAAa,iBAAb,cAAoCA,uBAAAA,UAA8B;CAChE,eAAe,CAAC,aAAa,UAAU;CAEvC;CAEA,YAAY,QAA8B;AACxC,QAAM,OAAO;AACb,OAAK,WAAW,OAAO;;;;;;;CAQzB,MAAc,cAAc,KAA8C;AAExE,MAAI,CAAC,sBAAsB,KAAK,IAAI,CAClC,OAAM,IAAI,MACR,oGACD;AAEH,MAAI;GACF,MAAM,cAAc,MAAMC,iBAAG,SAAS,KAAK,YAAY,IAAI,CAAC;AAC5D,OAAI,CAAC,YACH;AAEF,UAAO;WAEA,GAAQ;AAEf,OAAI,UAAU,KAAK,EAAE,SAAS,SAC5B;AAEF,SAAM,IAAI,MACR,2CACE,KAAK,SACN,YAAY,KAAK,UAAU,EAAE,GAC/B;;;;;;;CAQL,MAAc,eAAe,SAAqB,KAAa;AAC7D,MAAI;AACF,SAAMA,iBAAG,UAAU,KAAK,YAAY,IAAI,EAAE,QAAQ;WAC3C,OAAO;AACd,SAAM,IAAI,MACR,+BAA+B,KAAK,YAClC,IACD,CAAC,YAAY,KAAK,UAAU,MAAM,GACpC;;;;;;;CAQL,YAAoB,KAAqB;AACvC,MAAI;GACF,MAAM,eAAe,GAAG,IAAI;AAG5B,OAAI,CAAC,sBAAsB,KAAK,IAAI,CAClC,OAAM,IAAI,MAAM,8BAA8B,MAAM;GAGtD,MAAM,WAAWC,UAAK,QAAQ,KAAK,UAAU,aAAa;GAC1D,MAAM,aAAaA,UAAK,QAAQ,KAAK,SAAS;AAE9C,OAAI,CAAC,SAAS,WAAW,WAAW,CAClC,OAAM,IAAI,MACR,gBAAgB,IAAI,wDACJ,KAAK,SAAS,eAAe,WAC9C;AAGH,UAAO;WACA,GAAG;AACV,SAAM,IAAI,MACR,oCAAoC,IAAI,YAAY,OAAO,EAAE,GAC9D;;;;;;;;CASL,MAAM,KAAK,MAAgB;EACzB,MAAM,SAAqC,EAAE;AAC7C,OAAK,MAAM,OAAO,MAAM;GACtB,MAAM,cAAc,MAAM,KAAK,cAAc,IAAI;AACjD,UAAO,KAAK,YAAY;;AAE1B,SAAO;;;;;;;CAQT,MAAM,KAAK,eAAsD;AAC/D,QAAM,QAAQ,IACZ,cAAc,KAAK,CAAC,KAAK,WAAW,KAAK,eAAe,OAAO,IAAI,CAAC,CACrE;;;;;;;CAQH,MAAM,QAAQ,MAA+B;AAC3C,QAAM,QAAQ,IAAI,KAAK,KAAK,QAAQD,iBAAG,OAAO,KAAK,YAAY,IAAI,CAAC,CAAC,CAAC;;;;;;;;CASxE,OAAO,UAAU,QAAyC;EAExD,MAAM,WADW,MAAMA,iBAAG,QAAQ,KAAK,SAAS,EACvB,KAAK,SAAS,KAAK,QAAQ,QAAQ,GAAG,CAAC;AAChE,OAAK,MAAM,OAAO,QAChB,KAAI,WAAW,KAAA,KAAa,IAAI,WAAW,OAAO,CAChD,OAAM;;;;;;;;CAWZ,aAAa,SAAS,UAA2C;AAC/D,MAAI;AAEF,SAAMA,iBAAG,OAAO,UAAUA,iBAAG,UAAU,OAAOA,iBAAG,UAAU,KAAK;UAC1D;AACN,OAAI;AAEF,UAAMA,iBAAG,MAAM,UAAU,EAAE,WAAW,MAAM,CAAC;YACtC,OAAO;AACd,UAAM,IAAI,MACR,4CAA4C,SAAS,YAAY,KAAK,UACpE,MACD,GACF;;;AAIL,SAAO,IAAI,KAAK,EAAE,UAAU,CAAC"}
|
|
1
|
+
{"version":3,"file":"file_system.cjs","names":["BaseStore","fs","path"],"sources":["../../src/storage/file_system.ts"],"sourcesContent":["import * as fs from \"node:fs/promises\";\nimport * as path from \"node:path\";\nimport { BaseStore } from \"@langchain/core/stores\";\n\n/**\n * File system implementation of the BaseStore using a dictionary. Used for\n * storing key-value pairs in the file system.\n * @example\n * ```typescript\n * const store = await LocalFileStore.fromPath(\"./messages\");\n * await store.mset(\n * Array.from({ length: 5 }).map((_, index) => [\n * `message:id:${index}`,\n * new TextEncoder().encode(\n * JSON.stringify(\n * index % 2 === 0\n * ? new AIMessage(\"ai stuff...\")\n * : new HumanMessage(\"human stuff...\"),\n * ),\n * ),\n * ]),\n * );\n * const retrievedMessages = await store.mget([\"message:id:0\", \"message:id:1\"]);\n * console.log(retrievedMessages.map((v) => new TextDecoder().decode(v)));\n * for await (const key of store.yieldKeys(\"message:id:\")) {\n * await store.mdelete([key]);\n * }\n * ```\n *\n * @security **Security Notice** This file store\n * can alter any text file in the provided directory and any subfolders.\n * Make sure that the path you specify when initializing the store is free\n * of other files.\n */\nexport class LocalFileStore extends BaseStore<string, Uint8Array> {\n lc_namespace = [\"langchain\", \"storage\"];\n\n rootPath: string;\n\n private keyLocks: Map<string, Promise<void>> = new Map();\n\n constructor(fields: { rootPath: string }) {\n super(fields);\n this.rootPath = fields.rootPath;\n }\n\n /**\n * Read and parse the file at the given path.\n * @param key The key to read the file for.\n * @returns Promise that resolves to the parsed file content.\n */\n private async getParsedFile(key: string): Promise<Uint8Array | undefined> {\n // Validate the key to prevent path traversal\n if (!/^[a-zA-Z0-9_\\-:.]+$/.test(key)) {\n throw new Error(\n \"Invalid key. Only alphanumeric characters, underscores, hyphens, colons, and periods are allowed.\"\n );\n }\n try {\n const fileContent = await fs.readFile(this.getFullPath(key));\n if (!fileContent) {\n return undefined;\n }\n return fileContent;\n // oxlint-disable-next-line @typescript-eslint/no-explicit-any\n } catch (e: any) {\n // File does not exist yet.\n if (\"code\" in e && e.code === \"ENOENT\") {\n return undefined;\n }\n throw new Error(\n `Error reading and parsing file at path: ${\n this.rootPath\n }.\\nError: ${JSON.stringify(e)}`\n );\n }\n }\n\n /**\n * Writes the given key-value pairs to the file at the given path.\n * @param fileContent An object with the key-value pairs to be written to the file.\n */\n private async setFileContent(content: Uint8Array, key: string) {\n await this.withKeyLock(key, async () => {\n const fullPath = this.getFullPath(key);\n try {\n await this.writeFileAtomically(content, fullPath);\n } catch (error) {\n throw new Error(\n `Error writing file at path: ${fullPath}.\\nError: ${JSON.stringify(\n error\n )}`\n );\n }\n });\n }\n\n /**\n * Returns the full path of the file where the value of the given key is stored.\n * @param key the key to get the full path for\n */\n private getFullPath(key: string): string {\n try {\n const keyAsTxtFile = `${key}.txt`;\n\n // Validate the key to prevent path traversal\n if (!/^[a-zA-Z0-9_.\\-/]+$/.test(key)) {\n throw new Error(`Invalid characters in key: ${key}`);\n }\n\n const fullPath = path.resolve(this.rootPath, keyAsTxtFile);\n const commonPath = path.resolve(this.rootPath);\n\n if (!fullPath.startsWith(commonPath)) {\n throw new Error(\n `Invalid key: ${key}. Key should be relative to the root path. ` +\n `Root path: ${this.rootPath}, Full path: ${fullPath}`\n );\n }\n\n return fullPath;\n } catch (e) {\n throw new Error(\n `Error getting full path for key: ${key}.\\nError: ${String(e)}`\n );\n }\n }\n\n /**\n * Retrieves the values associated with the given keys from the store.\n * @param keys Keys to retrieve values for.\n * @returns Array of values associated with the given keys.\n */\n async mget(keys: string[]) {\n const values: (Uint8Array | undefined)[] = [];\n for (const key of keys) {\n const fileContent = await this.getParsedFile(key);\n values.push(fileContent);\n }\n return values;\n }\n\n /**\n * Sets the values for the given keys in the store.\n * The last value for duplicate keys will be used.\n * @param keyValuePairs Array of key-value pairs to set in the store.\n * @returns Promise that resolves when all key-value pairs have been set.\n */\n async mset(keyValuePairs: [string, Uint8Array][]): Promise<void> {\n const deduped = new Map<string, Uint8Array>();\n for (const [key, value] of keyValuePairs) {\n deduped.set(key, value);\n }\n\n await Promise.all(\n Array.from(deduped.entries(), ([key, value]) =>\n this.setFileContent(value, key)\n )\n );\n }\n\n /**\n * Deletes the given keys and their associated values from the store.\n * @param keys Keys to delete from the store.\n * @returns Promise that resolves when all keys have been deleted.\n */\n async mdelete(keys: string[]): Promise<void> {\n await Promise.all(\n keys.map((key) =>\n this.withKeyLock(key, async () => {\n try {\n await fs.unlink(this.getFullPath(key));\n } catch (error) {\n if (!error || (error as { code?: string }).code !== \"ENOENT\") {\n throw error;\n }\n }\n })\n )\n );\n }\n\n /**\n * Asynchronous generator that yields keys from the store. If a prefix is\n * provided, it only yields keys that start with the prefix.\n * @param prefix Optional prefix to filter keys.\n * @returns AsyncGenerator that yields keys from the store.\n */\n async *yieldKeys(prefix?: string): AsyncGenerator<string> {\n const allFiles: string[] = await fs.readdir(this.rootPath);\n const allKeys = allFiles\n .filter((file) => file.endsWith(\".txt\"))\n .map((file) => file.replace(/\\.txt$/, \"\"));\n for (const key of allKeys) {\n if (prefix === undefined || key.startsWith(prefix)) {\n yield key;\n }\n }\n }\n\n /**\n * Static method for initializing the class.\n * Preforms a check to see if the directory exists, and if not, creates it.\n * @param path Path to the directory.\n * @returns Promise that resolves to an instance of the class.\n */\n static async fromPath(rootPath: string): Promise<LocalFileStore> {\n try {\n // Verifies the directory exists at the provided path, and that it is readable and writable.\n await fs.access(rootPath, fs.constants.R_OK | fs.constants.W_OK);\n } catch {\n try {\n // Directory does not exist, create it.\n await fs.mkdir(rootPath, { recursive: true });\n } catch (error) {\n throw new Error(\n `An error occurred creating directory at: ${rootPath}.\\nError: ${JSON.stringify(\n error\n )}`\n );\n }\n }\n\n // Clean up orphaned temp files left by interrupted atomic writes.\n try {\n const entries = await fs.readdir(rootPath);\n await Promise.all(\n entries\n .filter((file) => file.endsWith(\".tmp\"))\n .map((tempFile) =>\n fs.unlink(path.join(rootPath, tempFile)).catch(() => {})\n )\n );\n } catch {\n // Ignore cleanup errors.\n }\n\n return new this({ rootPath });\n }\n\n /**\n * Ensures calls for the same key run sequentially by chaining promises.\n * @param key Key to serialize operations for.\n * @param fn Async work to execute while the lock is held.\n * @returns Promise resolving with the callback result once the lock releases.\n */\n private async withKeyLock<T>(key: string, fn: () => Promise<T>): Promise<T> {\n const previous = this.keyLocks.get(key) ?? Promise.resolve();\n const waitForPrevious = previous.catch(() => {});\n\n let resolveCurrent: (() => void) | undefined;\n const current = new Promise<void>((resolve) => {\n resolveCurrent = resolve;\n });\n\n const tail = waitForPrevious.then(() => current);\n this.keyLocks.set(key, tail);\n\n await waitForPrevious;\n try {\n return await fn();\n } finally {\n resolveCurrent?.();\n if (this.keyLocks.get(key) === tail) {\n this.keyLocks.delete(key);\n }\n }\n }\n\n /**\n * Writes data to a temporary file before atomically renaming it into place.\n * @param content Serialized value to persist.\n * @param fullPath Destination path for the stored key.\n */\n private async writeFileAtomically(content: Uint8Array, fullPath: string) {\n const directory = path.dirname(fullPath);\n await fs.mkdir(directory, { recursive: true });\n\n const tempPath = `${fullPath}.${Date.now()}-${Math.random()\n .toString(16)\n .slice(2)}.tmp`;\n\n try {\n await fs.writeFile(tempPath, content);\n\n try {\n await fs.rename(tempPath, fullPath);\n } catch (renameError) {\n const code = (renameError as { code?: string }).code;\n if (renameError && (code === \"EPERM\" || code === \"EACCES\")) {\n await fs.writeFile(fullPath, content);\n await fs.unlink(tempPath).catch(() => {});\n } else {\n throw renameError;\n }\n }\n } catch (error) {\n await fs.unlink(tempPath).catch(() => {});\n throw error;\n }\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAkCA,IAAa,iBAAb,cAAoCA,uBAAAA,UAA8B;CAChE,eAAe,CAAC,aAAa,UAAU;CAEvC;CAEA,2BAA+C,IAAI,KAAK;CAExD,YAAY,QAA8B;AACxC,QAAM,OAAO;AACb,OAAK,WAAW,OAAO;;;;;;;CAQzB,MAAc,cAAc,KAA8C;AAExE,MAAI,CAAC,sBAAsB,KAAK,IAAI,CAClC,OAAM,IAAI,MACR,oGACD;AAEH,MAAI;GACF,MAAM,cAAc,MAAMC,iBAAG,SAAS,KAAK,YAAY,IAAI,CAAC;AAC5D,OAAI,CAAC,YACH;AAEF,UAAO;WAEA,GAAQ;AAEf,OAAI,UAAU,KAAK,EAAE,SAAS,SAC5B;AAEF,SAAM,IAAI,MACR,2CACE,KAAK,SACN,YAAY,KAAK,UAAU,EAAE,GAC/B;;;;;;;CAQL,MAAc,eAAe,SAAqB,KAAa;AAC7D,QAAM,KAAK,YAAY,KAAK,YAAY;GACtC,MAAM,WAAW,KAAK,YAAY,IAAI;AACtC,OAAI;AACF,UAAM,KAAK,oBAAoB,SAAS,SAAS;YAC1C,OAAO;AACd,UAAM,IAAI,MACR,+BAA+B,SAAS,YAAY,KAAK,UACvD,MACD,GACF;;IAEH;;;;;;CAOJ,YAAoB,KAAqB;AACvC,MAAI;GACF,MAAM,eAAe,GAAG,IAAI;AAG5B,OAAI,CAAC,sBAAsB,KAAK,IAAI,CAClC,OAAM,IAAI,MAAM,8BAA8B,MAAM;GAGtD,MAAM,WAAWC,UAAK,QAAQ,KAAK,UAAU,aAAa;GAC1D,MAAM,aAAaA,UAAK,QAAQ,KAAK,SAAS;AAE9C,OAAI,CAAC,SAAS,WAAW,WAAW,CAClC,OAAM,IAAI,MACR,gBAAgB,IAAI,wDACJ,KAAK,SAAS,eAAe,WAC9C;AAGH,UAAO;WACA,GAAG;AACV,SAAM,IAAI,MACR,oCAAoC,IAAI,YAAY,OAAO,EAAE,GAC9D;;;;;;;;CASL,MAAM,KAAK,MAAgB;EACzB,MAAM,SAAqC,EAAE;AAC7C,OAAK,MAAM,OAAO,MAAM;GACtB,MAAM,cAAc,MAAM,KAAK,cAAc,IAAI;AACjD,UAAO,KAAK,YAAY;;AAE1B,SAAO;;;;;;;;CAST,MAAM,KAAK,eAAsD;EAC/D,MAAM,0BAAU,IAAI,KAAyB;AAC7C,OAAK,MAAM,CAAC,KAAK,UAAU,cACzB,SAAQ,IAAI,KAAK,MAAM;AAGzB,QAAM,QAAQ,IACZ,MAAM,KAAK,QAAQ,SAAS,GAAG,CAAC,KAAK,WACnC,KAAK,eAAe,OAAO,IAAI,CAChC,CACF;;;;;;;CAQH,MAAM,QAAQ,MAA+B;AAC3C,QAAM,QAAQ,IACZ,KAAK,KAAK,QACR,KAAK,YAAY,KAAK,YAAY;AAChC,OAAI;AACF,UAAMD,iBAAG,OAAO,KAAK,YAAY,IAAI,CAAC;YAC/B,OAAO;AACd,QAAI,CAAC,SAAU,MAA4B,SAAS,SAClD,OAAM;;IAGV,CACH,CACF;;;;;;;;CASH,OAAO,UAAU,QAAyC;EAExD,MAAM,WADqB,MAAMA,iBAAG,QAAQ,KAAK,SAAS,EAEvD,QAAQ,SAAS,KAAK,SAAS,OAAO,CAAC,CACvC,KAAK,SAAS,KAAK,QAAQ,UAAU,GAAG,CAAC;AAC5C,OAAK,MAAM,OAAO,QAChB,KAAI,WAAW,KAAA,KAAa,IAAI,WAAW,OAAO,CAChD,OAAM;;;;;;;;CAWZ,aAAa,SAAS,UAA2C;AAC/D,MAAI;AAEF,SAAMA,iBAAG,OAAO,UAAUA,iBAAG,UAAU,OAAOA,iBAAG,UAAU,KAAK;UAC1D;AACN,OAAI;AAEF,UAAMA,iBAAG,MAAM,UAAU,EAAE,WAAW,MAAM,CAAC;YACtC,OAAO;AACd,UAAM,IAAI,MACR,4CAA4C,SAAS,YAAY,KAAK,UACpE,MACD,GACF;;;AAKL,MAAI;GACF,MAAM,UAAU,MAAMA,iBAAG,QAAQ,SAAS;AAC1C,SAAM,QAAQ,IACZ,QACG,QAAQ,SAAS,KAAK,SAAS,OAAO,CAAC,CACvC,KAAK,aACJA,iBAAG,OAAOC,UAAK,KAAK,UAAU,SAAS,CAAC,CAAC,YAAY,GAAG,CACzD,CACJ;UACK;AAIR,SAAO,IAAI,KAAK,EAAE,UAAU,CAAC;;;;;;;;CAS/B,MAAc,YAAe,KAAa,IAAkC;EAE1E,MAAM,mBADW,KAAK,SAAS,IAAI,IAAI,IAAI,QAAQ,SAAS,EAC3B,YAAY,GAAG;EAEhD,IAAI;EACJ,MAAM,UAAU,IAAI,SAAe,YAAY;AAC7C,oBAAiB;IACjB;EAEF,MAAM,OAAO,gBAAgB,WAAW,QAAQ;AAChD,OAAK,SAAS,IAAI,KAAK,KAAK;AAE5B,QAAM;AACN,MAAI;AACF,UAAO,MAAM,IAAI;YACT;AACR,qBAAkB;AAClB,OAAI,KAAK,SAAS,IAAI,IAAI,KAAK,KAC7B,MAAK,SAAS,OAAO,IAAI;;;;;;;;CAU/B,MAAc,oBAAoB,SAAqB,UAAkB;EACvE,MAAM,YAAYA,UAAK,QAAQ,SAAS;AACxC,QAAMD,iBAAG,MAAM,WAAW,EAAE,WAAW,MAAM,CAAC;EAE9C,MAAM,WAAW,GAAG,SAAS,GAAG,KAAK,KAAK,CAAC,GAAG,KAAK,QAAQ,CACxD,SAAS,GAAG,CACZ,MAAM,EAAE,CAAC;AAEZ,MAAI;AACF,SAAMA,iBAAG,UAAU,UAAU,QAAQ;AAErC,OAAI;AACF,UAAMA,iBAAG,OAAO,UAAU,SAAS;YAC5B,aAAa;IACpB,MAAM,OAAQ,YAAkC;AAChD,QAAI,gBAAgB,SAAS,WAAW,SAAS,WAAW;AAC1D,WAAMA,iBAAG,UAAU,UAAU,QAAQ;AACrC,WAAMA,iBAAG,OAAO,SAAS,CAAC,YAAY,GAAG;UAEzC,OAAM;;WAGH,OAAO;AACd,SAAMA,iBAAG,OAAO,SAAS,CAAC,YAAY,GAAG;AACzC,SAAM"}
|
|
@@ -34,6 +34,7 @@ import { BaseStore } from "@langchain/core/stores";
|
|
|
34
34
|
declare class LocalFileStore extends BaseStore<string, Uint8Array> {
|
|
35
35
|
lc_namespace: string[];
|
|
36
36
|
rootPath: string;
|
|
37
|
+
private keyLocks;
|
|
37
38
|
constructor(fields: {
|
|
38
39
|
rootPath: string;
|
|
39
40
|
});
|
|
@@ -52,6 +53,7 @@ declare class LocalFileStore extends BaseStore<string, Uint8Array> {
|
|
|
52
53
|
mget(keys: string[]): Promise<(Uint8Array<ArrayBufferLike> | undefined)[]>;
|
|
53
54
|
/**
|
|
54
55
|
* Sets the values for the given keys in the store.
|
|
56
|
+
* The last value for duplicate keys will be used.
|
|
55
57
|
* @param keyValuePairs Array of key-value pairs to set in the store.
|
|
56
58
|
* @returns Promise that resolves when all key-value pairs have been set.
|
|
57
59
|
*/
|
|
@@ -76,6 +78,8 @@ declare class LocalFileStore extends BaseStore<string, Uint8Array> {
|
|
|
76
78
|
* @returns Promise that resolves to an instance of the class.
|
|
77
79
|
*/
|
|
78
80
|
static fromPath(rootPath: string): Promise<LocalFileStore>;
|
|
81
|
+
private withKeyLock;
|
|
82
|
+
private writeFileAtomically;
|
|
79
83
|
}
|
|
80
84
|
//#endregion
|
|
81
85
|
export { LocalFileStore };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"file_system.d.cts","names":[],"sources":["../../src/storage/file_system.ts"],"mappings":";;;;;AAkCA;;;;;;;;;;;;;;;;;;;;;;;;;;;;cAAa,cAAA,SAAuB,SAAA,SAAkB,UAAA;EACpD,YAAA;EAEA,QAAA;
|
|
1
|
+
{"version":3,"file":"file_system.d.cts","names":[],"sources":["../../src/storage/file_system.ts"],"mappings":";;;;;AAkCA;;;;;;;;;;;;;;;;;;;;;;;;;;;;cAAa,cAAA,SAAuB,SAAA,SAAkB,UAAA;EACpD,YAAA;EAEA,QAAA;EAAA,QAEQ,QAAA;EAER,WAAA,CAAY,MAAA;IAAU,QAAA;EAAA;EAAA,QAUR,aAAA;EAAA,QA+BA,cAAA;EAoFA;;;;EAAA,QAjEN,WAAA;EAyGK;;;;;EAzEP,IAAA,CAAK,IAAA,aAAc,OAAA,EAAA,UAAA,CAAA,eAAA;EA6IQ;;;;;;EA9H3B,IAAA,CAAK,aAAA,WAAwB,UAAA,MAAgB,OAAA;;;;;;EAkB7C,OAAA,CAAQ,IAAA,aAAiB,OAAA;;;;;;;EAsBxB,SAAA,CAAU,MAAA,YAAkB,cAAA;;;;;;;SAkBtB,QAAA,CAAS,QAAA,WAAmB,OAAA,CAAQ,cAAA;EAAA,QAwCnC,WAAA;EAAA,QA4BA,mBAAA;AAAA"}
|
|
@@ -34,6 +34,7 @@ import { BaseStore } from "@langchain/core/stores";
|
|
|
34
34
|
declare class LocalFileStore extends BaseStore<string, Uint8Array> {
|
|
35
35
|
lc_namespace: string[];
|
|
36
36
|
rootPath: string;
|
|
37
|
+
private keyLocks;
|
|
37
38
|
constructor(fields: {
|
|
38
39
|
rootPath: string;
|
|
39
40
|
});
|
|
@@ -52,6 +53,7 @@ declare class LocalFileStore extends BaseStore<string, Uint8Array> {
|
|
|
52
53
|
mget(keys: string[]): Promise<(Uint8Array<ArrayBufferLike> | undefined)[]>;
|
|
53
54
|
/**
|
|
54
55
|
* Sets the values for the given keys in the store.
|
|
56
|
+
* The last value for duplicate keys will be used.
|
|
55
57
|
* @param keyValuePairs Array of key-value pairs to set in the store.
|
|
56
58
|
* @returns Promise that resolves when all key-value pairs have been set.
|
|
57
59
|
*/
|
|
@@ -76,6 +78,8 @@ declare class LocalFileStore extends BaseStore<string, Uint8Array> {
|
|
|
76
78
|
* @returns Promise that resolves to an instance of the class.
|
|
77
79
|
*/
|
|
78
80
|
static fromPath(rootPath: string): Promise<LocalFileStore>;
|
|
81
|
+
private withKeyLock;
|
|
82
|
+
private writeFileAtomically;
|
|
79
83
|
}
|
|
80
84
|
//#endregion
|
|
81
85
|
export { LocalFileStore };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"file_system.d.ts","names":[],"sources":["../../src/storage/file_system.ts"],"mappings":";;;;;AAkCA;;;;;;;;;;;;;;;;;;;;;;;;;;;;cAAa,cAAA,SAAuB,SAAA,SAAkB,UAAA;EACpD,YAAA;EAEA,QAAA;
|
|
1
|
+
{"version":3,"file":"file_system.d.ts","names":[],"sources":["../../src/storage/file_system.ts"],"mappings":";;;;;AAkCA;;;;;;;;;;;;;;;;;;;;;;;;;;;;cAAa,cAAA,SAAuB,SAAA,SAAkB,UAAA;EACpD,YAAA;EAEA,QAAA;EAAA,QAEQ,QAAA;EAER,WAAA,CAAY,MAAA;IAAU,QAAA;EAAA;EAAA,QAUR,aAAA;EAAA,QA+BA,cAAA;EAoFA;;;;EAAA,QAjEN,WAAA;EAyGK;;;;;EAzEP,IAAA,CAAK,IAAA,aAAc,OAAA,EAAA,UAAA,CAAA,eAAA;EA6IQ;;;;;;EA9H3B,IAAA,CAAK,aAAA,WAAwB,UAAA,MAAgB,OAAA;;;;;;EAkB7C,OAAA,CAAQ,IAAA,aAAiB,OAAA;;;;;;;EAsBxB,SAAA,CAAU,MAAA,YAAkB,cAAA;;;;;;;SAkBtB,QAAA,CAAS,QAAA,WAAmB,OAAA,CAAQ,cAAA;EAAA,QAwCnC,WAAA;EAAA,QA4BA,mBAAA;AAAA"}
|
|
@@ -37,6 +37,7 @@ var file_system_exports = /* @__PURE__ */ __exportAll({ LocalFileStore: () => Lo
|
|
|
37
37
|
var LocalFileStore = class extends BaseStore {
|
|
38
38
|
lc_namespace = ["langchain", "storage"];
|
|
39
39
|
rootPath;
|
|
40
|
+
keyLocks = /* @__PURE__ */ new Map();
|
|
40
41
|
constructor(fields) {
|
|
41
42
|
super(fields);
|
|
42
43
|
this.rootPath = fields.rootPath;
|
|
@@ -62,11 +63,14 @@ var LocalFileStore = class extends BaseStore {
|
|
|
62
63
|
* @param fileContent An object with the key-value pairs to be written to the file.
|
|
63
64
|
*/
|
|
64
65
|
async setFileContent(content, key) {
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
66
|
+
await this.withKeyLock(key, async () => {
|
|
67
|
+
const fullPath = this.getFullPath(key);
|
|
68
|
+
try {
|
|
69
|
+
await this.writeFileAtomically(content, fullPath);
|
|
70
|
+
} catch (error) {
|
|
71
|
+
throw new Error(`Error writing file at path: ${fullPath}.\nError: ${JSON.stringify(error)}`);
|
|
72
|
+
}
|
|
73
|
+
});
|
|
70
74
|
}
|
|
71
75
|
/**
|
|
72
76
|
* Returns the full path of the file where the value of the given key is stored.
|
|
@@ -99,11 +103,14 @@ var LocalFileStore = class extends BaseStore {
|
|
|
99
103
|
}
|
|
100
104
|
/**
|
|
101
105
|
* Sets the values for the given keys in the store.
|
|
106
|
+
* The last value for duplicate keys will be used.
|
|
102
107
|
* @param keyValuePairs Array of key-value pairs to set in the store.
|
|
103
108
|
* @returns Promise that resolves when all key-value pairs have been set.
|
|
104
109
|
*/
|
|
105
110
|
async mset(keyValuePairs) {
|
|
106
|
-
|
|
111
|
+
const deduped = /* @__PURE__ */ new Map();
|
|
112
|
+
for (const [key, value] of keyValuePairs) deduped.set(key, value);
|
|
113
|
+
await Promise.all(Array.from(deduped.entries(), ([key, value]) => this.setFileContent(value, key)));
|
|
107
114
|
}
|
|
108
115
|
/**
|
|
109
116
|
* Deletes the given keys and their associated values from the store.
|
|
@@ -111,7 +118,13 @@ var LocalFileStore = class extends BaseStore {
|
|
|
111
118
|
* @returns Promise that resolves when all keys have been deleted.
|
|
112
119
|
*/
|
|
113
120
|
async mdelete(keys) {
|
|
114
|
-
await Promise.all(keys.map((key) =>
|
|
121
|
+
await Promise.all(keys.map((key) => this.withKeyLock(key, async () => {
|
|
122
|
+
try {
|
|
123
|
+
await fs$1.unlink(this.getFullPath(key));
|
|
124
|
+
} catch (error) {
|
|
125
|
+
if (!error || error.code !== "ENOENT") throw error;
|
|
126
|
+
}
|
|
127
|
+
})));
|
|
115
128
|
}
|
|
116
129
|
/**
|
|
117
130
|
* Asynchronous generator that yields keys from the store. If a prefix is
|
|
@@ -120,7 +133,7 @@ var LocalFileStore = class extends BaseStore {
|
|
|
120
133
|
* @returns AsyncGenerator that yields keys from the store.
|
|
121
134
|
*/
|
|
122
135
|
async *yieldKeys(prefix) {
|
|
123
|
-
const allKeys = (await fs$1.readdir(this.rootPath)).
|
|
136
|
+
const allKeys = (await fs$1.readdir(this.rootPath)).filter((file) => file.endsWith(".txt")).map((file) => file.replace(/\.txt$/, ""));
|
|
124
137
|
for (const key of allKeys) if (prefix === void 0 || key.startsWith(prefix)) yield key;
|
|
125
138
|
}
|
|
126
139
|
/**
|
|
@@ -139,8 +152,59 @@ var LocalFileStore = class extends BaseStore {
|
|
|
139
152
|
throw new Error(`An error occurred creating directory at: ${rootPath}.\nError: ${JSON.stringify(error)}`);
|
|
140
153
|
}
|
|
141
154
|
}
|
|
155
|
+
try {
|
|
156
|
+
const entries = await fs$1.readdir(rootPath);
|
|
157
|
+
await Promise.all(entries.filter((file) => file.endsWith(".tmp")).map((tempFile) => fs$1.unlink(path$1.join(rootPath, tempFile)).catch(() => {})));
|
|
158
|
+
} catch {}
|
|
142
159
|
return new this({ rootPath });
|
|
143
160
|
}
|
|
161
|
+
/**
|
|
162
|
+
* Ensures calls for the same key run sequentially by chaining promises.
|
|
163
|
+
* @param key Key to serialize operations for.
|
|
164
|
+
* @param fn Async work to execute while the lock is held.
|
|
165
|
+
* @returns Promise resolving with the callback result once the lock releases.
|
|
166
|
+
*/
|
|
167
|
+
async withKeyLock(key, fn) {
|
|
168
|
+
const waitForPrevious = (this.keyLocks.get(key) ?? Promise.resolve()).catch(() => {});
|
|
169
|
+
let resolveCurrent;
|
|
170
|
+
const current = new Promise((resolve) => {
|
|
171
|
+
resolveCurrent = resolve;
|
|
172
|
+
});
|
|
173
|
+
const tail = waitForPrevious.then(() => current);
|
|
174
|
+
this.keyLocks.set(key, tail);
|
|
175
|
+
await waitForPrevious;
|
|
176
|
+
try {
|
|
177
|
+
return await fn();
|
|
178
|
+
} finally {
|
|
179
|
+
resolveCurrent?.();
|
|
180
|
+
if (this.keyLocks.get(key) === tail) this.keyLocks.delete(key);
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
/**
|
|
184
|
+
* Writes data to a temporary file before atomically renaming it into place.
|
|
185
|
+
* @param content Serialized value to persist.
|
|
186
|
+
* @param fullPath Destination path for the stored key.
|
|
187
|
+
*/
|
|
188
|
+
async writeFileAtomically(content, fullPath) {
|
|
189
|
+
const directory = path$1.dirname(fullPath);
|
|
190
|
+
await fs$1.mkdir(directory, { recursive: true });
|
|
191
|
+
const tempPath = `${fullPath}.${Date.now()}-${Math.random().toString(16).slice(2)}.tmp`;
|
|
192
|
+
try {
|
|
193
|
+
await fs$1.writeFile(tempPath, content);
|
|
194
|
+
try {
|
|
195
|
+
await fs$1.rename(tempPath, fullPath);
|
|
196
|
+
} catch (renameError) {
|
|
197
|
+
const code = renameError.code;
|
|
198
|
+
if (renameError && (code === "EPERM" || code === "EACCES")) {
|
|
199
|
+
await fs$1.writeFile(fullPath, content);
|
|
200
|
+
await fs$1.unlink(tempPath).catch(() => {});
|
|
201
|
+
} else throw renameError;
|
|
202
|
+
}
|
|
203
|
+
} catch (error) {
|
|
204
|
+
await fs$1.unlink(tempPath).catch(() => {});
|
|
205
|
+
throw error;
|
|
206
|
+
}
|
|
207
|
+
}
|
|
144
208
|
};
|
|
145
209
|
//#endregion
|
|
146
210
|
export { LocalFileStore, file_system_exports };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"file_system.js","names":["fs","path"],"sources":["../../src/storage/file_system.ts"],"sourcesContent":["import * as fs from \"node:fs/promises\";\nimport * as path from \"node:path\";\nimport { BaseStore } from \"@langchain/core/stores\";\n\n/**\n * File system implementation of the BaseStore using a dictionary. Used for\n * storing key-value pairs in the file system.\n * @example\n * ```typescript\n * const store = await LocalFileStore.fromPath(\"./messages\");\n * await store.mset(\n * Array.from({ length: 5 }).map((_, index) => [\n * `message:id:${index}`,\n * new TextEncoder().encode(\n * JSON.stringify(\n * index % 2 === 0\n * ? new AIMessage(\"ai stuff...\")\n * : new HumanMessage(\"human stuff...\"),\n * ),\n * ),\n * ]),\n * );\n * const retrievedMessages = await store.mget([\"message:id:0\", \"message:id:1\"]);\n * console.log(retrievedMessages.map((v) => new TextDecoder().decode(v)));\n * for await (const key of store.yieldKeys(\"message:id:\")) {\n * await store.mdelete([key]);\n * }\n * ```\n *\n * @security **Security Notice** This file store\n * can alter any text file in the provided directory and any subfolders.\n * Make sure that the path you specify when initializing the store is free\n * of other files.\n */\nexport class LocalFileStore extends BaseStore<string, Uint8Array> {\n lc_namespace = [\"langchain\", \"storage\"];\n\n rootPath: string;\n\n constructor(fields: { rootPath: string }) {\n super(fields);\n this.rootPath = fields.rootPath;\n }\n\n /**\n * Read and parse the file at the given path.\n * @param key The key to read the file for.\n * @returns Promise that resolves to the parsed file content.\n */\n private async getParsedFile(key: string): Promise<Uint8Array | undefined> {\n // Validate the key to prevent path traversal\n if (!/^[a-zA-Z0-9_\\-:.]+$/.test(key)) {\n throw new Error(\n \"Invalid key. Only alphanumeric characters, underscores, hyphens, colons, and periods are allowed.\"\n );\n }\n try {\n const fileContent = await fs.readFile(this.getFullPath(key));\n if (!fileContent) {\n return undefined;\n }\n return fileContent;\n // oxlint-disable-next-line @typescript-eslint/no-explicit-any\n } catch (e: any) {\n // File does not exist yet.\n if (\"code\" in e && e.code === \"ENOENT\") {\n return undefined;\n }\n throw new Error(\n `Error reading and parsing file at path: ${\n this.rootPath\n }.\\nError: ${JSON.stringify(e)}`\n );\n }\n }\n\n /**\n * Writes the given key-value pairs to the file at the given path.\n * @param fileContent An object with the key-value pairs to be written to the file.\n */\n private async setFileContent(content: Uint8Array, key: string) {\n try {\n await fs.writeFile(this.getFullPath(key), content);\n } catch (error) {\n throw new Error(\n `Error writing file at path: ${this.getFullPath(\n key\n )}.\\nError: ${JSON.stringify(error)}`\n );\n }\n }\n\n /**\n * Returns the full path of the file where the value of the given key is stored.\n * @param key the key to get the full path for\n */\n private getFullPath(key: string): string {\n try {\n const keyAsTxtFile = `${key}.txt`;\n\n // Validate the key to prevent path traversal\n if (!/^[a-zA-Z0-9_.\\-/]+$/.test(key)) {\n throw new Error(`Invalid characters in key: ${key}`);\n }\n\n const fullPath = path.resolve(this.rootPath, keyAsTxtFile);\n const commonPath = path.resolve(this.rootPath);\n\n if (!fullPath.startsWith(commonPath)) {\n throw new Error(\n `Invalid key: ${key}. Key should be relative to the root path. ` +\n `Root path: ${this.rootPath}, Full path: ${fullPath}`\n );\n }\n\n return fullPath;\n } catch (e) {\n throw new Error(\n `Error getting full path for key: ${key}.\\nError: ${String(e)}`\n );\n }\n }\n\n /**\n * Retrieves the values associated with the given keys from the store.\n * @param keys Keys to retrieve values for.\n * @returns Array of values associated with the given keys.\n */\n async mget(keys: string[]) {\n const values: (Uint8Array | undefined)[] = [];\n for (const key of keys) {\n const fileContent = await this.getParsedFile(key);\n values.push(fileContent);\n }\n return values;\n }\n\n /**\n * Sets the values for the given keys in the store.\n * @param keyValuePairs Array of key-value pairs to set in the store.\n * @returns Promise that resolves when all key-value pairs have been set.\n */\n async mset(keyValuePairs: [string, Uint8Array][]): Promise<void> {\n await Promise.all(\n keyValuePairs.map(([key, value]) => this.setFileContent(value, key))\n );\n }\n\n /**\n * Deletes the given keys and their associated values from the store.\n * @param keys Keys to delete from the store.\n * @returns Promise that resolves when all keys have been deleted.\n */\n async mdelete(keys: string[]): Promise<void> {\n await Promise.all(keys.map((key) => fs.unlink(this.getFullPath(key))));\n }\n\n /**\n * Asynchronous generator that yields keys from the store. If a prefix is\n * provided, it only yields keys that start with the prefix.\n * @param prefix Optional prefix to filter keys.\n * @returns AsyncGenerator that yields keys from the store.\n */\n async *yieldKeys(prefix?: string): AsyncGenerator<string> {\n const allFiles = await fs.readdir(this.rootPath);\n const allKeys = allFiles.map((file) => file.replace(\".txt\", \"\"));\n for (const key of allKeys) {\n if (prefix === undefined || key.startsWith(prefix)) {\n yield key;\n }\n }\n }\n\n /**\n * Static method for initializing the class.\n * Preforms a check to see if the directory exists, and if not, creates it.\n * @param path Path to the directory.\n * @returns Promise that resolves to an instance of the class.\n */\n static async fromPath(rootPath: string): Promise<LocalFileStore> {\n try {\n // Verifies the directory exists at the provided path, and that it is readable and writable.\n await fs.access(rootPath, fs.constants.R_OK | fs.constants.W_OK);\n } catch {\n try {\n // Directory does not exist, create it.\n await fs.mkdir(rootPath, { recursive: true });\n } catch (error) {\n throw new Error(\n `An error occurred creating directory at: ${rootPath}.\\nError: ${JSON.stringify(\n error\n )}`\n );\n }\n }\n\n return new this({ rootPath });\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAkCA,IAAa,iBAAb,cAAoC,UAA8B;CAChE,eAAe,CAAC,aAAa,UAAU;CAEvC;CAEA,YAAY,QAA8B;AACxC,QAAM,OAAO;AACb,OAAK,WAAW,OAAO;;;;;;;CAQzB,MAAc,cAAc,KAA8C;AAExE,MAAI,CAAC,sBAAsB,KAAK,IAAI,CAClC,OAAM,IAAI,MACR,oGACD;AAEH,MAAI;GACF,MAAM,cAAc,MAAMA,KAAG,SAAS,KAAK,YAAY,IAAI,CAAC;AAC5D,OAAI,CAAC,YACH;AAEF,UAAO;WAEA,GAAQ;AAEf,OAAI,UAAU,KAAK,EAAE,SAAS,SAC5B;AAEF,SAAM,IAAI,MACR,2CACE,KAAK,SACN,YAAY,KAAK,UAAU,EAAE,GAC/B;;;;;;;CAQL,MAAc,eAAe,SAAqB,KAAa;AAC7D,MAAI;AACF,SAAMA,KAAG,UAAU,KAAK,YAAY,IAAI,EAAE,QAAQ;WAC3C,OAAO;AACd,SAAM,IAAI,MACR,+BAA+B,KAAK,YAClC,IACD,CAAC,YAAY,KAAK,UAAU,MAAM,GACpC;;;;;;;CAQL,YAAoB,KAAqB;AACvC,MAAI;GACF,MAAM,eAAe,GAAG,IAAI;AAG5B,OAAI,CAAC,sBAAsB,KAAK,IAAI,CAClC,OAAM,IAAI,MAAM,8BAA8B,MAAM;GAGtD,MAAM,WAAWC,OAAK,QAAQ,KAAK,UAAU,aAAa;GAC1D,MAAM,aAAaA,OAAK,QAAQ,KAAK,SAAS;AAE9C,OAAI,CAAC,SAAS,WAAW,WAAW,CAClC,OAAM,IAAI,MACR,gBAAgB,IAAI,wDACJ,KAAK,SAAS,eAAe,WAC9C;AAGH,UAAO;WACA,GAAG;AACV,SAAM,IAAI,MACR,oCAAoC,IAAI,YAAY,OAAO,EAAE,GAC9D;;;;;;;;CASL,MAAM,KAAK,MAAgB;EACzB,MAAM,SAAqC,EAAE;AAC7C,OAAK,MAAM,OAAO,MAAM;GACtB,MAAM,cAAc,MAAM,KAAK,cAAc,IAAI;AACjD,UAAO,KAAK,YAAY;;AAE1B,SAAO;;;;;;;CAQT,MAAM,KAAK,eAAsD;AAC/D,QAAM,QAAQ,IACZ,cAAc,KAAK,CAAC,KAAK,WAAW,KAAK,eAAe,OAAO,IAAI,CAAC,CACrE;;;;;;;CAQH,MAAM,QAAQ,MAA+B;AAC3C,QAAM,QAAQ,IAAI,KAAK,KAAK,QAAQD,KAAG,OAAO,KAAK,YAAY,IAAI,CAAC,CAAC,CAAC;;;;;;;;CASxE,OAAO,UAAU,QAAyC;EAExD,MAAM,WADW,MAAMA,KAAG,QAAQ,KAAK,SAAS,EACvB,KAAK,SAAS,KAAK,QAAQ,QAAQ,GAAG,CAAC;AAChE,OAAK,MAAM,OAAO,QAChB,KAAI,WAAW,KAAA,KAAa,IAAI,WAAW,OAAO,CAChD,OAAM;;;;;;;;CAWZ,aAAa,SAAS,UAA2C;AAC/D,MAAI;AAEF,SAAMA,KAAG,OAAO,UAAUA,KAAG,UAAU,OAAOA,KAAG,UAAU,KAAK;UAC1D;AACN,OAAI;AAEF,UAAMA,KAAG,MAAM,UAAU,EAAE,WAAW,MAAM,CAAC;YACtC,OAAO;AACd,UAAM,IAAI,MACR,4CAA4C,SAAS,YAAY,KAAK,UACpE,MACD,GACF;;;AAIL,SAAO,IAAI,KAAK,EAAE,UAAU,CAAC"}
|
|
1
|
+
{"version":3,"file":"file_system.js","names":["fs","path"],"sources":["../../src/storage/file_system.ts"],"sourcesContent":["import * as fs from \"node:fs/promises\";\nimport * as path from \"node:path\";\nimport { BaseStore } from \"@langchain/core/stores\";\n\n/**\n * File system implementation of the BaseStore using a dictionary. Used for\n * storing key-value pairs in the file system.\n * @example\n * ```typescript\n * const store = await LocalFileStore.fromPath(\"./messages\");\n * await store.mset(\n * Array.from({ length: 5 }).map((_, index) => [\n * `message:id:${index}`,\n * new TextEncoder().encode(\n * JSON.stringify(\n * index % 2 === 0\n * ? new AIMessage(\"ai stuff...\")\n * : new HumanMessage(\"human stuff...\"),\n * ),\n * ),\n * ]),\n * );\n * const retrievedMessages = await store.mget([\"message:id:0\", \"message:id:1\"]);\n * console.log(retrievedMessages.map((v) => new TextDecoder().decode(v)));\n * for await (const key of store.yieldKeys(\"message:id:\")) {\n * await store.mdelete([key]);\n * }\n * ```\n *\n * @security **Security Notice** This file store\n * can alter any text file in the provided directory and any subfolders.\n * Make sure that the path you specify when initializing the store is free\n * of other files.\n */\nexport class LocalFileStore extends BaseStore<string, Uint8Array> {\n lc_namespace = [\"langchain\", \"storage\"];\n\n rootPath: string;\n\n private keyLocks: Map<string, Promise<void>> = new Map();\n\n constructor(fields: { rootPath: string }) {\n super(fields);\n this.rootPath = fields.rootPath;\n }\n\n /**\n * Read and parse the file at the given path.\n * @param key The key to read the file for.\n * @returns Promise that resolves to the parsed file content.\n */\n private async getParsedFile(key: string): Promise<Uint8Array | undefined> {\n // Validate the key to prevent path traversal\n if (!/^[a-zA-Z0-9_\\-:.]+$/.test(key)) {\n throw new Error(\n \"Invalid key. Only alphanumeric characters, underscores, hyphens, colons, and periods are allowed.\"\n );\n }\n try {\n const fileContent = await fs.readFile(this.getFullPath(key));\n if (!fileContent) {\n return undefined;\n }\n return fileContent;\n // oxlint-disable-next-line @typescript-eslint/no-explicit-any\n } catch (e: any) {\n // File does not exist yet.\n if (\"code\" in e && e.code === \"ENOENT\") {\n return undefined;\n }\n throw new Error(\n `Error reading and parsing file at path: ${\n this.rootPath\n }.\\nError: ${JSON.stringify(e)}`\n );\n }\n }\n\n /**\n * Writes the given key-value pairs to the file at the given path.\n * @param fileContent An object with the key-value pairs to be written to the file.\n */\n private async setFileContent(content: Uint8Array, key: string) {\n await this.withKeyLock(key, async () => {\n const fullPath = this.getFullPath(key);\n try {\n await this.writeFileAtomically(content, fullPath);\n } catch (error) {\n throw new Error(\n `Error writing file at path: ${fullPath}.\\nError: ${JSON.stringify(\n error\n )}`\n );\n }\n });\n }\n\n /**\n * Returns the full path of the file where the value of the given key is stored.\n * @param key the key to get the full path for\n */\n private getFullPath(key: string): string {\n try {\n const keyAsTxtFile = `${key}.txt`;\n\n // Validate the key to prevent path traversal\n if (!/^[a-zA-Z0-9_.\\-/]+$/.test(key)) {\n throw new Error(`Invalid characters in key: ${key}`);\n }\n\n const fullPath = path.resolve(this.rootPath, keyAsTxtFile);\n const commonPath = path.resolve(this.rootPath);\n\n if (!fullPath.startsWith(commonPath)) {\n throw new Error(\n `Invalid key: ${key}. Key should be relative to the root path. ` +\n `Root path: ${this.rootPath}, Full path: ${fullPath}`\n );\n }\n\n return fullPath;\n } catch (e) {\n throw new Error(\n `Error getting full path for key: ${key}.\\nError: ${String(e)}`\n );\n }\n }\n\n /**\n * Retrieves the values associated with the given keys from the store.\n * @param keys Keys to retrieve values for.\n * @returns Array of values associated with the given keys.\n */\n async mget(keys: string[]) {\n const values: (Uint8Array | undefined)[] = [];\n for (const key of keys) {\n const fileContent = await this.getParsedFile(key);\n values.push(fileContent);\n }\n return values;\n }\n\n /**\n * Sets the values for the given keys in the store.\n * The last value for duplicate keys will be used.\n * @param keyValuePairs Array of key-value pairs to set in the store.\n * @returns Promise that resolves when all key-value pairs have been set.\n */\n async mset(keyValuePairs: [string, Uint8Array][]): Promise<void> {\n const deduped = new Map<string, Uint8Array>();\n for (const [key, value] of keyValuePairs) {\n deduped.set(key, value);\n }\n\n await Promise.all(\n Array.from(deduped.entries(), ([key, value]) =>\n this.setFileContent(value, key)\n )\n );\n }\n\n /**\n * Deletes the given keys and their associated values from the store.\n * @param keys Keys to delete from the store.\n * @returns Promise that resolves when all keys have been deleted.\n */\n async mdelete(keys: string[]): Promise<void> {\n await Promise.all(\n keys.map((key) =>\n this.withKeyLock(key, async () => {\n try {\n await fs.unlink(this.getFullPath(key));\n } catch (error) {\n if (!error || (error as { code?: string }).code !== \"ENOENT\") {\n throw error;\n }\n }\n })\n )\n );\n }\n\n /**\n * Asynchronous generator that yields keys from the store. If a prefix is\n * provided, it only yields keys that start with the prefix.\n * @param prefix Optional prefix to filter keys.\n * @returns AsyncGenerator that yields keys from the store.\n */\n async *yieldKeys(prefix?: string): AsyncGenerator<string> {\n const allFiles: string[] = await fs.readdir(this.rootPath);\n const allKeys = allFiles\n .filter((file) => file.endsWith(\".txt\"))\n .map((file) => file.replace(/\\.txt$/, \"\"));\n for (const key of allKeys) {\n if (prefix === undefined || key.startsWith(prefix)) {\n yield key;\n }\n }\n }\n\n /**\n * Static method for initializing the class.\n * Preforms a check to see if the directory exists, and if not, creates it.\n * @param path Path to the directory.\n * @returns Promise that resolves to an instance of the class.\n */\n static async fromPath(rootPath: string): Promise<LocalFileStore> {\n try {\n // Verifies the directory exists at the provided path, and that it is readable and writable.\n await fs.access(rootPath, fs.constants.R_OK | fs.constants.W_OK);\n } catch {\n try {\n // Directory does not exist, create it.\n await fs.mkdir(rootPath, { recursive: true });\n } catch (error) {\n throw new Error(\n `An error occurred creating directory at: ${rootPath}.\\nError: ${JSON.stringify(\n error\n )}`\n );\n }\n }\n\n // Clean up orphaned temp files left by interrupted atomic writes.\n try {\n const entries = await fs.readdir(rootPath);\n await Promise.all(\n entries\n .filter((file) => file.endsWith(\".tmp\"))\n .map((tempFile) =>\n fs.unlink(path.join(rootPath, tempFile)).catch(() => {})\n )\n );\n } catch {\n // Ignore cleanup errors.\n }\n\n return new this({ rootPath });\n }\n\n /**\n * Ensures calls for the same key run sequentially by chaining promises.\n * @param key Key to serialize operations for.\n * @param fn Async work to execute while the lock is held.\n * @returns Promise resolving with the callback result once the lock releases.\n */\n private async withKeyLock<T>(key: string, fn: () => Promise<T>): Promise<T> {\n const previous = this.keyLocks.get(key) ?? Promise.resolve();\n const waitForPrevious = previous.catch(() => {});\n\n let resolveCurrent: (() => void) | undefined;\n const current = new Promise<void>((resolve) => {\n resolveCurrent = resolve;\n });\n\n const tail = waitForPrevious.then(() => current);\n this.keyLocks.set(key, tail);\n\n await waitForPrevious;\n try {\n return await fn();\n } finally {\n resolveCurrent?.();\n if (this.keyLocks.get(key) === tail) {\n this.keyLocks.delete(key);\n }\n }\n }\n\n /**\n * Writes data to a temporary file before atomically renaming it into place.\n * @param content Serialized value to persist.\n * @param fullPath Destination path for the stored key.\n */\n private async writeFileAtomically(content: Uint8Array, fullPath: string) {\n const directory = path.dirname(fullPath);\n await fs.mkdir(directory, { recursive: true });\n\n const tempPath = `${fullPath}.${Date.now()}-${Math.random()\n .toString(16)\n .slice(2)}.tmp`;\n\n try {\n await fs.writeFile(tempPath, content);\n\n try {\n await fs.rename(tempPath, fullPath);\n } catch (renameError) {\n const code = (renameError as { code?: string }).code;\n if (renameError && (code === \"EPERM\" || code === \"EACCES\")) {\n await fs.writeFile(fullPath, content);\n await fs.unlink(tempPath).catch(() => {});\n } else {\n throw renameError;\n }\n }\n } catch (error) {\n await fs.unlink(tempPath).catch(() => {});\n throw error;\n }\n }\n}\n"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@langchain/classic",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.31",
|
|
4
4
|
"description": "Old abstractions from LangChain.js",
|
|
5
5
|
"author": "LangChain",
|
|
6
6
|
"license": "MIT",
|
|
@@ -130,7 +130,7 @@
|
|
|
130
130
|
"@vitest/coverage-v8": "^3.2.4",
|
|
131
131
|
"@xata.io/client": "^0.30.1",
|
|
132
132
|
"@zilliz/milvus2-sdk-node": "^2.3.5",
|
|
133
|
-
"axios": "^1.
|
|
133
|
+
"axios": "^1.15.0",
|
|
134
134
|
"cheerio": "1.2.0",
|
|
135
135
|
"chromadb": "^1.5.3",
|
|
136
136
|
"cohere-ai": "^7.14.0",
|
|
@@ -168,26 +168,26 @@
|
|
|
168
168
|
"weaviate-client": "^3.12.0",
|
|
169
169
|
"zod-to-json-schema": "^3.25.2",
|
|
170
170
|
"@langchain/anthropic": "1.3.26",
|
|
171
|
-
"@langchain/aws": "1.3.
|
|
171
|
+
"@langchain/aws": "1.3.5",
|
|
172
172
|
"@langchain/cloudflare": "1.0.4",
|
|
173
|
-
"@langchain/core": "^1.1.
|
|
174
|
-
"@langchain/deepseek": "1.0.
|
|
175
|
-
"@langchain/fireworks": "0.1.
|
|
173
|
+
"@langchain/core": "^1.1.40",
|
|
174
|
+
"@langchain/deepseek": "1.0.24",
|
|
175
|
+
"@langchain/fireworks": "0.1.2",
|
|
176
176
|
"@langchain/google-cloud-sql-pg": "1.0.22",
|
|
177
|
-
"@langchain/google-
|
|
178
|
-
"@langchain/google-
|
|
179
|
-
"@langchain/google-vertexai": "2.1.
|
|
180
|
-
"@langchain/google-vertexai
|
|
177
|
+
"@langchain/google-genai": "2.1.27",
|
|
178
|
+
"@langchain/google-common": "2.1.27",
|
|
179
|
+
"@langchain/google-vertexai-web": "2.1.27",
|
|
180
|
+
"@langchain/google-vertexai": "2.1.27",
|
|
181
181
|
"@langchain/groq": "1.2.0",
|
|
182
182
|
"@langchain/mistralai": "1.0.7",
|
|
183
183
|
"@langchain/mongodb": "1.1.0",
|
|
184
|
+
"@langchain/redis": "1.1.1",
|
|
184
185
|
"@langchain/ollama": "1.2.6",
|
|
185
|
-
"@langchain/redis": "1.1.0",
|
|
186
186
|
"@langchain/textsplitters": "1.0.1",
|
|
187
|
-
"@langchain/xai": "1.3.
|
|
187
|
+
"@langchain/xai": "1.3.16",
|
|
188
188
|
"@langchain/tsconfig": "0.0.1",
|
|
189
|
-
"langchain": "1.
|
|
190
|
-
"
|
|
189
|
+
"@langchain/together-ai": "0.1.2",
|
|
190
|
+
"langchain": "1.3.3"
|
|
191
191
|
},
|
|
192
192
|
"peerDependencies": {
|
|
193
193
|
"@langchain/core": "^1.0.0",
|
|
@@ -214,7 +214,7 @@
|
|
|
214
214
|
"uuid": "^10.0.0",
|
|
215
215
|
"yaml": "^2.8.3",
|
|
216
216
|
"zod": "^3.25.76 || ^4",
|
|
217
|
-
"@langchain/openai": "1.4.
|
|
217
|
+
"@langchain/openai": "1.4.4",
|
|
218
218
|
"@langchain/textsplitters": "1.0.1"
|
|
219
219
|
},
|
|
220
220
|
"optionalDependencies": {
|