Skip to content

fix(listen/v2): allow extra fields on TurnInfo models (5.3.x)#736

Merged
dg-coreylweathers merged 1 commit into
v5from
fix/flux-v2-turninfo-allow-extra-fields
Jun 26, 2026
Merged

fix(listen/v2): allow extra fields on TurnInfo models (5.3.x)#736
dg-coreylweathers merged 1 commit into
v5from
fix/flux-v2-turninfo-allow-extra-fields

Conversation

@andreyas1337

@andreyas1337 andreyas1337 commented Jun 26, 2026

Copy link
Copy Markdown

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.

     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: ConnectedListenV2ConnectedEvent, ErrorListenV2FatalErrorEvent, TurnInfo+start/endListenV2TurnInfoEvent.
  • 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

Flux (flux-general-en) now returns additional per-word fields `start` and
`end` (type double) on every word in a `TurnInfo` message — see the Flux API
reference. The 5.3.x socket models declare only `word`/`confidence` with
`extra = "forbid"`, so each TurnInfo frame fails pydantic validation in
`listen/v2/socket_client.py` (`parse_obj_as(V2SocketClientResponse, ...)`).

On 5.3.0 this raises an uncaught ValidationError that tears down
`start_listening()` (it only catches WebSocketException/JSONDecodeError); on
5.3.2/5.3.3 (construct_type) the frame is silently mis-resolved to
`ListenV2ConnectedEvent`, dropping the transcript and words.

Relax `extra` to "allow" on `ListenV2TurnInfoEventWordsItem` and
`ListenV2TurnInfoEvent` so unknown additive fields are tolerated (and retained),
matching the `extra="allow"` posture already shipped on main (6.x/7.x).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@andreyas1337 andreyas1337 requested a review from lukeocodes as a code owner June 26, 2026 15:08
@andreyas1337 andreyas1337 changed the title fix(listen/v2): allow extra fields on Flux TurnInfo models (5.3.x) fix(listen/v2): allow extra fields on TurnInfo models (5.3.x) Jun 26, 2026
@dg-coreylweathers dg-coreylweathers merged commit be07aa6 into v5 Jun 26, 2026
14 checks passed
@dg-coreylweathers dg-coreylweathers deleted the fix/flux-v2-turninfo-allow-extra-fields branch June 26, 2026 16:07
dg-coreylweathers added a commit that referenced this pull request Jun 26, 2026
Maintenance release of the 5.3.x line.

Bumps the version `5.3.3` → `5.3.4` (manifest, `pyproject.toml`,
`client_wrapper.py` SDK header) and adds the CHANGELOG entry for #736.

### Included
- #736 — fix(listen/v2): allow extra fields on Flux `TurnInfo` models
(`extra="forbid"` → `"allow"`), preventing the crash (5.3.0) / silent
transcript drop (5.3.2/5.3.3) when Flux emits per-word `start`/`end`.

### Release mechanism
After this merges, pushing tag `v5.3.4` triggers
`.github/workflows/maintenance-release.yml`, which compiles, tests,
builds, and publishes to PyPI. A GitHub Release will be created with
`--latest=false` so `7.3.1` keeps the repo "Latest" badge.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-authored-by: Corey Weathers <coreyweathers@coreys-mbp.mynetworksettings.com>
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
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