SIGN IN SIGN UP

feat(regen): add Speak V2 streaming, agent update-listen, and Flux EOT tuning (#742)

## Summary

Fern SDK regeneration (2026-07-08). Pulls in new generator output,
re-applies the hand-maintained patches, extends the socket-client
convention to the new client, and adds test + example coverage for the
new surface.

## New public surface (Fern-generated this regen)

- **Speak V2 — Flux TTS WebSocket** (`client.speak.v2.connect(...)`):
new `speak/v2/` package (client, socket client, request/response types)
plus top-level `SpeakV2Encoding/MipOptOut/Model/SampleRate/Tag`.
- **Agent mid-session listen reconfigure**: `send_update_listen(...)`
with `AgentV1UpdateListen`/`AgentV1UpdateListenListen`, and the
`AgentV1ListenUpdated` server acknowledgement.
- **Flux end-of-turn tuning fields** on the listen providers:
`eot_threshold`, `eager_eot_threshold`, `eot_timeout_ms`.

## Manual-patch reconciliation

The generator caught up on **none** of the existing back-compat
concerns, so **all 25 frozen patches were re-applied** (nothing dropped
from `.fernignore`), preserving the new generator additions:

- Legacy alias re-exports merged back into the package `__init__.py`
files (kept alongside the new `SpeakV2*` / `AgentV1*` entries).
- `language_hint` → `language_hints` migration shim re-added to the
three listen-provider models (kept alongside the new `eot_*` fields).
- Agent socket client: `_sanitize_numeric_types`, broad `except
Exception`, optional `send_keep_alive` (kept the new
`send_update_listen`).
- listen/v1, listen/v2, speak/v1 socket clients, `query_encoder` bool
coercion, `agent_v1settings*` back-compat, create-key alias + wire test
— all restored.

### Judgment call — listen/v2 `send_configure` shim: **kept**
The generator now emits complete
`ListenV2Configure`/`ListenV2ConfigureSuccess` models, but adopting them
changes the public signature (raw dict → model, typed responses) — a
breaking change. The `typing.Any` shim is kept for this maintenance
regen; typed-model adoption is deferred to a future major/minor.

## New: Speak V2 socket-client patch

The newly generated `speak/v2/socket_client.py` was made consistent with
the other four socket clients and frozen:
- Broad `except Exception` (preserves the custom-transport error
contract — a transport can raise arbitrary types; the narrow generated
catch would leak them past the message loop).
- Optional `send_flush()` / `send_close()` for no-payload control
messages (parity with `speak/v1`).

## Test & example coverage

- `tests/custom/test_speak_v2_socket.py` — send serialization,
response-type parsing, broad-except error emission, and **forward
compatibility**: an unknown/future message type passes through untouched
on all four receive paths (sync + async `recv()` and the
`start_listening` loop), so a newer server never breaks an older client.
- `tests/custom/test_speak_v2_connect_wire.py` — **connect request wire
shape** (sync + async): targets the `/v2/speak` path and serializes
query params, incl. `mip_opt_out=True` → `"true"`; doubles as the
wire-level guard on the frozen `query_encoder` bool coercion. Frozen in
`.fernignore` since Fern never generates websocket-connect wire tests.
- `tests/custom/test_eot_thresholds_feature.py` — `eot_*` fields
serialize on all three listen providers.
- `tests/custom/test_agent_update_listen.py` — `send_update_listen`
serialization + `AgentV1ListenUpdated` resolution.
- `tests/manual/speak/v2/connect/` — standalone scripts (main / async /
with_auth_token / with_raw_response), parity with `speak/v1`; now accept
a `DEEPGRAM_BASE_URL` override for non-prod (staging) targeting.
- `examples/25-text-to-speech-streaming-v2.py` (+ `examples/README.md`
entry).
- `reference.md` — new "Speak V2 Connect" section and
`send_update_listen` added to the Agent V1 send methods.

## Verification

- `mypy src/` — clean (844 files)
- `mypy tests/typecheck` — clean
- `pytest` — 318 passed, 1 skipped (pre-review baseline; the review
follow-up tests below add 6 more and pass)
- `pytest tests/custom` — 205 passed, 1 skipped (includes the new
forward-compat + connect-wire tests)
- `ruff check` / `mypy` on the review follow-up files — clean

## Release telemetry — version drift fix

`client_wrapper.py` is a `generic` extra-file in
`release-please-config.json`, but the generic updater only rewrites
lines carrying an annotation — and the two version header lines had
none, so release-please never bumped the wire telemetry version. It
drifts from the published package version (the pre-existing pattern:
main shipping 7.4.0 while sending 7.3.2, and this branch would send
7.4.1 against a 7.5.0 release).

- Added `# x-release-please-version` to the `User-Agent` and
`X-Fern-SDK-Version` header lines so they bump in lockstep with the
release.
- Froze `client_wrapper.py` in `.fernignore` — it is a *live*
Fern-generated file, so a regen would strip the annotations otherwise —
and documented the freeze + re-apply step in `AGENTS.md`.

## Live end-to-end ✅ validated against staging

Prod `/v2/speak` (Flux) returns HTTP 400 at the handshake until GA, so
the manual connect scripts accept a `DEEPGRAM_BASE_URL` override to
point at staging:

```
DEEPGRAM_BASE_URL=wss://api.staging.deepgram.com DEEPGRAM_API_KEY=<staging-key> \
  poetry run python tests/manual/speak/v2/connect/main.py
```

**Ran against staging (`wss://api.staging.deepgram.com`), both sync and
async:** Connect → Speak → Flush → Close completes end to end —
`Connected` (model `alexis`, v `2026-07-02.2`) → `SpeechStarted` → 20
binary audio frames (~77 KB) → `Flushed` → `SpeechMetadata` →
`SessionMetadata` → clean close. The forward-compat and connect-wire
unit tests remain the regression guards; this confirms the real
handshake + streaming path.

---------

Co-authored-by: fern-api[bot] <115122769+fern-api[bot]@users.noreply.github.com>
G
Greg Holmes committed
69a2445c7c0c5ce1726566331f13030df64062e5
Parent: 0c2bf79
Committed by GitHub <noreply@github.com> on 7/13/2026, 3:53:31 PM