mirror of
https://github.com/crewAIInc/crewAI.git
synced 2026-09-21 10:26:25 +00:00
fix(cli): open the conversational TUI for a declarative chat flow (#7060)
* fix(cli): open the conversational TUI for a declarative chat flow `crewai run` refused a declarative conversational flow and told the user to drive it from Python. That was wrong: the conversational TUI already exists and already does this job. `CrewRunApp(conversational=True)` renders a chat pane and drives `handle_turn` per message (crew_run_tui.py:833-935), and `kickoff_flow._run_conversational_flow_tui` launches it for a Python conversational Flow. A declaration-built flow satisfies everything that TUI needs -- `handle_turn`, a settable `defer_trace_finalization`, and `finalize_session_traces()` -- so it now routes there instead of exiting. A chat loop still needs a terminal. A headless run (`is_interactive()` false, which folds in CREWAI_DMN) says what it would have needed rather than kicking off a single turn and presenting that as the whole conversation. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix(cli): do not send a human-feedback chat flow to the Textual TUI A declaration can carry both a `conversational:` block and a method with `human_feedback:` -- verified, both predicates return True on the same flow. Routing it to the chat TUI hangs: the runtime collects feedback with a blocking `input()` (flow/runtime/__init__.py:3719) that Textual cannot service, so the prompt is never shown. The STEPS TUI already declines these for exactly this reason. Such a flow now falls back to the terminal REPL, which can prompt. Also updates the guide in en/ar/ko/pt-BR: it still said `crewai run` has no chat loop and exits, which is now the opposite of what the CLI does. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> * fix(cli): reject --inputs on a conversational flow instead of dropping it The conversational branch returns before `_resolve_flow_inputs`, and the TUI calls `handle_turn(message)` -- which owns the kickoff inputs, passing `{"id": session_id}` itself. So any `--inputs` value was silently discarded and the conversation ran as if it had been applied. It now errors, and says that resuming a session by id is not wired up yet rather than implying it worked. Also corrects the Arabic guide: `مُوجّه محجوز` reads as "reserved router", not the blocking prompt it describes. Both found by CodeRabbit on #7060. * fix(cli): document the conversational routing exceptions Three review follow-ups: - The Arabic guide read خدمته (masculine) against the feminine مُطالبة introduced by the last fix. - Both docstrings described a routing path that now has exceptions: a conversational declaration rejects --inputs and skips state-schema resolution, and a human-feedback one uses the terminal REPL. - The --inputs rejection test accepted SystemExit(0); it now pins code 1. Found by CodeRabbit on #7060. * fix(cli): reject --inputs on a chat flow even when it parses empty parse_inputs_json returns {} both when the option is absent and when the user passes --inputs "{}", so the falsy check started the TUI for the second case while the docs said it was unsupported. The conversational path now takes whether the option was supplied, not what it parsed to. Documents the restriction in en, ar, ko and pt-BR. Found by CodeRabbit on #7060. * test(cli): pin the headless conversational exit status pytest.raises(SystemExit) also accepts SystemExit(0), so the error path could regress to a successful exit unnoticed. Found by CodeRabbit on #7060. --------- Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -59,6 +59,10 @@ def run_declarative_flow(definition: str | Path, inputs: str | None = None) -> N
|
||||
JSON is layered on top as an override, missing required fields are prompted
|
||||
for interactively, and everything is validated against the schema before
|
||||
kickoff — so a bare ``crewai run`` on a configured flow just works.
|
||||
|
||||
A conversational declaration takes none of that: each turn's input is the
|
||||
message typed into the chat, so ``--inputs`` is rejected and no
|
||||
state-schema resolution runs.
|
||||
"""
|
||||
# Load the project's .env before kickoff, mirroring the JSON-crew path
|
||||
# (run_crew._run_json_crew) so flow projects pick up API keys/config the
|
||||
@@ -74,15 +78,8 @@ def run_declarative_flow(definition: str | Path, inputs: str | None = None) -> N
|
||||
flow = load_declarative_flow(definition)
|
||||
|
||||
if _flow_is_conversational(flow):
|
||||
click.secho(
|
||||
" This flow declares `conversational`, and `crewai run` has no chat "
|
||||
"loop yet — it would run a single turn and exit.\n"
|
||||
" Drive it from Python for now: `flow.chat()` for a terminal REPL, "
|
||||
"or `flow.handle_turn(message, session_id=...)` per message.",
|
||||
fg="yellow",
|
||||
err=True,
|
||||
)
|
||||
raise SystemExit(1)
|
||||
_run_conversational_declarative_flow(flow, inputs is not None)
|
||||
return
|
||||
|
||||
resolved_inputs = _resolve_flow_inputs(flow, provided)
|
||||
|
||||
@@ -108,6 +105,62 @@ def run_declarative_flow(definition: str | Path, inputs: str | None = None) -> N
|
||||
click.echo(_format_result(result))
|
||||
|
||||
|
||||
def _run_conversational_declarative_flow(
|
||||
flow: Flow[Any], inputs_supplied: bool
|
||||
) -> None:
|
||||
"""Run a declarative chat flow on the conversational TUI.
|
||||
|
||||
The same TUI a Python conversational Flow gets from ``crewai run``; it
|
||||
drives ``handle_turn`` per message. A chat loop needs a terminal, so a
|
||||
headless run says what it would have needed rather than kicking off one
|
||||
turn and exiting as if that were the whole conversation.
|
||||
|
||||
Two flows do not reach that TUI: one passed ``--inputs``, which it has
|
||||
nowhere to put, and one using ``@human_feedback``, which needs the
|
||||
terminal ``flow.chat()`` REPL because the runtime collects feedback with a
|
||||
blocking prompt Textual cannot service.
|
||||
"""
|
||||
if inputs_supplied:
|
||||
# Whether ``--inputs`` was passed at all, not whether it parsed to
|
||||
# anything: ``--inputs '{}'`` is a request for something unsupported and
|
||||
# has to be answered, not silently accepted as no inputs.
|
||||
# The TUI calls ``handle_turn(message)``, which owns the kickoff inputs
|
||||
# (it passes ``{"id": session_id}`` itself). There is nowhere to put
|
||||
# these without fighting it, so say so rather than accepting them and
|
||||
# running a conversation that quietly ignored them.
|
||||
click.secho(
|
||||
" `--inputs` is not supported for a conversational flow: each turn's "
|
||||
"input is the message you type.\n"
|
||||
" Resuming a session by id is not wired up yet — use "
|
||||
"`flow.handle_turn(message, session_id=...)` from Python for that.",
|
||||
fg="red",
|
||||
err=True,
|
||||
)
|
||||
raise SystemExit(1)
|
||||
|
||||
if not is_interactive():
|
||||
click.secho(
|
||||
" This flow is conversational, which needs an interactive terminal.\n"
|
||||
" Drive it from Python instead: `flow.handle_turn(message, "
|
||||
"session_id=...)` per message, or `flow.stream_turn(...)` to stream.",
|
||||
fg="yellow",
|
||||
err=True,
|
||||
)
|
||||
raise SystemExit(1)
|
||||
|
||||
if _flow_uses_human_feedback(flow):
|
||||
# Same reason the STEPS TUI declines these: the runtime collects feedback
|
||||
# with a blocking ``input()`` (flow/runtime/__init__.py), which Textual
|
||||
# cannot service -- the prompt would never be shown and the run would
|
||||
# hang. A terminal REPL can, so fall back to one.
|
||||
flow.chat()
|
||||
return
|
||||
|
||||
from crewai_cli.kickoff_flow import _run_conversational_flow_tui
|
||||
|
||||
_run_conversational_flow_tui(flow)
|
||||
|
||||
|
||||
def _run_declarative_flow_tui(
|
||||
flow: Flow[Any], resolved_inputs: dict[str, Any] | None
|
||||
) -> Any:
|
||||
|
||||
Reference in New Issue
Block a user