diff --git a/.fern/metadata.json b/.fern/metadata.json index 1955a2a..3c25483 100644 --- a/.fern/metadata.json +++ b/.fern/metadata.json @@ -12,7 +12,7 @@ "enable-wire-tests": true, "runtime-version": true }, - "originGitCommit": "9dcbd3d5e8d36420319e1b33988613ad2cb2be10", + "originGitCommit": "59a99c27e5f91158072fc98b3567fc88c3315f67", "originGitCommitIsDirty": true, "invokedBy": "manual", "sdkVersion": "0.11.1" diff --git a/.fernignore b/.fernignore index aacb4e3..001477f 100644 --- a/.fernignore +++ b/.fernignore @@ -98,6 +98,10 @@ src/main/java/com/deepgram/resources/agent/v1/types/AgentV1UpdateListenListenPro src/main/java/com/deepgram/resources/agent/v1/types/AgentV1SettingsAgentListenProvider.java src/main/java/com/deepgram/resources/agent/v1/types/AgentV1SettingsAgentContextListenProvider.java +# Preserve binary-compatible Visitor implementations from before this generated enum added +# CONTROL_COMBINATION_INVALID. The default method routes old visitors through visitUnknown. +src/main/java/com/deepgram/resources/speak/v2/types/SpeakV2ConfigureFailureCode.java + # Build and project configuration build.gradle settings.gradle diff --git a/AGENTS.md b/AGENTS.md index ea1c345..ecec18a 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -53,8 +53,9 @@ Current temporarily frozen files: - `src/main/java/com/deepgram/resources/speak/v2/websocket/V2WebSocketClient.java` and `src/main/java/com/deepgram/resources/listen/v2/websocket/V2WebSocketClient.java` - forward-compat patch (both clients). Fern's generated `handleIncomingMessage` dispatcher routes any unrecognized message type to `onError` with "Update your SDK version...", which makes a benign new server control frame look fatal to a deployed client. Patched so the unrecognized-type branch is a no-op — the raw frame is already delivered via `onMessage(String)` earlier in the method, so consumers still see it. Mirrors the JS/Python SDKs' forward-compat behavior and is regression-guarded by `src/test/java/com/deepgram/SpeakV2ForwardCompatTest.java` and `src/test/java/com/deepgram/ListenV2ForwardCompatTest.java`. These two clients also carry the streaming query-param patches described in the next entry. Use the standard `.bak` swap/restore workflow during regen review; re-apply the no-op to both after regen, and unfreeze once the generator stops treating unknown frames as errors. - `src/main/java/com/deepgram/resources/listen/v1/websocket/V1WebSocketClient.java` and `src/main/java/com/deepgram/resources/speak/v1/websocket/V1WebSocketClient.java` (and the v2 clients above) - streaming query-param patches on the generated `connect()` builders. Two fixes: (1) multi-value serialization — array-valued params (listen: `keyterm`, `keywords`, `replace`, `search`, `tag`, `extra`, `language_hint`; speak: `tag`) were serialized with `String.valueOf(union.get())`, collapsing a `List` into one param (`keyterm=[a, b]`) instead of repeats (`keyterm=a&keyterm=b`); (2) an `additionalProperties` escape hatch — the builder exposes `additionalProperty(key, value)` for unmodeled params (e.g. `no_delay`) but `connect()` never emitted them to the URL. Both patched to route through `QueryStringMapper(arraysAsRepeats=true)`, matching the REST path. Listen V1 also forwards a parsed server `Error` to `onError` as a `ListenV1ErrorException` (carrying the typed `ListenV1Error`) when no `onErrorMessage` handler is registered, preserving the prior generic error-handler behavior. Use the standard `.bak` swap/restore workflow during regen review; re-apply after regen and unfreeze once the generator emits array params as repeats and serializes `additionalProperties` on the WS `connect()` path (tracked as an upstream Fern request). - Fields-less message types carrying a manual `hashCode()` patch (Fern generates `equals()` but no `hashCode()` for these, violating the Object contract): `src/main/java/com/deepgram/resources/listen/v2/types/ListenV2CloseStream.java`, `src/main/java/com/deepgram/resources/listen/v2/types/ListenV2ForceEndTurn.java`, `src/main/java/com/deepgram/resources/speak/v2/types/SpeakV2Close.java`, `src/main/java/com/deepgram/resources/speak/v2/types/SpeakV2Flush.java`, and the `AgentV1*` event types `src/main/java/com/deepgram/resources/agent/v1/types/{AgentV1ListenUpdated,AgentV1SpeakUpdated,AgentV1AgentAudioDone,AgentV1SettingsApplied,AgentV1UserStartedSpeaking,AgentV1KeepAlive,AgentV1ThinkUpdated,AgentV1PromptUpdated,AgentV1ForceEndTurn}.java`. Use the standard `.bak` swap/restore workflow during regen review; drop the patches and unfreeze all of them once the generator emits a matching equals/hashCode pair for fields-less types (tracked as an upstream Fern request). -- `src/main/java/com/deepgram/types/DeepgramModel.java` - restores the `FLUX_RENEE_EN` constant that generator 4.18.0 dropped. The voice is live: `POST /v2/speak?model=flux-renee-en` returns 200 with valid audio, and the name resolves in the server's model registry (an invented `flux-*` name is rejected with `INVALID_QUERY_PARAMETER`), so the removal is a spec regression rather than a retirement, and dropping the constant would break 0.8.0 callers for nothing. Five touchpoints: the constant, the `Value` enum entry, the `visit()` case, the `valueOf()` case, and the `Visitor` method. **This file is unlike the other temporarily frozen ones — it receives frequent additive spec changes (4.18.0 alone added 25 constants), so on the next regen do NOT restore the `.bak` wholesale.** Diff the `.bak` against the newly generated file, carry forward every new voice, and re-apply only the `FLUX_RENEE_EN` touchpoints. Drop the patch and unfreeze once the spec lists the voice again (tracked as an upstream spec request). +- `src/main/java/com/deepgram/types/DeepgramModel.java` - restores the `FLUX_RENEE_EN` constant that generator 4.18.0 dropped. Renee is an unlisted voice: the spec owner removed it from the catalog on purpose (#project-flux-tts, 2026-08-19), but it still synthesizes (`POST /v2/speak?model=flux-renee-en` returns 200 with valid audio), so dropping the constant would be a source break for 0.8.0+ callers over a catalog delisting alone. The constant and its `Visitor` method are marked `@Deprecated` with a note pointing callers to a listed voice. Five touchpoints: the constant, the `Value` enum entry, the `visit()` case, the `valueOf()` case, and the `Visitor` method (the constant and `Visitor` method carry the `@Deprecated` tag and Javadoc). **This file is unlike the other temporarily frozen ones — it receives frequent additive spec changes (4.18.0 alone added 25 constants), so on the next regen do NOT restore the `.bak` wholesale.** Diff the `.bak` against the newly generated file, carry forward every new voice, and re-apply only the `FLUX_RENEE_EN` touchpoints. The spec will not list the voice again, so drop the Renee touchpoints at the next major release; keep the file frozen until then for the reasons above. - Union default-variant fix on the agent listen-provider unions: `src/main/java/com/deepgram/resources/agent/v1/types/AgentV1UpdateListenListenProvider.java`, `src/main/java/com/deepgram/resources/agent/v1/types/AgentV1SettingsAgentListenProvider.java`, `src/main/java/com/deepgram/resources/agent/v1/types/AgentV1SettingsAgentContextListenProvider.java`. `version` is an optional discriminator, so a provider payload without it is valid (and is what 0.7.x emits), but Fern points `@JsonTypeInfo` `defaultImpl` at the empty-bodied `_UnknownValue`, so such a payload deserializes to an unknown variant carrying `null` — `getProvider()` returns `null` and re-serialization emits `{"provider":null}`, silently dropping the provider on the wire. Patched to `defaultImpl = V2Value` on each; guarded by `src/test/java/com/deepgram/AgentSettingsProviderDefaultTest.java`. Use the standard `.bak` swap/restore workflow during regen review; drop the patches and unfreeze once the generator stops defaulting unions to the empty `_UnknownValue` (tracked as an upstream Fern request). +- `src/main/java/com/deepgram/resources/speak/v2/types/SpeakV2ConfigureFailureCode.java` makes `Visitor.visitControlCombinationInvalid()` a default method that delegates to `visitUnknown("CONTROL_COMBINATION_INVALID")`. This restores source compatibility for `Visitor` implementations written against 0.8.0–0.10.x, before the value existed (0.11.0 shipped the method as abstract and listed it as breaking), while retaining the new generated enum value. Guarded by `src/test/java/com/deepgram/RegenTypesTest.java`. Use the standard `.bak` swap/restore workflow during regen review so new failure codes from the spec are carried forward, and re-apply only the default method. Keep frozen until the next major release. ### Prepare repo for regeneration diff --git a/README.md b/README.md index 21204b2..aeade6c 100644 --- a/README.md +++ b/README.md @@ -302,8 +302,8 @@ ws.connect(V1ConnectOptions.builder() .get(10, TimeUnit.SECONDS); // Update Nova-3 keyterms and numerals without reconnecting. Keep the keyterm list under the -// 500-token limit: an over-limit update currently stops transcription without an Error, and -// the server closes the stream. +// 500-token limit: an over-limit update does not close the stream, and the server may reply +// with a ConfigureFailure (CONFIGURE_KEYTERM_LIMIT_EXCEEDED), which this client reports to onError. ws.sendConfigure(ListenV1Configure.builder() .keyterms(List.of("Deepgram")) .features(Map.of("numerals", true)) diff --git a/docs/Migrating-v0.8-to-v0.9.md b/docs/Migrating-v0.8-to-v0.9.md index 68c9c24..916d6cc 100644 --- a/docs/Migrating-v0.8-to-v0.9.md +++ b/docs/Migrating-v0.8-to-v0.9.md @@ -183,6 +183,8 @@ ConversationHistoryMessage msg = ConversationHistoryMessage.builder() This is called out only because the generator dropped `FLUX_RENEE_EN` in this cycle and the SDK deliberately does not ship that removal. The voice is live: `POST /v2/speak?model=flux-renee-en` returns `200` with valid audio, and the name resolves in the server's model registry (an invented `flux-*` name is rejected with `INVALID_QUERY_PARAMETER`). The constant is restored and frozen, so existing references keep compiling. +> **Later update:** `flux-renee-en` is an unlisted voice, so releases after 0.11.1 mark `DeepgramModel.FLUX_RENEE_EN` `@Deprecated`. The voice still synthesizes and the constant still compiles; prefer a listed Flux voice from `GET /v2/models` for new code. + `DeepgramModel` remains a forward-compatible enum, so if you need to send a value the current SDK does not model, `DeepgramModel.valueOf("some-model-name")` still works. ### Speak V2 Connect Speed diff --git a/examples/listen/LiveReconfigure.java b/examples/listen/LiveReconfigure.java index 6da7fcc..af027be 100644 --- a/examples/listen/LiveReconfigure.java +++ b/examples/listen/LiveReconfigure.java @@ -15,8 +15,9 @@ /** * Reconfigures an active Nova-3 Listen V1 stream without reconnecting. * - *

Keep the keyterm list under the 500-token limit: an over-limit update currently stops transcription without an - * {@code Error}, and the server closes the stream. + *

Keep the keyterm list under the 500-token limit: an over-limit update does not close the stream, and the server + * may reply with a {@code ConfigureFailure} ({@code CONFIGURE_KEYTERM_LIMIT_EXCEEDED}), which this client reports to + * {@code onError}. * *

Usage: {@code DEEPGRAM_API_KEY=... java LiveReconfigure} */ diff --git a/src/main/java/com/deepgram/core/ClientOptions.java b/src/main/java/com/deepgram/core/ClientOptions.java index b2c87ee..66dfa86 100644 --- a/src/main/java/com/deepgram/core/ClientOptions.java +++ b/src/main/java/com/deepgram/core/ClientOptions.java @@ -146,9 +146,10 @@ public Optional retryJitterFactor() { *

* In-flight calls are not cancelled or awaited. When this client owns its OkHttpClient, later * asynchronous HTTP requests can fail when the dispatcher rejects work, while synchronous - * requests may still run. An OkHttpClient supplied through {@code httpClient(...)} remains - * caller-owned and running. Options derived via {@code Builder.from(...)} share lifecycle - * state; calling this method more than once has no further effect. + * requests may still run. Options derived from this one via + * {@code Builder.from(...)} share the same dispatcher and connection pool, so closing either + * releases them for both. A caller-supplied OkHttpClient remains running. Calling this method + * more than once has no further effect. *

* WebSocket clients created from this client that are still connected are disconnected * first (whether or not the OkHttpClient is owned), so they stop reconnecting before the diff --git a/src/main/java/com/deepgram/resources/listen/v1/types/ListenV1Configure.java b/src/main/java/com/deepgram/resources/listen/v1/types/ListenV1Configure.java index 967d27e..4232f3f 100644 --- a/src/main/java/com/deepgram/resources/listen/v1/types/ListenV1Configure.java +++ b/src/main/java/com/deepgram/resources/listen/v1/types/ListenV1Configure.java @@ -50,8 +50,9 @@ public String getType() { * including keyterms set with the keyterm query parameter. Send an empty array to clear all keyterms. Omit * the field, or set it to null, to keep the current keyterms. *

Each entry is a plain term or phrase with no weights or intensifiers. The 500-token keyterm limit that - * applies to the keyterm query parameter also applies to each update. An over-limit update returns an - * Error, and the stream keeps its previous keyterms.

+ * applies to the keyterm query parameter also applies to each update. An over-limit update currently stops + * transcription without an Error; after about 30 seconds, the server closes the stream with 1011 + * (NET-0000). Keep each list under the limit and check its size before sending.

*/ @JsonProperty("keyterms") public Optional> getKeyterms() { @@ -119,8 +120,9 @@ public Builder from(ListenV1Configure other) { * including keyterms set with the keyterm query parameter. Send an empty array to clear all keyterms. Omit * the field, or set it to null, to keep the current keyterms.

*

Each entry is a plain term or phrase with no weights or intensifiers. The 500-token keyterm limit that - * applies to the keyterm query parameter also applies to each update. An over-limit update returns an - * Error, and the stream keeps its previous keyterms.

+ * applies to the keyterm query parameter also applies to each update. An over-limit update currently stops + * transcription without an Error; after about 30 seconds, the server closes the stream with 1011 + * (NET-0000). Keep each list under the limit and check its size before sending.

*/ @JsonSetter(value = "keyterms", nulls = Nulls.SKIP) public Builder keyterms(Optional> keyterms) { diff --git a/src/main/java/com/deepgram/resources/speak/v2/types/SpeakV2ConfigureFailureCode.java b/src/main/java/com/deepgram/resources/speak/v2/types/SpeakV2ConfigureFailureCode.java index 63ec45a..f678e97 100644 --- a/src/main/java/com/deepgram/resources/speak/v2/types/SpeakV2ConfigureFailureCode.java +++ b/src/main/java/com/deepgram/resources/speak/v2/types/SpeakV2ConfigureFailureCode.java @@ -110,7 +110,9 @@ public interface Visitor { T visitSpeedNotSupported(); - T visitControlCombinationInvalid(); + default T visitControlCombinationInvalid() { + return visitUnknown("CONTROL_COMBINATION_INVALID"); + } T visitInternalError(); diff --git a/src/main/java/com/deepgram/types/DeepgramModel.java b/src/main/java/com/deepgram/types/DeepgramModel.java index 6444d7f..9fa2f8b 100644 --- a/src/main/java/com/deepgram/types/DeepgramModel.java +++ b/src/main/java/com/deepgram/types/DeepgramModel.java @@ -13,6 +13,11 @@ public final class DeepgramModel { public static final DeepgramModel FLUX_KELSEY_EN = new DeepgramModel(Value.FLUX_KELSEY_EN, "flux-kelsey-en"); + /** + * @deprecated {@code flux-renee-en} is an unlisted voice and may be removed in a future major + * release. Prefer a listed Flux voice; {@code GET /v2/models} returns the current catalog. + */ + @Deprecated public static final DeepgramModel FLUX_RENEE_EN = new DeepgramModel(Value.FLUX_RENEE_EN, "flux-renee-en"); public static final DeepgramModel FLUX_SIENNA_EN = new DeepgramModel(Value.FLUX_SIENNA_EN, "flux-sienna-en"); @@ -826,6 +831,8 @@ public enum Value { FLUX_KELSEY_EN, + FLUX_RENEE_EN, + FLUX_KIT_EN, FLUX_MAEVE_EN, @@ -846,8 +853,6 @@ public enum Value { FLUX_PRIYA_EN, - FLUX_RENEE_EN, - FLUX_RUFUS_EN, FLUX_SEAN_EN, @@ -1030,6 +1035,13 @@ public interface Visitor { T visitFluxKelseyEn(); + /** + * @deprecated {@code flux-renee-en} is an unlisted voice and may be removed in a future major + * release. Prefer a listed Flux voice; {@code GET /v2/models} returns the current catalog. + */ + @Deprecated + T visitFluxReneeEn(); + T visitFluxKitEn(); T visitFluxMaeveEn(); @@ -1050,8 +1062,6 @@ public interface Visitor { T visitFluxPriyaEn(); - T visitFluxReneeEn(); - T visitFluxRufusEn(); T visitFluxSeanEn(); diff --git a/src/test/java/com/deepgram/RegenTypesTest.java b/src/test/java/com/deepgram/RegenTypesTest.java index e0a44b5..554853d 100644 --- a/src/test/java/com/deepgram/RegenTypesTest.java +++ b/src/test/java/com/deepgram/RegenTypesTest.java @@ -21,6 +21,7 @@ import com.deepgram.resources.listen.v2.types.ListenV2TurnInfoWordsItem; import com.deepgram.resources.listen.v2.types.ListenV2Warning; import com.deepgram.resources.speak.v2.types.SpeakV2Close; +import com.deepgram.resources.speak.v2.types.SpeakV2ConfigureFailureCode; import com.deepgram.resources.speak.v2.types.SpeakV2Flush; import com.deepgram.types.Deepgram; import com.deepgram.types.DeepgramListenProviderV2; @@ -421,6 +422,46 @@ void intentsDeserializeDirectSegments() throws Exception { }); }); } + + } + + @Nested + @DisplayName("SpeakV2 ConfigureFailure visitor compatibility") + class ConfigureFailureVisitorCompatibility { + + @Test + @DisplayName("control combination invalid uses visitUnknown when an older visitor omits it") + void controlCombinationInvalidUsesUnknownVisitorMethod() { + SpeakV2ConfigureFailureCode.Visitor visitor = new SpeakV2ConfigureFailureCode.Visitor<>() { + @Override + public String visitSpeedOutOfRange() { + return "speed-out-of-range"; + } + + @Override + public String visitSpeedIncrementInvalid() { + return "speed-increment-invalid"; + } + + @Override + public String visitSpeedNotSupported() { + return "speed-not-supported"; + } + + @Override + public String visitInternalError() { + return "internal-error"; + } + + @Override + public String visitUnknown(String unknownType) { + return "unknown:" + unknownType; + } + }; + + assertThat(SpeakV2ConfigureFailureCode.CONTROL_COMBINATION_INVALID.visit(visitor)) + .isEqualTo("unknown:CONTROL_COMBINATION_INVALID"); + } } @Nested