fix(conversational): persist the assistant reply from custom @listen routes (#6766) - #6772
Conversation
…routes (crewAIInc#6766) @persist snapshots state per method inside kickoff(). handle_turn()'s fallback append -- the only thing that records the reply of a custom @listen route that just returns a string -- ran after the last snapshot, so that reply never reached the backend. Built-in routes were unaffected because they call append_assistant_message() inside the method body. Run each turn on a fresh Flow instance (the web-server pattern) and the assistant side of the conversation silently disappears from restored history, leaving a user-only monologue. Take one more snapshot right after the fallback append, in handle_turn() and stream_turn() alike.
📝 WalkthroughWalkthroughThe runtime adds a labeled snapshot hook and routes method completion persistence through it. Conversational and streaming turns persist fallback assistant replies after appending them. Tests cover persistence across fresh flow instances and prevent duplicate assistant messages. ChangesConversational persistence
Suggested reviewers: 🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
🧹 Nitpick comments (1)
lib/crewai/src/crewai/experimental/conversational_mixin.py (1)
430-430: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick winAdd persistence coverage for
stream_turn().No test runs
stream_turn()to completion, creates a fresh flow instance, and checks that the fallback assistant reply is restored from persistence. Add that behavior test to cover the same web-server restart pattern thatTestPersistedReplyFromCustomRouteuses forhandle_turn().🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@lib/crewai/src/crewai/experimental/conversational_mixin.py` at line 430, Add a persistence test for the conversational mixin’s stream_turn() flow, following the restart pattern used by TestPersistedReplyFromCustomRoute: run stream_turn() to completion, create a fresh flow instance, and verify the persisted fallback assistant reply is restored. Keep the existing handle_turn coverage unchanged and target the test around _persist_state_snapshot("stream_turn").Source: Coding guidelines
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Nitpick comments:
In `@lib/crewai/src/crewai/experimental/conversational_mixin.py`:
- Line 430: Add a persistence test for the conversational mixin’s stream_turn()
flow, following the restart pattern used by TestPersistedReplyFromCustomRoute:
run stream_turn() to completion, create a fresh flow instance, and verify the
persisted fallback assistant reply is restored. Keep the existing handle_turn
coverage unchanged and target the test around
_persist_state_snapshot("stream_turn").
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro Plus
Run ID: 59283000-1265-44ae-87bc-c35790b5a72c
📒 Files selected for processing (3)
lib/crewai/src/crewai/experimental/conversational_mixin.pylib/crewai/src/crewai/flow/runtime/__init__.pylib/crewai/tests/test_flow_conversation.py
Summary
Fixes #6766. Replies produced by a custom
@listenroute never reached the persistence backend, so across turns run on freshFlowinstances (the normal web-server pattern) the assistant side of the conversation silently disappeared and the agent could no longer recall its own prior statements.Root cause
@persistsnapshots state per method insidekickoff().handle_turn()appends the assistant reply afterkickoff()returns:That fallback append is the only thing recording the reply of a custom route that just returns a string, and it lands after the final snapshot. The built-in
converse_turnis unaffected because it callsappend_assistant_message()inside the method body, so its reply is part of that method's snapshot.Fix
Take one more snapshot right after the fallback append.
Flow._persist_method_completion()is split so the snapshot half is reusable as_persist_state_snapshot(label, persist_definition=None), defaulting to the flow-level@persistconfig;handle_turn()andstream_turn()call it after appending. When no@persistis configured it is a no-op, and handlers that append their own reply are untouched (the existing_assistant_reply_appendedguard already skips the fallback).Verification
Ran against
crewai==1.15.9, whoseconversational_mixin.pyandflow/runtime/__init__.pyare byte-identical tomain.Issue repro (LLM router replaced with an overridden
route_turn()so no API call is needed), before:after:
Test suites (
test_flow_conversation.py,test_flow_persistence.py,test_flow_persistence_factory.py,test_flow_from_definition.py): 207 passed, 21 skipped, with the single pre-existing failureTestDeferredFlowLifecycleEvents::test_finalize_batch_is_idempotentalso failing unpatched onmain.The new
TestPersistedReplyFromCustomRoute::test_custom_route_reply_survives_a_fresh_instancefails without the fix and passes with it; the companion test asserts a handler that appends its own reply still persists exactly one assistant message.ruff==0.15.1 check/formatwith the repopyproject.toml: no new findings (the 4I001reported are pre-existing and reproduce unpatched when the files are linted outside the full source tree).AI-assisted, human reviewed.
Generated with Claude Code