UNPKG

@langchain/core

Version:
1 lines 19.6 kB
{"version":3,"file":"index.cjs","names":["constructor: typeof Serializable","aliases: { [key: string]: string }","value: unknown","isEscapedObject","unescapeValue","getEnvironmentVariable","coreImportMap","module:\n | (typeof importMaps)[\"langchain_core\"][keyof (typeof importMaps)[\"langchain_core\"]]\n | (typeof importMaps)[\"langchain\"][keyof (typeof importMaps)[\"langchain\"]]\n | OptionalImportMap[keyof OptionalImportMap]\n | null","defaultOptionalImportEntrypoints","optionalImportEntrypoints","module","finalImportMap:\n | (typeof importMaps)[\"langchain\"]\n | (typeof importMaps)[\"langchain_core\"]","importMapKey: string","get_lc_unique_name","mapKeys","keyFromJson","result: Record<string, unknown>","text: string","options?: LoadOptions","context: ReviverContext"],"sources":["../../src/load/index.ts"],"sourcesContent":["/**\n * Load LangChain objects from JSON strings or objects.\n *\n * ## How it works\n *\n * Each `Serializable` LangChain object has a unique identifier (its \"class path\"),\n * which is a list of strings representing the module path and class name. For example:\n *\n * - `AIMessage` -> `[\"langchain_core\", \"messages\", \"ai\", \"AIMessage\"]`\n * - `ChatPromptTemplate` -> `[\"langchain_core\", \"prompts\", \"chat\", \"ChatPromptTemplate\"]`\n *\n * When deserializing, the class path is validated against supported namespaces.\n *\n * ## Security model\n *\n * The `secretsFromEnv` parameter controls whether secrets can be loaded from environment\n * variables:\n *\n * - `false` (default): Secrets must be provided in `secretsMap`. If a secret is not\n * found, `null` is returned instead of loading from environment variables.\n * - `true`: If a secret is not found in `secretsMap`, it will be loaded from\n * environment variables. Use this only in trusted environments.\n *\n * ### Injection protection (escape-based)\n *\n * During serialization, plain objects that contain an `'lc'` key are escaped by wrapping\n * them: `{\"__lc_escaped__\": {...}}`. During deserialization, escaped objects are unwrapped\n * and returned as plain objects, NOT instantiated as LC objects.\n *\n * This is an allowlist approach: only objects explicitly produced by\n * `Serializable.toJSON()` (which are NOT escaped) are treated as LC objects;\n * everything else is user data.\n *\n * @module\n */\n\nimport {\n Serializable,\n SerializedConstructor,\n SerializedNotImplemented,\n SerializedSecret,\n get_lc_unique_name,\n} from \"./serializable.js\";\nimport { optionalImportEntrypoints as defaultOptionalImportEntrypoints } from \"./import_constants.js\";\nimport * as coreImportMap from \"./import_map.js\";\nimport type { OptionalImportMap, SecretMap } from \"./import_type.js\";\nimport { type SerializedFields, keyFromJson, mapKeys } from \"./map_keys.js\";\nimport { getEnvironmentVariable } from \"../utils/env.js\";\nimport { isEscapedObject, unescapeValue } from \"./validation.js\";\n\n/**\n * Options for loading serialized LangChain objects.\n *\n * @remarks\n * **Security considerations:**\n *\n * Deserialization can instantiate arbitrary classes from the allowed namespaces.\n * When loading untrusted data, be aware that:\n *\n * 1. **`secretsFromEnv`**: Defaults to `false`. Setting to `true` allows the\n * deserializer to read environment variables, which could leak secrets if\n * the serialized data contains malicious secret references.\n *\n * 2. **`importMap` / `optionalImportsMap`**: These allow extending which classes\n * can be instantiated. Never populate these from user input. Only include\n * modules you explicitly trust.\n *\n * 3. **Class instantiation**: Allowed classes will have their constructors called\n * with the deserialized kwargs. If a class performs side effects in its\n * constructor (network calls, file I/O, etc.), those will execute.\n */\nexport interface LoadOptions {\n /**\n * A map of secrets to load. Keys are secret identifiers, values are the secret values.\n *\n * If a secret is not found in this map and `secretsFromEnv` is `false`, an error is\n * thrown. If `secretsFromEnv` is `true`, the secret will be loaded from environment\n * variables (if not found there either, an error is thrown).\n */\n secretsMap?: SecretMap;\n\n /**\n * Whether to load secrets from environment variables when not found in `secretsMap`.\n *\n * @default false\n *\n * @remarks\n * **Security warning:** Setting this to `true` allows the deserializer to read\n * environment variables, which could be a security risk if the serialized data\n * is not trusted. Only set this to `true` when deserializing data from trusted\n * sources (e.g., your own database, not user input).\n */\n secretsFromEnv?: boolean;\n\n /**\n * A map of optional imports. Keys are namespace paths (e.g., \"langchain_community/llms\"),\n * values are the imported modules.\n *\n * @remarks\n * **Security warning:** This extends which classes can be instantiated during\n * deserialization. Never populate this map with values derived from user input.\n * Only include modules that you explicitly trust and have reviewed.\n *\n * Classes in these modules can be instantiated with attacker-controlled kwargs\n * if the serialized data is untrusted.\n */\n optionalImportsMap?: OptionalImportMap;\n\n /**\n * Additional optional import entrypoints to allow beyond the defaults.\n *\n * @remarks\n * **Security warning:** This extends which namespace paths are considered valid\n * for deserialization. Never populate this array with values derived from user\n * input. Each entrypoint you add expands the attack surface for deserialization.\n */\n optionalImportEntrypoints?: string[];\n\n /**\n * Additional import map for the \"langchain\" namespace.\n *\n * @remarks\n * **Security warning:** This extends which classes can be instantiated during\n * deserialization. Never populate this map with values derived from user input.\n * Only include modules that you explicitly trust and have reviewed.\n *\n * Any class exposed through this map can be instantiated with attacker-controlled\n * kwargs if the serialized data is untrusted.\n */\n importMap?: Record<string, unknown>;\n\n /**\n * Maximum recursion depth allowed during deserialization.\n *\n * @default 50\n *\n * @remarks\n * This limit protects against denial-of-service attacks using deeply nested\n * JSON structures that could cause stack overflow. If your legitimate data\n * requires deeper nesting, you can increase this limit.\n */\n maxDepth?: number;\n}\n\n/**\n * Default maximum recursion depth for deserialization.\n * This provides protection against DoS attacks via deeply nested structures.\n */\nconst DEFAULT_MAX_DEPTH = 50;\n\nfunction combineAliasesAndInvert(constructor: typeof Serializable) {\n const aliases: { [key: string]: string } = {};\n for (\n let current = constructor;\n current && current.prototype;\n current = Object.getPrototypeOf(current)\n ) {\n Object.assign(aliases, Reflect.get(current.prototype, \"lc_aliases\"));\n }\n return Object.entries(aliases).reduce((acc, [key, value]) => {\n acc[value] = key;\n return acc;\n }, {} as Record<string, string>);\n}\n\ninterface ReviverContext {\n optionalImportsMap: OptionalImportMap;\n optionalImportEntrypoints: string[];\n secretsMap: SecretMap;\n secretsFromEnv: boolean;\n importMap: Record<string, unknown>;\n path: string[];\n depth: number;\n maxDepth: number;\n}\n\n/**\n * Recursively revive a value, handling escape markers and LC objects.\n *\n * This function handles:\n * 1. Escaped dicts - unwrapped and returned as plain objects\n * 2. LC secret objects - resolved from secretsMap or env\n * 3. LC constructor objects - instantiated\n * 4. Regular objects/arrays - recursed into\n */\nasync function reviver(this: ReviverContext, value: unknown): Promise<unknown> {\n const {\n optionalImportsMap,\n optionalImportEntrypoints,\n importMap,\n secretsMap,\n secretsFromEnv,\n path,\n depth,\n maxDepth,\n } = this;\n const pathStr = path.join(\".\");\n\n // Check recursion depth to prevent DoS via deeply nested structures\n if (depth > maxDepth) {\n throw new Error(\n `Maximum recursion depth (${maxDepth}) exceeded during deserialization. ` +\n `This may indicate a malicious payload or you may need to increase maxDepth.`\n );\n }\n\n // If not an object, return as-is\n if (typeof value !== \"object\" || value == null) {\n return value;\n }\n\n // Handle arrays - recurse into elements\n if (Array.isArray(value)) {\n return Promise.all(\n value.map((v, i) =>\n reviver.call({ ...this, path: [...path, `${i}`], depth: depth + 1 }, v)\n )\n );\n }\n\n // It's an object - check for escape marker FIRST\n const record = value as Record<string, unknown>;\n if (isEscapedObject(record)) {\n // This is an escaped user object - unwrap and return as-is (no LC processing)\n return unescapeValue(record);\n }\n\n // Check for LC secret object\n if (\n \"lc\" in record &&\n \"type\" in record &&\n \"id\" in record &&\n record.lc === 1 &&\n record.type === \"secret\"\n ) {\n const serialized = record as unknown as SerializedSecret;\n const [key] = serialized.id;\n if (key in secretsMap) {\n return secretsMap[key as keyof SecretMap];\n } else if (secretsFromEnv) {\n const secretValueInEnv = getEnvironmentVariable(key);\n if (secretValueInEnv) {\n return secretValueInEnv;\n }\n }\n throw new Error(`Missing secret \"${key}\" at ${pathStr}`);\n }\n\n // Check for LC not_implemented object\n if (\n \"lc\" in record &&\n \"type\" in record &&\n \"id\" in record &&\n record.lc === 1 &&\n record.type === \"not_implemented\"\n ) {\n const serialized = record as unknown as SerializedNotImplemented;\n const str = JSON.stringify(serialized);\n throw new Error(\n `Trying to load an object that doesn't implement serialization: ${pathStr} -> ${str}`\n );\n }\n\n // Check for LC constructor object\n if (\n \"lc\" in record &&\n \"type\" in record &&\n \"id\" in record &&\n \"kwargs\" in record &&\n record.lc === 1 &&\n record.type === \"constructor\"\n ) {\n const serialized = record as unknown as SerializedConstructor;\n const str = JSON.stringify(serialized);\n const [name, ...namespaceReverse] = serialized.id.slice().reverse();\n const namespace = namespaceReverse.reverse();\n const importMaps = { langchain_core: coreImportMap, langchain: importMap };\n\n let module:\n | (typeof importMaps)[\"langchain_core\"][keyof (typeof importMaps)[\"langchain_core\"]]\n | (typeof importMaps)[\"langchain\"][keyof (typeof importMaps)[\"langchain\"]]\n | OptionalImportMap[keyof OptionalImportMap]\n | null = null;\n\n const optionalImportNamespaceAliases = [namespace.join(\"/\")];\n if (namespace[0] === \"langchain_community\") {\n optionalImportNamespaceAliases.push(\n [\"langchain\", ...namespace.slice(1)].join(\"/\")\n );\n }\n const matchingNamespaceAlias = optionalImportNamespaceAliases.find(\n (alias) => alias in optionalImportsMap\n );\n if (\n defaultOptionalImportEntrypoints\n .concat(optionalImportEntrypoints)\n .includes(namespace.join(\"/\")) ||\n matchingNamespaceAlias\n ) {\n if (matchingNamespaceAlias !== undefined) {\n module = await optionalImportsMap[\n matchingNamespaceAlias as keyof typeof optionalImportsMap\n ];\n } else {\n throw new Error(\n `Missing key \"${namespace.join(\n \"/\"\n )}\" for ${pathStr} in load(optionalImportsMap={})`\n );\n }\n } else {\n let finalImportMap:\n | (typeof importMaps)[\"langchain\"]\n | (typeof importMaps)[\"langchain_core\"];\n // Currently, we only support langchain and langchain_core imports.\n if (namespace[0] === \"langchain\" || namespace[0] === \"langchain_core\") {\n finalImportMap = importMaps[namespace[0]];\n namespace.shift();\n } else {\n throw new Error(`Invalid namespace: ${pathStr} -> ${str}`);\n }\n\n // The root namespace \"langchain\" is not a valid import.\n if (namespace.length === 0) {\n throw new Error(`Invalid namespace: ${pathStr} -> ${str}`);\n }\n\n // Find the longest matching namespace.\n let importMapKey: string;\n do {\n importMapKey = namespace.join(\"__\");\n if (importMapKey in finalImportMap) {\n break;\n } else {\n namespace.pop();\n }\n } while (namespace.length > 0);\n\n // If no matching namespace is found, throw an error.\n if (importMapKey in finalImportMap) {\n module = finalImportMap[importMapKey as keyof typeof finalImportMap];\n }\n }\n\n if (typeof module !== \"object\" || module === null) {\n throw new Error(`Invalid namespace: ${pathStr} -> ${str}`);\n }\n\n // Extract the builder from the import map.\n const builder =\n // look for a named export with the same name as the class\n module[name as keyof typeof module] ??\n // look for an export with a lc_name property matching the class name\n // this is necessary for classes that are minified\n Object.values(module).find(\n (v) =>\n typeof v === \"function\" &&\n get_lc_unique_name(v as typeof Serializable) === name\n );\n if (typeof builder !== \"function\") {\n throw new Error(`Invalid identifer: ${pathStr} -> ${str}`);\n }\n\n // Recurse on the arguments, which may be serialized objects themselves\n const kwargs = await reviver.call(\n { ...this, path: [...path, \"kwargs\"], depth: depth + 1 },\n serialized.kwargs\n );\n\n // Construct the object\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const instance = new (builder as any)(\n mapKeys(\n kwargs as SerializedFields,\n keyFromJson,\n combineAliasesAndInvert(builder)\n )\n );\n\n // Minification in severless/edge runtimes will mange the\n // name of classes presented in traces. As the names in import map\n // are present as-is even with minification, use these names instead\n Object.defineProperty(instance.constructor, \"name\", { value: name });\n\n return instance;\n }\n\n // Regular object - recurse into values\n const result: Record<string, unknown> = {};\n for (const [key, val] of Object.entries(record)) {\n result[key] = await reviver.call(\n { ...this, path: [...path, key], depth: depth + 1 },\n val\n );\n }\n return result;\n}\n\n/**\n * Load a LangChain object from a JSON string.\n *\n * @param text - The JSON string to parse and load.\n * @param options - Options for loading.\n * @returns The loaded LangChain object.\n *\n * @example\n * ```typescript\n * import { load } from \"@langchain/core/load\";\n * import { AIMessage } from \"@langchain/core/messages\";\n *\n * // Basic usage - secrets must be provided explicitly\n * const msg = await load<AIMessage>(jsonString);\n *\n * // With secrets from a map\n * const msg = await load<AIMessage>(jsonString, {\n * secretsMap: { OPENAI_API_KEY: \"sk-...\" }\n * });\n *\n * // Allow loading secrets from environment (use with caution)\n * const msg = await load<AIMessage>(jsonString, {\n * secretsFromEnv: true\n * });\n * ```\n */\nexport async function load<T>(text: string, options?: LoadOptions): Promise<T> {\n const json = JSON.parse(text);\n\n const context: ReviverContext = {\n optionalImportsMap: options?.optionalImportsMap ?? {},\n optionalImportEntrypoints: options?.optionalImportEntrypoints ?? [],\n secretsMap: options?.secretsMap ?? {},\n secretsFromEnv: options?.secretsFromEnv ?? false,\n importMap: options?.importMap ?? {},\n path: [\"$\"],\n depth: 0,\n maxDepth: options?.maxDepth ?? DEFAULT_MAX_DEPTH,\n };\n\n return reviver.call(context, json) as Promise<T>;\n}\n"],"mappings":"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"}