{"version":3,"file":"serializable.cjs","names":["escapeIfNeeded","mapKeys","keyToJson"],"sources":["../../src/load/serializable.ts"],"sourcesContent":["import { type SerializedFields, keyToJson, mapKeys } from \"./map_keys.js\";\nimport { escapeIfNeeded } from \"./validation.js\";\n\nexport interface BaseSerialized<T extends string> {\n  lc: number;\n  type: T;\n  id: string[];\n  name?: string;\n  // oxlint-disable-next-line @typescript-eslint/no-explicit-any\n  graph?: Record<string, any>;\n}\n\nexport interface SerializedConstructor extends BaseSerialized<\"constructor\"> {\n  kwargs: SerializedFields;\n}\n\nexport interface SerializedSecret extends BaseSerialized<\"secret\"> {}\n\nexport interface SerializedNotImplemented extends BaseSerialized<\"not_implemented\"> {}\n\nexport type Serialized =\n  | SerializedConstructor\n  | SerializedSecret\n  | SerializedNotImplemented;\n\nfunction shallowCopy<T extends object>(obj: T): T {\n  return Array.isArray(obj) ? ([...obj] as T) : ({ ...obj } as T);\n}\n\nfunction replaceSecrets(\n  root: SerializedFields,\n  secretsMap: { [key: string]: string }\n): SerializedFields {\n  const result = shallowCopy(root);\n  for (const [path, secretId] of Object.entries(secretsMap)) {\n    const [last, ...partsReverse] = path.split(\".\").reverse();\n    // oxlint-disable-next-line @typescript-eslint/no-explicit-any\n    let current: any = result;\n    for (const part of partsReverse.reverse()) {\n      if (current[part] === undefined) {\n        break;\n      }\n      current[part] = shallowCopy(current[part]);\n      current = current[part];\n    }\n    if (current[last] !== undefined) {\n      current[last] = {\n        lc: 1,\n        type: \"secret\",\n        id: [secretId],\n      };\n    }\n  }\n  return result;\n}\n\n/**\n * Get a unique name for the module, rather than parent class implementations.\n * Should not be subclassed, subclass lc_name above instead.\n */\nexport function get_lc_unique_name(\n  serializableClass: typeof Serializable\n): string {\n  // \"super\" here would refer to the parent class of Serializable,\n  // when we want the parent class of the module actually calling this method.\n  const parentClass = Object.getPrototypeOf(serializableClass);\n  const lcNameIsSubclassed =\n    typeof serializableClass.lc_name === \"function\" &&\n    (typeof parentClass.lc_name !== \"function\" ||\n      serializableClass.lc_name() !== parentClass.lc_name());\n  if (lcNameIsSubclassed) {\n    return serializableClass.lc_name();\n  } else {\n    return serializableClass.name;\n  }\n}\n\n/**\n * Interface for objects that can be serialized.\n * This is a duck-typed interface to avoid circular imports.\n */\nexport interface SerializableLike {\n  lc_serializable: boolean;\n  lc_secrets?: Record<string, string>;\n  toJSON(): {\n    lc: number;\n    type: string;\n    id: string[];\n    kwargs?: Record<string, unknown>;\n  };\n}\n\nexport interface SerializableInterface {\n  get lc_id(): string[];\n}\n\nexport abstract class Serializable implements SerializableInterface {\n  lc_serializable = false;\n\n  lc_kwargs: SerializedFields;\n\n  /**\n   * A path to the module that contains the class, eg. [\"langchain\", \"llms\"]\n   * Usually should be the same as the entrypoint the class is exported from.\n   */\n  abstract lc_namespace: string[];\n\n  /**\n   * The name of the serializable. Override to provide an alias or\n   * to preserve the serialized module name in minified environments.\n   *\n   * Implemented as a static method to support loading logic.\n   */\n  static lc_name(): string {\n    return this.name;\n  }\n\n  /**\n   * The final serialized identifier for the module.\n   */\n  get lc_id(): string[] {\n    return [\n      ...this.lc_namespace,\n      get_lc_unique_name(this.constructor as typeof Serializable),\n    ];\n  }\n\n  /**\n   * A map of secrets, which will be omitted from serialization.\n   * Keys are paths to the secret in constructor args, e.g. \"foo.bar.baz\".\n   * Values are the secret ids, which will be used when deserializing.\n   */\n  get lc_secrets(): { [key: string]: string } | undefined {\n    return undefined;\n  }\n\n  /**\n   * A map of additional attributes to merge with constructor args.\n   * Keys are the attribute names, e.g. \"foo\".\n   * Values are the attribute values, which will be serialized.\n   * These attributes need to be accepted by the constructor as arguments.\n   */\n  get lc_attributes(): SerializedFields | undefined {\n    return undefined;\n  }\n\n  /**\n   * A map of aliases for constructor args.\n   * Keys are the attribute names, e.g. \"foo\".\n   * Values are the alias that will replace the key in serialization.\n   * This is used to eg. make argument names match Python.\n   */\n  get lc_aliases(): { [key: string]: string } | undefined {\n    return undefined;\n  }\n\n  /**\n   * A manual list of keys that should be serialized.\n   * If not overridden, all fields passed into the constructor will be serialized.\n   */\n  get lc_serializable_keys(): string[] | undefined {\n    return undefined;\n  }\n\n  constructor(kwargs?: SerializedFields, ..._args: never[]) {\n    if (this.lc_serializable_keys !== undefined) {\n      this.lc_kwargs = Object.fromEntries(\n        Object.entries(kwargs || {}).filter(([key]) =>\n          this.lc_serializable_keys?.includes(key)\n        )\n      );\n    } else {\n      this.lc_kwargs = kwargs ?? {};\n    }\n  }\n\n  toJSON(): Serialized {\n    if (!this.lc_serializable) {\n      return this.toJSONNotImplemented();\n    }\n    if (\n      // oxlint-disable-next-line no-instanceof/no-instanceof\n      this.lc_kwargs instanceof Serializable ||\n      typeof this.lc_kwargs !== \"object\" ||\n      Array.isArray(this.lc_kwargs)\n    ) {\n      // We do not support serialization of classes with arg not a POJO\n      // I'm aware the check above isn't as strict as it could be\n      return this.toJSONNotImplemented();\n    }\n\n    const aliases: { [key: string]: string } = {};\n    const secrets: { [key: string]: string } = {};\n    const kwargs = Object.keys(this.lc_kwargs).reduce((acc, key) => {\n      acc[key] = key in this ? this[key as keyof this] : this.lc_kwargs[key];\n      return acc;\n    }, {} as SerializedFields);\n    // get secrets, attributes and aliases from all superclasses\n    for (\n      let current = Object.getPrototypeOf(this);\n      current;\n      current = Object.getPrototypeOf(current)\n    ) {\n      Object.assign(aliases, Reflect.get(current, \"lc_aliases\", this));\n      Object.assign(secrets, Reflect.get(current, \"lc_secrets\", this));\n      Object.assign(kwargs, Reflect.get(current, \"lc_attributes\", this));\n    }\n\n    // include all secrets used, even if not in kwargs,\n    // will be replaced with sentinel value in replaceSecrets\n    Object.keys(secrets).forEach((keyPath) => {\n      // oxlint-disable-next-line @typescript-eslint/no-explicit-any\n      let read: any = this;\n\n      // oxlint-disable-next-line @typescript-eslint/no-explicit-any\n      let write: any = kwargs;\n\n      const [last, ...partsReverse] = keyPath.split(\".\").reverse();\n      for (const key of partsReverse.reverse()) {\n        if (!(key in read) || read[key] === undefined) return;\n        if (!(key in write) || write[key] === undefined) {\n          if (typeof read[key] === \"object\" && read[key] != null) {\n            write[key] = {};\n          } else if (Array.isArray(read[key])) {\n            write[key] = [];\n          }\n        }\n\n        read = read[key];\n        write = write[key];\n      }\n\n      if (last in read && read[last] !== undefined) {\n        write[last] = write[last] || read[last];\n      }\n    });\n\n    const escapedKwargs: SerializedFields = {};\n    // Track current path to detect circular references (but not shared references)\n    // The Serializable object itself stays in the path to detect self-references in kwargs\n    const pathSet = new WeakSet<object>();\n    pathSet.add(this);\n    for (const [key, value] of Object.entries(kwargs)) {\n      escapedKwargs[key] = escapeIfNeeded(value, pathSet);\n    }\n\n    // Now add secret markers - these are added AFTER escaping so they won't be escaped\n    const kwargsWithSecrets = Object.keys(secrets).length\n      ? replaceSecrets(escapedKwargs, secrets)\n      : escapedKwargs;\n\n    // Finally transform keys to JSON format\n    const processedKwargs = mapKeys(kwargsWithSecrets, keyToJson, aliases);\n\n    return {\n      lc: 1,\n      type: \"constructor\",\n      id: this.lc_id,\n      kwargs: processedKwargs,\n    };\n  }\n\n  toJSONNotImplemented(): SerializedNotImplemented {\n    return {\n      lc: 1,\n      type: \"not_implemented\",\n      id: this.lc_id,\n    };\n  }\n}\n"],"mappings":"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"}