Skip to content

feat: SDK 7.7.0 — Flux TTS controls, Flux STT fix, listen redact/numerals - #92

Open
GregHolmes wants to merge 16 commits into
mainfrom
feat/sdk-7.7.0
Open

feat: SDK 7.7.0 — Flux TTS controls, Flux STT fix, listen redact/numerals#92
GregHolmes wants to merge 16 commits into
mainfrom
feat/sdk-7.7.0

Conversation

@GregHolmes

@GregHolmes GregHolmes commented Aug 12, 2026

Copy link
Copy Markdown
Contributor

Brings the CLI up to date with Deepgram Python SDK 7.6.0 + 7.7.0 in one release. The API-facing changes are additive and existing v1 behavior remains covered.

SDK and package constraints

  • deepgram-sdk is bounded at >=7.7.0,<8 across the workspace.
  • Internal minimums ensure upgrades install compatible components: core >=0.2.15, Speak >=0.0.4, and Listen >=0.0.14.
  • uv.lock is intentionally gitignored; local uv lock --check resolves 143 packages.
  • The release PR must publish core before Speak, Listen, and the root package.

Speak — Flux TTS streaming controls (7.7.0)

  • New dg speak --speed (0.85–1.15, 0.05 steps) and beta --expressivity (-2..2), forwarded to speak.v2.connect() only when set.
  • Both controls are validated up front and rejected for non-flux-* models.
  • Routing now uses the documented flux- boundary; bare names and typos stay on the v1 pass-through path.
  • send_interrupt() / send_configure() barge-in remain intentionally unexposed because they target live agent pipelines, not one-shot dg speak.

Listen — redact / numerals + Flux STT fix (7.6.0 + 7.7.0)

  • New repeatable dg listen --redact and --numerals, applied to prerecorded and live paths.
  • Flux validates its numbers / aggressive_numbers vocabulary up front; v1 continues to accept categories such as pci and ssn.
  • Flux STT (listen v2) streaming is fixed:
    • v2 no longer receives v1-only params (language, smart_format, punctuate, channels, diarize, interim_results) that caused HTTP 400.
    • TurnInfo events are assembled per turn and finite streams flush the final in-flight turn.
    • Fatal Error frames propagate code + description and exit non-zero.
    • Flux rejects multichannel raw audio before streaming instead of sending stereo bytes as mono.
    • --diarize warns and is cleared consistently from forwarding, UI, and result metadata.
    • Partial/null Flux word timings are backfilled for valid saved captions.
  • Flux file/URL and invalid-redact misuse now produce visible non-zero Click errors.

Docs

  • Added Aura-2 Spanish voice examples and a pointer to dg models.
  • Expressivity's beta/default status is consistent in README, CLI help/examples, client docs, and generated developer guidance.
  • Regenerated package READMEs now match the SDK constraint.

Live e2e safety

  • Six live tests cover Flux TTS controls, Aura Spanish, Flux numerals, Flux redaction, nova-3 v1, and Flux SRT timestamps.
  • An exported API key alone never enables network tests.
  • Live runs require RUN_LIVE_E2E=1 plus either an explicit DEEPGRAM_BASE_URL or RUN_LIVE_E2E_PRODUCTION=1.
  • Seven deterministic tests cover the opt-in gate without making network calls.

Verification

  • Full non-live suite: 1026 passed, 6 skipped.
  • Exported key with live opt-ins unset: 7 passed, 6 skipped, no network calls.
  • make check: Ruff format/check + mypy clean across 115 source files.
  • Generated README check: clean.
  • uv lock --check: 143 packages resolved.
  • Root/core/Speak/Listen wheels and sdists build; all pass Twine checks.
  • Built wheel metadata contains the expected SDK bound and cross-package minimums.

Production behavior was verified during development for Flux TTS controls, Aura Spanish, Flux STT numerals/redaction, nova-3 v1, and Flux SRT timestamps. Version and changelog generation remain owned by release-please.

Bump deepgram-sdk 7.5.0 -> 7.7.0 across the workspace and expose the new
Flux (Speak v2) streaming controls on `dg speak`:

- `--speed` (0.85-1.15, 0.05 steps) and `--expressivity` (-2..2) forward to
  `speak.v2.connect()` via `speak_text_stream()`; only sent when set.
- Both are validated up front and rejected for Aura (v1) models, so misuse
  fails with a clear message instead of a mid-stream server error.
- Update --help, examples, agent_help, the skill-generator snippet, README,
  and add tests (forwarding, Aura rejection, invalid speed/expressivity).
Expose --redact (numbers, aggressive_numbers, or v1 values like pci/ssn)
and --numerals on `dg listen`, applied to both prerecorded and live paths.

Also fixes Flux STT (listen v2) streaming, which was broken since it was
added:

- `_ws_url` sent v1-only params (language, smart_format, punctuate,
  channels, diarize, interim_results) to the v2 endpoint, which rejected
  them with HTTP 400. Build the param set per API version instead.
- The message parser only understood v1 `Results`; Flux emits turn-based
  `TurnInfo` events. Add `_handle_v2_turn` with per-turn state, plus
  `_flush_v2` to emit the final turn when a finite stream closes before an
  `EndOfTurn` (common for files/stdin).

Verified against staging: `--model flux-general-en --numerals` yields
digit transcripts, `--redact numbers` redacts them, and nova-3 (v1) is
unaffected. Adds tests for _ws_url params and v2 turn finalization.
SDK 7.6.0 added Aura-2 Spanish voices (e.g. aura-2-selena-es). These need
no CLI code change — the model is passed through as a string — so just add
an example and a pointer to `dg models` for the current list.
The Flux STT (listen v2) HTTP 400 fix hinges on _ws_url NOT sending
v1-only params (language, smart_format, punctuate, channels, diarize,
interim_results) to the v2 endpoint, but nothing asserted that directly —
a revert would silently rebreak Flux STT. Add explicit v2-omits /
v1-includes assertions.
Add tests/e2e/ that drive command handle() against the real Deepgram API
in-process, covering the release's new functionality end to end:

- speak Flux --speed/--expressivity -> valid WAV
- speak aura-2-selena-es (multilingual) -> MP3 over REST
- listen Flux STT (v2) --numerals -> digits in transcript
- listen Flux STT (v2) --numerals --redact numbers -> redacted
- listen nova-3 (v1) baseline -> guards the v2-fix regression

Gated on DEEPGRAM_API_KEY (skipped otherwise), so they never run in the
CI matrix (no secret) but run locally/manually with a key exported. Set
DEEPGRAM_BASE_URL to target staging. Verified: 5 skip without a key, 5
pass against production.
…(v2)

Address review findings on PR #92:
- listen: emit a stderr note when --diarize is set against a Flux STT
  (v2) model, instead of silently dropping it. v1 paths unaffected.
- disambiguate user-facing copy now that both Flux surfaces exist:
  "Flux TTS" in speak, "Flux STT" in listen (help text, the Aura
  rejection error, and README comments).
- bound the SDK pin to >=7.7.0,<8 across all packages so a future
  breaking major isn't pulled in automatically.

Gate: make check clean; full suite + live e2e (production) green.
Two upfront guards in `listen` so Flux (v2) misuse fails with a clear
message instead of an opaque server HTTP 400:

- Reject `--redact` values outside {numbers, aggressive_numbers} on Flux
  models, mirroring speak's Flux-flag validation.
- Reject Flux model + pre-recorded file/URL (v2 is streaming-only) before
  it routes to /v1/listen and surfaces a wrapped header dump.

Also reformat test_speak_command.py to satisfy ruff format.
The v1 API accepts multiple redact categories (redact=pci&redact=numbers)
but the flag was single-valued. Mark --redact multiple=True and normalise
to a tuple.

- REST path passes a list so Fern's query encoder expands it into repeated
  params (a tuple is left unexpanded).
- WebSocket path uses urlencode(doseq=True) to expand the sequence.
- Flux (v2) validation now checks every supplied value against the
  numbers/aggressive_numbers vocabulary.
The S2 guard now blocks flux model + file before dispatch, so the two
TestFluxModelAutoVersion tests that asserted flux+file reaching
_prerecorded with api_version=2 crashed (mock never called).

Split them to match the new behaviour: flux+file now asserts the
streaming-only error result, and the flux→v2 assertion moves to the mic
streaming path (which is where a Flux model is actually valid).
Rewrite dict() calls as literals (C408) and rename ambiguous `l` loop
variable to `line` (E741). No behaviour change.
…rrors

Review of the Flux STT work surfaced three issues:

- Captions crashed / mis-timed on Flux STT. TurnInfo words carry only
  {word, confidence} (no per-word start/end), so the live cue span fell
  back to 00:00:00 and the end-of-stream batch save raised
  KeyError: 'start'. Key caption timing off the turn's guaranteed
  audio_window_start/end and backfill per-word timings from it so both the
  live cues and captions_from_words() produce valid output.
- Flux STT fatal errors (control frame type "Error") were silently
  dropped, leaving the user with an empty transcript. Surface code +
  description to stderr.
- speak: note that --expressivity is beta and fixed for the connection.

Adds unit coverage for the audio-window caption timing (incl. the batch
save that used to KeyError) and the error-frame surface.
Close the two coverage gaps left from the Flux STT review:

- Unit: assert _ws_mic catches a KeyboardInterrupt at the gather await,
  runs _flush_v2, and returns the in-flight turn's transcript (rather than
  losing the final partial turn on Ctrl-C).
- Live e2e: Flux STT (v2) --srt captions must carry well-formed, non-zero
  timestamps (keyed off TurnInfo.audio_window_*), guarding the caption fix
  and the batch-save path that previously raised KeyError: 'start'.
…n edges)

Fill the unit-coverage gaps the review surfaced in the new Flux/redact logic:

- _prerecorded builds redact as a list + numerals "true" (Fern repeats the
  query param) — previously only the routing-to-_prerecorded was tested.
- _timed_v2_words: synth-on-empty, window-spread, and real-timing passthrough.
- v2 Update interim print, and EndOfTurn with an empty transcript (no-op).

All new logic paths now unit-covered; the only remaining misses in
command.py are pre-existing branches and the real mic/stdin/websocket I/O
loops, which the live e2e suite exercises.

@dg-coreylweathers dg-coreylweathers left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Approved — verified via two independent review passes (details in review notes). All prior findings resolved at 074090d; CI green 38/38; live-verified against production. Three small non-blocking should-fixes noted for follow-up: (1) bare '-m flux' on dg speak exits 0 on failure, (2) invalid --redact on v1 prerecorded is silent + exit 0, (3) Ctrl-C on 'dg listen -' drops the transcript/--save-to output that the mic path preserves. Please confirm publish ordering: core 0.2.15 must land before speak/listen/root.

… on Ctrl-C

Three error-contract fixes surfaced in review:

- speak: the REST (Speak v1) handler returned BaseResult(status="error")
  instead of raising, which the framework prints but exits 0. Now that a
  bare/typo'd `flux` model routes to REST (not the raising v2 path), its
  failure regressed to exit 0. Raise ClickException so it exits non-zero.
- listen: the prerecorded REST handler had the same return-not-raise sink;
  a --redact value the v1 endpoint refuses printed nothing and exited 0.
  Raise instead so the rejection is visible and non-zero.
- listen: `dg listen -` (stdin) dropped the partial transcript and --save-to
  on Ctrl-C because _ws_stdin re-raised the interrupt. Mirror the mic path:
  catch KeyboardInterrupt/CancelledError, drain, and fall through to the
  flush + return so the transcript survives (fatal errors still propagate).

Regression tests: REST API error exits non-zero (speak flux/aura, listen v1);
stdin Ctrl-C preserves transcript and writes --save-to. Full suite 1037 passed.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants