Move the canonical API into crewai.flow while preserving experimental imports and declarative references through compatibility aliases.
Co-authored-by: Cursor <cursoragent@cursor.com>
* fix(flows): persist custom conversational replies after fallback append
Custom @listen routes that only return a public string were appended after kickoff, so @persist snapshots missed the assistant turn. Snapshot again from the same persist path once the fallback writes state.messages.
* test(flows): cover @persist restore of custom conversational replies
Add regression coverage for custom @listen returns across fresh Flow instances, no double-append on built-in converse, and a single user/assistant message-added event.
* docs: note that public listen returns persist as assistant replies
* feat(flows): enhance conversational flow documentation and APIs
- Updated the description to clarify the use of `handle_turn` and structured streaming in multi-turn chat applications.
- Added a warning about the experimental nature of the conversational features.
- Improved the overview section to include structured streaming and refined the explanation of session handling.
- Enhanced the API documentation for `handle_turn`, `stream_turn`, and `chat` methods, emphasizing their roles in conversational flows.
- Clarified the turn lifecycle and the handling of user messages within the flow.
- Updated examples to reflect changes in message handling and session tracing.
- Ensured consistency across language versions in the documentation.
* feat(flow): deprecate answer_from_history route
Guide conversational flows toward the existing converse route while preserving compatibility warnings and schema metadata.
Co-authored-by: Cursor <cursoragent@cursor.com>
* stacklevel=3 raising it higher
---------
Co-authored-by: Cursor <cursoragent@cursor.com>
* feat(flow): let a declaration name the router's response format
`conversational.router.response_format` was typed `Any` and dropped with a
warning, because `_router_response_format` hands its value straight to
`llm.call(response_format=...)`, which needs a real class. So the router always
used its synthesized fallback: `intent: str` with the route labels only in a
field description.
The field now takes the same `{"python": "module.path.Class"}` shape a crew
agent's `response_format` uses, resolved through the same
`_resolve_model_class`. That brings the project-root containment with it -- a
declaration cannot reach outside the project to import code -- and gives the
router a `Literal[...]` of the real route labels instead of a bare string.
The DSL projection now emits that shape too, so a live class on a Python flow
round-trips as `{"python": ...}` rather than an opaque `{"ref": ...}` that
nothing could reload.
A bare `module:qualname` ref is now a load-time validation error instead of
being silently discarded; the test that pinned the old drop-with-warning
behavior is updated to assert that.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* fix(flow): do not project a response format that cannot be reloaded
`_python_reference` emitted a dotted path for any class, including two that
cannot be imported back: a non-Pydantic class, and one defined inside a
function, whose `__qualname__` carries `<locals>`. The definition then held a
ref that only failed when something tried to resolve it.
Both are dropped at projection time with a warning naming why, so a reload
falls back to the synthesized response format. The live class still drives the
running flow; only the projection omits it.
Found by CodeRabbit on #7063.
* test(flows): assert the response_format omission warnings
The projection tests only checked for None, so removing the warning that tells
an author their response_format was dropped would still pass.
Found by CodeRabbit on #7063.
* fix(flows): only project a response_format ref that imports back
The check rejected <locals> classes but still emitted a path for a nested one.
The loader splits a ref on its last dot, so module.Outer.Route resolves
module.Outer as a module that does not exist - proven: reload raised
JSONProjectError. A create_model() class held only in a local is unreachable
the same way.
Projection now confirms module.qualname resolves back to the class, against the
already-imported module so it never triggers an import.
Found by CodeRabbit on #7063.
---------
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
* feat(flow): let a chat flow declare its own state shape
A conversational declaration could only use `state: {type: pydantic, ref: ...}`
pointing at a `ConversationState` subclass. Every other shape loaded clean and
then died on the first turn -- inline `json_schema` and a non-subclass ref with
`AttributeError: 'StateWithId' object has no attribute 'messages'`, and
`type: dict` with `AttributeError: 'dict' object has no attribute 'id'`.
`Flow._compose_extension_state_model` is a new runtime extension seam -- the
seventh alongside the existing six -- applied to the model built from `state:`
before the engine wraps it for `id`. The conversational mixin uses it to add
the chat fields to whatever the declaration asked for, so declared fields and
defaults survive; a model that already extends `ConversationState` is returned
untouched, so today's supported shape is a no-op.
`dict` and `unknown` state cannot carry those fields at all, so the default
extension state supplies the real shape (seeded from the declared defaults
where they fit) rather than forbidding it. Raising instead would break
construction, and `Flow[dict]` with `conversational = True` constructs today --
`crewai flow plot` and definition-only consumers would stop working.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* fix(flow): keep declared defaults and cover an unbuildable state model
Two review follow-ups on the declared-state work.
A `dict` state's defaults are arbitrary keys, and the fallback kept only the
ones matching `ConversationState`, so `{"type": "dict", "default": {"topic":
"ai"}}` lost `topic` before the first turn and an action reading `state.topic`
would fail. They are carried as extras now.
And a declared `pydantic`/`json_schema` state whose model cannot be built --
a bad ref, an invalid schema -- fell through to a plain dict with none of the
chat fields, so the turn died on `state.id` instead. The engine now re-asks the
extension in that case, as if nothing had been declared.
Found by Cursor and CodeRabbit on #7061.
* refactor(flows): drop the unreachable extension-state fallback
The _initial_state_t branch sat after an unconditional return. The
state_definition is None case and _conversation_state_with_defaults now cover
every path that used to reach it.
Found by Cursor on #7061.
---------
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
* feat(flow): accept crew-style LLM config in a conversational declaration
`conversational.llm` / `intent_llm` / `answer_from_history_llm` and
`router.llm` only accepted a model-id string, so a declaration could not set
`max_tokens`, `temperature` or anything else a declarative crew's agent can.
`_coerce_llm` now delegates to `crewai.utilities.llm_utils.create_llm` -- the
same helper the crew/agent declaration layer resolves through -- so the shapes
match: a model-id string, a config mapping, an `LLMDefinition`, or a live
`LLM`/`BaseLLM` passed straight through.
It keeps one thing `create_llm` does not: `create_llm(1234)` takes the int as a
model name and returns an LLM that only fails later with a provider error. A
declaration is hand-written, so a non-string, non-mapping value raises now
instead. A mapping missing `model` keeps `create_llm`'s own message.
The contract fields stay permissively typed and gain descriptions naming the
accepted shapes. Tightening them to `str | LLMDefinition` would break the DSL
projection: a live custom `BaseLLM` whose config dump lacks a `model` key
degrades to a `{"ref": ...}` mapping, which such a type would reject -- turning
`flow_definition()` on an existing Python flow into a validation error.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* fix(flow): resolve declared LLM mappings for intent classification
`intent_llm` and `answer_from_history_llm` were documented as taking the same
shapes as `llm`, but both route through `_collapse_to_outcome`, whose own
coercion accepts only `str | BaseLLM`. A declared config mapping reached it
unchanged and raised "Invalid llm type: <class 'dict'>" mid-turn -- so the
documentation promised something that failed.
Also fixes the field descriptions. The previous commit's `llm` description
landed on `FlowConversationalRouterDefinition.llm` rather than
`FlowConversationalDefinition.llm`, because both fields are literally
`llm: Any = None` and the first match won. All four now describe their own
field and name every accepted shape, including `LLMDefinition` and a live
instance.
Test changes: adds an `LLMDefinition` resolution case, and the declared-mapping
turn test now patches `create_llm` to prove the mapping reaches it instead of
swapping the config out beforehand, which proved nothing.
Found by Cursor and CodeRabbit on #7062.
* docs(flows): name the full router LLM precedence; pin the coercion path
The conversational llm description skipped intent_llm in the router fallback
order (router.llm, then intent_llm, then llm), and the intent_llm test replaced
the declared mapping with a scripted LLM before the turn, so it never exercised
the coercion. Patch create_llm instead: removing the coercion now fails the
test with "Invalid llm type: <class 'dict'>".
Found by CodeRabbit on #7062.
---------
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
* test(flow): unskip the conversational end-to-end suite
The `conversational_graph_broken` marker parked 21 end-to-end conversational
tests with the reason "the definition-first start migration intentionally
stopped scanning inherited methods, so that graph no longer registers".
That is no longer true: `_iter_flow_methods` walks the MRO for
`__conversational_only__` methods (dsl/_utils.py:406-420), so a
`conversational = True` subclass does register `route_conversation`,
`converse_turn`, `end_conversation` and `answer_from_history_turn` — which
`test_flow_definition.py:391-407` already asserts.
Removing the marker takes the file from 47 passed / 21 skipped to 68 passed.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* feat(flow): make conversational opt-in unmistakable
Opting a Flow into chat took two statements, and forgetting one failed
silently. With `@ConversationConfig(...)` but no `conversational = True`:
`FlowDefinition.conversational` came back `None`, the built-in graph never
registered, and `handle_turn()` returned `None` without appending a message
or raising — while `chat()` reported "only available on conversational flows"
on a class that was literally decorated with a conversational config.
Three changes:
- `ConversationConfig.__call__` now also sets `conversational = True`. Every
field on the config is consumed only by the conversational graph, so a
decorated non-conversational Flow could only ever discard it.
- `FlowConversationalDefinition.enabled` defaults to True. The block is absent
on non-conversational flows, so declaring it is the opt-in; `enabled: false`
remains an explicit opt-out.
- `handle_turn()` raises like `chat()` and `stream_turn()` already do instead
of silently returning `None`.
Setting `conversational = True` by hand still works and is still the way to
opt in without a config.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* feat(flow): let a declaration drive conversational mode
`FlowDefinition.conversational` was written by the DSL projection and read by
nothing: every conversational gate resolved through `type(self).flow_definition()`
— the class projection — instead of `self._definition`, the declaration a flow
was actually built from. So `Flow.from_declaration()` on a definition with a
conversational block produced a flow that reported itself non-conversational,
dropped the user message, and never registered its declared routes.
Resolution rules, applied consistently:
- Structure (enabled, methods, route labels, builtin/internal routes) comes
from `self._definition`, which is the loaded declaration for a declarative
flow and the class projection otherwise. Both paths now agree.
- Behavior (`conversational_config`) still prefers the class attribute, which
can hold live objects — a configured LLM, a custom BaseLLM, a response_format
model class — that the serializable definition degrades to a config dict or a
`module:qualname` ref. Reading the definition first would silently downgrade
every decorated Python flow. A declaration-built flow has no class config, so
`_config_from_definition` supplies one, cached for stable identity.
- A declared `state:` block is never replaced. `_create_default_extension_state`
is consulted before `_create_definition_state`, so returning `ConversationState`
there discarded every field the declaration asked for. It now yields to a
declared state and only supplies the default when nothing else does.
The class-scoped `_is_conversational` / `_conversational_definition`
classmethods are gone; the existing instance-scoped `_is_conversational_enabled`
is the single gate.
A router `response_format` that survived serialization as a ref or schema dict
is dropped with a warning rather than handed to `llm.call()`, which needs a
real class.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* feat(flow): synthesize the built-in conversational methods for declarations
A declaration carrying `conversational: {}` loaded clean and then ran zero
methods and returned `None`, because the four built-in graph handlers are
inherited from `_ConversationalMixin` and a declaration has nothing to inherit
from. Authors had to name `crewai.experimental.conversational_mixin:_Conversational
Mixin.route_conversation` and three siblings by hand.
`Flow._extend_definition` is a new runtime extension hook, called once
`_definition` is resolved and before methods are bound. The conversational
mixin overrides it to fill in `route_conversation`, `converse_turn`,
`end_conversation` and `answer_from_history_turn` when they are missing, using
the same code refs the DSL projection already emits so a declaration and a
class projection of the same flow produce identical method definitions.
Synthesis is deliberately a runtime concern, not a contract one:
`FlowDefinition` stays independent of the authoring layer and of the engine,
as `test_flow_definition_contract_is_dsl_agnostic` requires, and a loaded
declaration still serializes back to exactly what its author wrote.
Route descriptions are now carried by the contract. The DSL projects a handler
docstring's first line into `FlowMethodDefinition.description`, and the router
catalog reads that before falling back to the live docstring. This also fixes
a real defect: for a declarative flow `getattr(type(self), handler_name, None)`
is `None`, and the old code read `None.__doc__` — so the router LLM was told a
route's description was "The type of the None singleton."
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* fix(flow): let an agent or crew handler reply in a conversation (#7034)
`handle_turn` promotes a handler's return value to the assistant message when
the handler did not append one itself, but the check required `isinstance(result,
str)`. Declarative `agent` and `crew` actions return `LiteAgentOutput` and
`CrewOutput`, whose text lives on `.raw` — so the most natural declarative
handler was exactly the one whose reply never reached the transcript.
`_is_public_turn_result` now unwraps `.raw` before deciding, matching
`_stringify_result`, which already did. The routing-artefact guards are applied
to the unwrapped text, so an output echoing a route label or this turn's intent
is still not promoted.
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
* fix(flow): keep privately recorded agent results out of the transcript
Unwrapping `.raw` in `_is_public_turn_result` made the end-of-turn fallback
promote `LiteAgentOutput` / `CrewOutput` objects that the handler had already
recorded via `append_agent_result` with the default private visibility. That
call does not set `_assistant_reply_appended`, so the fallback republished the
very object the handler asked to keep private — defeating
`visible_agent_outputs`.
Reproduced against `main` for contrast: no leak before the unwrap, leak after.
`append_agent_result` now remembers the object it recorded for the duration of
the turn, and the fallback skips anything already routed that way. The check is
identity-based on purpose: a handler that records scratch work privately and
then returns a user-facing summary still gets that summary promoted, which a
simple "handler already handled it" flag would have broken.
Found by Cursor Bugbot on #7033.
* feat(flow): mark a declarative chat flow conversational on the instance
A declaration enables chat through `conversational.enabled`, without the
`conversational = True` class attribute. Callers outside this package
capability-check that attribute -- the AG-UI serving guide states it as a
requirement -- so it disagreed with `_is_conversational_enabled()` and a
declarative conversational flow looked non-conversational from outside.
`_extend_definition` now sets it on the instance when the definition enables
chat. Instance-only on purpose: the DSL projection reads the attribute off the
*class* to decide whether to emit a conversational block, so setting it there
would make every later subclass look conversational.
Verified on a real declarative flow: `conversational` and `stream_turn` both
now satisfy the documented capability check, while `Flow.conversational` and
any later subclass stay False.
* refactor(flow): derive routing-artefact labels from the effective routes
`_is_public_turn_result` matched a literal set of route labels, duplicating
knowledge that `_effective_builtin_routes()` already owns. A declaration that
adds a builtin route was not covered, so a handler echoing that label could be
promoted into the transcript -- the same class of divergence already fixed for
`route_turn`.
Verified the derived set is byte-identical to the old literal one for a
class-based flow, so this is a pure generalization: `conversation` and
`route_to_flow` stay explicit because neither is a route.
Also replaces a tuple-index lambda in the chat REPL test with a named
`input_fn`; it relied on tuple evaluation order and on the list being mutated
before its length was read.
Both found by CodeRabbit on #7033.
---------
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
Co-authored-by: ViditOstwal <viditostwal@gmail.com>
* test(flow): unskip the conversational end-to-end suite
The `conversational_graph_broken` marker parked 21 end-to-end conversational
tests with the reason "the definition-first start migration intentionally
stopped scanning inherited methods, so that graph no longer registers".
That is no longer true: `_iter_flow_methods` walks the MRO for
`__conversational_only__` methods (dsl/_utils.py:406-420), so a
`conversational = True` subclass does register `route_conversation`,
`converse_turn`, `end_conversation` and `answer_from_history_turn` — which
`test_flow_definition.py:391-407` already asserts.
Removing the marker takes the file from 47 passed / 21 skipped to 68 passed.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* feat(flow): make conversational opt-in unmistakable
Opting a Flow into chat took two statements, and forgetting one failed
silently. With `@ConversationConfig(...)` but no `conversational = True`:
`FlowDefinition.conversational` came back `None`, the built-in graph never
registered, and `handle_turn()` returned `None` without appending a message
or raising — while `chat()` reported "only available on conversational flows"
on a class that was literally decorated with a conversational config.
Three changes:
- `ConversationConfig.__call__` now also sets `conversational = True`. Every
field on the config is consumed only by the conversational graph, so a
decorated non-conversational Flow could only ever discard it.
- `FlowConversationalDefinition.enabled` defaults to True. The block is absent
on non-conversational flows, so declaring it is the opt-in; `enabled: false`
remains an explicit opt-out.
- `handle_turn()` raises like `chat()` and `stream_turn()` already do instead
of silently returning `None`.
Setting `conversational = True` by hand still works and is still the way to
opt in without a config.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* feat(flow): let a declaration drive conversational mode
`FlowDefinition.conversational` was written by the DSL projection and read by
nothing: every conversational gate resolved through `type(self).flow_definition()`
— the class projection — instead of `self._definition`, the declaration a flow
was actually built from. So `Flow.from_declaration()` on a definition with a
conversational block produced a flow that reported itself non-conversational,
dropped the user message, and never registered its declared routes.
Resolution rules, applied consistently:
- Structure (enabled, methods, route labels, builtin/internal routes) comes
from `self._definition`, which is the loaded declaration for a declarative
flow and the class projection otherwise. Both paths now agree.
- Behavior (`conversational_config`) still prefers the class attribute, which
can hold live objects — a configured LLM, a custom BaseLLM, a response_format
model class — that the serializable definition degrades to a config dict or a
`module:qualname` ref. Reading the definition first would silently downgrade
every decorated Python flow. A declaration-built flow has no class config, so
`_config_from_definition` supplies one, cached for stable identity.
- A declared `state:` block is never replaced. `_create_default_extension_state`
is consulted before `_create_definition_state`, so returning `ConversationState`
there discarded every field the declaration asked for. It now yields to a
declared state and only supplies the default when nothing else does.
The class-scoped `_is_conversational` / `_conversational_definition`
classmethods are gone; the existing instance-scoped `_is_conversational_enabled`
is the single gate.
A router `response_format` that survived serialization as a ref or schema dict
is dropped with a warning rather than handed to `llm.call()`, which needs a
real class.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
Co-authored-by: ViditOstwal <viditostwal@gmail.com>
Co-authored-by: Vidit Ostwal <110953813+Vidit-Ostwal@users.noreply.github.com>
* test(flow): unskip the conversational end-to-end suite
The `conversational_graph_broken` marker parked 21 end-to-end conversational
tests with the reason "the definition-first start migration intentionally
stopped scanning inherited methods, so that graph no longer registers".
That is no longer true: `_iter_flow_methods` walks the MRO for
`__conversational_only__` methods (dsl/_utils.py:406-420), so a
`conversational = True` subclass does register `route_conversation`,
`converse_turn`, `end_conversation` and `answer_from_history_turn` — which
`test_flow_definition.py:391-407` already asserts.
Removing the marker takes the file from 47 passed / 21 skipped to 68 passed.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* feat(flow): make conversational opt-in unmistakable
Opting a Flow into chat took two statements, and forgetting one failed
silently. With `@ConversationConfig(...)` but no `conversational = True`:
`FlowDefinition.conversational` came back `None`, the built-in graph never
registered, and `handle_turn()` returned `None` without appending a message
or raising — while `chat()` reported "only available on conversational flows"
on a class that was literally decorated with a conversational config.
Three changes:
- `ConversationConfig.__call__` now also sets `conversational = True`. Every
field on the config is consumed only by the conversational graph, so a
decorated non-conversational Flow could only ever discard it.
- `FlowConversationalDefinition.enabled` defaults to True. The block is absent
on non-conversational flows, so declaring it is the opt-in; `enabled: false`
remains an explicit opt-out.
- `handle_turn()` raises like `chat()` and `stream_turn()` already do instead
of silently returning `None`.
Setting `conversational = True` by hand still works and is still the way to
opt in without a config.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
The `conversational_graph_broken` marker parked 21 end-to-end conversational
tests with the reason "the definition-first start migration intentionally
stopped scanning inherited methods, so that graph no longer registers".
That is no longer true: `_iter_flow_methods` walks the MRO for
`__conversational_only__` methods (dsl/_utils.py:406-420), so a
`conversational = True` subclass does register `route_conversation`,
`converse_turn`, `end_conversation` and `answer_from_history_turn` — which
`test_flow_definition.py:391-407` already asserts.
Removing the marker takes the file from 47 passed / 21 skipped to 68 passed.
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
handle_turn() (and stream_turn) decided "did the handler append its
reply?" by snapshotting the assistant-message count before kickoff and
appending the stringified result when the count came back unchanged. A
handler that appends its reply and then trims state.messages to a cap —
a normal bounded-context pattern — left the count unchanged, so the
fallback appended the reply a second time on every turn once trimming
engaged, and the duplicates then crowded real turns out of the capped
window.
Replace the count heuristic with an explicit per-turn flag:
append_assistant_message() sets _assistant_reply_appended, handle_turn
and stream_turn clear it before kickoff and only fall back when no
assistant message was appended during the turn. The now-unused
_assistant_message_count() helper is removed.
Fixes EPD-181.
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
In conversational flows, a falsy return from an overridden route_turn()
fell back to the sticky state.last_intent from a previous turn, silently
re-running the prior turn's handler for an unhandled input.
The fallback exists for the legacy default_intents path, where
receive_user_message() classifies the intent fresh each turn. Track that
per-turn classification in _turn_classified_intent (cleared on every turn
reset) and route on it instead, so a falsy route_turn() now falls through
to the built-in answer_from_history/converse defaults and never reuses
stale routing state.
Fixes EPD-176.
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Co-authored-by: Lorenze Jay <63378463+lorenzejay@users.noreply.github.com>
* Track conversational flow turn usage in telemetry
* adjusted name to flow:conversation_turn
* only mark on turn completed event
* ensure tui also emits these events
`StateProxy` looked like a thread-safety boundary, but it only protected
a small slice of state operations. Some examples of operations that were
not covered:
- `self.state.counter += 1`, `self.state["counter"] += 1` (increments)
- `self.state.user.profile.score += 1` (nested object mutations)
- `self.state.config["limits"]["max"] = 10` (mutation through model fields)
- `self.state.items[0].status = "done"` (list/container mutations)
This commit decided to remove it completely for simplicity and
performance:
- Simpler runtime code
- attr read: 24x faster, attr write: 27x faster, list append: 19x faster (local benchmark)
- Clearer concurrency contract (lifecycle locks remain, but arbitrary
shared state mutation is not presented as thread-safe)
* improve one less route
* flows in flows, new agent executor causing early trace batch finalization
* addressing comments
* addressing comments pt2
* lint and typecheck fix
* decouple convo logic from runtime and added a conversational_definition
* type check fix
* always defer traces for convo and so fix tests to reflect that
* Migrate @listen/@router runtime to read from FlowDefinition
The runtime now resolves listener conditions, router status, and emit
values from `FlowMethodDefinition` instead of legacy method metadata and
the `_listeners`/`_routers`/`_router_emit` registries.
* Evaluate AND/OR listener conditions over the definition shape via
`_evaluate_definition_condition`
* Drop the class registries and the `FlowMeta` extraction that built
them; stop stamping `__trigger_methods__`, `__is_router__`,
`__router_emit__`, and friends
* `@human_feedback` emit now lives only on its config
* Simplify conditionals DSL
* Remove `_start_methods` and `__is_start_method__` stamping
* Add helpers to read start info from the definition
* Scan `__dict__` instead of `dir()` to find flow methods
* feat: add conversation message and route selection events
- Introduced `ConversationMessageAddedEvent` and `ConversationRouteSelectedEvent` to enhance conversational flow tracking.
- Updated event listeners to emit these events during message handling and routing decisions.
- Enhanced the `_ConversationalMixin` class to emit events for user and assistant messages, as well as selected routes.
- Added tests to verify the correct emission of these events during conversational turns.
* ensure flow started events only emiited once
* refactor(tracing): rename trace event handler methods to action event handlers
Updated the class to replace with for and events, improving clarity in event handling.
Additionally, adjusted comments in the class to clarify the application of pending user messages in relation to state restoration and flow scope initialization.
* fix(conversational_mixin): handle empty message index in route events
Updated the message index handling in the class to return when there are no messages. Added tests to ensure that route events do not reference index zero when the transcript is empty, and verified the correct emission of conversation message events during flow handling.
* feat: add conversational flows documentation and chat session support
- Introduced a new guide for building multi-turn chat applications using , detailing session management and message handling.
- Added class to facilitate chat interactions, including streaming support and event handling.
- Implemented for class-level defaults and improved input normalization for conversational turns.
- Enhanced event listeners to manage flow events and tracing more effectively, including support for nested crew executions.
- Added tests for conversational flow helpers and kickoff parameters to ensure functionality and reliability.
* linted
* feat: enhance flow event tracing and session management
- Updated TraceCollectionListener to handle nested flows without re-claiming parent session batches.
- Ensured that method execution events are always emitted for tracing, regardless of flow event suppression.
- Improved finalization logic for flow trace batches to respect session deferral flags.
- Added tests to verify that method execution events are emitted correctly when flow events are suppressed and that deferred session finalization is respected in nested flows.
* updated docs
* feat: introduce experimental conversational flow framework
- Added a new module for conversational flow, including classes for managing conversation state, messages, and events.
- Implemented and for structured intent handling and routing.
- Enhanced the class to support turn-oriented conversational applications with built-in routing and message handling.
- Updated to include new classes in the public API.
- Added tests to validate the functionality of the new conversational flow features.
* handled docs
* feat(flow): enhance conversational flow handling and tracing
- Introduced support for deferred multi-turn tracing to maintain continuous event sequences.
- Updated method to delegate to restored checkpoint flows, improving session management.
- Added tests to validate the new tracing behavior and ensure correct event handling in conversational flows.
* fix multimodal test
* better conversational
* adjusted prompt
* drop unused
* fix test
* refactor: rename to and update related documentation
This commit refactors the class to for clarity and consistency across the codebase. The documentation has been updated to reflect this change, ensuring that references to the new class are accurate. Additionally, the alias for legacy imports is maintained for backward compatibility. The changes enhance the overall structure and readability of the conversational flow implementation.
* fix test
* adding experimetnal indicators
* fix test and reloaded cassettes
* cleanup ConversationalFlow class
* addressing double finalization and fixed tests
* improve on emphemeral tracing and adddressing comments