{"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":";;;;;;;;;AAyBA,SAAS,YAA8B,KAAW;AAChD,QAAO,MAAM,QAAQ,IAAI,GAAI,CAAC,GAAG,IAAI,GAAU,EAAE,GAAG,KAAK;;AAG3D,SAAS,eACP,MACA,YACkB;CAClB,MAAM,SAAS,YAAY,KAAK;AAChC,MAAK,MAAM,CAAC,MAAM,aAAa,OAAO,QAAQ,WAAW,EAAE;EACzD,MAAM,CAAC,MAAM,GAAG,gBAAgB,KAAK,MAAM,IAAI,CAAC,SAAS;EAEzD,IAAI,UAAe;AACnB,OAAK,MAAM,QAAQ,aAAa,SAAS,EAAE;AACzC,OAAI,QAAQ,UAAU,KAAA,EACpB;AAEF,WAAQ,QAAQ,YAAY,QAAQ,MAAM;AAC1C,aAAU,QAAQ;;AAEpB,MAAI,QAAQ,UAAU,KAAA,EACpB,SAAQ,QAAQ;GACd,IAAI;GACJ,MAAM;GACN,IAAI,CAAC,SAAS;GACf;;AAGL,QAAO;;;;;;AAOT,SAAgB,mBACd,mBACQ;CAGR,MAAM,cAAc,OAAO,eAAe,kBAAkB;AAK5D,KAHE,OAAO,kBAAkB,YAAY,eACpC,OAAO,YAAY,YAAY,cAC9B,kBAAkB,SAAS,KAAK,YAAY,SAAS,EAEvD,QAAO,kBAAkB,SAAS;KAElC,QAAO,kBAAkB;;AAuB7B,IAAsB,eAAtB,MAAsB,aAA8C;CAClE,kBAAkB;CAElB;;;;;;;CAcA,OAAO,UAAkB;AACvB,SAAO,KAAK;;;;;CAMd,IAAI,QAAkB;AACpB,SAAO,CACL,GAAG,KAAK,cACR,mBAAmB,KAAK,YAAmC,CAC5D;;;;;;;CAQH,IAAI,aAAoD;;;;;;;CAUxD,IAAI,gBAA8C;;;;;;;CAUlD,IAAI,aAAoD;;;;;CAQxD,IAAI,uBAA6C;CAIjD,YAAY,QAA2B,GAAG,OAAgB;AACxD,MAAI,KAAK,yBAAyB,KAAA,EAChC,MAAK,YAAY,OAAO,YACtB,OAAO,QAAQ,UAAU,EAAE,CAAC,CAAC,QAAQ,CAAC,SACpC,KAAK,sBAAsB,SAAS,IAAI,CACzC,CACF;MAED,MAAK,YAAY,UAAU,EAAE;;CAIjC,SAAqB;AACnB,MAAI,CAAC,KAAK,gBACR,QAAO,KAAK,sBAAsB;AAEpC,MAEE,KAAK,qBAAqB,gBAC1B,OAAO,KAAK,cAAc,YAC1B,MAAM,QAAQ,KAAK,UAAU,CAI7B,QAAO,KAAK,sBAAsB;EAGpC,MAAM,UAAqC,EAAE;EAC7C,MAAM,UAAqC,EAAE;EAC7C,MAAM,SAAS,OAAO,KAAK,KAAK,UAAU,CAAC,QAAQ,KAAK,QAAQ;AAC9D,OAAI,OAAO,OAAO,OAAO,KAAK,OAAqB,KAAK,UAAU;AAClE,UAAO;KACN,EAAE,CAAqB;AAE1B,OACE,IAAI,UAAU,OAAO,eAAe,KAAK,EACzC,SACA,UAAU,OAAO,eAAe,QAAQ,EACxC;AACA,UAAO,OAAO,SAAS,QAAQ,IAAI,SAAS,cAAc,KAAK,CAAC;AAChE,UAAO,OAAO,SAAS,QAAQ,IAAI,SAAS,cAAc,KAAK,CAAC;AAChE,UAAO,OAAO,QAAQ,QAAQ,IAAI,SAAS,iBAAiB,KAAK,CAAC;;AAKpE,SAAO,KAAK,QAAQ,CAAC,SAAS,YAAY;GAExC,IAAI,OAAY;GAGhB,IAAI,QAAa;GAEjB,MAAM,CAAC,MAAM,GAAG,gBAAgB,QAAQ,MAAM,IAAI,CAAC,SAAS;AAC5D,QAAK,MAAM,OAAO,aAAa,SAAS,EAAE;AACxC,QAAI,EAAE,OAAO,SAAS,KAAK,SAAS,KAAA,EAAW;AAC/C,QAAI,EAAE,OAAO,UAAU,MAAM,SAAS,KAAA;SAChC,OAAO,KAAK,SAAS,YAAY,KAAK,QAAQ,KAChD,OAAM,OAAO,EAAE;cACN,MAAM,QAAQ,KAAK,KAAK,CACjC,OAAM,OAAO,EAAE;;AAInB,WAAO,KAAK;AACZ,YAAQ,MAAM;;AAGhB,OAAI,QAAQ,QAAQ,KAAK,UAAU,KAAA,EACjC,OAAM,QAAQ,MAAM,SAAS,KAAK;IAEpC;EAEF,MAAM,gBAAkC,EAAE;EAG1C,MAAM,0BAAU,IAAI,SAAiB;AACrC,UAAQ,IAAI,KAAK;AACjB,OAAK,MAAM,CAAC,KAAK,UAAU,OAAO,QAAQ,OAAO,CAC/C,eAAc,OAAOA,mBAAAA,eAAe,OAAO,QAAQ;EASrD,MAAM,kBAAkBC,iBAAAA,QALE,OAAO,KAAK,QAAQ,CAAC,SAC3C,eAAe,eAAe,QAAQ,GACtC,eAG+CC,iBAAAA,WAAW,QAAQ;AAEtE,SAAO;GACL,IAAI;GACJ,MAAM;GACN,IAAI,KAAK;GACT,QAAQ;GACT;;CAGH,uBAAiD;AAC/C,SAAO;GACL,IAAI;GACJ,MAAM;GACN,IAAI,KAAK;GACV"}