Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .fern/metadata.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
"enable-wire-tests": true,
"runtime-version": true
},
"originGitCommit": "9dcbd3d5e8d36420319e1b33988613ad2cb2be10",
"originGitCommit": "59a99c27e5f91158072fc98b3567fc88c3315f67",
"originGitCommitIsDirty": true,
"invokedBy": "manual",
"sdkVersion": "0.11.1"
Expand Down
4 changes: 4 additions & 0 deletions .fernignore
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
3 changes: 2 additions & 1 deletion AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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))
Expand Down
2 changes: 2 additions & 0 deletions docs/Migrating-v0.8-to-v0.9.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
5 changes: 3 additions & 2 deletions examples/listen/LiveReconfigure.java
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,9 @@
/**
* Reconfigures an active Nova-3 Listen V1 stream without reconnecting.
*
* <p>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.
* <p>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}.
*
* <p>Usage: {@code DEEPGRAM_API_KEY=... java LiveReconfigure}
*/
Expand Down
7 changes: 4 additions & 3 deletions src/main/java/com/deepgram/core/ClientOptions.java
Original file line number Diff line number Diff line change
Expand Up @@ -146,9 +146,10 @@ public Optional<Double> retryJitterFactor() {
* <p>
* 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.
* <p>
* 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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,8 +50,9 @@ public String getType() {
* including keyterms set with the <code>keyterm</code> query parameter. Send an empty array to clear all keyterms. Omit
* the field, or set it to <code>null</code>, to keep the current keyterms.
* <p>Each entry is a plain term or phrase with no weights or intensifiers. The 500-token keyterm limit that
* applies to the <code>keyterm</code> query parameter also applies to each update. An over-limit update returns an
* <code>Error</code>, and the stream keeps its previous keyterms.</p>
* applies to the <code>keyterm</code> query parameter also applies to each update. An over-limit update currently stops
* transcription without an <code>Error</code>; after about 30 seconds, the server closes the stream with <code>1011</code>
* (<code>NET-0000</code>). Keep each list under the limit and check its size before sending.</p>
*/
@JsonProperty("keyterms")
public Optional<List<String>> getKeyterms() {
Expand Down Expand Up @@ -119,8 +120,9 @@ public Builder from(ListenV1Configure other) {
* including keyterms set with the <code>keyterm</code> query parameter. Send an empty array to clear all keyterms. Omit
* the field, or set it to <code>null</code>, to keep the current keyterms.</p>
* <p>Each entry is a plain term or phrase with no weights or intensifiers. The 500-token keyterm limit that
* applies to the <code>keyterm</code> query parameter also applies to each update. An over-limit update returns an
* <code>Error</code>, and the stream keeps its previous keyterms.</p>
* applies to the <code>keyterm</code> query parameter also applies to each update. An over-limit update currently stops
* transcription without an <code>Error</code>; after about 30 seconds, the server closes the stream with <code>1011</code>
* (<code>NET-0000</code>). Keep each list under the limit and check its size before sending.</p>
*/
@JsonSetter(value = "keyterms", nulls = Nulls.SKIP)
public Builder keyterms(Optional<List<String>> keyterms) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,9 @@ public interface Visitor<T> {

T visitSpeedNotSupported();

T visitControlCombinationInvalid();
default T visitControlCombinationInvalid() {
return visitUnknown("CONTROL_COMBINATION_INVALID");
}

T visitInternalError();

Expand Down
18 changes: 14 additions & 4 deletions src/main/java/com/deepgram/types/DeepgramModel.java
Original file line number Diff line number Diff line change
Expand Up @@ -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");
Expand Down Expand Up @@ -826,6 +831,8 @@ public enum Value {

FLUX_KELSEY_EN,

FLUX_RENEE_EN,

FLUX_KIT_EN,

FLUX_MAEVE_EN,
Expand All @@ -846,8 +853,6 @@ public enum Value {

FLUX_PRIYA_EN,

FLUX_RENEE_EN,

FLUX_RUFUS_EN,

FLUX_SEAN_EN,
Expand Down Expand Up @@ -1030,6 +1035,13 @@ public interface Visitor<T> {

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();
Expand All @@ -1050,8 +1062,6 @@ public interface Visitor<T> {

T visitFluxPriyaEn();

T visitFluxReneeEn();

T visitFluxRufusEn();

T visitFluxSeanEn();
Expand Down
41 changes: 41 additions & 0 deletions src/test/java/com/deepgram/RegenTypesTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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<String> 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
Expand Down
Loading