Skip to content

Commit daf1843

Browse files
lchoquelclaude
andcommitted
Recompute undeclared set after PipeParallel deterministic fixes
After Step 3 nulls out combined_output and sets output to "Anything" on PipeParallel specs, the undeclared set was never reduced, causing Step 4 to trigger the LLM pipeline unnecessarily for concepts whose only references were just eliminated. Add _collect_local_bare_concept_codes helper that scans all pipe specs and concept definitions for locally-referenced bare concept codes, and use it after Step 3 to remove concepts that are no longer referenced from the undeclared set before the LLM call. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent d28946c commit daf1843

File tree

1 file changed

+54
-0
lines changed

1 file changed

+54
-0
lines changed

pipelex/builder/builder_loop.py

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -222,6 +222,13 @@ async def _fix_undeclared_concept_references(
222222
log.info(f"🔧 Setting output of PipeParallel '{pipe_code}' to 'Anything'")
223223
pipe_spec.output = "Anything"
224224

225+
# Recompute undeclared: some concepts may no longer be referenced after PipeParallel fixes
226+
still_referenced = self._collect_local_bare_concept_codes(pipelex_bundle_spec=pipelex_bundle_spec)
227+
no_longer_referenced = undeclared - still_referenced
228+
if no_longer_referenced:
229+
log.info(f"🔧 Concepts no longer referenced after PipeParallel fixes: {', '.join(sorted(no_longer_referenced))}")
230+
undeclared -= no_longer_referenced
231+
225232
# Step 4: Create remaining undeclared concepts via pipeline
226233
if undeclared:
227234
# Build context for the LLM
@@ -388,6 +395,53 @@ def _prune_unreachable_specs(self, pipelex_bundle_spec: PipelexBundleSpec) -> Pi
388395

389396
return pipelex_bundle_spec
390397

398+
@staticmethod
399+
def _collect_local_bare_concept_codes(pipelex_bundle_spec: PipelexBundleSpec) -> set[str]:
400+
"""Collect bare concept codes referenced locally from pipe specs and concept definitions.
401+
402+
Only includes references whose domain prefix is absent or matches the bundle domain.
403+
This is used to determine which concepts are still actively referenced after spec mutations.
404+
405+
Args:
406+
pipelex_bundle_spec: The bundle spec to scan
407+
408+
Returns:
409+
Set of bare concept codes (without domain prefix) that are referenced
410+
"""
411+
domain = pipelex_bundle_spec.domain
412+
referenced: set[str] = set()
413+
414+
def _add_if_local(concept_ref_or_code: str) -> None:
415+
if "." not in concept_ref_or_code or concept_ref_or_code.split(".", maxsplit=1)[0] == domain:
416+
bare_code = concept_ref_or_code.rsplit(".", maxsplit=1)[-1] if "." in concept_ref_or_code else concept_ref_or_code
417+
referenced.add(bare_code)
418+
419+
# Scan pipe specs
420+
if pipelex_bundle_spec.pipe:
421+
for pipe_spec in pipelex_bundle_spec.pipe.values():
422+
_add_if_local(parse_concept_with_multiplicity(pipe_spec.output).concept_ref_or_code)
423+
if pipe_spec.inputs:
424+
for input_concept_str in pipe_spec.inputs.values():
425+
_add_if_local(parse_concept_with_multiplicity(input_concept_str).concept_ref_or_code)
426+
if isinstance(pipe_spec, PipeParallelSpec) and pipe_spec.combined_output:
427+
_add_if_local(parse_concept_with_multiplicity(pipe_spec.combined_output).concept_ref_or_code)
428+
429+
# Scan concept definitions
430+
if pipelex_bundle_spec.concept:
431+
for concept_spec_or_name in pipelex_bundle_spec.concept.values():
432+
if not isinstance(concept_spec_or_name, ConceptSpec):
433+
continue
434+
if concept_spec_or_name.refines:
435+
_add_if_local(concept_spec_or_name.refines)
436+
if concept_spec_or_name.structure:
437+
for field_spec in concept_spec_or_name.structure.values():
438+
if field_spec.concept_ref:
439+
_add_if_local(field_spec.concept_ref)
440+
if field_spec.item_concept_ref:
441+
_add_if_local(field_spec.item_concept_ref)
442+
443+
return referenced
444+
391445
def _fix_bundle_validation_error(
392446
self,
393447
bundle_error: ValidateBundleError,

0 commit comments

Comments
 (0)