Skip to content

🌿 Fern Regeneration -- November 10, 2025 - #1

Closed
fern-api[bot] wants to merge 2 commits into
mainfrom
fern-bot/2025-11-10T17-42Z
Closed

🌿 Fern Regeneration -- November 10, 2025#1
fern-api[bot] wants to merge 2 commits into
mainfrom
fern-bot/2025-11-10T17-42Z

Conversation

@fern-api

@fern-api fern-api Bot commented Nov 10, 2025

Copy link
Copy Markdown
Contributor

This PR regenerates code to match the latest API Definition.

@GregHolmes GregHolmes closed this Mar 25, 2026
GregHolmes added a commit that referenced this pull request Jul 9, 2026
Finding 6 (ours): applyOptionsOverride rewired five separate volatile fields
while getNextDelay() read three of them, so a concurrent override could yield a
torn (min, growFactor, max) set. Hold the overridable options behind a single
volatile ReconnectOptions reference; readers snapshot it once for a consistent
view. maxEnqueuedMessages stays a separate final field (not overridable).

Finding 5 (generator bug, also flagged upstream): the TimeoutException retry
branch omitted the closing paren ('(retry attempt #1' -> '(retry attempt #1)').
dg-coreylweathers pushed a commit that referenced this pull request Jul 13, 2026
…T tuning (#73)

## Summary

Fern SDK regeneration for 2026-07-09, with manual patches reconciled
against the new generator output (`dd2d427 SDK regeneration`), plus a
review pass that fixed a generator contract bug in the new output and
two reconnect-listener issues, and the follow-ups from the #73 review.

## Regenerated output (Fern-owned, new features)

- **Speak v2 WebSocket**: new `resources/speak/v2/websocket/` client
(`V2WebSocketClient`, `V2ConnectOptions`) + `SpeakV2*` message types and
`SpeakV2Encoding/SampleRate/Tag/MipOptOut`.
- **Agent update-listen**: `AgentV1UpdateListen`,
`AgentV1ListenUpdated`, `AgentV1UpdateListenListen`, plus
`V1WebSocketClient` updates.
- `DeepgramListenProviderV2` expanded.

## Manual patches reconciled (existing freezes)

All three temporarily-frozen files were reverted to generator defaults
by the regen and had their patches **re-applied** (none obsolete);
`.fernignore` restored to original paths; `.bak` files deleted:

- **`core/ClientOptions.java`** — correct SDK header constants + `//
x-release-please-version` markers; kept `0.6.0` (release-please owns
versioning), not Fern's `0.6.1`.
- **`core/ReconnectingWebSocketListener.java`** — `maxRetries(0)`
"connect once, don't retry" semantics, configurable
`connectionTimeoutMs`, and the `applyOptionsOverride(...)` hook
(required by the frozen `core/transport/TransportWebSocketFactory`).
- **`listen/v2/types/ListenV2CloseStream.java`** — `hashCode()` to match
generator's `equals()`.

## Review fixes

**1. Fields-less `equals()`/`hashCode()` contract bug (generator).** The
generator emits `equals()` (all-instances-equal) but no `hashCode()` for
fields-less message types. Fixed + froze **all 11** affected types
(`hashCode() { return getClass().hashCode(); }`): `ListenV2CloseStream`,
`SpeakV2Close`, `SpeakV2Flush`, and the 8 `AgentV1*` event types.
Stopgap — durable fix is upstream in Fern.

**2. `ReconnectingWebSocketListener` reconnect-options torn read
(ours).** `applyOptionsOverride` rewired five separate `volatile` fields
while `getNextDelay()` read three; a concurrent override could produce
an inconsistent (min, growFactor, max) set. Now held behind a single
`volatile ReconnectOptions` reference so reads snapshot a consistent
set.

**3. `TimeoutException` message unbalanced paren (generator).** The
retry branch produced `(retry attempt #1` with no closing paren. Fixed
locally; also flagged upstream since the generator emits it.

## Review follow-ups (#73 review)

**P0 — Flux model on `/v2/speak`.** The flagship example and the gated
integration test used `aura-2-thalia-en`; switched both to
`flux-alexis-en` (StreamingTtsV2.java, IntegrationTest.java), matching
the JS/Python SDKs. ✅ **Ran against staging** (`DEEPGRAM_SPEAK_V2_WS=1`
+ `DEEPGRAM_BASE_URL=wss://api.staging.deepgram.com`): `Connected` →
`SpeechStarted` → `Flushed` → binary audio → clean close. Running it
surfaced a latent test bug — the test closed the socket the instant
`Flushed` arrived, but Flux streams audio frames *after* `Flushed`, so
audio was truncated to 0 bytes; fixed to wait for an audio frame before
Close (and `setUp` now honors `DEEPGRAM_BASE_URL` so the opt-in test can
target staging).

**P0 — Unknown server messages no longer fatal.** The generated speak v2
dispatcher routed any unrecognized message type to `onError` ("Update
your SDK version…"), making benign new server control frames look fatal
to deployed voice agents. Downgraded the unknown-type branch to a
**no-op** — the raw frame is already delivered via `onMessage(String)`
earlier in `handleIncomingMessage`, so consumers still see it. Froze
`speak/v2/websocket/V2WebSocketClient.java` (+ AGENTS.md note) so the
patch survives regen, and added `SpeakV2ForwardCompatTest` as a
regression guard. Mirrors JS/Python. **Note:** the listen v2 client has
the identical generated bug — flagged for a follow-up (out of scope for
this Speak V2 PR).

**P1 — `metadata.json` disclosure corrected + reset.** The prior
disclosure (below) called `sdkVersion: 0.6.1` "generator-owned"; it is
in fact **release-please-tracked** (`release-please-config.json`
`$.sdkVersion`). Reset it to `0.6.0` so main carries no phantom
pre-bump; the manifest (`0.6.0`) drives and release-please computes the
next version at release.

**⚠️ Scope addition beyond the review — listen v2 forward-compat parity
(`b9a9bc6`).** While fixing speak v2,
`listen/v2/websocket/V2WebSocketClient.java:474` was found to have the
identical generated bug (unknown frames → `onError`). Applied the same
no-op patch + freeze (+ AGENTS.md) and a `ListenV2ForwardCompatTest`
guard, so forward-compat is consistent across speak v2, listen v2, and
the JS/Python SDKs. This wasn't in the original review scope — flagged
in a PR comment; can be split into its own PR if preferred.

## Known, deferred

- `V2WebSocketClient` `timeoutExecutor` dead field — generator artifact
in unfrozen generated code; left for the upstream Fern request rather
than freezing another file. (P2)
- Aggregate fields-less `hashCode` test could be parameterized to name
the failing type on failure — noted as polish. (P2)

## Docs

- `AGENTS.md`: genericized the regen branch prefix
(`<YOUR_INITIALS>/sdk-gen-...`) and completed the temporarily-frozen
file list (all `hashCode` freezes + the new `V2WebSocketClient`
forward-compat freeze now documented).

## Verification

`./gradlew test compileExamples` — **BUILD SUCCESSFUL** (includes the
new `SpeakV2ForwardCompatTest`).

## Tests & examples added

- **`SpeakV2ForwardCompatTest`** — drives the private speak v2
dispatcher with an unknown `{"type":"FutureMessage"}` frame and asserts
it is delivered raw via `onMessage(String)` and **not** routed to
`onError`. Regression guard for the forward-compat fix above.
- **`RegenTypesTest`** — `FieldsLessMessageContract` case asserts the
`equals`/`hashCode` contract for all newly-patched fields-less types, so
a future regen dropping a `hashCode()` is caught.
- **`examples/speak/StreamingTtsV2.java`** — exercises the new Speak v2
WebSocket client (now on `flux-alexis-en`), mirroring the v1 example;
compile-covered via `compileExamples`.
- **`IntegrationTest`** — opt-in Speak v2 WebSocket test
(`testIntegration_SpeakV2WebSocket`, Tier 2), now on `flux-alexis-en`:
connect → sendSpeak/sendFlush/sendClose → streamed audio, asserting a
Flushed message and non-zero audio bytes. Gated on
`DEEPGRAM_SPEAK_V2_WS` and **skipped by default**; run it against
staging before merge.

Not added (by design): a torn-read unit test for the reconnect snapshot
(timing-dependent/flaky), and an agent update-listen example
(nice-to-have, no dependency).

---------

Co-authored-by: fern-api[bot] <115122769+fern-api[bot]@users.noreply.github.com>
GregHolmes added a commit that referenced this pull request Aug 12, 2026
…provider + google version (#92)

Regenerates the SDK against the latest Fern generator (`4.10.1` →
`4.16.0`) and API spec (`ff8fd2b` → `03f0677`), reconciles the
hand-maintained patches, and documents the resulting breaking changes
with a migration guide. Supersedes the reverted #89.

## Base
Stacked on the revert branch (#91). Until that merges, this PR's diff
includes the revert commit; merge #91 first (or it rides along).

## ⛔ Blocked / do not merge yet
Regenerated from an **unmerged spec branch**: `.fern/metadata.json`
`originGitCommit` `03f0677` lives only on `origin/jherlihy/flux-tts-ga`,
not on `deepgram-docs` `main`. The launch surface here
(`breaks_applied`, `ConfigureFailure`, `expressivity`, `speed`,
`redact`) exists only on that branch. Kept as a **draft** until it
lands.
- **Blocked-on:** `deepgram-docs#1094` (Flux TTS copy) + the
`jherlihy/flux-tts-ga` spec branch merging to `deepgram-docs` `main`.

## Breaking changes (pre-1.0, source/compile-time only)
All three follow the API definition; on-the-wire payloads for existing
requests are unchanged. Full before/after in
[`docs/Migrating-v0.7-to-v0.8.md`](./docs/Migrating-v0.7-to-v0.8.md).

1. **`AgentV1UpdateListenListen.provider`** retyped
`DeepgramListenProviderV2` → `AgentV1UpdateListenListenProvider` (V1/V2
union). Wrap with `AgentV1UpdateListenListenProvider.v2(...)`; read via
`getV2()`.
2. **`Google.version`** retyped `Optional<String>` →
`Optional<GoogleThinkProviderVersion>`. Use the enum constants
(`V1BETA`, `AI_STUDIO_V1BETA`, `GEMINI_ENTERPRISE_AGENT_V1`).
3. **`SpeakV2SpeechMetadataControlsApplied`** gains a required
`breaksApplied` field (new builder step; new `getBreaksApplied()`). Read
paths unaffected.

Origin verified: #2 and #3 are new in the latest spec; #1's union shape
existed in #89 but was previously hidden behind an in-SDK shim we
intentionally did not carry forward.

## Reconciliation (post-regen review)
Diffed each of the 17 `.bak` patches against the freshly generated
originals — **all 17 still needed**, none dropped. `.fernignore`
restored to its pre-prep state; all `.bak` files deleted.
- **13 restored verbatim** (generator produced no other changes): the 11
fields-less `hashCode()` types, `ReconnectingWebSocketListener`
(override hook / `connectionTimeoutMs` / `maxRetries(0)` semantics), and
the `listen/v1` + `speak/v1` websocket clients (query-param repeats +
`additionalProperties`).
- **2 surgically merged** (`listen/v2` + `speak/v2` websocket clients):
re-applied `QueryStringMapper` array-param serialization, the
`additionalProperties` escape hatch, and the forward-compat
unknown-message no-op, **while keeping the generator's new features**
(speak/v2 `sendInterrupt`/`sendConfigure` +
`onSpeechInterrupted`/`onConfigureSuccess`/`onConfigureFailure`;
listen/v2 `redact`).
- **`ClientOptions`**: kept the new retry-tuning options +
`ResponseDecompressionInterceptor`; re-applied only the two `//
x-release-please-version` header lines (colon SDK-name form), removed
the generator's `getSdkVersion()` helper (build does not stamp the JAR
manifest, so it would drift).

## Additive highlights
Speak V2 interrupt/configure, Listen V2 `redact`, Speak V2
`speed`/`expressivity`, new Deepgram Flux TTS voices, client retry
tuning, automatic response decompression.

## Tests added
- `ListenV2ConnectWireTest`: new `redact` connect param (present as wire
`numbers`, omitted when absent).
- `RegenTypesTest` → "2026-08-11 regen type shapes": the three breaking
shapes (provider union v2 factory/`getV2`/serialization,
`Google.version` enum wire value, `ListenV2Redact` wire values).

## Verification
- `./gradlew unitTest` ✅ · `spotlessCheck` ✅ · `compileExamples` ✅
- `./gradlew integrationTest` ✅ (Tier 1 + Tier 2 against a live key);
opt-in Speak V2 WS integration ✅ (returned audio over the new v2 WS
path)
- Ran all non-`manage` examples: 19 pass, 7 long-running streaming/agent
examples connected and worked, 4 environmental (proxy / file-arg /
callback URL / SageMaker) — no SDK regressions.

## Post-review additions (from #91 review)
- **Re-applied the union default-variant fix** the #89 revert forfeited
(issue #93): `defaultImpl = V2Value` on
`AgentV1UpdateListenListenProvider`,
`AgentV1SettingsAgentListenProvider`,
`AgentV1SettingsAgentContextListenProvider`, so a provider payload
omitting the optional `version` discriminator (what 0.7.x emits)
deserializes as V2 instead of dropping to `{"provider":null}`. Frozen in
`.fernignore` + `AGENTS.md`, guarded by the re-added
`AgentSettingsProviderDefaultTest`. The `getProvider()` return-type shim
is intentionally **not** re-applied — that retype is a deliberate
breaking change (see the migration guide).

## Core behavior changes (generator 4.10.1 → 4.16.0)
Beyond the spec features, the generator upgrade changes core HTTP-client
behavior worth calling out in the release notes:
- **Automatic response decompression** is now installed by default
(`ResponseDecompressionInterceptor`): gzip/deflate-encoded HTTP
responses are transparently decoded. No API change; transparent to
callers.
- **New optional retry tuning** on `ClientOptions.Builder`:
`initialRetryDelayMillis`, `maxRetryDelayMillis`, `retryJitterFactor`.
All defaulted — existing behavior is unchanged unless set.

## Follow-ups
- Once this lands, release-please cuts a fresh release at **0.8.0**
(breaking `feat!`).
- **Changelog needs a manual edit before releasing.** `5b6323a` (#89
`feat(regen)`) is still in release-please's range since `v0.7.1`, and
release-please can't pair a revert with the commit it reverts — so the
generated 0.8.0 notes will re-list the reverted #89 features
(`diarize_info`, force-end-turn) under Features. Hand-edit the release
PR's `CHANGELOG.md` to drop them before merging it.

BREAKING CHANGE: `AgentV1UpdateListenListen.provider` is now
`AgentV1UpdateListenListenProvider` (V1/V2 union); `Google.version` is
now `Optional<GoogleThinkProviderVersion>`;
`SpeakV2SpeechMetadataControlsApplied` has a new required
`breaksApplied` field. See docs/Migrating-v0.7-to-v0.8.md.

---------

Co-authored-by: fern-api[bot] <115122769+fern-api[bot]@users.noreply.github.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