{"version":3,"file":"text_comparison.cjs","names":[],"sources":["../../../src/batteries/tools/text_comparison/index.ts"],"sourcesContent":["/**\n * Pre-constructed tools for text diffs, similarity checks, and edit-distance comparisons.\n *\n * @module @nhtio/adk/batteries/tools/text_comparison\n *\n * @remarks\n * Pre-constructed bundled tools for the `text_comparison` category. Import individually, the whole\n * category, or import every tool via `@nhtio/adk/batteries`.\n */\n\nimport { Tool } from '@nhtio/adk/common'\nimport { diffLines, diffWords } from 'diff'\nimport { validator } from '@nhtio/validation'\nimport { distance } from 'fastest-levenshtein'\n\n/**\n * Compare two texts and emit a unified-style diff plus an additions/removals summary.\n *\n * @remarks\n * `mode: 'lines'` (default) compares line-by-line; `mode: 'words'` compares word-by-word. The\n * output is clipped at 60 diff lines — the suffix `… (N more lines truncated)` is appended if\n * the diff is larger. Identical inputs short-circuit with `Texts are identical`.\n */\nexport const textDiffTool = new Tool({\n  name: 'text_diff',\n  description:\n    'Compare two texts and show what changed. Returns a summary of additions, deletions, and a unified-style diff.',\n  inputSchema: validator.object({\n    text_a: validator.string().required().allow('').description('Original text (before)'),\n    text_b: validator.string().required().allow('').description('New text (after)'),\n    mode: validator\n      .string()\n      .valid('lines', 'words')\n      .default('lines')\n      .description('Compare line-by-line or word-by-word (default: lines)'),\n  }),\n  handler: async (args) => {\n    const {\n      text_a: a,\n      text_b: b,\n      mode,\n    } = args as {\n      text_a: string\n      text_b: string\n      mode: 'lines' | 'words'\n    }\n\n    if (a === b) return 'Texts are identical — no differences found.'\n\n    const changes = mode === 'words' ? diffWords(a, b) : diffLines(a, b)\n\n    let added = 0\n    let removed = 0\n    let unchanged = 0\n    const diffOutput: string[] = []\n\n    for (const part of changes) {\n      const unit = mode === 'words' ? (part.value.match(/\\S+/g) ?? []).length : (part.count ?? 1)\n      if (part.added) {\n        added += unit\n        for (const line of part.value.split('\\n').filter((l) => l !== '' || mode === 'words')) {\n          if (line !== '') diffOutput.push(`+ ${line}`)\n        }\n      } else if (part.removed) {\n        removed += unit\n        for (const line of part.value.split('\\n').filter((l) => l !== '' || mode === 'words')) {\n          if (line !== '') diffOutput.push(`- ${line}`)\n        }\n      } else {\n        unchanged += unit\n      }\n    }\n\n    const unit = mode === 'lines' ? 'line' : 'word'\n    const summary = `+${added} ${unit}${added !== 1 ? 's' : ''} added, -${removed} ${unit}${removed !== 1 ? 's' : ''} removed, ${unchanged} unchanged`\n\n    const MAX_DIFF_LINES = 60\n    const truncated =\n      diffOutput.length > MAX_DIFF_LINES\n        ? `\\n… (${diffOutput.length - MAX_DIFF_LINES} more lines truncated)`\n        : ''\n    return [summary, '', ...diffOutput.slice(0, MAX_DIFF_LINES)].join('\\n') + truncated\n  },\n})\n\n/**\n * Report the Levenshtein edit distance and a similarity percentage between two strings.\n *\n * @remarks\n * Useful for fuzzy matching. The percentage is `(1 - distance / max(len_a, len_b)) * 100`, with\n * a human-readable label appended (`identical` / `very similar` / `similar` / `somewhat\n * similar` / `different` / `very different`).\n */\nexport const stringSimilarityTool = new Tool({\n  name: 'string_similarity',\n  description:\n    'Calculate edit distance and similarity percentage between two strings using Levenshtein distance. Useful for fuzzy matching.',\n  inputSchema: validator.object({\n    a: validator.string().required().allow('').description('First string'),\n    b: validator.string().required().allow('').description('Second string'),\n    case_insensitive: validator\n      .boolean()\n      .default(false)\n      .description('Ignore case when comparing (default: false)'),\n  }),\n  handler: async (args) => {\n    let { a, b } = args as { a: string; b: string }\n    const { case_insensitive: ci } = args as { case_insensitive: boolean }\n\n    if (ci) {\n      a = a.toLowerCase()\n      b = b.toLowerCase()\n    }\n\n    const dist = distance(a, b)\n    const maxLen = Math.max(a.length, b.length)\n    const pct = maxLen === 0 ? 100 : Number.parseFloat(((1 - dist / maxLen) * 100).toFixed(1))\n\n    const label =\n      pct === 100\n        ? 'identical'\n        : pct >= 90\n          ? 'very similar'\n          : pct >= 70\n            ? 'similar'\n            : pct >= 50\n              ? 'somewhat similar'\n              : pct >= 30\n                ? 'different'\n                : 'very different'\n\n    return `Edit distance: ${dist}\\nSimilarity: ${pct}% (${label})`\n  },\n})\n"],"mappings":"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"}