Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 3 additions & 8 deletions packages/deepctl-cmd-listen/src/deepctl_cmd_listen/command.py
Original file line number Diff line number Diff line change
Expand Up @@ -258,6 +258,7 @@ def handle(
) -> BaseResult:
source: str | None = kwargs.get("source")
use_mic: bool = kwargs.get("mic", False)
guided_flow = False

# ── Resolve mode ───────────────────────────────────────────────
if source == "-":
Expand All @@ -280,12 +281,12 @@ def handle(
),
)
else:
# Interactive: ask the user
selected_mode, selected_source = self._interactive_select_source()
if not selected_mode:
return BaseResult(status="cancelled", message="Cancelled.")
mode = selected_mode
source = selected_source
guided_flow = True

# ── Gather options ─────────────────────────────────────────────
model = kwargs.get("model") or "nova-3"
Expand Down Expand Up @@ -322,13 +323,7 @@ def handle(
if caption_format and not diarize:
diarize = False # keep explicit — user can add --diarize themselves

# Interactive feature selection when user chose source interactively
# (signals they want a guided experience)
if (
not _agentic
and mode in ("prerecorded_file", "prerecorded_url")
and not any([diarize, summarize, topics, sentiment])
):
if guided_flow and mode in ("prerecorded_file", "prerecorded_url"):
diarize, summarize, topics, sentiment = self._interactive_features()

# ── Dispatch ───────────────────────────────────────────────────
Expand Down
92 changes: 92 additions & 0 deletions packages/deepctl-cmd-listen/tests/unit/test_listen_command.py
Original file line number Diff line number Diff line change
Expand Up @@ -241,6 +241,98 @@ def test_handle_explicit_stdin_dash(
assert result.source == "stdin"


class TestGuidedFlow:
"""Verify the guided-flow gate: prompts only fire on bare `dg listen`."""

@pytest.fixture
def command(self):
return ListenCommand()

@pytest.fixture
def common_kwargs(self):
return {
"client": Mock(spec=DeepgramClient),
"config": Mock(spec=Config),
"auth_manager": Mock(spec=AuthManager),
}

def test_url_arg_skips_both_prompts(self, command, common_kwargs):
with patch.object(command, "_interactive_features") as feat, patch.object(
command, "_interactive_select_source"
) as src, patch.object(
command, "_prerecorded", return_value=BaseResult(status="ok")
):
command.handle(
**common_kwargs, source="https://example.com/audio.wav"
)
assert feat.call_count == 0
assert src.call_count == 0

def test_file_arg_skips_both_prompts(self, command, common_kwargs):
with patch.object(command, "_interactive_features") as feat, patch.object(
command, "_interactive_select_source"
) as src, patch.object(
command, "_prerecorded", return_value=BaseResult(status="ok")
):
command.handle(**common_kwargs, source="/tmp/audio.wav")
assert feat.call_count == 0
assert src.call_count == 0

def test_url_arg_with_diarize_skips_both_prompts(
self, command, common_kwargs
):
with patch.object(command, "_interactive_features") as feat, patch.object(
command, "_interactive_select_source"
) as src, patch.object(
command, "_prerecorded", return_value=BaseResult(status="ok")
):
command.handle(
**common_kwargs,
source="https://example.com/audio.wav",
diarize=True,
)
assert feat.call_count == 0
assert src.call_count == 0

def test_bare_invocation_runs_full_guided_flow(
self, command, common_kwargs
):
with patch.object(
command,
"_interactive_features",
return_value=(False, False, False, False),
) as feat, patch.object(
command,
"_interactive_select_source",
return_value=("prerecorded_url", "https://x.com/a.wav"),
) as src, patch.object(
command, "_prerecorded", return_value=BaseResult(status="ok")
), patch(
"sys.stdin"
) as mock_stdin, patch(
"deepctl_cmd_listen.command._agentic", False
):
mock_stdin.isatty.return_value = True
command.handle(**common_kwargs)
assert src.call_count == 1
assert feat.call_count == 1

def test_cancelled_source_select_returns_cancelled(
self, command, common_kwargs
):
with patch.object(
command, "_interactive_select_source", return_value=(None, None)
), patch.object(command, "_interactive_features") as feat, patch(
"sys.stdin"
) as mock_stdin, patch(
"deepctl_cmd_listen.command._agentic", False
):
mock_stdin.isatty.return_value = True
result = command.handle(**common_kwargs)
assert result.status == "cancelled"
assert feat.call_count == 0


class TestListenResult:
"""Test ListenResult model fields and defaults."""

Expand Down
Loading