Skip to content

Extract Mistral Workflows into standalone pipelex-mistralai-workflows package - #873

Closed
lchoquel wants to merge 17 commits into
devfrom
feature/Adapt-mistral-workflows
Closed

Extract Mistral Workflows into standalone pipelex-mistralai-workflows package#873
lchoquel wants to merge 17 commits into
devfrom
feature/Adapt-mistral-workflows

Conversation

@lchoquel

@lchoquel lchoquel commented May 7, 2026

Copy link
Copy Markdown
Member

Summary

  • Promotes the framework-agnostic embedding boundary out of the in-tree Mistral Workflows plugin into a new pipelex.runtime_bridge package — boundary types, run_pipe_via_bridge, PipelexExecutionMode, ensure_pipelex_booted, agnostic exceptions. Any host runtime can now embed Pipelex through this surface, not just Mistral Workflows.
  • Removes the pipelex[mistralai-workflows] extra and the entire pipelex.plugins.mistralai_workflows.* subtree (activities, streaming, dependency wrapper, docs, tests). Mistral-Workflows-shaped activities now live in the new pipelex-mistralai-workflows repo on PyPI.
  • Updates CHANGELOG.md [Unreleased] with the migration note, drops the mistralai-workflows mypy override block from pyproject.toml, deletes the two docs/under-the-hood/mistralai-workflows-* pages and their mkdocs.yml entries.
  • Layer-1 runtime_bridge unit + integration tests cover dispatch, validation, execution-mode selection, input model construction, and direct in-process bridge execution. Layer-2/3 tests (Mistral activity contract, Temporal end-to-end) move to the new repo's CI.

Migration (for existing users of pipelex[mistralai-workflows])

```text
pip install pipelex-mistralai-workflows

imports change: pipelex.plugins.mistralai_workflows.* → pipelex_mistralai_workflows.*

```

No behavior changes; activities, boundary types, and execution modes are identical to the previous in-tree plugin.

Test plan

  • `make agent-check` clean (pyright + mypy across the full source tree)
  • `make agent-test` green
  • All four §A11 `git grep` invariants satisfied (no residual references to the old plugin path / extra / docs)
  • Layer-2 Mistral activity tests + layer-3 Temporal tests pass in the new `pipelex-mistralai-workflows` repo (`make agent-check` + `make agent-test` clean there as well)
  • Reviewer to sanity-check that no downstream caller of `pipelex.plugins.mistralai_workflows.*` exists in their codebase before merging

Follow-ups (tracked in `TODOS.md` Stream D)

  • Ship a pipelex release exposing `pipelex.runtime_bridge`; `pipelex-mistralai-workflows` v0.1.0 pins `pipelex>=0.27.0` (or the actual shipped version).
  • Strip the editable `[tool.uv.sources]` override in `pipelex-mistralai-workflows/pyproject.toml` and publish v0.1.0 to PyPI.
  • Cookbook entry under `pipelex-cookbook/examples/c_advanced/mistral-workflows/` (deferred).

Summary by cubic

Extracted the Mistral Workflows integration into the standalone pipelex-mistralai-workflows package and introduced a framework-agnostic pipelex.runtime_bridge to run Pipelex pipes from any host runtime. Upgraded to the mistralai v2 client and bumped Python/Temporal versions.

  • New Features

    • Added pipelex.runtime_bridge: boundary models, run_pipe_via_bridge, PipelexExecutionMode, ensure_pipelex_booted, and bridge-specific errors.
    • Removed the pipelex[mistralai-workflows] extra and in-tree pipelex.plugins.mistralai_workflows.*; Mistral-shaped activities now live in pipelex-mistralai-workflows.
    • Updated Mistral LLM integration to mistralai.client.* (Mistral, models, errors) and added unit/integration tests for the bridge; host-runtime tests moved to the new repo.
  • Migration

    • Install pipelex-mistralai-workflows and update imports: pipelex.plugins.mistralai_workflows.*pipelex_mistralai_workflows.*.
    • Requirements: Python >= 3.12, mistralai >= 2.4.4, temporalio == 1.24.0. If you import Mistral types directly, switch to mistralai.client.*.
    • No behavior changes; activities, boundary types, and execution modes are unchanged.

Written for commit a5fb4dd. Summary will update on new commits.

lchoquel added 15 commits May 6, 2026 21:37
- Updated `mistralai` dependency to version 2.4.4 in `pyproject.toml`.
- Added a new `TODOS.md` file for planning and progress tracking of Mistral Workflows integration.
- Introduced new activities and bridge for running Pipelex pipes within Mistral Workflows.
- Implemented execution modes for Pipelex pipes in Mistral Workflows.
- Added exception handling specific to Mistral Workflows.
- Updated various imports to align with the new `mistralai.client` structure.
- Enhanced type checking and error handling in workflow scripts.
- Added integration tests for the new Mistral Workflows activities.
- Introduced the `pipelex.plugins.mistralai_workflows` plugin, enabling invocation of Pipelex pipes within Mistral Workflows activities.
- Added three execution modes: `DIRECT`, `TEMPORAL_BLOCKING`, and `TEMPORAL_FIRE_AND_FORGET`, allowing flexible integration options.
- Created documentation for the new plugin and usage recipes, detailing installation and execution modes.
- Implemented integration tests for both blocking and fire-and-forget modes to ensure functionality.
- Updated `CHANGELOG.md` to reflect these additions and improvements.
- Introduced `pipelex_run_pipe_offloaded` activity to handle large payloads by leveraging Mistral's `ActivityInOutOffloadingInterceptor`, allowing seamless streaming of oversized data through blob storage.
- Updated `CHANGELOG.md` to document the new offloaded activity and its usage.
- Added integration tests for the offloaded activity to ensure correct payload handling and functionality.
- Enhanced documentation with examples for using the new offloaded activity and its configuration requirements.
- Added `pipelex_run_pipe_streaming` activity to enable real-time progress tracking of Pipelex pipes within Mistral Workflows, emitting lifecycle events (`CustomTaskStarted`, `CustomTaskInProgress`, `CustomTaskCompleted`, `CustomTaskFailed`).
- Enhanced `DIRECT` execution mode to provide per-step event updates, allowing detailed observability of pipe execution.
- Updated `CHANGELOG.md` to document the new streaming activity and its features.
- Created integration tests for the streaming activity to validate event emissions and functionality.
- Improved documentation with examples for using the new streaming variant in workflows.
- Implemented per-step event updates for the `pipelex_run_pipe_streaming` activity in `DIRECT` execution mode, allowing detailed observability of pipe execution through `CustomTaskInProgress` events.
- Updated `CHANGELOG.md` to reflect the new features and improvements in streaming support.
- Enhanced integration tests to validate the emission of per-step events during multi-step pipe executions.
- Improved documentation with examples for utilizing the new streaming features in workflows.
Remove the `output: PipelexPipeRunOutput | None = None` initializer and the
trailing `assert output is not None` in `_run_streaming_with_per_step_events`.
The narrowing was assert-based (stripped under `python -O`) and only existed
to satisfy the type checker after a `try`/`finally` cleanup block. With the
pre-init dropped, pyright narrows `output` from the `try` body alone, since
the post-`try/finally` code is only reachable on success.

Extend the drain comment to call out that the cleanup serves both the happy
path (snapshot ordering) and the failure path (publishing pending in-progress
events before `Task.__aexit__` emits `CustomTaskFailed`).
- Updated the `instructor` dependency to a new commit hash for improved functionality.
Relocate the in-tree TODOS.md to wip/mistral-workflows-sub-module.md and
add wip/mistral-workflows-plugin-extract.md outlining the extraction of
the Mistral Workflows integration into its own pip-installable plugin
package scaffolded from pipelex-starter-python.
…gration

- Introduced a new `pipelex.runtime_bridge` package with core components for executing Pipelex pipes in various modes (DIRECT, TEMPORAL_BLOCKING, TEMPORAL_FIRE_AND_FORGET).
- Added `ensure_pipelex_booted` function for idempotent initialization of Pipelex.
- Created input/output models (`PipelexPipeRunInput`, `PipelexPipeRunOutput`) for structured data handling.
- Implemented error handling with custom exceptions for runtime bridge operations.
- Developed comprehensive integration tests for the runtime bridge, covering direct execution and library crate handling.
- Updated documentation and TODOs to reflect the new structure and functionality.
- Extracted Mistral Workflows integration from `pipelex` into a new package `pipelex-mistralai-workflows`, allowing for independent installation and usage.
- Removed the `pipelex.plugins.mistralai_workflows` modules and updated the core runtime bridge to be framework-agnostic.
- Updated `CHANGELOG.md` to reflect the changes and migration instructions for users.
- Deleted outdated documentation and tests related to the previous plugin structure.
- Ensured no behavioral changes; all existing functionalities remain intact under the new package structure.
… updates and clarifying remaining tasks for plugin extraction
@lchoquel
lchoquel marked this pull request as draft May 7, 2026 11:10
@lchoquel
lchoquel changed the base branch from main to dev May 7, 2026 11:10

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 5ccd4c33ba

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +231 to +232
library_manager.teardown(library_id=library_id)
teardown_current_library()

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Restore the caller's current library after scoped runs

When library_crate_dump is used from a context that already has an active library, this finally block always clears the ContextVar via teardown_current_library() instead of restoring the previous library id. That makes the bridge call leak state back to its caller: subsequent code in the same async context that expects the previously selected library will fail with “No current library set” or resolve against no library. Capture the current library before set_current_library(...) and restore it here, as the Temporal hydration helper does.

Useful? React with 👍 / 👎.

@greptile-apps

greptile-apps Bot commented May 7, 2026

Copy link
Copy Markdown

Greptile Summary

Extracts the Mistral Workflows integration out of the pipelex monorepo into a standalone pipelex-mistralai-workflows package, and promotes the embedding boundary into a new pipelex.runtime_bridge module usable by any host runtime.

  • New pipelex.runtime_bridge package — adds PipelexPipeRunInput/PipelexPipeRunOutput boundary models, run_pipe_via_bridge dispatcher (DIRECT / TEMPORAL_BLOCKING / TEMPORAL_FIRE_AND_FORGET), ensure_pipelex_booted idempotent boot helper, and a matching exception hierarchy; covered by unit + integration tests.
  • mistralai SDK bumped to 2.x — all mistralai.* imports migrated to mistralai.client.* sub-paths; Messages type replaced with ChatCompletionRequestMessage; a missing message is None guard added to mistral_llm_worker.py.
  • pyproject.toml housekeeping — removes the mistralai-workflows optional extra; bumps requires-python to >=3.12, temporalio to 1.24.0; pins instructor to a git fork pending an upstream PR merge.

Confidence Score: 3/5

The core bridge logic and SDK migration are sound, but the temporal unit tests silently depend on an optional extra and will break in base CI environments, and the instructor fork pin introduces a supply-chain dependency outside the project's control.

The temporal dispatch unit tests do not mock _require_pipelex_temporal_extra, meaning they will raise MissingPipelexTemporalExtraError in any environment that runs unit tests without the temporal extra installed — a real defect in the new test suite. Additionally instructor is pinned to a personal fork at a specific commit with no tracked deadline for reverting, which is an unreviewed external dependency. These two issues together warrant caution before merging.

tests/unit/pipelex/runtime_bridge/test_dispatch.py needs the temporal extra guard mocked; pyproject.toml needs the instructor fork pin addressed and the stale 3.10/3.11 classifiers removed.

Important Files Changed

Filename Overview
pipelex/runtime_bridge/bridge.py New framework-agnostic bridge entry-point; solid dispatch logic, but _scoped_library_for_crate doesn't restore the prior current library on teardown, risking state corruption in concurrent workers.
pipelex/runtime_bridge/exceptions.py New exception hierarchy for the bridge; minor naming inconsistency (PipelexRuntimeBridgeError base vs PipelexBridgeRuntimeError leaf) but functionally sound.
pipelex/runtime_bridge/execution_mode.py Clean StrEnum with stable string values and two well-tested boolean properties; no issues.
pipelex/runtime_bridge/bootstrap.py Idempotent ensure_pipelex_booted helper; straightforward singleton guard.
pyproject.toml Raises requires-python to 3.12, bumps mistralai to 2.x and temporalio to 1.24; classifiers still list 3.10/3.11, and instructor is pinned to an unreviewed personal git fork.
tests/unit/pipelex/runtime_bridge/test_dispatch.py Dispatch unit tests for TEMPORAL modes don't mock _require_pipelex_temporal_extra, so they fail in environments without the temporal extra installed.
pipelex/plugins/mistral/mistral_llm_worker.py Updates to mistralai.client.* import paths for SDK 2.x; adds a null-check for response.choices[0].message that was missing before.
pipelex/plugins/mistral/mistral_llms.py Switches from Data to `BaseModelCard
tests/unit/pipelex/runtime_bridge/test_validation.py Thorough validation and decoding tests; no issues found.
tests/integration/pipelex/runtime_bridge/test_bridge_direct.py Integration tests cover DIRECT mode end-to-end including library_crate_dump round-trip and dynamic concept serialisation; no issues.

Comments Outside Diff (3)

  1. pyproject.toml, line 10-14 (link)

    P2 requires-python was raised to >=3.12 but the classifiers still advertise Python 3.10 and 3.11 support. PyPI and pip use the classifiers for package discovery; leaving stale classifiers may mislead installers or tooling that relies on them.

    Prompt To Fix With AI
    This is a comment left during a code review.
    Path: pyproject.toml
    Line: 10-14
    
    Comment:
    `requires-python` was raised to `>=3.12` but the classifiers still advertise Python 3.10 and 3.11 support. PyPI and pip use the classifiers for package discovery; leaving stale classifiers may mislead installers or tooling that relies on them.
    
    
    
    How can I resolve this? If you propose a fix, please make it concise.
  2. pyproject.toml, line 627-630 (link)

    P2 instructor pinned to an unreviewed personal git fork

    The new [tool.uv.sources] block pins instructor to github.com/Ian321/instructor.git at a specific commit hash. The comment explains it's a stopgap until instructor#2298 is merged. Supply-chain risk aside, this makes the package non-installable in air-gapped or git-restricted environments and locks in an unreviewed fork indefinitely unless the follow-up is tracked with urgency. The PR description's follow-up list does not include reverting this pin, which raises the chance it lingers.

    Prompt To Fix With AI
    This is a comment left during a code review.
    Path: pyproject.toml
    Line: 627-630
    
    Comment:
    **`instructor` pinned to an unreviewed personal git fork**
    
    The new `[tool.uv.sources]` block pins `instructor` to `github.com/Ian321/instructor.git` at a specific commit hash. The comment explains it's a stopgap until [instructor#2298](https://github.com/567-labs/instructor/pull/2298) is merged. Supply-chain risk aside, this makes the package non-installable in air-gapped or git-restricted environments and locks in an unreviewed fork indefinitely unless the follow-up is tracked with urgency. The PR description's follow-up list does not include reverting this pin, which raises the chance it lingers.
    
    How can I resolve this? If you propose a fix, please make it concise.
  3. pipelex/runtime_bridge/bridge.py, line 396-419 (link)

    P2 teardown_current_library() does not restore the previously active library

    set_current_library(library_id=library_id) replaces whatever library was active before the bridge was called, but the finally block unconditionally calls teardown_current_library() rather than restoring the prior library. If any caller had a current library set before invoking run_pipe_via_bridge with a library_crate_dump, that library is silently cleared on exit. In concurrent activity workers where multiple invocations share state, this can leave the current library in an undefined state for other in-flight calls.

    Prompt To Fix With AI
    This is a comment left during a code review.
    Path: pipelex/runtime_bridge/bridge.py
    Line: 396-419
    
    Comment:
    **`teardown_current_library()` does not restore the previously active library**
    
    `set_current_library(library_id=library_id)` replaces whatever library was active before the bridge was called, but the `finally` block unconditionally calls `teardown_current_library()` rather than restoring the prior library. If any caller had a current library set before invoking `run_pipe_via_bridge` with a `library_crate_dump`, that library is silently cleared on exit. In concurrent activity workers where multiple invocations share state, this can leave the current library in an undefined state for other in-flight calls.
    
    How can I resolve this? If you propose a fix, please make it concise.
Prompt To Fix All With AI
Fix the following 4 code review issues. Work through them one at a time, proposing concise fixes.

---

### Issue 1 of 4
tests/unit/pipelex/runtime_bridge/test_dispatch.py:69-94
**Temporal unit tests depend on `temporalio` being installed**

`_require_pipelex_temporal_extra()` is called inside `run_pipe_via_bridge` *before* the lazy import of `make_temporal_pipe_run`. In any CI environment that runs these unit tests without the `temporal` extra, the call will raise `MissingPipelexTemporalExtraError` before the mocked factory is ever reached — making the test fail with the wrong error. Adding `mocker.patch("pipelex.runtime_bridge.bridge._require_pipelex_temporal_extra")` to both temporal dispatch tests would make them truly isolated from the optional dependency. The same issue applies to `test_temporal_fire_and_forget_returns_workflow_id_without_completion`.

### Issue 2 of 4
pyproject.toml:10-14
`requires-python` was raised to `>=3.12` but the classifiers still advertise Python 3.10 and 3.11 support. PyPI and pip use the classifiers for package discovery; leaving stale classifiers may mislead installers or tooling that relies on them.

```suggestion
classifiers = [
  "Programming Language :: Python :: 3",
  "Programming Language :: Python :: 3.12",
```

### Issue 3 of 4
pyproject.toml:627-630
**`instructor` pinned to an unreviewed personal git fork**

The new `[tool.uv.sources]` block pins `instructor` to `github.com/Ian321/instructor.git` at a specific commit hash. The comment explains it's a stopgap until [instructor#2298](https://github.com/567-labs/instructor/pull/2298) is merged. Supply-chain risk aside, this makes the package non-installable in air-gapped or git-restricted environments and locks in an unreviewed fork indefinitely unless the follow-up is tracked with urgency. The PR description's follow-up list does not include reverting this pin, which raises the chance it lingers.

### Issue 4 of 4
pipelex/runtime_bridge/bridge.py:396-419
**`teardown_current_library()` does not restore the previously active library**

`set_current_library(library_id=library_id)` replaces whatever library was active before the bridge was called, but the `finally` block unconditionally calls `teardown_current_library()` rather than restoring the prior library. If any caller had a current library set before invoking `run_pipe_via_bridge` with a `library_crate_dump`, that library is silently cleared on exit. In concurrent activity workers where multiple invocations share state, this can leave the current library in an undefined state for other in-flight calls.

Reviews (1): Last reviewed commit: "Refactor and streamline Mistral Workflow..." | Re-trigger Greptile

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No issues found across 61 files

Comment on lines +69 to +94
async def test_temporal_blocking_dispatches_to_temporal_pipe_run(self, mocker: MockerFixture) -> None:
fake_job = _make_fake_pipe_job(mocker=mocker, pipe_code="fake_pipe", pipeline_run_id="caller-run-id")
mocker.patch(
"pipelex.runtime_bridge.bridge.build_pipe_job_from_input",
return_value=fake_job,
)

fake_output = PipeOutput(
working_memory=WorkingMemoryFactory.make_empty(),
pipeline_run_id="temporal-run-id",
)
fake_temporal_run = mocker.AsyncMock(return_value=fake_output)
fake_factory = mocker.patch("pipelex.temporal.tprl_pipe.temporal_pipe_run.make_temporal_pipe_run")
fake_factory.return_value.run = fake_temporal_run

result = await run_pipe_via_bridge(
PipelexPipeRunInput(
pipe_code="fake_pipe",
execution_mode=PipelexExecutionMode.TEMPORAL_BLOCKING,
)
)

fake_factory.assert_called_once()
assert fake_temporal_run.await_count == 1
assert result.is_completed is True
assert result.workflow_id == "temporal-run-id"

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Temporal unit tests depend on temporalio being installed

_require_pipelex_temporal_extra() is called inside run_pipe_via_bridge before the lazy import of make_temporal_pipe_run. In any CI environment that runs these unit tests without the temporal extra, the call will raise MissingPipelexTemporalExtraError before the mocked factory is ever reached — making the test fail with the wrong error. Adding mocker.patch("pipelex.runtime_bridge.bridge._require_pipelex_temporal_extra") to both temporal dispatch tests would make them truly isolated from the optional dependency. The same issue applies to test_temporal_fire_and_forget_returns_workflow_id_without_completion.

Prompt To Fix With AI
This is a comment left during a code review.
Path: tests/unit/pipelex/runtime_bridge/test_dispatch.py
Line: 69-94

Comment:
**Temporal unit tests depend on `temporalio` being installed**

`_require_pipelex_temporal_extra()` is called inside `run_pipe_via_bridge` *before* the lazy import of `make_temporal_pipe_run`. In any CI environment that runs these unit tests without the `temporal` extra, the call will raise `MissingPipelexTemporalExtraError` before the mocked factory is ever reached — making the test fail with the wrong error. Adding `mocker.patch("pipelex.runtime_bridge.bridge._require_pipelex_temporal_extra")` to both temporal dispatch tests would make them truly isolated from the optional dependency. The same issue applies to `test_temporal_fire_and_forget_returns_workflow_id_without_completion`.

How can I resolve this? If you propose a fix, please make it concise.

@lchoquel lchoquel closed this May 11, 2026
@lchoquel
lchoquel deleted the feature/Adapt-mistral-workflows branch May 11, 2026 13:23
@github-actions github-actions Bot locked and limited conversation to collaborators May 11, 2026
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant