Skip to content

Commit 97b3c7b

Browse files
authored
Fix batch ingest embed cli flags (#1700)
1 parent bdd7209 commit 97b3c7b

2 files changed

Lines changed: 111 additions & 9 deletions

File tree

nemo_retriever/src/nemo_retriever/ingest_modes/batch.py

Lines changed: 32 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -255,6 +255,22 @@ def __init__(
255255
self._extract_html_kwargs: Dict[str, Any] = {} # noqa: F821
256256
self._use_nemotron_parse_only: bool = False
257257

258+
@staticmethod
259+
def _positive_int(value: Any) -> int | None:
260+
try:
261+
parsed = int(value)
262+
except (TypeError, ValueError):
263+
return None
264+
return parsed if parsed > 0 else None
265+
266+
@staticmethod
267+
def _positive_float(value: Any) -> float | None:
268+
try:
269+
parsed = float(value)
270+
except (TypeError, ValueError):
271+
return None
272+
return parsed if parsed > 0.0 else None
273+
258274
def files(self, documents: Union[str, List[str]]) -> "BatchIngestor":
259275
"""
260276
Add local files for batch processing.
@@ -857,16 +873,21 @@ def embed(
857873
resolved = resolved.model_copy(update={"api_key": resolve_remote_api_key()})
858874

859875
kwargs = build_embed_kwargs(resolved, include_batch_tuning=True)
876+
embed_batch_size = (
877+
self._positive_int(kwargs.get("embed_batch_size")) or self._requested_plan.get_embed_batch_size()
878+
)
879+
embed_workers = self._positive_int(kwargs.get("embed_workers"))
880+
embed_initial_actors = embed_workers or self._requested_plan.get_embed_initial_actors()
881+
embed_min_actors = embed_workers or self._requested_plan.get_embed_min_actors()
882+
embed_max_actors = embed_workers or self._requested_plan.get_embed_max_actors()
860883

861884
# Remaining kwargs are forwarded to the actor constructor.
862885
embed_modality = resolved.embed_modality
863886
embed_granularity = resolved.embed_granularity
864887
self._tasks.append(("embed", dict(kwargs)))
865888

866889
# We want to create Ray batches that are of the same size as the embed_batch_size.
867-
self._rd_dataset = self._rd_dataset.repartition(
868-
target_num_rows_per_block=self._requested_plan.get_embed_batch_size()
869-
)
890+
self._rd_dataset = self._rd_dataset.repartition(target_num_rows_per_block=embed_batch_size)
870891

871892
if embed_granularity == "page":
872893
_row_fn = partial(
@@ -884,7 +905,7 @@ def embed(
884905
)
885906
self._rd_dataset = self._rd_dataset.map_batches(
886907
_row_fn,
887-
batch_size=self._requested_plan.get_embed_batch_size(),
908+
batch_size=embed_batch_size,
888909
batch_format="pandas",
889910
num_cpus=1,
890911
)
@@ -894,17 +915,19 @@ def embed(
894915
if endpoint:
895916
embed_actor_num_gpus = 0 # We do not need GPU resources if invoking a remote NIM endpoint
896917
else:
897-
embed_actor_num_gpus = self._requested_plan.get_embed_gpus_per_actor()
918+
embed_actor_num_gpus = (
919+
self._positive_float(kwargs.get("gpu_embed")) or self._requested_plan.get_embed_gpus_per_actor()
920+
)
898921

899922
self._rd_dataset = self._rd_dataset.map_batches(
900923
_BatchEmbedActor,
901-
batch_size=self._requested_plan.get_embed_batch_size(),
924+
batch_size=embed_batch_size,
902925
batch_format="pandas",
903926
num_gpus=embed_actor_num_gpus, # pulled from if statement above
904927
compute=rd.ActorPoolStrategy(
905-
initial_size=self._requested_plan.get_embed_initial_actors(),
906-
min_size=self._requested_plan.get_embed_min_actors(),
907-
max_size=self._requested_plan.get_embed_max_actors(),
928+
initial_size=embed_initial_actors,
929+
min_size=embed_min_actors,
930+
max_size=embed_max_actors,
908931
),
909932
fn_constructor_kwargs={"params": resolved},
910933
)

nemo_retriever/tests/test_batch_ingestor.py

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
pytest.importorskip("ray")
66

77
from nemo_retriever.ingest_modes.batch import BatchIngestor
8+
from nemo_retriever.params import EmbedParams
89

910

1011
class _DummyClusterResources:
@@ -21,6 +22,34 @@ def available_gpu_count(self) -> int:
2122
return 0
2223

2324

25+
class _DummyGpuClusterResources:
26+
def total_cpu_count(self) -> int:
27+
return 16
28+
29+
def total_gpu_count(self) -> int:
30+
return 2
31+
32+
def available_cpu_count(self) -> int:
33+
return 16
34+
35+
def available_gpu_count(self) -> int:
36+
return 2
37+
38+
39+
class _DummyDataset:
40+
def __init__(self) -> None:
41+
self.repartition_calls: list[int] = []
42+
self.map_batches_calls: list[dict[str, object]] = []
43+
44+
def repartition(self, *, target_num_rows_per_block: int):
45+
self.repartition_calls.append(target_num_rows_per_block)
46+
return self
47+
48+
def map_batches(self, fn, **kwargs):
49+
self.map_batches_calls.append({"fn": fn, **kwargs})
50+
return self
51+
52+
2453
def test_batch_ingestor_filters_none_runtime_env_vars(monkeypatch) -> None:
2554
captured: dict[str, object] = {}
2655
dummy_ctx = SimpleNamespace(enable_rich_progress_bars=False, use_ray_tqdm=True)
@@ -56,3 +85,53 @@ def test_batch_ingestor_filters_none_runtime_env_vars(monkeypatch) -> None:
5685
}
5786
assert dummy_ctx.enable_rich_progress_bars is True
5887
assert dummy_ctx.use_ray_tqdm is False
88+
89+
90+
def test_batch_ingestor_embed_honors_batch_tuning(monkeypatch) -> None:
91+
dummy_ctx = SimpleNamespace(enable_rich_progress_bars=False, use_ray_tqdm=True)
92+
93+
monkeypatch.setattr(
94+
"nemo_retriever.ingest_modes.batch.ray.init",
95+
lambda **kwargs: None,
96+
)
97+
monkeypatch.setattr(
98+
"nemo_retriever.ingest_modes.batch.rd.DataContext.get_current",
99+
lambda: dummy_ctx,
100+
)
101+
monkeypatch.setattr(
102+
"nemo_retriever.ingest_modes.batch.gather_cluster_resources",
103+
lambda _ray: _DummyGpuClusterResources(),
104+
)
105+
monkeypatch.setattr(
106+
"nemo_retriever.ingest_modes.batch.rd.ActorPoolStrategy",
107+
lambda *, initial_size, min_size, max_size: SimpleNamespace(
108+
initial_size=initial_size,
109+
min_size=min_size,
110+
max_size=max_size,
111+
),
112+
)
113+
114+
ingestor = BatchIngestor(documents=[])
115+
dataset = _DummyDataset()
116+
ingestor._rd_dataset = dataset
117+
118+
ingestor.embed(
119+
EmbedParams(
120+
model_name="nvidia/llama-nemotron-embed-vl-1b-v2",
121+
embed_granularity="page",
122+
batch_tuning={
123+
"embed_workers": 1,
124+
"embed_batch_size": 1,
125+
"gpu_embed": 1.0,
126+
},
127+
)
128+
)
129+
130+
assert dataset.repartition_calls == [1]
131+
assert dataset.map_batches_calls[0]["batch_size"] == 1
132+
assert dataset.map_batches_calls[1]["batch_size"] == 1
133+
assert dataset.map_batches_calls[1]["num_gpus"] == 1.0
134+
compute = dataset.map_batches_calls[1]["compute"]
135+
assert compute.initial_size == 1
136+
assert compute.min_size == 1
137+
assert compute.max_size == 1

0 commit comments

Comments
 (0)