Move Pipecat Flows into core Pipecat (pipecat.flows) - #4882
Conversation
Codecov Report❌ Patch coverage is
🚀 New features to boost your workflow:
|
The fragment was placeholder-named while the PR didn't exist yet; now that it's #4882, rename it so towncrier links the changelog entry to the PR.
markbackman
left a comment
There was a problem hiding this comment.
LGTM. Before approving, I want to confirm that the Flows code is just a copy/paste of the source with corrected import paths.
| - [Example apps](https://github.com/pipecat-ai/pipecat-examples) — complete applications that you can use as starting points for development | ||
| Once installed, there are a few ways to start building: | ||
|
|
||
| ### From code examples |
There was a problem hiding this comment.
Nice catch.
We might want to first point people to the CLI, then examples.
Do we want Flows to be mentioned here too? We might be able to drop it.
There was a problem hiding this comment.
Restructured this file a bit since you reviewed, actually.
| 2. Install the module | ||
| ```bash | ||
| uv tool install "pipecat-ai[cli]" | ||
| pipecat create |
There was a problem hiding this comment.
Soon this will be init. Left it as create (the old way) since there are a few other places in this file that use the old paradigm, that we'll want to update simultaneously as part of the CLI-updating effort
markbackman
left a comment
There was a problem hiding this comment.
Looks good. Just a few small findings.
I'll have Claude do a review too, just to be sure.
| """Optional convention TypedDict for ``status``/``error`` results. | ||
|
|
||
| .. deprecated:: 1.5.0 | ||
| No replacement. ``FlowResult`` is no longer required or referenced by |
There was a problem hiding this comment.
To be compliant with the deprecations parser, the text should be:
No replacement. FlowResult is no longer required or referenced by any
handler type, and Pipecat's upstream function-call-result contract is
Any — define your own TypedDict or return any JSON-serializable value.
Will be removed in 2.0.0.
There was a problem hiding this comment.
Claude added this
One more thing on FlowResult beyond the text: it's a class, and our convention says classes/functions/methods/properties get the PEP 702 @deprecated decorator (not just the directive). I'd add it here.
Caveat worth knowing: on a TypedDict, @deprecated emits no runtime warning — constructing FlowResult(...) just builds a plain dict and bypasses the wrapped __new__. But it does set __deprecated__, so pyright/mypy and IDEs flag usages statically (e.g. a strikethrough on -> FlowResult). For an annotation-only type that's the only useful signal anyway, so the decorator is a strict improvement with no runtime downside.
Adding it also means cleaning the one internal reference: manager.py._call_handler was annotated -> FlowResult | ConsolidatedFunctionResult, which our own type-checking would then flag. Since the deprecation note itself says the real contract is Any, I changed it to Any | ConsolidatedFunctionResult and dropped the import. (The __init__.py re-export stays — exporting a deprecated symbol for backcompat is expected.)
Leaving it directive-only would also work — it's documented, in the registry, and the marker test passes either way; the only thing you'd lose is that static-checker detection. I lean toward adding the decorator, but flagging it since it's a judgment call.
There was a problem hiding this comment.
Fixed the deprecations-parser-compliance change with 4890d38
| summary_prompt: Required prompt text when using RESET_WITH_SUMMARY. | ||
|
|
||
| .. deprecated:: 1.5.0 | ||
| Deprecated along with RESET_WITH_SUMMARY. Use |
There was a problem hiding this comment.
Deprecation text should be:
Use ``LLMContextSummaryConfig.summarization_prompt`` instead
(deprecated along with ``RESET_WITH_SUMMARY``). Will be removed
in 2.0.0.
markbackman
left a comment
There was a problem hiding this comment.
Code review
Reviewed the move of Pipecat Flows into core for bugs and CLAUDE.md/AGENTS.md compliance. The import rewrite, re-exports, the standalone-Flows guard, and the task→worker deprecation handling all check out, and the new flows test suite passes. Four documentation/deprecation-convention issues are noted inline — all minor and non-blocking.
| from pipecat.flows.manager import FlowManager | ||
|
|
||
|
|
||
| class FlowResult(TypedDict, total=False): |
There was a problem hiding this comment.
FlowResult is missing the @deprecated decorator. It's registered as "kind": "class" in scripts/deprecations/deprecations.json and carries a .. deprecated:: directive, but a deprecated class must also carry the PEP 702 runtime decorator per AGENTS.md:
classes, functions, methods, and properties use the PEP 702
@deprecateddecorator frompipecat.utils.deprecationwith a string-literal message matching the canonical template
deprecated is already imported (line 34), and applying it to a TypedDict is valid:
| class FlowResult(TypedDict, total=False): | |
| @deprecated( | |
| "`FlowResult` is deprecated since 1.5.0 and will be removed in 2.0.0. No replacement." | |
| ) | |
| class FlowResult(TypedDict, total=False): |
There was a problem hiding this comment.
This is the same finding as above.
#4882 (comment)
| .. deprecated:: 1.5.0 | ||
| Use Pipecat's native context summarization instead. To trigger | ||
| on-demand summarization during a node transition, push an | ||
| ``LLMSummarizeContextFrame`` in a pre-action. See | ||
| https://docs.pipecat.ai/guides/fundamentals/context-summarization | ||
| Will be removed in 2.0.0. |
There was a problem hiding this comment.
Deprecation directive should lead with the concrete replacement. The body opens with "Use Pipecat's native context summarization instead." — a feature-area description — while the actual replacement symbol (LLMSummarizeContextFrame, which this entry records as the replacement in scripts/deprecations/deprecations.json) is buried in the second sentence. AGENTS.md:
Its body must lead with the replacement as the first reference —
Use :class:`X` instead.
Lead with the symbol, then give the how-to, e.g.:
.. deprecated:: 1.5.0
Use :class:`LLMSummarizeContextFrame` instead — push it in a pre-action to
trigger on-demand summarization during a node transition. See
https://docs.pipecat.ai/guides/fundamentals/context-summarization
Will be removed in 2.0.0.
The mirrored message field for this entry in scripts/deprecations/deprecations.json needs the same change.
| .. deprecated:: 1.5.0 | ||
| Deprecated along with RESET_WITH_SUMMARY. Use | ||
| ``LLMContextSummaryConfig.summarization_prompt`` instead. | ||
| Will be removed in 2.0.0. |
There was a problem hiding this comment.
Deprecation directive leads with a contextual reference. The body opens with "Deprecated along with RESET_WITH_SUMMARY." — a related-but-not-replacement API — before naming the replacement. AGENTS.md:
Its body must lead with the replacement as the first reference … never lead with a contextual reference (the deprecated thing itself, a
DeprecationWarning, or a related-but-not-replacement API)
Lead with the replacement instead, e.g.:
.. deprecated:: 1.5.0
Use ``LLMContextSummaryConfig.summarization_prompt`` instead. Deprecated
along with ``RESET_WITH_SUMMARY``; will be removed in 2.0.0.
The mirrored message field for the summary_prompt entry in scripts/deprecations/deprecations.json needs the same change.
There was a problem hiding this comment.
I think this is the same as #4882 (comment)
| # Typically, you would just switch LLMs like this: | ||
| # await flow_manager.task.queue_frames([ManuallySwitchServiceFrame(service=new_llm)]) | ||
|
|
||
| # But because we're in a tool call, and tool calls result in upstream | ||
| # updates from the assistant context aggregator, we're pushing the | ||
| # LLM-switching frame upstream from the aggregator to guarantee that the | ||
| # switch happens before the LLM is run with the tool call result. |
There was a problem hiding this comment.
Comment shows an alternative-not-taken that points at a deprecated API. The "Typically, you would just switch LLMs like this" lines demonstrate flow_manager.task.queue_frames(...), but FlowManager.task is deprecated in this very PR (replacement: worker) — so the "typical" example teaches the deprecated interface. Per AGENTS.md → Writing for Future Readers:
Leave the current moment out of it. … alternatives considered and not taken … usually isn't worth a future reader's time
Drop the commented-out alternative and keep the rationale for the actual approach:
| # Typically, you would just switch LLMs like this: | |
| # await flow_manager.task.queue_frames([ManuallySwitchServiceFrame(service=new_llm)]) | |
| # But because we're in a tool call, and tool calls result in upstream | |
| # updates from the assistant context aggregator, we're pushing the | |
| # LLM-switching frame upstream from the aggregator to guarantee that the | |
| # switch happens before the LLM is run with the tool call result. | |
| # Because we're in a tool call, and tool calls result in upstream | |
| # updates from the assistant context aggregator, we're pushing the | |
| # LLM-switching frame upstream from the aggregator to guarantee that the | |
| # switch happens before the LLM is run with the tool call result. |
There was a problem hiding this comment.
I agree with updating flow_manager.task to flow_manager.worker. I disagree with the feedback to remove mention of it entirely, as it's illustrative of what's going on. Fixing accordingly.
Pipecat Flows previously shipped as a separate pipecat-ai-flows package layered on top of pipecat-ai — a separate install, and a standing risk of version drift between the two. Fold the framework into core Pipecat under the pipecat.flows namespace so `from pipecat.flows import FlowManager` works with no extra install. Flows adds no new dependencies (loguru and docstring_parser are already core), so pyproject is unchanged. The deprecation markers carried Pipecat Flows' own version numbers, which are meaningless in pipecat-ai's single version line — re-baseline them onto it: everything that arrives already-deprecated reads "deprecated since 1.5.0" (the release that introduces pipecat.flows) and "removed in 2.0.0". While here, bring the markers up to Pipecat's conventions: convert the flows_direct_function alias and the FlowManager.task property to the @deprecated decorator, and regenerate the deprecation registry.
Bring the Flows test suite into tests/, renamed with a flows_ infix (test_actions.py -> test_flows_actions.py, etc.) so the files sit unambiguously alongside the existing suite; the shared helper module becomes tests/flows_test_helpers.py (no test_ prefix, so pytest doesn't collect it — mirroring tests/aic_mocks.py). Imports and @patch targets are rewritten from pipecat_flows to pipecat.flows. Two move-time adaptations: the tests now run against core pipecat's current OpenAI client, which rejects an empty api_key, so the dummy LLMs use api_key="test-key" (matching the rest of the suite); and the helper import points at tests.flows_test_helpers.
Relocate the Flows example apps under examples/flows/ alongside the rest of the Pipecat examples, with imports rewritten from pipecat_flows to pipecat.flows. The quickstart's hello_world.py is flattened to the top of the directory (no quickstart/ subfolder), and the hold-music asset used by warm_transfer.py moves with it. The examples already use Pipecat's current example conventions (WorkerRunner, the development runner, eval-transport support), so no code changes were needed beyond the namespace. The new examples/flows/README.md merges the old examples + quickstart READMEs and drops the separate-package install steps, since Flows now ships with pipecat-ai; the top-level examples README gains a flows/ entry.
Make the documentation pipeline aware of pipecat.flows now that Flows lives in core: - docs/api/conf.py: add pipecat.flows to the autodoc module list so the Sphinx API reference discovers it (apidoc already scans all of src/pipecat). - update-docs skill: add a Pipecat Flows section to SOURCE_DOC_MAPPING.md mapping flows/*.py to the existing api-reference/pipecat-flows/ and pipecat-flows/guides/ pages, and teach SKILL.md to detect src/pipecat/flows changes and check the Flows guides. This folds in the equivalent skill from the pipecat-flows repo. - update-docs.yml: trigger the docs workflow on src/pipecat/flows/** changes.
Now that Flows ships with pipecat-ai, surface it from the top-level README and tidy the surrounding structure: - Rename the install section to "Installation" and add a "Getting started" section with three on-ramps: from code examples, using the CLI, and using Pipecat Flows. - The code-examples list now leads with the getting-started examples (instead of "Foundational"), keeps a link to the full examples tree, and points at the Flows examples. - Reframe the ecosystem "Structured conversations" entry: Flows is built in (pipecat.flows), not a separate repo. - Normalize section headers to sentence case. Add a changelog fragment announcing the move and the pipecat_flows -> pipecat.flows import change, linking the archived pipecat-flows changelog.
The fragment was placeholder-named while the PR didn't exist yet; now that it's #4882, rename it so towncrier links the changelog entry to the PR.
The first pass added a standalone "Pipecat Flows" section with its own API-reference, guide, and other-pages tables wedged between the mapping's tiers — which broke the tiered-lookup structure the skill relies on (Step 4: tier 1 known exceptions → tier 2 patterns → tier 3 search fallback). Spot-checking the search fallback against the docs repo confirmed the explicit mappings earn their keep: grepping a class name resolves to 0 pages (`flows/adapters.py`'s `LLMAdapter` isn't named in `overview.mdx`) or to many (`FlowManager` → 4 Flows pages), so Flows can't rely on the fallback. So the Flows reference pages now sit in the tier-1 explicit table alongside the other non-pattern entries (RTVI, the pipeline, transport params), in the same two-column shape. The guide tables are dropped from the mapping entirely — Step 7 already finds guides by grepping class names, now across both `pipecat/` and `pipecat-flows/`.
Step 3 of the skill already ignores every `__init__.py` globally, so no other `__init__.py` is singled out in the skip list. Listing `flows/__init__.py` explicitly was inconsistent and unnecessary.
… Flows Older `pipecat-ai-flows` releases allow `pipecat-ai<2`, so one can be installed next to a Pipecat that already includes Flows as `pipecat.flows` — a redundant, easily confused setup that the standalone package's own pin can't prevent retroactively. On import of `pipecat.flows`, detect the standalone package (via `find_spec`, without importing it, so its own deprecation warning doesn't also fire) and log an error telling the user to drop `pipecat-ai-flows` and import from `pipecat.flows`.
- Fold Installation into a single "Getting started" with uv as a shared prerequisite up top, then two paths: starting a new project with the CLI (links the quickstart guide and CLI reference) and manual installation (the uv steps), with a segue into the examples. - Promote "Code examples" back to its own section; drop the getting-started examples bullet and rename "All examples" to "Focused examples" — small agents that each illustrate one or two specific services or concepts. - Trim the ecosystem "Structured conversations" entry to a one-liner linking the Flows guide (no import detail), and drop the now-redundant "Using Pipecat Flows" sub-section.
"Contributing to the framework" and "Contributing" read as near-duplicates. Rename the dev-environment-setup section to "Developing Pipecat" so the word "Contributing" is unique to the community-process section, and repoint the two example READMEs that linked to its anchor. Also tidy the Flows examples README: drop the redundant import-path note, retitle the intro example section to "Hello, world", and trim its blurb (the run/connect steps it duplicated live in Setup).
The coexistence guard lived in pipecat.flows.__init__, so it only ran when something imported pipecat.flows. But an app still on the standalone package does `from pipecat_flows import ...`, which pulls in core pipecat (and its __init__) but never pipecat.flows — so the guard never fired, and the most likely misconfiguration (running an old Flows app against a Flows-bearing Pipecat) went unwarned. Move the check to the top-level pipecat/__init__.py, which runs on any pipecat import. Gate it like the version banner so it stays quiet for the pipecat/pc CLI. Reword the changelog accordingly.
The deprecation directives carried over with the Flows source pass the audit's parse gate but produced wrong records in the generated deprecation registry, because the generator's "first reference is the replacement" rule keys off the first backticked token, not the prose. - FlowResult's no-replacement directive backticked the class itself, so the registry recorded FlowResult as its own replacement (relation use_existing). Drop the backticks throughout the body so it reads as a true no-replacement deprecation (relation none). - ContextStrategyConfig.summary_prompt led with the contextual "Deprecated along with RESET_WITH_SUMMARY" clause; lead with the replacement instead so the first reference is the real target. - Tidy a run-on where the RESET_WITH_SUMMARY guide URL butted against the removal sentence. Regenerate deprecations.json to match.
Per the deprecation convention, classes/functions/methods/properties get the PEP 702 @deprecated decorator in addition to the directive, so type checkers and IDEs flag usages statically. FlowResult only had the directive. On a TypedDict the decorator emits no runtime warning — constructing FlowResult(...) builds a plain dict and bypasses the wrapped __new__ — but it does set __deprecated__, which is the only useful signal for an annotation-only type anyway, so it's a strict improvement. Adding it means our own type-checking would flag the one internal use, FlowManager._call_handler's return annotation. The deprecation note already states the real contract is Any, so widen it to Any | ConsolidatedFunctionResult and drop the import. The __init__ re-export stays — exporting a deprecated symbol for backcompat is expected.
The deprecation directive opened with the feature-area description
("Use Pipecat's native context summarization instead"), burying the
concrete replacement symbol in the second sentence. The convention is to
lead with the replacement as the first reference, so it's also what the
registry records as the replacement.
Lead with :class:`LLMSummarizeContextFrame`, then the how-to, and
regenerate the mirrored message in deprecations.json.
The CLI's `create` command has been folded into `init` on main, so the getting-started example's `pipecat create` no longer resolves. Point it at `pipecat init`.
22a8329 to
3ec7e44
Compare
| pipecat init | ||
| ``` | ||
|
|
||
| Follow the [quickstart guide](https://docs.pipecat.ai/getting-started/quickstart) to get your first bot running, or see the [CLI reference](https://docs.pipecat.ai/api-reference/cli/overview) for more options. |
There was a problem hiding this comment.
Instead of the CLI option, we might want to link to:
https://docs.pipecat.ai/pipecat/get-started/build-your-next-bot
You can see the staged content, which will be released in the next version:
https://daily-main.mintlify.app/pipecat/get-started/build-your-next-bot
markbackman
left a comment
There was a problem hiding this comment.
LGTM!
Just one small, non-blocking suggestion in the README.
The pipecat.flows reference pages are generated and reachable by URL, but the API reference sidebar (docs/api/index.rst) is a hand-curated, hidden toctree that didn't list Flows after it moved into core Pipecat (pipecat-ai#4882). Add an entry between Extensions and Frames so Flows is discoverable in the reference-server.pipecat.ai navigation.
Pipecat Flows moved from its standalone package/repo into core Pipecat (pipecat-ai/pipecat#4882). Update the docs so Flows reads as built in rather than a separate add-on: - Drop the separate `uv add pipecat-ai-flows` install; Flows ships with pipecat-ai - Update imports from `pipecat_flows` to `pipecat.flows` - Repoint repo and example links to pipecat-ai/pipecat (examples/flows/) - Point the Flows API reference card at reference-server.pipecat.ai - Reword "add-on framework" and the Pipecat/Flows relationship section to describe Flows as a layer built on the pipeline - Add a namespace note to the 1.0 migration guide
Summary
Pipecat Flows previously shipped as a separate
pipecat-ai-flowspackage layered on top ofpipecat-ai— a separate install, and a standing risk of version drift between the two. This PR folds the framework into core Pipecat under thepipecat.flowsnamespace, sofrom pipecat.flows import FlowManagerworks out of the box with nothing extra to install.Flows adds no new dependencies (
loguruanddocstring_parserare already core), sopyproject.tomlis unchanged and there is no new extra.What's here
src/pipecat/flows/. Internal imports rewrittenpipecat_flows.*→pipecat.flows.*.pipecat.flows) and removed in 2.0.0. While here, the markers were brought to Pipecat's conventions — theflows_direct_functionalias and theFlowManager.taskproperty now use the@deprecateddecorator — and the deprecation registry was regenerated.pipecatimport, if the deprecated standalonepipecat-ai-flowspackage is also installed (detected viafind_spec, without importing it), Pipecat logs an error telling the user to uninstall it. It lives in the top-levelpipecat/__init__.pyso it fires even for apps still importing the oldpipecat_flows(which never importspipecat.flows) — catching already-publishedpipecat-ai-flowsversions that allowpipecat-ai<2and so can sit alongside built-in Flows.tests/, renamed with aflows_infix; shared helper →tests/flows_test_helpers.py.examples/flows/(the quickstart'shello_world.pyflattened to the top); merged README dropping the separate-install steps;flows/entry added to the examples index.pipecat.flowsadded to the Sphinx autodoc list; theupdate-docsskill and workflow now cover Flows (reference pages mapped in the tier-1 table; guides handled by the existing grep step).Decisions baked in
pipecat.flows; clean file copy (full history stays in the archived flows repo, linked from the changelog).pipecat-ai-flowspackage gets a final frozen-snapshot release, then the repo is archived — see the companion PR pipecat-flows#290 (which also explains why a clean break rather than apipecat_flows→pipecat.flowsshim).Verification
ruff format --check+ruff check: cleanpyright: 0 errorsNote on versioning
The introducing release is assumed to be 1.5.0. If it ends up different, update the
.. deprecated:: 1.5.0directives and the changelog fragment here, and the companion PR's dependency cap (< <introducing version>) and README reference.🤖 Generated with Claude Code