Skip to content

Commit be07aa6

Browse files
andreyas1337claude
andauthored
fix(listen/v2): allow extra fields on TurnInfo models (5.3.x) (#736)
## Problem The v2 listen (Flux) `TurnInfo` message can include additional **per-word fields `start` and `end`** (type `double`) beyond the `word`/`confidence` the 5.3.x models declare. This was observed intermittently against the live API (roughly 1 in 6 connections in testing); I have not confirmed the full server-side rollout scope. The model `ListenV2TurnInfoEventWordsItem` declares only `word` + `confidence` with `extra = "forbid"`, so any frame carrying extra fields fails pydantic validation in `listen/v2/socket_client.py` (`parse_obj_as(V2SocketClientResponse, ...)`). Observed impact on the released 5.3.x line: - **5.3.0**: uncaught `ValidationError` propagates out of `start_listening()` (which only catches `WebSocketException` / `JSONDecodeError`) and tears down the listen loop; it never reaches the `EventType.ERROR` handler. - **5.3.2 / 5.3.3**: `construct_type` doesn't raise, but the strict word model still fails the union's validated pass, so the frame is silently mis-resolved to `ListenV2ConnectedEvent` — transcript and words dropped, no error (silent data loss). ## Fix Relax `extra` from `"forbid"` to `"allow"` on `ListenV2TurnInfoEventWordsItem` and `ListenV2TurnInfoEvent`, so unknown additive fields are tolerated and retained. Model-agnostic — it doesn't assume which model or which fields. Verified: a frame with `start`/`end` then parses as `ListenV2TurnInfoEvent` with the fields preserved. ```diff class Config: frozen = True - extra = "forbid" + extra = "allow" ``` (applied to both models in the file) ## Safety / side-effect analysis - **No code-execution risk.** Pydantic `extra="allow"` only stores unknown JSON values as data attributes (`__pydantic_extra__`); it does not eval/import/instantiate types. Not pickle/YAML-style gadget deserialization. Inbound data also comes from the Deepgram API over TLS, not arbitrary user input. - **Union resolution stays correct (tested).** `V2SocketClientResponse = Union[Connected, TurnInfo, FatalError]`. `TurnInfo` has required fields that `Connected`/`FatalError` lack, and those two keep `extra="forbid"`, so a permissive `TurnInfo` cannot greedily capture them. Verified: `Connected` → `ListenV2ConnectedEvent`, `Error` → `ListenV2FatalErrorEvent`, `TurnInfo+start/end` → `ListenV2TurnInfoEvent`. - **Required-field validation preserved (tested).** A `TurnInfo` frame missing a required field (e.g. `transcript`) still raises `ValidationError`. Only *unknown additive* fields are now tolerated. - **Memory:** negligible — `json.loads` already materializes the full payload before pydantic runs; `allow` just retains references (bounded by message size). - **Tradeoff:** loses the loud failure on unexpected additive fields (a contract-drift signal) — but that brittleness is exactly the bug, and this matches `main`'s posture. ### `allow` vs `ignore` - **`allow`** (chosen): tolerates **and exposes** the new fields, so callers can read `word.start` / `word.end`. Matches `main` (6.x/7.x). - **`ignore`**: also fixes the crash/mis-typing but **discards** extras — more conservative (nothing unvalidated retained or re-serialized), at the cost of the timestamps being unavailable. Happy to switch to `ignore` if the team prefers minimal surface. ## Notes for reviewers - `main` (6.x/7.x) already ships `extra="allow"` on the equivalent model, so this only affects the released **5.3.x** line — hence targeting `v5`. - This file is **Fern-generated** (`# auto-generated by Fern from our API Definition`); if `v5` is regenerated the proper fix belongs in the API definition. This is a direct patch on the frozen maintenance branch. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
1 parent 0378dfd commit be07aa6

1 file changed

Lines changed: 2 additions & 2 deletions

File tree

src/deepgram/extensions/types/sockets/listen_v2_turn_info_event.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ def dict(self, **kwargs) -> dict:
1919

2020
class Config:
2121
frozen = True
22-
extra = "forbid"
22+
extra = "allow"
2323

2424

2525
class ListenV2TurnInfoEvent(UniversalBaseModel):
@@ -44,4 +44,4 @@ def dict(self, **kwargs) -> dict:
4444

4545
class Config:
4646
frozen = True
47-
extra = "forbid"
47+
extra = "allow"

0 commit comments

Comments
 (0)