Skip to content

feat(kt-facts): DecompositionPipeline routes through registry-resolved provider (Phase 4 #44)#280

Merged
charlie83Gs merged 2 commits intofeat/backend-engine-fact-decomposition-pluginfrom
feat/decomposition-pipeline-provider-resolution
Apr 21, 2026
Merged

feat(kt-facts): DecompositionPipeline routes through registry-resolved provider (Phase 4 #44)#280
charlie83Gs merged 2 commits intofeat/backend-engine-fact-decomposition-pluginfrom
feat/decomposition-pipeline-provider-resolution

Conversation

@charlie83Gs
Copy link
Copy Markdown
Contributor

Summary

Consumer-side wire-up for Phase 4. `DecompositionPipeline` now accepts an optional `fact_decomposition_provider` kwarg; `extract_text` routes through `provider.decompose` when one is injected, else falls back to the pipeline-owned `TextExtractor`.

`kt_hatchet.composition` gains `resolve_fact_decomposition_provider` — companion to `resolve_phase_settings`. Workers call both at task entry to translate `ctx.config.composition.fact_decomposition` into a ready-to-inject provider instance via the plugin registry.

`decompose_chunk_task` in worker-search is the first consumer. Other call sites (`decompose_source_task`, `GatherFactsPipeline`, `building/unified`, `nodes/pipeline`) still run legacy `TextExtractor` — they follow in subsequent PRs once this proves the shape.

Why

Closes the consumer half of Phase 4 #44 — ABC + plugin + pipeline integration all present, first worker wired. Demonstrates the full round-trip: `composition.fact_decomposition` id → plugin registry lookup → `FactDecompositionProvider` instance → pipeline routes extraction through it.

Based on #279 (adds `backend-engine-fact-decomposition` plugin contributing `llm-default`). Rebase onto main after #279 merges.

Test plan

  • `uv run --project libs/kt-facts pytest libs/kt-facts/tests/ --ignore=tests/integration -x -q` — 234/234 green (2 new pinning provider-routing + fallback)
  • `uv run --project libs/kt-hatchet pytest libs/kt-hatchet/tests/test_composition.py -x -v` — 9/9 green (4 new pinning provider-resolver helper)
  • `uv run --project services/worker-search pytest services/worker-search/tests/ -x -q` — 8/8 green
  • CI: full workspace tests after rebase

🤖 Generated with Claude Code

charlie83Gs and others added 2 commits April 20, 2026 18:44
…d provider (Phase 4 #44)

Adds ``fact_decomposition_provider`` kwarg to
``DecompositionPipeline.__init__``. When set, ``extract_text`` builds
a :class:`DecompositionContext` from the existing call-site args
(concept / query_context / source_url → source_id, source_title →
``options``) and delegates to ``provider.decompose``. When ``None``,
extraction falls back to the pipeline-owned ``TextExtractor`` (legacy
path for callers not yet wired to the registry).

``kt_hatchet.composition`` gains ``resolve_fact_decomposition_provider``,
a companion to ``resolve_phase_settings`` — workers call it at task
entry to translate ``ctx.config.composition.fact_decomposition`` into
a ready-to-inject provider instance via the plugin registry. Returns
``None`` on the legacy paths (no graph, no services, empty
composition, no plugin registered) so callers fall back cleanly.

``decompose_chunk_task`` in worker-search is the first consumer —
threads both resolvers into its ``DecompositionPipeline`` construction.
Other DecompositionPipeline call sites (decompose_source_task,
GatherFactsPipeline, building/unified / nodes/pipeline) still run the
legacy TextExtractor path and follow in subsequent PRs.

Tests: 2 new in ``test_pipeline_phase_settings.py`` pinning the
provider-routing vs. fallback branches; 4 new in ``test_composition.py``
pinning the provider-resolver helper (happy-path / graph_id None /
empty-composition / registry-miss).

Builds on #279 (backend-engine-fact-decomposition plugin), stacked
branch — rebase onto main after #279 merges.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Three review items addressed:

1. ``DecompositionContext.source_title`` promoted to typed field on the
   ABC instead of side-channeled through ``options``. Pipeline +
   built-in adapter drop the options-bag hop. ABC now carries every
   legacy ``TextExtractor`` kwarg as a first-class field; ``options``
   remains reserved for phase_settings-style per-graph knobs (model /
   thinking_level / batch_size) that land in Phase 4 follow-ups.
   Prevents other provider implementations from copying the pattern.

2. ``resolve_fact_decomposition_provider`` now logs a WARNING when a
   composition names a provider id no plugin contributes on this
   worker (rolling-deployment case). Was silent before — flagged as
   tension with fail-fast. Tolerant stance preserved (still returns
   ``None`` so caller falls back to legacy ``TextExtractor``) but the
   unresolved id no longer vanishes. Mirrors
   ``validate_all_graph_types`` canary policy.

3. Drop unused ``AsyncMock`` import in
   ``test_pipeline_phase_settings.py`` — ruff would flag.

Test updates:
- ``test_extract_text_routes_through_injected_provider`` now asserts
  ``ctx.source_title`` directly instead of ``ctx.options["source_title"]``.
- ``test_fact_decomposition_provider_not_registered_warns_and_returns_none``
  renamed + asserts WARN record present with graph_id + provider_id.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@charlie83Gs
Copy link
Copy Markdown
Contributor Author

Review fixes pushed. Three issues addressed:

  1. source_title → typed field on ABCDecompositionContext.source_title promoted from options["source_title"] side-channel to first-class field on FactDecompositionProvider's ABC. Pipeline + built-in adapter drop the options hop. options now reserved for phase_settings-style per-graph knobs (model/thinking_level/batch_size); typed fields carry every legacy TextExtractor kwarg. Prevents the pattern from propagating to other providers.

  2. Silent fallback on unregistered provider → WARN logresolve_fact_decomposition_provider now logs a WARNING when composition names an id no plugin contributes on this worker (rolling-deployment case). Tolerant stance preserved (still returns None so caller falls back to legacy TextExtractor) but the unresolved id no longer vanishes silently. Mirrors the canary policy in validate_all_graph_types. Test renamed to test_fact_decomposition_provider_not_registered_warns_and_returns_none and asserts WARN record w/ graph_id + provider_id.

  3. Unused AsyncMock import — dropped.

On remaining points:

Tests: 122 kt-core-engine-api + 7 facts + 9 composition + 5 plugin — all green.

@charlie83Gs charlie83Gs merged commit 774e4b1 into feat/backend-engine-fact-decomposition-plugin Apr 21, 2026
2 checks passed
@charlie83Gs charlie83Gs deleted the feat/decomposition-pipeline-provider-resolution branch April 21, 2026 14:31
@github-actions
Copy link
Copy Markdown


Thank you for your submission, we really appreciate it. Like many open-source projects, we ask that you sign our Contributor License Agreement before we can accept your contribution. You can sign the CLA by just posting a Pull Request Comment same as the below format.


I have read the CLA Document and I hereby sign the CLA


You can retrigger this bot by commenting recheck in this Pull Request. Posted by the CLA Assistant Lite bot.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant