Skip to content

v0.41.0

Latest

Choose a tag to compare

@github-actions github-actions released this 30 Jul 12:18
de46945

[v0.41.0] - 2026-07-30

Highlights

Importing the Pipelex runtime now loads zero interpreter modules, down from 50 — you can embed the inference engine without loading a line of the MTHDS interpreter. pipelex.hub split into pipelex.runtime_hub and pipelex.interpreter_hub, the boot sequence gained a RuntimeBoot layer seam, and the vendor adapters, the MTHDS parser and the Pipe machinery each moved to the package that owns them. The boundary is enforced in CI (make check-hub-layering) and pinned by a subprocess import-closure test. This is a large breaking cycle: many import paths moved, and PipelexInterpreterErrorMthdsParserError changes a wire-visible error_type. See Hub layering convention.

Added

  • Claude 5 Support: claude-5-sonnet is now served by the Pipelex gateway.
  • Error Identity Snapshotting: Added a generate-error-identity (gei) CLI command and Make target that commits a snapshot of every PipelexError subclass's (error_type, title, type_uri) triple, so renaming an error class — which changes the error_type string consumers branch on — surfaces as a reviewable diff instead of silently breaking them. Documented in Error Model.
  • Hub Layering CI Enforcement: Added make check-hub-layering (chl), now a required CI status check (lint-hub-layering), to enforce the one-way boundary between the runtime and interpreter layers. The rule is transitive — it reports any runtime-layer module that reaches the interpreter hub, naming the shortest import chain and the line of its first hop.
  • Concept Registry Boundary: A golden-set AST test over pipelex/core/concepts/** pins which modules may read the process-global class registry — only the materialization write side may.
  • Vendor-Neutrality Pinning: Added strict tests pinning pipelex.cogt's third-party import roots and its exact cogt → pipelex.providers edges, so a new inference-provider SDK import or a new inversion shows up as a diff. Both packages are runtime-layer, so no other gate can see these edges.
  • Registry Sort Enforcement: The keyword-only guard now enforces the sort order of the subject_grants.toml registry (unsorted-grant violation) to prevent silent breakage during bulk rewrites. See Keyword-only arguments.
  • Test Duration Gating: Added a test ensuring .test_durations contains no dead paths after bulk file moves, keeping CI shard balancing accurate.
  • Registration Surface Docs: New Registration surface page documents every place a new pipe kind has to be registered.

Changed

  • The Hub Split: pipelex.hub has been split into two distinct lifecycles to keep the runtime from loading the method interpreter: pipelex.runtime_hub (process-scoped infrastructure: config, console, secrets, storage, telemetry, model deck, inference workers) and pipelex.interpreter_hub (library-scoped method machinery: library manager, pipe router, pipeline manager). get_pipelex_hub splits into get_runtime_hub / get_interpreter_hub. There is no alias shim — a stale import fails loudly rather than silently resolving to the wrong layer. (Breaking)
  • Composition Root: Pipelex is now the interpreter-layer boot composed on top of the new RuntimeBoot layer seam (pipelex/runtime_boot.py), which stands up the whole runtime while loading zero interpreter modules. Inheritance preserves every attribute address, so no consumer changes; Pipelex.__init__ takes keyword-only config_dir in place of the inert positional config_dir_path, and the dead is_pipelex_service_enabled attribute is gone. (Breaking)
  • Plugin & Provider Separation: Built-in vendor adapters (OpenAI, Anthropic, etc.) moved from pipelex.plugins to pipelex.providers; interpreter-touching built-in plugins (direct, pipe_func) moved to pipelex.interpreter_plugins; pipelex.plugins now strictly houses the plugin mechanism (contracts, registrars). The plugin contract itself is untouched — an out-of-tree plugin still imports pipelex.plugins.contract / registrar and is still discovered through the pipelex.plugins entry-point group. build_registrar now requires builtin_plugins= and core_unconditional_plugin_names= instead of importing them, which is what keeps pipelex.plugins importable without the interpreter. (Breaking)
  • Core Restructuring & MTHDS Parser: The MTHDS parser moved out of core/ into pipelex.mthds_parsing, with PipelexInterpreter renamed to MthdsParser and PipelexInterpreterError renamed to MthdsParserError. The rename is wire-visible: the class name is the error_type clients branch on, and its type_uri moves to errors/mthds-parser-error/ (a docs redirect keeps the retired URI resolving; a consumer switching on the string does not get one). Core's Pipe machinery (abstracts, factories, blueprint, validation, rendering) moved to pipelex.pipe_machinery, and the pipe-kind registration manifest moved to pipelex.pipe_machinery.registry_models, leaving CoreRegistryModels with the value model only. (Breaking)
  • Concept Purity: Concept is now pure data. Class resolution has been removed from the wire model and delegated to ConceptProviderAbstract.get_structure_class(concept=...), and Concept.are_concept_compatible becomes the pure Concept.are_compatible_by_declaration, composed by ConceptLibrary.is_compatible. Three defects close with it: cross-package refines aliases now resolve at five call sites that compared Concept values directly, an unresolvable structure class now raises ConceptStructureClassNotFoundError instead of silently answering False, and native.Anything is answered as a declared property rather than raised. (Breaking)
  • Injected Providers in core/: Core's data model resolves concepts through an injected provider instead of reaching into the hub — StuffFactory, InputShaper, WorkingMemoryFactory, InputStuffSpecsFactory and StuffSpecFactory all take a required concept_provider. The new read-side contract ConceptProviderAbstract owns that surface, which ConceptLibraryAbstract extends, keeping library management in the interpreter layer. (Breaking)
  • Graph Rendering Split: graph_rendering's bundle-driven half is now pipelex.pipeline.bundle_graph_rendering (generate_graph_for_bundle / generate_view_for_bundle); GraphFormat and render_graph_from_spec stay put. The split is along "do I need a loaded method?" — producing a graph spec from a bundle requires dry-running it, rendering an existing spec does not. pipelex.graph, pipelex.tracing, pipelex.observer, pipelex.errors and pipelex.test_extras join the declared runtime layer. (Breaking)
  • Type Relocations: Leaf models moved to the packages that own them, preventing import leaks: SpecialPipelineId / JobMetadata / JobCategory / UnitJobId to pipelex.system.job_metadata, JobMetadataError to pipelex.system.exceptions, PipeRunMode to pipelex.system.pipe_run_mode, PipeRunParamKey to pipelex.system.pipe_run_param_key, PipeRunError to pipelex.core.pipes.exceptions, TraceContext to pipelex.system.trace_context, DataInclusionConfig to pipelex.system.data_inclusion_config, TextFormat / TemplatingStyle / TagStyle to pipelex.tools.templating.*, TemplateCategory to pipelex.tools.jinja2.template_category, and the resolved-field layer to pipelex.core.concepts.resolved_fields. The two pipe renderers regrouped into pipelex.pipe_machinery.rendering. The TOML config shape is unchanged throughout — only the Python homes moved. (Breaking)
  • LLM Prompts: LLMPromptBlueprint no longer has a templating_style field; the style is now passed as a run-scoped parameter to make_llm_prompt. TemplateBlueprint.templating_style stays and now correctly takes precedence over the run-derived style. (Breaking)
  • Image Generation: Geometry mappings moved inward from the vendor adapters to pipelex/cogt/img_gen/GoogleImgGenFactory / OpenAIImgGenFactory are now ImgGenGeminiMapping / ImgGenGptMapping, with wire-literal aliases following the model family rather than the vendor. They were never vendor adapters: they map a cogt-owned taxonomy onto provider wire values, and no family is served by a single adapter. (Breaking)

Fixed

  • Lint-Clean Codegen Artifacts: Generated Python and TypeScript artifacts are now lint-clean by construction, emitting modern typing (builtin generics, X | None), double-quoted Literal members, isort-grouped imports, idiomatic docstrings, pre-wrapped long lines, blank JSDoc lines carrying no trailing space, and a header-only file when there is nothing to emit. This prevents consumer formatters (Ruff/Prettier) from rewriting bytes and invalidating the [hand-edited] codegen stamp. Emitted bytes change for all three targets, so a committed projection reports drift until it is regenerated, and the lint exclusion on generated paths is no longer needed. Documented in Codegen Projections. (Breaking)
  • Prompt Rendering Determinism: Rendering an LLM prompt no longer mutates the library-held pipe object; a pipe's serialized form is now identical before and after runs, and in-process deck or config changes take effect immediately instead of being shadowed by the first run's cached value.
  • Sandbox PipeFunc Structures: A custom PipeFunc returning a non-native concept structure failed in the sandbox with an opaque "Function not found in registry" error — the structure class neither travelled nor was generated, so its import raised ModuleNotFoundError, which the registration path silently swallowed. structures.py is now regenerated from the crate before customer code is registered, and a failed import logs its real cause instead of being swallowed.
  • Boot & Teardown Resilience: A partway-failing teardown no longer permanently wedges the process — the whole un-poisoning set now runs in a finally, not just the singleton de-registration, since anything left behind alongside it turns a loud failure into a silent one. A failed boot no longer leaves the telemetry manager singleton behind or leaks a plugin runtime; plugin teardown is now best-effort per callback so one failing plugin cannot strand another's resources; an injected telemetry manager that raises no longer aborts the release suite; a second make() of an already-booted class is now explicitly refused; and the singleton lookup iterates a copy rather than a live process-global dict that grows during a run. teardown() no longer propagates a plugin callback's exception. (Breaking)
  • Layering Leaks: Built-in vendor adapters were pulling the entire interpreter into the runtime layer by importing MissingDependencyError from the all-errors aggregate pipelex.exceptions; they now import directly from pipelex.system.exceptions. Both gates were blind to it, which is what motivated the guard's new transitive rule.
  • Runtime Structure Generator Typing: The runtime StructureGenerator emitted Optional[X] / List / Dict where the codegen emitters emit modern typing, despite both rendering the same resolved-type tree. Both now agree, with unions folded inside quoted forward references so the annotation stays a single deferred expression.
  • Config Initialization: config_dir passed at boot is no longer silently ignored and now correctly reaches setup_config.
  • Documentation & CLI: Fixed pipelex build --help advertising a non-existent typescript format (it is a downstream use of --format schema); corrected hub.set_observer() documentation, which advertised an inert API, to reflect the Pipelex.make(observers=...) mechanism; rewrote the validation feature page, which promised an inter-step concept check that does not exist and cannot exist given working-memory resolution; and removed a duplicate set_library_manager call in Pipelex.setup.

What's Changed

  • Generate concept structures in the sandbox for custom PipeFuncs by @thomashebrard in #1061
  • refactor(hub): split pipelex.hub into runtime_hub + interpreter_hub by @lchoquel in #1062
  • fix(cli): correct build output format help and validation docs by @lchoquel in #1068
  • fix(hub): move the interpreter-touching built-in plugins out of the runtime layer, and make the layering guard transitive by @lchoquel in #1064
  • fix(codegen): emit lint-clean artifacts so a consumer's formatter cannot break the stamp by @lchoquel in #1070
  • fix(hub): complete the layer boundary — placement, so the predicate can say what it means by @lchoquel in #1071
  • refactor(core): evict the ambient class-registry reads from the Concept wire model by @lchoquel in #1072
  • refactor(boot): give the composition root the layer seam every other package has by @lchoquel in #1073
  • fix(llm): keep the prompting style run-scoped so rendering cannot mutate the library-held pipe by @lchoquel in #1074
  • Release v0.41.0 by @lchoquel in #1075

Full Changelog: v0.40.0...v0.41.0