{"version":3,"file":"template.cjs","names":["addLangChainErrorFields"],"sources":["../../src/prompts/template.ts"],"sourcesContent":["import mustache from \"mustache\";\nimport { MessageContent } from \"../messages/index.js\";\nimport type { InputValues } from \"../utils/types/index.js\";\nimport { addLangChainErrorFields } from \"../errors/index.js\";\n\n// Use unescaped HTML, passed per render call so the shared mustache module\n// keeps escaping for unrelated callers in the same process.\n// https://github.com/janl/mustache.js?tab=readme-ov-file#variables\nconst MUSTACHE_RENDER_OPTIONS: mustache.RenderOptions = {\n  escape: (text) => text,\n};\n\n/**\n * Type that specifies the format of a template.\n */\nexport type TemplateFormat = \"f-string\" | \"mustache\";\n\n/**\n * Type that represents a node in a parsed format string. It can be either\n * a literal text or a variable name.\n */\nexport type ParsedTemplateNode =\n  | { type: \"literal\"; text: string }\n  | { type: \"variable\"; name: string };\n\n/**\n * Alias for `ParsedTemplateNode` since it is the same for\n * both f-string and mustache templates.\n */\nexport type ParsedFStringNode = ParsedTemplateNode;\n\nexport const parseFString = (template: string): ParsedTemplateNode[] => {\n  // Core logic replicated from internals of pythons built in Formatter class.\n  // https://github.com/python/cpython/blob/135ec7cefbaffd516b77362ad2b2ad1025af462e/Objects/stringlib/unicode_format.h#L700-L706\n  const chars = template.split(\"\");\n  const nodes: ParsedTemplateNode[] = [];\n\n  const nextBracket = (bracket: \"}\" | \"{\" | \"{}\", start: number) => {\n    for (let i = start; i < chars.length; i += 1) {\n      if (bracket.includes(chars[i])) {\n        return i;\n      }\n    }\n    return -1;\n  };\n\n  let i = 0;\n  while (i < chars.length) {\n    if (chars[i] === \"{\" && i + 1 < chars.length && chars[i + 1] === \"{\") {\n      nodes.push({ type: \"literal\", text: \"{\" });\n      i += 2;\n    } else if (\n      chars[i] === \"}\" &&\n      i + 1 < chars.length &&\n      chars[i + 1] === \"}\"\n    ) {\n      nodes.push({ type: \"literal\", text: \"}\" });\n      i += 2;\n    } else if (chars[i] === \"{\") {\n      const j = nextBracket(\"}\", i);\n      if (j < 0) {\n        throw new Error(\"Unclosed '{' in template.\");\n      }\n\n      nodes.push({\n        type: \"variable\",\n        name: chars.slice(i + 1, j).join(\"\"),\n      });\n      i = j + 1;\n    } else if (chars[i] === \"}\") {\n      throw new Error(\"Single '}' in template.\");\n    } else {\n      const next = nextBracket(\"{}\", i);\n      const text = (next < 0 ? chars.slice(i) : chars.slice(i, next)).join(\"\");\n      nodes.push({ type: \"literal\", text });\n      i = next < 0 ? chars.length : next;\n    }\n  }\n  return nodes;\n};\n\n/**\n * Convert the result of mustache.parse into an array of ParsedTemplateNode,\n * to make it compatible with other LangChain string parsing template formats.\n *\n * @param {mustache.TemplateSpans} template The result of parsing a mustache template with the mustache.js library.\n * @param {string[]} context Array of section variable names for nested context\n * @returns {ParsedTemplateNode[]}\n */\nconst mustacheTemplateToNodes = (\n  template: mustache.TemplateSpans,\n  context: string[] = []\n): ParsedTemplateNode[] => {\n  const nodes: ParsedTemplateNode[] = [];\n\n  for (const temp of template) {\n    if (temp[0] === \"name\") {\n      const name = temp[1].includes(\".\") ? temp[1].split(\".\")[0] : temp[1];\n      nodes.push({ type: \"variable\", name });\n    } else if ([\"#\", \"&\", \"^\", \">\"].includes(temp[0])) {\n      // # represents a section, \"&\" represents an unescaped variable.\n      // These should both be considered variables.\n      nodes.push({ type: \"variable\", name: temp[1] });\n\n      // If this is a section with nested content, recursively process it\n      if (temp[0] === \"#\" && temp.length > 4 && Array.isArray(temp[4])) {\n        const newContext = [...context, temp[1]];\n        const nestedNodes = mustacheTemplateToNodes(temp[4], newContext);\n        nodes.push(...nestedNodes);\n      }\n    } else {\n      nodes.push({ type: \"literal\", text: temp[1] });\n    }\n  }\n\n  return nodes;\n};\n\nexport const parseMustache = (template: string) => {\n  const parsed = mustache.parse(template);\n  return mustacheTemplateToNodes(parsed);\n};\n\nexport const interpolateFString = (template: string, values: InputValues) => {\n  return parseFString(template).reduce((res, node) => {\n    if (node.type === \"variable\") {\n      if (node.name in values) {\n        const stringValue =\n          typeof values[node.name] === \"string\"\n            ? values[node.name]\n            : JSON.stringify(values[node.name]);\n        return res + stringValue;\n      }\n      throw new Error(`(f-string) Missing value for input ${node.name}`);\n    }\n\n    return res + node.text;\n  }, \"\");\n};\n\nexport const interpolateMustache = (template: string, values: InputValues) => {\n  return mustache.render(template, values, undefined, MUSTACHE_RENDER_OPTIONS);\n};\n\n/**\n * Type that represents a function that takes a template string and a set\n * of input values, and returns a string where all variables in the\n * template have been replaced with their corresponding values.\n */\ntype Interpolator = (template: string, values: InputValues) => string;\n\n/**\n * Type that represents a function that takes a template string and\n * returns an array of `ParsedTemplateNode`.\n */\ntype Parser = (template: string) => ParsedTemplateNode[];\n\nexport const DEFAULT_FORMATTER_MAPPING: Record<TemplateFormat, Interpolator> = {\n  \"f-string\": interpolateFString,\n  mustache: interpolateMustache,\n};\n\nexport const DEFAULT_PARSER_MAPPING: Record<TemplateFormat, Parser> = {\n  \"f-string\": parseFString,\n  mustache: parseMustache,\n};\n\nexport const renderTemplate = (\n  template: string,\n  templateFormat: TemplateFormat,\n  inputValues: InputValues\n) => {\n  try {\n    return DEFAULT_FORMATTER_MAPPING[templateFormat](template, inputValues);\n  } catch (e) {\n    const error = addLangChainErrorFields(e, \"INVALID_PROMPT_INPUT\");\n    throw error;\n  }\n};\n\nexport const parseTemplate = (\n  template: string,\n  templateFormat: TemplateFormat\n) => DEFAULT_PARSER_MAPPING[templateFormat](template);\n\nexport const checkValidTemplate = (\n  template: MessageContent,\n  templateFormat: TemplateFormat,\n  inputVariables: string[]\n) => {\n  if (!(templateFormat in DEFAULT_FORMATTER_MAPPING)) {\n    const validFormats = Object.keys(DEFAULT_FORMATTER_MAPPING);\n    throw new Error(`Invalid template format. Got \\`${templateFormat}\\`;\n                         should be one of ${validFormats}`);\n  }\n  try {\n    // Build dummy inputs using Object.fromEntries to avoid prototype pollution\n    // from dynamic property assignment with user-provided keys\n    const dummyInputs: InputValues = Object.fromEntries(\n      inputVariables.map((v) => [v, \"foo\"])\n    );\n    if (Array.isArray(template)) {\n      template.forEach((message) => {\n        if (\n          message.type === \"text\" &&\n          \"text\" in message &&\n          typeof message.text === \"string\"\n        ) {\n          renderTemplate(message.text, templateFormat, dummyInputs);\n        } else if (message.type === \"image_url\") {\n          if (typeof message.image_url === \"string\") {\n            renderTemplate(message.image_url, templateFormat, dummyInputs);\n          } else if (\n            typeof message.image_url === \"object\" &&\n            message.image_url !== null &&\n            \"url\" in message.image_url &&\n            typeof message.image_url.url === \"string\"\n          ) {\n            const imageUrl = message.image_url.url;\n            renderTemplate(imageUrl, templateFormat, dummyInputs);\n          }\n        } else {\n          throw new Error(\n            `Invalid message template received. ${JSON.stringify(\n              message,\n              null,\n              2\n            )}`\n          );\n        }\n      });\n    } else {\n      renderTemplate(template, templateFormat, dummyInputs);\n    }\n    // oxlint-disable-next-line @typescript-eslint/no-explicit-any\n  } catch (e: any) {\n    throw new Error(`Invalid prompt schema: ${e.message}`);\n  }\n};\n"],"mappings":"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"}