from __future__ import annotations from typing import Dict import polars as pl from tools.base import BaseTool class FindReplaceTool(BaseTool): """Look up values in a Find/Replace table and replace matching fields.""" def execute(self, inputs: Dict[str, pl.DataFrame]) -> Dict[str, pl.DataFrame]: target = inputs.get("Target", inputs.get("Input", pl.DataFrame())) find_df = inputs.get("Find", inputs.get("Replace", pl.DataFrame())) if target.is_empty() or find_df.is_empty() or self.config is None: return {"Output": target} field = self._cfg("Field", "") or "" find_field = self._cfg("FindField", "") or "" replace_field = self._cfg("ReplaceField", "") or "" whole_word = (self._cfg("WholeWord", "False") or "False").lower() == "true" if not (field and find_field and replace_field): return {"Output": target} find_values = find_df[find_field].cast(pl.String).to_list() replace_values = find_df[replace_field].cast(pl.String).to_list() lookup = dict(zip(find_values, replace_values)) def _replace_fn(val: str | None) -> str | None: if val is None: return None for find, rep in lookup.items(): if whole_word: import re val = re.sub(r"\b" + re.escape(find) + r"\b", rep, val) else: val = val.replace(find, rep) return val series = target[field].cast(pl.String).map_elements(_replace_fn, return_dtype=pl.String) return {"Output": target.with_columns(series.alias(field))}