Skip to content

PipelineTemplate.instantiate(auto-discover) constructs every installed provider before checking referenced providers #67

Description

@goanpeca

Summary

PipelineTemplate.instantiate(providers=None) auto-discovers provider entry
points, then eagerly instantiates every discovered provider class before it
checks which providers the template actually references. A valid template that
uses one provider can therefore fail because an unrelated installed provider has
constructor requirements, missing optional dependencies, import-time side
effects, or expensive client setup.

The method should resolve the template's referenced provider names first, then
instantiate only those providers.

Affected files

  • libs/core/genblaze_core/pipeline/template.py:125-137 (auto-discovery instantiates the full discovered map)
  • libs/core/genblaze_core/pipeline/template.py:150-156 (referenced provider lookup happens only after all providers have been constructed)
  • libs/core/tests/unit/test_pipeline_template.py:108-200 (instantiation tests always pass an explicit provider dict; no providers=None coverage)
  • docs/features/pipeline-templates.md:67 (documents auto-discovery as a supported path)

Evidence

Minimal reproduction from the requested conda env:

from genblaze_core.pipeline.template import PipelineTemplate, StepTemplate
from genblaze_core.providers import registry
from genblaze_core.testing import MockProvider

class BadProvider:
    def __init__(self):
        raise RuntimeError("requires credentials")

orig = registry.discover_providers
try:
    registry.discover_providers = lambda: {"mock": MockProvider, "bad": BadProvider}
    template = PipelineTemplate(
        name="only-mock",
        steps=[StepTemplate(provider_name="mock", model="m")],
    )
    template.instantiate()
finally:
    registry.discover_providers = orig

Output:

GenblazeError
Failed to instantiate provider 'bad'. Pass providers= explicitly if constructor requires arguments: requires credentials

The template references only mock, but instantiation fails because the unused
bad entry point is constructed first.

There is a smaller version of the same problem for empty templates:
instantiate(providers=None) performs discovery and provider construction before
raising Template has no steps.

Why it matters

Auto-discovery is meant to make template loading ergonomic. In plugin-heavy
environments, constructing every installed provider is the opposite: unrelated
plugins can break templates, add startup latency, or force credentials for
providers the user is not using. It also makes debugging misleading because the
error names a provider absent from the template.

Suggested fix

Reorder and narrow the logic:

  1. Validate self.steps before discovery.
  2. Compute required_provider_names = {st.provider_name for st in self.steps}.
  3. If providers is None, call discover_providers() to get classes, but
    instantiate only the required names.
  4. Report missing required providers with the existing "Available" message.
  5. Add tests for providers=None with an unused discovered provider whose
    constructor raises, and for empty templates not invoking discovery.

If providers with credential kwargs should be supported by auto-discovery, reuse
or extend the existing instantiate_with_credential() helper in
genblaze_core.providers.registry instead of hand-rolling constructor fallback
inside templates.

Acceptance criteria

  • A template that references provider A can instantiate with auto-discovery
    even if an unrelated discovered provider B cannot be constructed.
  • Empty templates fail before provider discovery/instantiation.
  • Missing referenced providers still produce a clear error listing available
    provider names.
  • Unit tests cover the providers=None path.

Metadata

Metadata

Assignees

No one assigned

    Type

    Fields

    No fields configured for Bug.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions