From 7cff366ae5c23f1a9acee6bc6a276e3172faf596 Mon Sep 17 00:00:00 2001 From: Greg Holmes Date: Wed, 7 Oct 2026 11:46:03 +0100 Subject: [PATCH 1/7] chore: initialize SDK regeneration branch From 70e3c58481d0746a1b62fb7d3e0f6bf592c7b450 Mon Sep 17 00:00:00 2001 From: Greg Holmes Date: Wed, 7 Oct 2026 11:46:23 +0100 Subject: [PATCH 2/7] chore: unfreeze files pending regen --- .fernignore | 46 +- .../com/deepgram/core/ClientOptions.java.bak | 408 +++++++ .../ReconnectingWebSocketListener.java.bak | 622 ++++++++++ .../v1/types/AgentV1AgentAudioDone.java.bak | 83 ++ .../v1/types/AgentV1ForceEndTurn.java.bak | 83 ++ .../agent/v1/types/AgentV1KeepAlive.java.bak | 83 ++ .../v1/types/AgentV1ListenUpdated.java.bak | 83 ++ .../v1/types/AgentV1PromptUpdated.java.bak | 83 ++ ...ettingsAgentContextListenProvider.java.bak | 224 ++++ ...gentV1SettingsAgentListenProvider.java.bak | 224 ++++ .../v1/types/AgentV1SettingsApplied.java.bak | 83 ++ .../v1/types/AgentV1SpeakUpdated.java.bak | 83 ++ .../v1/types/AgentV1ThinkUpdated.java.bak | 83 ++ ...AgentV1UpdateListenListenProvider.java.bak | 224 ++++ .../types/AgentV1UserStartedSpeaking.java.bak | 83 ++ .../v1/websocket/V1WebSocketClient.java.bak | 554 +++++++++ .../v2/types/ListenV2CloseStream.java.bak | 83 ++ .../v2/types/ListenV2ForceEndTurn.java.bak | 83 ++ .../v2/websocket/V2WebSocketClient.java.bak | 563 +++++++++ .../v1/websocket/V1WebSocketClient.java.bak | 467 +++++++ .../speak/v2/types/SpeakV2Close.java.bak | 83 ++ .../speak/v2/types/SpeakV2Flush.java.bak | 83 ++ .../v2/websocket/V2WebSocketClient.java.bak | 630 ++++++++++ .../com/deepgram/types/DeepgramModel.java.bak | 1071 +++++++++++++++++ 24 files changed, 6089 insertions(+), 23 deletions(-) create mode 100644 src/main/java/com/deepgram/core/ClientOptions.java.bak create mode 100644 src/main/java/com/deepgram/core/ReconnectingWebSocketListener.java.bak create mode 100644 src/main/java/com/deepgram/resources/agent/v1/types/AgentV1AgentAudioDone.java.bak create mode 100644 src/main/java/com/deepgram/resources/agent/v1/types/AgentV1ForceEndTurn.java.bak create mode 100644 src/main/java/com/deepgram/resources/agent/v1/types/AgentV1KeepAlive.java.bak create mode 100644 src/main/java/com/deepgram/resources/agent/v1/types/AgentV1ListenUpdated.java.bak create mode 100644 src/main/java/com/deepgram/resources/agent/v1/types/AgentV1PromptUpdated.java.bak create mode 100644 src/main/java/com/deepgram/resources/agent/v1/types/AgentV1SettingsAgentContextListenProvider.java.bak create mode 100644 src/main/java/com/deepgram/resources/agent/v1/types/AgentV1SettingsAgentListenProvider.java.bak create mode 100644 src/main/java/com/deepgram/resources/agent/v1/types/AgentV1SettingsApplied.java.bak create mode 100644 src/main/java/com/deepgram/resources/agent/v1/types/AgentV1SpeakUpdated.java.bak create mode 100644 src/main/java/com/deepgram/resources/agent/v1/types/AgentV1ThinkUpdated.java.bak create mode 100644 src/main/java/com/deepgram/resources/agent/v1/types/AgentV1UpdateListenListenProvider.java.bak create mode 100644 src/main/java/com/deepgram/resources/agent/v1/types/AgentV1UserStartedSpeaking.java.bak create mode 100644 src/main/java/com/deepgram/resources/listen/v1/websocket/V1WebSocketClient.java.bak create mode 100644 src/main/java/com/deepgram/resources/listen/v2/types/ListenV2CloseStream.java.bak create mode 100644 src/main/java/com/deepgram/resources/listen/v2/types/ListenV2ForceEndTurn.java.bak create mode 100644 src/main/java/com/deepgram/resources/listen/v2/websocket/V2WebSocketClient.java.bak create mode 100644 src/main/java/com/deepgram/resources/speak/v1/websocket/V1WebSocketClient.java.bak create mode 100644 src/main/java/com/deepgram/resources/speak/v2/types/SpeakV2Close.java.bak create mode 100644 src/main/java/com/deepgram/resources/speak/v2/types/SpeakV2Flush.java.bak create mode 100644 src/main/java/com/deepgram/resources/speak/v2/websocket/V2WebSocketClient.java.bak create mode 100644 src/main/java/com/deepgram/types/DeepgramModel.java.bak diff --git a/.fernignore b/.fernignore index ef6a835..4733372 100644 --- a/.fernignore +++ b/.fernignore @@ -16,7 +16,7 @@ src/main/java/com/deepgram/AsyncDeepgramClientBuilder.java # Fern regen overwrites these with incorrect SDK names and strips the markers. # Fern 4.22.1 closes tracked WebSockets before SDK-owned OkHttp resources; retain that lifecycle behavior while # reapplying only these headers. -src/main/java/com/deepgram/core/ClientOptions.java +src/main/java/com/deepgram/core/ClientOptions.java.bak # Transport abstraction (pluggable transport for SageMaker, etc.) src/main/java/com/deepgram/core/transport/ @@ -24,7 +24,7 @@ src/main/java/com/deepgram/core/transport/ # Bug fixes for maxRetries(0) semantics ("connect once, don't retry") and a # configurable connectionTimeoutMs on ReconnectOptions (was hardcoded 4000ms). # Pull this back out once the fixes are upstreamed into the Fern generator. -src/main/java/com/deepgram/core/ReconnectingWebSocketListener.java +src/main/java/com/deepgram/core/ReconnectingWebSocketListener.java.bak # Forward-compat patch: Fern's generated dispatcher routes any unrecognized message # type to the error handler ("Update your SDK version..."), which makes benign new @@ -34,8 +34,8 @@ src/main/java/com/deepgram/core/ReconnectingWebSocketListener.java # NOTE: the listen v2 and speak v2 clients below also carry the streaming query-param patches # (multi-value serialization + the additionalProperties escape hatch) documented lower down — # keep them frozen until those fixes are upstreamed too. -src/main/java/com/deepgram/resources/speak/v2/websocket/V2WebSocketClient.java -src/main/java/com/deepgram/resources/listen/v2/websocket/V2WebSocketClient.java +src/main/java/com/deepgram/resources/speak/v2/websocket/V2WebSocketClient.java.bak +src/main/java/com/deepgram/resources/listen/v2/websocket/V2WebSocketClient.java.bak # Streaming query-param patches on the generated WS connect() builders. Two fixes: # (1) Multi-value serialization: array-valued params (listen: keyterm, keywords, replace, search, @@ -49,8 +49,8 @@ src/main/java/com/deepgram/resources/listen/v2/websocket/V2WebSocketClient.java # listen v2 and speak v2 clients are frozen above; freeze the listen v1 and speak v1 clients here too # so the fixes survive regen. Re-apply after regen; unfreeze once the generator emits array params as # repeats and serializes additionalProperties on the WS connect path (tracked as an upstream Fern request). -src/main/java/com/deepgram/resources/listen/v1/websocket/V1WebSocketClient.java -src/main/java/com/deepgram/resources/speak/v1/websocket/V1WebSocketClient.java +src/main/java/com/deepgram/resources/listen/v1/websocket/V1WebSocketClient.java.bak +src/main/java/com/deepgram/resources/speak/v1/websocket/V1WebSocketClient.java.bak # 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 @@ -62,25 +62,25 @@ src/main/java/com/deepgram/resources/speak/v1/websocket/V1WebSocketClient.java # changes -- 4.18.0 alone added 25 constants. On the next regen do NOT restore the .bak wholesale: # diff it against the newly generated file, carry forward every new voice, and re-apply only the # FLUX_RENEE_EN touchpoints (constant, Value enum, visit() case, valueOf() case, Visitor method). -src/main/java/com/deepgram/types/DeepgramModel.java +src/main/java/com/deepgram/types/DeepgramModel.java.bak # Manual equals/hashCode contract fix: Fern generates equals() (all instances equal) but no # hashCode() for fields-less message types, violating the Object contract. We add a consistent # hashCode() to each. Unfreeze and drop these patches once the generator emits a matching # equals/hashCode pair for fields-less types (tracked as an upstream Fern request). -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 -src/main/java/com/deepgram/resources/agent/v1/types/AgentV1ListenUpdated.java -src/main/java/com/deepgram/resources/agent/v1/types/AgentV1SpeakUpdated.java -src/main/java/com/deepgram/resources/agent/v1/types/AgentV1AgentAudioDone.java -src/main/java/com/deepgram/resources/agent/v1/types/AgentV1SettingsApplied.java -src/main/java/com/deepgram/resources/agent/v1/types/AgentV1UserStartedSpeaking.java -src/main/java/com/deepgram/resources/agent/v1/types/AgentV1KeepAlive.java -src/main/java/com/deepgram/resources/agent/v1/types/AgentV1ThinkUpdated.java -src/main/java/com/deepgram/resources/agent/v1/types/AgentV1PromptUpdated.java -src/main/java/com/deepgram/resources/agent/v1/types/AgentV1ForceEndTurn.java +src/main/java/com/deepgram/resources/listen/v2/types/ListenV2CloseStream.java.bak +src/main/java/com/deepgram/resources/listen/v2/types/ListenV2ForceEndTurn.java.bak +src/main/java/com/deepgram/resources/speak/v2/types/SpeakV2Close.java.bak +src/main/java/com/deepgram/resources/speak/v2/types/SpeakV2Flush.java.bak +src/main/java/com/deepgram/resources/agent/v1/types/AgentV1ListenUpdated.java.bak +src/main/java/com/deepgram/resources/agent/v1/types/AgentV1SpeakUpdated.java.bak +src/main/java/com/deepgram/resources/agent/v1/types/AgentV1AgentAudioDone.java.bak +src/main/java/com/deepgram/resources/agent/v1/types/AgentV1SettingsApplied.java.bak +src/main/java/com/deepgram/resources/agent/v1/types/AgentV1UserStartedSpeaking.java.bak +src/main/java/com/deepgram/resources/agent/v1/types/AgentV1KeepAlive.java.bak +src/main/java/com/deepgram/resources/agent/v1/types/AgentV1ThinkUpdated.java.bak +src/main/java/com/deepgram/resources/agent/v1/types/AgentV1PromptUpdated.java.bak +src/main/java/com/deepgram/resources/agent/v1/types/AgentV1ForceEndTurn.java.bak # Union default-variant fix (STOPGAP). The agent listen-provider unions declare `version` as an # optional discriminator, so a provider payload without it is valid (and is what 0.7.x emits). Fern @@ -89,9 +89,9 @@ src/main/java/com/deepgram/resources/agent/v1/types/AgentV1ForceEndTurn.java # {"provider":null}, silently dropping the caller's provider on the wire. Patched to # defaultImpl = V2Value on each. Guarded by AgentSettingsProviderDefaultTest. Unfreeze and drop once # the generator stops defaulting unions to the empty _UnknownValue (tracked as an upstream Fern request). -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 +src/main/java/com/deepgram/resources/agent/v1/types/AgentV1UpdateListenListenProvider.java.bak +src/main/java/com/deepgram/resources/agent/v1/types/AgentV1SettingsAgentListenProvider.java.bak +src/main/java/com/deepgram/resources/agent/v1/types/AgentV1SettingsAgentContextListenProvider.java.bak # Build and project configuration build.gradle diff --git a/src/main/java/com/deepgram/core/ClientOptions.java.bak b/src/main/java/com/deepgram/core/ClientOptions.java.bak new file mode 100644 index 0000000..4d7a26a --- /dev/null +++ b/src/main/java/com/deepgram/core/ClientOptions.java.bak @@ -0,0 +1,408 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ +package com.deepgram.core; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.function.Supplier; +import okhttp3.OkHttpClient; + +public final class ClientOptions { + private final Environment environment; + + private final Map headers; + + private final Map> headerSuppliers; + + private final OkHttpClient httpClient; + + private final boolean ownsHttpClient; + + private final AtomicBoolean closed; + + private final Set openWebSockets; + + private final int timeout; + + private final int maxRetries; + + private final Optional initialRetryDelayMillis; + + private final Optional maxRetryDelayMillis; + + private final Optional retryJitterFactor; + + private final Optional webSocketFactory; + + private final Optional logging; + + private ClientOptions( + Environment environment, + Map headers, + Map> headerSuppliers, + OkHttpClient httpClient, + boolean ownsHttpClient, + AtomicBoolean closed, + Set openWebSockets, + int timeout, + int maxRetries, + Optional initialRetryDelayMillis, + Optional maxRetryDelayMillis, + Optional retryJitterFactor, + Optional webSocketFactory, + Optional logging) { + this.environment = environment; + this.headers = new HashMap<>(); + this.headers.putAll(headers); + this.headers.putAll(new HashMap() { + { + put("User-Agent", "com.deepgram:deepgram-java-sdk/0.11.0"); // x-release-please-version + put("X-Fern-Language", "JAVA"); + put("X-Fern-SDK-Name", "com.deepgram:deepgram-java-sdk"); + put("X-Fern-SDK-Version", "0.11.0"); // x-release-please-version + } + }); + this.headerSuppliers = headerSuppliers; + this.httpClient = httpClient; + this.ownsHttpClient = ownsHttpClient; + this.closed = closed; + this.openWebSockets = openWebSockets; + this.timeout = timeout; + this.maxRetries = maxRetries; + this.initialRetryDelayMillis = initialRetryDelayMillis; + this.maxRetryDelayMillis = maxRetryDelayMillis; + this.retryJitterFactor = retryJitterFactor; + this.webSocketFactory = webSocketFactory; + this.logging = logging; + } + + public Environment environment() { + return this.environment; + } + + public Map headers(RequestOptions requestOptions) { + Map values = new HashMap<>(this.headers); + headerSuppliers.forEach((key, supplier) -> { + values.put(key, supplier.get()); + }); + if (requestOptions != null) { + values.putAll(requestOptions.getHeaders()); + } + return values; + } + + public int timeout(RequestOptions requestOptions) { + if (requestOptions == null) { + return this.timeout; + } + return requestOptions.getTimeout().orElse(this.timeout); + } + + public OkHttpClient httpClient() { + return this.httpClient; + } + + public OkHttpClient httpClientWithTimeout(RequestOptions requestOptions) { + if (requestOptions == null) { + return this.httpClient; + } + return this.httpClient + .newBuilder() + .callTimeout(requestOptions.getTimeout().get(), requestOptions.getTimeoutTimeUnit()) + .connectTimeout(0, TimeUnit.SECONDS) + .writeTimeout(0, TimeUnit.SECONDS) + .readTimeout(0, TimeUnit.SECONDS) + .build(); + } + + public int maxRetries() { + return this.maxRetries; + } + + public Optional initialRetryDelayMillis() { + return this.initialRetryDelayMillis; + } + + public Optional maxRetryDelayMillis() { + return this.maxRetryDelayMillis; + } + + public Optional retryJitterFactor() { + return this.retryJitterFactor; + } + + /** + * Releases resources owned by this client. Only shuts down the underlying OkHttpClient's + * dispatcher executor and evicts its connection pool when this client created that + * OkHttpClient itself; an OkHttpClient supplied via httpClient is left running, since the + * caller owns its lifecycle. + *

+ * 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. + *

+ * 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 + * dispatcher goes away. Any WebSocket connect or reconnect attempted afterwards fails with + * an {@code IllegalStateException} explaining that the client has been closed. + */ + public void close() { + if (!this.closed.compareAndSet(false, true)) { + return; + } + for (AutoCloseable webSocket : new ArrayList<>(this.openWebSockets)) { + try { + webSocket.close(); + } catch (Exception e) { + // best effort; keep closing the remaining sockets and the HTTP client + } + } + this.openWebSockets.clear(); + if (!this.ownsHttpClient) { + return; + } + this.httpClient.dispatcher().executorService().shutdown(); + this.httpClient.connectionPool().evictAll(); + } + + /** + * Returns whether {@link #close()} has been called on this client. + */ + public boolean isClosed() { + return this.closed.get(); + } + + /** + * Tracks a connected WebSocket client so that {@link #close()} disconnects it. + * + * @throws IllegalStateException if this client has already been closed + */ + public void registerWebSocket(AutoCloseable webSocket) { + this.openWebSockets.add(webSocket); + if (this.closed.get()) { + this.openWebSockets.remove(webSocket); + throw new IllegalStateException("root client has been closed"); + } + } + + /** + * Stops tracking a WebSocket client that has been disconnected. + */ + public void unregisterWebSocket(AutoCloseable webSocket) { + this.openWebSockets.remove(webSocket); + } + + public Optional webSocketFactory() { + return this.webSocketFactory; + } + + public Optional logging() { + return this.logging; + } + + public static Builder builder() { + return new Builder(); + } + + public static class Builder { + private Environment environment; + + private final Map headers = new HashMap<>(); + + private final Map> headerSuppliers = new HashMap<>(); + + private int maxRetries = 2; + + private Optional initialRetryDelayMillis = Optional.empty(); + + private Optional maxRetryDelayMillis = Optional.empty(); + + private Optional retryJitterFactor = Optional.empty(); + + private Optional timeout = Optional.empty(); + + private OkHttpClient httpClient = null; + + private boolean ownsHttpClient = true; + + private AtomicBoolean closed; + + private Set openWebSockets; + + private Optional logging = Optional.empty(); + + private Optional webSocketFactory = Optional.empty(); + + public Builder environment(Environment environment) { + this.environment = environment; + return this; + } + + public Builder addHeader(String key, String value) { + if (value != null) { + this.headers.put(key, value); + } + return this; + } + + public Builder addHeader(String key, Supplier value) { + this.headerSuppliers.put(key, value); + return this; + } + + /** + * Override the timeout in seconds. Defaults to 60 seconds. + */ + public Builder timeout(int timeout) { + this.timeout = Optional.of(timeout); + return this; + } + + /** + * Override the timeout in seconds. Defaults to 60 seconds. + */ + public Builder timeout(Optional timeout) { + this.timeout = timeout; + return this; + } + + /** + * Override the maximum number of retries. Defaults to 2 retries. + */ + public Builder maxRetries(int maxRetries) { + this.maxRetries = maxRetries; + return this; + } + + /** + * Override the initial delay (in milliseconds) used for exponential backoff between retries. Defaults to 1000 milliseconds. + */ + public Builder initialRetryDelayMillis(long initialRetryDelayMillis) { + this.initialRetryDelayMillis = Optional.of(initialRetryDelayMillis); + return this; + } + + /** + * Override the maximum delay (in milliseconds) between retries. Defaults to 60000 milliseconds. + */ + public Builder maxRetryDelayMillis(long maxRetryDelayMillis) { + this.maxRetryDelayMillis = Optional.of(maxRetryDelayMillis); + return this; + } + + /** + * Override the jitter factor (between 0 and 1) applied to retry delays. Defaults to 0.2. + */ + public Builder retryJitterFactor(double retryJitterFactor) { + this.retryJitterFactor = Optional.of(retryJitterFactor); + return this; + } + + /** + * Sets the underlying OkHttp client. The caller retains ownership of its lifecycle: + * close() will not shut down its dispatcher executor or evict its connection pool. + */ + public Builder httpClient(OkHttpClient httpClient) { + this.httpClient = httpClient; + this.ownsHttpClient = httpClient == null; + return this; + } + + /** + * Set a custom WebSocketFactory for creating WebSocket connections. + */ + public Builder webSocketFactory(WebSocketFactory webSocketFactory) { + this.webSocketFactory = Optional.of(webSocketFactory); + return this; + } + + /** + * Configure logging for the SDK. Silent by default — no log output unless explicitly configured. + */ + public Builder logging(LogConfig logging) { + this.logging = Optional.of(logging); + return this; + } + + public ClientOptions build() { + OkHttpClient.Builder httpClientBuilder = + this.httpClient != null ? this.httpClient.newBuilder() : new OkHttpClient.Builder(); + + if (this.httpClient != null) { + timeout.ifPresent(timeout -> httpClientBuilder + .callTimeout(timeout, TimeUnit.SECONDS) + .connectTimeout(0, TimeUnit.SECONDS) + .writeTimeout(0, TimeUnit.SECONDS) + .readTimeout(0, TimeUnit.SECONDS)); + } else { + httpClientBuilder + .callTimeout(this.timeout.orElse(60), TimeUnit.SECONDS) + .connectTimeout(0, TimeUnit.SECONDS) + .writeTimeout(0, TimeUnit.SECONDS) + .readTimeout(0, TimeUnit.SECONDS) + .addInterceptor(new RetryInterceptor( + this.maxRetries, + this.initialRetryDelayMillis, + this.maxRetryDelayMillis, + this.retryJitterFactor)); + } + + Logger logger = Logger.from(this.logging); + httpClientBuilder.addInterceptor(new LoggingInterceptor(logger)); + httpClientBuilder.addInterceptor(new ResponseDecompressionInterceptor()); + + this.httpClient = httpClientBuilder.build(); + this.timeout = Optional.of(httpClient.callTimeoutMillis() / 1000); + + AtomicBoolean closedToUse = this.closed != null ? this.closed : new AtomicBoolean(false); + Set openWebSocketsToUse = + this.openWebSockets != null ? this.openWebSockets : ConcurrentHashMap.newKeySet(); + + return new ClientOptions( + environment, + headers, + headerSuppliers, + httpClient, + this.ownsHttpClient, + closedToUse, + openWebSocketsToUse, + this.timeout.get(), + this.maxRetries, + this.initialRetryDelayMillis, + this.maxRetryDelayMillis, + this.retryJitterFactor, + this.webSocketFactory, + this.logging); + } + + /** + * Create a new Builder initialized with values from an existing ClientOptions + */ + public static Builder from(ClientOptions clientOptions) { + Builder builder = new Builder(); + builder.environment = clientOptions.environment(); + builder.timeout = Optional.of(clientOptions.timeout(null)); + builder.httpClient = clientOptions.httpClient(); + builder.ownsHttpClient = clientOptions.ownsHttpClient; + builder.closed = clientOptions.closed; + builder.openWebSockets = clientOptions.openWebSockets; + builder.headers.putAll(clientOptions.headers); + builder.headerSuppliers.putAll(clientOptions.headerSuppliers); + builder.maxRetries = clientOptions.maxRetries(); + builder.initialRetryDelayMillis = clientOptions.initialRetryDelayMillis(); + builder.maxRetryDelayMillis = clientOptions.maxRetryDelayMillis(); + builder.retryJitterFactor = clientOptions.retryJitterFactor(); + builder.logging = clientOptions.logging(); + return builder; + } + } +} diff --git a/src/main/java/com/deepgram/core/ReconnectingWebSocketListener.java.bak b/src/main/java/com/deepgram/core/ReconnectingWebSocketListener.java.bak new file mode 100644 index 0000000..294316b --- /dev/null +++ b/src/main/java/com/deepgram/core/ReconnectingWebSocketListener.java.bak @@ -0,0 +1,622 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ +package com.deepgram.core; + +import static java.util.concurrent.TimeUnit.*; + +import java.util.ArrayList; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ConcurrentLinkedQueue; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.TimeoutException; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.function.BooleanSupplier; +import java.util.function.Consumer; +import java.util.function.Supplier; +import okhttp3.Response; +import okhttp3.WebSocket; +import okhttp3.WebSocketListener; +import okio.ByteString; + +/** + * WebSocketListener with automatic reconnection, exponential backoff, and message queuing. + * Provides production-ready resilience for WebSocket connections. + */ +public abstract class ReconnectingWebSocketListener extends WebSocketListener { + // A single volatile reference keeps an override internally consistent. + private volatile ReconnectOptions activeOptions; + + private final int maxEnqueuedMessages; + + private final AtomicInteger retryCount = new AtomicInteger(0); + + private final AtomicBoolean connectLock = new AtomicBoolean(false); + + private final AtomicBoolean shouldReconnect = new AtomicBoolean(true); + + protected volatile WebSocket webSocket; + + private volatile long connectionEstablishedTime = 0L; + + private final ConcurrentLinkedQueue messageQueue = new ConcurrentLinkedQueue<>(); + + private final ConcurrentLinkedQueue binaryMessageQueue = new ConcurrentLinkedQueue<>(); + + private final ScheduledExecutorService reconnectExecutor = Executors.newSingleThreadScheduledExecutor(); + + private final Supplier connectionSupplier; + + private final BooleanSupplier closedCheck; + + /** + * Creates a new reconnecting WebSocket listener. + * + * @param options Reconnection configuration options + * @param connectionSupplier Supplier that creates new WebSocket connections + */ + public ReconnectingWebSocketListener( + ReconnectingWebSocketListener.ReconnectOptions options, Supplier connectionSupplier) { + this(options, connectionSupplier, () -> false); + } + + /** + * Creates a new reconnecting WebSocket listener. + * + * @param options Reconnection configuration options + * @param connectionSupplier Supplier that creates new WebSocket connections + * @param closedCheck Returns true once the owning client has been closed; connect and + * reconnect attempts made after that point fail with an {@link IllegalStateException} + * instead of being retried + */ + public ReconnectingWebSocketListener( + ReconnectingWebSocketListener.ReconnectOptions options, + Supplier connectionSupplier, + BooleanSupplier closedCheck) { + this.activeOptions = options; + this.maxEnqueuedMessages = options.maxEnqueuedMessages; + this.connectionSupplier = connectionSupplier; + this.closedCheck = closedCheck; + } + + /** + * Replaces option-derived reconnect settings without rebuilding generated clients. + * + * @param options replacement options; {@code null} is a no-op + */ + public void applyOptionsOverride(ReconnectOptions options) { + if (options != null) { + this.activeOptions = options; + } + } + + /** + * Initiates a WebSocket connection with automatic reconnection enabled. + * + * Connection behavior: + * - Times out after {@code ReconnectOptions.connectionTimeoutMs} (default 4000ms) + * - Thread-safe via atomic lock (returns immediately if connection in progress) + * - {@code maxRetries} counts retries only; {@code maxRetries(0)} means connect once, do not retry + * + * Error handling: + * - TimeoutException: Includes retry attempt context + * - InterruptedException: Preserves thread interruption status + * - ExecutionException: Extracts actual cause and adds context + * - Owning client closed: reports an IllegalStateException once and stops reconnecting + */ + public void connect() { + if (!connectLock.compareAndSet(false, true)) { + return; + } + if (closedCheck.getAsBoolean()) { + shouldReconnect.set(false); + connectLock.set(false); + onWebSocketFailure(null, new IllegalStateException("root client has been closed"), null); + return; + } + ReconnectOptions options = this.activeOptions; + if (retryCount.get() > options.maxRetries) { + connectLock.set(false); + return; + } + try { + CompletableFuture connectionFuture = CompletableFuture.supplyAsync(connectionSupplier); + try { + webSocket = connectionFuture.get(options.connectionTimeoutMs, MILLISECONDS); + } catch (TimeoutException e) { + connectionFuture.cancel(true); + TimeoutException timeoutError = + new TimeoutException("WebSocket connection timeout after " + options.connectionTimeoutMs + " milliseconds" + + (retryCount.get() > 0 + ? " (retry attempt #" + retryCount.get() + ")" + : " (initial connection attempt)")); + onWebSocketFailure(null, timeoutError, null); + if (shouldReconnect.get()) { + scheduleReconnect(); + } + } catch (InterruptedException e) { + connectionFuture.cancel(true); + Thread.currentThread().interrupt(); + InterruptedException interruptError = new InterruptedException("WebSocket connection interrupted" + + (retryCount.get() > 0 + ? " during retry attempt #" + retryCount.get() + : " during initial connection")); + interruptError.initCause(e); + onWebSocketFailure(null, interruptError, null); + } catch (ExecutionException e) { + Throwable cause = e.getCause() != null ? e.getCause() : e; + String context = retryCount.get() > 0 + ? "WebSocket connection failed during retry attempt #" + retryCount.get() + : "WebSocket connection failed during initial attempt"; + RuntimeException wrappedException = new RuntimeException( + context + ": " + cause.getClass().getSimpleName() + ": " + cause.getMessage()); + wrappedException.initCause(cause); + onWebSocketFailure(null, wrappedException, null); + if (shouldReconnect.get()) { + scheduleReconnect(); + } + } + } finally { + connectLock.set(false); + } + } + + /** + * Disconnects the WebSocket and disables automatic reconnection. + * + * This method: + * - Disables automatic reconnection + * - Clears queued messages to prevent stale data + * - Closes the WebSocket with standard close code 1000 + * - Properly shuts down the reconnect executor to prevent thread leaks + * - Waits up to 5 seconds for executor termination + */ + public void disconnect() { + shouldReconnect.set(false); + messageQueue.clear(); + binaryMessageQueue.clear(); + if (webSocket != null) { + webSocket.close(1000, "Client disconnecting"); + } + reconnectExecutor.shutdown(); + try { + if (!reconnectExecutor.awaitTermination(5, SECONDS)) { + reconnectExecutor.shutdownNow(); + } + } catch (InterruptedException e) { + reconnectExecutor.shutdownNow(); + Thread.currentThread().interrupt(); + } + } + + /** + * Sends a message or queues it if not connected. + * + * Thread-safe: Synchronized to prevent race conditions with flushMessageQueue(). + * + * Behavior: + * - If connected: Attempts direct send, queues if buffer full + * - If disconnected: Queues message up to maxEnqueuedMessages limit + * - If queue full: Message is dropped + * + * @param message The message to send + * @return true if sent immediately, false if queued or dropped + */ + public synchronized boolean send(String message) { + return send(message, null); + } + + /** + * Sends a message or queues it if not connected, exposing the accepting socket. + * + * Behaves like {@link #send(String)}, but additionally invokes {@code onSent} with the + * WebSocket that accepted the message. The callback is only invoked when the message was + * sent directly, never when it was queued or dropped. This lets callers associate a + * protocol-level message with the specific connection it was delivered on, e.g. to decide + * in {@link #shouldReconnectAfterClose(WebSocket, int, String)} whether a later close of that same + * connection was expected. + * + * @param message The message to send + * @param onSent Callback receiving the WebSocket that accepted the message, or null + * @return true if sent immediately, false if queued or dropped + */ + public synchronized boolean send(String message, Consumer onSent) { + WebSocket ws = webSocket; + if (ws != null) { + boolean sent = ws.send(message); + if (sent && onSent != null) { + onSent.accept(ws); + } + if (!sent && messageQueue.size() < maxEnqueuedMessages) { + messageQueue.offer(message); + return false; + } + return sent; + } else { + if (messageQueue.size() < maxEnqueuedMessages) { + messageQueue.offer(message); + return false; + } + return false; + } + } + + /** + * Sends binary data or queues it if not connected. + * + * Thread-safe: Synchronized to prevent race conditions with flushMessageQueue(). + * + * Behavior: + * - If connected: Attempts direct send, queues if buffer full + * - If disconnected: Queues data up to maxEnqueuedMessages limit + * - If queue full: Data is dropped + * + * @param data The binary data to send + * @return true if sent immediately, false if queued or dropped + */ + public synchronized boolean sendBinary(ByteString data) { + return sendBinary(data, null); + } + + /** + * Sends binary data or queues it if not connected, exposing the accepting socket. + * + * Behaves like {@link #sendBinary(ByteString)}, but additionally invokes {@code onSent} with + * the WebSocket that accepted the data. The callback is only invoked when the data was sent + * directly, never when it was queued or dropped. + * + * @param data The binary data to send + * @param onSent Callback receiving the WebSocket that accepted the data, or null + * @return true if sent immediately, false if queued or dropped + */ + public synchronized boolean sendBinary(ByteString data, Consumer onSent) { + WebSocket ws = webSocket; + if (ws != null) { + boolean sent = ws.send(data); + if (sent && onSent != null) { + onSent.accept(ws); + } + if (!sent && binaryMessageQueue.size() < maxEnqueuedMessages) { + binaryMessageQueue.offer(data); + return false; + } + return sent; + } else { + if (binaryMessageQueue.size() < maxEnqueuedMessages) { + binaryMessageQueue.offer(data); + return false; + } + return false; + } + } + + /** + * Gets the current WebSocket instance. + * Thread-safe method to access the WebSocket connection. + * @return the WebSocket or null if not connected + */ + public WebSocket getWebSocket() { + return webSocket; + } + + /** + * @hidden + */ + @Override + public void onOpen(WebSocket webSocket, Response response) { + this.webSocket = webSocket; + connectionEstablishedTime = System.currentTimeMillis(); + retryCount.set(0); + flushMessageQueue(); + onWebSocketOpen(webSocket, response); + } + + @Override + public void onMessage(WebSocket webSocket, String text) { + onWebSocketMessage(webSocket, text); + } + + @Override + public void onMessage(WebSocket webSocket, ByteString bytes) { + onWebSocketBinaryMessage(webSocket, bytes); + } + + /** + * @hidden + */ + @Override + public void onFailure(WebSocket webSocket, Throwable t, Response response) { + this.webSocket = null; + long uptime = 0L; + if (connectionEstablishedTime > 0) { + uptime = System.currentTimeMillis() - connectionEstablishedTime; + if (uptime >= 5000) { + retryCount.set(0); + } + } + connectionEstablishedTime = 0L; + Throwable enhancedError = t; + if (t != null) { + String errorContext = "WebSocket connection failed"; + if (uptime > 0) { + errorContext += " after " + (uptime / 1000) + " seconds"; + } + if (response != null) { + errorContext += " with HTTP " + response.code() + " " + response.message(); + } + enhancedError = + new RuntimeException(errorContext + ": " + t.getClass().getSimpleName() + ": " + t.getMessage()); + enhancedError.initCause(t); + } + onWebSocketFailure(webSocket, enhancedError, response); + if (shouldReconnect.get()) { + scheduleReconnect(); + } + } + + /** + * Acknowledges a peer-initiated close so OkHttp can complete the close handshake and + * invoke {@link #onClosed(WebSocket, int, String)}. + * + * Code 1005 is a local "no status received" sentinel reported when the peer sent an empty + * close frame. It is not a valid code to put on the wire, so it is acknowledged with + * the normal closure code 1000 instead. + * + * @hidden + */ + @Override + public void onClosing(WebSocket webSocket, int code, String reason) { + webSocket.close(code == 1005 ? 1000 : code, reason); + } + + /** + * @hidden + */ + @Override + public void onClosed(WebSocket webSocket, int code, String reason) { + this.webSocket = null; + if (connectionEstablishedTime > 0) { + long uptime = System.currentTimeMillis() - connectionEstablishedTime; + if (uptime >= 5000) { + retryCount.set(0); + } + } + connectionEstablishedTime = 0L; + onWebSocketClosed(webSocket, code, reason); + if (shouldReconnect.get() && shouldReconnectAfterClose(webSocket, code, reason)) { + scheduleReconnect(); + } + } + + /** + * Decides whether a completed close handshake should trigger a reconnect. + * + * Only consulted when reconnection has not been disabled via {@link #disconnect()}. + * The default treats normal closure (1000) as terminal and reconnects on any + * other code. Subclasses can override this when protocol context establishes that a + * particular close is terminal, for example a no-status close following a message that + * ends the stream. + * + * @param webSocket The WebSocket that was closed + * @param code The close status code reported by OkHttp + * @param reason The close reason sent by the peer, or an empty string + * @return true to schedule a reconnect, false to stay disconnected + */ + protected boolean shouldReconnectAfterClose(WebSocket webSocket, int code, String reason) { + return code != 1000; + } + + /** + * Calculates the next reconnection delay using exponential backoff. + * + * Uses 0-based retry count where: + * - 0 = initial connection (not used by this method) + * - 1 = first retry (returns minReconnectionDelayMs) + * - 2+ = exponential backoff up to maxReconnectionDelayMs + */ + private long getNextDelay() { + if (retryCount.get() == 1) { + return activeOptions.minReconnectionDelayMs; + } + ReconnectOptions options = this.activeOptions; + long delay = (long) + (options.minReconnectionDelayMs * Math.pow(options.reconnectionDelayGrowFactor, retryCount.get() - 1)); + return Math.min(delay, options.maxReconnectionDelayMs); + } + + /** + * Schedules a reconnection attempt with appropriate delay. + * Increments retry count and uses exponential backoff. + * Does nothing once the owning client has been closed. + */ + private void scheduleReconnect() { + if (closedCheck.getAsBoolean()) { + shouldReconnect.set(false); + return; + } + retryCount.incrementAndGet(); + long delay = getNextDelay(); + reconnectExecutor.schedule(this::connect, delay, MILLISECONDS); + } + + /** + * Sends all queued messages after reconnection. + * + * Thread-safe: Synchronized to prevent race conditions with send() method. + * + * Algorithm: + * 1. Drains queue into temporary list to avoid holding lock during sends + * 2. Attempts to send each message in order + * 3. If any send fails, re-queues that message and all subsequent messages + * 4. Preserves message ordering during re-queueing + * 5. Repeats for binary message queue + */ + private synchronized void flushMessageQueue() { + WebSocket ws = webSocket; + if (ws != null) { + ArrayList tempQueue = new ArrayList<>(); + String message; + while ((message = messageQueue.poll()) != null) { + tempQueue.add(message); + } + for (int i = 0; i < tempQueue.size(); i++) { + if (!ws.send(tempQueue.get(i))) { + for (int j = i; j < tempQueue.size(); j++) { + messageQueue.offer(tempQueue.get(j)); + } + break; + } + } + ArrayList tempBinaryQueue = new ArrayList<>(); + ByteString binaryMsg; + while ((binaryMsg = binaryMessageQueue.poll()) != null) { + tempBinaryQueue.add(binaryMsg); + } + for (int i = 0; i < tempBinaryQueue.size(); i++) { + if (!ws.send(tempBinaryQueue.get(i))) { + for (int j = i; j < tempBinaryQueue.size(); j++) { + binaryMessageQueue.offer(tempBinaryQueue.get(j)); + } + break; + } + } + } + } + + protected abstract void onWebSocketOpen(WebSocket webSocket, Response response); + + protected abstract void onWebSocketMessage(WebSocket webSocket, String text); + + protected abstract void onWebSocketBinaryMessage(WebSocket webSocket, ByteString bytes); + + protected abstract void onWebSocketFailure(WebSocket webSocket, Throwable t, Response response); + + protected abstract void onWebSocketClosed(WebSocket webSocket, int code, String reason); + + /** + * Configuration options for automatic reconnection. + */ + public static final class ReconnectOptions { + public final long minReconnectionDelayMs; + + public final long maxReconnectionDelayMs; + + public final double reconnectionDelayGrowFactor; + + public final int maxRetries; + + public final int maxEnqueuedMessages; + + public final long connectionTimeoutMs; + + private ReconnectOptions(Builder builder) { + this.minReconnectionDelayMs = builder.minReconnectionDelayMs; + this.maxReconnectionDelayMs = builder.maxReconnectionDelayMs; + this.reconnectionDelayGrowFactor = builder.reconnectionDelayGrowFactor; + this.maxRetries = builder.maxRetries; + this.maxEnqueuedMessages = builder.maxEnqueuedMessages; + this.connectionTimeoutMs = builder.connectionTimeoutMs; + } + + public static Builder builder() { + return new Builder(); + } + + public static final class Builder { + private long minReconnectionDelayMs; + + private long maxReconnectionDelayMs; + + private double reconnectionDelayGrowFactor; + + private int maxRetries; + + private int maxEnqueuedMessages; + + private long connectionTimeoutMs; + + public Builder() { + this.minReconnectionDelayMs = 1000; + this.maxReconnectionDelayMs = 10000; + this.reconnectionDelayGrowFactor = 1.3; + this.maxRetries = 2147483647; + this.maxEnqueuedMessages = 1000; + this.connectionTimeoutMs = 4000; + } + + public Builder minReconnectionDelayMs(long minReconnectionDelayMs) { + this.minReconnectionDelayMs = minReconnectionDelayMs; + return this; + } + + public Builder maxReconnectionDelayMs(long maxReconnectionDelayMs) { + this.maxReconnectionDelayMs = maxReconnectionDelayMs; + return this; + } + + public Builder reconnectionDelayGrowFactor(double reconnectionDelayGrowFactor) { + this.reconnectionDelayGrowFactor = reconnectionDelayGrowFactor; + return this; + } + + public Builder maxRetries(int maxRetries) { + this.maxRetries = maxRetries; + return this; + } + + public Builder maxEnqueuedMessages(int maxEnqueuedMessages) { + this.maxEnqueuedMessages = maxEnqueuedMessages; + return this; + } + + /** + * Sets the per-attempt connection timeout in milliseconds. Defaults to {@code 4000}. + */ + public Builder connectionTimeoutMs(long connectionTimeoutMs) { + this.connectionTimeoutMs = connectionTimeoutMs; + return this; + } + + /** + * Builds the ReconnectOptions with validation. + * + * Validates that: + * - All delay values are positive + * - minReconnectionDelayMs <= maxReconnectionDelayMs + * - reconnectionDelayGrowFactor >= 1.0 + * - maxRetries and maxEnqueuedMessages are non-negative + * + * @return The validated ReconnectOptions instance + * @throws IllegalArgumentException if configuration is invalid + */ + public ReconnectOptions build() { + if (minReconnectionDelayMs <= 0) { + throw new IllegalArgumentException("minReconnectionDelayMs must be positive"); + } + if (maxReconnectionDelayMs <= 0) { + throw new IllegalArgumentException("maxReconnectionDelayMs must be positive"); + } + if (minReconnectionDelayMs > maxReconnectionDelayMs) { + throw new IllegalArgumentException("minReconnectionDelayMs (" + minReconnectionDelayMs + + ") must not exceed maxReconnectionDelayMs (" + maxReconnectionDelayMs + ")"); + } + if (reconnectionDelayGrowFactor < 1.0) { + throw new IllegalArgumentException("reconnectionDelayGrowFactor must be >= 1.0"); + } + if (maxRetries < 0) { + throw new IllegalArgumentException("maxRetries must be non-negative"); + } + if (maxEnqueuedMessages < 0) { + throw new IllegalArgumentException("maxEnqueuedMessages must be non-negative"); + } + if (connectionTimeoutMs <= 0) { + throw new IllegalArgumentException("connectionTimeoutMs must be positive"); + } + return new ReconnectOptions(this); + } + } + } +} diff --git a/src/main/java/com/deepgram/resources/agent/v1/types/AgentV1AgentAudioDone.java.bak b/src/main/java/com/deepgram/resources/agent/v1/types/AgentV1AgentAudioDone.java.bak new file mode 100644 index 0000000..5962f4b --- /dev/null +++ b/src/main/java/com/deepgram/resources/agent/v1/types/AgentV1AgentAudioDone.java.bak @@ -0,0 +1,83 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ +package com.deepgram.resources.agent.v1.types; + +import com.deepgram.core.ObjectMappers; +import com.fasterxml.jackson.annotation.JsonAnyGetter; +import com.fasterxml.jackson.annotation.JsonAnySetter; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import java.util.HashMap; +import java.util.Map; + +@JsonInclude(JsonInclude.Include.NON_ABSENT) +@JsonDeserialize(builder = AgentV1AgentAudioDone.Builder.class) +public final class AgentV1AgentAudioDone { + private final Map additionalProperties; + + private AgentV1AgentAudioDone(Map additionalProperties) { + this.additionalProperties = additionalProperties; + } + + /** + * @return Message type identifier indicating the agent has finished sending audio + */ + @JsonProperty("type") + public String getType() { + return "AgentAudioDone"; + } + + @java.lang.Override + public boolean equals(Object other) { + if (this == other) return true; + return other instanceof AgentV1AgentAudioDone; + } + + @java.lang.Override + public int hashCode() { + return getClass().hashCode(); + } + + @JsonAnyGetter + public Map getAdditionalProperties() { + return this.additionalProperties; + } + + @java.lang.Override + public String toString() { + return ObjectMappers.stringify(this); + } + + public static Builder builder() { + return new Builder(); + } + + @JsonIgnoreProperties(ignoreUnknown = true) + public static final class Builder { + @JsonAnySetter + private Map additionalProperties = new HashMap<>(); + + private Builder() {} + + public Builder from(AgentV1AgentAudioDone other) { + return this; + } + + public AgentV1AgentAudioDone build() { + return new AgentV1AgentAudioDone(additionalProperties); + } + + public Builder additionalProperty(String key, Object value) { + this.additionalProperties.put(key, value); + return this; + } + + public Builder additionalProperties(Map additionalProperties) { + this.additionalProperties.putAll(additionalProperties); + return this; + } + } +} diff --git a/src/main/java/com/deepgram/resources/agent/v1/types/AgentV1ForceEndTurn.java.bak b/src/main/java/com/deepgram/resources/agent/v1/types/AgentV1ForceEndTurn.java.bak new file mode 100644 index 0000000..9bd5f76 --- /dev/null +++ b/src/main/java/com/deepgram/resources/agent/v1/types/AgentV1ForceEndTurn.java.bak @@ -0,0 +1,83 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ +package com.deepgram.resources.agent.v1.types; + +import com.deepgram.core.ObjectMappers; +import com.fasterxml.jackson.annotation.JsonAnyGetter; +import com.fasterxml.jackson.annotation.JsonAnySetter; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import java.util.HashMap; +import java.util.Map; + +@JsonInclude(JsonInclude.Include.NON_ABSENT) +@JsonDeserialize(builder = AgentV1ForceEndTurn.Builder.class) +public final class AgentV1ForceEndTurn { + private final Map additionalProperties; + + private AgentV1ForceEndTurn(Map additionalProperties) { + this.additionalProperties = additionalProperties; + } + + /** + * @return Message type identifier for forcing the end of the current turn + */ + @JsonProperty("type") + public String getType() { + return "ForceEndTurn"; + } + + @java.lang.Override + public boolean equals(Object other) { + if (this == other) return true; + return other instanceof AgentV1ForceEndTurn; + } + + @java.lang.Override + public int hashCode() { + return getClass().hashCode(); + } + + @JsonAnyGetter + public Map getAdditionalProperties() { + return this.additionalProperties; + } + + @java.lang.Override + public String toString() { + return ObjectMappers.stringify(this); + } + + public static Builder builder() { + return new Builder(); + } + + @JsonIgnoreProperties(ignoreUnknown = true) + public static final class Builder { + @JsonAnySetter + private Map additionalProperties = new HashMap<>(); + + private Builder() {} + + public Builder from(AgentV1ForceEndTurn other) { + return this; + } + + public AgentV1ForceEndTurn build() { + return new AgentV1ForceEndTurn(additionalProperties); + } + + public Builder additionalProperty(String key, Object value) { + this.additionalProperties.put(key, value); + return this; + } + + public Builder additionalProperties(Map additionalProperties) { + this.additionalProperties.putAll(additionalProperties); + return this; + } + } +} diff --git a/src/main/java/com/deepgram/resources/agent/v1/types/AgentV1KeepAlive.java.bak b/src/main/java/com/deepgram/resources/agent/v1/types/AgentV1KeepAlive.java.bak new file mode 100644 index 0000000..f82f59e --- /dev/null +++ b/src/main/java/com/deepgram/resources/agent/v1/types/AgentV1KeepAlive.java.bak @@ -0,0 +1,83 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ +package com.deepgram.resources.agent.v1.types; + +import com.deepgram.core.ObjectMappers; +import com.fasterxml.jackson.annotation.JsonAnyGetter; +import com.fasterxml.jackson.annotation.JsonAnySetter; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import java.util.HashMap; +import java.util.Map; + +@JsonInclude(JsonInclude.Include.NON_ABSENT) +@JsonDeserialize(builder = AgentV1KeepAlive.Builder.class) +public final class AgentV1KeepAlive { + private final Map additionalProperties; + + private AgentV1KeepAlive(Map additionalProperties) { + this.additionalProperties = additionalProperties; + } + + /** + * @return Message type identifier + */ + @JsonProperty("type") + public String getType() { + return "KeepAlive"; + } + + @java.lang.Override + public boolean equals(Object other) { + if (this == other) return true; + return other instanceof AgentV1KeepAlive; + } + + @java.lang.Override + public int hashCode() { + return getClass().hashCode(); + } + + @JsonAnyGetter + public Map getAdditionalProperties() { + return this.additionalProperties; + } + + @java.lang.Override + public String toString() { + return ObjectMappers.stringify(this); + } + + public static Builder builder() { + return new Builder(); + } + + @JsonIgnoreProperties(ignoreUnknown = true) + public static final class Builder { + @JsonAnySetter + private Map additionalProperties = new HashMap<>(); + + private Builder() {} + + public Builder from(AgentV1KeepAlive other) { + return this; + } + + public AgentV1KeepAlive build() { + return new AgentV1KeepAlive(additionalProperties); + } + + public Builder additionalProperty(String key, Object value) { + this.additionalProperties.put(key, value); + return this; + } + + public Builder additionalProperties(Map additionalProperties) { + this.additionalProperties.putAll(additionalProperties); + return this; + } + } +} diff --git a/src/main/java/com/deepgram/resources/agent/v1/types/AgentV1ListenUpdated.java.bak b/src/main/java/com/deepgram/resources/agent/v1/types/AgentV1ListenUpdated.java.bak new file mode 100644 index 0000000..43b8628 --- /dev/null +++ b/src/main/java/com/deepgram/resources/agent/v1/types/AgentV1ListenUpdated.java.bak @@ -0,0 +1,83 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ +package com.deepgram.resources.agent.v1.types; + +import com.deepgram.core.ObjectMappers; +import com.fasterxml.jackson.annotation.JsonAnyGetter; +import com.fasterxml.jackson.annotation.JsonAnySetter; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import java.util.HashMap; +import java.util.Map; + +@JsonInclude(JsonInclude.Include.NON_ABSENT) +@JsonDeserialize(builder = AgentV1ListenUpdated.Builder.class) +public final class AgentV1ListenUpdated { + private final Map additionalProperties; + + private AgentV1ListenUpdated(Map additionalProperties) { + this.additionalProperties = additionalProperties; + } + + /** + * @return Message type identifier for listen update confirmation + */ + @JsonProperty("type") + public String getType() { + return "ListenUpdated"; + } + + @java.lang.Override + public boolean equals(Object other) { + if (this == other) return true; + return other instanceof AgentV1ListenUpdated; + } + + @java.lang.Override + public int hashCode() { + return getClass().hashCode(); + } + + @JsonAnyGetter + public Map getAdditionalProperties() { + return this.additionalProperties; + } + + @java.lang.Override + public String toString() { + return ObjectMappers.stringify(this); + } + + public static Builder builder() { + return new Builder(); + } + + @JsonIgnoreProperties(ignoreUnknown = true) + public static final class Builder { + @JsonAnySetter + private Map additionalProperties = new HashMap<>(); + + private Builder() {} + + public Builder from(AgentV1ListenUpdated other) { + return this; + } + + public AgentV1ListenUpdated build() { + return new AgentV1ListenUpdated(additionalProperties); + } + + public Builder additionalProperty(String key, Object value) { + this.additionalProperties.put(key, value); + return this; + } + + public Builder additionalProperties(Map additionalProperties) { + this.additionalProperties.putAll(additionalProperties); + return this; + } + } +} diff --git a/src/main/java/com/deepgram/resources/agent/v1/types/AgentV1PromptUpdated.java.bak b/src/main/java/com/deepgram/resources/agent/v1/types/AgentV1PromptUpdated.java.bak new file mode 100644 index 0000000..e6efced --- /dev/null +++ b/src/main/java/com/deepgram/resources/agent/v1/types/AgentV1PromptUpdated.java.bak @@ -0,0 +1,83 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ +package com.deepgram.resources.agent.v1.types; + +import com.deepgram.core.ObjectMappers; +import com.fasterxml.jackson.annotation.JsonAnyGetter; +import com.fasterxml.jackson.annotation.JsonAnySetter; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import java.util.HashMap; +import java.util.Map; + +@JsonInclude(JsonInclude.Include.NON_ABSENT) +@JsonDeserialize(builder = AgentV1PromptUpdated.Builder.class) +public final class AgentV1PromptUpdated { + private final Map additionalProperties; + + private AgentV1PromptUpdated(Map additionalProperties) { + this.additionalProperties = additionalProperties; + } + + /** + * @return Message type identifier for prompt update confirmation + */ + @JsonProperty("type") + public String getType() { + return "PromptUpdated"; + } + + @java.lang.Override + public boolean equals(Object other) { + if (this == other) return true; + return other instanceof AgentV1PromptUpdated; + } + + @java.lang.Override + public int hashCode() { + return getClass().hashCode(); + } + + @JsonAnyGetter + public Map getAdditionalProperties() { + return this.additionalProperties; + } + + @java.lang.Override + public String toString() { + return ObjectMappers.stringify(this); + } + + public static Builder builder() { + return new Builder(); + } + + @JsonIgnoreProperties(ignoreUnknown = true) + public static final class Builder { + @JsonAnySetter + private Map additionalProperties = new HashMap<>(); + + private Builder() {} + + public Builder from(AgentV1PromptUpdated other) { + return this; + } + + public AgentV1PromptUpdated build() { + return new AgentV1PromptUpdated(additionalProperties); + } + + public Builder additionalProperty(String key, Object value) { + this.additionalProperties.put(key, value); + return this; + } + + public Builder additionalProperties(Map additionalProperties) { + this.additionalProperties.putAll(additionalProperties); + return this; + } + } +} diff --git a/src/main/java/com/deepgram/resources/agent/v1/types/AgentV1SettingsAgentContextListenProvider.java.bak b/src/main/java/com/deepgram/resources/agent/v1/types/AgentV1SettingsAgentContextListenProvider.java.bak new file mode 100644 index 0000000..393f1f9 --- /dev/null +++ b/src/main/java/com/deepgram/resources/agent/v1/types/AgentV1SettingsAgentContextListenProvider.java.bak @@ -0,0 +1,224 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ +package com.deepgram.resources.agent.v1.types; + +import com.deepgram.types.DeepgramListenProviderV1; +import com.deepgram.types.DeepgramListenProviderV2; +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonSubTypes; +import com.fasterxml.jackson.annotation.JsonTypeInfo; +import com.fasterxml.jackson.annotation.JsonTypeName; +import com.fasterxml.jackson.annotation.JsonUnwrapped; +import com.fasterxml.jackson.annotation.JsonValue; +import java.util.Objects; +import java.util.Optional; + +public final class AgentV1SettingsAgentContextListenProvider { + private final Value value; + + @JsonCreator(mode = JsonCreator.Mode.DELEGATING) + private AgentV1SettingsAgentContextListenProvider(Value value) { + this.value = value; + } + + public T visit(Visitor visitor) { + return value.visit(visitor); + } + + public static AgentV1SettingsAgentContextListenProvider v1(DeepgramListenProviderV1 value) { + return new AgentV1SettingsAgentContextListenProvider(new V1Value(value)); + } + + public static AgentV1SettingsAgentContextListenProvider v2(DeepgramListenProviderV2 value) { + return new AgentV1SettingsAgentContextListenProvider(new V2Value(value)); + } + + public boolean isV1() { + return value instanceof V1Value; + } + + public boolean isV2() { + return value instanceof V2Value; + } + + public boolean _isUnknown() { + return value instanceof _UnknownValue; + } + + public Optional getV1() { + if (isV1()) { + return Optional.of(((V1Value) value).value); + } + return Optional.empty(); + } + + public Optional getV2() { + if (isV2()) { + return Optional.of(((V2Value) value).value); + } + return Optional.empty(); + } + + public Optional _getUnknown() { + if (_isUnknown()) { + return Optional.of(((_UnknownValue) value).value); + } + return Optional.empty(); + } + + @Override + public boolean equals(Object other) { + if (this == other) return true; + return other instanceof AgentV1SettingsAgentContextListenProvider + && value.equals(((AgentV1SettingsAgentContextListenProvider) other).value); + } + + @Override + public int hashCode() { + return Objects.hash(value); + } + + @Override + public String toString() { + return value.toString(); + } + + @JsonValue + private Value getValue() { + return this.value; + } + + public interface Visitor { + T visitV1(DeepgramListenProviderV1 v1); + + T visitV2(DeepgramListenProviderV2 v2); + + T _visitUnknown(Object unknownType); + } + + @JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "version", visible = true, defaultImpl = V2Value.class) + @JsonSubTypes({@JsonSubTypes.Type(V1Value.class), @JsonSubTypes.Type(V2Value.class)}) + @JsonIgnoreProperties(ignoreUnknown = true) + private interface Value { + T visit(Visitor visitor); + } + + @JsonTypeName("v1") + @JsonIgnoreProperties("version") + private static final class V1Value implements Value { + @JsonUnwrapped + @JsonIgnoreProperties(value = "version", allowSetters = true) + private DeepgramListenProviderV1 value; + + @JsonCreator(mode = JsonCreator.Mode.PROPERTIES) + private V1Value() {} + + private V1Value(DeepgramListenProviderV1 value) { + this.value = value; + } + + @java.lang.Override + public T visit(Visitor visitor) { + return visitor.visitV1(value); + } + + @java.lang.Override + public boolean equals(Object other) { + if (this == other) return true; + return other instanceof V1Value && equalTo((V1Value) other); + } + + private boolean equalTo(V1Value other) { + return value.equals(other.value); + } + + @java.lang.Override + public int hashCode() { + return Objects.hash(this.value); + } + + @java.lang.Override + public String toString() { + return "AgentV1SettingsAgentContextListenProvider{" + "value: " + value + "}"; + } + } + + @JsonTypeName("v2") + @JsonIgnoreProperties("version") + private static final class V2Value implements Value { + @JsonUnwrapped + @JsonIgnoreProperties(value = "version", allowSetters = true) + private DeepgramListenProviderV2 value; + + @JsonCreator(mode = JsonCreator.Mode.PROPERTIES) + private V2Value() {} + + private V2Value(DeepgramListenProviderV2 value) { + this.value = value; + } + + @java.lang.Override + public T visit(Visitor visitor) { + return visitor.visitV2(value); + } + + @java.lang.Override + public boolean equals(Object other) { + if (this == other) return true; + return other instanceof V2Value && equalTo((V2Value) other); + } + + private boolean equalTo(V2Value other) { + return value.equals(other.value); + } + + @java.lang.Override + public int hashCode() { + return Objects.hash(this.value); + } + + @java.lang.Override + public String toString() { + return "AgentV1SettingsAgentContextListenProvider{" + "value: " + value + "}"; + } + } + + @JsonIgnoreProperties("version") + private static final class _UnknownValue implements Value { + private String type; + + @JsonValue + private Object value; + + @JsonCreator(mode = JsonCreator.Mode.PROPERTIES) + private _UnknownValue(@JsonProperty("value") Object value) {} + + @java.lang.Override + public T visit(Visitor visitor) { + return visitor._visitUnknown(value); + } + + @java.lang.Override + public boolean equals(Object other) { + if (this == other) return true; + return other instanceof _UnknownValue && equalTo((_UnknownValue) other); + } + + private boolean equalTo(_UnknownValue other) { + return type.equals(other.type) && value.equals(other.value); + } + + @java.lang.Override + public int hashCode() { + return Objects.hash(this.type, this.value); + } + + @java.lang.Override + public String toString() { + return "AgentV1SettingsAgentContextListenProvider{" + "type: " + type + ", value: " + value + "}"; + } + } +} diff --git a/src/main/java/com/deepgram/resources/agent/v1/types/AgentV1SettingsAgentListenProvider.java.bak b/src/main/java/com/deepgram/resources/agent/v1/types/AgentV1SettingsAgentListenProvider.java.bak new file mode 100644 index 0000000..345ba2f --- /dev/null +++ b/src/main/java/com/deepgram/resources/agent/v1/types/AgentV1SettingsAgentListenProvider.java.bak @@ -0,0 +1,224 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ +package com.deepgram.resources.agent.v1.types; + +import com.deepgram.types.DeepgramListenProviderV1; +import com.deepgram.types.DeepgramListenProviderV2; +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonSubTypes; +import com.fasterxml.jackson.annotation.JsonTypeInfo; +import com.fasterxml.jackson.annotation.JsonTypeName; +import com.fasterxml.jackson.annotation.JsonUnwrapped; +import com.fasterxml.jackson.annotation.JsonValue; +import java.util.Objects; +import java.util.Optional; + +public final class AgentV1SettingsAgentListenProvider { + private final Value value; + + @JsonCreator(mode = JsonCreator.Mode.DELEGATING) + private AgentV1SettingsAgentListenProvider(Value value) { + this.value = value; + } + + public T visit(Visitor visitor) { + return value.visit(visitor); + } + + public static AgentV1SettingsAgentListenProvider v1(DeepgramListenProviderV1 value) { + return new AgentV1SettingsAgentListenProvider(new V1Value(value)); + } + + public static AgentV1SettingsAgentListenProvider v2(DeepgramListenProviderV2 value) { + return new AgentV1SettingsAgentListenProvider(new V2Value(value)); + } + + public boolean isV1() { + return value instanceof V1Value; + } + + public boolean isV2() { + return value instanceof V2Value; + } + + public boolean _isUnknown() { + return value instanceof _UnknownValue; + } + + public Optional getV1() { + if (isV1()) { + return Optional.of(((V1Value) value).value); + } + return Optional.empty(); + } + + public Optional getV2() { + if (isV2()) { + return Optional.of(((V2Value) value).value); + } + return Optional.empty(); + } + + public Optional _getUnknown() { + if (_isUnknown()) { + return Optional.of(((_UnknownValue) value).value); + } + return Optional.empty(); + } + + @Override + public boolean equals(Object other) { + if (this == other) return true; + return other instanceof AgentV1SettingsAgentListenProvider + && value.equals(((AgentV1SettingsAgentListenProvider) other).value); + } + + @Override + public int hashCode() { + return Objects.hash(value); + } + + @Override + public String toString() { + return value.toString(); + } + + @JsonValue + private Value getValue() { + return this.value; + } + + public interface Visitor { + T visitV1(DeepgramListenProviderV1 v1); + + T visitV2(DeepgramListenProviderV2 v2); + + T _visitUnknown(Object unknownType); + } + + @JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "version", visible = true, defaultImpl = V2Value.class) + @JsonSubTypes({@JsonSubTypes.Type(V1Value.class), @JsonSubTypes.Type(V2Value.class)}) + @JsonIgnoreProperties(ignoreUnknown = true) + private interface Value { + T visit(Visitor visitor); + } + + @JsonTypeName("v1") + @JsonIgnoreProperties("version") + private static final class V1Value implements Value { + @JsonUnwrapped + @JsonIgnoreProperties(value = "version", allowSetters = true) + private DeepgramListenProviderV1 value; + + @JsonCreator(mode = JsonCreator.Mode.PROPERTIES) + private V1Value() {} + + private V1Value(DeepgramListenProviderV1 value) { + this.value = value; + } + + @java.lang.Override + public T visit(Visitor visitor) { + return visitor.visitV1(value); + } + + @java.lang.Override + public boolean equals(Object other) { + if (this == other) return true; + return other instanceof V1Value && equalTo((V1Value) other); + } + + private boolean equalTo(V1Value other) { + return value.equals(other.value); + } + + @java.lang.Override + public int hashCode() { + return Objects.hash(this.value); + } + + @java.lang.Override + public String toString() { + return "AgentV1SettingsAgentListenProvider{" + "value: " + value + "}"; + } + } + + @JsonTypeName("v2") + @JsonIgnoreProperties("version") + private static final class V2Value implements Value { + @JsonUnwrapped + @JsonIgnoreProperties(value = "version", allowSetters = true) + private DeepgramListenProviderV2 value; + + @JsonCreator(mode = JsonCreator.Mode.PROPERTIES) + private V2Value() {} + + private V2Value(DeepgramListenProviderV2 value) { + this.value = value; + } + + @java.lang.Override + public T visit(Visitor visitor) { + return visitor.visitV2(value); + } + + @java.lang.Override + public boolean equals(Object other) { + if (this == other) return true; + return other instanceof V2Value && equalTo((V2Value) other); + } + + private boolean equalTo(V2Value other) { + return value.equals(other.value); + } + + @java.lang.Override + public int hashCode() { + return Objects.hash(this.value); + } + + @java.lang.Override + public String toString() { + return "AgentV1SettingsAgentListenProvider{" + "value: " + value + "}"; + } + } + + @JsonIgnoreProperties("version") + private static final class _UnknownValue implements Value { + private String type; + + @JsonValue + private Object value; + + @JsonCreator(mode = JsonCreator.Mode.PROPERTIES) + private _UnknownValue(@JsonProperty("value") Object value) {} + + @java.lang.Override + public T visit(Visitor visitor) { + return visitor._visitUnknown(value); + } + + @java.lang.Override + public boolean equals(Object other) { + if (this == other) return true; + return other instanceof _UnknownValue && equalTo((_UnknownValue) other); + } + + private boolean equalTo(_UnknownValue other) { + return type.equals(other.type) && value.equals(other.value); + } + + @java.lang.Override + public int hashCode() { + return Objects.hash(this.type, this.value); + } + + @java.lang.Override + public String toString() { + return "AgentV1SettingsAgentListenProvider{" + "type: " + type + ", value: " + value + "}"; + } + } +} diff --git a/src/main/java/com/deepgram/resources/agent/v1/types/AgentV1SettingsApplied.java.bak b/src/main/java/com/deepgram/resources/agent/v1/types/AgentV1SettingsApplied.java.bak new file mode 100644 index 0000000..ad1fdb2 --- /dev/null +++ b/src/main/java/com/deepgram/resources/agent/v1/types/AgentV1SettingsApplied.java.bak @@ -0,0 +1,83 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ +package com.deepgram.resources.agent.v1.types; + +import com.deepgram.core.ObjectMappers; +import com.fasterxml.jackson.annotation.JsonAnyGetter; +import com.fasterxml.jackson.annotation.JsonAnySetter; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import java.util.HashMap; +import java.util.Map; + +@JsonInclude(JsonInclude.Include.NON_ABSENT) +@JsonDeserialize(builder = AgentV1SettingsApplied.Builder.class) +public final class AgentV1SettingsApplied { + private final Map additionalProperties; + + private AgentV1SettingsApplied(Map additionalProperties) { + this.additionalProperties = additionalProperties; + } + + /** + * @return Message type identifier for settings applied confirmation + */ + @JsonProperty("type") + public String getType() { + return "SettingsApplied"; + } + + @java.lang.Override + public boolean equals(Object other) { + if (this == other) return true; + return other instanceof AgentV1SettingsApplied; + } + + @java.lang.Override + public int hashCode() { + return getClass().hashCode(); + } + + @JsonAnyGetter + public Map getAdditionalProperties() { + return this.additionalProperties; + } + + @java.lang.Override + public String toString() { + return ObjectMappers.stringify(this); + } + + public static Builder builder() { + return new Builder(); + } + + @JsonIgnoreProperties(ignoreUnknown = true) + public static final class Builder { + @JsonAnySetter + private Map additionalProperties = new HashMap<>(); + + private Builder() {} + + public Builder from(AgentV1SettingsApplied other) { + return this; + } + + public AgentV1SettingsApplied build() { + return new AgentV1SettingsApplied(additionalProperties); + } + + public Builder additionalProperty(String key, Object value) { + this.additionalProperties.put(key, value); + return this; + } + + public Builder additionalProperties(Map additionalProperties) { + this.additionalProperties.putAll(additionalProperties); + return this; + } + } +} diff --git a/src/main/java/com/deepgram/resources/agent/v1/types/AgentV1SpeakUpdated.java.bak b/src/main/java/com/deepgram/resources/agent/v1/types/AgentV1SpeakUpdated.java.bak new file mode 100644 index 0000000..8b134b5 --- /dev/null +++ b/src/main/java/com/deepgram/resources/agent/v1/types/AgentV1SpeakUpdated.java.bak @@ -0,0 +1,83 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ +package com.deepgram.resources.agent.v1.types; + +import com.deepgram.core.ObjectMappers; +import com.fasterxml.jackson.annotation.JsonAnyGetter; +import com.fasterxml.jackson.annotation.JsonAnySetter; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import java.util.HashMap; +import java.util.Map; + +@JsonInclude(JsonInclude.Include.NON_ABSENT) +@JsonDeserialize(builder = AgentV1SpeakUpdated.Builder.class) +public final class AgentV1SpeakUpdated { + private final Map additionalProperties; + + private AgentV1SpeakUpdated(Map additionalProperties) { + this.additionalProperties = additionalProperties; + } + + /** + * @return Message type identifier for speak update confirmation + */ + @JsonProperty("type") + public String getType() { + return "SpeakUpdated"; + } + + @java.lang.Override + public boolean equals(Object other) { + if (this == other) return true; + return other instanceof AgentV1SpeakUpdated; + } + + @java.lang.Override + public int hashCode() { + return getClass().hashCode(); + } + + @JsonAnyGetter + public Map getAdditionalProperties() { + return this.additionalProperties; + } + + @java.lang.Override + public String toString() { + return ObjectMappers.stringify(this); + } + + public static Builder builder() { + return new Builder(); + } + + @JsonIgnoreProperties(ignoreUnknown = true) + public static final class Builder { + @JsonAnySetter + private Map additionalProperties = new HashMap<>(); + + private Builder() {} + + public Builder from(AgentV1SpeakUpdated other) { + return this; + } + + public AgentV1SpeakUpdated build() { + return new AgentV1SpeakUpdated(additionalProperties); + } + + public Builder additionalProperty(String key, Object value) { + this.additionalProperties.put(key, value); + return this; + } + + public Builder additionalProperties(Map additionalProperties) { + this.additionalProperties.putAll(additionalProperties); + return this; + } + } +} diff --git a/src/main/java/com/deepgram/resources/agent/v1/types/AgentV1ThinkUpdated.java.bak b/src/main/java/com/deepgram/resources/agent/v1/types/AgentV1ThinkUpdated.java.bak new file mode 100644 index 0000000..2d67cad --- /dev/null +++ b/src/main/java/com/deepgram/resources/agent/v1/types/AgentV1ThinkUpdated.java.bak @@ -0,0 +1,83 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ +package com.deepgram.resources.agent.v1.types; + +import com.deepgram.core.ObjectMappers; +import com.fasterxml.jackson.annotation.JsonAnyGetter; +import com.fasterxml.jackson.annotation.JsonAnySetter; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import java.util.HashMap; +import java.util.Map; + +@JsonInclude(JsonInclude.Include.NON_ABSENT) +@JsonDeserialize(builder = AgentV1ThinkUpdated.Builder.class) +public final class AgentV1ThinkUpdated { + private final Map additionalProperties; + + private AgentV1ThinkUpdated(Map additionalProperties) { + this.additionalProperties = additionalProperties; + } + + /** + * @return Message type identifier for think update confirmation + */ + @JsonProperty("type") + public String getType() { + return "ThinkUpdated"; + } + + @java.lang.Override + public boolean equals(Object other) { + if (this == other) return true; + return other instanceof AgentV1ThinkUpdated; + } + + @java.lang.Override + public int hashCode() { + return getClass().hashCode(); + } + + @JsonAnyGetter + public Map getAdditionalProperties() { + return this.additionalProperties; + } + + @java.lang.Override + public String toString() { + return ObjectMappers.stringify(this); + } + + public static Builder builder() { + return new Builder(); + } + + @JsonIgnoreProperties(ignoreUnknown = true) + public static final class Builder { + @JsonAnySetter + private Map additionalProperties = new HashMap<>(); + + private Builder() {} + + public Builder from(AgentV1ThinkUpdated other) { + return this; + } + + public AgentV1ThinkUpdated build() { + return new AgentV1ThinkUpdated(additionalProperties); + } + + public Builder additionalProperty(String key, Object value) { + this.additionalProperties.put(key, value); + return this; + } + + public Builder additionalProperties(Map additionalProperties) { + this.additionalProperties.putAll(additionalProperties); + return this; + } + } +} diff --git a/src/main/java/com/deepgram/resources/agent/v1/types/AgentV1UpdateListenListenProvider.java.bak b/src/main/java/com/deepgram/resources/agent/v1/types/AgentV1UpdateListenListenProvider.java.bak new file mode 100644 index 0000000..e739aec --- /dev/null +++ b/src/main/java/com/deepgram/resources/agent/v1/types/AgentV1UpdateListenListenProvider.java.bak @@ -0,0 +1,224 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ +package com.deepgram.resources.agent.v1.types; + +import com.deepgram.types.DeepgramListenProviderV1; +import com.deepgram.types.DeepgramListenProviderV2; +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonSubTypes; +import com.fasterxml.jackson.annotation.JsonTypeInfo; +import com.fasterxml.jackson.annotation.JsonTypeName; +import com.fasterxml.jackson.annotation.JsonUnwrapped; +import com.fasterxml.jackson.annotation.JsonValue; +import java.util.Objects; +import java.util.Optional; + +public final class AgentV1UpdateListenListenProvider { + private final Value value; + + @JsonCreator(mode = JsonCreator.Mode.DELEGATING) + private AgentV1UpdateListenListenProvider(Value value) { + this.value = value; + } + + public T visit(Visitor visitor) { + return value.visit(visitor); + } + + public static AgentV1UpdateListenListenProvider v1(DeepgramListenProviderV1 value) { + return new AgentV1UpdateListenListenProvider(new V1Value(value)); + } + + public static AgentV1UpdateListenListenProvider v2(DeepgramListenProviderV2 value) { + return new AgentV1UpdateListenListenProvider(new V2Value(value)); + } + + public boolean isV1() { + return value instanceof V1Value; + } + + public boolean isV2() { + return value instanceof V2Value; + } + + public boolean _isUnknown() { + return value instanceof _UnknownValue; + } + + public Optional getV1() { + if (isV1()) { + return Optional.of(((V1Value) value).value); + } + return Optional.empty(); + } + + public Optional getV2() { + if (isV2()) { + return Optional.of(((V2Value) value).value); + } + return Optional.empty(); + } + + public Optional _getUnknown() { + if (_isUnknown()) { + return Optional.of(((_UnknownValue) value).value); + } + return Optional.empty(); + } + + @Override + public boolean equals(Object other) { + if (this == other) return true; + return other instanceof AgentV1UpdateListenListenProvider + && value.equals(((AgentV1UpdateListenListenProvider) other).value); + } + + @Override + public int hashCode() { + return Objects.hash(value); + } + + @Override + public String toString() { + return value.toString(); + } + + @JsonValue + private Value getValue() { + return this.value; + } + + public interface Visitor { + T visitV1(DeepgramListenProviderV1 v1); + + T visitV2(DeepgramListenProviderV2 v2); + + T _visitUnknown(Object unknownType); + } + + @JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "version", visible = true, defaultImpl = V2Value.class) + @JsonSubTypes({@JsonSubTypes.Type(V1Value.class), @JsonSubTypes.Type(V2Value.class)}) + @JsonIgnoreProperties(ignoreUnknown = true) + private interface Value { + T visit(Visitor visitor); + } + + @JsonTypeName("v1") + @JsonIgnoreProperties("version") + private static final class V1Value implements Value { + @JsonUnwrapped + @JsonIgnoreProperties(value = "version", allowSetters = true) + private DeepgramListenProviderV1 value; + + @JsonCreator(mode = JsonCreator.Mode.PROPERTIES) + private V1Value() {} + + private V1Value(DeepgramListenProviderV1 value) { + this.value = value; + } + + @java.lang.Override + public T visit(Visitor visitor) { + return visitor.visitV1(value); + } + + @java.lang.Override + public boolean equals(Object other) { + if (this == other) return true; + return other instanceof V1Value && equalTo((V1Value) other); + } + + private boolean equalTo(V1Value other) { + return value.equals(other.value); + } + + @java.lang.Override + public int hashCode() { + return Objects.hash(this.value); + } + + @java.lang.Override + public String toString() { + return "AgentV1UpdateListenListenProvider{" + "value: " + value + "}"; + } + } + + @JsonTypeName("v2") + @JsonIgnoreProperties("version") + private static final class V2Value implements Value { + @JsonUnwrapped + @JsonIgnoreProperties(value = "version", allowSetters = true) + private DeepgramListenProviderV2 value; + + @JsonCreator(mode = JsonCreator.Mode.PROPERTIES) + private V2Value() {} + + private V2Value(DeepgramListenProviderV2 value) { + this.value = value; + } + + @java.lang.Override + public T visit(Visitor visitor) { + return visitor.visitV2(value); + } + + @java.lang.Override + public boolean equals(Object other) { + if (this == other) return true; + return other instanceof V2Value && equalTo((V2Value) other); + } + + private boolean equalTo(V2Value other) { + return value.equals(other.value); + } + + @java.lang.Override + public int hashCode() { + return Objects.hash(this.value); + } + + @java.lang.Override + public String toString() { + return "AgentV1UpdateListenListenProvider{" + "value: " + value + "}"; + } + } + + @JsonIgnoreProperties("version") + private static final class _UnknownValue implements Value { + private String type; + + @JsonValue + private Object value; + + @JsonCreator(mode = JsonCreator.Mode.PROPERTIES) + private _UnknownValue(@JsonProperty("value") Object value) {} + + @java.lang.Override + public T visit(Visitor visitor) { + return visitor._visitUnknown(value); + } + + @java.lang.Override + public boolean equals(Object other) { + if (this == other) return true; + return other instanceof _UnknownValue && equalTo((_UnknownValue) other); + } + + private boolean equalTo(_UnknownValue other) { + return type.equals(other.type) && value.equals(other.value); + } + + @java.lang.Override + public int hashCode() { + return Objects.hash(this.type, this.value); + } + + @java.lang.Override + public String toString() { + return "AgentV1UpdateListenListenProvider{" + "type: " + type + ", value: " + value + "}"; + } + } +} diff --git a/src/main/java/com/deepgram/resources/agent/v1/types/AgentV1UserStartedSpeaking.java.bak b/src/main/java/com/deepgram/resources/agent/v1/types/AgentV1UserStartedSpeaking.java.bak new file mode 100644 index 0000000..3556d31 --- /dev/null +++ b/src/main/java/com/deepgram/resources/agent/v1/types/AgentV1UserStartedSpeaking.java.bak @@ -0,0 +1,83 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ +package com.deepgram.resources.agent.v1.types; + +import com.deepgram.core.ObjectMappers; +import com.fasterxml.jackson.annotation.JsonAnyGetter; +import com.fasterxml.jackson.annotation.JsonAnySetter; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import java.util.HashMap; +import java.util.Map; + +@JsonInclude(JsonInclude.Include.NON_ABSENT) +@JsonDeserialize(builder = AgentV1UserStartedSpeaking.Builder.class) +public final class AgentV1UserStartedSpeaking { + private final Map additionalProperties; + + private AgentV1UserStartedSpeaking(Map additionalProperties) { + this.additionalProperties = additionalProperties; + } + + /** + * @return Message type identifier indicating that the user has begun speaking + */ + @JsonProperty("type") + public String getType() { + return "UserStartedSpeaking"; + } + + @java.lang.Override + public boolean equals(Object other) { + if (this == other) return true; + return other instanceof AgentV1UserStartedSpeaking; + } + + @java.lang.Override + public int hashCode() { + return getClass().hashCode(); + } + + @JsonAnyGetter + public Map getAdditionalProperties() { + return this.additionalProperties; + } + + @java.lang.Override + public String toString() { + return ObjectMappers.stringify(this); + } + + public static Builder builder() { + return new Builder(); + } + + @JsonIgnoreProperties(ignoreUnknown = true) + public static final class Builder { + @JsonAnySetter + private Map additionalProperties = new HashMap<>(); + + private Builder() {} + + public Builder from(AgentV1UserStartedSpeaking other) { + return this; + } + + public AgentV1UserStartedSpeaking build() { + return new AgentV1UserStartedSpeaking(additionalProperties); + } + + public Builder additionalProperty(String key, Object value) { + this.additionalProperties.put(key, value); + return this; + } + + public Builder additionalProperties(Map additionalProperties) { + this.additionalProperties.putAll(additionalProperties); + return this; + } + } +} diff --git a/src/main/java/com/deepgram/resources/listen/v1/websocket/V1WebSocketClient.java.bak b/src/main/java/com/deepgram/resources/listen/v1/websocket/V1WebSocketClient.java.bak new file mode 100644 index 0000000..278a2c9 --- /dev/null +++ b/src/main/java/com/deepgram/resources/listen/v1/websocket/V1WebSocketClient.java.bak @@ -0,0 +1,554 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ +package com.deepgram.resources.listen.v1.websocket; + +import com.deepgram.core.ClientOptions; +import com.deepgram.core.DisconnectReason; +import com.deepgram.core.ObjectMappers; +import com.deepgram.core.QueryStringMapper; +import com.deepgram.core.ReconnectingWebSocketListener; +import com.deepgram.core.RequestOptions; +import com.deepgram.core.WebSocketReadyState; +import com.deepgram.resources.listen.v1.types.ListenV1CloseStream; +import com.deepgram.resources.listen.v1.types.ListenV1Finalize; +import com.deepgram.resources.listen.v1.types.ListenV1KeepAlive; +import com.deepgram.resources.listen.v1.types.ListenV1Metadata; +import com.deepgram.resources.listen.v1.types.ListenV1Results; +import com.deepgram.resources.listen.v1.types.ListenV1SpeechStarted; +import com.deepgram.resources.listen.v1.types.ListenV1UtteranceEnd; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ScheduledExecutorService; +import java.util.function.Consumer; +import okhttp3.HttpUrl; +import okhttp3.OkHttpClient; +import okhttp3.Request; +import okhttp3.Response; +import okhttp3.WebSocket; +import okio.ByteString; + +/** + * WebSocket client for the v1 channel. + * Provides real-time bidirectional communication with strongly-typed messages. + */ +public class V1WebSocketClient implements AutoCloseable { + protected final ClientOptions clientOptions; + + private final ObjectMapper objectMapper; + + private final OkHttpClient okHttpClient; + + private ScheduledExecutorService timeoutExecutor; + + private volatile WebSocketReadyState readyState = WebSocketReadyState.CLOSED; + + private volatile Runnable onConnectedHandler; + + private volatile Consumer onDisconnectedHandler; + + private volatile Consumer onErrorHandler; + + private volatile Consumer onMessageHandler; + + private volatile ReconnectingWebSocketListener.ReconnectOptions reconnectOptions; + + private CompletableFuture connectionFuture; + + private ReconnectingWebSocketListener reconnectingListener; + + private volatile Consumer resultsHandler; + + private volatile Consumer metadataHandler; + + private volatile Consumer utteranceEndHandler; + + private volatile Consumer speechStartedHandler; + + /** + * Creates a new async WebSocket client for the v1 channel. + */ + public V1WebSocketClient(ClientOptions clientOptions) { + this.clientOptions = clientOptions; + this.objectMapper = ObjectMappers.JSON_MAPPER; + this.okHttpClient = clientOptions.httpClient(); + } + + /** + * Establishes the WebSocket connection asynchronously with automatic reconnection. + * @return a CompletableFuture that completes when the connection is established + * @param options connection options including query parameters + */ + public CompletableFuture connect(V1ConnectOptions options) { + connectionFuture = new CompletableFuture<>(); + String baseUrl = clientOptions.environment().getProductionURL(); + String fullPath = "/v1/listen"; + if (baseUrl.endsWith("/") && fullPath.startsWith("/")) { + fullPath = fullPath.substring(1); + } else if (!baseUrl.endsWith("/") && !fullPath.startsWith("/")) { + fullPath = "/" + fullPath; + } + // OkHttp's HttpUrl only supports http/https schemes; convert wss/ws for URL parsing + if (baseUrl.startsWith("wss://")) { + baseUrl = "https://" + baseUrl.substring(6); + } else if (baseUrl.startsWith("ws://")) { + baseUrl = "http://" + baseUrl.substring(5); + } + HttpUrl parsedUrl = HttpUrl.parse(baseUrl + fullPath); + if (parsedUrl == null) { + throw new IllegalArgumentException("Invalid WebSocket URL: " + baseUrl + fullPath); + } + HttpUrl.Builder urlBuilder = parsedUrl.newBuilder(); + if (options.getCallback() != null && options.getCallback().isPresent()) { + urlBuilder.addQueryParameter( + "callback", String.valueOf(options.getCallback().get())); + } + if (options.getCallbackMethod() != null && options.getCallbackMethod().isPresent()) { + urlBuilder.addQueryParameter( + "callback_method", + String.valueOf(options.getCallbackMethod().get())); + } + if (options.getChannels() != null && options.getChannels().isPresent()) { + urlBuilder.addQueryParameter( + "channels", String.valueOf(options.getChannels().get())); + } + if (options.getDetectEntities() != null && options.getDetectEntities().isPresent()) { + urlBuilder.addQueryParameter( + "detect_entities", + String.valueOf(options.getDetectEntities().get())); + } + if (options.getDiarize() != null && options.getDiarize().isPresent()) { + urlBuilder.addQueryParameter( + "diarize", String.valueOf(options.getDiarize().get())); + } + if (options.getDiarizeModel() != null && options.getDiarizeModel().isPresent()) { + urlBuilder.addQueryParameter( + "diarize_model", String.valueOf(options.getDiarizeModel().get())); + } + if (options.getDictation() != null && options.getDictation().isPresent()) { + urlBuilder.addQueryParameter( + "dictation", String.valueOf(options.getDictation().get())); + } + if (options.getEncoding() != null && options.getEncoding().isPresent()) { + urlBuilder.addQueryParameter( + "encoding", String.valueOf(options.getEncoding().get())); + } + if (options.getEndpointing() != null && options.getEndpointing().isPresent()) { + urlBuilder.addQueryParameter( + "endpointing", String.valueOf(options.getEndpointing().get())); + } + if (options.getExtra() != null && options.getExtra().isPresent()) { + QueryStringMapper.addQueryParameter(urlBuilder, "extra", options.getExtra().get().get(), true); + } + if (options.getInterimResults() != null && options.getInterimResults().isPresent()) { + urlBuilder.addQueryParameter( + "interim_results", + String.valueOf(options.getInterimResults().get())); + } + if (options.getKeyterm() != null && options.getKeyterm().isPresent()) { + QueryStringMapper.addQueryParameter(urlBuilder, "keyterm", options.getKeyterm().get().get(), true); + } + if (options.getKeywords() != null && options.getKeywords().isPresent()) { + QueryStringMapper.addQueryParameter(urlBuilder, "keywords", options.getKeywords().get().get(), true); + } + if (options.getLanguage() != null && options.getLanguage().isPresent()) { + urlBuilder.addQueryParameter( + "language", String.valueOf(options.getLanguage().get())); + } + if (options.getMipOptOut() != null && options.getMipOptOut().isPresent()) { + urlBuilder.addQueryParameter( + "mip_opt_out", String.valueOf(options.getMipOptOut().get())); + } + urlBuilder.addQueryParameter("model", String.valueOf(options.getModel())); + if (options.getMultichannel() != null && options.getMultichannel().isPresent()) { + urlBuilder.addQueryParameter( + "multichannel", String.valueOf(options.getMultichannel().get())); + } + if (options.getNumerals() != null && options.getNumerals().isPresent()) { + urlBuilder.addQueryParameter( + "numerals", String.valueOf(options.getNumerals().get())); + } + if (options.getProfanityFilter() != null && options.getProfanityFilter().isPresent()) { + urlBuilder.addQueryParameter( + "profanity_filter", + String.valueOf(options.getProfanityFilter().get())); + } + if (options.getPunctuate() != null && options.getPunctuate().isPresent()) { + urlBuilder.addQueryParameter( + "punctuate", String.valueOf(options.getPunctuate().get())); + } + if (options.getRedact() != null && options.getRedact().isPresent()) { + urlBuilder.addQueryParameter( + "redact", String.valueOf(options.getRedact().get())); + } + if (options.getReplace() != null && options.getReplace().isPresent()) { + QueryStringMapper.addQueryParameter(urlBuilder, "replace", options.getReplace().get().get(), true); + } + if (options.getSampleRate() != null && options.getSampleRate().isPresent()) { + urlBuilder.addQueryParameter( + "sample_rate", String.valueOf(options.getSampleRate().get())); + } + if (options.getSearch() != null && options.getSearch().isPresent()) { + QueryStringMapper.addQueryParameter(urlBuilder, "search", options.getSearch().get().get(), true); + } + if (options.getSmartFormat() != null && options.getSmartFormat().isPresent()) { + urlBuilder.addQueryParameter( + "smart_format", String.valueOf(options.getSmartFormat().get())); + } + if (options.getTag() != null && options.getTag().isPresent()) { + QueryStringMapper.addQueryParameter(urlBuilder, "tag", options.getTag().get().get(), true); + } + if (options.getUtteranceEndMs() != null && options.getUtteranceEndMs().isPresent()) { + urlBuilder.addQueryParameter( + "utterance_end_ms", + String.valueOf(options.getUtteranceEndMs().get())); + } + if (options.getVadEvents() != null && options.getVadEvents().isPresent()) { + urlBuilder.addQueryParameter( + "vad_events", String.valueOf(options.getVadEvents().get())); + } + if (options.getVersion() != null && options.getVersion().isPresent()) { + urlBuilder.addQueryParameter( + "version", String.valueOf(options.getVersion().get())); + } + if (options.getAdditionalProperties() != null) { + options.getAdditionalProperties().forEach((key, value) -> { + if (value != null) { + QueryStringMapper.addQueryParameter(urlBuilder, key, value, true); + } + }); + } + Request.Builder requestBuilder = new Request.Builder().url(urlBuilder.build()); + clientOptions.headers((RequestOptions) null).forEach(requestBuilder::addHeader); + final Request request = requestBuilder.build(); + this.readyState = WebSocketReadyState.CONNECTING; + ReconnectingWebSocketListener.ReconnectOptions reconnectOpts = this.reconnectOptions != null + ? this.reconnectOptions + : ReconnectingWebSocketListener.ReconnectOptions.builder().build(); + this.reconnectingListener = + new ReconnectingWebSocketListener( + reconnectOpts, + () -> { + if (clientOptions.isClosed()) { + throw new IllegalStateException("root client has been closed"); + } + if (clientOptions.webSocketFactory().isPresent()) { + return clientOptions + .webSocketFactory() + .get() + .create(request, this.reconnectingListener); + } else { + return okHttpClient.newWebSocket(request, this.reconnectingListener); + } + }, + clientOptions::isClosed) { + @Override + protected void onWebSocketOpen(WebSocket webSocket, Response response) { + readyState = WebSocketReadyState.OPEN; + if (onConnectedHandler != null) { + onConnectedHandler.run(); + } + connectionFuture.complete(null); + } + + @Override + protected void onWebSocketMessage(WebSocket webSocket, String text) { + handleIncomingMessage(text); + } + + @Override + protected void onWebSocketBinaryMessage(WebSocket webSocket, ByteString bytes) {} + + @Override + protected void onWebSocketFailure(WebSocket webSocket, Throwable t, Response response) { + readyState = WebSocketReadyState.CLOSED; + if (onErrorHandler != null) { + onErrorHandler.accept(new RuntimeException(t)); + } + connectionFuture.completeExceptionally(t); + } + + @Override + protected void onWebSocketClosed(WebSocket webSocket, int code, String reason) { + readyState = WebSocketReadyState.CLOSED; + if (onDisconnectedHandler != null) { + onDisconnectedHandler.accept(new DisconnectReason(code, reason)); + } + } + }; + clientOptions.registerWebSocket(this); + reconnectingListener.connect(); + return connectionFuture; + } + + /** + * Disconnects the WebSocket connection and releases resources. + */ + public void disconnect() { + clientOptions.unregisterWebSocket(this); + if (reconnectingListener != null) { + reconnectingListener.disconnect(); + } + if (timeoutExecutor != null) { + timeoutExecutor.shutdownNow(); + timeoutExecutor = null; + } + } + + /** + * Gets the current state of the WebSocket connection. + * + * This provides the actual connection state, similar to the W3C WebSocket API. + * + * @return the current WebSocket ready state + */ + public WebSocketReadyState getReadyState() { + return readyState; + } + + /** + * Sends a ListenV1Media message to the server asynchronously. + * @param message the message to send + * @return a CompletableFuture that completes when the message is sent + */ + public CompletableFuture sendMedia(ByteString message) { + CompletableFuture future = new CompletableFuture<>(); + try { + assertSocketIsOpen(); + // Use reconnecting listener's sendBinary method which handles queuing + reconnectingListener.sendBinary(message); + future.complete(null); + } catch (Exception e) { + future.completeExceptionally(new RuntimeException("Failed to send binary data", e)); + } + return future; + } + + /** + * Sends a ListenV1Finalize message to the server asynchronously. + * @param message the message to send + * @return a CompletableFuture that completes when the message is sent + */ + public CompletableFuture sendFinalize(ListenV1Finalize message) { + return sendMessage(message); + } + + /** + * Sends a ListenV1CloseStream message to the server asynchronously. + * @param message the message to send + * @return a CompletableFuture that completes when the message is sent + */ + public CompletableFuture sendCloseStream(ListenV1CloseStream message) { + return sendMessage(message); + } + + /** + * Sends a ListenV1KeepAlive message to the server asynchronously. + * @param message the message to send + * @return a CompletableFuture that completes when the message is sent + */ + public CompletableFuture sendKeepAlive(ListenV1KeepAlive message) { + return sendMessage(message); + } + + /** + * Registers a handler for ListenV1Results messages from the server. + * @param handler the handler to invoke when a message is received + */ + public void onResults(Consumer handler) { + this.resultsHandler = handler; + } + + /** + * Registers a handler for ListenV1Metadata messages from the server. + * @param handler the handler to invoke when a message is received + */ + public void onMetadata(Consumer handler) { + this.metadataHandler = handler; + } + + /** + * Registers a handler for ListenV1UtteranceEnd messages from the server. + * @param handler the handler to invoke when a message is received + */ + public void onUtteranceEnd(Consumer handler) { + this.utteranceEndHandler = handler; + } + + /** + * Registers a handler for ListenV1SpeechStarted messages from the server. + * @param handler the handler to invoke when a message is received + */ + public void onSpeechStarted(Consumer handler) { + this.speechStartedHandler = handler; + } + + /** + * Registers a handler called when the connection is established. + * @param handler the handler to invoke when connected + */ + public void onConnected(Runnable handler) { + this.onConnectedHandler = handler; + } + + /** + * Registers a handler called when the connection is closed. + * @param handler the handler to invoke when disconnected + */ + public void onDisconnected(Consumer handler) { + this.onDisconnectedHandler = handler; + } + + /** + * Registers a handler called when an error occurs. + * @param handler the handler to invoke on error + */ + public void onError(Consumer handler) { + this.onErrorHandler = handler; + } + + /** + * Registers a handler called for every incoming text message. + * The handler receives the raw JSON string before type-specific dispatch. + * @param handler the handler to invoke with the raw message JSON + */ + public void onMessage(Consumer handler) { + this.onMessageHandler = handler; + } + + /** + * Configures reconnection behavior. Must be called before {@link #connect}. + * + * @param options the reconnection options (backoff, retries, queue size) + */ + public void reconnectOptions(ReconnectingWebSocketListener.ReconnectOptions options) { + this.reconnectOptions = options; + } + + /** + * Closes this WebSocket client, releasing all resources. + * Equivalent to calling {@link #disconnect()}. + */ + @Override + public void close() { + disconnect(); + } + + /** + * Ensures the WebSocket is connected and ready to send messages. + * @throws IllegalStateException if the socket is not connected or not open + */ + private void assertSocketIsOpen() { + if (reconnectingListener.getWebSocket() == null) { + throw new IllegalStateException("WebSocket is not connected. Call connect() first."); + } + if (readyState != WebSocketReadyState.OPEN) { + throw new IllegalStateException("WebSocket is not open. Current state: " + readyState); + } + } + + private CompletableFuture sendMessage(Object body) { + CompletableFuture future = new CompletableFuture<>(); + try { + assertSocketIsOpen(); + String json = objectMapper.writeValueAsString(body); + // Use reconnecting listener's send method which handles queuing + reconnectingListener.send(json); + future.complete(null); + } catch (IllegalStateException e) { + future.completeExceptionally(e); + } catch (Exception e) { + future.completeExceptionally(new RuntimeException("Failed to send message", e)); + } + return future; + } + + private void handleIncomingMessage(String json) { + try { + if (onMessageHandler != null) { + onMessageHandler.accept(json); + } + JsonNode node = objectMapper.readTree(json); + if (node == null || node.isNull()) { + throw new IllegalArgumentException("Received null or invalid JSON message"); + } + if (node.has("transaction_key") + && node.has("request_id") + && node.has("sha256") + && node.has("created") + && node.has("duration") + && node.has("channels") + && "Metadata".equals(node.path("type").asText())) { + ListenV1Metadata metadataHandlerEvent = null; + try { + metadataHandlerEvent = objectMapper.treeToValue(node, ListenV1Metadata.class); + } catch (Exception e) { + } + if (metadataHandlerEvent != null) { + if (metadataHandler != null) { + metadataHandler.accept(metadataHandlerEvent); + } + return; + } + } + if (node.has("channel_index") + && node.has("duration") + && node.has("start") + && node.has("channel") + && node.has("metadata") + && "Results".equals(node.path("type").asText())) { + ListenV1Results resultsHandlerEvent = null; + try { + resultsHandlerEvent = objectMapper.treeToValue(node, ListenV1Results.class); + } catch (Exception e) { + } + if (resultsHandlerEvent != null) { + if (resultsHandler != null) { + resultsHandler.accept(resultsHandlerEvent); + } + return; + } + } + if (node.has("channel") + && node.has("last_word_end") + && "UtteranceEnd".equals(node.path("type").asText())) { + ListenV1UtteranceEnd utteranceEndHandlerEvent = null; + try { + utteranceEndHandlerEvent = objectMapper.treeToValue(node, ListenV1UtteranceEnd.class); + } catch (Exception e) { + } + if (utteranceEndHandlerEvent != null) { + if (utteranceEndHandler != null) { + utteranceEndHandler.accept(utteranceEndHandlerEvent); + } + return; + } + } + if (node.has("channel") + && node.has("timestamp") + && "SpeechStarted".equals(node.path("type").asText())) { + ListenV1SpeechStarted speechStartedHandlerEvent = null; + try { + speechStartedHandlerEvent = objectMapper.treeToValue(node, ListenV1SpeechStarted.class); + } catch (Exception e) { + } + if (speechStartedHandlerEvent != null) { + if (speechStartedHandler != null) { + speechStartedHandler.accept(speechStartedHandlerEvent); + } + return; + } + } + if (onErrorHandler != null) { + onErrorHandler.accept(new RuntimeException( + "Unrecognized WebSocket message: " + json.substring(0, Math.min(200, json.length())) + + "... Update your SDK version to support new message types.")); + } + } catch (Exception e) { + if (onErrorHandler != null) { + onErrorHandler.accept(e); + } + } + } +} diff --git a/src/main/java/com/deepgram/resources/listen/v2/types/ListenV2CloseStream.java.bak b/src/main/java/com/deepgram/resources/listen/v2/types/ListenV2CloseStream.java.bak new file mode 100644 index 0000000..c9f2f35 --- /dev/null +++ b/src/main/java/com/deepgram/resources/listen/v2/types/ListenV2CloseStream.java.bak @@ -0,0 +1,83 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ +package com.deepgram.resources.listen.v2.types; + +import com.deepgram.core.ObjectMappers; +import com.fasterxml.jackson.annotation.JsonAnyGetter; +import com.fasterxml.jackson.annotation.JsonAnySetter; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import java.util.HashMap; +import java.util.Map; + +@JsonInclude(JsonInclude.Include.NON_ABSENT) +@JsonDeserialize(builder = ListenV2CloseStream.Builder.class) +public final class ListenV2CloseStream { + private final Map additionalProperties; + + private ListenV2CloseStream(Map additionalProperties) { + this.additionalProperties = additionalProperties; + } + + /** + * @return Message type identifier + */ + @JsonProperty("type") + public String getType() { + return "CloseStream"; + } + + @java.lang.Override + public boolean equals(Object other) { + if (this == other) return true; + return other instanceof ListenV2CloseStream; + } + + @java.lang.Override + public int hashCode() { + return getClass().hashCode(); + } + + @JsonAnyGetter + public Map getAdditionalProperties() { + return this.additionalProperties; + } + + @java.lang.Override + public String toString() { + return ObjectMappers.stringify(this); + } + + public static Builder builder() { + return new Builder(); + } + + @JsonIgnoreProperties(ignoreUnknown = true) + public static final class Builder { + @JsonAnySetter + private Map additionalProperties = new HashMap<>(); + + private Builder() {} + + public Builder from(ListenV2CloseStream other) { + return this; + } + + public ListenV2CloseStream build() { + return new ListenV2CloseStream(additionalProperties); + } + + public Builder additionalProperty(String key, Object value) { + this.additionalProperties.put(key, value); + return this; + } + + public Builder additionalProperties(Map additionalProperties) { + this.additionalProperties.putAll(additionalProperties); + return this; + } + } +} diff --git a/src/main/java/com/deepgram/resources/listen/v2/types/ListenV2ForceEndTurn.java.bak b/src/main/java/com/deepgram/resources/listen/v2/types/ListenV2ForceEndTurn.java.bak new file mode 100644 index 0000000..894bd3c --- /dev/null +++ b/src/main/java/com/deepgram/resources/listen/v2/types/ListenV2ForceEndTurn.java.bak @@ -0,0 +1,83 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ +package com.deepgram.resources.listen.v2.types; + +import com.deepgram.core.ObjectMappers; +import com.fasterxml.jackson.annotation.JsonAnyGetter; +import com.fasterxml.jackson.annotation.JsonAnySetter; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import java.util.HashMap; +import java.util.Map; + +@JsonInclude(JsonInclude.Include.NON_ABSENT) +@JsonDeserialize(builder = ListenV2ForceEndTurn.Builder.class) +public final class ListenV2ForceEndTurn { + private final Map additionalProperties; + + private ListenV2ForceEndTurn(Map additionalProperties) { + this.additionalProperties = additionalProperties; + } + + /** + * @return Message type identifier + */ + @JsonProperty("type") + public String getType() { + return "ForceEndTurn"; + } + + @java.lang.Override + public boolean equals(Object other) { + if (this == other) return true; + return other instanceof ListenV2ForceEndTurn; + } + + @java.lang.Override + public int hashCode() { + return getClass().hashCode(); + } + + @JsonAnyGetter + public Map getAdditionalProperties() { + return this.additionalProperties; + } + + @java.lang.Override + public String toString() { + return ObjectMappers.stringify(this); + } + + public static Builder builder() { + return new Builder(); + } + + @JsonIgnoreProperties(ignoreUnknown = true) + public static final class Builder { + @JsonAnySetter + private Map additionalProperties = new HashMap<>(); + + private Builder() {} + + public Builder from(ListenV2ForceEndTurn other) { + return this; + } + + public ListenV2ForceEndTurn build() { + return new ListenV2ForceEndTurn(additionalProperties); + } + + public Builder additionalProperty(String key, Object value) { + this.additionalProperties.put(key, value); + return this; + } + + public Builder additionalProperties(Map additionalProperties) { + this.additionalProperties.putAll(additionalProperties); + return this; + } + } +} diff --git a/src/main/java/com/deepgram/resources/listen/v2/websocket/V2WebSocketClient.java.bak b/src/main/java/com/deepgram/resources/listen/v2/websocket/V2WebSocketClient.java.bak new file mode 100644 index 0000000..5f55fea --- /dev/null +++ b/src/main/java/com/deepgram/resources/listen/v2/websocket/V2WebSocketClient.java.bak @@ -0,0 +1,563 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ +package com.deepgram.resources.listen.v2.websocket; + +import com.deepgram.core.ClientOptions; +import com.deepgram.core.DisconnectReason; +import com.deepgram.core.ObjectMappers; +import com.deepgram.core.QueryStringMapper; +import com.deepgram.core.ReconnectingWebSocketListener; +import com.deepgram.core.RequestOptions; +import com.deepgram.core.WebSocketReadyState; +import com.deepgram.resources.listen.v2.types.ListenV2CloseStream; +import com.deepgram.resources.listen.v2.types.ListenV2Configure; +import com.deepgram.resources.listen.v2.types.ListenV2ConfigureFailure; +import com.deepgram.resources.listen.v2.types.ListenV2ConfigureSuccess; +import com.deepgram.resources.listen.v2.types.ListenV2Connected; +import com.deepgram.resources.listen.v2.types.ListenV2FatalError; +import com.deepgram.resources.listen.v2.types.ListenV2ForceEndTurn; +import com.deepgram.resources.listen.v2.types.ListenV2TurnInfo; +import com.deepgram.resources.listen.v2.types.ListenV2Warning; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.atomic.AtomicReference; +import java.util.function.Consumer; +import okhttp3.HttpUrl; +import okhttp3.OkHttpClient; +import okhttp3.Request; +import okhttp3.Response; +import okhttp3.WebSocket; +import okio.ByteString; + +/** + * WebSocket client for the v2 channel. + * Provides real-time bidirectional communication with strongly-typed messages. + */ +public class V2WebSocketClient implements AutoCloseable { + protected final ClientOptions clientOptions; + + private final ObjectMapper objectMapper; + + private final OkHttpClient okHttpClient; + + private ScheduledExecutorService timeoutExecutor; + + private volatile WebSocketReadyState readyState = WebSocketReadyState.CLOSED; + + private volatile Runnable onConnectedHandler; + + private volatile Consumer onDisconnectedHandler; + + private volatile Consumer onErrorHandler; + + private volatile Consumer onMessageHandler; + + private volatile ReconnectingWebSocketListener.ReconnectOptions reconnectOptions; + + private CompletableFuture connectionFuture; + + private ReconnectingWebSocketListener reconnectingListener; + + private final AtomicReference closeStreamSocket = new AtomicReference<>(); + + private volatile Consumer connectedHandler; + + private volatile Consumer turnInfoHandler; + + private volatile Consumer configureSuccessHandler; + + private volatile Consumer configureFailureHandler; + + private volatile Consumer warningHandler; + + private volatile Consumer errorHandler; + + /** + * Creates a new async WebSocket client for the v2 channel. + */ + public V2WebSocketClient(ClientOptions clientOptions) { + this.clientOptions = clientOptions; + this.objectMapper = ObjectMappers.JSON_MAPPER; + this.okHttpClient = clientOptions.httpClient(); + } + + /** + * Establishes the WebSocket connection asynchronously with automatic reconnection. + * @return a CompletableFuture that completes when the connection is established + * @param options connection options including query parameters + */ + public CompletableFuture connect(V2ConnectOptions options) { + connectionFuture = new CompletableFuture<>(); + closeStreamSocket.set(null); + String baseUrl = clientOptions.environment().getProductionURL(); + String fullPath = "/v2/listen"; + if (baseUrl.endsWith("/") && fullPath.startsWith("/")) { + fullPath = fullPath.substring(1); + } else if (!baseUrl.endsWith("/") && !fullPath.startsWith("/")) { + fullPath = "/" + fullPath; + } + // OkHttp's HttpUrl only supports http/https schemes; convert wss/ws for URL parsing + if (baseUrl.startsWith("wss://")) { + baseUrl = "https://" + baseUrl.substring(6); + } else if (baseUrl.startsWith("ws://")) { + baseUrl = "http://" + baseUrl.substring(5); + } + HttpUrl parsedUrl = HttpUrl.parse(baseUrl + fullPath); + if (parsedUrl == null) { + throw new IllegalArgumentException("Invalid WebSocket URL: " + baseUrl + fullPath); + } + HttpUrl.Builder urlBuilder = parsedUrl.newBuilder(); + urlBuilder.addQueryParameter("model", String.valueOf(options.getModel())); + if (options.getEncoding() != null && options.getEncoding().isPresent()) { + urlBuilder.addQueryParameter( + "encoding", String.valueOf(options.getEncoding().get())); + } + if (options.getSampleRate() != null && options.getSampleRate().isPresent()) { + urlBuilder.addQueryParameter( + "sample_rate", String.valueOf(options.getSampleRate().get())); + } + if (options.getEagerEotThreshold() != null + && options.getEagerEotThreshold().isPresent()) { + urlBuilder.addQueryParameter( + "eager_eot_threshold", + String.valueOf(options.getEagerEotThreshold().get())); + } + if (options.getEotThreshold() != null && options.getEotThreshold().isPresent()) { + urlBuilder.addQueryParameter( + "eot_threshold", String.valueOf(options.getEotThreshold().get())); + } + if (options.getEotTimeoutMs() != null && options.getEotTimeoutMs().isPresent()) { + urlBuilder.addQueryParameter( + "eot_timeout_ms", String.valueOf(options.getEotTimeoutMs().get())); + } + if (options.getKeyterm() != null && options.getKeyterm().isPresent()) { + QueryStringMapper.addQueryParameter( + urlBuilder, "keyterm", options.getKeyterm().get().get(), true); + } + if (options.getLanguageHint() != null && options.getLanguageHint().isPresent()) { + QueryStringMapper.addQueryParameter( + urlBuilder, "language_hint", options.getLanguageHint().get().get(), true); + } + if (options.getProfanityFilter() != null && options.getProfanityFilter().isPresent()) { + urlBuilder.addQueryParameter( + "profanity_filter", + String.valueOf(options.getProfanityFilter().get())); + } + if (options.getNumerals() != null && options.getNumerals().isPresent()) { + urlBuilder.addQueryParameter( + "numerals", String.valueOf(options.getNumerals().get())); + } + if (options.getRedact() != null && options.getRedact().isPresent()) { + urlBuilder.addQueryParameter( + "redact", String.valueOf(options.getRedact().get())); + } + if (options.getMipOptOut() != null && options.getMipOptOut().isPresent()) { + urlBuilder.addQueryParameter( + "mip_opt_out", String.valueOf(options.getMipOptOut().get())); + } + if (options.getTag() != null && options.getTag().isPresent()) { + QueryStringMapper.addQueryParameter(urlBuilder, "tag", options.getTag().get().get(), true); + } + if (options.getAdditionalProperties() != null) { + options.getAdditionalProperties().forEach((key, value) -> { + if (value != null) { + QueryStringMapper.addQueryParameter(urlBuilder, key, value, true); + } + }); + } + Request.Builder requestBuilder = new Request.Builder().url(urlBuilder.build()); + clientOptions.headers((RequestOptions) null).forEach(requestBuilder::addHeader); + final Request request = requestBuilder.build(); + this.readyState = WebSocketReadyState.CONNECTING; + ReconnectingWebSocketListener.ReconnectOptions reconnectOpts = this.reconnectOptions != null + ? this.reconnectOptions + : ReconnectingWebSocketListener.ReconnectOptions.builder().build(); + this.reconnectingListener = + new ReconnectingWebSocketListener( + reconnectOpts, + () -> { + if (clientOptions.isClosed()) { + throw new IllegalStateException("root client has been closed"); + } + if (clientOptions.webSocketFactory().isPresent()) { + return clientOptions + .webSocketFactory() + .get() + .create(request, this.reconnectingListener); + } else { + return okHttpClient.newWebSocket(request, this.reconnectingListener); + } + }, + clientOptions::isClosed) { + @Override + protected void onWebSocketOpen(WebSocket webSocket, Response response) { + closeStreamSocket.set(null); + readyState = WebSocketReadyState.OPEN; + if (onConnectedHandler != null) { + onConnectedHandler.run(); + } + connectionFuture.complete(null); + } + + @Override + protected void onWebSocketMessage(WebSocket webSocket, String text) { + handleIncomingMessage(text); + } + + @Override + protected void onWebSocketBinaryMessage(WebSocket webSocket, ByteString bytes) {} + + @Override + protected void onWebSocketFailure(WebSocket webSocket, Throwable t, Response response) { + readyState = WebSocketReadyState.CLOSED; + if (onErrorHandler != null) { + onErrorHandler.accept(new RuntimeException(t)); + } + connectionFuture.completeExceptionally(t); + } + + @Override + protected void onWebSocketClosed(WebSocket webSocket, int code, String reason) { + readyState = WebSocketReadyState.CLOSED; + if (onDisconnectedHandler != null) { + onDisconnectedHandler.accept(new DisconnectReason(code, reason)); + } + } + + @Override + protected boolean shouldReconnectAfterClose(WebSocket webSocket, int code, String reason) { + return super.shouldReconnectAfterClose(webSocket, code, reason) + && (code != 1005 || closeStreamSocket.get() != webSocket); + } + }; + clientOptions.registerWebSocket(this); + reconnectingListener.connect(); + return connectionFuture; + } + + /** + * Disconnects the WebSocket connection and releases resources. + */ + public void disconnect() { + clientOptions.unregisterWebSocket(this); + if (reconnectingListener != null) { + reconnectingListener.disconnect(); + } + if (timeoutExecutor != null) { + timeoutExecutor.shutdownNow(); + timeoutExecutor = null; + } + } + + /** + * Gets the current state of the WebSocket connection. + * + * This provides the actual connection state, similar to the W3C WebSocket API. + * + * @return the current WebSocket ready state + */ + public WebSocketReadyState getReadyState() { + return readyState; + } + + /** + * Sends a ListenV2Media message to the server asynchronously. + * @param message the message to send + * @return a CompletableFuture that completes when the message is sent + */ + public CompletableFuture sendMedia(ByteString message) { + CompletableFuture future = new CompletableFuture<>(); + try { + assertSocketIsOpen(); + // Use reconnecting listener's sendBinary method which handles queuing + reconnectingListener.sendBinary(message); + future.complete(null); + } catch (Exception e) { + future.completeExceptionally(new RuntimeException("Failed to send binary data", e)); + } + return future; + } + + /** + * Sends a ListenV2CloseStream message to the server asynchronously. A queued CloseStream does + * not suppress reconnects because it might never reach the server. + * @param message the message to send + * @return a CompletableFuture that completes when the message is sent + */ + public CompletableFuture sendCloseStream(ListenV2CloseStream message) { + return sendMessage(message, closeStreamSocket::set); + } + + /** + * Sends a ListenV2ForceEndTurn message to the server asynchronously. + * @param message the message to send + * @return a CompletableFuture that completes when the message is sent + */ + public CompletableFuture sendForceEndTurn(ListenV2ForceEndTurn message) { + return sendMessage(message); + } + + /** + * Sends a ListenV2Configure message to the server asynchronously. + * @param message the message to send + * @return a CompletableFuture that completes when the message is sent + */ + public CompletableFuture sendConfigure(ListenV2Configure message) { + return sendMessage(message); + } + + /** + * Registers a handler for ListenV2Connected messages from the server. + * @param handler the handler to invoke when a message is received + */ + public void onConnected(Consumer handler) { + this.connectedHandler = handler; + } + + /** + * Registers a handler for ListenV2TurnInfo messages from the server. + * @param handler the handler to invoke when a message is received + */ + public void onTurnInfo(Consumer handler) { + this.turnInfoHandler = handler; + } + + /** + * Registers a handler for ListenV2ConfigureSuccess messages from the server. + * @param handler the handler to invoke when a message is received + */ + public void onConfigureSuccess(Consumer handler) { + this.configureSuccessHandler = handler; + } + + /** + * Registers a handler for ListenV2ConfigureFailure messages from the server. + * @param handler the handler to invoke when a message is received + */ + public void onConfigureFailure(Consumer handler) { + this.configureFailureHandler = handler; + } + + /** + * Registers a handler for ListenV2Warning messages from the server. + * @param handler the handler to invoke when a message is received + */ + public void onWarning(Consumer handler) { + this.warningHandler = handler; + } + + /** + * Registers a handler for ListenV2FatalError messages from the server. + * @param handler the handler to invoke when a message is received + */ + public void onErrorMessage(Consumer handler) { + this.errorHandler = handler; + } + + /** + * Registers a handler called when the connection is established. + * @param handler the handler to invoke when connected + */ + public void onConnected(Runnable handler) { + this.onConnectedHandler = handler; + } + + /** + * Registers a handler called when the connection is closed. + * @param handler the handler to invoke when disconnected + */ + public void onDisconnected(Consumer handler) { + this.onDisconnectedHandler = handler; + } + + /** + * Registers a handler called when an error occurs. + * @param handler the handler to invoke on error + */ + public void onError(Consumer handler) { + this.onErrorHandler = handler; + } + + /** + * Registers a handler called for every incoming text message. + * The handler receives the raw JSON string before type-specific dispatch. + * @param handler the handler to invoke with the raw message JSON + */ + public void onMessage(Consumer handler) { + this.onMessageHandler = handler; + } + + /** + * Configures reconnection behavior. Must be called before {@link #connect}. + * + * @param options the reconnection options (backoff, retries, queue size) + */ + public void reconnectOptions(ReconnectingWebSocketListener.ReconnectOptions options) { + this.reconnectOptions = options; + } + + /** + * Closes this WebSocket client, releasing all resources. + * Equivalent to calling {@link #disconnect()}. + */ + @Override + public void close() { + disconnect(); + } + + /** + * Ensures the WebSocket is connected and ready to send messages. + * @throws IllegalStateException if the socket is not connected or not open + */ + private void assertSocketIsOpen() { + if (reconnectingListener.getWebSocket() == null) { + throw new IllegalStateException("WebSocket is not connected. Call connect() first."); + } + if (readyState != WebSocketReadyState.OPEN) { + throw new IllegalStateException("WebSocket is not open. Current state: " + readyState); + } + } + + private CompletableFuture sendMessage(Object body) { + return sendMessage(body, null); + } + + private CompletableFuture sendMessage(Object body, Consumer onSent) { + CompletableFuture future = new CompletableFuture<>(); + try { + assertSocketIsOpen(); + String json = objectMapper.writeValueAsString(body); + // Use reconnecting listener's send method which handles queuing + reconnectingListener.send(json, onSent); + future.complete(null); + } catch (IllegalStateException e) { + future.completeExceptionally(e); + } catch (Exception e) { + future.completeExceptionally(new RuntimeException("Failed to send message", e)); + } + return future; + } + + private void handleIncomingMessage(String json) { + try { + if (onMessageHandler != null) { + onMessageHandler.accept(json); + } + JsonNode node = objectMapper.readTree(json); + if (node == null || node.isNull()) { + throw new IllegalArgumentException("Received null or invalid JSON message"); + } + if (node.has("request_id") + && node.has("sequence_id") + && node.has("event") + && node.has("turn_index") + && node.has("audio_window_start") + && node.has("audio_window_end") + && node.has("transcript") + && node.has("words") + && node.has("end_of_turn_confidence") + && "TurnInfo".equals(node.path("type").asText())) { + ListenV2TurnInfo turnInfoHandlerEvent = null; + try { + turnInfoHandlerEvent = objectMapper.treeToValue(node, ListenV2TurnInfo.class); + } catch (Exception e) { + } + if (turnInfoHandlerEvent != null) { + if (turnInfoHandler != null) { + turnInfoHandler.accept(turnInfoHandlerEvent); + } + return; + } + } + if (node.has("request_id") + && node.has("thresholds") + && node.has("keyterms") + && node.has("sequence_id") + && "ConfigureSuccess".equals(node.path("type").asText())) { + ListenV2ConfigureSuccess configureSuccessHandlerEvent = null; + try { + configureSuccessHandlerEvent = objectMapper.treeToValue(node, ListenV2ConfigureSuccess.class); + } catch (Exception e) { + } + if (configureSuccessHandlerEvent != null) { + if (configureSuccessHandler != null) { + configureSuccessHandler.accept(configureSuccessHandlerEvent); + } + return; + } + } + if (node.has("request_id") + && node.has("sequence_id") + && node.has("code") + && node.has("description") + && "Warning".equals(node.path("type").asText())) { + ListenV2Warning warningHandlerEvent = null; + try { + warningHandlerEvent = objectMapper.treeToValue(node, ListenV2Warning.class); + } catch (Exception e) { + } + if (warningHandlerEvent != null) { + if (warningHandler != null) { + warningHandler.accept(warningHandlerEvent); + } + return; + } + } + if (node.has("sequence_id") + && node.has("code") + && node.has("description") + && "Error".equals(node.path("type").asText())) { + ListenV2FatalError errorHandlerEvent = null; + try { + errorHandlerEvent = objectMapper.treeToValue(node, ListenV2FatalError.class); + } catch (Exception e) { + } + if (errorHandlerEvent != null) { + if (errorHandler != null) { + errorHandler.accept(errorHandlerEvent); + } + return; + } + } + if (node.has("request_id") + && node.has("sequence_id") + && "Connected".equals(node.path("type").asText())) { + ListenV2Connected connectedHandlerEvent = null; + try { + connectedHandlerEvent = objectMapper.treeToValue(node, ListenV2Connected.class); + } catch (Exception e) { + } + if (connectedHandlerEvent != null) { + if (connectedHandler != null) { + connectedHandler.accept(connectedHandlerEvent); + } + return; + } + } + if (node.has("request_id") + && node.has("sequence_id") + && "ConfigureFailure".equals(node.path("type").asText())) { + ListenV2ConfigureFailure configureFailureHandlerEvent = null; + try { + configureFailureHandlerEvent = objectMapper.treeToValue(node, ListenV2ConfigureFailure.class); + } catch (Exception e) { + } + if (configureFailureHandlerEvent != null) { + if (configureFailureHandler != null) { + configureFailureHandler.accept(configureFailureHandlerEvent); + } + return; + } + } + // The raw frame is already delivered to onMessage(String), so ignore unknown typed + // frames for forward compatibility with newly added server control messages. + } catch (Exception e) { + if (onErrorHandler != null) { + onErrorHandler.accept(e); + } + } + } +} diff --git a/src/main/java/com/deepgram/resources/speak/v1/websocket/V1WebSocketClient.java.bak b/src/main/java/com/deepgram/resources/speak/v1/websocket/V1WebSocketClient.java.bak new file mode 100644 index 0000000..e6824a3 --- /dev/null +++ b/src/main/java/com/deepgram/resources/speak/v1/websocket/V1WebSocketClient.java.bak @@ -0,0 +1,467 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ +package com.deepgram.resources.speak.v1.websocket; + +import com.deepgram.core.ClientOptions; +import com.deepgram.core.DisconnectReason; +import com.deepgram.core.ObjectMappers; +import com.deepgram.core.QueryStringMapper; +import com.deepgram.core.ReconnectingWebSocketListener; +import com.deepgram.core.RequestOptions; +import com.deepgram.core.WebSocketReadyState; +import com.deepgram.resources.speak.v1.types.SpeakV1Clear; +import com.deepgram.resources.speak.v1.types.SpeakV1Cleared; +import com.deepgram.resources.speak.v1.types.SpeakV1Close; +import com.deepgram.resources.speak.v1.types.SpeakV1Flush; +import com.deepgram.resources.speak.v1.types.SpeakV1Flushed; +import com.deepgram.resources.speak.v1.types.SpeakV1Metadata; +import com.deepgram.resources.speak.v1.types.SpeakV1Text; +import com.deepgram.resources.speak.v1.types.SpeakV1Warning; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ScheduledExecutorService; +import java.util.function.Consumer; +import okhttp3.HttpUrl; +import okhttp3.OkHttpClient; +import okhttp3.Request; +import okhttp3.Response; +import okhttp3.WebSocket; +import okio.ByteString; + +/** + * WebSocket client for the v1 channel. + * Provides real-time bidirectional communication with strongly-typed messages. + */ +public class V1WebSocketClient implements AutoCloseable { + protected final ClientOptions clientOptions; + + private final ObjectMapper objectMapper; + + private final OkHttpClient okHttpClient; + + private ScheduledExecutorService timeoutExecutor; + + private volatile WebSocketReadyState readyState = WebSocketReadyState.CLOSED; + + private volatile Runnable onConnectedHandler; + + private volatile Consumer onDisconnectedHandler; + + private volatile Consumer onErrorHandler; + + private volatile Consumer onMessageHandler; + + private volatile ReconnectingWebSocketListener.ReconnectOptions reconnectOptions; + + private CompletableFuture connectionFuture; + + private ReconnectingWebSocketListener reconnectingListener; + + private volatile Consumer speakV1AudioHandler; + + private volatile Consumer metadataHandler; + + private volatile Consumer flushedHandler; + + private volatile Consumer clearedHandler; + + private volatile Consumer warningHandler; + + /** + * Creates a new async WebSocket client for the v1 channel. + */ + public V1WebSocketClient(ClientOptions clientOptions) { + this.clientOptions = clientOptions; + this.objectMapper = ObjectMappers.JSON_MAPPER; + this.okHttpClient = clientOptions.httpClient(); + } + + /** + * Establishes the WebSocket connection asynchronously with automatic reconnection. + * @return a CompletableFuture that completes when the connection is established + * @param options connection options including query parameters + */ + public CompletableFuture connect(V1ConnectOptions options) { + connectionFuture = new CompletableFuture<>(); + String baseUrl = clientOptions.environment().getProductionURL(); + String fullPath = "/v1/speak"; + if (baseUrl.endsWith("/") && fullPath.startsWith("/")) { + fullPath = fullPath.substring(1); + } else if (!baseUrl.endsWith("/") && !fullPath.startsWith("/")) { + fullPath = "/" + fullPath; + } + // OkHttp's HttpUrl only supports http/https schemes; convert wss/ws for URL parsing + if (baseUrl.startsWith("wss://")) { + baseUrl = "https://" + baseUrl.substring(6); + } else if (baseUrl.startsWith("ws://")) { + baseUrl = "http://" + baseUrl.substring(5); + } + HttpUrl parsedUrl = HttpUrl.parse(baseUrl + fullPath); + if (parsedUrl == null) { + throw new IllegalArgumentException("Invalid WebSocket URL: " + baseUrl + fullPath); + } + HttpUrl.Builder urlBuilder = parsedUrl.newBuilder(); + if (options.getEncoding() != null && options.getEncoding().isPresent()) { + urlBuilder.addQueryParameter( + "encoding", String.valueOf(options.getEncoding().get())); + } + if (options.getMipOptOut() != null && options.getMipOptOut().isPresent()) { + urlBuilder.addQueryParameter( + "mip_opt_out", String.valueOf(options.getMipOptOut().get())); + } + if (options.getModel() != null && options.getModel().isPresent()) { + urlBuilder.addQueryParameter( + "model", String.valueOf(options.getModel().get())); + } + if (options.getSampleRate() != null && options.getSampleRate().isPresent()) { + urlBuilder.addQueryParameter( + "sample_rate", String.valueOf(options.getSampleRate().get())); + } + if (options.getSpeed() != null && options.getSpeed().isPresent()) { + urlBuilder.addQueryParameter( + "speed", String.valueOf(options.getSpeed().get())); + } + if (options.getAdditionalProperties() != null) { + options.getAdditionalProperties().forEach((key, value) -> { + if (value != null) { + QueryStringMapper.addQueryParameter(urlBuilder, key, value, true); + } + }); + } + Request.Builder requestBuilder = new Request.Builder().url(urlBuilder.build()); + clientOptions.headers((RequestOptions) null).forEach(requestBuilder::addHeader); + final Request request = requestBuilder.build(); + this.readyState = WebSocketReadyState.CONNECTING; + ReconnectingWebSocketListener.ReconnectOptions reconnectOpts = this.reconnectOptions != null + ? this.reconnectOptions + : ReconnectingWebSocketListener.ReconnectOptions.builder().build(); + this.reconnectingListener = + new ReconnectingWebSocketListener( + reconnectOpts, + () -> { + if (clientOptions.isClosed()) { + throw new IllegalStateException("root client has been closed"); + } + if (clientOptions.webSocketFactory().isPresent()) { + return clientOptions + .webSocketFactory() + .get() + .create(request, this.reconnectingListener); + } else { + return okHttpClient.newWebSocket(request, this.reconnectingListener); + } + }, + clientOptions::isClosed) { + @Override + protected void onWebSocketOpen(WebSocket webSocket, Response response) { + readyState = WebSocketReadyState.OPEN; + if (onConnectedHandler != null) { + onConnectedHandler.run(); + } + connectionFuture.complete(null); + } + + @Override + protected void onWebSocketMessage(WebSocket webSocket, String text) { + handleIncomingMessage(text); + } + + @Override + protected void onWebSocketBinaryMessage(WebSocket webSocket, ByteString bytes) { + if (speakV1AudioHandler != null) { + speakV1AudioHandler.accept(bytes); + } + } + + @Override + protected void onWebSocketFailure(WebSocket webSocket, Throwable t, Response response) { + readyState = WebSocketReadyState.CLOSED; + if (onErrorHandler != null) { + onErrorHandler.accept(new RuntimeException(t)); + } + connectionFuture.completeExceptionally(t); + } + + @Override + protected void onWebSocketClosed(WebSocket webSocket, int code, String reason) { + readyState = WebSocketReadyState.CLOSED; + if (onDisconnectedHandler != null) { + onDisconnectedHandler.accept(new DisconnectReason(code, reason)); + } + } + }; + clientOptions.registerWebSocket(this); + reconnectingListener.connect(); + return connectionFuture; + } + + /** + * Establishes the WebSocket connection asynchronously with default options. + * @return a CompletableFuture that completes when the connection is established + */ + public CompletableFuture connect() { + return connect(V1ConnectOptions.builder().build()); + } + + /** + * Disconnects the WebSocket connection and releases resources. + */ + public void disconnect() { + clientOptions.unregisterWebSocket(this); + if (reconnectingListener != null) { + reconnectingListener.disconnect(); + } + if (timeoutExecutor != null) { + timeoutExecutor.shutdownNow(); + timeoutExecutor = null; + } + } + + /** + * Gets the current state of the WebSocket connection. + * + * This provides the actual connection state, similar to the W3C WebSocket API. + * + * @return the current WebSocket ready state + */ + public WebSocketReadyState getReadyState() { + return readyState; + } + + /** + * Sends a SpeakV1Text message to the server asynchronously. + * @param message the message to send + * @return a CompletableFuture that completes when the message is sent + */ + public CompletableFuture sendText(SpeakV1Text message) { + return sendMessage(message); + } + + /** + * Sends a SpeakV1Flush message to the server asynchronously. + * @param message the message to send + * @return a CompletableFuture that completes when the message is sent + */ + public CompletableFuture sendFlush(SpeakV1Flush message) { + return sendMessage(message); + } + + /** + * Sends a SpeakV1Clear message to the server asynchronously. + * @param message the message to send + * @return a CompletableFuture that completes when the message is sent + */ + public CompletableFuture sendClear(SpeakV1Clear message) { + return sendMessage(message); + } + + /** + * Sends a SpeakV1Close message to the server asynchronously. + * @param message the message to send + * @return a CompletableFuture that completes when the message is sent + */ + public CompletableFuture sendClose(SpeakV1Close message) { + return sendMessage(message); + } + + /** + * Registers a handler for SpeakV1Audio messages from the server. + * @param handler the handler to invoke when a message is received + */ + public void onSpeakV1Audio(Consumer handler) { + this.speakV1AudioHandler = handler; + } + + /** + * Registers a handler for SpeakV1Metadata messages from the server. + * @param handler the handler to invoke when a message is received + */ + public void onMetadata(Consumer handler) { + this.metadataHandler = handler; + } + + /** + * Registers a handler for SpeakV1Flushed messages from the server. + * @param handler the handler to invoke when a message is received + */ + public void onFlushed(Consumer handler) { + this.flushedHandler = handler; + } + + /** + * Registers a handler for SpeakV1Cleared messages from the server. + * @param handler the handler to invoke when a message is received + */ + public void onCleared(Consumer handler) { + this.clearedHandler = handler; + } + + /** + * Registers a handler for SpeakV1Warning messages from the server. + * @param handler the handler to invoke when a message is received + */ + public void onWarning(Consumer handler) { + this.warningHandler = handler; + } + + /** + * Registers a handler called when the connection is established. + * @param handler the handler to invoke when connected + */ + public void onConnected(Runnable handler) { + this.onConnectedHandler = handler; + } + + /** + * Registers a handler called when the connection is closed. + * @param handler the handler to invoke when disconnected + */ + public void onDisconnected(Consumer handler) { + this.onDisconnectedHandler = handler; + } + + /** + * Registers a handler called when an error occurs. + * @param handler the handler to invoke on error + */ + public void onError(Consumer handler) { + this.onErrorHandler = handler; + } + + /** + * Registers a handler called for every incoming text message. + * The handler receives the raw JSON string before type-specific dispatch. + * @param handler the handler to invoke with the raw message JSON + */ + public void onMessage(Consumer handler) { + this.onMessageHandler = handler; + } + + /** + * Configures reconnection behavior. Must be called before {@link #connect}. + * + * @param options the reconnection options (backoff, retries, queue size) + */ + public void reconnectOptions(ReconnectingWebSocketListener.ReconnectOptions options) { + this.reconnectOptions = options; + } + + /** + * Closes this WebSocket client, releasing all resources. + * Equivalent to calling {@link #disconnect()}. + */ + @Override + public void close() { + disconnect(); + } + + /** + * Ensures the WebSocket is connected and ready to send messages. + * @throws IllegalStateException if the socket is not connected or not open + */ + private void assertSocketIsOpen() { + if (reconnectingListener.getWebSocket() == null) { + throw new IllegalStateException("WebSocket is not connected. Call connect() first."); + } + if (readyState != WebSocketReadyState.OPEN) { + throw new IllegalStateException("WebSocket is not open. Current state: " + readyState); + } + } + + private CompletableFuture sendMessage(Object body) { + CompletableFuture future = new CompletableFuture<>(); + try { + assertSocketIsOpen(); + String json = objectMapper.writeValueAsString(body); + // Use reconnecting listener's send method which handles queuing + reconnectingListener.send(json); + future.complete(null); + } catch (IllegalStateException e) { + future.completeExceptionally(e); + } catch (Exception e) { + future.completeExceptionally(new RuntimeException("Failed to send message", e)); + } + return future; + } + + private void handleIncomingMessage(String json) { + try { + if (onMessageHandler != null) { + onMessageHandler.accept(json); + } + JsonNode node = objectMapper.readTree(json); + if (node == null || node.isNull()) { + throw new IllegalArgumentException("Received null or invalid JSON message"); + } + if (node.has("request_id") + && node.has("model_name") + && node.has("model_version") + && node.has("model_uuid") + && "Metadata".equals(node.path("type").asText())) { + SpeakV1Metadata metadataHandlerEvent = null; + try { + metadataHandlerEvent = objectMapper.treeToValue(node, SpeakV1Metadata.class); + } catch (Exception e) { + } + if (metadataHandlerEvent != null) { + if (metadataHandler != null) { + metadataHandler.accept(metadataHandlerEvent); + } + return; + } + } + if (node.has("description") + && node.has("code") + && "Warning".equals(node.path("type").asText())) { + SpeakV1Warning warningHandlerEvent = null; + try { + warningHandlerEvent = objectMapper.treeToValue(node, SpeakV1Warning.class); + } catch (Exception e) { + } + if (warningHandlerEvent != null) { + if (warningHandler != null) { + warningHandler.accept(warningHandlerEvent); + } + return; + } + } + if (node.has("type") && node.has("sequence_id")) { + SpeakV1Flushed flushedHandlerEvent = null; + try { + flushedHandlerEvent = objectMapper.treeToValue(node, SpeakV1Flushed.class); + } catch (Exception e) { + } + if (flushedHandlerEvent != null) { + if (flushedHandler != null) { + flushedHandler.accept(flushedHandlerEvent); + } + return; + } + } + if (node.has("type") && node.has("sequence_id")) { + SpeakV1Cleared clearedHandlerEvent = null; + try { + clearedHandlerEvent = objectMapper.treeToValue(node, SpeakV1Cleared.class); + } catch (Exception e) { + } + if (clearedHandlerEvent != null) { + if (clearedHandler != null) { + clearedHandler.accept(clearedHandlerEvent); + } + return; + } + } + if (onErrorHandler != null) { + onErrorHandler.accept(new RuntimeException( + "Unrecognized WebSocket message: " + json.substring(0, Math.min(200, json.length())) + + "... Update your SDK version to support new message types.")); + } + } catch (Exception e) { + if (onErrorHandler != null) { + onErrorHandler.accept(e); + } + } + } +} diff --git a/src/main/java/com/deepgram/resources/speak/v2/types/SpeakV2Close.java.bak b/src/main/java/com/deepgram/resources/speak/v2/types/SpeakV2Close.java.bak new file mode 100644 index 0000000..7fecd79 --- /dev/null +++ b/src/main/java/com/deepgram/resources/speak/v2/types/SpeakV2Close.java.bak @@ -0,0 +1,83 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ +package com.deepgram.resources.speak.v2.types; + +import com.deepgram.core.ObjectMappers; +import com.fasterxml.jackson.annotation.JsonAnyGetter; +import com.fasterxml.jackson.annotation.JsonAnySetter; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import java.util.HashMap; +import java.util.Map; + +@JsonInclude(JsonInclude.Include.NON_ABSENT) +@JsonDeserialize(builder = SpeakV2Close.Builder.class) +public final class SpeakV2Close { + private final Map additionalProperties; + + private SpeakV2Close(Map additionalProperties) { + this.additionalProperties = additionalProperties; + } + + /** + * @return Message type identifier + */ + @JsonProperty("type") + public String getType() { + return "Close"; + } + + @java.lang.Override + public boolean equals(Object other) { + if (this == other) return true; + return other instanceof SpeakV2Close; + } + + @java.lang.Override + public int hashCode() { + return getClass().hashCode(); + } + + @JsonAnyGetter + public Map getAdditionalProperties() { + return this.additionalProperties; + } + + @java.lang.Override + public String toString() { + return ObjectMappers.stringify(this); + } + + public static Builder builder() { + return new Builder(); + } + + @JsonIgnoreProperties(ignoreUnknown = true) + public static final class Builder { + @JsonAnySetter + private Map additionalProperties = new HashMap<>(); + + private Builder() {} + + public Builder from(SpeakV2Close other) { + return this; + } + + public SpeakV2Close build() { + return new SpeakV2Close(additionalProperties); + } + + public Builder additionalProperty(String key, Object value) { + this.additionalProperties.put(key, value); + return this; + } + + public Builder additionalProperties(Map additionalProperties) { + this.additionalProperties.putAll(additionalProperties); + return this; + } + } +} diff --git a/src/main/java/com/deepgram/resources/speak/v2/types/SpeakV2Flush.java.bak b/src/main/java/com/deepgram/resources/speak/v2/types/SpeakV2Flush.java.bak new file mode 100644 index 0000000..130da0d --- /dev/null +++ b/src/main/java/com/deepgram/resources/speak/v2/types/SpeakV2Flush.java.bak @@ -0,0 +1,83 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ +package com.deepgram.resources.speak.v2.types; + +import com.deepgram.core.ObjectMappers; +import com.fasterxml.jackson.annotation.JsonAnyGetter; +import com.fasterxml.jackson.annotation.JsonAnySetter; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import java.util.HashMap; +import java.util.Map; + +@JsonInclude(JsonInclude.Include.NON_ABSENT) +@JsonDeserialize(builder = SpeakV2Flush.Builder.class) +public final class SpeakV2Flush { + private final Map additionalProperties; + + private SpeakV2Flush(Map additionalProperties) { + this.additionalProperties = additionalProperties; + } + + /** + * @return Message type identifier + */ + @JsonProperty("type") + public String getType() { + return "Flush"; + } + + @java.lang.Override + public boolean equals(Object other) { + if (this == other) return true; + return other instanceof SpeakV2Flush; + } + + @java.lang.Override + public int hashCode() { + return getClass().hashCode(); + } + + @JsonAnyGetter + public Map getAdditionalProperties() { + return this.additionalProperties; + } + + @java.lang.Override + public String toString() { + return ObjectMappers.stringify(this); + } + + public static Builder builder() { + return new Builder(); + } + + @JsonIgnoreProperties(ignoreUnknown = true) + public static final class Builder { + @JsonAnySetter + private Map additionalProperties = new HashMap<>(); + + private Builder() {} + + public Builder from(SpeakV2Flush other) { + return this; + } + + public SpeakV2Flush build() { + return new SpeakV2Flush(additionalProperties); + } + + public Builder additionalProperty(String key, Object value) { + this.additionalProperties.put(key, value); + return this; + } + + public Builder additionalProperties(Map additionalProperties) { + this.additionalProperties.putAll(additionalProperties); + return this; + } + } +} diff --git a/src/main/java/com/deepgram/resources/speak/v2/websocket/V2WebSocketClient.java.bak b/src/main/java/com/deepgram/resources/speak/v2/websocket/V2WebSocketClient.java.bak new file mode 100644 index 0000000..b4e01f7 --- /dev/null +++ b/src/main/java/com/deepgram/resources/speak/v2/websocket/V2WebSocketClient.java.bak @@ -0,0 +1,630 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ +package com.deepgram.resources.speak.v2.websocket; + +import com.deepgram.core.ClientOptions; +import com.deepgram.core.DisconnectReason; +import com.deepgram.core.ObjectMappers; +import com.deepgram.core.QueryStringMapper; +import com.deepgram.core.ReconnectingWebSocketListener; +import com.deepgram.core.RequestOptions; +import com.deepgram.core.WebSocketReadyState; +import com.deepgram.resources.speak.v2.types.SpeakV2Close; +import com.deepgram.resources.speak.v2.types.SpeakV2Configure; +import com.deepgram.resources.speak.v2.types.SpeakV2ConfigureFailure; +import com.deepgram.resources.speak.v2.types.SpeakV2ConfigureSuccess; +import com.deepgram.resources.speak.v2.types.SpeakV2Connected; +import com.deepgram.resources.speak.v2.types.SpeakV2Error; +import com.deepgram.resources.speak.v2.types.SpeakV2Flush; +import com.deepgram.resources.speak.v2.types.SpeakV2Flushed; +import com.deepgram.resources.speak.v2.types.SpeakV2Interrupt; +import com.deepgram.resources.speak.v2.types.SpeakV2SessionMetadata; +import com.deepgram.resources.speak.v2.types.SpeakV2Speak; +import com.deepgram.resources.speak.v2.types.SpeakV2SpeechInterrupted; +import com.deepgram.resources.speak.v2.types.SpeakV2SpeechMetadata; +import com.deepgram.resources.speak.v2.types.SpeakV2SpeechStarted; +import com.deepgram.resources.speak.v2.types.SpeakV2Warning; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ScheduledExecutorService; +import java.util.function.Consumer; +import okhttp3.HttpUrl; +import okhttp3.OkHttpClient; +import okhttp3.Request; +import okhttp3.Response; +import okhttp3.WebSocket; +import okio.ByteString; + +/** + * WebSocket client for the v2 channel. + * Provides real-time bidirectional communication with strongly-typed messages. + */ +public class V2WebSocketClient implements AutoCloseable { + protected final ClientOptions clientOptions; + + private final ObjectMapper objectMapper; + + private final OkHttpClient okHttpClient; + + private ScheduledExecutorService timeoutExecutor; + + private volatile WebSocketReadyState readyState = WebSocketReadyState.CLOSED; + + private volatile Runnable onConnectedHandler; + + private volatile Consumer onDisconnectedHandler; + + private volatile Consumer onErrorHandler; + + private volatile Consumer onMessageHandler; + + private volatile ReconnectingWebSocketListener.ReconnectOptions reconnectOptions; + + private CompletableFuture connectionFuture; + + private ReconnectingWebSocketListener reconnectingListener; + + private volatile Consumer speakV2AudioHandler; + + private volatile Consumer connectedHandler; + + private volatile Consumer speechStartedHandler; + + private volatile Consumer speechMetadataHandler; + + private volatile Consumer speechInterruptedHandler; + + private volatile Consumer flushedHandler; + + private volatile Consumer sessionMetadataHandler; + + private volatile Consumer configureSuccessHandler; + + private volatile Consumer configureFailureHandler; + + private volatile Consumer warningHandler; + + private volatile Consumer errorHandler; + + /** + * Creates a new async WebSocket client for the v2 channel. + */ + public V2WebSocketClient(ClientOptions clientOptions) { + this.clientOptions = clientOptions; + this.objectMapper = ObjectMappers.JSON_MAPPER; + this.okHttpClient = clientOptions.httpClient(); + } + + /** + * Establishes the WebSocket connection asynchronously with automatic reconnection. + * @return a CompletableFuture that completes when the connection is established + * @param options connection options including query parameters + */ + public CompletableFuture connect(V2ConnectOptions options) { + connectionFuture = new CompletableFuture<>(); + String baseUrl = clientOptions.environment().getProductionURL(); + String fullPath = "/v2/speak"; + if (baseUrl.endsWith("/") && fullPath.startsWith("/")) { + fullPath = fullPath.substring(1); + } else if (!baseUrl.endsWith("/") && !fullPath.startsWith("/")) { + fullPath = "/" + fullPath; + } + // OkHttp's HttpUrl only supports http/https schemes; convert wss/ws for URL parsing + if (baseUrl.startsWith("wss://")) { + baseUrl = "https://" + baseUrl.substring(6); + } else if (baseUrl.startsWith("ws://")) { + baseUrl = "http://" + baseUrl.substring(5); + } + HttpUrl parsedUrl = HttpUrl.parse(baseUrl + fullPath); + if (parsedUrl == null) { + throw new IllegalArgumentException("Invalid WebSocket URL: " + baseUrl + fullPath); + } + HttpUrl.Builder urlBuilder = parsedUrl.newBuilder(); + urlBuilder.addQueryParameter("model", String.valueOf(options.getModel())); + if (options.getEncoding() != null && options.getEncoding().isPresent()) { + urlBuilder.addQueryParameter( + "encoding", String.valueOf(options.getEncoding().get())); + } + if (options.getSampleRate() != null && options.getSampleRate().isPresent()) { + urlBuilder.addQueryParameter( + "sample_rate", String.valueOf(options.getSampleRate().get())); + } + if (options.getSpeed() != null && options.getSpeed().isPresent()) { + urlBuilder.addQueryParameter( + "speed", String.valueOf(options.getSpeed().get())); + } + if (options.getExpressivity() != null && options.getExpressivity().isPresent()) { + urlBuilder.addQueryParameter( + "expressivity", String.valueOf(options.getExpressivity().get())); + } + if (options.getMipOptOut() != null && options.getMipOptOut().isPresent()) { + urlBuilder.addQueryParameter( + "mip_opt_out", String.valueOf(options.getMipOptOut().get())); + } + if (options.getTag() != null && options.getTag().isPresent()) { + QueryStringMapper.addQueryParameter(urlBuilder, "tag", options.getTag().get().get(), true); + } + if (options.getAdditionalProperties() != null) { + options.getAdditionalProperties().forEach((key, value) -> { + if (value != null) { + QueryStringMapper.addQueryParameter(urlBuilder, key, value, true); + } + }); + } + Request.Builder requestBuilder = new Request.Builder().url(urlBuilder.build()); + clientOptions.headers((RequestOptions) null).forEach(requestBuilder::addHeader); + final Request request = requestBuilder.build(); + this.readyState = WebSocketReadyState.CONNECTING; + ReconnectingWebSocketListener.ReconnectOptions reconnectOpts = this.reconnectOptions != null + ? this.reconnectOptions + : ReconnectingWebSocketListener.ReconnectOptions.builder().build(); + this.reconnectingListener = + new ReconnectingWebSocketListener( + reconnectOpts, + () -> { + if (clientOptions.isClosed()) { + throw new IllegalStateException("root client has been closed"); + } + if (clientOptions.webSocketFactory().isPresent()) { + return clientOptions + .webSocketFactory() + .get() + .create(request, this.reconnectingListener); + } else { + return okHttpClient.newWebSocket(request, this.reconnectingListener); + } + }, + clientOptions::isClosed) { + @Override + protected void onWebSocketOpen(WebSocket webSocket, Response response) { + readyState = WebSocketReadyState.OPEN; + if (onConnectedHandler != null) { + onConnectedHandler.run(); + } + connectionFuture.complete(null); + } + + @Override + protected void onWebSocketMessage(WebSocket webSocket, String text) { + handleIncomingMessage(text); + } + + @Override + protected void onWebSocketBinaryMessage(WebSocket webSocket, ByteString bytes) { + if (speakV2AudioHandler != null) { + speakV2AudioHandler.accept(bytes); + } + } + + @Override + protected void onWebSocketFailure(WebSocket webSocket, Throwable t, Response response) { + readyState = WebSocketReadyState.CLOSED; + if (onErrorHandler != null) { + onErrorHandler.accept(new RuntimeException(t)); + } + connectionFuture.completeExceptionally(t); + } + + @Override + protected void onWebSocketClosed(WebSocket webSocket, int code, String reason) { + readyState = WebSocketReadyState.CLOSED; + if (onDisconnectedHandler != null) { + onDisconnectedHandler.accept(new DisconnectReason(code, reason)); + } + } + }; + clientOptions.registerWebSocket(this); + reconnectingListener.connect(); + return connectionFuture; + } + + /** + * Disconnects the WebSocket connection and releases resources. + */ + public void disconnect() { + clientOptions.unregisterWebSocket(this); + if (reconnectingListener != null) { + reconnectingListener.disconnect(); + } + if (timeoutExecutor != null) { + timeoutExecutor.shutdownNow(); + timeoutExecutor = null; + } + } + + /** + * Gets the current state of the WebSocket connection. + * + * This provides the actual connection state, similar to the W3C WebSocket API. + * + * @return the current WebSocket ready state + */ + public WebSocketReadyState getReadyState() { + return readyState; + } + + /** + * Sends a SpeakV2Speak message to the server asynchronously. + * @param message the message to send + * @return a CompletableFuture that completes when the message is sent + */ + public CompletableFuture sendSpeak(SpeakV2Speak message) { + return sendMessage(message); + } + + /** + * Sends a SpeakV2Flush message to the server asynchronously. + * @param message the message to send + * @return a CompletableFuture that completes when the message is sent + */ + public CompletableFuture sendFlush(SpeakV2Flush message) { + return sendMessage(message); + } + + /** + * Sends a SpeakV2Interrupt message to the server asynchronously. + * @param message the message to send + * @return a CompletableFuture that completes when the message is sent + */ + public CompletableFuture sendInterrupt(SpeakV2Interrupt message) { + return sendMessage(message); + } + + /** + * Sends a SpeakV2Configure message to the server asynchronously. + * @param message the message to send + * @return a CompletableFuture that completes when the message is sent + */ + public CompletableFuture sendConfigure(SpeakV2Configure message) { + return sendMessage(message); + } + + /** + * Sends a SpeakV2Close message to the server asynchronously. + * @param message the message to send + * @return a CompletableFuture that completes when the message is sent + */ + public CompletableFuture sendClose(SpeakV2Close message) { + return sendMessage(message); + } + + /** + * Registers a handler for SpeakV2Audio messages from the server. + * @param handler the handler to invoke when a message is received + */ + public void onSpeakV2Audio(Consumer handler) { + this.speakV2AudioHandler = handler; + } + + /** + * Registers a handler for SpeakV2Connected messages from the server. + * @param handler the handler to invoke when a message is received + */ + public void onConnected(Consumer handler) { + this.connectedHandler = handler; + } + + /** + * Registers a handler for SpeakV2SpeechStarted messages from the server. + * @param handler the handler to invoke when a message is received + */ + public void onSpeechStarted(Consumer handler) { + this.speechStartedHandler = handler; + } + + /** + * Registers a handler for SpeakV2SpeechMetadata messages from the server. + * @param handler the handler to invoke when a message is received + */ + public void onSpeechMetadata(Consumer handler) { + this.speechMetadataHandler = handler; + } + + /** + * Registers a handler for SpeakV2SpeechInterrupted messages from the server. + * @param handler the handler to invoke when a message is received + */ + public void onSpeechInterrupted(Consumer handler) { + this.speechInterruptedHandler = handler; + } + + /** + * Registers a handler for SpeakV2Flushed messages from the server. + * @param handler the handler to invoke when a message is received + */ + public void onFlushed(Consumer handler) { + this.flushedHandler = handler; + } + + /** + * Registers a handler for SpeakV2SessionMetadata messages from the server. + * @param handler the handler to invoke when a message is received + */ + public void onSessionMetadata(Consumer handler) { + this.sessionMetadataHandler = handler; + } + + /** + * Registers a handler for SpeakV2ConfigureSuccess messages from the server. + * @param handler the handler to invoke when a message is received + */ + public void onConfigureSuccess(Consumer handler) { + this.configureSuccessHandler = handler; + } + + /** + * Registers a handler for SpeakV2ConfigureFailure messages from the server. + * @param handler the handler to invoke when a message is received + */ + public void onConfigureFailure(Consumer handler) { + this.configureFailureHandler = handler; + } + + /** + * Registers a handler for SpeakV2Warning messages from the server. + * @param handler the handler to invoke when a message is received + */ + public void onWarning(Consumer handler) { + this.warningHandler = handler; + } + + /** + * Registers a handler for SpeakV2Error messages from the server. + * @param handler the handler to invoke when a message is received + */ + public void onErrorMessage(Consumer handler) { + this.errorHandler = handler; + } + + /** + * Registers a handler called when the connection is established. + * @param handler the handler to invoke when connected + */ + public void onConnected(Runnable handler) { + this.onConnectedHandler = handler; + } + + /** + * Registers a handler called when the connection is closed. + * @param handler the handler to invoke when disconnected + */ + public void onDisconnected(Consumer handler) { + this.onDisconnectedHandler = handler; + } + + /** + * Registers a handler called when an error occurs. + * @param handler the handler to invoke on error + */ + public void onError(Consumer handler) { + this.onErrorHandler = handler; + } + + /** + * Registers a handler called for every incoming text message. + * The handler receives the raw JSON string before type-specific dispatch. + * @param handler the handler to invoke with the raw message JSON + */ + public void onMessage(Consumer handler) { + this.onMessageHandler = handler; + } + + /** + * Configures reconnection behavior. Must be called before {@link #connect}. + * + * @param options the reconnection options (backoff, retries, queue size) + */ + public void reconnectOptions(ReconnectingWebSocketListener.ReconnectOptions options) { + this.reconnectOptions = options; + } + + /** + * Closes this WebSocket client, releasing all resources. + * Equivalent to calling {@link #disconnect()}. + */ + @Override + public void close() { + disconnect(); + } + + /** + * Ensures the WebSocket is connected and ready to send messages. + * @throws IllegalStateException if the socket is not connected or not open + */ + private void assertSocketIsOpen() { + if (reconnectingListener.getWebSocket() == null) { + throw new IllegalStateException("WebSocket is not connected. Call connect() first."); + } + if (readyState != WebSocketReadyState.OPEN) { + throw new IllegalStateException("WebSocket is not open. Current state: " + readyState); + } + } + + private CompletableFuture sendMessage(Object body) { + CompletableFuture future = new CompletableFuture<>(); + try { + assertSocketIsOpen(); + String json = objectMapper.writeValueAsString(body); + // Use reconnecting listener's send method which handles queuing + reconnectingListener.send(json); + future.complete(null); + } catch (IllegalStateException e) { + future.completeExceptionally(e); + } catch (Exception e) { + future.completeExceptionally(new RuntimeException("Failed to send message", e)); + } + return future; + } + + private void handleIncomingMessage(String json) { + try { + if (onMessageHandler != null) { + onMessageHandler.accept(json); + } + JsonNode node = objectMapper.readTree(json); + if (node == null || node.isNull()) { + throw new IllegalArgumentException("Received null or invalid JSON message"); + } + if (node.has("speech_id") + && node.has("audio_duration_ms") + && node.has("input_character_count") + && node.has("billable_character_count") + && node.has("controls_applied") + && "SpeechMetadata".equals(node.path("type").asText())) { + SpeakV2SpeechMetadata speechMetadataHandlerEvent = null; + try { + speechMetadataHandlerEvent = objectMapper.treeToValue(node, SpeakV2SpeechMetadata.class); + } catch (Exception e) { + } + if (speechMetadataHandlerEvent != null) { + if (speechMetadataHandler != null) { + speechMetadataHandler.accept(speechMetadataHandlerEvent); + } + return; + } + } + if (node.has("request_id") + && node.has("model_name") + && node.has("model_version") + && node.has("model_uuids") + && "Connected".equals(node.path("type").asText())) { + SpeakV2Connected connectedHandlerEvent = null; + try { + connectedHandlerEvent = objectMapper.treeToValue(node, SpeakV2Connected.class); + } catch (Exception e) { + } + if (connectedHandlerEvent != null) { + if (connectedHandler != null) { + connectedHandler.accept(connectedHandlerEvent); + } + return; + } + } + if (node.has("total_audio_duration_ms") + && node.has("total_input_character_count") + && node.has("total_billable_character_count") + && "SessionMetadata".equals(node.path("type").asText())) { + SpeakV2SessionMetadata sessionMetadataHandlerEvent = null; + try { + sessionMetadataHandlerEvent = objectMapper.treeToValue(node, SpeakV2SessionMetadata.class); + } catch (Exception e) { + } + if (sessionMetadataHandlerEvent != null) { + if (sessionMetadataHandler != null) { + sessionMetadataHandler.accept(sessionMetadataHandlerEvent); + } + return; + } + } + if (node.has("audio_played_ms") + && node.has("metadata") + && "SpeechInterrupted".equals(node.path("type").asText())) { + SpeakV2SpeechInterrupted speechInterruptedHandlerEvent = null; + try { + speechInterruptedHandlerEvent = objectMapper.treeToValue(node, SpeakV2SpeechInterrupted.class); + } catch (Exception e) { + } + if (speechInterruptedHandlerEvent != null) { + if (speechInterruptedHandler != null) { + speechInterruptedHandler.accept(speechInterruptedHandlerEvent); + } + return; + } + } + if (node.has("code") + && node.has("description") + && "ConfigureFailure".equals(node.path("type").asText())) { + SpeakV2ConfigureFailure configureFailureHandlerEvent = null; + try { + configureFailureHandlerEvent = objectMapper.treeToValue(node, SpeakV2ConfigureFailure.class); + } catch (Exception e) { + } + if (configureFailureHandlerEvent != null) { + if (configureFailureHandler != null) { + configureFailureHandler.accept(configureFailureHandlerEvent); + } + return; + } + } + if (node.has("code") + && node.has("description") + && "Warning".equals(node.path("type").asText())) { + SpeakV2Warning warningHandlerEvent = null; + try { + warningHandlerEvent = objectMapper.treeToValue(node, SpeakV2Warning.class); + } catch (Exception e) { + } + if (warningHandlerEvent != null) { + if (warningHandler != null) { + warningHandler.accept(warningHandlerEvent); + } + return; + } + } + if (node.has("code") + && node.has("description") + && "Error".equals(node.path("type").asText())) { + SpeakV2Error errorHandlerEvent = null; + try { + errorHandlerEvent = objectMapper.treeToValue(node, SpeakV2Error.class); + } catch (Exception e) { + } + if (errorHandlerEvent != null) { + if (errorHandler != null) { + errorHandler.accept(errorHandlerEvent); + } + return; + } + } + if (node.has("speech_id") + && "SpeechStarted".equals(node.path("type").asText())) { + SpeakV2SpeechStarted speechStartedHandlerEvent = null; + try { + speechStartedHandlerEvent = objectMapper.treeToValue(node, SpeakV2SpeechStarted.class); + } catch (Exception e) { + } + if (speechStartedHandlerEvent != null) { + if (speechStartedHandler != null) { + speechStartedHandler.accept(speechStartedHandlerEvent); + } + return; + } + } + if (node.has("speech_id") && "Flushed".equals(node.path("type").asText())) { + SpeakV2Flushed flushedHandlerEvent = null; + try { + flushedHandlerEvent = objectMapper.treeToValue(node, SpeakV2Flushed.class); + } catch (Exception e) { + } + if (flushedHandlerEvent != null) { + if (flushedHandler != null) { + flushedHandler.accept(flushedHandlerEvent); + } + return; + } + } + if (node.has("applied") + && "ConfigureSuccess".equals(node.path("type").asText())) { + SpeakV2ConfigureSuccess configureSuccessHandlerEvent = null; + try { + configureSuccessHandlerEvent = objectMapper.treeToValue(node, SpeakV2ConfigureSuccess.class); + } catch (Exception e) { + } + if (configureSuccessHandlerEvent != null) { + if (configureSuccessHandler != null) { + configureSuccessHandler.accept(configureSuccessHandlerEvent); + } + return; + } + } + // The raw frame is already delivered to onMessage(String), so ignore unknown typed + // frames for forward compatibility with newly added server control messages. + } catch (Exception e) { + if (onErrorHandler != null) { + onErrorHandler.accept(e); + } + } + } +} diff --git a/src/main/java/com/deepgram/types/DeepgramModel.java.bak b/src/main/java/com/deepgram/types/DeepgramModel.java.bak new file mode 100644 index 0000000..6444d7f --- /dev/null +++ b/src/main/java/com/deepgram/types/DeepgramModel.java.bak @@ -0,0 +1,1071 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ +package com.deepgram.types; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonValue; + +public final class DeepgramModel { + public static final DeepgramModel AURA2SIRIO_ES = new DeepgramModel(Value.AURA2SIRIO_ES, "aura-2-sirio-es"); + + public static final DeepgramModel AURA2HERA_EN = new DeepgramModel(Value.AURA2HERA_EN, "aura-2-hera-en"); + + public static final DeepgramModel FLUX_KELSEY_EN = new DeepgramModel(Value.FLUX_KELSEY_EN, "flux-kelsey-en"); + + 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"); + + public static final DeepgramModel AURA2HARMONIA_EN = + new DeepgramModel(Value.AURA2HARMONIA_EN, "aura-2-harmonia-en"); + + public static final DeepgramModel AURA2CARINA_ES = new DeepgramModel(Value.AURA2CARINA_ES, "aura-2-carina-es"); + + public static final DeepgramModel FLUX_MEENA_EN = new DeepgramModel(Value.FLUX_MEENA_EN, "flux-meena-en"); + + public static final DeepgramModel FLUX_MEGHAN_EN = new DeepgramModel(Value.FLUX_MEGHAN_EN, "flux-meghan-en"); + + public static final DeepgramModel AURA_ZEUS_EN = new DeepgramModel(Value.AURA_ZEUS_EN, "aura-zeus-en"); + + public static final DeepgramModel AURA2HERMES_EN = new DeepgramModel(Value.AURA2HERMES_EN, "aura-2-hermes-en"); + + public static final DeepgramModel FLUX_CONOR_EN = new DeepgramModel(Value.FLUX_CONOR_EN, "flux-conor-en"); + + public static final DeepgramModel AURA2NEPTUNE_EN = new DeepgramModel(Value.AURA2NEPTUNE_EN, "aura-2-neptune-en"); + + public static final DeepgramModel FLUX_BROOKE_EN = new DeepgramModel(Value.FLUX_BROOKE_EN, "flux-brooke-en"); + + public static final DeepgramModel FLUX_RUFUS_EN = new DeepgramModel(Value.FLUX_RUFUS_EN, "flux-rufus-en"); + + public static final DeepgramModel FLUX_WES_EN = new DeepgramModel(Value.FLUX_WES_EN, "flux-wes-en"); + + public static final DeepgramModel AURA2CALLISTA_EN = + new DeepgramModel(Value.AURA2CALLISTA_EN, "aura-2-callista-en"); + + public static final DeepgramModel AURA2AURORA_EN = new DeepgramModel(Value.AURA2AURORA_EN, "aura-2-aurora-en"); + + public static final DeepgramModel AURA2OPHELIA_EN = new DeepgramModel(Value.AURA2OPHELIA_EN, "aura-2-ophelia-en"); + + public static final DeepgramModel FLUX_BRUCE_EN = new DeepgramModel(Value.FLUX_BRUCE_EN, "flux-bruce-en"); + + public static final DeepgramModel AURA2APOLLO_EN = new DeepgramModel(Value.AURA2APOLLO_EN, "aura-2-apollo-en"); + + public static final DeepgramModel AURA2DELIA_EN = new DeepgramModel(Value.AURA2DELIA_EN, "aura-2-delia-en"); + + public static final DeepgramModel AURA2MARS_EN = new DeepgramModel(Value.AURA2MARS_EN, "aura-2-mars-en"); + + public static final DeepgramModel AURA_HERA_EN = new DeepgramModel(Value.AURA_HERA_EN, "aura-hera-en"); + + public static final DeepgramModel FLUX_MAEVE_EN = new DeepgramModel(Value.FLUX_MAEVE_EN, "flux-maeve-en"); + + public static final DeepgramModel FLUX_MARCUS_EN = new DeepgramModel(Value.FLUX_MARCUS_EN, "flux-marcus-en"); + + public static final DeepgramModel AURA2ARIES_EN = new DeepgramModel(Value.AURA2ARIES_EN, "aura-2-aries-en"); + + public static final DeepgramModel FLUX_PAIGE_EN = new DeepgramModel(Value.FLUX_PAIGE_EN, "flux-paige-en"); + + public static final DeepgramModel AURA2JUNO_EN = new DeepgramModel(Value.AURA2JUNO_EN, "aura-2-juno-en"); + + public static final DeepgramModel FLUX_DREW_EN = new DeepgramModel(Value.FLUX_DREW_EN, "flux-drew-en"); + + public static final DeepgramModel AURA2JAVIER_ES = new DeepgramModel(Value.AURA2JAVIER_ES, "aura-2-javier-es"); + + public static final DeepgramModel AURA2ANDROMEDA_EN = + new DeepgramModel(Value.AURA2ANDROMEDA_EN, "aura-2-andromeda-en"); + + public static final DeepgramModel AURA2SATURN_EN = new DeepgramModel(Value.AURA2SATURN_EN, "aura-2-saturn-en"); + + public static final DeepgramModel AURA2IRIS_EN = new DeepgramModel(Value.AURA2IRIS_EN, "aura-2-iris-en"); + + public static final DeepgramModel AURA_ANGUS_EN = new DeepgramModel(Value.AURA_ANGUS_EN, "aura-angus-en"); + + public static final DeepgramModel FLUX_KAI_EN = new DeepgramModel(Value.FLUX_KAI_EN, "flux-kai-en"); + + public static final DeepgramModel FLUX_BRITTANY_EN = new DeepgramModel(Value.FLUX_BRITTANY_EN, "flux-brittany-en"); + + public static final DeepgramModel FLUX_SEAN_EN = new DeepgramModel(Value.FLUX_SEAN_EN, "flux-sean-en"); + + public static final DeepgramModel AURA2HELENA_EN = new DeepgramModel(Value.AURA2HELENA_EN, "aura-2-helena-en"); + + public static final DeepgramModel FLUX_HALEY_EN = new DeepgramModel(Value.FLUX_HALEY_EN, "flux-haley-en"); + + public static final DeepgramModel AURA_STELLA_EN = new DeepgramModel(Value.AURA_STELLA_EN, "aura-stella-en"); + + public static final DeepgramModel FLUX_JACK_EN = new DeepgramModel(Value.FLUX_JACK_EN, "flux-jack-en"); + + public static final DeepgramModel AURA2DRACO_EN = new DeepgramModel(Value.AURA2DRACO_EN, "aura-2-draco-en"); + + public static final DeepgramModel FLUX_PRIYA_EN = new DeepgramModel(Value.FLUX_PRIYA_EN, "flux-priya-en"); + + public static final DeepgramModel AURA_HELIOS_EN = new DeepgramModel(Value.AURA_HELIOS_EN, "aura-helios-en"); + + public static final DeepgramModel AURA2JANUS_EN = new DeepgramModel(Value.AURA2JANUS_EN, "aura-2-janus-en"); + + public static final DeepgramModel AURA2NESTOR_ES = new DeepgramModel(Value.AURA2NESTOR_ES, "aura-2-nestor-es"); + + public static final DeepgramModel AURA2ORION_EN = new DeepgramModel(Value.AURA2ORION_EN, "aura-2-orion-en"); + + public static final DeepgramModel AURA2MINERVA_EN = new DeepgramModel(Value.AURA2MINERVA_EN, "aura-2-minerva-en"); + + public static final DeepgramModel AURA2ALVARO_ES = new DeepgramModel(Value.AURA2ALVARO_ES, "aura-2-alvaro-es"); + + public static final DeepgramModel AURA_PERSEUS_EN = new DeepgramModel(Value.AURA_PERSEUS_EN, "aura-perseus-en"); + + public static final DeepgramModel AURA2VESTA_EN = new DeepgramModel(Value.AURA2VESTA_EN, "aura-2-vesta-en"); + + public static final DeepgramModel FLUX_COLE_EN = new DeepgramModel(Value.FLUX_COLE_EN, "flux-cole-en"); + + public static final DeepgramModel AURA2ZEUS_EN = new DeepgramModel(Value.AURA2ZEUS_EN, "aura-2-zeus-en"); + + public static final DeepgramModel AURA2ORPHEUS_EN = new DeepgramModel(Value.AURA2ORPHEUS_EN, "aura-2-orpheus-en"); + + public static final DeepgramModel AURA2THALIA_EN = new DeepgramModel(Value.AURA2THALIA_EN, "aura-2-thalia-en"); + + public static final DeepgramModel FLUX_NAVEEN_EN = new DeepgramModel(Value.FLUX_NAVEEN_EN, "flux-naveen-en"); + + public static final DeepgramModel AURA2ASTERIA_EN = new DeepgramModel(Value.AURA2ASTERIA_EN, "aura-2-asteria-en"); + + public static final DeepgramModel AURA2SELENA_ES = new DeepgramModel(Value.AURA2SELENA_ES, "aura-2-selena-es"); + + public static final DeepgramModel FLUX_MARCELO_EN = new DeepgramModel(Value.FLUX_MARCELO_EN, "flux-marcelo-en"); + + public static final DeepgramModel FLUX_ELISE_EN = new DeepgramModel(Value.FLUX_ELISE_EN, "flux-elise-en"); + + public static final DeepgramModel FLUX_ALEXIS_EN = new DeepgramModel(Value.FLUX_ALEXIS_EN, "flux-alexis-en"); + + public static final DeepgramModel AURA_LUNA_EN = new DeepgramModel(Value.AURA_LUNA_EN, "aura-luna-en"); + + public static final DeepgramModel AURA_ORPHEUS_EN = new DeepgramModel(Value.AURA_ORPHEUS_EN, "aura-orpheus-en"); + + public static final DeepgramModel AURA2AMALTHEA_EN = + new DeepgramModel(Value.AURA2AMALTHEA_EN, "aura-2-amalthea-en"); + + public static final DeepgramModel FLUX_DONOVAN_EN = new DeepgramModel(Value.FLUX_DONOVAN_EN, "flux-donovan-en"); + + public static final DeepgramModel AURA2SELENE_EN = new DeepgramModel(Value.AURA2SELENE_EN, "aura-2-selene-en"); + + public static final DeepgramModel FLUX_TANNER_EN = new DeepgramModel(Value.FLUX_TANNER_EN, "flux-tanner-en"); + + public static final DeepgramModel FLUX_SHARON_EN = new DeepgramModel(Value.FLUX_SHARON_EN, "flux-sharon-en"); + + public static final DeepgramModel AURA2LUNA_EN = new DeepgramModel(Value.AURA2LUNA_EN, "aura-2-luna-en"); + + public static final DeepgramModel AURA2PHOEBE_EN = new DeepgramModel(Value.AURA2PHOEBE_EN, "aura-2-phoebe-en"); + + public static final DeepgramModel FLUX_HANNAH_EN = new DeepgramModel(Value.FLUX_HANNAH_EN, "flux-hannah-en"); + + public static final DeepgramModel AURA2DIANA_ES = new DeepgramModel(Value.AURA2DIANA_ES, "aura-2-diana-es"); + + public static final DeepgramModel AURA_ORION_EN = new DeepgramModel(Value.AURA_ORION_EN, "aura-orion-en"); + + public static final DeepgramModel AURA2ATHENA_EN = new DeepgramModel(Value.AURA2ATHENA_EN, "aura-2-athena-en"); + + public static final DeepgramModel AURA_ARCAS_EN = new DeepgramModel(Value.AURA_ARCAS_EN, "aura-arcas-en"); + + public static final DeepgramModel AURA2THEIA_EN = new DeepgramModel(Value.AURA2THEIA_EN, "aura-2-theia-en"); + + public static final DeepgramModel FLUX_WADE_EN = new DeepgramModel(Value.FLUX_WADE_EN, "flux-wade-en"); + + public static final DeepgramModel FLUX_HEATHER_EN = new DeepgramModel(Value.FLUX_HEATHER_EN, "flux-heather-en"); + + public static final DeepgramModel FLUX_CLIFF_EN = new DeepgramModel(Value.FLUX_CLIFF_EN, "flux-cliff-en"); + + public static final DeepgramModel AURA2JUPITER_EN = new DeepgramModel(Value.AURA2JUPITER_EN, "aura-2-jupiter-en"); + + public static final DeepgramModel FLUX_KIT_EN = new DeepgramModel(Value.FLUX_KIT_EN, "flux-kit-en"); + + public static final DeepgramModel AURA2AQUILA_ES = new DeepgramModel(Value.AURA2AQUILA_ES, "aura-2-aquila-es"); + + public static final DeepgramModel AURA2CORA_EN = new DeepgramModel(Value.AURA2CORA_EN, "aura-2-cora-en"); + + public static final DeepgramModel AURA2CORDELIA_EN = + new DeepgramModel(Value.AURA2CORDELIA_EN, "aura-2-cordelia-en"); + + public static final DeepgramModel AURA2ATLAS_EN = new DeepgramModel(Value.AURA2ATLAS_EN, "aura-2-atlas-en"); + + public static final DeepgramModel AURA2HYPERION_EN = + new DeepgramModel(Value.AURA2HYPERION_EN, "aura-2-hyperion-en"); + + public static final DeepgramModel AURA2CELESTE_ES = new DeepgramModel(Value.AURA2CELESTE_ES, "aura-2-celeste-es"); + + public static final DeepgramModel AURA2PLUTO_EN = new DeepgramModel(Value.AURA2PLUTO_EN, "aura-2-pluto-en"); + + public static final DeepgramModel FLUX_BREE_EN = new DeepgramModel(Value.FLUX_BREE_EN, "flux-bree-en"); + + public static final DeepgramModel AURA2ARCAS_EN = new DeepgramModel(Value.AURA2ARCAS_EN, "aura-2-arcas-en"); + + public static final DeepgramModel FLUX_COLIN_EN = new DeepgramModel(Value.FLUX_COLIN_EN, "flux-colin-en"); + + public static final DeepgramModel AURA_ATHENA_EN = new DeepgramModel(Value.AURA_ATHENA_EN, "aura-athena-en"); + + public static final DeepgramModel AURA2ODYSSEUS_EN = + new DeepgramModel(Value.AURA2ODYSSEUS_EN, "aura-2-odysseus-en"); + + public static final DeepgramModel AURA2PANDORA_EN = new DeepgramModel(Value.AURA2PANDORA_EN, "aura-2-pandora-en"); + + public static final DeepgramModel FLUX_MILES_EN = new DeepgramModel(Value.FLUX_MILES_EN, "flux-miles-en"); + + public static final DeepgramModel AURA_ASTERIA_EN = new DeepgramModel(Value.AURA_ASTERIA_EN, "aura-asteria-en"); + + public static final DeepgramModel AURA2ELECTRA_EN = new DeepgramModel(Value.AURA2ELECTRA_EN, "aura-2-electra-en"); + + public static final DeepgramModel FLUX_GEMMA_EN = new DeepgramModel(Value.FLUX_GEMMA_EN, "flux-gemma-en"); + + public static final DeepgramModel AURA2ESTRELLA_ES = + new DeepgramModel(Value.AURA2ESTRELLA_ES, "aura-2-estrella-es"); + + private final Value value; + + private final String string; + + DeepgramModel(Value value, String string) { + this.value = value; + this.string = string; + } + + public Value getEnumValue() { + return value; + } + + @java.lang.Override + @JsonValue + public String toString() { + return this.string; + } + + @java.lang.Override + public boolean equals(Object other) { + return (this == other) + || (other instanceof DeepgramModel && this.string.equals(((DeepgramModel) other).string)); + } + + @java.lang.Override + public int hashCode() { + return this.string.hashCode(); + } + + public T visit(Visitor visitor) { + switch (value) { + case AURA2SIRIO_ES: + return visitor.visitAura2SirioEs(); + case AURA2HERA_EN: + return visitor.visitAura2HeraEn(); + case FLUX_KELSEY_EN: + return visitor.visitFluxKelseyEn(); + case FLUX_RENEE_EN: + return visitor.visitFluxReneeEn(); + case FLUX_SIENNA_EN: + return visitor.visitFluxSiennaEn(); + case AURA2HARMONIA_EN: + return visitor.visitAura2HarmoniaEn(); + case AURA2CARINA_ES: + return visitor.visitAura2CarinaEs(); + case FLUX_MEENA_EN: + return visitor.visitFluxMeenaEn(); + case FLUX_MEGHAN_EN: + return visitor.visitFluxMeghanEn(); + case AURA_ZEUS_EN: + return visitor.visitAuraZeusEn(); + case AURA2HERMES_EN: + return visitor.visitAura2HermesEn(); + case FLUX_CONOR_EN: + return visitor.visitFluxConorEn(); + case AURA2NEPTUNE_EN: + return visitor.visitAura2NeptuneEn(); + case FLUX_BROOKE_EN: + return visitor.visitFluxBrookeEn(); + case FLUX_RUFUS_EN: + return visitor.visitFluxRufusEn(); + case FLUX_WES_EN: + return visitor.visitFluxWesEn(); + case AURA2CALLISTA_EN: + return visitor.visitAura2CallistaEn(); + case AURA2AURORA_EN: + return visitor.visitAura2AuroraEn(); + case AURA2OPHELIA_EN: + return visitor.visitAura2OpheliaEn(); + case FLUX_BRUCE_EN: + return visitor.visitFluxBruceEn(); + case AURA2APOLLO_EN: + return visitor.visitAura2ApolloEn(); + case AURA2DELIA_EN: + return visitor.visitAura2DeliaEn(); + case AURA2MARS_EN: + return visitor.visitAura2MarsEn(); + case AURA_HERA_EN: + return visitor.visitAuraHeraEn(); + case FLUX_MAEVE_EN: + return visitor.visitFluxMaeveEn(); + case FLUX_MARCUS_EN: + return visitor.visitFluxMarcusEn(); + case AURA2ARIES_EN: + return visitor.visitAura2AriesEn(); + case FLUX_PAIGE_EN: + return visitor.visitFluxPaigeEn(); + case AURA2JUNO_EN: + return visitor.visitAura2JunoEn(); + case FLUX_DREW_EN: + return visitor.visitFluxDrewEn(); + case AURA2JAVIER_ES: + return visitor.visitAura2JavierEs(); + case AURA2ANDROMEDA_EN: + return visitor.visitAura2AndromedaEn(); + case AURA2SATURN_EN: + return visitor.visitAura2SaturnEn(); + case AURA2IRIS_EN: + return visitor.visitAura2IrisEn(); + case AURA_ANGUS_EN: + return visitor.visitAuraAngusEn(); + case FLUX_KAI_EN: + return visitor.visitFluxKaiEn(); + case FLUX_BRITTANY_EN: + return visitor.visitFluxBrittanyEn(); + case FLUX_SEAN_EN: + return visitor.visitFluxSeanEn(); + case AURA2HELENA_EN: + return visitor.visitAura2HelenaEn(); + case FLUX_HALEY_EN: + return visitor.visitFluxHaleyEn(); + case AURA_STELLA_EN: + return visitor.visitAuraStellaEn(); + case FLUX_JACK_EN: + return visitor.visitFluxJackEn(); + case AURA2DRACO_EN: + return visitor.visitAura2DracoEn(); + case FLUX_PRIYA_EN: + return visitor.visitFluxPriyaEn(); + case AURA_HELIOS_EN: + return visitor.visitAuraHeliosEn(); + case AURA2JANUS_EN: + return visitor.visitAura2JanusEn(); + case AURA2NESTOR_ES: + return visitor.visitAura2NestorEs(); + case AURA2ORION_EN: + return visitor.visitAura2OrionEn(); + case AURA2MINERVA_EN: + return visitor.visitAura2MinervaEn(); + case AURA2ALVARO_ES: + return visitor.visitAura2AlvaroEs(); + case AURA_PERSEUS_EN: + return visitor.visitAuraPerseusEn(); + case AURA2VESTA_EN: + return visitor.visitAura2VestaEn(); + case FLUX_COLE_EN: + return visitor.visitFluxColeEn(); + case AURA2ZEUS_EN: + return visitor.visitAura2ZeusEn(); + case AURA2ORPHEUS_EN: + return visitor.visitAura2OrpheusEn(); + case AURA2THALIA_EN: + return visitor.visitAura2ThaliaEn(); + case FLUX_NAVEEN_EN: + return visitor.visitFluxNaveenEn(); + case AURA2ASTERIA_EN: + return visitor.visitAura2AsteriaEn(); + case AURA2SELENA_ES: + return visitor.visitAura2SelenaEs(); + case FLUX_MARCELO_EN: + return visitor.visitFluxMarceloEn(); + case FLUX_ELISE_EN: + return visitor.visitFluxEliseEn(); + case FLUX_ALEXIS_EN: + return visitor.visitFluxAlexisEn(); + case AURA_LUNA_EN: + return visitor.visitAuraLunaEn(); + case AURA_ORPHEUS_EN: + return visitor.visitAuraOrpheusEn(); + case AURA2AMALTHEA_EN: + return visitor.visitAura2AmaltheaEn(); + case FLUX_DONOVAN_EN: + return visitor.visitFluxDonovanEn(); + case AURA2SELENE_EN: + return visitor.visitAura2SeleneEn(); + case FLUX_TANNER_EN: + return visitor.visitFluxTannerEn(); + case FLUX_SHARON_EN: + return visitor.visitFluxSharonEn(); + case AURA2LUNA_EN: + return visitor.visitAura2LunaEn(); + case AURA2PHOEBE_EN: + return visitor.visitAura2PhoebeEn(); + case FLUX_HANNAH_EN: + return visitor.visitFluxHannahEn(); + case AURA2DIANA_ES: + return visitor.visitAura2DianaEs(); + case AURA_ORION_EN: + return visitor.visitAuraOrionEn(); + case AURA2ATHENA_EN: + return visitor.visitAura2AthenaEn(); + case AURA_ARCAS_EN: + return visitor.visitAuraArcasEn(); + case AURA2THEIA_EN: + return visitor.visitAura2TheiaEn(); + case FLUX_WADE_EN: + return visitor.visitFluxWadeEn(); + case FLUX_HEATHER_EN: + return visitor.visitFluxHeatherEn(); + case FLUX_CLIFF_EN: + return visitor.visitFluxCliffEn(); + case AURA2JUPITER_EN: + return visitor.visitAura2JupiterEn(); + case FLUX_KIT_EN: + return visitor.visitFluxKitEn(); + case AURA2AQUILA_ES: + return visitor.visitAura2AquilaEs(); + case AURA2CORA_EN: + return visitor.visitAura2CoraEn(); + case AURA2CORDELIA_EN: + return visitor.visitAura2CordeliaEn(); + case AURA2ATLAS_EN: + return visitor.visitAura2AtlasEn(); + case AURA2HYPERION_EN: + return visitor.visitAura2HyperionEn(); + case AURA2CELESTE_ES: + return visitor.visitAura2CelesteEs(); + case AURA2PLUTO_EN: + return visitor.visitAura2PlutoEn(); + case FLUX_BREE_EN: + return visitor.visitFluxBreeEn(); + case AURA2ARCAS_EN: + return visitor.visitAura2ArcasEn(); + case FLUX_COLIN_EN: + return visitor.visitFluxColinEn(); + case AURA_ATHENA_EN: + return visitor.visitAuraAthenaEn(); + case AURA2ODYSSEUS_EN: + return visitor.visitAura2OdysseusEn(); + case AURA2PANDORA_EN: + return visitor.visitAura2PandoraEn(); + case FLUX_MILES_EN: + return visitor.visitFluxMilesEn(); + case AURA_ASTERIA_EN: + return visitor.visitAuraAsteriaEn(); + case AURA2ELECTRA_EN: + return visitor.visitAura2ElectraEn(); + case FLUX_GEMMA_EN: + return visitor.visitFluxGemmaEn(); + case AURA2ESTRELLA_ES: + return visitor.visitAura2EstrellaEs(); + case UNKNOWN: + default: + return visitor.visitUnknown(string); + } + } + + @JsonCreator(mode = JsonCreator.Mode.DELEGATING) + public static DeepgramModel valueOf(String value) { + switch (value) { + case "aura-2-sirio-es": + return AURA2SIRIO_ES; + case "aura-2-hera-en": + return AURA2HERA_EN; + case "flux-kelsey-en": + return FLUX_KELSEY_EN; + case "flux-renee-en": + return FLUX_RENEE_EN; + case "flux-sienna-en": + return FLUX_SIENNA_EN; + case "aura-2-harmonia-en": + return AURA2HARMONIA_EN; + case "aura-2-carina-es": + return AURA2CARINA_ES; + case "flux-meena-en": + return FLUX_MEENA_EN; + case "flux-meghan-en": + return FLUX_MEGHAN_EN; + case "aura-zeus-en": + return AURA_ZEUS_EN; + case "aura-2-hermes-en": + return AURA2HERMES_EN; + case "flux-conor-en": + return FLUX_CONOR_EN; + case "aura-2-neptune-en": + return AURA2NEPTUNE_EN; + case "flux-brooke-en": + return FLUX_BROOKE_EN; + case "flux-rufus-en": + return FLUX_RUFUS_EN; + case "flux-wes-en": + return FLUX_WES_EN; + case "aura-2-callista-en": + return AURA2CALLISTA_EN; + case "aura-2-aurora-en": + return AURA2AURORA_EN; + case "aura-2-ophelia-en": + return AURA2OPHELIA_EN; + case "flux-bruce-en": + return FLUX_BRUCE_EN; + case "aura-2-apollo-en": + return AURA2APOLLO_EN; + case "aura-2-delia-en": + return AURA2DELIA_EN; + case "aura-2-mars-en": + return AURA2MARS_EN; + case "aura-hera-en": + return AURA_HERA_EN; + case "flux-maeve-en": + return FLUX_MAEVE_EN; + case "flux-marcus-en": + return FLUX_MARCUS_EN; + case "aura-2-aries-en": + return AURA2ARIES_EN; + case "flux-paige-en": + return FLUX_PAIGE_EN; + case "aura-2-juno-en": + return AURA2JUNO_EN; + case "flux-drew-en": + return FLUX_DREW_EN; + case "aura-2-javier-es": + return AURA2JAVIER_ES; + case "aura-2-andromeda-en": + return AURA2ANDROMEDA_EN; + case "aura-2-saturn-en": + return AURA2SATURN_EN; + case "aura-2-iris-en": + return AURA2IRIS_EN; + case "aura-angus-en": + return AURA_ANGUS_EN; + case "flux-kai-en": + return FLUX_KAI_EN; + case "flux-brittany-en": + return FLUX_BRITTANY_EN; + case "flux-sean-en": + return FLUX_SEAN_EN; + case "aura-2-helena-en": + return AURA2HELENA_EN; + case "flux-haley-en": + return FLUX_HALEY_EN; + case "aura-stella-en": + return AURA_STELLA_EN; + case "flux-jack-en": + return FLUX_JACK_EN; + case "aura-2-draco-en": + return AURA2DRACO_EN; + case "flux-priya-en": + return FLUX_PRIYA_EN; + case "aura-helios-en": + return AURA_HELIOS_EN; + case "aura-2-janus-en": + return AURA2JANUS_EN; + case "aura-2-nestor-es": + return AURA2NESTOR_ES; + case "aura-2-orion-en": + return AURA2ORION_EN; + case "aura-2-minerva-en": + return AURA2MINERVA_EN; + case "aura-2-alvaro-es": + return AURA2ALVARO_ES; + case "aura-perseus-en": + return AURA_PERSEUS_EN; + case "aura-2-vesta-en": + return AURA2VESTA_EN; + case "flux-cole-en": + return FLUX_COLE_EN; + case "aura-2-zeus-en": + return AURA2ZEUS_EN; + case "aura-2-orpheus-en": + return AURA2ORPHEUS_EN; + case "aura-2-thalia-en": + return AURA2THALIA_EN; + case "flux-naveen-en": + return FLUX_NAVEEN_EN; + case "aura-2-asteria-en": + return AURA2ASTERIA_EN; + case "aura-2-selena-es": + return AURA2SELENA_ES; + case "flux-marcelo-en": + return FLUX_MARCELO_EN; + case "flux-elise-en": + return FLUX_ELISE_EN; + case "flux-alexis-en": + return FLUX_ALEXIS_EN; + case "aura-luna-en": + return AURA_LUNA_EN; + case "aura-orpheus-en": + return AURA_ORPHEUS_EN; + case "aura-2-amalthea-en": + return AURA2AMALTHEA_EN; + case "flux-donovan-en": + return FLUX_DONOVAN_EN; + case "aura-2-selene-en": + return AURA2SELENE_EN; + case "flux-tanner-en": + return FLUX_TANNER_EN; + case "flux-sharon-en": + return FLUX_SHARON_EN; + case "aura-2-luna-en": + return AURA2LUNA_EN; + case "aura-2-phoebe-en": + return AURA2PHOEBE_EN; + case "flux-hannah-en": + return FLUX_HANNAH_EN; + case "aura-2-diana-es": + return AURA2DIANA_ES; + case "aura-orion-en": + return AURA_ORION_EN; + case "aura-2-athena-en": + return AURA2ATHENA_EN; + case "aura-arcas-en": + return AURA_ARCAS_EN; + case "aura-2-theia-en": + return AURA2THEIA_EN; + case "flux-wade-en": + return FLUX_WADE_EN; + case "flux-heather-en": + return FLUX_HEATHER_EN; + case "flux-cliff-en": + return FLUX_CLIFF_EN; + case "aura-2-jupiter-en": + return AURA2JUPITER_EN; + case "flux-kit-en": + return FLUX_KIT_EN; + case "aura-2-aquila-es": + return AURA2AQUILA_ES; + case "aura-2-cora-en": + return AURA2CORA_EN; + case "aura-2-cordelia-en": + return AURA2CORDELIA_EN; + case "aura-2-atlas-en": + return AURA2ATLAS_EN; + case "aura-2-hyperion-en": + return AURA2HYPERION_EN; + case "aura-2-celeste-es": + return AURA2CELESTE_ES; + case "aura-2-pluto-en": + return AURA2PLUTO_EN; + case "flux-bree-en": + return FLUX_BREE_EN; + case "aura-2-arcas-en": + return AURA2ARCAS_EN; + case "flux-colin-en": + return FLUX_COLIN_EN; + case "aura-athena-en": + return AURA_ATHENA_EN; + case "aura-2-odysseus-en": + return AURA2ODYSSEUS_EN; + case "aura-2-pandora-en": + return AURA2PANDORA_EN; + case "flux-miles-en": + return FLUX_MILES_EN; + case "aura-asteria-en": + return AURA_ASTERIA_EN; + case "aura-2-electra-en": + return AURA2ELECTRA_EN; + case "flux-gemma-en": + return FLUX_GEMMA_EN; + case "aura-2-estrella-es": + return AURA2ESTRELLA_ES; + default: + return new DeepgramModel(Value.UNKNOWN, value); + } + } + + public enum Value { + AURA_ASTERIA_EN, + + AURA_LUNA_EN, + + AURA_STELLA_EN, + + AURA_ATHENA_EN, + + AURA_HERA_EN, + + AURA_ORION_EN, + + AURA_ARCAS_EN, + + AURA_PERSEUS_EN, + + AURA_ANGUS_EN, + + AURA_ORPHEUS_EN, + + AURA_HELIOS_EN, + + AURA_ZEUS_EN, + + AURA2AMALTHEA_EN, + + AURA2ANDROMEDA_EN, + + AURA2APOLLO_EN, + + AURA2ARCAS_EN, + + AURA2ARIES_EN, + + AURA2ASTERIA_EN, + + AURA2ATHENA_EN, + + AURA2ATLAS_EN, + + AURA2AURORA_EN, + + AURA2CALLISTA_EN, + + AURA2CORA_EN, + + AURA2CORDELIA_EN, + + AURA2DELIA_EN, + + AURA2DRACO_EN, + + AURA2ELECTRA_EN, + + AURA2HARMONIA_EN, + + AURA2HELENA_EN, + + AURA2HERA_EN, + + AURA2HERMES_EN, + + AURA2HYPERION_EN, + + AURA2IRIS_EN, + + AURA2JANUS_EN, + + AURA2JUNO_EN, + + AURA2JUPITER_EN, + + AURA2LUNA_EN, + + AURA2MARS_EN, + + AURA2MINERVA_EN, + + AURA2NEPTUNE_EN, + + AURA2ODYSSEUS_EN, + + AURA2OPHELIA_EN, + + AURA2ORION_EN, + + AURA2ORPHEUS_EN, + + AURA2PANDORA_EN, + + AURA2PHOEBE_EN, + + AURA2PLUTO_EN, + + AURA2SATURN_EN, + + AURA2SELENE_EN, + + AURA2THALIA_EN, + + AURA2THEIA_EN, + + AURA2VESTA_EN, + + AURA2ZEUS_EN, + + AURA2SIRIO_ES, + + AURA2NESTOR_ES, + + AURA2CARINA_ES, + + AURA2CELESTE_ES, + + AURA2ALVARO_ES, + + AURA2DIANA_ES, + + AURA2AQUILA_ES, + + AURA2SELENA_ES, + + AURA2ESTRELLA_ES, + + AURA2JAVIER_ES, + + FLUX_ALEXIS_EN, + + FLUX_BREE_EN, + + FLUX_BRITTANY_EN, + + FLUX_BROOKE_EN, + + FLUX_BRUCE_EN, + + FLUX_CLIFF_EN, + + FLUX_COLE_EN, + + FLUX_COLIN_EN, + + FLUX_CONOR_EN, + + FLUX_DONOVAN_EN, + + FLUX_DREW_EN, + + FLUX_ELISE_EN, + + FLUX_GEMMA_EN, + + FLUX_HALEY_EN, + + FLUX_HANNAH_EN, + + FLUX_HEATHER_EN, + + FLUX_JACK_EN, + + FLUX_KAI_EN, + + FLUX_KELSEY_EN, + + FLUX_KIT_EN, + + FLUX_MAEVE_EN, + + FLUX_MARCELO_EN, + + FLUX_MARCUS_EN, + + FLUX_MEENA_EN, + + FLUX_MEGHAN_EN, + + FLUX_MILES_EN, + + FLUX_NAVEEN_EN, + + FLUX_PAIGE_EN, + + FLUX_PRIYA_EN, + + FLUX_RENEE_EN, + + FLUX_RUFUS_EN, + + FLUX_SEAN_EN, + + FLUX_SHARON_EN, + + FLUX_SIENNA_EN, + + FLUX_TANNER_EN, + + FLUX_WADE_EN, + + FLUX_WES_EN, + + UNKNOWN + } + + public interface Visitor { + T visitAuraAsteriaEn(); + + T visitAuraLunaEn(); + + T visitAuraStellaEn(); + + T visitAuraAthenaEn(); + + T visitAuraHeraEn(); + + T visitAuraOrionEn(); + + T visitAuraArcasEn(); + + T visitAuraPerseusEn(); + + T visitAuraAngusEn(); + + T visitAuraOrpheusEn(); + + T visitAuraHeliosEn(); + + T visitAuraZeusEn(); + + T visitAura2AmaltheaEn(); + + T visitAura2AndromedaEn(); + + T visitAura2ApolloEn(); + + T visitAura2ArcasEn(); + + T visitAura2AriesEn(); + + T visitAura2AsteriaEn(); + + T visitAura2AthenaEn(); + + T visitAura2AtlasEn(); + + T visitAura2AuroraEn(); + + T visitAura2CallistaEn(); + + T visitAura2CoraEn(); + + T visitAura2CordeliaEn(); + + T visitAura2DeliaEn(); + + T visitAura2DracoEn(); + + T visitAura2ElectraEn(); + + T visitAura2HarmoniaEn(); + + T visitAura2HelenaEn(); + + T visitAura2HeraEn(); + + T visitAura2HermesEn(); + + T visitAura2HyperionEn(); + + T visitAura2IrisEn(); + + T visitAura2JanusEn(); + + T visitAura2JunoEn(); + + T visitAura2JupiterEn(); + + T visitAura2LunaEn(); + + T visitAura2MarsEn(); + + T visitAura2MinervaEn(); + + T visitAura2NeptuneEn(); + + T visitAura2OdysseusEn(); + + T visitAura2OpheliaEn(); + + T visitAura2OrionEn(); + + T visitAura2OrpheusEn(); + + T visitAura2PandoraEn(); + + T visitAura2PhoebeEn(); + + T visitAura2PlutoEn(); + + T visitAura2SaturnEn(); + + T visitAura2SeleneEn(); + + T visitAura2ThaliaEn(); + + T visitAura2TheiaEn(); + + T visitAura2VestaEn(); + + T visitAura2ZeusEn(); + + T visitAura2SirioEs(); + + T visitAura2NestorEs(); + + T visitAura2CarinaEs(); + + T visitAura2CelesteEs(); + + T visitAura2AlvaroEs(); + + T visitAura2DianaEs(); + + T visitAura2AquilaEs(); + + T visitAura2SelenaEs(); + + T visitAura2EstrellaEs(); + + T visitAura2JavierEs(); + + T visitFluxAlexisEn(); + + T visitFluxBreeEn(); + + T visitFluxBrittanyEn(); + + T visitFluxBrookeEn(); + + T visitFluxBruceEn(); + + T visitFluxCliffEn(); + + T visitFluxColeEn(); + + T visitFluxColinEn(); + + T visitFluxConorEn(); + + T visitFluxDonovanEn(); + + T visitFluxDrewEn(); + + T visitFluxEliseEn(); + + T visitFluxGemmaEn(); + + T visitFluxHaleyEn(); + + T visitFluxHannahEn(); + + T visitFluxHeatherEn(); + + T visitFluxJackEn(); + + T visitFluxKaiEn(); + + T visitFluxKelseyEn(); + + T visitFluxKitEn(); + + T visitFluxMaeveEn(); + + T visitFluxMarceloEn(); + + T visitFluxMarcusEn(); + + T visitFluxMeenaEn(); + + T visitFluxMeghanEn(); + + T visitFluxMilesEn(); + + T visitFluxNaveenEn(); + + T visitFluxPaigeEn(); + + T visitFluxPriyaEn(); + + T visitFluxReneeEn(); + + T visitFluxRufusEn(); + + T visitFluxSeanEn(); + + T visitFluxSharonEn(); + + T visitFluxSiennaEn(); + + T visitFluxTannerEn(); + + T visitFluxWadeEn(); + + T visitFluxWesEn(); + + T visitUnknown(String unknownType); + } +} From 089bc677f7f8e9878d0d0ca451f8ba0f0c4c0727 Mon Sep 17 00:00:00 2001 From: "fern-api[bot]" <115122769+fern-api[bot]@users.noreply.github.com> Date: Wed, 7 Oct 2026 10:49:05 +0000 Subject: [PATCH 3/7] SDK regeneration --- .fern/metadata.json | 6 +- .../java/com/deepgram/core/ClientOptions.java | 18 +- .../core/ReconnectingWebSocketListener.java | 61 ++-- .../agent/v1/types/AgentV1AgentAudioDone.java | 5 - .../types/AgentV1CustomFromThinkProvider.java | 125 ++++++++ .../types/AgentV1CustomToThinkProvider.java | 125 ++++++++ .../agent/v1/types/AgentV1ForceEndTurn.java | 5 - .../agent/v1/types/AgentV1KeepAlive.java | 5 - .../agent/v1/types/AgentV1ListenUpdated.java | 5 - .../agent/v1/types/AgentV1PromptUpdated.java | 5 - ...tV1SettingsAgentContextListenProvider.java | 2 +- .../AgentV1SettingsAgentListenProvider.java | 2 +- .../v1/types/AgentV1SettingsApplied.java | 5 - .../agent/v1/types/AgentV1SpeakUpdated.java | 5 - .../agent/v1/types/AgentV1ThinkUpdated.java | 5 - .../AgentV1UpdateListenListenProvider.java | 2 +- .../v1/types/AgentV1UserStartedSpeaking.java | 5 - .../agent/v1/websocket/V1WebSocketClient.java | 36 +++ .../listen/v1/types/ListenV1Configure.java | 165 +++++++++++ .../listen/v1/types/ListenV1Error.java | 267 ++++++++++++++++++ .../v1/websocket/V1WebSocketClient.java | 61 +++- .../listen/v2/types/ListenV2CloseStream.java | 5 - .../listen/v2/types/ListenV2ForceEndTurn.java | 5 - .../v2/websocket/V2WebSocketClient.java | 47 +-- .../speak/v1/websocket/V1WebSocketClient.java | 8 - .../speak/v2/types/SpeakV2Close.java | 5 - .../speak/v2/types/SpeakV2Flush.java | 5 - .../speak/v2/websocket/V2WebSocketClient.java | 17 +- .../com/deepgram/types/DeepgramModel.java | 10 - 29 files changed, 820 insertions(+), 197 deletions(-) create mode 100644 src/main/java/com/deepgram/resources/agent/v1/types/AgentV1CustomFromThinkProvider.java create mode 100644 src/main/java/com/deepgram/resources/agent/v1/types/AgentV1CustomToThinkProvider.java create mode 100644 src/main/java/com/deepgram/resources/listen/v1/types/ListenV1Configure.java create mode 100644 src/main/java/com/deepgram/resources/listen/v1/types/ListenV1Error.java diff --git a/.fern/metadata.json b/.fern/metadata.json index f5b9f8f..1cb7167 100644 --- a/.fern/metadata.json +++ b/.fern/metadata.json @@ -12,8 +12,8 @@ "enable-wire-tests": true, "runtime-version": true }, - "originGitCommit": "e252995bcdb25f3e12d46ae342a2b93d0c1085f9", + "originGitCommit": "9dcbd3d5e8d36420319e1b33988613ad2cb2be10", "originGitCommitIsDirty": true, "invokedBy": "manual", - "sdkVersion": "0.11.0" -} + "sdkVersion": "0.11.1" +} \ No newline at end of file diff --git a/src/main/java/com/deepgram/core/ClientOptions.java b/src/main/java/com/deepgram/core/ClientOptions.java index 4d7a26a..b9ef146 100644 --- a/src/main/java/com/deepgram/core/ClientOptions.java +++ b/src/main/java/com/deepgram/core/ClientOptions.java @@ -63,10 +63,10 @@ private ClientOptions( this.headers.putAll(headers); this.headers.putAll(new HashMap() { { - put("User-Agent", "com.deepgram:deepgram-java-sdk/0.11.0"); // x-release-please-version + put("User-Agent", "com.deepgram.deepgram-java-sdk/" + getSdkVersion()); put("X-Fern-Language", "JAVA"); put("X-Fern-SDK-Name", "com.deepgram:deepgram-java-sdk"); - put("X-Fern-SDK-Version", "0.11.0"); // x-release-please-version + put("X-Fern-SDK-Version", getSdkVersion()); } }); this.headerSuppliers = headerSuppliers; @@ -98,6 +98,11 @@ public Map headers(RequestOptions requestOptions) { return values; } + private static String getSdkVersion() { + String version = ClientOptions.class.getPackage().getImplementationVersion(); + return version != null ? version : "0.11.1"; + } + public int timeout(RequestOptions requestOptions) { if (requestOptions == null) { return this.timeout; @@ -144,11 +149,10 @@ public Optional retryJitterFactor() { * OkHttpClient itself; an OkHttpClient supplied via httpClient is left running, since the * caller owns its lifecycle. *

- * 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. + * In-flight calls are not cancelled or awaited, and any request issued after this method + * returns fails with a {@code RejectedExecutionException}. Options derived from this one via + * {@code Builder.from(...)} share the same dispatcher and connection pool, so closing either + * releases them for both. 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/core/ReconnectingWebSocketListener.java b/src/main/java/com/deepgram/core/ReconnectingWebSocketListener.java index 294316b..dc32da2 100644 --- a/src/main/java/com/deepgram/core/ReconnectingWebSocketListener.java +++ b/src/main/java/com/deepgram/core/ReconnectingWebSocketListener.java @@ -27,8 +27,13 @@ * Provides production-ready resilience for WebSocket connections. */ public abstract class ReconnectingWebSocketListener extends WebSocketListener { - // A single volatile reference keeps an override internally consistent. - private volatile ReconnectOptions activeOptions; + private final long minReconnectionDelayMs; + + private final long maxReconnectionDelayMs; + + private final double reconnectionDelayGrowFactor; + + private final int maxRetries; private final int maxEnqueuedMessages; @@ -76,30 +81,22 @@ public ReconnectingWebSocketListener( ReconnectingWebSocketListener.ReconnectOptions options, Supplier connectionSupplier, BooleanSupplier closedCheck) { - this.activeOptions = options; + this.minReconnectionDelayMs = options.minReconnectionDelayMs; + this.maxReconnectionDelayMs = options.maxReconnectionDelayMs; + this.reconnectionDelayGrowFactor = options.reconnectionDelayGrowFactor; + this.maxRetries = options.maxRetries; this.maxEnqueuedMessages = options.maxEnqueuedMessages; this.connectionSupplier = connectionSupplier; this.closedCheck = closedCheck; } - /** - * Replaces option-derived reconnect settings without rebuilding generated clients. - * - * @param options replacement options; {@code null} is a no-op - */ - public void applyOptionsOverride(ReconnectOptions options) { - if (options != null) { - this.activeOptions = options; - } - } - /** * Initiates a WebSocket connection with automatic reconnection enabled. * * Connection behavior: - * - Times out after {@code ReconnectOptions.connectionTimeoutMs} (default 4000ms) + * - Times out after 4000 milliseconds * - Thread-safe via atomic lock (returns immediately if connection in progress) - * - {@code maxRetries} counts retries only; {@code maxRetries(0)} means connect once, do not retry + * - Retry count not incremented for initial connection attempt * * Error handling: * - TimeoutException: Includes retry attempt context @@ -117,19 +114,18 @@ public void connect() { onWebSocketFailure(null, new IllegalStateException("root client has been closed"), null); return; } - ReconnectOptions options = this.activeOptions; - if (retryCount.get() > options.maxRetries) { + if (retryCount.get() >= maxRetries) { connectLock.set(false); return; } try { CompletableFuture connectionFuture = CompletableFuture.supplyAsync(connectionSupplier); try { - webSocket = connectionFuture.get(options.connectionTimeoutMs, MILLISECONDS); + webSocket = connectionFuture.get(4000, MILLISECONDS); } catch (TimeoutException e) { connectionFuture.cancel(true); TimeoutException timeoutError = - new TimeoutException("WebSocket connection timeout after " + options.connectionTimeoutMs + " milliseconds" + new TimeoutException("WebSocket connection timeout after " + 4000 + " milliseconds" + (retryCount.get() > 0 ? " (retry attempt #" + retryCount.get() + ")" : " (initial connection attempt)")); @@ -419,12 +415,10 @@ protected boolean shouldReconnectAfterClose(WebSocket webSocket, int code, Strin */ private long getNextDelay() { if (retryCount.get() == 1) { - return activeOptions.minReconnectionDelayMs; + return minReconnectionDelayMs; } - ReconnectOptions options = this.activeOptions; - long delay = (long) - (options.minReconnectionDelayMs * Math.pow(options.reconnectionDelayGrowFactor, retryCount.get() - 1)); - return Math.min(delay, options.maxReconnectionDelayMs); + long delay = (long) (minReconnectionDelayMs * Math.pow(reconnectionDelayGrowFactor, retryCount.get() - 1)); + return Math.min(delay, maxReconnectionDelayMs); } /** @@ -510,15 +504,12 @@ public static final class ReconnectOptions { public final int maxEnqueuedMessages; - public final long connectionTimeoutMs; - private ReconnectOptions(Builder builder) { this.minReconnectionDelayMs = builder.minReconnectionDelayMs; this.maxReconnectionDelayMs = builder.maxReconnectionDelayMs; this.reconnectionDelayGrowFactor = builder.reconnectionDelayGrowFactor; this.maxRetries = builder.maxRetries; this.maxEnqueuedMessages = builder.maxEnqueuedMessages; - this.connectionTimeoutMs = builder.connectionTimeoutMs; } public static Builder builder() { @@ -536,15 +527,12 @@ public static final class Builder { private int maxEnqueuedMessages; - private long connectionTimeoutMs; - public Builder() { this.minReconnectionDelayMs = 1000; this.maxReconnectionDelayMs = 10000; this.reconnectionDelayGrowFactor = 1.3; this.maxRetries = 2147483647; this.maxEnqueuedMessages = 1000; - this.connectionTimeoutMs = 4000; } public Builder minReconnectionDelayMs(long minReconnectionDelayMs) { @@ -572,14 +560,6 @@ public Builder maxEnqueuedMessages(int maxEnqueuedMessages) { return this; } - /** - * Sets the per-attempt connection timeout in milliseconds. Defaults to {@code 4000}. - */ - public Builder connectionTimeoutMs(long connectionTimeoutMs) { - this.connectionTimeoutMs = connectionTimeoutMs; - return this; - } - /** * Builds the ReconnectOptions with validation. * @@ -612,9 +592,6 @@ public ReconnectOptions build() { if (maxEnqueuedMessages < 0) { throw new IllegalArgumentException("maxEnqueuedMessages must be non-negative"); } - if (connectionTimeoutMs <= 0) { - throw new IllegalArgumentException("connectionTimeoutMs must be positive"); - } return new ReconnectOptions(this); } } diff --git a/src/main/java/com/deepgram/resources/agent/v1/types/AgentV1AgentAudioDone.java b/src/main/java/com/deepgram/resources/agent/v1/types/AgentV1AgentAudioDone.java index 5962f4b..71825ed 100644 --- a/src/main/java/com/deepgram/resources/agent/v1/types/AgentV1AgentAudioDone.java +++ b/src/main/java/com/deepgram/resources/agent/v1/types/AgentV1AgentAudioDone.java @@ -36,11 +36,6 @@ public boolean equals(Object other) { return other instanceof AgentV1AgentAudioDone; } - @java.lang.Override - public int hashCode() { - return getClass().hashCode(); - } - @JsonAnyGetter public Map getAdditionalProperties() { return this.additionalProperties; diff --git a/src/main/java/com/deepgram/resources/agent/v1/types/AgentV1CustomFromThinkProvider.java b/src/main/java/com/deepgram/resources/agent/v1/types/AgentV1CustomFromThinkProvider.java new file mode 100644 index 0000000..777078c --- /dev/null +++ b/src/main/java/com/deepgram/resources/agent/v1/types/AgentV1CustomFromThinkProvider.java @@ -0,0 +1,125 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ +package com.deepgram.resources.agent.v1.types; + +import com.deepgram.core.ObjectMappers; +import com.fasterxml.jackson.annotation.JsonAnyGetter; +import com.fasterxml.jackson.annotation.JsonAnySetter; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonSetter; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import java.util.HashMap; +import java.util.Map; +import java.util.Objects; + +@JsonInclude(JsonInclude.Include.NON_ABSENT) +@JsonDeserialize(builder = AgentV1CustomFromThinkProvider.Builder.class) +public final class AgentV1CustomFromThinkProvider { + private final Object content; + + private final Map additionalProperties; + + private AgentV1CustomFromThinkProvider(Object content, Map additionalProperties) { + this.content = content; + this.additionalProperties = additionalProperties; + } + + /** + * @return Message type identifier for a custom payload returned by the think provider + */ + @JsonProperty("type") + public String getType() { + return "__customFromThinkProvider"; + } + + @JsonProperty("content") + public Object getContent() { + return content; + } + + @java.lang.Override + public boolean equals(Object other) { + if (this == other) return true; + return other instanceof AgentV1CustomFromThinkProvider && equalTo((AgentV1CustomFromThinkProvider) other); + } + + @JsonAnyGetter + public Map getAdditionalProperties() { + return this.additionalProperties; + } + + private boolean equalTo(AgentV1CustomFromThinkProvider other) { + return content.equals(other.content); + } + + @java.lang.Override + public int hashCode() { + return Objects.hash(this.content); + } + + @java.lang.Override + public String toString() { + return ObjectMappers.stringify(this); + } + + public static ContentStage builder() { + return new Builder(); + } + + public interface ContentStage { + _FinalStage content(Object content); + + Builder from(AgentV1CustomFromThinkProvider other); + } + + public interface _FinalStage { + AgentV1CustomFromThinkProvider build(); + + _FinalStage additionalProperty(String key, Object value); + + _FinalStage additionalProperties(Map additionalProperties); + } + + @JsonIgnoreProperties(ignoreUnknown = true) + public static final class Builder implements ContentStage, _FinalStage { + private Object content; + + @JsonAnySetter + private Map additionalProperties = new HashMap<>(); + + private Builder() {} + + @java.lang.Override + public Builder from(AgentV1CustomFromThinkProvider other) { + content(other.getContent()); + return this; + } + + @java.lang.Override + @JsonSetter("content") + public _FinalStage content(Object content) { + this.content = content; + return this; + } + + @java.lang.Override + public AgentV1CustomFromThinkProvider build() { + return new AgentV1CustomFromThinkProvider(content, additionalProperties); + } + + @java.lang.Override + public Builder additionalProperty(String key, Object value) { + this.additionalProperties.put(key, value); + return this; + } + + @java.lang.Override + public Builder additionalProperties(Map additionalProperties) { + this.additionalProperties.putAll(additionalProperties); + return this; + } + } +} diff --git a/src/main/java/com/deepgram/resources/agent/v1/types/AgentV1CustomToThinkProvider.java b/src/main/java/com/deepgram/resources/agent/v1/types/AgentV1CustomToThinkProvider.java new file mode 100644 index 0000000..5dc5b4b --- /dev/null +++ b/src/main/java/com/deepgram/resources/agent/v1/types/AgentV1CustomToThinkProvider.java @@ -0,0 +1,125 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ +package com.deepgram.resources.agent.v1.types; + +import com.deepgram.core.ObjectMappers; +import com.fasterxml.jackson.annotation.JsonAnyGetter; +import com.fasterxml.jackson.annotation.JsonAnySetter; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonSetter; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import java.util.HashMap; +import java.util.Map; +import java.util.Objects; + +@JsonInclude(JsonInclude.Include.NON_ABSENT) +@JsonDeserialize(builder = AgentV1CustomToThinkProvider.Builder.class) +public final class AgentV1CustomToThinkProvider { + private final Object content; + + private final Map additionalProperties; + + private AgentV1CustomToThinkProvider(Object content, Map additionalProperties) { + this.content = content; + this.additionalProperties = additionalProperties; + } + + /** + * @return Message type identifier for sending a custom payload to the think provider + */ + @JsonProperty("type") + public String getType() { + return "__customToThinkProvider"; + } + + @JsonProperty("content") + public Object getContent() { + return content; + } + + @java.lang.Override + public boolean equals(Object other) { + if (this == other) return true; + return other instanceof AgentV1CustomToThinkProvider && equalTo((AgentV1CustomToThinkProvider) other); + } + + @JsonAnyGetter + public Map getAdditionalProperties() { + return this.additionalProperties; + } + + private boolean equalTo(AgentV1CustomToThinkProvider other) { + return content.equals(other.content); + } + + @java.lang.Override + public int hashCode() { + return Objects.hash(this.content); + } + + @java.lang.Override + public String toString() { + return ObjectMappers.stringify(this); + } + + public static ContentStage builder() { + return new Builder(); + } + + public interface ContentStage { + _FinalStage content(Object content); + + Builder from(AgentV1CustomToThinkProvider other); + } + + public interface _FinalStage { + AgentV1CustomToThinkProvider build(); + + _FinalStage additionalProperty(String key, Object value); + + _FinalStage additionalProperties(Map additionalProperties); + } + + @JsonIgnoreProperties(ignoreUnknown = true) + public static final class Builder implements ContentStage, _FinalStage { + private Object content; + + @JsonAnySetter + private Map additionalProperties = new HashMap<>(); + + private Builder() {} + + @java.lang.Override + public Builder from(AgentV1CustomToThinkProvider other) { + content(other.getContent()); + return this; + } + + @java.lang.Override + @JsonSetter("content") + public _FinalStage content(Object content) { + this.content = content; + return this; + } + + @java.lang.Override + public AgentV1CustomToThinkProvider build() { + return new AgentV1CustomToThinkProvider(content, additionalProperties); + } + + @java.lang.Override + public Builder additionalProperty(String key, Object value) { + this.additionalProperties.put(key, value); + return this; + } + + @java.lang.Override + public Builder additionalProperties(Map additionalProperties) { + this.additionalProperties.putAll(additionalProperties); + return this; + } + } +} diff --git a/src/main/java/com/deepgram/resources/agent/v1/types/AgentV1ForceEndTurn.java b/src/main/java/com/deepgram/resources/agent/v1/types/AgentV1ForceEndTurn.java index 9bd5f76..1672d2d 100644 --- a/src/main/java/com/deepgram/resources/agent/v1/types/AgentV1ForceEndTurn.java +++ b/src/main/java/com/deepgram/resources/agent/v1/types/AgentV1ForceEndTurn.java @@ -36,11 +36,6 @@ public boolean equals(Object other) { return other instanceof AgentV1ForceEndTurn; } - @java.lang.Override - public int hashCode() { - return getClass().hashCode(); - } - @JsonAnyGetter public Map getAdditionalProperties() { return this.additionalProperties; diff --git a/src/main/java/com/deepgram/resources/agent/v1/types/AgentV1KeepAlive.java b/src/main/java/com/deepgram/resources/agent/v1/types/AgentV1KeepAlive.java index f82f59e..7842266 100644 --- a/src/main/java/com/deepgram/resources/agent/v1/types/AgentV1KeepAlive.java +++ b/src/main/java/com/deepgram/resources/agent/v1/types/AgentV1KeepAlive.java @@ -36,11 +36,6 @@ public boolean equals(Object other) { return other instanceof AgentV1KeepAlive; } - @java.lang.Override - public int hashCode() { - return getClass().hashCode(); - } - @JsonAnyGetter public Map getAdditionalProperties() { return this.additionalProperties; diff --git a/src/main/java/com/deepgram/resources/agent/v1/types/AgentV1ListenUpdated.java b/src/main/java/com/deepgram/resources/agent/v1/types/AgentV1ListenUpdated.java index 43b8628..65878c9 100644 --- a/src/main/java/com/deepgram/resources/agent/v1/types/AgentV1ListenUpdated.java +++ b/src/main/java/com/deepgram/resources/agent/v1/types/AgentV1ListenUpdated.java @@ -36,11 +36,6 @@ public boolean equals(Object other) { return other instanceof AgentV1ListenUpdated; } - @java.lang.Override - public int hashCode() { - return getClass().hashCode(); - } - @JsonAnyGetter public Map getAdditionalProperties() { return this.additionalProperties; diff --git a/src/main/java/com/deepgram/resources/agent/v1/types/AgentV1PromptUpdated.java b/src/main/java/com/deepgram/resources/agent/v1/types/AgentV1PromptUpdated.java index e6efced..68d71cc 100644 --- a/src/main/java/com/deepgram/resources/agent/v1/types/AgentV1PromptUpdated.java +++ b/src/main/java/com/deepgram/resources/agent/v1/types/AgentV1PromptUpdated.java @@ -36,11 +36,6 @@ public boolean equals(Object other) { return other instanceof AgentV1PromptUpdated; } - @java.lang.Override - public int hashCode() { - return getClass().hashCode(); - } - @JsonAnyGetter public Map getAdditionalProperties() { return this.additionalProperties; diff --git a/src/main/java/com/deepgram/resources/agent/v1/types/AgentV1SettingsAgentContextListenProvider.java b/src/main/java/com/deepgram/resources/agent/v1/types/AgentV1SettingsAgentContextListenProvider.java index 393f1f9..eea1585 100644 --- a/src/main/java/com/deepgram/resources/agent/v1/types/AgentV1SettingsAgentContextListenProvider.java +++ b/src/main/java/com/deepgram/resources/agent/v1/types/AgentV1SettingsAgentContextListenProvider.java @@ -99,7 +99,7 @@ public interface Visitor { T _visitUnknown(Object unknownType); } - @JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "version", visible = true, defaultImpl = V2Value.class) + @JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "version", visible = true, defaultImpl = _UnknownValue.class) @JsonSubTypes({@JsonSubTypes.Type(V1Value.class), @JsonSubTypes.Type(V2Value.class)}) @JsonIgnoreProperties(ignoreUnknown = true) private interface Value { diff --git a/src/main/java/com/deepgram/resources/agent/v1/types/AgentV1SettingsAgentListenProvider.java b/src/main/java/com/deepgram/resources/agent/v1/types/AgentV1SettingsAgentListenProvider.java index 345ba2f..4309ec0 100644 --- a/src/main/java/com/deepgram/resources/agent/v1/types/AgentV1SettingsAgentListenProvider.java +++ b/src/main/java/com/deepgram/resources/agent/v1/types/AgentV1SettingsAgentListenProvider.java @@ -99,7 +99,7 @@ public interface Visitor { T _visitUnknown(Object unknownType); } - @JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "version", visible = true, defaultImpl = V2Value.class) + @JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "version", visible = true, defaultImpl = _UnknownValue.class) @JsonSubTypes({@JsonSubTypes.Type(V1Value.class), @JsonSubTypes.Type(V2Value.class)}) @JsonIgnoreProperties(ignoreUnknown = true) private interface Value { diff --git a/src/main/java/com/deepgram/resources/agent/v1/types/AgentV1SettingsApplied.java b/src/main/java/com/deepgram/resources/agent/v1/types/AgentV1SettingsApplied.java index ad1fdb2..0d60b72 100644 --- a/src/main/java/com/deepgram/resources/agent/v1/types/AgentV1SettingsApplied.java +++ b/src/main/java/com/deepgram/resources/agent/v1/types/AgentV1SettingsApplied.java @@ -36,11 +36,6 @@ public boolean equals(Object other) { return other instanceof AgentV1SettingsApplied; } - @java.lang.Override - public int hashCode() { - return getClass().hashCode(); - } - @JsonAnyGetter public Map getAdditionalProperties() { return this.additionalProperties; diff --git a/src/main/java/com/deepgram/resources/agent/v1/types/AgentV1SpeakUpdated.java b/src/main/java/com/deepgram/resources/agent/v1/types/AgentV1SpeakUpdated.java index 8b134b5..747aa9d 100644 --- a/src/main/java/com/deepgram/resources/agent/v1/types/AgentV1SpeakUpdated.java +++ b/src/main/java/com/deepgram/resources/agent/v1/types/AgentV1SpeakUpdated.java @@ -36,11 +36,6 @@ public boolean equals(Object other) { return other instanceof AgentV1SpeakUpdated; } - @java.lang.Override - public int hashCode() { - return getClass().hashCode(); - } - @JsonAnyGetter public Map getAdditionalProperties() { return this.additionalProperties; diff --git a/src/main/java/com/deepgram/resources/agent/v1/types/AgentV1ThinkUpdated.java b/src/main/java/com/deepgram/resources/agent/v1/types/AgentV1ThinkUpdated.java index 2d67cad..5668a71 100644 --- a/src/main/java/com/deepgram/resources/agent/v1/types/AgentV1ThinkUpdated.java +++ b/src/main/java/com/deepgram/resources/agent/v1/types/AgentV1ThinkUpdated.java @@ -36,11 +36,6 @@ public boolean equals(Object other) { return other instanceof AgentV1ThinkUpdated; } - @java.lang.Override - public int hashCode() { - return getClass().hashCode(); - } - @JsonAnyGetter public Map getAdditionalProperties() { return this.additionalProperties; diff --git a/src/main/java/com/deepgram/resources/agent/v1/types/AgentV1UpdateListenListenProvider.java b/src/main/java/com/deepgram/resources/agent/v1/types/AgentV1UpdateListenListenProvider.java index e739aec..e693a04 100644 --- a/src/main/java/com/deepgram/resources/agent/v1/types/AgentV1UpdateListenListenProvider.java +++ b/src/main/java/com/deepgram/resources/agent/v1/types/AgentV1UpdateListenListenProvider.java @@ -99,7 +99,7 @@ public interface Visitor { T _visitUnknown(Object unknownType); } - @JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "version", visible = true, defaultImpl = V2Value.class) + @JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "version", visible = true, defaultImpl = _UnknownValue.class) @JsonSubTypes({@JsonSubTypes.Type(V1Value.class), @JsonSubTypes.Type(V2Value.class)}) @JsonIgnoreProperties(ignoreUnknown = true) private interface Value { diff --git a/src/main/java/com/deepgram/resources/agent/v1/types/AgentV1UserStartedSpeaking.java b/src/main/java/com/deepgram/resources/agent/v1/types/AgentV1UserStartedSpeaking.java index 3556d31..edb80f8 100644 --- a/src/main/java/com/deepgram/resources/agent/v1/types/AgentV1UserStartedSpeaking.java +++ b/src/main/java/com/deepgram/resources/agent/v1/types/AgentV1UserStartedSpeaking.java @@ -36,11 +36,6 @@ public boolean equals(Object other) { return other instanceof AgentV1UserStartedSpeaking; } - @java.lang.Override - public int hashCode() { - return getClass().hashCode(); - } - @JsonAnyGetter public Map getAdditionalProperties() { return this.additionalProperties; diff --git a/src/main/java/com/deepgram/resources/agent/v1/websocket/V1WebSocketClient.java b/src/main/java/com/deepgram/resources/agent/v1/websocket/V1WebSocketClient.java index 5bf4d73..1aa1b9d 100644 --- a/src/main/java/com/deepgram/resources/agent/v1/websocket/V1WebSocketClient.java +++ b/src/main/java/com/deepgram/resources/agent/v1/websocket/V1WebSocketClient.java @@ -13,6 +13,8 @@ import com.deepgram.resources.agent.v1.types.AgentV1AgentStartedSpeaking; import com.deepgram.resources.agent.v1.types.AgentV1AgentThinking; import com.deepgram.resources.agent.v1.types.AgentV1ConversationText; +import com.deepgram.resources.agent.v1.types.AgentV1CustomFromThinkProvider; +import com.deepgram.resources.agent.v1.types.AgentV1CustomToThinkProvider; import com.deepgram.resources.agent.v1.types.AgentV1Error; import com.deepgram.resources.agent.v1.types.AgentV1ForceEndTurn; import com.deepgram.resources.agent.v1.types.AgentV1FunctionCallCancelled; @@ -111,6 +113,8 @@ public class V1WebSocketClient implements AutoCloseable { private volatile Consumer agentAudioDoneHandler; + private volatile Consumer customFromThinkProviderHandler; + private volatile Consumer errorHandler; private volatile Consumer warningHandler; @@ -334,6 +338,15 @@ public CompletableFuture sendForceEndTurn(AgentV1ForceEndTurn message) { return sendMessage(message); } + /** + * Sends an AgentV1CustomToThinkProvider message to the server asynchronously. + * @param message the message to send + * @return a CompletableFuture that completes when the message is sent + */ + public CompletableFuture sendCustomToThinkProvider(AgentV1CustomToThinkProvider message) { + return sendMessage(message); + } + /** * Sends an AgentV1Media message to the server asynchronously. * @param message the message to send @@ -480,6 +493,14 @@ public void onAgentAudioDone(Consumer handler) { this.agentAudioDoneHandler = handler; } + /** + * Registers a handler for AgentV1CustomFromThinkProvider messages from the server. + * @param handler the handler to invoke when a message is received + */ + public void onCustomFromThinkProvider(Consumer handler) { + this.customFromThinkProviderHandler = handler; + } + /** * Registers a handler for AgentV1Error messages from the server. * @param handler the handler to invoke when a message is received @@ -748,6 +769,21 @@ private void handleIncomingMessage(String json) { return; } } + if (node.has("content") + && "__customFromThinkProvider".equals(node.path("type").asText())) { + AgentV1CustomFromThinkProvider customFromThinkProviderHandlerEvent = null; + try { + customFromThinkProviderHandlerEvent = + objectMapper.treeToValue(node, AgentV1CustomFromThinkProvider.class); + } catch (Exception e) { + } + if (customFromThinkProviderHandlerEvent != null) { + if (customFromThinkProviderHandler != null) { + customFromThinkProviderHandler.accept(customFromThinkProviderHandlerEvent); + } + return; + } + } if ("ListenUpdated".equals(node.path("type").asText())) { AgentV1ListenUpdated listenUpdatedHandlerEvent = null; try { 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 new file mode 100644 index 0000000..967d27e --- /dev/null +++ b/src/main/java/com/deepgram/resources/listen/v1/types/ListenV1Configure.java @@ -0,0 +1,165 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ +package com.deepgram.resources.listen.v1.types; + +import com.deepgram.core.ObjectMappers; +import com.fasterxml.jackson.annotation.JsonAnyGetter; +import com.fasterxml.jackson.annotation.JsonAnySetter; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonSetter; +import com.fasterxml.jackson.annotation.Nulls; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Optional; + +@JsonInclude(JsonInclude.Include.NON_ABSENT) +@JsonDeserialize(builder = ListenV1Configure.Builder.class) +public final class ListenV1Configure { + private final Optional> keyterms; + + private final Optional> features; + + private final Map additionalProperties; + + private ListenV1Configure( + Optional> keyterms, + Optional> features, + Map additionalProperties) { + this.keyterms = keyterms; + this.features = features; + this.additionalProperties = additionalProperties; + } + + /** + * @return Message type identifier + */ + @JsonProperty("type") + public String getType() { + return "Configure"; + } + + /** + * @return Replaces the stream's keyterms. Compatible with all Nova-3 models, monolingual and multilingual; on other + * models the server returns an Error with code KeytermsNotSupported. Each array replaces the entire list, + * 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.

+ */ + @JsonProperty("keyterms") + public Optional> getKeyterms() { + return keyterms; + } + + /** + * @return Turns formatting features on or off. Each key is a feature name and each value is a boolean, for + * example {"numerals": true}. + */ + @JsonProperty("features") + public Optional> getFeatures() { + return features; + } + + @java.lang.Override + public boolean equals(Object other) { + if (this == other) return true; + return other instanceof ListenV1Configure && equalTo((ListenV1Configure) other); + } + + @JsonAnyGetter + public Map getAdditionalProperties() { + return this.additionalProperties; + } + + private boolean equalTo(ListenV1Configure other) { + return keyterms.equals(other.keyterms) && features.equals(other.features); + } + + @java.lang.Override + public int hashCode() { + return Objects.hash(this.keyterms, this.features); + } + + @java.lang.Override + public String toString() { + return ObjectMappers.stringify(this); + } + + public static Builder builder() { + return new Builder(); + } + + @JsonIgnoreProperties(ignoreUnknown = true) + public static final class Builder { + private Optional> keyterms = Optional.empty(); + + private Optional> features = Optional.empty(); + + @JsonAnySetter + private Map additionalProperties = new HashMap<>(); + + private Builder() {} + + public Builder from(ListenV1Configure other) { + keyterms(other.getKeyterms()); + features(other.getFeatures()); + return this; + } + + /** + *

Replaces the stream's keyterms. Compatible with all Nova-3 models, monolingual and multilingual; on other + * models the server returns an Error with code KeytermsNotSupported. Each array replaces the entire list, + * 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.

+ */ + @JsonSetter(value = "keyterms", nulls = Nulls.SKIP) + public Builder keyterms(Optional> keyterms) { + this.keyterms = keyterms; + return this; + } + + public Builder keyterms(List keyterms) { + this.keyterms = Optional.ofNullable(keyterms); + return this; + } + + /** + *

Turns formatting features on or off. Each key is a feature name and each value is a boolean, for + * example {"numerals": true}.

+ */ + @JsonSetter(value = "features", nulls = Nulls.SKIP) + public Builder features(Optional> features) { + this.features = features; + return this; + } + + public Builder features(Map features) { + this.features = Optional.ofNullable(features); + return this; + } + + public ListenV1Configure build() { + return new ListenV1Configure(keyterms, features, additionalProperties); + } + + public Builder additionalProperty(String key, Object value) { + this.additionalProperties.put(key, value); + return this; + } + + public Builder additionalProperties(Map additionalProperties) { + this.additionalProperties.putAll(additionalProperties); + return this; + } + } +} diff --git a/src/main/java/com/deepgram/resources/listen/v1/types/ListenV1Error.java b/src/main/java/com/deepgram/resources/listen/v1/types/ListenV1Error.java new file mode 100644 index 0000000..fd0a4e0 --- /dev/null +++ b/src/main/java/com/deepgram/resources/listen/v1/types/ListenV1Error.java @@ -0,0 +1,267 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ +package com.deepgram.resources.listen.v1.types; + +import com.deepgram.core.ObjectMappers; +import com.fasterxml.jackson.annotation.JsonAnyGetter; +import com.fasterxml.jackson.annotation.JsonAnySetter; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonSetter; +import com.fasterxml.jackson.annotation.Nulls; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import java.util.HashMap; +import java.util.Map; +import java.util.Objects; +import java.util.Optional; +import org.jetbrains.annotations.NotNull; + +@JsonInclude(JsonInclude.Include.NON_ABSENT) +@JsonDeserialize(builder = ListenV1Error.Builder.class) +public final class ListenV1Error { + private final String variant; + + private final String description; + + private final Optional code; + + private final Optional message; + + private final Map additionalProperties; + + private ListenV1Error( + String variant, + String description, + Optional code, + Optional message, + Map additionalProperties) { + this.variant = variant; + this.description = description; + this.code = code; + this.message = message; + this.additionalProperties = additionalProperties; + } + + /** + * @return Message type identifier for error responses + */ + @JsonProperty("type") + public String getType() { + return "Error"; + } + + /** + * @return The error category. SchemaError means the server could not parse a client text message. + * InvalidConfigureMessage means the server rejected a Configure message. + */ + @JsonProperty("variant") + public String getVariant() { + return variant; + } + + /** + * @return A human-readable description of what went wrong + */ + @JsonProperty("description") + public String getDescription() { + return description; + } + + /** + * @return Identifies the reason for an InvalidConfigureMessage error, for example KeytermsNotSupported + * when keyterms are sent on a model other than Nova-3. + */ + @JsonProperty("code") + public Optional getCode() { + return code; + } + + /** + * @return For SchemaError, the original client message that could not be parsed + */ + @JsonProperty("message") + public Optional getMessage() { + return message; + } + + @java.lang.Override + public boolean equals(Object other) { + if (this == other) return true; + return other instanceof ListenV1Error && equalTo((ListenV1Error) other); + } + + @JsonAnyGetter + public Map getAdditionalProperties() { + return this.additionalProperties; + } + + private boolean equalTo(ListenV1Error other) { + return variant.equals(other.variant) + && description.equals(other.description) + && code.equals(other.code) + && message.equals(other.message); + } + + @java.lang.Override + public int hashCode() { + return Objects.hash(this.variant, this.description, this.code, this.message); + } + + @java.lang.Override + public String toString() { + return ObjectMappers.stringify(this); + } + + public static VariantStage builder() { + return new Builder(); + } + + public interface VariantStage { + /** + *

The error category. SchemaError means the server could not parse a client text message. + * InvalidConfigureMessage means the server rejected a Configure message.

+ */ + DescriptionStage variant(@NotNull String variant); + + Builder from(ListenV1Error other); + } + + public interface DescriptionStage { + /** + *

A human-readable description of what went wrong

+ */ + _FinalStage description(@NotNull String description); + } + + public interface _FinalStage { + ListenV1Error build(); + + _FinalStage additionalProperty(String key, Object value); + + _FinalStage additionalProperties(Map additionalProperties); + + /** + *

Identifies the reason for an InvalidConfigureMessage error, for example KeytermsNotSupported + * when keyterms are sent on a model other than Nova-3.

+ */ + _FinalStage code(Optional code); + + _FinalStage code(String code); + + /** + *

For SchemaError, the original client message that could not be parsed

+ */ + _FinalStage message(Optional message); + + _FinalStage message(String message); + } + + @JsonIgnoreProperties(ignoreUnknown = true) + public static final class Builder implements VariantStage, DescriptionStage, _FinalStage { + private String variant; + + private String description; + + private Optional message = Optional.empty(); + + private Optional code = Optional.empty(); + + @JsonAnySetter + private Map additionalProperties = new HashMap<>(); + + private Builder() {} + + @java.lang.Override + public Builder from(ListenV1Error other) { + variant(other.getVariant()); + description(other.getDescription()); + code(other.getCode()); + message(other.getMessage()); + return this; + } + + /** + *

The error category. SchemaError means the server could not parse a client text message. + * InvalidConfigureMessage means the server rejected a Configure message.

+ * @return Reference to {@code this} so that method calls can be chained together. + */ + @java.lang.Override + @JsonSetter("variant") + public DescriptionStage variant(@NotNull String variant) { + this.variant = Objects.requireNonNull(variant, "variant must not be null"); + return this; + } + + /** + *

A human-readable description of what went wrong

+ * @return Reference to {@code this} so that method calls can be chained together. + */ + @java.lang.Override + @JsonSetter("description") + public _FinalStage description(@NotNull String description) { + this.description = Objects.requireNonNull(description, "description must not be null"); + return this; + } + + /** + *

For SchemaError, the original client message that could not be parsed

+ * @return Reference to {@code this} so that method calls can be chained together. + */ + @java.lang.Override + public _FinalStage message(String message) { + this.message = Optional.ofNullable(message); + return this; + } + + /** + *

For SchemaError, the original client message that could not be parsed

+ */ + @java.lang.Override + @JsonSetter(value = "message", nulls = Nulls.SKIP) + public _FinalStage message(Optional message) { + this.message = message; + return this; + } + + /** + *

Identifies the reason for an InvalidConfigureMessage error, for example KeytermsNotSupported + * when keyterms are sent on a model other than Nova-3.

+ * @return Reference to {@code this} so that method calls can be chained together. + */ + @java.lang.Override + public _FinalStage code(String code) { + this.code = Optional.ofNullable(code); + return this; + } + + /** + *

Identifies the reason for an InvalidConfigureMessage error, for example KeytermsNotSupported + * when keyterms are sent on a model other than Nova-3.

+ */ + @java.lang.Override + @JsonSetter(value = "code", nulls = Nulls.SKIP) + public _FinalStage code(Optional code) { + this.code = code; + return this; + } + + @java.lang.Override + public ListenV1Error build() { + return new ListenV1Error(variant, description, code, message, additionalProperties); + } + + @java.lang.Override + public Builder additionalProperty(String key, Object value) { + this.additionalProperties.put(key, value); + return this; + } + + @java.lang.Override + public Builder additionalProperties(Map additionalProperties) { + this.additionalProperties.putAll(additionalProperties); + return this; + } + } +} diff --git a/src/main/java/com/deepgram/resources/listen/v1/websocket/V1WebSocketClient.java b/src/main/java/com/deepgram/resources/listen/v1/websocket/V1WebSocketClient.java index 278a2c9..a91c4e1 100644 --- a/src/main/java/com/deepgram/resources/listen/v1/websocket/V1WebSocketClient.java +++ b/src/main/java/com/deepgram/resources/listen/v1/websocket/V1WebSocketClient.java @@ -6,11 +6,12 @@ import com.deepgram.core.ClientOptions; import com.deepgram.core.DisconnectReason; import com.deepgram.core.ObjectMappers; -import com.deepgram.core.QueryStringMapper; import com.deepgram.core.ReconnectingWebSocketListener; import com.deepgram.core.RequestOptions; import com.deepgram.core.WebSocketReadyState; import com.deepgram.resources.listen.v1.types.ListenV1CloseStream; +import com.deepgram.resources.listen.v1.types.ListenV1Configure; +import com.deepgram.resources.listen.v1.types.ListenV1Error; import com.deepgram.resources.listen.v1.types.ListenV1Finalize; import com.deepgram.resources.listen.v1.types.ListenV1KeepAlive; import com.deepgram.resources.listen.v1.types.ListenV1Metadata; @@ -66,6 +67,8 @@ public class V1WebSocketClient implements AutoCloseable { private volatile Consumer speechStartedHandler; + private volatile Consumer errorHandler; + /** * Creates a new async WebSocket client for the v1 channel. */ @@ -139,7 +142,8 @@ public CompletableFuture connect(V1ConnectOptions options) { "endpointing", String.valueOf(options.getEndpointing().get())); } if (options.getExtra() != null && options.getExtra().isPresent()) { - QueryStringMapper.addQueryParameter(urlBuilder, "extra", options.getExtra().get().get(), true); + urlBuilder.addQueryParameter( + "extra", String.valueOf(options.getExtra().get())); } if (options.getInterimResults() != null && options.getInterimResults().isPresent()) { urlBuilder.addQueryParameter( @@ -147,10 +151,12 @@ public CompletableFuture connect(V1ConnectOptions options) { String.valueOf(options.getInterimResults().get())); } if (options.getKeyterm() != null && options.getKeyterm().isPresent()) { - QueryStringMapper.addQueryParameter(urlBuilder, "keyterm", options.getKeyterm().get().get(), true); + urlBuilder.addQueryParameter( + "keyterm", String.valueOf(options.getKeyterm().get())); } if (options.getKeywords() != null && options.getKeywords().isPresent()) { - QueryStringMapper.addQueryParameter(urlBuilder, "keywords", options.getKeywords().get().get(), true); + urlBuilder.addQueryParameter( + "keywords", String.valueOf(options.getKeywords().get())); } if (options.getLanguage() != null && options.getLanguage().isPresent()) { urlBuilder.addQueryParameter( @@ -183,21 +189,23 @@ public CompletableFuture connect(V1ConnectOptions options) { "redact", String.valueOf(options.getRedact().get())); } if (options.getReplace() != null && options.getReplace().isPresent()) { - QueryStringMapper.addQueryParameter(urlBuilder, "replace", options.getReplace().get().get(), true); + urlBuilder.addQueryParameter( + "replace", String.valueOf(options.getReplace().get())); } if (options.getSampleRate() != null && options.getSampleRate().isPresent()) { urlBuilder.addQueryParameter( "sample_rate", String.valueOf(options.getSampleRate().get())); } if (options.getSearch() != null && options.getSearch().isPresent()) { - QueryStringMapper.addQueryParameter(urlBuilder, "search", options.getSearch().get().get(), true); + urlBuilder.addQueryParameter( + "search", String.valueOf(options.getSearch().get())); } if (options.getSmartFormat() != null && options.getSmartFormat().isPresent()) { urlBuilder.addQueryParameter( "smart_format", String.valueOf(options.getSmartFormat().get())); } if (options.getTag() != null && options.getTag().isPresent()) { - QueryStringMapper.addQueryParameter(urlBuilder, "tag", options.getTag().get().get(), true); + urlBuilder.addQueryParameter("tag", String.valueOf(options.getTag().get())); } if (options.getUtteranceEndMs() != null && options.getUtteranceEndMs().isPresent()) { urlBuilder.addQueryParameter( @@ -212,13 +220,6 @@ public CompletableFuture connect(V1ConnectOptions options) { urlBuilder.addQueryParameter( "version", String.valueOf(options.getVersion().get())); } - if (options.getAdditionalProperties() != null) { - options.getAdditionalProperties().forEach((key, value) -> { - if (value != null) { - QueryStringMapper.addQueryParameter(urlBuilder, key, value, true); - } - }); - } Request.Builder requestBuilder = new Request.Builder().url(urlBuilder.build()); clientOptions.headers((RequestOptions) null).forEach(requestBuilder::addHeader); final Request request = requestBuilder.build(); @@ -352,6 +353,15 @@ public CompletableFuture sendKeepAlive(ListenV1KeepAlive message) { return sendMessage(message); } + /** + * Sends a ListenV1Configure message to the server asynchronously. + * @param message the message to send + * @return a CompletableFuture that completes when the message is sent + */ + public CompletableFuture sendConfigure(ListenV1Configure message) { + return sendMessage(message); + } + /** * Registers a handler for ListenV1Results messages from the server. * @param handler the handler to invoke when a message is received @@ -384,6 +394,14 @@ public void onSpeechStarted(Consumer handler) { this.speechStartedHandler = handler; } + /** + * Registers a handler for ListenV1Error messages from the server. + * @param handler the handler to invoke when a message is received + */ + public void onErrorMessage(Consumer handler) { + this.errorHandler = handler; + } + /** * Registers a handler called when the connection is established. * @param handler the handler to invoke when connected @@ -540,6 +558,21 @@ private void handleIncomingMessage(String json) { return; } } + if (node.has("variant") + && node.has("description") + && "Error".equals(node.path("type").asText())) { + ListenV1Error errorHandlerEvent = null; + try { + errorHandlerEvent = objectMapper.treeToValue(node, ListenV1Error.class); + } catch (Exception e) { + } + if (errorHandlerEvent != null) { + if (errorHandler != null) { + errorHandler.accept(errorHandlerEvent); + } + return; + } + } if (onErrorHandler != null) { onErrorHandler.accept(new RuntimeException( "Unrecognized WebSocket message: " + json.substring(0, Math.min(200, json.length())) diff --git a/src/main/java/com/deepgram/resources/listen/v2/types/ListenV2CloseStream.java b/src/main/java/com/deepgram/resources/listen/v2/types/ListenV2CloseStream.java index c9f2f35..e6308e2 100644 --- a/src/main/java/com/deepgram/resources/listen/v2/types/ListenV2CloseStream.java +++ b/src/main/java/com/deepgram/resources/listen/v2/types/ListenV2CloseStream.java @@ -36,11 +36,6 @@ public boolean equals(Object other) { return other instanceof ListenV2CloseStream; } - @java.lang.Override - public int hashCode() { - return getClass().hashCode(); - } - @JsonAnyGetter public Map getAdditionalProperties() { return this.additionalProperties; diff --git a/src/main/java/com/deepgram/resources/listen/v2/types/ListenV2ForceEndTurn.java b/src/main/java/com/deepgram/resources/listen/v2/types/ListenV2ForceEndTurn.java index 894bd3c..c3dbd38 100644 --- a/src/main/java/com/deepgram/resources/listen/v2/types/ListenV2ForceEndTurn.java +++ b/src/main/java/com/deepgram/resources/listen/v2/types/ListenV2ForceEndTurn.java @@ -36,11 +36,6 @@ public boolean equals(Object other) { return other instanceof ListenV2ForceEndTurn; } - @java.lang.Override - public int hashCode() { - return getClass().hashCode(); - } - @JsonAnyGetter public Map getAdditionalProperties() { return this.additionalProperties; diff --git a/src/main/java/com/deepgram/resources/listen/v2/websocket/V2WebSocketClient.java b/src/main/java/com/deepgram/resources/listen/v2/websocket/V2WebSocketClient.java index 5f55fea..84bf5df 100644 --- a/src/main/java/com/deepgram/resources/listen/v2/websocket/V2WebSocketClient.java +++ b/src/main/java/com/deepgram/resources/listen/v2/websocket/V2WebSocketClient.java @@ -6,7 +6,6 @@ import com.deepgram.core.ClientOptions; import com.deepgram.core.DisconnectReason; import com.deepgram.core.ObjectMappers; -import com.deepgram.core.QueryStringMapper; import com.deepgram.core.ReconnectingWebSocketListener; import com.deepgram.core.RequestOptions; import com.deepgram.core.WebSocketReadyState; @@ -23,7 +22,6 @@ import com.fasterxml.jackson.databind.ObjectMapper; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ScheduledExecutorService; -import java.util.concurrent.atomic.AtomicReference; import java.util.function.Consumer; import okhttp3.HttpUrl; import okhttp3.OkHttpClient; @@ -61,8 +59,6 @@ public class V2WebSocketClient implements AutoCloseable { private ReconnectingWebSocketListener reconnectingListener; - private final AtomicReference closeStreamSocket = new AtomicReference<>(); - private volatile Consumer connectedHandler; private volatile Consumer turnInfoHandler; @@ -91,7 +87,6 @@ public V2WebSocketClient(ClientOptions clientOptions) { */ public CompletableFuture connect(V2ConnectOptions options) { connectionFuture = new CompletableFuture<>(); - closeStreamSocket.set(null); String baseUrl = clientOptions.environment().getProductionURL(); String fullPath = "/v2/listen"; if (baseUrl.endsWith("/") && fullPath.startsWith("/")) { @@ -134,12 +129,12 @@ public CompletableFuture connect(V2ConnectOptions options) { "eot_timeout_ms", String.valueOf(options.getEotTimeoutMs().get())); } if (options.getKeyterm() != null && options.getKeyterm().isPresent()) { - QueryStringMapper.addQueryParameter( - urlBuilder, "keyterm", options.getKeyterm().get().get(), true); + urlBuilder.addQueryParameter( + "keyterm", String.valueOf(options.getKeyterm().get())); } if (options.getLanguageHint() != null && options.getLanguageHint().isPresent()) { - QueryStringMapper.addQueryParameter( - urlBuilder, "language_hint", options.getLanguageHint().get().get(), true); + urlBuilder.addQueryParameter( + "language_hint", String.valueOf(options.getLanguageHint().get())); } if (options.getProfanityFilter() != null && options.getProfanityFilter().isPresent()) { urlBuilder.addQueryParameter( @@ -159,14 +154,7 @@ public CompletableFuture connect(V2ConnectOptions options) { "mip_opt_out", String.valueOf(options.getMipOptOut().get())); } if (options.getTag() != null && options.getTag().isPresent()) { - QueryStringMapper.addQueryParameter(urlBuilder, "tag", options.getTag().get().get(), true); - } - if (options.getAdditionalProperties() != null) { - options.getAdditionalProperties().forEach((key, value) -> { - if (value != null) { - QueryStringMapper.addQueryParameter(urlBuilder, key, value, true); - } - }); + urlBuilder.addQueryParameter("tag", String.valueOf(options.getTag().get())); } Request.Builder requestBuilder = new Request.Builder().url(urlBuilder.build()); clientOptions.headers((RequestOptions) null).forEach(requestBuilder::addHeader); @@ -194,7 +182,6 @@ public CompletableFuture connect(V2ConnectOptions options) { clientOptions::isClosed) { @Override protected void onWebSocketOpen(WebSocket webSocket, Response response) { - closeStreamSocket.set(null); readyState = WebSocketReadyState.OPEN; if (onConnectedHandler != null) { onConnectedHandler.run(); @@ -226,12 +213,6 @@ protected void onWebSocketClosed(WebSocket webSocket, int code, String reason) { onDisconnectedHandler.accept(new DisconnectReason(code, reason)); } } - - @Override - protected boolean shouldReconnectAfterClose(WebSocket webSocket, int code, String reason) { - return super.shouldReconnectAfterClose(webSocket, code, reason) - && (code != 1005 || closeStreamSocket.get() != webSocket); - } }; clientOptions.registerWebSocket(this); reconnectingListener.connect(); @@ -282,13 +263,12 @@ public CompletableFuture sendMedia(ByteString message) { } /** - * Sends a ListenV2CloseStream message to the server asynchronously. A queued CloseStream does - * not suppress reconnects because it might never reach the server. + * Sends a ListenV2CloseStream message to the server asynchronously. * @param message the message to send * @return a CompletableFuture that completes when the message is sent */ public CompletableFuture sendCloseStream(ListenV2CloseStream message) { - return sendMessage(message, closeStreamSocket::set); + return sendMessage(message); } /** @@ -422,16 +402,12 @@ private void assertSocketIsOpen() { } private CompletableFuture sendMessage(Object body) { - return sendMessage(body, null); - } - - private CompletableFuture sendMessage(Object body, Consumer onSent) { CompletableFuture future = new CompletableFuture<>(); try { assertSocketIsOpen(); String json = objectMapper.writeValueAsString(body); // Use reconnecting listener's send method which handles queuing - reconnectingListener.send(json, onSent); + reconnectingListener.send(json); future.complete(null); } catch (IllegalStateException e) { future.completeExceptionally(e); @@ -552,8 +528,11 @@ private void handleIncomingMessage(String json) { return; } } - // The raw frame is already delivered to onMessage(String), so ignore unknown typed - // frames for forward compatibility with newly added server control messages. + if (onErrorHandler != null) { + onErrorHandler.accept(new RuntimeException( + "Unrecognized WebSocket message: " + json.substring(0, Math.min(200, json.length())) + + "... Update your SDK version to support new message types.")); + } } catch (Exception e) { if (onErrorHandler != null) { onErrorHandler.accept(e); diff --git a/src/main/java/com/deepgram/resources/speak/v1/websocket/V1WebSocketClient.java b/src/main/java/com/deepgram/resources/speak/v1/websocket/V1WebSocketClient.java index e6824a3..6dd298a 100644 --- a/src/main/java/com/deepgram/resources/speak/v1/websocket/V1WebSocketClient.java +++ b/src/main/java/com/deepgram/resources/speak/v1/websocket/V1WebSocketClient.java @@ -6,7 +6,6 @@ import com.deepgram.core.ClientOptions; import com.deepgram.core.DisconnectReason; import com.deepgram.core.ObjectMappers; -import com.deepgram.core.QueryStringMapper; import com.deepgram.core.ReconnectingWebSocketListener; import com.deepgram.core.RequestOptions; import com.deepgram.core.WebSocketReadyState; @@ -123,13 +122,6 @@ public CompletableFuture connect(V1ConnectOptions options) { urlBuilder.addQueryParameter( "speed", String.valueOf(options.getSpeed().get())); } - if (options.getAdditionalProperties() != null) { - options.getAdditionalProperties().forEach((key, value) -> { - if (value != null) { - QueryStringMapper.addQueryParameter(urlBuilder, key, value, true); - } - }); - } Request.Builder requestBuilder = new Request.Builder().url(urlBuilder.build()); clientOptions.headers((RequestOptions) null).forEach(requestBuilder::addHeader); final Request request = requestBuilder.build(); diff --git a/src/main/java/com/deepgram/resources/speak/v2/types/SpeakV2Close.java b/src/main/java/com/deepgram/resources/speak/v2/types/SpeakV2Close.java index 7fecd79..2e96a5c 100644 --- a/src/main/java/com/deepgram/resources/speak/v2/types/SpeakV2Close.java +++ b/src/main/java/com/deepgram/resources/speak/v2/types/SpeakV2Close.java @@ -36,11 +36,6 @@ public boolean equals(Object other) { return other instanceof SpeakV2Close; } - @java.lang.Override - public int hashCode() { - return getClass().hashCode(); - } - @JsonAnyGetter public Map getAdditionalProperties() { return this.additionalProperties; diff --git a/src/main/java/com/deepgram/resources/speak/v2/types/SpeakV2Flush.java b/src/main/java/com/deepgram/resources/speak/v2/types/SpeakV2Flush.java index 130da0d..acd717d 100644 --- a/src/main/java/com/deepgram/resources/speak/v2/types/SpeakV2Flush.java +++ b/src/main/java/com/deepgram/resources/speak/v2/types/SpeakV2Flush.java @@ -36,11 +36,6 @@ public boolean equals(Object other) { return other instanceof SpeakV2Flush; } - @java.lang.Override - public int hashCode() { - return getClass().hashCode(); - } - @JsonAnyGetter public Map getAdditionalProperties() { return this.additionalProperties; diff --git a/src/main/java/com/deepgram/resources/speak/v2/websocket/V2WebSocketClient.java b/src/main/java/com/deepgram/resources/speak/v2/websocket/V2WebSocketClient.java index b4e01f7..07f6c36 100644 --- a/src/main/java/com/deepgram/resources/speak/v2/websocket/V2WebSocketClient.java +++ b/src/main/java/com/deepgram/resources/speak/v2/websocket/V2WebSocketClient.java @@ -6,7 +6,6 @@ import com.deepgram.core.ClientOptions; import com.deepgram.core.DisconnectReason; import com.deepgram.core.ObjectMappers; -import com.deepgram.core.QueryStringMapper; import com.deepgram.core.ReconnectingWebSocketListener; import com.deepgram.core.RequestOptions; import com.deepgram.core.WebSocketReadyState; @@ -144,14 +143,7 @@ public CompletableFuture connect(V2ConnectOptions options) { "mip_opt_out", String.valueOf(options.getMipOptOut().get())); } if (options.getTag() != null && options.getTag().isPresent()) { - QueryStringMapper.addQueryParameter(urlBuilder, "tag", options.getTag().get().get(), true); - } - if (options.getAdditionalProperties() != null) { - options.getAdditionalProperties().forEach((key, value) -> { - if (value != null) { - QueryStringMapper.addQueryParameter(urlBuilder, key, value, true); - } - }); + urlBuilder.addQueryParameter("tag", String.valueOf(options.getTag().get())); } Request.Builder requestBuilder = new Request.Builder().url(urlBuilder.build()); clientOptions.headers((RequestOptions) null).forEach(requestBuilder::addHeader); @@ -619,8 +611,11 @@ private void handleIncomingMessage(String json) { return; } } - // The raw frame is already delivered to onMessage(String), so ignore unknown typed - // frames for forward compatibility with newly added server control messages. + if (onErrorHandler != null) { + onErrorHandler.accept(new RuntimeException( + "Unrecognized WebSocket message: " + json.substring(0, Math.min(200, json.length())) + + "... Update your SDK version to support new message types.")); + } } catch (Exception e) { if (onErrorHandler != null) { onErrorHandler.accept(e); diff --git a/src/main/java/com/deepgram/types/DeepgramModel.java b/src/main/java/com/deepgram/types/DeepgramModel.java index 6444d7f..fc7c08c 100644 --- a/src/main/java/com/deepgram/types/DeepgramModel.java +++ b/src/main/java/com/deepgram/types/DeepgramModel.java @@ -13,8 +13,6 @@ public final class DeepgramModel { public static final DeepgramModel FLUX_KELSEY_EN = new DeepgramModel(Value.FLUX_KELSEY_EN, "flux-kelsey-en"); - 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"); public static final DeepgramModel AURA2HARMONIA_EN = @@ -253,8 +251,6 @@ public T visit(Visitor visitor) { return visitor.visitAura2HeraEn(); case FLUX_KELSEY_EN: return visitor.visitFluxKelseyEn(); - case FLUX_RENEE_EN: - return visitor.visitFluxReneeEn(); case FLUX_SIENNA_EN: return visitor.visitFluxSiennaEn(); case AURA2HARMONIA_EN: @@ -462,8 +458,6 @@ public static DeepgramModel valueOf(String value) { return AURA2HERA_EN; case "flux-kelsey-en": return FLUX_KELSEY_EN; - case "flux-renee-en": - return FLUX_RENEE_EN; case "flux-sienna-en": return FLUX_SIENNA_EN; case "aura-2-harmonia-en": @@ -846,8 +840,6 @@ public enum Value { FLUX_PRIYA_EN, - FLUX_RENEE_EN, - FLUX_RUFUS_EN, FLUX_SEAN_EN, @@ -1050,8 +1042,6 @@ public interface Visitor { T visitFluxPriyaEn(); - T visitFluxReneeEn(); - T visitFluxRufusEn(); T visitFluxSeanEn(); From 4b6d55fab03d0cfb3f79123b3a6be9c079e2446c Mon Sep 17 00:00:00 2001 From: Greg Holmes Date: Wed, 7 Oct 2026 12:00:57 +0100 Subject: [PATCH 4/7] chore: re-apply manual patches after regen --- .fernignore | 46 +- .../java/com/deepgram/core/ClientOptions.java | 9 +- .../com/deepgram/core/ClientOptions.java.bak | 408 ------- .../core/ReconnectingWebSocketListener.java | 61 +- .../ReconnectingWebSocketListener.java.bak | 622 ---------- .../agent/v1/types/AgentV1AgentAudioDone.java | 5 + .../v1/types/AgentV1AgentAudioDone.java.bak | 83 -- .../agent/v1/types/AgentV1ForceEndTurn.java | 5 + .../v1/types/AgentV1ForceEndTurn.java.bak | 83 -- .../agent/v1/types/AgentV1KeepAlive.java | 5 + .../agent/v1/types/AgentV1KeepAlive.java.bak | 83 -- .../agent/v1/types/AgentV1ListenUpdated.java | 5 + .../v1/types/AgentV1ListenUpdated.java.bak | 83 -- .../agent/v1/types/AgentV1PromptUpdated.java | 5 + .../v1/types/AgentV1PromptUpdated.java.bak | 83 -- ...tV1SettingsAgentContextListenProvider.java | 2 +- ...ettingsAgentContextListenProvider.java.bak | 224 ---- .../AgentV1SettingsAgentListenProvider.java | 2 +- ...gentV1SettingsAgentListenProvider.java.bak | 224 ---- .../v1/types/AgentV1SettingsApplied.java | 5 + .../v1/types/AgentV1SettingsApplied.java.bak | 83 -- .../agent/v1/types/AgentV1SpeakUpdated.java | 5 + .../v1/types/AgentV1SpeakUpdated.java.bak | 83 -- .../agent/v1/types/AgentV1ThinkUpdated.java | 5 + .../v1/types/AgentV1ThinkUpdated.java.bak | 83 -- .../AgentV1UpdateListenListenProvider.java | 2 +- ...AgentV1UpdateListenListenProvider.java.bak | 224 ---- .../v1/types/AgentV1UserStartedSpeaking.java | 5 + .../types/AgentV1UserStartedSpeaking.java.bak | 83 -- .../v1/websocket/V1WebSocketClient.java | 25 +- .../v1/websocket/V1WebSocketClient.java.bak | 554 --------- .../listen/v2/types/ListenV2CloseStream.java | 5 + .../v2/types/ListenV2CloseStream.java.bak | 83 -- .../listen/v2/types/ListenV2ForceEndTurn.java | 5 + .../v2/types/ListenV2ForceEndTurn.java.bak | 83 -- .../v2/websocket/V2WebSocketClient.java | 47 +- .../v2/websocket/V2WebSocketClient.java.bak | 563 --------- .../speak/v1/websocket/V1WebSocketClient.java | 8 + .../v1/websocket/V1WebSocketClient.java.bak | 467 ------- .../speak/v2/types/SpeakV2Close.java | 5 + .../speak/v2/types/SpeakV2Close.java.bak | 83 -- .../speak/v2/types/SpeakV2Flush.java | 5 + .../speak/v2/types/SpeakV2Flush.java.bak | 83 -- .../speak/v2/websocket/V2WebSocketClient.java | 17 +- .../v2/websocket/V2WebSocketClient.java.bak | 630 ---------- .../com/deepgram/types/DeepgramModel.java | 10 + .../com/deepgram/types/DeepgramModel.java.bak | 1071 ----------------- .../deepgram/AgentV1ControlFrameWireTest.java | 69 ++ .../ListenV1ConfigureErrorWebSocketTest.java | 104 ++ 49 files changed, 385 insertions(+), 6148 deletions(-) delete mode 100644 src/main/java/com/deepgram/core/ClientOptions.java.bak delete mode 100644 src/main/java/com/deepgram/core/ReconnectingWebSocketListener.java.bak delete mode 100644 src/main/java/com/deepgram/resources/agent/v1/types/AgentV1AgentAudioDone.java.bak delete mode 100644 src/main/java/com/deepgram/resources/agent/v1/types/AgentV1ForceEndTurn.java.bak delete mode 100644 src/main/java/com/deepgram/resources/agent/v1/types/AgentV1KeepAlive.java.bak delete mode 100644 src/main/java/com/deepgram/resources/agent/v1/types/AgentV1ListenUpdated.java.bak delete mode 100644 src/main/java/com/deepgram/resources/agent/v1/types/AgentV1PromptUpdated.java.bak delete mode 100644 src/main/java/com/deepgram/resources/agent/v1/types/AgentV1SettingsAgentContextListenProvider.java.bak delete mode 100644 src/main/java/com/deepgram/resources/agent/v1/types/AgentV1SettingsAgentListenProvider.java.bak delete mode 100644 src/main/java/com/deepgram/resources/agent/v1/types/AgentV1SettingsApplied.java.bak delete mode 100644 src/main/java/com/deepgram/resources/agent/v1/types/AgentV1SpeakUpdated.java.bak delete mode 100644 src/main/java/com/deepgram/resources/agent/v1/types/AgentV1ThinkUpdated.java.bak delete mode 100644 src/main/java/com/deepgram/resources/agent/v1/types/AgentV1UpdateListenListenProvider.java.bak delete mode 100644 src/main/java/com/deepgram/resources/agent/v1/types/AgentV1UserStartedSpeaking.java.bak delete mode 100644 src/main/java/com/deepgram/resources/listen/v1/websocket/V1WebSocketClient.java.bak delete mode 100644 src/main/java/com/deepgram/resources/listen/v2/types/ListenV2CloseStream.java.bak delete mode 100644 src/main/java/com/deepgram/resources/listen/v2/types/ListenV2ForceEndTurn.java.bak delete mode 100644 src/main/java/com/deepgram/resources/listen/v2/websocket/V2WebSocketClient.java.bak delete mode 100644 src/main/java/com/deepgram/resources/speak/v1/websocket/V1WebSocketClient.java.bak delete mode 100644 src/main/java/com/deepgram/resources/speak/v2/types/SpeakV2Close.java.bak delete mode 100644 src/main/java/com/deepgram/resources/speak/v2/types/SpeakV2Flush.java.bak delete mode 100644 src/main/java/com/deepgram/resources/speak/v2/websocket/V2WebSocketClient.java.bak delete mode 100644 src/main/java/com/deepgram/types/DeepgramModel.java.bak create mode 100644 src/test/java/com/deepgram/ListenV1ConfigureErrorWebSocketTest.java diff --git a/.fernignore b/.fernignore index 4733372..ef6a835 100644 --- a/.fernignore +++ b/.fernignore @@ -16,7 +16,7 @@ src/main/java/com/deepgram/AsyncDeepgramClientBuilder.java # Fern regen overwrites these with incorrect SDK names and strips the markers. # Fern 4.22.1 closes tracked WebSockets before SDK-owned OkHttp resources; retain that lifecycle behavior while # reapplying only these headers. -src/main/java/com/deepgram/core/ClientOptions.java.bak +src/main/java/com/deepgram/core/ClientOptions.java # Transport abstraction (pluggable transport for SageMaker, etc.) src/main/java/com/deepgram/core/transport/ @@ -24,7 +24,7 @@ src/main/java/com/deepgram/core/transport/ # Bug fixes for maxRetries(0) semantics ("connect once, don't retry") and a # configurable connectionTimeoutMs on ReconnectOptions (was hardcoded 4000ms). # Pull this back out once the fixes are upstreamed into the Fern generator. -src/main/java/com/deepgram/core/ReconnectingWebSocketListener.java.bak +src/main/java/com/deepgram/core/ReconnectingWebSocketListener.java # Forward-compat patch: Fern's generated dispatcher routes any unrecognized message # type to the error handler ("Update your SDK version..."), which makes benign new @@ -34,8 +34,8 @@ src/main/java/com/deepgram/core/ReconnectingWebSocketListener.java.bak # NOTE: the listen v2 and speak v2 clients below also carry the streaming query-param patches # (multi-value serialization + the additionalProperties escape hatch) documented lower down — # keep them frozen until those fixes are upstreamed too. -src/main/java/com/deepgram/resources/speak/v2/websocket/V2WebSocketClient.java.bak -src/main/java/com/deepgram/resources/listen/v2/websocket/V2WebSocketClient.java.bak +src/main/java/com/deepgram/resources/speak/v2/websocket/V2WebSocketClient.java +src/main/java/com/deepgram/resources/listen/v2/websocket/V2WebSocketClient.java # Streaming query-param patches on the generated WS connect() builders. Two fixes: # (1) Multi-value serialization: array-valued params (listen: keyterm, keywords, replace, search, @@ -49,8 +49,8 @@ src/main/java/com/deepgram/resources/listen/v2/websocket/V2WebSocketClient.java. # listen v2 and speak v2 clients are frozen above; freeze the listen v1 and speak v1 clients here too # so the fixes survive regen. Re-apply after regen; unfreeze once the generator emits array params as # repeats and serializes additionalProperties on the WS connect path (tracked as an upstream Fern request). -src/main/java/com/deepgram/resources/listen/v1/websocket/V1WebSocketClient.java.bak -src/main/java/com/deepgram/resources/speak/v1/websocket/V1WebSocketClient.java.bak +src/main/java/com/deepgram/resources/listen/v1/websocket/V1WebSocketClient.java +src/main/java/com/deepgram/resources/speak/v1/websocket/V1WebSocketClient.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 @@ -62,25 +62,25 @@ src/main/java/com/deepgram/resources/speak/v1/websocket/V1WebSocketClient.java.b # changes -- 4.18.0 alone added 25 constants. On the next regen do NOT restore the .bak wholesale: # diff it against the newly generated file, carry forward every new voice, and re-apply only the # FLUX_RENEE_EN touchpoints (constant, Value enum, visit() case, valueOf() case, Visitor method). -src/main/java/com/deepgram/types/DeepgramModel.java.bak +src/main/java/com/deepgram/types/DeepgramModel.java # Manual equals/hashCode contract fix: Fern generates equals() (all instances equal) but no # hashCode() for fields-less message types, violating the Object contract. We add a consistent # hashCode() to each. Unfreeze and drop these patches once the generator emits a matching # equals/hashCode pair for fields-less types (tracked as an upstream Fern request). -src/main/java/com/deepgram/resources/listen/v2/types/ListenV2CloseStream.java.bak -src/main/java/com/deepgram/resources/listen/v2/types/ListenV2ForceEndTurn.java.bak -src/main/java/com/deepgram/resources/speak/v2/types/SpeakV2Close.java.bak -src/main/java/com/deepgram/resources/speak/v2/types/SpeakV2Flush.java.bak -src/main/java/com/deepgram/resources/agent/v1/types/AgentV1ListenUpdated.java.bak -src/main/java/com/deepgram/resources/agent/v1/types/AgentV1SpeakUpdated.java.bak -src/main/java/com/deepgram/resources/agent/v1/types/AgentV1AgentAudioDone.java.bak -src/main/java/com/deepgram/resources/agent/v1/types/AgentV1SettingsApplied.java.bak -src/main/java/com/deepgram/resources/agent/v1/types/AgentV1UserStartedSpeaking.java.bak -src/main/java/com/deepgram/resources/agent/v1/types/AgentV1KeepAlive.java.bak -src/main/java/com/deepgram/resources/agent/v1/types/AgentV1ThinkUpdated.java.bak -src/main/java/com/deepgram/resources/agent/v1/types/AgentV1PromptUpdated.java.bak -src/main/java/com/deepgram/resources/agent/v1/types/AgentV1ForceEndTurn.java.bak +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 +src/main/java/com/deepgram/resources/agent/v1/types/AgentV1ListenUpdated.java +src/main/java/com/deepgram/resources/agent/v1/types/AgentV1SpeakUpdated.java +src/main/java/com/deepgram/resources/agent/v1/types/AgentV1AgentAudioDone.java +src/main/java/com/deepgram/resources/agent/v1/types/AgentV1SettingsApplied.java +src/main/java/com/deepgram/resources/agent/v1/types/AgentV1UserStartedSpeaking.java +src/main/java/com/deepgram/resources/agent/v1/types/AgentV1KeepAlive.java +src/main/java/com/deepgram/resources/agent/v1/types/AgentV1ThinkUpdated.java +src/main/java/com/deepgram/resources/agent/v1/types/AgentV1PromptUpdated.java +src/main/java/com/deepgram/resources/agent/v1/types/AgentV1ForceEndTurn.java # Union default-variant fix (STOPGAP). The agent listen-provider unions declare `version` as an # optional discriminator, so a provider payload without it is valid (and is what 0.7.x emits). Fern @@ -89,9 +89,9 @@ src/main/java/com/deepgram/resources/agent/v1/types/AgentV1ForceEndTurn.java.bak # {"provider":null}, silently dropping the caller's provider on the wire. Patched to # defaultImpl = V2Value on each. Guarded by AgentSettingsProviderDefaultTest. Unfreeze and drop once # the generator stops defaulting unions to the empty _UnknownValue (tracked as an upstream Fern request). -src/main/java/com/deepgram/resources/agent/v1/types/AgentV1UpdateListenListenProvider.java.bak -src/main/java/com/deepgram/resources/agent/v1/types/AgentV1SettingsAgentListenProvider.java.bak -src/main/java/com/deepgram/resources/agent/v1/types/AgentV1SettingsAgentContextListenProvider.java.bak +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 # Build and project configuration build.gradle diff --git a/src/main/java/com/deepgram/core/ClientOptions.java b/src/main/java/com/deepgram/core/ClientOptions.java index b9ef146..36be71d 100644 --- a/src/main/java/com/deepgram/core/ClientOptions.java +++ b/src/main/java/com/deepgram/core/ClientOptions.java @@ -63,10 +63,10 @@ private ClientOptions( this.headers.putAll(headers); this.headers.putAll(new HashMap() { { - put("User-Agent", "com.deepgram.deepgram-java-sdk/" + getSdkVersion()); + put("User-Agent", "com.deepgram:deepgram-java-sdk/0.11.1"); // x-release-please-version put("X-Fern-Language", "JAVA"); put("X-Fern-SDK-Name", "com.deepgram:deepgram-java-sdk"); - put("X-Fern-SDK-Version", getSdkVersion()); + put("X-Fern-SDK-Version", "0.11.1"); // x-release-please-version } }); this.headerSuppliers = headerSuppliers; @@ -98,11 +98,6 @@ public Map headers(RequestOptions requestOptions) { return values; } - private static String getSdkVersion() { - String version = ClientOptions.class.getPackage().getImplementationVersion(); - return version != null ? version : "0.11.1"; - } - public int timeout(RequestOptions requestOptions) { if (requestOptions == null) { return this.timeout; diff --git a/src/main/java/com/deepgram/core/ClientOptions.java.bak b/src/main/java/com/deepgram/core/ClientOptions.java.bak deleted file mode 100644 index 4d7a26a..0000000 --- a/src/main/java/com/deepgram/core/ClientOptions.java.bak +++ /dev/null @@ -1,408 +0,0 @@ -/** - * This file was auto-generated by Fern from our API Definition. - */ -package com.deepgram.core; - -import java.util.ArrayList; -import java.util.HashMap; -import java.util.Map; -import java.util.Optional; -import java.util.Set; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.atomic.AtomicBoolean; -import java.util.function.Supplier; -import okhttp3.OkHttpClient; - -public final class ClientOptions { - private final Environment environment; - - private final Map headers; - - private final Map> headerSuppliers; - - private final OkHttpClient httpClient; - - private final boolean ownsHttpClient; - - private final AtomicBoolean closed; - - private final Set openWebSockets; - - private final int timeout; - - private final int maxRetries; - - private final Optional initialRetryDelayMillis; - - private final Optional maxRetryDelayMillis; - - private final Optional retryJitterFactor; - - private final Optional webSocketFactory; - - private final Optional logging; - - private ClientOptions( - Environment environment, - Map headers, - Map> headerSuppliers, - OkHttpClient httpClient, - boolean ownsHttpClient, - AtomicBoolean closed, - Set openWebSockets, - int timeout, - int maxRetries, - Optional initialRetryDelayMillis, - Optional maxRetryDelayMillis, - Optional retryJitterFactor, - Optional webSocketFactory, - Optional logging) { - this.environment = environment; - this.headers = new HashMap<>(); - this.headers.putAll(headers); - this.headers.putAll(new HashMap() { - { - put("User-Agent", "com.deepgram:deepgram-java-sdk/0.11.0"); // x-release-please-version - put("X-Fern-Language", "JAVA"); - put("X-Fern-SDK-Name", "com.deepgram:deepgram-java-sdk"); - put("X-Fern-SDK-Version", "0.11.0"); // x-release-please-version - } - }); - this.headerSuppliers = headerSuppliers; - this.httpClient = httpClient; - this.ownsHttpClient = ownsHttpClient; - this.closed = closed; - this.openWebSockets = openWebSockets; - this.timeout = timeout; - this.maxRetries = maxRetries; - this.initialRetryDelayMillis = initialRetryDelayMillis; - this.maxRetryDelayMillis = maxRetryDelayMillis; - this.retryJitterFactor = retryJitterFactor; - this.webSocketFactory = webSocketFactory; - this.logging = logging; - } - - public Environment environment() { - return this.environment; - } - - public Map headers(RequestOptions requestOptions) { - Map values = new HashMap<>(this.headers); - headerSuppliers.forEach((key, supplier) -> { - values.put(key, supplier.get()); - }); - if (requestOptions != null) { - values.putAll(requestOptions.getHeaders()); - } - return values; - } - - public int timeout(RequestOptions requestOptions) { - if (requestOptions == null) { - return this.timeout; - } - return requestOptions.getTimeout().orElse(this.timeout); - } - - public OkHttpClient httpClient() { - return this.httpClient; - } - - public OkHttpClient httpClientWithTimeout(RequestOptions requestOptions) { - if (requestOptions == null) { - return this.httpClient; - } - return this.httpClient - .newBuilder() - .callTimeout(requestOptions.getTimeout().get(), requestOptions.getTimeoutTimeUnit()) - .connectTimeout(0, TimeUnit.SECONDS) - .writeTimeout(0, TimeUnit.SECONDS) - .readTimeout(0, TimeUnit.SECONDS) - .build(); - } - - public int maxRetries() { - return this.maxRetries; - } - - public Optional initialRetryDelayMillis() { - return this.initialRetryDelayMillis; - } - - public Optional maxRetryDelayMillis() { - return this.maxRetryDelayMillis; - } - - public Optional retryJitterFactor() { - return this.retryJitterFactor; - } - - /** - * Releases resources owned by this client. Only shuts down the underlying OkHttpClient's - * dispatcher executor and evicts its connection pool when this client created that - * OkHttpClient itself; an OkHttpClient supplied via httpClient is left running, since the - * caller owns its lifecycle. - *

- * 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. - *

- * 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 - * dispatcher goes away. Any WebSocket connect or reconnect attempted afterwards fails with - * an {@code IllegalStateException} explaining that the client has been closed. - */ - public void close() { - if (!this.closed.compareAndSet(false, true)) { - return; - } - for (AutoCloseable webSocket : new ArrayList<>(this.openWebSockets)) { - try { - webSocket.close(); - } catch (Exception e) { - // best effort; keep closing the remaining sockets and the HTTP client - } - } - this.openWebSockets.clear(); - if (!this.ownsHttpClient) { - return; - } - this.httpClient.dispatcher().executorService().shutdown(); - this.httpClient.connectionPool().evictAll(); - } - - /** - * Returns whether {@link #close()} has been called on this client. - */ - public boolean isClosed() { - return this.closed.get(); - } - - /** - * Tracks a connected WebSocket client so that {@link #close()} disconnects it. - * - * @throws IllegalStateException if this client has already been closed - */ - public void registerWebSocket(AutoCloseable webSocket) { - this.openWebSockets.add(webSocket); - if (this.closed.get()) { - this.openWebSockets.remove(webSocket); - throw new IllegalStateException("root client has been closed"); - } - } - - /** - * Stops tracking a WebSocket client that has been disconnected. - */ - public void unregisterWebSocket(AutoCloseable webSocket) { - this.openWebSockets.remove(webSocket); - } - - public Optional webSocketFactory() { - return this.webSocketFactory; - } - - public Optional logging() { - return this.logging; - } - - public static Builder builder() { - return new Builder(); - } - - public static class Builder { - private Environment environment; - - private final Map headers = new HashMap<>(); - - private final Map> headerSuppliers = new HashMap<>(); - - private int maxRetries = 2; - - private Optional initialRetryDelayMillis = Optional.empty(); - - private Optional maxRetryDelayMillis = Optional.empty(); - - private Optional retryJitterFactor = Optional.empty(); - - private Optional timeout = Optional.empty(); - - private OkHttpClient httpClient = null; - - private boolean ownsHttpClient = true; - - private AtomicBoolean closed; - - private Set openWebSockets; - - private Optional logging = Optional.empty(); - - private Optional webSocketFactory = Optional.empty(); - - public Builder environment(Environment environment) { - this.environment = environment; - return this; - } - - public Builder addHeader(String key, String value) { - if (value != null) { - this.headers.put(key, value); - } - return this; - } - - public Builder addHeader(String key, Supplier value) { - this.headerSuppliers.put(key, value); - return this; - } - - /** - * Override the timeout in seconds. Defaults to 60 seconds. - */ - public Builder timeout(int timeout) { - this.timeout = Optional.of(timeout); - return this; - } - - /** - * Override the timeout in seconds. Defaults to 60 seconds. - */ - public Builder timeout(Optional timeout) { - this.timeout = timeout; - return this; - } - - /** - * Override the maximum number of retries. Defaults to 2 retries. - */ - public Builder maxRetries(int maxRetries) { - this.maxRetries = maxRetries; - return this; - } - - /** - * Override the initial delay (in milliseconds) used for exponential backoff between retries. Defaults to 1000 milliseconds. - */ - public Builder initialRetryDelayMillis(long initialRetryDelayMillis) { - this.initialRetryDelayMillis = Optional.of(initialRetryDelayMillis); - return this; - } - - /** - * Override the maximum delay (in milliseconds) between retries. Defaults to 60000 milliseconds. - */ - public Builder maxRetryDelayMillis(long maxRetryDelayMillis) { - this.maxRetryDelayMillis = Optional.of(maxRetryDelayMillis); - return this; - } - - /** - * Override the jitter factor (between 0 and 1) applied to retry delays. Defaults to 0.2. - */ - public Builder retryJitterFactor(double retryJitterFactor) { - this.retryJitterFactor = Optional.of(retryJitterFactor); - return this; - } - - /** - * Sets the underlying OkHttp client. The caller retains ownership of its lifecycle: - * close() will not shut down its dispatcher executor or evict its connection pool. - */ - public Builder httpClient(OkHttpClient httpClient) { - this.httpClient = httpClient; - this.ownsHttpClient = httpClient == null; - return this; - } - - /** - * Set a custom WebSocketFactory for creating WebSocket connections. - */ - public Builder webSocketFactory(WebSocketFactory webSocketFactory) { - this.webSocketFactory = Optional.of(webSocketFactory); - return this; - } - - /** - * Configure logging for the SDK. Silent by default — no log output unless explicitly configured. - */ - public Builder logging(LogConfig logging) { - this.logging = Optional.of(logging); - return this; - } - - public ClientOptions build() { - OkHttpClient.Builder httpClientBuilder = - this.httpClient != null ? this.httpClient.newBuilder() : new OkHttpClient.Builder(); - - if (this.httpClient != null) { - timeout.ifPresent(timeout -> httpClientBuilder - .callTimeout(timeout, TimeUnit.SECONDS) - .connectTimeout(0, TimeUnit.SECONDS) - .writeTimeout(0, TimeUnit.SECONDS) - .readTimeout(0, TimeUnit.SECONDS)); - } else { - httpClientBuilder - .callTimeout(this.timeout.orElse(60), TimeUnit.SECONDS) - .connectTimeout(0, TimeUnit.SECONDS) - .writeTimeout(0, TimeUnit.SECONDS) - .readTimeout(0, TimeUnit.SECONDS) - .addInterceptor(new RetryInterceptor( - this.maxRetries, - this.initialRetryDelayMillis, - this.maxRetryDelayMillis, - this.retryJitterFactor)); - } - - Logger logger = Logger.from(this.logging); - httpClientBuilder.addInterceptor(new LoggingInterceptor(logger)); - httpClientBuilder.addInterceptor(new ResponseDecompressionInterceptor()); - - this.httpClient = httpClientBuilder.build(); - this.timeout = Optional.of(httpClient.callTimeoutMillis() / 1000); - - AtomicBoolean closedToUse = this.closed != null ? this.closed : new AtomicBoolean(false); - Set openWebSocketsToUse = - this.openWebSockets != null ? this.openWebSockets : ConcurrentHashMap.newKeySet(); - - return new ClientOptions( - environment, - headers, - headerSuppliers, - httpClient, - this.ownsHttpClient, - closedToUse, - openWebSocketsToUse, - this.timeout.get(), - this.maxRetries, - this.initialRetryDelayMillis, - this.maxRetryDelayMillis, - this.retryJitterFactor, - this.webSocketFactory, - this.logging); - } - - /** - * Create a new Builder initialized with values from an existing ClientOptions - */ - public static Builder from(ClientOptions clientOptions) { - Builder builder = new Builder(); - builder.environment = clientOptions.environment(); - builder.timeout = Optional.of(clientOptions.timeout(null)); - builder.httpClient = clientOptions.httpClient(); - builder.ownsHttpClient = clientOptions.ownsHttpClient; - builder.closed = clientOptions.closed; - builder.openWebSockets = clientOptions.openWebSockets; - builder.headers.putAll(clientOptions.headers); - builder.headerSuppliers.putAll(clientOptions.headerSuppliers); - builder.maxRetries = clientOptions.maxRetries(); - builder.initialRetryDelayMillis = clientOptions.initialRetryDelayMillis(); - builder.maxRetryDelayMillis = clientOptions.maxRetryDelayMillis(); - builder.retryJitterFactor = clientOptions.retryJitterFactor(); - builder.logging = clientOptions.logging(); - return builder; - } - } -} diff --git a/src/main/java/com/deepgram/core/ReconnectingWebSocketListener.java b/src/main/java/com/deepgram/core/ReconnectingWebSocketListener.java index dc32da2..b51aacc 100644 --- a/src/main/java/com/deepgram/core/ReconnectingWebSocketListener.java +++ b/src/main/java/com/deepgram/core/ReconnectingWebSocketListener.java @@ -27,13 +27,7 @@ * Provides production-ready resilience for WebSocket connections. */ public abstract class ReconnectingWebSocketListener extends WebSocketListener { - private final long minReconnectionDelayMs; - - private final long maxReconnectionDelayMs; - - private final double reconnectionDelayGrowFactor; - - private final int maxRetries; + private volatile ReconnectOptions activeOptions; private final int maxEnqueuedMessages; @@ -81,22 +75,30 @@ public ReconnectingWebSocketListener( ReconnectingWebSocketListener.ReconnectOptions options, Supplier connectionSupplier, BooleanSupplier closedCheck) { - this.minReconnectionDelayMs = options.minReconnectionDelayMs; - this.maxReconnectionDelayMs = options.maxReconnectionDelayMs; - this.reconnectionDelayGrowFactor = options.reconnectionDelayGrowFactor; - this.maxRetries = options.maxRetries; + this.activeOptions = options; this.maxEnqueuedMessages = options.maxEnqueuedMessages; this.connectionSupplier = connectionSupplier; this.closedCheck = closedCheck; } + /** + * Replaces option-derived reconnect settings without rebuilding generated clients. + * + * @param options replacement options; {@code null} is a no-op + */ + public void applyOptionsOverride(ReconnectOptions options) { + if (options != null) { + this.activeOptions = options; + } + } + /** * Initiates a WebSocket connection with automatic reconnection enabled. * * Connection behavior: - * - Times out after 4000 milliseconds + * - Times out after {@code ReconnectOptions.connectionTimeoutMs} (default 4000ms) * - Thread-safe via atomic lock (returns immediately if connection in progress) - * - Retry count not incremented for initial connection attempt + * - {@code maxRetries} counts retries only; {@code maxRetries(0)} means connect once, do not retry * * Error handling: * - TimeoutException: Includes retry attempt context @@ -114,18 +116,20 @@ public void connect() { onWebSocketFailure(null, new IllegalStateException("root client has been closed"), null); return; } - if (retryCount.get() >= maxRetries) { + ReconnectOptions options = this.activeOptions; + if (retryCount.get() > options.maxRetries) { connectLock.set(false); return; } try { CompletableFuture connectionFuture = CompletableFuture.supplyAsync(connectionSupplier); try { - webSocket = connectionFuture.get(4000, MILLISECONDS); + webSocket = connectionFuture.get(options.connectionTimeoutMs, MILLISECONDS); } catch (TimeoutException e) { connectionFuture.cancel(true); TimeoutException timeoutError = - new TimeoutException("WebSocket connection timeout after " + 4000 + " milliseconds" + new TimeoutException("WebSocket connection timeout after " + options.connectionTimeoutMs + + " milliseconds" + (retryCount.get() > 0 ? " (retry attempt #" + retryCount.get() + ")" : " (initial connection attempt)")); @@ -415,10 +419,12 @@ protected boolean shouldReconnectAfterClose(WebSocket webSocket, int code, Strin */ private long getNextDelay() { if (retryCount.get() == 1) { - return minReconnectionDelayMs; + return activeOptions.minReconnectionDelayMs; } - long delay = (long) (minReconnectionDelayMs * Math.pow(reconnectionDelayGrowFactor, retryCount.get() - 1)); - return Math.min(delay, maxReconnectionDelayMs); + ReconnectOptions options = this.activeOptions; + long delay = (long) + (options.minReconnectionDelayMs * Math.pow(options.reconnectionDelayGrowFactor, retryCount.get() - 1)); + return Math.min(delay, options.maxReconnectionDelayMs); } /** @@ -504,12 +510,15 @@ public static final class ReconnectOptions { public final int maxEnqueuedMessages; + public final long connectionTimeoutMs; + private ReconnectOptions(Builder builder) { this.minReconnectionDelayMs = builder.minReconnectionDelayMs; this.maxReconnectionDelayMs = builder.maxReconnectionDelayMs; this.reconnectionDelayGrowFactor = builder.reconnectionDelayGrowFactor; this.maxRetries = builder.maxRetries; this.maxEnqueuedMessages = builder.maxEnqueuedMessages; + this.connectionTimeoutMs = builder.connectionTimeoutMs; } public static Builder builder() { @@ -527,12 +536,15 @@ public static final class Builder { private int maxEnqueuedMessages; + private long connectionTimeoutMs; + public Builder() { this.minReconnectionDelayMs = 1000; this.maxReconnectionDelayMs = 10000; this.reconnectionDelayGrowFactor = 1.3; this.maxRetries = 2147483647; this.maxEnqueuedMessages = 1000; + this.connectionTimeoutMs = 4000; } public Builder minReconnectionDelayMs(long minReconnectionDelayMs) { @@ -560,6 +572,14 @@ public Builder maxEnqueuedMessages(int maxEnqueuedMessages) { return this; } + /** + * Sets the per-attempt connection timeout in milliseconds. Defaults to {@code 4000}. + */ + public Builder connectionTimeoutMs(long connectionTimeoutMs) { + this.connectionTimeoutMs = connectionTimeoutMs; + return this; + } + /** * Builds the ReconnectOptions with validation. * @@ -592,6 +612,9 @@ public ReconnectOptions build() { if (maxEnqueuedMessages < 0) { throw new IllegalArgumentException("maxEnqueuedMessages must be non-negative"); } + if (connectionTimeoutMs <= 0) { + throw new IllegalArgumentException("connectionTimeoutMs must be positive"); + } return new ReconnectOptions(this); } } diff --git a/src/main/java/com/deepgram/core/ReconnectingWebSocketListener.java.bak b/src/main/java/com/deepgram/core/ReconnectingWebSocketListener.java.bak deleted file mode 100644 index 294316b..0000000 --- a/src/main/java/com/deepgram/core/ReconnectingWebSocketListener.java.bak +++ /dev/null @@ -1,622 +0,0 @@ -/** - * This file was auto-generated by Fern from our API Definition. - */ -package com.deepgram.core; - -import static java.util.concurrent.TimeUnit.*; - -import java.util.ArrayList; -import java.util.concurrent.CompletableFuture; -import java.util.concurrent.ConcurrentLinkedQueue; -import java.util.concurrent.ExecutionException; -import java.util.concurrent.Executors; -import java.util.concurrent.ScheduledExecutorService; -import java.util.concurrent.TimeoutException; -import java.util.concurrent.atomic.AtomicBoolean; -import java.util.concurrent.atomic.AtomicInteger; -import java.util.function.BooleanSupplier; -import java.util.function.Consumer; -import java.util.function.Supplier; -import okhttp3.Response; -import okhttp3.WebSocket; -import okhttp3.WebSocketListener; -import okio.ByteString; - -/** - * WebSocketListener with automatic reconnection, exponential backoff, and message queuing. - * Provides production-ready resilience for WebSocket connections. - */ -public abstract class ReconnectingWebSocketListener extends WebSocketListener { - // A single volatile reference keeps an override internally consistent. - private volatile ReconnectOptions activeOptions; - - private final int maxEnqueuedMessages; - - private final AtomicInteger retryCount = new AtomicInteger(0); - - private final AtomicBoolean connectLock = new AtomicBoolean(false); - - private final AtomicBoolean shouldReconnect = new AtomicBoolean(true); - - protected volatile WebSocket webSocket; - - private volatile long connectionEstablishedTime = 0L; - - private final ConcurrentLinkedQueue messageQueue = new ConcurrentLinkedQueue<>(); - - private final ConcurrentLinkedQueue binaryMessageQueue = new ConcurrentLinkedQueue<>(); - - private final ScheduledExecutorService reconnectExecutor = Executors.newSingleThreadScheduledExecutor(); - - private final Supplier connectionSupplier; - - private final BooleanSupplier closedCheck; - - /** - * Creates a new reconnecting WebSocket listener. - * - * @param options Reconnection configuration options - * @param connectionSupplier Supplier that creates new WebSocket connections - */ - public ReconnectingWebSocketListener( - ReconnectingWebSocketListener.ReconnectOptions options, Supplier connectionSupplier) { - this(options, connectionSupplier, () -> false); - } - - /** - * Creates a new reconnecting WebSocket listener. - * - * @param options Reconnection configuration options - * @param connectionSupplier Supplier that creates new WebSocket connections - * @param closedCheck Returns true once the owning client has been closed; connect and - * reconnect attempts made after that point fail with an {@link IllegalStateException} - * instead of being retried - */ - public ReconnectingWebSocketListener( - ReconnectingWebSocketListener.ReconnectOptions options, - Supplier connectionSupplier, - BooleanSupplier closedCheck) { - this.activeOptions = options; - this.maxEnqueuedMessages = options.maxEnqueuedMessages; - this.connectionSupplier = connectionSupplier; - this.closedCheck = closedCheck; - } - - /** - * Replaces option-derived reconnect settings without rebuilding generated clients. - * - * @param options replacement options; {@code null} is a no-op - */ - public void applyOptionsOverride(ReconnectOptions options) { - if (options != null) { - this.activeOptions = options; - } - } - - /** - * Initiates a WebSocket connection with automatic reconnection enabled. - * - * Connection behavior: - * - Times out after {@code ReconnectOptions.connectionTimeoutMs} (default 4000ms) - * - Thread-safe via atomic lock (returns immediately if connection in progress) - * - {@code maxRetries} counts retries only; {@code maxRetries(0)} means connect once, do not retry - * - * Error handling: - * - TimeoutException: Includes retry attempt context - * - InterruptedException: Preserves thread interruption status - * - ExecutionException: Extracts actual cause and adds context - * - Owning client closed: reports an IllegalStateException once and stops reconnecting - */ - public void connect() { - if (!connectLock.compareAndSet(false, true)) { - return; - } - if (closedCheck.getAsBoolean()) { - shouldReconnect.set(false); - connectLock.set(false); - onWebSocketFailure(null, new IllegalStateException("root client has been closed"), null); - return; - } - ReconnectOptions options = this.activeOptions; - if (retryCount.get() > options.maxRetries) { - connectLock.set(false); - return; - } - try { - CompletableFuture connectionFuture = CompletableFuture.supplyAsync(connectionSupplier); - try { - webSocket = connectionFuture.get(options.connectionTimeoutMs, MILLISECONDS); - } catch (TimeoutException e) { - connectionFuture.cancel(true); - TimeoutException timeoutError = - new TimeoutException("WebSocket connection timeout after " + options.connectionTimeoutMs + " milliseconds" - + (retryCount.get() > 0 - ? " (retry attempt #" + retryCount.get() + ")" - : " (initial connection attempt)")); - onWebSocketFailure(null, timeoutError, null); - if (shouldReconnect.get()) { - scheduleReconnect(); - } - } catch (InterruptedException e) { - connectionFuture.cancel(true); - Thread.currentThread().interrupt(); - InterruptedException interruptError = new InterruptedException("WebSocket connection interrupted" - + (retryCount.get() > 0 - ? " during retry attempt #" + retryCount.get() - : " during initial connection")); - interruptError.initCause(e); - onWebSocketFailure(null, interruptError, null); - } catch (ExecutionException e) { - Throwable cause = e.getCause() != null ? e.getCause() : e; - String context = retryCount.get() > 0 - ? "WebSocket connection failed during retry attempt #" + retryCount.get() - : "WebSocket connection failed during initial attempt"; - RuntimeException wrappedException = new RuntimeException( - context + ": " + cause.getClass().getSimpleName() + ": " + cause.getMessage()); - wrappedException.initCause(cause); - onWebSocketFailure(null, wrappedException, null); - if (shouldReconnect.get()) { - scheduleReconnect(); - } - } - } finally { - connectLock.set(false); - } - } - - /** - * Disconnects the WebSocket and disables automatic reconnection. - * - * This method: - * - Disables automatic reconnection - * - Clears queued messages to prevent stale data - * - Closes the WebSocket with standard close code 1000 - * - Properly shuts down the reconnect executor to prevent thread leaks - * - Waits up to 5 seconds for executor termination - */ - public void disconnect() { - shouldReconnect.set(false); - messageQueue.clear(); - binaryMessageQueue.clear(); - if (webSocket != null) { - webSocket.close(1000, "Client disconnecting"); - } - reconnectExecutor.shutdown(); - try { - if (!reconnectExecutor.awaitTermination(5, SECONDS)) { - reconnectExecutor.shutdownNow(); - } - } catch (InterruptedException e) { - reconnectExecutor.shutdownNow(); - Thread.currentThread().interrupt(); - } - } - - /** - * Sends a message or queues it if not connected. - * - * Thread-safe: Synchronized to prevent race conditions with flushMessageQueue(). - * - * Behavior: - * - If connected: Attempts direct send, queues if buffer full - * - If disconnected: Queues message up to maxEnqueuedMessages limit - * - If queue full: Message is dropped - * - * @param message The message to send - * @return true if sent immediately, false if queued or dropped - */ - public synchronized boolean send(String message) { - return send(message, null); - } - - /** - * Sends a message or queues it if not connected, exposing the accepting socket. - * - * Behaves like {@link #send(String)}, but additionally invokes {@code onSent} with the - * WebSocket that accepted the message. The callback is only invoked when the message was - * sent directly, never when it was queued or dropped. This lets callers associate a - * protocol-level message with the specific connection it was delivered on, e.g. to decide - * in {@link #shouldReconnectAfterClose(WebSocket, int, String)} whether a later close of that same - * connection was expected. - * - * @param message The message to send - * @param onSent Callback receiving the WebSocket that accepted the message, or null - * @return true if sent immediately, false if queued or dropped - */ - public synchronized boolean send(String message, Consumer onSent) { - WebSocket ws = webSocket; - if (ws != null) { - boolean sent = ws.send(message); - if (sent && onSent != null) { - onSent.accept(ws); - } - if (!sent && messageQueue.size() < maxEnqueuedMessages) { - messageQueue.offer(message); - return false; - } - return sent; - } else { - if (messageQueue.size() < maxEnqueuedMessages) { - messageQueue.offer(message); - return false; - } - return false; - } - } - - /** - * Sends binary data or queues it if not connected. - * - * Thread-safe: Synchronized to prevent race conditions with flushMessageQueue(). - * - * Behavior: - * - If connected: Attempts direct send, queues if buffer full - * - If disconnected: Queues data up to maxEnqueuedMessages limit - * - If queue full: Data is dropped - * - * @param data The binary data to send - * @return true if sent immediately, false if queued or dropped - */ - public synchronized boolean sendBinary(ByteString data) { - return sendBinary(data, null); - } - - /** - * Sends binary data or queues it if not connected, exposing the accepting socket. - * - * Behaves like {@link #sendBinary(ByteString)}, but additionally invokes {@code onSent} with - * the WebSocket that accepted the data. The callback is only invoked when the data was sent - * directly, never when it was queued or dropped. - * - * @param data The binary data to send - * @param onSent Callback receiving the WebSocket that accepted the data, or null - * @return true if sent immediately, false if queued or dropped - */ - public synchronized boolean sendBinary(ByteString data, Consumer onSent) { - WebSocket ws = webSocket; - if (ws != null) { - boolean sent = ws.send(data); - if (sent && onSent != null) { - onSent.accept(ws); - } - if (!sent && binaryMessageQueue.size() < maxEnqueuedMessages) { - binaryMessageQueue.offer(data); - return false; - } - return sent; - } else { - if (binaryMessageQueue.size() < maxEnqueuedMessages) { - binaryMessageQueue.offer(data); - return false; - } - return false; - } - } - - /** - * Gets the current WebSocket instance. - * Thread-safe method to access the WebSocket connection. - * @return the WebSocket or null if not connected - */ - public WebSocket getWebSocket() { - return webSocket; - } - - /** - * @hidden - */ - @Override - public void onOpen(WebSocket webSocket, Response response) { - this.webSocket = webSocket; - connectionEstablishedTime = System.currentTimeMillis(); - retryCount.set(0); - flushMessageQueue(); - onWebSocketOpen(webSocket, response); - } - - @Override - public void onMessage(WebSocket webSocket, String text) { - onWebSocketMessage(webSocket, text); - } - - @Override - public void onMessage(WebSocket webSocket, ByteString bytes) { - onWebSocketBinaryMessage(webSocket, bytes); - } - - /** - * @hidden - */ - @Override - public void onFailure(WebSocket webSocket, Throwable t, Response response) { - this.webSocket = null; - long uptime = 0L; - if (connectionEstablishedTime > 0) { - uptime = System.currentTimeMillis() - connectionEstablishedTime; - if (uptime >= 5000) { - retryCount.set(0); - } - } - connectionEstablishedTime = 0L; - Throwable enhancedError = t; - if (t != null) { - String errorContext = "WebSocket connection failed"; - if (uptime > 0) { - errorContext += " after " + (uptime / 1000) + " seconds"; - } - if (response != null) { - errorContext += " with HTTP " + response.code() + " " + response.message(); - } - enhancedError = - new RuntimeException(errorContext + ": " + t.getClass().getSimpleName() + ": " + t.getMessage()); - enhancedError.initCause(t); - } - onWebSocketFailure(webSocket, enhancedError, response); - if (shouldReconnect.get()) { - scheduleReconnect(); - } - } - - /** - * Acknowledges a peer-initiated close so OkHttp can complete the close handshake and - * invoke {@link #onClosed(WebSocket, int, String)}. - * - * Code 1005 is a local "no status received" sentinel reported when the peer sent an empty - * close frame. It is not a valid code to put on the wire, so it is acknowledged with - * the normal closure code 1000 instead. - * - * @hidden - */ - @Override - public void onClosing(WebSocket webSocket, int code, String reason) { - webSocket.close(code == 1005 ? 1000 : code, reason); - } - - /** - * @hidden - */ - @Override - public void onClosed(WebSocket webSocket, int code, String reason) { - this.webSocket = null; - if (connectionEstablishedTime > 0) { - long uptime = System.currentTimeMillis() - connectionEstablishedTime; - if (uptime >= 5000) { - retryCount.set(0); - } - } - connectionEstablishedTime = 0L; - onWebSocketClosed(webSocket, code, reason); - if (shouldReconnect.get() && shouldReconnectAfterClose(webSocket, code, reason)) { - scheduleReconnect(); - } - } - - /** - * Decides whether a completed close handshake should trigger a reconnect. - * - * Only consulted when reconnection has not been disabled via {@link #disconnect()}. - * The default treats normal closure (1000) as terminal and reconnects on any - * other code. Subclasses can override this when protocol context establishes that a - * particular close is terminal, for example a no-status close following a message that - * ends the stream. - * - * @param webSocket The WebSocket that was closed - * @param code The close status code reported by OkHttp - * @param reason The close reason sent by the peer, or an empty string - * @return true to schedule a reconnect, false to stay disconnected - */ - protected boolean shouldReconnectAfterClose(WebSocket webSocket, int code, String reason) { - return code != 1000; - } - - /** - * Calculates the next reconnection delay using exponential backoff. - * - * Uses 0-based retry count where: - * - 0 = initial connection (not used by this method) - * - 1 = first retry (returns minReconnectionDelayMs) - * - 2+ = exponential backoff up to maxReconnectionDelayMs - */ - private long getNextDelay() { - if (retryCount.get() == 1) { - return activeOptions.minReconnectionDelayMs; - } - ReconnectOptions options = this.activeOptions; - long delay = (long) - (options.minReconnectionDelayMs * Math.pow(options.reconnectionDelayGrowFactor, retryCount.get() - 1)); - return Math.min(delay, options.maxReconnectionDelayMs); - } - - /** - * Schedules a reconnection attempt with appropriate delay. - * Increments retry count and uses exponential backoff. - * Does nothing once the owning client has been closed. - */ - private void scheduleReconnect() { - if (closedCheck.getAsBoolean()) { - shouldReconnect.set(false); - return; - } - retryCount.incrementAndGet(); - long delay = getNextDelay(); - reconnectExecutor.schedule(this::connect, delay, MILLISECONDS); - } - - /** - * Sends all queued messages after reconnection. - * - * Thread-safe: Synchronized to prevent race conditions with send() method. - * - * Algorithm: - * 1. Drains queue into temporary list to avoid holding lock during sends - * 2. Attempts to send each message in order - * 3. If any send fails, re-queues that message and all subsequent messages - * 4. Preserves message ordering during re-queueing - * 5. Repeats for binary message queue - */ - private synchronized void flushMessageQueue() { - WebSocket ws = webSocket; - if (ws != null) { - ArrayList tempQueue = new ArrayList<>(); - String message; - while ((message = messageQueue.poll()) != null) { - tempQueue.add(message); - } - for (int i = 0; i < tempQueue.size(); i++) { - if (!ws.send(tempQueue.get(i))) { - for (int j = i; j < tempQueue.size(); j++) { - messageQueue.offer(tempQueue.get(j)); - } - break; - } - } - ArrayList tempBinaryQueue = new ArrayList<>(); - ByteString binaryMsg; - while ((binaryMsg = binaryMessageQueue.poll()) != null) { - tempBinaryQueue.add(binaryMsg); - } - for (int i = 0; i < tempBinaryQueue.size(); i++) { - if (!ws.send(tempBinaryQueue.get(i))) { - for (int j = i; j < tempBinaryQueue.size(); j++) { - binaryMessageQueue.offer(tempBinaryQueue.get(j)); - } - break; - } - } - } - } - - protected abstract void onWebSocketOpen(WebSocket webSocket, Response response); - - protected abstract void onWebSocketMessage(WebSocket webSocket, String text); - - protected abstract void onWebSocketBinaryMessage(WebSocket webSocket, ByteString bytes); - - protected abstract void onWebSocketFailure(WebSocket webSocket, Throwable t, Response response); - - protected abstract void onWebSocketClosed(WebSocket webSocket, int code, String reason); - - /** - * Configuration options for automatic reconnection. - */ - public static final class ReconnectOptions { - public final long minReconnectionDelayMs; - - public final long maxReconnectionDelayMs; - - public final double reconnectionDelayGrowFactor; - - public final int maxRetries; - - public final int maxEnqueuedMessages; - - public final long connectionTimeoutMs; - - private ReconnectOptions(Builder builder) { - this.minReconnectionDelayMs = builder.minReconnectionDelayMs; - this.maxReconnectionDelayMs = builder.maxReconnectionDelayMs; - this.reconnectionDelayGrowFactor = builder.reconnectionDelayGrowFactor; - this.maxRetries = builder.maxRetries; - this.maxEnqueuedMessages = builder.maxEnqueuedMessages; - this.connectionTimeoutMs = builder.connectionTimeoutMs; - } - - public static Builder builder() { - return new Builder(); - } - - public static final class Builder { - private long minReconnectionDelayMs; - - private long maxReconnectionDelayMs; - - private double reconnectionDelayGrowFactor; - - private int maxRetries; - - private int maxEnqueuedMessages; - - private long connectionTimeoutMs; - - public Builder() { - this.minReconnectionDelayMs = 1000; - this.maxReconnectionDelayMs = 10000; - this.reconnectionDelayGrowFactor = 1.3; - this.maxRetries = 2147483647; - this.maxEnqueuedMessages = 1000; - this.connectionTimeoutMs = 4000; - } - - public Builder minReconnectionDelayMs(long minReconnectionDelayMs) { - this.minReconnectionDelayMs = minReconnectionDelayMs; - return this; - } - - public Builder maxReconnectionDelayMs(long maxReconnectionDelayMs) { - this.maxReconnectionDelayMs = maxReconnectionDelayMs; - return this; - } - - public Builder reconnectionDelayGrowFactor(double reconnectionDelayGrowFactor) { - this.reconnectionDelayGrowFactor = reconnectionDelayGrowFactor; - return this; - } - - public Builder maxRetries(int maxRetries) { - this.maxRetries = maxRetries; - return this; - } - - public Builder maxEnqueuedMessages(int maxEnqueuedMessages) { - this.maxEnqueuedMessages = maxEnqueuedMessages; - return this; - } - - /** - * Sets the per-attempt connection timeout in milliseconds. Defaults to {@code 4000}. - */ - public Builder connectionTimeoutMs(long connectionTimeoutMs) { - this.connectionTimeoutMs = connectionTimeoutMs; - return this; - } - - /** - * Builds the ReconnectOptions with validation. - * - * Validates that: - * - All delay values are positive - * - minReconnectionDelayMs <= maxReconnectionDelayMs - * - reconnectionDelayGrowFactor >= 1.0 - * - maxRetries and maxEnqueuedMessages are non-negative - * - * @return The validated ReconnectOptions instance - * @throws IllegalArgumentException if configuration is invalid - */ - public ReconnectOptions build() { - if (minReconnectionDelayMs <= 0) { - throw new IllegalArgumentException("minReconnectionDelayMs must be positive"); - } - if (maxReconnectionDelayMs <= 0) { - throw new IllegalArgumentException("maxReconnectionDelayMs must be positive"); - } - if (minReconnectionDelayMs > maxReconnectionDelayMs) { - throw new IllegalArgumentException("minReconnectionDelayMs (" + minReconnectionDelayMs - + ") must not exceed maxReconnectionDelayMs (" + maxReconnectionDelayMs + ")"); - } - if (reconnectionDelayGrowFactor < 1.0) { - throw new IllegalArgumentException("reconnectionDelayGrowFactor must be >= 1.0"); - } - if (maxRetries < 0) { - throw new IllegalArgumentException("maxRetries must be non-negative"); - } - if (maxEnqueuedMessages < 0) { - throw new IllegalArgumentException("maxEnqueuedMessages must be non-negative"); - } - if (connectionTimeoutMs <= 0) { - throw new IllegalArgumentException("connectionTimeoutMs must be positive"); - } - return new ReconnectOptions(this); - } - } - } -} diff --git a/src/main/java/com/deepgram/resources/agent/v1/types/AgentV1AgentAudioDone.java b/src/main/java/com/deepgram/resources/agent/v1/types/AgentV1AgentAudioDone.java index 71825ed..5962f4b 100644 --- a/src/main/java/com/deepgram/resources/agent/v1/types/AgentV1AgentAudioDone.java +++ b/src/main/java/com/deepgram/resources/agent/v1/types/AgentV1AgentAudioDone.java @@ -36,6 +36,11 @@ public boolean equals(Object other) { return other instanceof AgentV1AgentAudioDone; } + @java.lang.Override + public int hashCode() { + return getClass().hashCode(); + } + @JsonAnyGetter public Map getAdditionalProperties() { return this.additionalProperties; diff --git a/src/main/java/com/deepgram/resources/agent/v1/types/AgentV1AgentAudioDone.java.bak b/src/main/java/com/deepgram/resources/agent/v1/types/AgentV1AgentAudioDone.java.bak deleted file mode 100644 index 5962f4b..0000000 --- a/src/main/java/com/deepgram/resources/agent/v1/types/AgentV1AgentAudioDone.java.bak +++ /dev/null @@ -1,83 +0,0 @@ -/** - * This file was auto-generated by Fern from our API Definition. - */ -package com.deepgram.resources.agent.v1.types; - -import com.deepgram.core.ObjectMappers; -import com.fasterxml.jackson.annotation.JsonAnyGetter; -import com.fasterxml.jackson.annotation.JsonAnySetter; -import com.fasterxml.jackson.annotation.JsonIgnoreProperties; -import com.fasterxml.jackson.annotation.JsonInclude; -import com.fasterxml.jackson.annotation.JsonProperty; -import com.fasterxml.jackson.databind.annotation.JsonDeserialize; -import java.util.HashMap; -import java.util.Map; - -@JsonInclude(JsonInclude.Include.NON_ABSENT) -@JsonDeserialize(builder = AgentV1AgentAudioDone.Builder.class) -public final class AgentV1AgentAudioDone { - private final Map additionalProperties; - - private AgentV1AgentAudioDone(Map additionalProperties) { - this.additionalProperties = additionalProperties; - } - - /** - * @return Message type identifier indicating the agent has finished sending audio - */ - @JsonProperty("type") - public String getType() { - return "AgentAudioDone"; - } - - @java.lang.Override - public boolean equals(Object other) { - if (this == other) return true; - return other instanceof AgentV1AgentAudioDone; - } - - @java.lang.Override - public int hashCode() { - return getClass().hashCode(); - } - - @JsonAnyGetter - public Map getAdditionalProperties() { - return this.additionalProperties; - } - - @java.lang.Override - public String toString() { - return ObjectMappers.stringify(this); - } - - public static Builder builder() { - return new Builder(); - } - - @JsonIgnoreProperties(ignoreUnknown = true) - public static final class Builder { - @JsonAnySetter - private Map additionalProperties = new HashMap<>(); - - private Builder() {} - - public Builder from(AgentV1AgentAudioDone other) { - return this; - } - - public AgentV1AgentAudioDone build() { - return new AgentV1AgentAudioDone(additionalProperties); - } - - public Builder additionalProperty(String key, Object value) { - this.additionalProperties.put(key, value); - return this; - } - - public Builder additionalProperties(Map additionalProperties) { - this.additionalProperties.putAll(additionalProperties); - return this; - } - } -} diff --git a/src/main/java/com/deepgram/resources/agent/v1/types/AgentV1ForceEndTurn.java b/src/main/java/com/deepgram/resources/agent/v1/types/AgentV1ForceEndTurn.java index 1672d2d..9bd5f76 100644 --- a/src/main/java/com/deepgram/resources/agent/v1/types/AgentV1ForceEndTurn.java +++ b/src/main/java/com/deepgram/resources/agent/v1/types/AgentV1ForceEndTurn.java @@ -36,6 +36,11 @@ public boolean equals(Object other) { return other instanceof AgentV1ForceEndTurn; } + @java.lang.Override + public int hashCode() { + return getClass().hashCode(); + } + @JsonAnyGetter public Map getAdditionalProperties() { return this.additionalProperties; diff --git a/src/main/java/com/deepgram/resources/agent/v1/types/AgentV1ForceEndTurn.java.bak b/src/main/java/com/deepgram/resources/agent/v1/types/AgentV1ForceEndTurn.java.bak deleted file mode 100644 index 9bd5f76..0000000 --- a/src/main/java/com/deepgram/resources/agent/v1/types/AgentV1ForceEndTurn.java.bak +++ /dev/null @@ -1,83 +0,0 @@ -/** - * This file was auto-generated by Fern from our API Definition. - */ -package com.deepgram.resources.agent.v1.types; - -import com.deepgram.core.ObjectMappers; -import com.fasterxml.jackson.annotation.JsonAnyGetter; -import com.fasterxml.jackson.annotation.JsonAnySetter; -import com.fasterxml.jackson.annotation.JsonIgnoreProperties; -import com.fasterxml.jackson.annotation.JsonInclude; -import com.fasterxml.jackson.annotation.JsonProperty; -import com.fasterxml.jackson.databind.annotation.JsonDeserialize; -import java.util.HashMap; -import java.util.Map; - -@JsonInclude(JsonInclude.Include.NON_ABSENT) -@JsonDeserialize(builder = AgentV1ForceEndTurn.Builder.class) -public final class AgentV1ForceEndTurn { - private final Map additionalProperties; - - private AgentV1ForceEndTurn(Map additionalProperties) { - this.additionalProperties = additionalProperties; - } - - /** - * @return Message type identifier for forcing the end of the current turn - */ - @JsonProperty("type") - public String getType() { - return "ForceEndTurn"; - } - - @java.lang.Override - public boolean equals(Object other) { - if (this == other) return true; - return other instanceof AgentV1ForceEndTurn; - } - - @java.lang.Override - public int hashCode() { - return getClass().hashCode(); - } - - @JsonAnyGetter - public Map getAdditionalProperties() { - return this.additionalProperties; - } - - @java.lang.Override - public String toString() { - return ObjectMappers.stringify(this); - } - - public static Builder builder() { - return new Builder(); - } - - @JsonIgnoreProperties(ignoreUnknown = true) - public static final class Builder { - @JsonAnySetter - private Map additionalProperties = new HashMap<>(); - - private Builder() {} - - public Builder from(AgentV1ForceEndTurn other) { - return this; - } - - public AgentV1ForceEndTurn build() { - return new AgentV1ForceEndTurn(additionalProperties); - } - - public Builder additionalProperty(String key, Object value) { - this.additionalProperties.put(key, value); - return this; - } - - public Builder additionalProperties(Map additionalProperties) { - this.additionalProperties.putAll(additionalProperties); - return this; - } - } -} diff --git a/src/main/java/com/deepgram/resources/agent/v1/types/AgentV1KeepAlive.java b/src/main/java/com/deepgram/resources/agent/v1/types/AgentV1KeepAlive.java index 7842266..f82f59e 100644 --- a/src/main/java/com/deepgram/resources/agent/v1/types/AgentV1KeepAlive.java +++ b/src/main/java/com/deepgram/resources/agent/v1/types/AgentV1KeepAlive.java @@ -36,6 +36,11 @@ public boolean equals(Object other) { return other instanceof AgentV1KeepAlive; } + @java.lang.Override + public int hashCode() { + return getClass().hashCode(); + } + @JsonAnyGetter public Map getAdditionalProperties() { return this.additionalProperties; diff --git a/src/main/java/com/deepgram/resources/agent/v1/types/AgentV1KeepAlive.java.bak b/src/main/java/com/deepgram/resources/agent/v1/types/AgentV1KeepAlive.java.bak deleted file mode 100644 index f82f59e..0000000 --- a/src/main/java/com/deepgram/resources/agent/v1/types/AgentV1KeepAlive.java.bak +++ /dev/null @@ -1,83 +0,0 @@ -/** - * This file was auto-generated by Fern from our API Definition. - */ -package com.deepgram.resources.agent.v1.types; - -import com.deepgram.core.ObjectMappers; -import com.fasterxml.jackson.annotation.JsonAnyGetter; -import com.fasterxml.jackson.annotation.JsonAnySetter; -import com.fasterxml.jackson.annotation.JsonIgnoreProperties; -import com.fasterxml.jackson.annotation.JsonInclude; -import com.fasterxml.jackson.annotation.JsonProperty; -import com.fasterxml.jackson.databind.annotation.JsonDeserialize; -import java.util.HashMap; -import java.util.Map; - -@JsonInclude(JsonInclude.Include.NON_ABSENT) -@JsonDeserialize(builder = AgentV1KeepAlive.Builder.class) -public final class AgentV1KeepAlive { - private final Map additionalProperties; - - private AgentV1KeepAlive(Map additionalProperties) { - this.additionalProperties = additionalProperties; - } - - /** - * @return Message type identifier - */ - @JsonProperty("type") - public String getType() { - return "KeepAlive"; - } - - @java.lang.Override - public boolean equals(Object other) { - if (this == other) return true; - return other instanceof AgentV1KeepAlive; - } - - @java.lang.Override - public int hashCode() { - return getClass().hashCode(); - } - - @JsonAnyGetter - public Map getAdditionalProperties() { - return this.additionalProperties; - } - - @java.lang.Override - public String toString() { - return ObjectMappers.stringify(this); - } - - public static Builder builder() { - return new Builder(); - } - - @JsonIgnoreProperties(ignoreUnknown = true) - public static final class Builder { - @JsonAnySetter - private Map additionalProperties = new HashMap<>(); - - private Builder() {} - - public Builder from(AgentV1KeepAlive other) { - return this; - } - - public AgentV1KeepAlive build() { - return new AgentV1KeepAlive(additionalProperties); - } - - public Builder additionalProperty(String key, Object value) { - this.additionalProperties.put(key, value); - return this; - } - - public Builder additionalProperties(Map additionalProperties) { - this.additionalProperties.putAll(additionalProperties); - return this; - } - } -} diff --git a/src/main/java/com/deepgram/resources/agent/v1/types/AgentV1ListenUpdated.java b/src/main/java/com/deepgram/resources/agent/v1/types/AgentV1ListenUpdated.java index 65878c9..43b8628 100644 --- a/src/main/java/com/deepgram/resources/agent/v1/types/AgentV1ListenUpdated.java +++ b/src/main/java/com/deepgram/resources/agent/v1/types/AgentV1ListenUpdated.java @@ -36,6 +36,11 @@ public boolean equals(Object other) { return other instanceof AgentV1ListenUpdated; } + @java.lang.Override + public int hashCode() { + return getClass().hashCode(); + } + @JsonAnyGetter public Map getAdditionalProperties() { return this.additionalProperties; diff --git a/src/main/java/com/deepgram/resources/agent/v1/types/AgentV1ListenUpdated.java.bak b/src/main/java/com/deepgram/resources/agent/v1/types/AgentV1ListenUpdated.java.bak deleted file mode 100644 index 43b8628..0000000 --- a/src/main/java/com/deepgram/resources/agent/v1/types/AgentV1ListenUpdated.java.bak +++ /dev/null @@ -1,83 +0,0 @@ -/** - * This file was auto-generated by Fern from our API Definition. - */ -package com.deepgram.resources.agent.v1.types; - -import com.deepgram.core.ObjectMappers; -import com.fasterxml.jackson.annotation.JsonAnyGetter; -import com.fasterxml.jackson.annotation.JsonAnySetter; -import com.fasterxml.jackson.annotation.JsonIgnoreProperties; -import com.fasterxml.jackson.annotation.JsonInclude; -import com.fasterxml.jackson.annotation.JsonProperty; -import com.fasterxml.jackson.databind.annotation.JsonDeserialize; -import java.util.HashMap; -import java.util.Map; - -@JsonInclude(JsonInclude.Include.NON_ABSENT) -@JsonDeserialize(builder = AgentV1ListenUpdated.Builder.class) -public final class AgentV1ListenUpdated { - private final Map additionalProperties; - - private AgentV1ListenUpdated(Map additionalProperties) { - this.additionalProperties = additionalProperties; - } - - /** - * @return Message type identifier for listen update confirmation - */ - @JsonProperty("type") - public String getType() { - return "ListenUpdated"; - } - - @java.lang.Override - public boolean equals(Object other) { - if (this == other) return true; - return other instanceof AgentV1ListenUpdated; - } - - @java.lang.Override - public int hashCode() { - return getClass().hashCode(); - } - - @JsonAnyGetter - public Map getAdditionalProperties() { - return this.additionalProperties; - } - - @java.lang.Override - public String toString() { - return ObjectMappers.stringify(this); - } - - public static Builder builder() { - return new Builder(); - } - - @JsonIgnoreProperties(ignoreUnknown = true) - public static final class Builder { - @JsonAnySetter - private Map additionalProperties = new HashMap<>(); - - private Builder() {} - - public Builder from(AgentV1ListenUpdated other) { - return this; - } - - public AgentV1ListenUpdated build() { - return new AgentV1ListenUpdated(additionalProperties); - } - - public Builder additionalProperty(String key, Object value) { - this.additionalProperties.put(key, value); - return this; - } - - public Builder additionalProperties(Map additionalProperties) { - this.additionalProperties.putAll(additionalProperties); - return this; - } - } -} diff --git a/src/main/java/com/deepgram/resources/agent/v1/types/AgentV1PromptUpdated.java b/src/main/java/com/deepgram/resources/agent/v1/types/AgentV1PromptUpdated.java index 68d71cc..e6efced 100644 --- a/src/main/java/com/deepgram/resources/agent/v1/types/AgentV1PromptUpdated.java +++ b/src/main/java/com/deepgram/resources/agent/v1/types/AgentV1PromptUpdated.java @@ -36,6 +36,11 @@ public boolean equals(Object other) { return other instanceof AgentV1PromptUpdated; } + @java.lang.Override + public int hashCode() { + return getClass().hashCode(); + } + @JsonAnyGetter public Map getAdditionalProperties() { return this.additionalProperties; diff --git a/src/main/java/com/deepgram/resources/agent/v1/types/AgentV1PromptUpdated.java.bak b/src/main/java/com/deepgram/resources/agent/v1/types/AgentV1PromptUpdated.java.bak deleted file mode 100644 index e6efced..0000000 --- a/src/main/java/com/deepgram/resources/agent/v1/types/AgentV1PromptUpdated.java.bak +++ /dev/null @@ -1,83 +0,0 @@ -/** - * This file was auto-generated by Fern from our API Definition. - */ -package com.deepgram.resources.agent.v1.types; - -import com.deepgram.core.ObjectMappers; -import com.fasterxml.jackson.annotation.JsonAnyGetter; -import com.fasterxml.jackson.annotation.JsonAnySetter; -import com.fasterxml.jackson.annotation.JsonIgnoreProperties; -import com.fasterxml.jackson.annotation.JsonInclude; -import com.fasterxml.jackson.annotation.JsonProperty; -import com.fasterxml.jackson.databind.annotation.JsonDeserialize; -import java.util.HashMap; -import java.util.Map; - -@JsonInclude(JsonInclude.Include.NON_ABSENT) -@JsonDeserialize(builder = AgentV1PromptUpdated.Builder.class) -public final class AgentV1PromptUpdated { - private final Map additionalProperties; - - private AgentV1PromptUpdated(Map additionalProperties) { - this.additionalProperties = additionalProperties; - } - - /** - * @return Message type identifier for prompt update confirmation - */ - @JsonProperty("type") - public String getType() { - return "PromptUpdated"; - } - - @java.lang.Override - public boolean equals(Object other) { - if (this == other) return true; - return other instanceof AgentV1PromptUpdated; - } - - @java.lang.Override - public int hashCode() { - return getClass().hashCode(); - } - - @JsonAnyGetter - public Map getAdditionalProperties() { - return this.additionalProperties; - } - - @java.lang.Override - public String toString() { - return ObjectMappers.stringify(this); - } - - public static Builder builder() { - return new Builder(); - } - - @JsonIgnoreProperties(ignoreUnknown = true) - public static final class Builder { - @JsonAnySetter - private Map additionalProperties = new HashMap<>(); - - private Builder() {} - - public Builder from(AgentV1PromptUpdated other) { - return this; - } - - public AgentV1PromptUpdated build() { - return new AgentV1PromptUpdated(additionalProperties); - } - - public Builder additionalProperty(String key, Object value) { - this.additionalProperties.put(key, value); - return this; - } - - public Builder additionalProperties(Map additionalProperties) { - this.additionalProperties.putAll(additionalProperties); - return this; - } - } -} diff --git a/src/main/java/com/deepgram/resources/agent/v1/types/AgentV1SettingsAgentContextListenProvider.java b/src/main/java/com/deepgram/resources/agent/v1/types/AgentV1SettingsAgentContextListenProvider.java index eea1585..393f1f9 100644 --- a/src/main/java/com/deepgram/resources/agent/v1/types/AgentV1SettingsAgentContextListenProvider.java +++ b/src/main/java/com/deepgram/resources/agent/v1/types/AgentV1SettingsAgentContextListenProvider.java @@ -99,7 +99,7 @@ public interface Visitor { T _visitUnknown(Object unknownType); } - @JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "version", visible = true, defaultImpl = _UnknownValue.class) + @JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "version", visible = true, defaultImpl = V2Value.class) @JsonSubTypes({@JsonSubTypes.Type(V1Value.class), @JsonSubTypes.Type(V2Value.class)}) @JsonIgnoreProperties(ignoreUnknown = true) private interface Value { diff --git a/src/main/java/com/deepgram/resources/agent/v1/types/AgentV1SettingsAgentContextListenProvider.java.bak b/src/main/java/com/deepgram/resources/agent/v1/types/AgentV1SettingsAgentContextListenProvider.java.bak deleted file mode 100644 index 393f1f9..0000000 --- a/src/main/java/com/deepgram/resources/agent/v1/types/AgentV1SettingsAgentContextListenProvider.java.bak +++ /dev/null @@ -1,224 +0,0 @@ -/** - * This file was auto-generated by Fern from our API Definition. - */ -package com.deepgram.resources.agent.v1.types; - -import com.deepgram.types.DeepgramListenProviderV1; -import com.deepgram.types.DeepgramListenProviderV2; -import com.fasterxml.jackson.annotation.JsonCreator; -import com.fasterxml.jackson.annotation.JsonIgnoreProperties; -import com.fasterxml.jackson.annotation.JsonProperty; -import com.fasterxml.jackson.annotation.JsonSubTypes; -import com.fasterxml.jackson.annotation.JsonTypeInfo; -import com.fasterxml.jackson.annotation.JsonTypeName; -import com.fasterxml.jackson.annotation.JsonUnwrapped; -import com.fasterxml.jackson.annotation.JsonValue; -import java.util.Objects; -import java.util.Optional; - -public final class AgentV1SettingsAgentContextListenProvider { - private final Value value; - - @JsonCreator(mode = JsonCreator.Mode.DELEGATING) - private AgentV1SettingsAgentContextListenProvider(Value value) { - this.value = value; - } - - public T visit(Visitor visitor) { - return value.visit(visitor); - } - - public static AgentV1SettingsAgentContextListenProvider v1(DeepgramListenProviderV1 value) { - return new AgentV1SettingsAgentContextListenProvider(new V1Value(value)); - } - - public static AgentV1SettingsAgentContextListenProvider v2(DeepgramListenProviderV2 value) { - return new AgentV1SettingsAgentContextListenProvider(new V2Value(value)); - } - - public boolean isV1() { - return value instanceof V1Value; - } - - public boolean isV2() { - return value instanceof V2Value; - } - - public boolean _isUnknown() { - return value instanceof _UnknownValue; - } - - public Optional getV1() { - if (isV1()) { - return Optional.of(((V1Value) value).value); - } - return Optional.empty(); - } - - public Optional getV2() { - if (isV2()) { - return Optional.of(((V2Value) value).value); - } - return Optional.empty(); - } - - public Optional _getUnknown() { - if (_isUnknown()) { - return Optional.of(((_UnknownValue) value).value); - } - return Optional.empty(); - } - - @Override - public boolean equals(Object other) { - if (this == other) return true; - return other instanceof AgentV1SettingsAgentContextListenProvider - && value.equals(((AgentV1SettingsAgentContextListenProvider) other).value); - } - - @Override - public int hashCode() { - return Objects.hash(value); - } - - @Override - public String toString() { - return value.toString(); - } - - @JsonValue - private Value getValue() { - return this.value; - } - - public interface Visitor { - T visitV1(DeepgramListenProviderV1 v1); - - T visitV2(DeepgramListenProviderV2 v2); - - T _visitUnknown(Object unknownType); - } - - @JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "version", visible = true, defaultImpl = V2Value.class) - @JsonSubTypes({@JsonSubTypes.Type(V1Value.class), @JsonSubTypes.Type(V2Value.class)}) - @JsonIgnoreProperties(ignoreUnknown = true) - private interface Value { - T visit(Visitor visitor); - } - - @JsonTypeName("v1") - @JsonIgnoreProperties("version") - private static final class V1Value implements Value { - @JsonUnwrapped - @JsonIgnoreProperties(value = "version", allowSetters = true) - private DeepgramListenProviderV1 value; - - @JsonCreator(mode = JsonCreator.Mode.PROPERTIES) - private V1Value() {} - - private V1Value(DeepgramListenProviderV1 value) { - this.value = value; - } - - @java.lang.Override - public T visit(Visitor visitor) { - return visitor.visitV1(value); - } - - @java.lang.Override - public boolean equals(Object other) { - if (this == other) return true; - return other instanceof V1Value && equalTo((V1Value) other); - } - - private boolean equalTo(V1Value other) { - return value.equals(other.value); - } - - @java.lang.Override - public int hashCode() { - return Objects.hash(this.value); - } - - @java.lang.Override - public String toString() { - return "AgentV1SettingsAgentContextListenProvider{" + "value: " + value + "}"; - } - } - - @JsonTypeName("v2") - @JsonIgnoreProperties("version") - private static final class V2Value implements Value { - @JsonUnwrapped - @JsonIgnoreProperties(value = "version", allowSetters = true) - private DeepgramListenProviderV2 value; - - @JsonCreator(mode = JsonCreator.Mode.PROPERTIES) - private V2Value() {} - - private V2Value(DeepgramListenProviderV2 value) { - this.value = value; - } - - @java.lang.Override - public T visit(Visitor visitor) { - return visitor.visitV2(value); - } - - @java.lang.Override - public boolean equals(Object other) { - if (this == other) return true; - return other instanceof V2Value && equalTo((V2Value) other); - } - - private boolean equalTo(V2Value other) { - return value.equals(other.value); - } - - @java.lang.Override - public int hashCode() { - return Objects.hash(this.value); - } - - @java.lang.Override - public String toString() { - return "AgentV1SettingsAgentContextListenProvider{" + "value: " + value + "}"; - } - } - - @JsonIgnoreProperties("version") - private static final class _UnknownValue implements Value { - private String type; - - @JsonValue - private Object value; - - @JsonCreator(mode = JsonCreator.Mode.PROPERTIES) - private _UnknownValue(@JsonProperty("value") Object value) {} - - @java.lang.Override - public T visit(Visitor visitor) { - return visitor._visitUnknown(value); - } - - @java.lang.Override - public boolean equals(Object other) { - if (this == other) return true; - return other instanceof _UnknownValue && equalTo((_UnknownValue) other); - } - - private boolean equalTo(_UnknownValue other) { - return type.equals(other.type) && value.equals(other.value); - } - - @java.lang.Override - public int hashCode() { - return Objects.hash(this.type, this.value); - } - - @java.lang.Override - public String toString() { - return "AgentV1SettingsAgentContextListenProvider{" + "type: " + type + ", value: " + value + "}"; - } - } -} diff --git a/src/main/java/com/deepgram/resources/agent/v1/types/AgentV1SettingsAgentListenProvider.java b/src/main/java/com/deepgram/resources/agent/v1/types/AgentV1SettingsAgentListenProvider.java index 4309ec0..345ba2f 100644 --- a/src/main/java/com/deepgram/resources/agent/v1/types/AgentV1SettingsAgentListenProvider.java +++ b/src/main/java/com/deepgram/resources/agent/v1/types/AgentV1SettingsAgentListenProvider.java @@ -99,7 +99,7 @@ public interface Visitor { T _visitUnknown(Object unknownType); } - @JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "version", visible = true, defaultImpl = _UnknownValue.class) + @JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "version", visible = true, defaultImpl = V2Value.class) @JsonSubTypes({@JsonSubTypes.Type(V1Value.class), @JsonSubTypes.Type(V2Value.class)}) @JsonIgnoreProperties(ignoreUnknown = true) private interface Value { diff --git a/src/main/java/com/deepgram/resources/agent/v1/types/AgentV1SettingsAgentListenProvider.java.bak b/src/main/java/com/deepgram/resources/agent/v1/types/AgentV1SettingsAgentListenProvider.java.bak deleted file mode 100644 index 345ba2f..0000000 --- a/src/main/java/com/deepgram/resources/agent/v1/types/AgentV1SettingsAgentListenProvider.java.bak +++ /dev/null @@ -1,224 +0,0 @@ -/** - * This file was auto-generated by Fern from our API Definition. - */ -package com.deepgram.resources.agent.v1.types; - -import com.deepgram.types.DeepgramListenProviderV1; -import com.deepgram.types.DeepgramListenProviderV2; -import com.fasterxml.jackson.annotation.JsonCreator; -import com.fasterxml.jackson.annotation.JsonIgnoreProperties; -import com.fasterxml.jackson.annotation.JsonProperty; -import com.fasterxml.jackson.annotation.JsonSubTypes; -import com.fasterxml.jackson.annotation.JsonTypeInfo; -import com.fasterxml.jackson.annotation.JsonTypeName; -import com.fasterxml.jackson.annotation.JsonUnwrapped; -import com.fasterxml.jackson.annotation.JsonValue; -import java.util.Objects; -import java.util.Optional; - -public final class AgentV1SettingsAgentListenProvider { - private final Value value; - - @JsonCreator(mode = JsonCreator.Mode.DELEGATING) - private AgentV1SettingsAgentListenProvider(Value value) { - this.value = value; - } - - public T visit(Visitor visitor) { - return value.visit(visitor); - } - - public static AgentV1SettingsAgentListenProvider v1(DeepgramListenProviderV1 value) { - return new AgentV1SettingsAgentListenProvider(new V1Value(value)); - } - - public static AgentV1SettingsAgentListenProvider v2(DeepgramListenProviderV2 value) { - return new AgentV1SettingsAgentListenProvider(new V2Value(value)); - } - - public boolean isV1() { - return value instanceof V1Value; - } - - public boolean isV2() { - return value instanceof V2Value; - } - - public boolean _isUnknown() { - return value instanceof _UnknownValue; - } - - public Optional getV1() { - if (isV1()) { - return Optional.of(((V1Value) value).value); - } - return Optional.empty(); - } - - public Optional getV2() { - if (isV2()) { - return Optional.of(((V2Value) value).value); - } - return Optional.empty(); - } - - public Optional _getUnknown() { - if (_isUnknown()) { - return Optional.of(((_UnknownValue) value).value); - } - return Optional.empty(); - } - - @Override - public boolean equals(Object other) { - if (this == other) return true; - return other instanceof AgentV1SettingsAgentListenProvider - && value.equals(((AgentV1SettingsAgentListenProvider) other).value); - } - - @Override - public int hashCode() { - return Objects.hash(value); - } - - @Override - public String toString() { - return value.toString(); - } - - @JsonValue - private Value getValue() { - return this.value; - } - - public interface Visitor { - T visitV1(DeepgramListenProviderV1 v1); - - T visitV2(DeepgramListenProviderV2 v2); - - T _visitUnknown(Object unknownType); - } - - @JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "version", visible = true, defaultImpl = V2Value.class) - @JsonSubTypes({@JsonSubTypes.Type(V1Value.class), @JsonSubTypes.Type(V2Value.class)}) - @JsonIgnoreProperties(ignoreUnknown = true) - private interface Value { - T visit(Visitor visitor); - } - - @JsonTypeName("v1") - @JsonIgnoreProperties("version") - private static final class V1Value implements Value { - @JsonUnwrapped - @JsonIgnoreProperties(value = "version", allowSetters = true) - private DeepgramListenProviderV1 value; - - @JsonCreator(mode = JsonCreator.Mode.PROPERTIES) - private V1Value() {} - - private V1Value(DeepgramListenProviderV1 value) { - this.value = value; - } - - @java.lang.Override - public T visit(Visitor visitor) { - return visitor.visitV1(value); - } - - @java.lang.Override - public boolean equals(Object other) { - if (this == other) return true; - return other instanceof V1Value && equalTo((V1Value) other); - } - - private boolean equalTo(V1Value other) { - return value.equals(other.value); - } - - @java.lang.Override - public int hashCode() { - return Objects.hash(this.value); - } - - @java.lang.Override - public String toString() { - return "AgentV1SettingsAgentListenProvider{" + "value: " + value + "}"; - } - } - - @JsonTypeName("v2") - @JsonIgnoreProperties("version") - private static final class V2Value implements Value { - @JsonUnwrapped - @JsonIgnoreProperties(value = "version", allowSetters = true) - private DeepgramListenProviderV2 value; - - @JsonCreator(mode = JsonCreator.Mode.PROPERTIES) - private V2Value() {} - - private V2Value(DeepgramListenProviderV2 value) { - this.value = value; - } - - @java.lang.Override - public T visit(Visitor visitor) { - return visitor.visitV2(value); - } - - @java.lang.Override - public boolean equals(Object other) { - if (this == other) return true; - return other instanceof V2Value && equalTo((V2Value) other); - } - - private boolean equalTo(V2Value other) { - return value.equals(other.value); - } - - @java.lang.Override - public int hashCode() { - return Objects.hash(this.value); - } - - @java.lang.Override - public String toString() { - return "AgentV1SettingsAgentListenProvider{" + "value: " + value + "}"; - } - } - - @JsonIgnoreProperties("version") - private static final class _UnknownValue implements Value { - private String type; - - @JsonValue - private Object value; - - @JsonCreator(mode = JsonCreator.Mode.PROPERTIES) - private _UnknownValue(@JsonProperty("value") Object value) {} - - @java.lang.Override - public T visit(Visitor visitor) { - return visitor._visitUnknown(value); - } - - @java.lang.Override - public boolean equals(Object other) { - if (this == other) return true; - return other instanceof _UnknownValue && equalTo((_UnknownValue) other); - } - - private boolean equalTo(_UnknownValue other) { - return type.equals(other.type) && value.equals(other.value); - } - - @java.lang.Override - public int hashCode() { - return Objects.hash(this.type, this.value); - } - - @java.lang.Override - public String toString() { - return "AgentV1SettingsAgentListenProvider{" + "type: " + type + ", value: " + value + "}"; - } - } -} diff --git a/src/main/java/com/deepgram/resources/agent/v1/types/AgentV1SettingsApplied.java b/src/main/java/com/deepgram/resources/agent/v1/types/AgentV1SettingsApplied.java index 0d60b72..ad1fdb2 100644 --- a/src/main/java/com/deepgram/resources/agent/v1/types/AgentV1SettingsApplied.java +++ b/src/main/java/com/deepgram/resources/agent/v1/types/AgentV1SettingsApplied.java @@ -36,6 +36,11 @@ public boolean equals(Object other) { return other instanceof AgentV1SettingsApplied; } + @java.lang.Override + public int hashCode() { + return getClass().hashCode(); + } + @JsonAnyGetter public Map getAdditionalProperties() { return this.additionalProperties; diff --git a/src/main/java/com/deepgram/resources/agent/v1/types/AgentV1SettingsApplied.java.bak b/src/main/java/com/deepgram/resources/agent/v1/types/AgentV1SettingsApplied.java.bak deleted file mode 100644 index ad1fdb2..0000000 --- a/src/main/java/com/deepgram/resources/agent/v1/types/AgentV1SettingsApplied.java.bak +++ /dev/null @@ -1,83 +0,0 @@ -/** - * This file was auto-generated by Fern from our API Definition. - */ -package com.deepgram.resources.agent.v1.types; - -import com.deepgram.core.ObjectMappers; -import com.fasterxml.jackson.annotation.JsonAnyGetter; -import com.fasterxml.jackson.annotation.JsonAnySetter; -import com.fasterxml.jackson.annotation.JsonIgnoreProperties; -import com.fasterxml.jackson.annotation.JsonInclude; -import com.fasterxml.jackson.annotation.JsonProperty; -import com.fasterxml.jackson.databind.annotation.JsonDeserialize; -import java.util.HashMap; -import java.util.Map; - -@JsonInclude(JsonInclude.Include.NON_ABSENT) -@JsonDeserialize(builder = AgentV1SettingsApplied.Builder.class) -public final class AgentV1SettingsApplied { - private final Map additionalProperties; - - private AgentV1SettingsApplied(Map additionalProperties) { - this.additionalProperties = additionalProperties; - } - - /** - * @return Message type identifier for settings applied confirmation - */ - @JsonProperty("type") - public String getType() { - return "SettingsApplied"; - } - - @java.lang.Override - public boolean equals(Object other) { - if (this == other) return true; - return other instanceof AgentV1SettingsApplied; - } - - @java.lang.Override - public int hashCode() { - return getClass().hashCode(); - } - - @JsonAnyGetter - public Map getAdditionalProperties() { - return this.additionalProperties; - } - - @java.lang.Override - public String toString() { - return ObjectMappers.stringify(this); - } - - public static Builder builder() { - return new Builder(); - } - - @JsonIgnoreProperties(ignoreUnknown = true) - public static final class Builder { - @JsonAnySetter - private Map additionalProperties = new HashMap<>(); - - private Builder() {} - - public Builder from(AgentV1SettingsApplied other) { - return this; - } - - public AgentV1SettingsApplied build() { - return new AgentV1SettingsApplied(additionalProperties); - } - - public Builder additionalProperty(String key, Object value) { - this.additionalProperties.put(key, value); - return this; - } - - public Builder additionalProperties(Map additionalProperties) { - this.additionalProperties.putAll(additionalProperties); - return this; - } - } -} diff --git a/src/main/java/com/deepgram/resources/agent/v1/types/AgentV1SpeakUpdated.java b/src/main/java/com/deepgram/resources/agent/v1/types/AgentV1SpeakUpdated.java index 747aa9d..8b134b5 100644 --- a/src/main/java/com/deepgram/resources/agent/v1/types/AgentV1SpeakUpdated.java +++ b/src/main/java/com/deepgram/resources/agent/v1/types/AgentV1SpeakUpdated.java @@ -36,6 +36,11 @@ public boolean equals(Object other) { return other instanceof AgentV1SpeakUpdated; } + @java.lang.Override + public int hashCode() { + return getClass().hashCode(); + } + @JsonAnyGetter public Map getAdditionalProperties() { return this.additionalProperties; diff --git a/src/main/java/com/deepgram/resources/agent/v1/types/AgentV1SpeakUpdated.java.bak b/src/main/java/com/deepgram/resources/agent/v1/types/AgentV1SpeakUpdated.java.bak deleted file mode 100644 index 8b134b5..0000000 --- a/src/main/java/com/deepgram/resources/agent/v1/types/AgentV1SpeakUpdated.java.bak +++ /dev/null @@ -1,83 +0,0 @@ -/** - * This file was auto-generated by Fern from our API Definition. - */ -package com.deepgram.resources.agent.v1.types; - -import com.deepgram.core.ObjectMappers; -import com.fasterxml.jackson.annotation.JsonAnyGetter; -import com.fasterxml.jackson.annotation.JsonAnySetter; -import com.fasterxml.jackson.annotation.JsonIgnoreProperties; -import com.fasterxml.jackson.annotation.JsonInclude; -import com.fasterxml.jackson.annotation.JsonProperty; -import com.fasterxml.jackson.databind.annotation.JsonDeserialize; -import java.util.HashMap; -import java.util.Map; - -@JsonInclude(JsonInclude.Include.NON_ABSENT) -@JsonDeserialize(builder = AgentV1SpeakUpdated.Builder.class) -public final class AgentV1SpeakUpdated { - private final Map additionalProperties; - - private AgentV1SpeakUpdated(Map additionalProperties) { - this.additionalProperties = additionalProperties; - } - - /** - * @return Message type identifier for speak update confirmation - */ - @JsonProperty("type") - public String getType() { - return "SpeakUpdated"; - } - - @java.lang.Override - public boolean equals(Object other) { - if (this == other) return true; - return other instanceof AgentV1SpeakUpdated; - } - - @java.lang.Override - public int hashCode() { - return getClass().hashCode(); - } - - @JsonAnyGetter - public Map getAdditionalProperties() { - return this.additionalProperties; - } - - @java.lang.Override - public String toString() { - return ObjectMappers.stringify(this); - } - - public static Builder builder() { - return new Builder(); - } - - @JsonIgnoreProperties(ignoreUnknown = true) - public static final class Builder { - @JsonAnySetter - private Map additionalProperties = new HashMap<>(); - - private Builder() {} - - public Builder from(AgentV1SpeakUpdated other) { - return this; - } - - public AgentV1SpeakUpdated build() { - return new AgentV1SpeakUpdated(additionalProperties); - } - - public Builder additionalProperty(String key, Object value) { - this.additionalProperties.put(key, value); - return this; - } - - public Builder additionalProperties(Map additionalProperties) { - this.additionalProperties.putAll(additionalProperties); - return this; - } - } -} diff --git a/src/main/java/com/deepgram/resources/agent/v1/types/AgentV1ThinkUpdated.java b/src/main/java/com/deepgram/resources/agent/v1/types/AgentV1ThinkUpdated.java index 5668a71..2d67cad 100644 --- a/src/main/java/com/deepgram/resources/agent/v1/types/AgentV1ThinkUpdated.java +++ b/src/main/java/com/deepgram/resources/agent/v1/types/AgentV1ThinkUpdated.java @@ -36,6 +36,11 @@ public boolean equals(Object other) { return other instanceof AgentV1ThinkUpdated; } + @java.lang.Override + public int hashCode() { + return getClass().hashCode(); + } + @JsonAnyGetter public Map getAdditionalProperties() { return this.additionalProperties; diff --git a/src/main/java/com/deepgram/resources/agent/v1/types/AgentV1ThinkUpdated.java.bak b/src/main/java/com/deepgram/resources/agent/v1/types/AgentV1ThinkUpdated.java.bak deleted file mode 100644 index 2d67cad..0000000 --- a/src/main/java/com/deepgram/resources/agent/v1/types/AgentV1ThinkUpdated.java.bak +++ /dev/null @@ -1,83 +0,0 @@ -/** - * This file was auto-generated by Fern from our API Definition. - */ -package com.deepgram.resources.agent.v1.types; - -import com.deepgram.core.ObjectMappers; -import com.fasterxml.jackson.annotation.JsonAnyGetter; -import com.fasterxml.jackson.annotation.JsonAnySetter; -import com.fasterxml.jackson.annotation.JsonIgnoreProperties; -import com.fasterxml.jackson.annotation.JsonInclude; -import com.fasterxml.jackson.annotation.JsonProperty; -import com.fasterxml.jackson.databind.annotation.JsonDeserialize; -import java.util.HashMap; -import java.util.Map; - -@JsonInclude(JsonInclude.Include.NON_ABSENT) -@JsonDeserialize(builder = AgentV1ThinkUpdated.Builder.class) -public final class AgentV1ThinkUpdated { - private final Map additionalProperties; - - private AgentV1ThinkUpdated(Map additionalProperties) { - this.additionalProperties = additionalProperties; - } - - /** - * @return Message type identifier for think update confirmation - */ - @JsonProperty("type") - public String getType() { - return "ThinkUpdated"; - } - - @java.lang.Override - public boolean equals(Object other) { - if (this == other) return true; - return other instanceof AgentV1ThinkUpdated; - } - - @java.lang.Override - public int hashCode() { - return getClass().hashCode(); - } - - @JsonAnyGetter - public Map getAdditionalProperties() { - return this.additionalProperties; - } - - @java.lang.Override - public String toString() { - return ObjectMappers.stringify(this); - } - - public static Builder builder() { - return new Builder(); - } - - @JsonIgnoreProperties(ignoreUnknown = true) - public static final class Builder { - @JsonAnySetter - private Map additionalProperties = new HashMap<>(); - - private Builder() {} - - public Builder from(AgentV1ThinkUpdated other) { - return this; - } - - public AgentV1ThinkUpdated build() { - return new AgentV1ThinkUpdated(additionalProperties); - } - - public Builder additionalProperty(String key, Object value) { - this.additionalProperties.put(key, value); - return this; - } - - public Builder additionalProperties(Map additionalProperties) { - this.additionalProperties.putAll(additionalProperties); - return this; - } - } -} diff --git a/src/main/java/com/deepgram/resources/agent/v1/types/AgentV1UpdateListenListenProvider.java b/src/main/java/com/deepgram/resources/agent/v1/types/AgentV1UpdateListenListenProvider.java index e693a04..e739aec 100644 --- a/src/main/java/com/deepgram/resources/agent/v1/types/AgentV1UpdateListenListenProvider.java +++ b/src/main/java/com/deepgram/resources/agent/v1/types/AgentV1UpdateListenListenProvider.java @@ -99,7 +99,7 @@ public interface Visitor { T _visitUnknown(Object unknownType); } - @JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "version", visible = true, defaultImpl = _UnknownValue.class) + @JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "version", visible = true, defaultImpl = V2Value.class) @JsonSubTypes({@JsonSubTypes.Type(V1Value.class), @JsonSubTypes.Type(V2Value.class)}) @JsonIgnoreProperties(ignoreUnknown = true) private interface Value { diff --git a/src/main/java/com/deepgram/resources/agent/v1/types/AgentV1UpdateListenListenProvider.java.bak b/src/main/java/com/deepgram/resources/agent/v1/types/AgentV1UpdateListenListenProvider.java.bak deleted file mode 100644 index e739aec..0000000 --- a/src/main/java/com/deepgram/resources/agent/v1/types/AgentV1UpdateListenListenProvider.java.bak +++ /dev/null @@ -1,224 +0,0 @@ -/** - * This file was auto-generated by Fern from our API Definition. - */ -package com.deepgram.resources.agent.v1.types; - -import com.deepgram.types.DeepgramListenProviderV1; -import com.deepgram.types.DeepgramListenProviderV2; -import com.fasterxml.jackson.annotation.JsonCreator; -import com.fasterxml.jackson.annotation.JsonIgnoreProperties; -import com.fasterxml.jackson.annotation.JsonProperty; -import com.fasterxml.jackson.annotation.JsonSubTypes; -import com.fasterxml.jackson.annotation.JsonTypeInfo; -import com.fasterxml.jackson.annotation.JsonTypeName; -import com.fasterxml.jackson.annotation.JsonUnwrapped; -import com.fasterxml.jackson.annotation.JsonValue; -import java.util.Objects; -import java.util.Optional; - -public final class AgentV1UpdateListenListenProvider { - private final Value value; - - @JsonCreator(mode = JsonCreator.Mode.DELEGATING) - private AgentV1UpdateListenListenProvider(Value value) { - this.value = value; - } - - public T visit(Visitor visitor) { - return value.visit(visitor); - } - - public static AgentV1UpdateListenListenProvider v1(DeepgramListenProviderV1 value) { - return new AgentV1UpdateListenListenProvider(new V1Value(value)); - } - - public static AgentV1UpdateListenListenProvider v2(DeepgramListenProviderV2 value) { - return new AgentV1UpdateListenListenProvider(new V2Value(value)); - } - - public boolean isV1() { - return value instanceof V1Value; - } - - public boolean isV2() { - return value instanceof V2Value; - } - - public boolean _isUnknown() { - return value instanceof _UnknownValue; - } - - public Optional getV1() { - if (isV1()) { - return Optional.of(((V1Value) value).value); - } - return Optional.empty(); - } - - public Optional getV2() { - if (isV2()) { - return Optional.of(((V2Value) value).value); - } - return Optional.empty(); - } - - public Optional _getUnknown() { - if (_isUnknown()) { - return Optional.of(((_UnknownValue) value).value); - } - return Optional.empty(); - } - - @Override - public boolean equals(Object other) { - if (this == other) return true; - return other instanceof AgentV1UpdateListenListenProvider - && value.equals(((AgentV1UpdateListenListenProvider) other).value); - } - - @Override - public int hashCode() { - return Objects.hash(value); - } - - @Override - public String toString() { - return value.toString(); - } - - @JsonValue - private Value getValue() { - return this.value; - } - - public interface Visitor { - T visitV1(DeepgramListenProviderV1 v1); - - T visitV2(DeepgramListenProviderV2 v2); - - T _visitUnknown(Object unknownType); - } - - @JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "version", visible = true, defaultImpl = V2Value.class) - @JsonSubTypes({@JsonSubTypes.Type(V1Value.class), @JsonSubTypes.Type(V2Value.class)}) - @JsonIgnoreProperties(ignoreUnknown = true) - private interface Value { - T visit(Visitor visitor); - } - - @JsonTypeName("v1") - @JsonIgnoreProperties("version") - private static final class V1Value implements Value { - @JsonUnwrapped - @JsonIgnoreProperties(value = "version", allowSetters = true) - private DeepgramListenProviderV1 value; - - @JsonCreator(mode = JsonCreator.Mode.PROPERTIES) - private V1Value() {} - - private V1Value(DeepgramListenProviderV1 value) { - this.value = value; - } - - @java.lang.Override - public T visit(Visitor visitor) { - return visitor.visitV1(value); - } - - @java.lang.Override - public boolean equals(Object other) { - if (this == other) return true; - return other instanceof V1Value && equalTo((V1Value) other); - } - - private boolean equalTo(V1Value other) { - return value.equals(other.value); - } - - @java.lang.Override - public int hashCode() { - return Objects.hash(this.value); - } - - @java.lang.Override - public String toString() { - return "AgentV1UpdateListenListenProvider{" + "value: " + value + "}"; - } - } - - @JsonTypeName("v2") - @JsonIgnoreProperties("version") - private static final class V2Value implements Value { - @JsonUnwrapped - @JsonIgnoreProperties(value = "version", allowSetters = true) - private DeepgramListenProviderV2 value; - - @JsonCreator(mode = JsonCreator.Mode.PROPERTIES) - private V2Value() {} - - private V2Value(DeepgramListenProviderV2 value) { - this.value = value; - } - - @java.lang.Override - public T visit(Visitor visitor) { - return visitor.visitV2(value); - } - - @java.lang.Override - public boolean equals(Object other) { - if (this == other) return true; - return other instanceof V2Value && equalTo((V2Value) other); - } - - private boolean equalTo(V2Value other) { - return value.equals(other.value); - } - - @java.lang.Override - public int hashCode() { - return Objects.hash(this.value); - } - - @java.lang.Override - public String toString() { - return "AgentV1UpdateListenListenProvider{" + "value: " + value + "}"; - } - } - - @JsonIgnoreProperties("version") - private static final class _UnknownValue implements Value { - private String type; - - @JsonValue - private Object value; - - @JsonCreator(mode = JsonCreator.Mode.PROPERTIES) - private _UnknownValue(@JsonProperty("value") Object value) {} - - @java.lang.Override - public T visit(Visitor visitor) { - return visitor._visitUnknown(value); - } - - @java.lang.Override - public boolean equals(Object other) { - if (this == other) return true; - return other instanceof _UnknownValue && equalTo((_UnknownValue) other); - } - - private boolean equalTo(_UnknownValue other) { - return type.equals(other.type) && value.equals(other.value); - } - - @java.lang.Override - public int hashCode() { - return Objects.hash(this.type, this.value); - } - - @java.lang.Override - public String toString() { - return "AgentV1UpdateListenListenProvider{" + "type: " + type + ", value: " + value + "}"; - } - } -} diff --git a/src/main/java/com/deepgram/resources/agent/v1/types/AgentV1UserStartedSpeaking.java b/src/main/java/com/deepgram/resources/agent/v1/types/AgentV1UserStartedSpeaking.java index edb80f8..3556d31 100644 --- a/src/main/java/com/deepgram/resources/agent/v1/types/AgentV1UserStartedSpeaking.java +++ b/src/main/java/com/deepgram/resources/agent/v1/types/AgentV1UserStartedSpeaking.java @@ -36,6 +36,11 @@ public boolean equals(Object other) { return other instanceof AgentV1UserStartedSpeaking; } + @java.lang.Override + public int hashCode() { + return getClass().hashCode(); + } + @JsonAnyGetter public Map getAdditionalProperties() { return this.additionalProperties; diff --git a/src/main/java/com/deepgram/resources/agent/v1/types/AgentV1UserStartedSpeaking.java.bak b/src/main/java/com/deepgram/resources/agent/v1/types/AgentV1UserStartedSpeaking.java.bak deleted file mode 100644 index 3556d31..0000000 --- a/src/main/java/com/deepgram/resources/agent/v1/types/AgentV1UserStartedSpeaking.java.bak +++ /dev/null @@ -1,83 +0,0 @@ -/** - * This file was auto-generated by Fern from our API Definition. - */ -package com.deepgram.resources.agent.v1.types; - -import com.deepgram.core.ObjectMappers; -import com.fasterxml.jackson.annotation.JsonAnyGetter; -import com.fasterxml.jackson.annotation.JsonAnySetter; -import com.fasterxml.jackson.annotation.JsonIgnoreProperties; -import com.fasterxml.jackson.annotation.JsonInclude; -import com.fasterxml.jackson.annotation.JsonProperty; -import com.fasterxml.jackson.databind.annotation.JsonDeserialize; -import java.util.HashMap; -import java.util.Map; - -@JsonInclude(JsonInclude.Include.NON_ABSENT) -@JsonDeserialize(builder = AgentV1UserStartedSpeaking.Builder.class) -public final class AgentV1UserStartedSpeaking { - private final Map additionalProperties; - - private AgentV1UserStartedSpeaking(Map additionalProperties) { - this.additionalProperties = additionalProperties; - } - - /** - * @return Message type identifier indicating that the user has begun speaking - */ - @JsonProperty("type") - public String getType() { - return "UserStartedSpeaking"; - } - - @java.lang.Override - public boolean equals(Object other) { - if (this == other) return true; - return other instanceof AgentV1UserStartedSpeaking; - } - - @java.lang.Override - public int hashCode() { - return getClass().hashCode(); - } - - @JsonAnyGetter - public Map getAdditionalProperties() { - return this.additionalProperties; - } - - @java.lang.Override - public String toString() { - return ObjectMappers.stringify(this); - } - - public static Builder builder() { - return new Builder(); - } - - @JsonIgnoreProperties(ignoreUnknown = true) - public static final class Builder { - @JsonAnySetter - private Map additionalProperties = new HashMap<>(); - - private Builder() {} - - public Builder from(AgentV1UserStartedSpeaking other) { - return this; - } - - public AgentV1UserStartedSpeaking build() { - return new AgentV1UserStartedSpeaking(additionalProperties); - } - - public Builder additionalProperty(String key, Object value) { - this.additionalProperties.put(key, value); - return this; - } - - public Builder additionalProperties(Map additionalProperties) { - this.additionalProperties.putAll(additionalProperties); - return this; - } - } -} diff --git a/src/main/java/com/deepgram/resources/listen/v1/websocket/V1WebSocketClient.java b/src/main/java/com/deepgram/resources/listen/v1/websocket/V1WebSocketClient.java index a91c4e1..d87d076 100644 --- a/src/main/java/com/deepgram/resources/listen/v1/websocket/V1WebSocketClient.java +++ b/src/main/java/com/deepgram/resources/listen/v1/websocket/V1WebSocketClient.java @@ -6,6 +6,7 @@ import com.deepgram.core.ClientOptions; import com.deepgram.core.DisconnectReason; import com.deepgram.core.ObjectMappers; +import com.deepgram.core.QueryStringMapper; import com.deepgram.core.ReconnectingWebSocketListener; import com.deepgram.core.RequestOptions; import com.deepgram.core.WebSocketReadyState; @@ -142,8 +143,7 @@ public CompletableFuture connect(V1ConnectOptions options) { "endpointing", String.valueOf(options.getEndpointing().get())); } if (options.getExtra() != null && options.getExtra().isPresent()) { - urlBuilder.addQueryParameter( - "extra", String.valueOf(options.getExtra().get())); + QueryStringMapper.addQueryParameter(urlBuilder, "extra", options.getExtra().get().get(), true); } if (options.getInterimResults() != null && options.getInterimResults().isPresent()) { urlBuilder.addQueryParameter( @@ -151,12 +151,10 @@ public CompletableFuture connect(V1ConnectOptions options) { String.valueOf(options.getInterimResults().get())); } if (options.getKeyterm() != null && options.getKeyterm().isPresent()) { - urlBuilder.addQueryParameter( - "keyterm", String.valueOf(options.getKeyterm().get())); + QueryStringMapper.addQueryParameter(urlBuilder, "keyterm", options.getKeyterm().get().get(), true); } if (options.getKeywords() != null && options.getKeywords().isPresent()) { - urlBuilder.addQueryParameter( - "keywords", String.valueOf(options.getKeywords().get())); + QueryStringMapper.addQueryParameter(urlBuilder, "keywords", options.getKeywords().get().get(), true); } if (options.getLanguage() != null && options.getLanguage().isPresent()) { urlBuilder.addQueryParameter( @@ -189,23 +187,21 @@ public CompletableFuture connect(V1ConnectOptions options) { "redact", String.valueOf(options.getRedact().get())); } if (options.getReplace() != null && options.getReplace().isPresent()) { - urlBuilder.addQueryParameter( - "replace", String.valueOf(options.getReplace().get())); + QueryStringMapper.addQueryParameter(urlBuilder, "replace", options.getReplace().get().get(), true); } if (options.getSampleRate() != null && options.getSampleRate().isPresent()) { urlBuilder.addQueryParameter( "sample_rate", String.valueOf(options.getSampleRate().get())); } if (options.getSearch() != null && options.getSearch().isPresent()) { - urlBuilder.addQueryParameter( - "search", String.valueOf(options.getSearch().get())); + QueryStringMapper.addQueryParameter(urlBuilder, "search", options.getSearch().get().get(), true); } if (options.getSmartFormat() != null && options.getSmartFormat().isPresent()) { urlBuilder.addQueryParameter( "smart_format", String.valueOf(options.getSmartFormat().get())); } if (options.getTag() != null && options.getTag().isPresent()) { - urlBuilder.addQueryParameter("tag", String.valueOf(options.getTag().get())); + QueryStringMapper.addQueryParameter(urlBuilder, "tag", options.getTag().get().get(), true); } if (options.getUtteranceEndMs() != null && options.getUtteranceEndMs().isPresent()) { urlBuilder.addQueryParameter( @@ -220,6 +216,13 @@ public CompletableFuture connect(V1ConnectOptions options) { urlBuilder.addQueryParameter( "version", String.valueOf(options.getVersion().get())); } + if (options.getAdditionalProperties() != null) { + options.getAdditionalProperties().forEach((key, value) -> { + if (value != null) { + QueryStringMapper.addQueryParameter(urlBuilder, key, value, true); + } + }); + } Request.Builder requestBuilder = new Request.Builder().url(urlBuilder.build()); clientOptions.headers((RequestOptions) null).forEach(requestBuilder::addHeader); final Request request = requestBuilder.build(); diff --git a/src/main/java/com/deepgram/resources/listen/v1/websocket/V1WebSocketClient.java.bak b/src/main/java/com/deepgram/resources/listen/v1/websocket/V1WebSocketClient.java.bak deleted file mode 100644 index 278a2c9..0000000 --- a/src/main/java/com/deepgram/resources/listen/v1/websocket/V1WebSocketClient.java.bak +++ /dev/null @@ -1,554 +0,0 @@ -/** - * This file was auto-generated by Fern from our API Definition. - */ -package com.deepgram.resources.listen.v1.websocket; - -import com.deepgram.core.ClientOptions; -import com.deepgram.core.DisconnectReason; -import com.deepgram.core.ObjectMappers; -import com.deepgram.core.QueryStringMapper; -import com.deepgram.core.ReconnectingWebSocketListener; -import com.deepgram.core.RequestOptions; -import com.deepgram.core.WebSocketReadyState; -import com.deepgram.resources.listen.v1.types.ListenV1CloseStream; -import com.deepgram.resources.listen.v1.types.ListenV1Finalize; -import com.deepgram.resources.listen.v1.types.ListenV1KeepAlive; -import com.deepgram.resources.listen.v1.types.ListenV1Metadata; -import com.deepgram.resources.listen.v1.types.ListenV1Results; -import com.deepgram.resources.listen.v1.types.ListenV1SpeechStarted; -import com.deepgram.resources.listen.v1.types.ListenV1UtteranceEnd; -import com.fasterxml.jackson.databind.JsonNode; -import com.fasterxml.jackson.databind.ObjectMapper; -import java.util.concurrent.CompletableFuture; -import java.util.concurrent.ScheduledExecutorService; -import java.util.function.Consumer; -import okhttp3.HttpUrl; -import okhttp3.OkHttpClient; -import okhttp3.Request; -import okhttp3.Response; -import okhttp3.WebSocket; -import okio.ByteString; - -/** - * WebSocket client for the v1 channel. - * Provides real-time bidirectional communication with strongly-typed messages. - */ -public class V1WebSocketClient implements AutoCloseable { - protected final ClientOptions clientOptions; - - private final ObjectMapper objectMapper; - - private final OkHttpClient okHttpClient; - - private ScheduledExecutorService timeoutExecutor; - - private volatile WebSocketReadyState readyState = WebSocketReadyState.CLOSED; - - private volatile Runnable onConnectedHandler; - - private volatile Consumer onDisconnectedHandler; - - private volatile Consumer onErrorHandler; - - private volatile Consumer onMessageHandler; - - private volatile ReconnectingWebSocketListener.ReconnectOptions reconnectOptions; - - private CompletableFuture connectionFuture; - - private ReconnectingWebSocketListener reconnectingListener; - - private volatile Consumer resultsHandler; - - private volatile Consumer metadataHandler; - - private volatile Consumer utteranceEndHandler; - - private volatile Consumer speechStartedHandler; - - /** - * Creates a new async WebSocket client for the v1 channel. - */ - public V1WebSocketClient(ClientOptions clientOptions) { - this.clientOptions = clientOptions; - this.objectMapper = ObjectMappers.JSON_MAPPER; - this.okHttpClient = clientOptions.httpClient(); - } - - /** - * Establishes the WebSocket connection asynchronously with automatic reconnection. - * @return a CompletableFuture that completes when the connection is established - * @param options connection options including query parameters - */ - public CompletableFuture connect(V1ConnectOptions options) { - connectionFuture = new CompletableFuture<>(); - String baseUrl = clientOptions.environment().getProductionURL(); - String fullPath = "/v1/listen"; - if (baseUrl.endsWith("/") && fullPath.startsWith("/")) { - fullPath = fullPath.substring(1); - } else if (!baseUrl.endsWith("/") && !fullPath.startsWith("/")) { - fullPath = "/" + fullPath; - } - // OkHttp's HttpUrl only supports http/https schemes; convert wss/ws for URL parsing - if (baseUrl.startsWith("wss://")) { - baseUrl = "https://" + baseUrl.substring(6); - } else if (baseUrl.startsWith("ws://")) { - baseUrl = "http://" + baseUrl.substring(5); - } - HttpUrl parsedUrl = HttpUrl.parse(baseUrl + fullPath); - if (parsedUrl == null) { - throw new IllegalArgumentException("Invalid WebSocket URL: " + baseUrl + fullPath); - } - HttpUrl.Builder urlBuilder = parsedUrl.newBuilder(); - if (options.getCallback() != null && options.getCallback().isPresent()) { - urlBuilder.addQueryParameter( - "callback", String.valueOf(options.getCallback().get())); - } - if (options.getCallbackMethod() != null && options.getCallbackMethod().isPresent()) { - urlBuilder.addQueryParameter( - "callback_method", - String.valueOf(options.getCallbackMethod().get())); - } - if (options.getChannels() != null && options.getChannels().isPresent()) { - urlBuilder.addQueryParameter( - "channels", String.valueOf(options.getChannels().get())); - } - if (options.getDetectEntities() != null && options.getDetectEntities().isPresent()) { - urlBuilder.addQueryParameter( - "detect_entities", - String.valueOf(options.getDetectEntities().get())); - } - if (options.getDiarize() != null && options.getDiarize().isPresent()) { - urlBuilder.addQueryParameter( - "diarize", String.valueOf(options.getDiarize().get())); - } - if (options.getDiarizeModel() != null && options.getDiarizeModel().isPresent()) { - urlBuilder.addQueryParameter( - "diarize_model", String.valueOf(options.getDiarizeModel().get())); - } - if (options.getDictation() != null && options.getDictation().isPresent()) { - urlBuilder.addQueryParameter( - "dictation", String.valueOf(options.getDictation().get())); - } - if (options.getEncoding() != null && options.getEncoding().isPresent()) { - urlBuilder.addQueryParameter( - "encoding", String.valueOf(options.getEncoding().get())); - } - if (options.getEndpointing() != null && options.getEndpointing().isPresent()) { - urlBuilder.addQueryParameter( - "endpointing", String.valueOf(options.getEndpointing().get())); - } - if (options.getExtra() != null && options.getExtra().isPresent()) { - QueryStringMapper.addQueryParameter(urlBuilder, "extra", options.getExtra().get().get(), true); - } - if (options.getInterimResults() != null && options.getInterimResults().isPresent()) { - urlBuilder.addQueryParameter( - "interim_results", - String.valueOf(options.getInterimResults().get())); - } - if (options.getKeyterm() != null && options.getKeyterm().isPresent()) { - QueryStringMapper.addQueryParameter(urlBuilder, "keyterm", options.getKeyterm().get().get(), true); - } - if (options.getKeywords() != null && options.getKeywords().isPresent()) { - QueryStringMapper.addQueryParameter(urlBuilder, "keywords", options.getKeywords().get().get(), true); - } - if (options.getLanguage() != null && options.getLanguage().isPresent()) { - urlBuilder.addQueryParameter( - "language", String.valueOf(options.getLanguage().get())); - } - if (options.getMipOptOut() != null && options.getMipOptOut().isPresent()) { - urlBuilder.addQueryParameter( - "mip_opt_out", String.valueOf(options.getMipOptOut().get())); - } - urlBuilder.addQueryParameter("model", String.valueOf(options.getModel())); - if (options.getMultichannel() != null && options.getMultichannel().isPresent()) { - urlBuilder.addQueryParameter( - "multichannel", String.valueOf(options.getMultichannel().get())); - } - if (options.getNumerals() != null && options.getNumerals().isPresent()) { - urlBuilder.addQueryParameter( - "numerals", String.valueOf(options.getNumerals().get())); - } - if (options.getProfanityFilter() != null && options.getProfanityFilter().isPresent()) { - urlBuilder.addQueryParameter( - "profanity_filter", - String.valueOf(options.getProfanityFilter().get())); - } - if (options.getPunctuate() != null && options.getPunctuate().isPresent()) { - urlBuilder.addQueryParameter( - "punctuate", String.valueOf(options.getPunctuate().get())); - } - if (options.getRedact() != null && options.getRedact().isPresent()) { - urlBuilder.addQueryParameter( - "redact", String.valueOf(options.getRedact().get())); - } - if (options.getReplace() != null && options.getReplace().isPresent()) { - QueryStringMapper.addQueryParameter(urlBuilder, "replace", options.getReplace().get().get(), true); - } - if (options.getSampleRate() != null && options.getSampleRate().isPresent()) { - urlBuilder.addQueryParameter( - "sample_rate", String.valueOf(options.getSampleRate().get())); - } - if (options.getSearch() != null && options.getSearch().isPresent()) { - QueryStringMapper.addQueryParameter(urlBuilder, "search", options.getSearch().get().get(), true); - } - if (options.getSmartFormat() != null && options.getSmartFormat().isPresent()) { - urlBuilder.addQueryParameter( - "smart_format", String.valueOf(options.getSmartFormat().get())); - } - if (options.getTag() != null && options.getTag().isPresent()) { - QueryStringMapper.addQueryParameter(urlBuilder, "tag", options.getTag().get().get(), true); - } - if (options.getUtteranceEndMs() != null && options.getUtteranceEndMs().isPresent()) { - urlBuilder.addQueryParameter( - "utterance_end_ms", - String.valueOf(options.getUtteranceEndMs().get())); - } - if (options.getVadEvents() != null && options.getVadEvents().isPresent()) { - urlBuilder.addQueryParameter( - "vad_events", String.valueOf(options.getVadEvents().get())); - } - if (options.getVersion() != null && options.getVersion().isPresent()) { - urlBuilder.addQueryParameter( - "version", String.valueOf(options.getVersion().get())); - } - if (options.getAdditionalProperties() != null) { - options.getAdditionalProperties().forEach((key, value) -> { - if (value != null) { - QueryStringMapper.addQueryParameter(urlBuilder, key, value, true); - } - }); - } - Request.Builder requestBuilder = new Request.Builder().url(urlBuilder.build()); - clientOptions.headers((RequestOptions) null).forEach(requestBuilder::addHeader); - final Request request = requestBuilder.build(); - this.readyState = WebSocketReadyState.CONNECTING; - ReconnectingWebSocketListener.ReconnectOptions reconnectOpts = this.reconnectOptions != null - ? this.reconnectOptions - : ReconnectingWebSocketListener.ReconnectOptions.builder().build(); - this.reconnectingListener = - new ReconnectingWebSocketListener( - reconnectOpts, - () -> { - if (clientOptions.isClosed()) { - throw new IllegalStateException("root client has been closed"); - } - if (clientOptions.webSocketFactory().isPresent()) { - return clientOptions - .webSocketFactory() - .get() - .create(request, this.reconnectingListener); - } else { - return okHttpClient.newWebSocket(request, this.reconnectingListener); - } - }, - clientOptions::isClosed) { - @Override - protected void onWebSocketOpen(WebSocket webSocket, Response response) { - readyState = WebSocketReadyState.OPEN; - if (onConnectedHandler != null) { - onConnectedHandler.run(); - } - connectionFuture.complete(null); - } - - @Override - protected void onWebSocketMessage(WebSocket webSocket, String text) { - handleIncomingMessage(text); - } - - @Override - protected void onWebSocketBinaryMessage(WebSocket webSocket, ByteString bytes) {} - - @Override - protected void onWebSocketFailure(WebSocket webSocket, Throwable t, Response response) { - readyState = WebSocketReadyState.CLOSED; - if (onErrorHandler != null) { - onErrorHandler.accept(new RuntimeException(t)); - } - connectionFuture.completeExceptionally(t); - } - - @Override - protected void onWebSocketClosed(WebSocket webSocket, int code, String reason) { - readyState = WebSocketReadyState.CLOSED; - if (onDisconnectedHandler != null) { - onDisconnectedHandler.accept(new DisconnectReason(code, reason)); - } - } - }; - clientOptions.registerWebSocket(this); - reconnectingListener.connect(); - return connectionFuture; - } - - /** - * Disconnects the WebSocket connection and releases resources. - */ - public void disconnect() { - clientOptions.unregisterWebSocket(this); - if (reconnectingListener != null) { - reconnectingListener.disconnect(); - } - if (timeoutExecutor != null) { - timeoutExecutor.shutdownNow(); - timeoutExecutor = null; - } - } - - /** - * Gets the current state of the WebSocket connection. - * - * This provides the actual connection state, similar to the W3C WebSocket API. - * - * @return the current WebSocket ready state - */ - public WebSocketReadyState getReadyState() { - return readyState; - } - - /** - * Sends a ListenV1Media message to the server asynchronously. - * @param message the message to send - * @return a CompletableFuture that completes when the message is sent - */ - public CompletableFuture sendMedia(ByteString message) { - CompletableFuture future = new CompletableFuture<>(); - try { - assertSocketIsOpen(); - // Use reconnecting listener's sendBinary method which handles queuing - reconnectingListener.sendBinary(message); - future.complete(null); - } catch (Exception e) { - future.completeExceptionally(new RuntimeException("Failed to send binary data", e)); - } - return future; - } - - /** - * Sends a ListenV1Finalize message to the server asynchronously. - * @param message the message to send - * @return a CompletableFuture that completes when the message is sent - */ - public CompletableFuture sendFinalize(ListenV1Finalize message) { - return sendMessage(message); - } - - /** - * Sends a ListenV1CloseStream message to the server asynchronously. - * @param message the message to send - * @return a CompletableFuture that completes when the message is sent - */ - public CompletableFuture sendCloseStream(ListenV1CloseStream message) { - return sendMessage(message); - } - - /** - * Sends a ListenV1KeepAlive message to the server asynchronously. - * @param message the message to send - * @return a CompletableFuture that completes when the message is sent - */ - public CompletableFuture sendKeepAlive(ListenV1KeepAlive message) { - return sendMessage(message); - } - - /** - * Registers a handler for ListenV1Results messages from the server. - * @param handler the handler to invoke when a message is received - */ - public void onResults(Consumer handler) { - this.resultsHandler = handler; - } - - /** - * Registers a handler for ListenV1Metadata messages from the server. - * @param handler the handler to invoke when a message is received - */ - public void onMetadata(Consumer handler) { - this.metadataHandler = handler; - } - - /** - * Registers a handler for ListenV1UtteranceEnd messages from the server. - * @param handler the handler to invoke when a message is received - */ - public void onUtteranceEnd(Consumer handler) { - this.utteranceEndHandler = handler; - } - - /** - * Registers a handler for ListenV1SpeechStarted messages from the server. - * @param handler the handler to invoke when a message is received - */ - public void onSpeechStarted(Consumer handler) { - this.speechStartedHandler = handler; - } - - /** - * Registers a handler called when the connection is established. - * @param handler the handler to invoke when connected - */ - public void onConnected(Runnable handler) { - this.onConnectedHandler = handler; - } - - /** - * Registers a handler called when the connection is closed. - * @param handler the handler to invoke when disconnected - */ - public void onDisconnected(Consumer handler) { - this.onDisconnectedHandler = handler; - } - - /** - * Registers a handler called when an error occurs. - * @param handler the handler to invoke on error - */ - public void onError(Consumer handler) { - this.onErrorHandler = handler; - } - - /** - * Registers a handler called for every incoming text message. - * The handler receives the raw JSON string before type-specific dispatch. - * @param handler the handler to invoke with the raw message JSON - */ - public void onMessage(Consumer handler) { - this.onMessageHandler = handler; - } - - /** - * Configures reconnection behavior. Must be called before {@link #connect}. - * - * @param options the reconnection options (backoff, retries, queue size) - */ - public void reconnectOptions(ReconnectingWebSocketListener.ReconnectOptions options) { - this.reconnectOptions = options; - } - - /** - * Closes this WebSocket client, releasing all resources. - * Equivalent to calling {@link #disconnect()}. - */ - @Override - public void close() { - disconnect(); - } - - /** - * Ensures the WebSocket is connected and ready to send messages. - * @throws IllegalStateException if the socket is not connected or not open - */ - private void assertSocketIsOpen() { - if (reconnectingListener.getWebSocket() == null) { - throw new IllegalStateException("WebSocket is not connected. Call connect() first."); - } - if (readyState != WebSocketReadyState.OPEN) { - throw new IllegalStateException("WebSocket is not open. Current state: " + readyState); - } - } - - private CompletableFuture sendMessage(Object body) { - CompletableFuture future = new CompletableFuture<>(); - try { - assertSocketIsOpen(); - String json = objectMapper.writeValueAsString(body); - // Use reconnecting listener's send method which handles queuing - reconnectingListener.send(json); - future.complete(null); - } catch (IllegalStateException e) { - future.completeExceptionally(e); - } catch (Exception e) { - future.completeExceptionally(new RuntimeException("Failed to send message", e)); - } - return future; - } - - private void handleIncomingMessage(String json) { - try { - if (onMessageHandler != null) { - onMessageHandler.accept(json); - } - JsonNode node = objectMapper.readTree(json); - if (node == null || node.isNull()) { - throw new IllegalArgumentException("Received null or invalid JSON message"); - } - if (node.has("transaction_key") - && node.has("request_id") - && node.has("sha256") - && node.has("created") - && node.has("duration") - && node.has("channels") - && "Metadata".equals(node.path("type").asText())) { - ListenV1Metadata metadataHandlerEvent = null; - try { - metadataHandlerEvent = objectMapper.treeToValue(node, ListenV1Metadata.class); - } catch (Exception e) { - } - if (metadataHandlerEvent != null) { - if (metadataHandler != null) { - metadataHandler.accept(metadataHandlerEvent); - } - return; - } - } - if (node.has("channel_index") - && node.has("duration") - && node.has("start") - && node.has("channel") - && node.has("metadata") - && "Results".equals(node.path("type").asText())) { - ListenV1Results resultsHandlerEvent = null; - try { - resultsHandlerEvent = objectMapper.treeToValue(node, ListenV1Results.class); - } catch (Exception e) { - } - if (resultsHandlerEvent != null) { - if (resultsHandler != null) { - resultsHandler.accept(resultsHandlerEvent); - } - return; - } - } - if (node.has("channel") - && node.has("last_word_end") - && "UtteranceEnd".equals(node.path("type").asText())) { - ListenV1UtteranceEnd utteranceEndHandlerEvent = null; - try { - utteranceEndHandlerEvent = objectMapper.treeToValue(node, ListenV1UtteranceEnd.class); - } catch (Exception e) { - } - if (utteranceEndHandlerEvent != null) { - if (utteranceEndHandler != null) { - utteranceEndHandler.accept(utteranceEndHandlerEvent); - } - return; - } - } - if (node.has("channel") - && node.has("timestamp") - && "SpeechStarted".equals(node.path("type").asText())) { - ListenV1SpeechStarted speechStartedHandlerEvent = null; - try { - speechStartedHandlerEvent = objectMapper.treeToValue(node, ListenV1SpeechStarted.class); - } catch (Exception e) { - } - if (speechStartedHandlerEvent != null) { - if (speechStartedHandler != null) { - speechStartedHandler.accept(speechStartedHandlerEvent); - } - return; - } - } - if (onErrorHandler != null) { - onErrorHandler.accept(new RuntimeException( - "Unrecognized WebSocket message: " + json.substring(0, Math.min(200, json.length())) - + "... Update your SDK version to support new message types.")); - } - } catch (Exception e) { - if (onErrorHandler != null) { - onErrorHandler.accept(e); - } - } - } -} diff --git a/src/main/java/com/deepgram/resources/listen/v2/types/ListenV2CloseStream.java b/src/main/java/com/deepgram/resources/listen/v2/types/ListenV2CloseStream.java index e6308e2..c9f2f35 100644 --- a/src/main/java/com/deepgram/resources/listen/v2/types/ListenV2CloseStream.java +++ b/src/main/java/com/deepgram/resources/listen/v2/types/ListenV2CloseStream.java @@ -36,6 +36,11 @@ public boolean equals(Object other) { return other instanceof ListenV2CloseStream; } + @java.lang.Override + public int hashCode() { + return getClass().hashCode(); + } + @JsonAnyGetter public Map getAdditionalProperties() { return this.additionalProperties; diff --git a/src/main/java/com/deepgram/resources/listen/v2/types/ListenV2CloseStream.java.bak b/src/main/java/com/deepgram/resources/listen/v2/types/ListenV2CloseStream.java.bak deleted file mode 100644 index c9f2f35..0000000 --- a/src/main/java/com/deepgram/resources/listen/v2/types/ListenV2CloseStream.java.bak +++ /dev/null @@ -1,83 +0,0 @@ -/** - * This file was auto-generated by Fern from our API Definition. - */ -package com.deepgram.resources.listen.v2.types; - -import com.deepgram.core.ObjectMappers; -import com.fasterxml.jackson.annotation.JsonAnyGetter; -import com.fasterxml.jackson.annotation.JsonAnySetter; -import com.fasterxml.jackson.annotation.JsonIgnoreProperties; -import com.fasterxml.jackson.annotation.JsonInclude; -import com.fasterxml.jackson.annotation.JsonProperty; -import com.fasterxml.jackson.databind.annotation.JsonDeserialize; -import java.util.HashMap; -import java.util.Map; - -@JsonInclude(JsonInclude.Include.NON_ABSENT) -@JsonDeserialize(builder = ListenV2CloseStream.Builder.class) -public final class ListenV2CloseStream { - private final Map additionalProperties; - - private ListenV2CloseStream(Map additionalProperties) { - this.additionalProperties = additionalProperties; - } - - /** - * @return Message type identifier - */ - @JsonProperty("type") - public String getType() { - return "CloseStream"; - } - - @java.lang.Override - public boolean equals(Object other) { - if (this == other) return true; - return other instanceof ListenV2CloseStream; - } - - @java.lang.Override - public int hashCode() { - return getClass().hashCode(); - } - - @JsonAnyGetter - public Map getAdditionalProperties() { - return this.additionalProperties; - } - - @java.lang.Override - public String toString() { - return ObjectMappers.stringify(this); - } - - public static Builder builder() { - return new Builder(); - } - - @JsonIgnoreProperties(ignoreUnknown = true) - public static final class Builder { - @JsonAnySetter - private Map additionalProperties = new HashMap<>(); - - private Builder() {} - - public Builder from(ListenV2CloseStream other) { - return this; - } - - public ListenV2CloseStream build() { - return new ListenV2CloseStream(additionalProperties); - } - - public Builder additionalProperty(String key, Object value) { - this.additionalProperties.put(key, value); - return this; - } - - public Builder additionalProperties(Map additionalProperties) { - this.additionalProperties.putAll(additionalProperties); - return this; - } - } -} diff --git a/src/main/java/com/deepgram/resources/listen/v2/types/ListenV2ForceEndTurn.java b/src/main/java/com/deepgram/resources/listen/v2/types/ListenV2ForceEndTurn.java index c3dbd38..894bd3c 100644 --- a/src/main/java/com/deepgram/resources/listen/v2/types/ListenV2ForceEndTurn.java +++ b/src/main/java/com/deepgram/resources/listen/v2/types/ListenV2ForceEndTurn.java @@ -36,6 +36,11 @@ public boolean equals(Object other) { return other instanceof ListenV2ForceEndTurn; } + @java.lang.Override + public int hashCode() { + return getClass().hashCode(); + } + @JsonAnyGetter public Map getAdditionalProperties() { return this.additionalProperties; diff --git a/src/main/java/com/deepgram/resources/listen/v2/types/ListenV2ForceEndTurn.java.bak b/src/main/java/com/deepgram/resources/listen/v2/types/ListenV2ForceEndTurn.java.bak deleted file mode 100644 index 894bd3c..0000000 --- a/src/main/java/com/deepgram/resources/listen/v2/types/ListenV2ForceEndTurn.java.bak +++ /dev/null @@ -1,83 +0,0 @@ -/** - * This file was auto-generated by Fern from our API Definition. - */ -package com.deepgram.resources.listen.v2.types; - -import com.deepgram.core.ObjectMappers; -import com.fasterxml.jackson.annotation.JsonAnyGetter; -import com.fasterxml.jackson.annotation.JsonAnySetter; -import com.fasterxml.jackson.annotation.JsonIgnoreProperties; -import com.fasterxml.jackson.annotation.JsonInclude; -import com.fasterxml.jackson.annotation.JsonProperty; -import com.fasterxml.jackson.databind.annotation.JsonDeserialize; -import java.util.HashMap; -import java.util.Map; - -@JsonInclude(JsonInclude.Include.NON_ABSENT) -@JsonDeserialize(builder = ListenV2ForceEndTurn.Builder.class) -public final class ListenV2ForceEndTurn { - private final Map additionalProperties; - - private ListenV2ForceEndTurn(Map additionalProperties) { - this.additionalProperties = additionalProperties; - } - - /** - * @return Message type identifier - */ - @JsonProperty("type") - public String getType() { - return "ForceEndTurn"; - } - - @java.lang.Override - public boolean equals(Object other) { - if (this == other) return true; - return other instanceof ListenV2ForceEndTurn; - } - - @java.lang.Override - public int hashCode() { - return getClass().hashCode(); - } - - @JsonAnyGetter - public Map getAdditionalProperties() { - return this.additionalProperties; - } - - @java.lang.Override - public String toString() { - return ObjectMappers.stringify(this); - } - - public static Builder builder() { - return new Builder(); - } - - @JsonIgnoreProperties(ignoreUnknown = true) - public static final class Builder { - @JsonAnySetter - private Map additionalProperties = new HashMap<>(); - - private Builder() {} - - public Builder from(ListenV2ForceEndTurn other) { - return this; - } - - public ListenV2ForceEndTurn build() { - return new ListenV2ForceEndTurn(additionalProperties); - } - - public Builder additionalProperty(String key, Object value) { - this.additionalProperties.put(key, value); - return this; - } - - public Builder additionalProperties(Map additionalProperties) { - this.additionalProperties.putAll(additionalProperties); - return this; - } - } -} diff --git a/src/main/java/com/deepgram/resources/listen/v2/websocket/V2WebSocketClient.java b/src/main/java/com/deepgram/resources/listen/v2/websocket/V2WebSocketClient.java index 84bf5df..5f55fea 100644 --- a/src/main/java/com/deepgram/resources/listen/v2/websocket/V2WebSocketClient.java +++ b/src/main/java/com/deepgram/resources/listen/v2/websocket/V2WebSocketClient.java @@ -6,6 +6,7 @@ import com.deepgram.core.ClientOptions; import com.deepgram.core.DisconnectReason; import com.deepgram.core.ObjectMappers; +import com.deepgram.core.QueryStringMapper; import com.deepgram.core.ReconnectingWebSocketListener; import com.deepgram.core.RequestOptions; import com.deepgram.core.WebSocketReadyState; @@ -22,6 +23,7 @@ import com.fasterxml.jackson.databind.ObjectMapper; import java.util.concurrent.CompletableFuture; import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.atomic.AtomicReference; import java.util.function.Consumer; import okhttp3.HttpUrl; import okhttp3.OkHttpClient; @@ -59,6 +61,8 @@ public class V2WebSocketClient implements AutoCloseable { private ReconnectingWebSocketListener reconnectingListener; + private final AtomicReference closeStreamSocket = new AtomicReference<>(); + private volatile Consumer connectedHandler; private volatile Consumer turnInfoHandler; @@ -87,6 +91,7 @@ public V2WebSocketClient(ClientOptions clientOptions) { */ public CompletableFuture connect(V2ConnectOptions options) { connectionFuture = new CompletableFuture<>(); + closeStreamSocket.set(null); String baseUrl = clientOptions.environment().getProductionURL(); String fullPath = "/v2/listen"; if (baseUrl.endsWith("/") && fullPath.startsWith("/")) { @@ -129,12 +134,12 @@ public CompletableFuture connect(V2ConnectOptions options) { "eot_timeout_ms", String.valueOf(options.getEotTimeoutMs().get())); } if (options.getKeyterm() != null && options.getKeyterm().isPresent()) { - urlBuilder.addQueryParameter( - "keyterm", String.valueOf(options.getKeyterm().get())); + QueryStringMapper.addQueryParameter( + urlBuilder, "keyterm", options.getKeyterm().get().get(), true); } if (options.getLanguageHint() != null && options.getLanguageHint().isPresent()) { - urlBuilder.addQueryParameter( - "language_hint", String.valueOf(options.getLanguageHint().get())); + QueryStringMapper.addQueryParameter( + urlBuilder, "language_hint", options.getLanguageHint().get().get(), true); } if (options.getProfanityFilter() != null && options.getProfanityFilter().isPresent()) { urlBuilder.addQueryParameter( @@ -154,7 +159,14 @@ public CompletableFuture connect(V2ConnectOptions options) { "mip_opt_out", String.valueOf(options.getMipOptOut().get())); } if (options.getTag() != null && options.getTag().isPresent()) { - urlBuilder.addQueryParameter("tag", String.valueOf(options.getTag().get())); + QueryStringMapper.addQueryParameter(urlBuilder, "tag", options.getTag().get().get(), true); + } + if (options.getAdditionalProperties() != null) { + options.getAdditionalProperties().forEach((key, value) -> { + if (value != null) { + QueryStringMapper.addQueryParameter(urlBuilder, key, value, true); + } + }); } Request.Builder requestBuilder = new Request.Builder().url(urlBuilder.build()); clientOptions.headers((RequestOptions) null).forEach(requestBuilder::addHeader); @@ -182,6 +194,7 @@ public CompletableFuture connect(V2ConnectOptions options) { clientOptions::isClosed) { @Override protected void onWebSocketOpen(WebSocket webSocket, Response response) { + closeStreamSocket.set(null); readyState = WebSocketReadyState.OPEN; if (onConnectedHandler != null) { onConnectedHandler.run(); @@ -213,6 +226,12 @@ protected void onWebSocketClosed(WebSocket webSocket, int code, String reason) { onDisconnectedHandler.accept(new DisconnectReason(code, reason)); } } + + @Override + protected boolean shouldReconnectAfterClose(WebSocket webSocket, int code, String reason) { + return super.shouldReconnectAfterClose(webSocket, code, reason) + && (code != 1005 || closeStreamSocket.get() != webSocket); + } }; clientOptions.registerWebSocket(this); reconnectingListener.connect(); @@ -263,12 +282,13 @@ public CompletableFuture sendMedia(ByteString message) { } /** - * Sends a ListenV2CloseStream message to the server asynchronously. + * Sends a ListenV2CloseStream message to the server asynchronously. A queued CloseStream does + * not suppress reconnects because it might never reach the server. * @param message the message to send * @return a CompletableFuture that completes when the message is sent */ public CompletableFuture sendCloseStream(ListenV2CloseStream message) { - return sendMessage(message); + return sendMessage(message, closeStreamSocket::set); } /** @@ -402,12 +422,16 @@ private void assertSocketIsOpen() { } private CompletableFuture sendMessage(Object body) { + return sendMessage(body, null); + } + + private CompletableFuture sendMessage(Object body, Consumer onSent) { CompletableFuture future = new CompletableFuture<>(); try { assertSocketIsOpen(); String json = objectMapper.writeValueAsString(body); // Use reconnecting listener's send method which handles queuing - reconnectingListener.send(json); + reconnectingListener.send(json, onSent); future.complete(null); } catch (IllegalStateException e) { future.completeExceptionally(e); @@ -528,11 +552,8 @@ private void handleIncomingMessage(String json) { return; } } - if (onErrorHandler != null) { - onErrorHandler.accept(new RuntimeException( - "Unrecognized WebSocket message: " + json.substring(0, Math.min(200, json.length())) - + "... Update your SDK version to support new message types.")); - } + // The raw frame is already delivered to onMessage(String), so ignore unknown typed + // frames for forward compatibility with newly added server control messages. } catch (Exception e) { if (onErrorHandler != null) { onErrorHandler.accept(e); diff --git a/src/main/java/com/deepgram/resources/listen/v2/websocket/V2WebSocketClient.java.bak b/src/main/java/com/deepgram/resources/listen/v2/websocket/V2WebSocketClient.java.bak deleted file mode 100644 index 5f55fea..0000000 --- a/src/main/java/com/deepgram/resources/listen/v2/websocket/V2WebSocketClient.java.bak +++ /dev/null @@ -1,563 +0,0 @@ -/** - * This file was auto-generated by Fern from our API Definition. - */ -package com.deepgram.resources.listen.v2.websocket; - -import com.deepgram.core.ClientOptions; -import com.deepgram.core.DisconnectReason; -import com.deepgram.core.ObjectMappers; -import com.deepgram.core.QueryStringMapper; -import com.deepgram.core.ReconnectingWebSocketListener; -import com.deepgram.core.RequestOptions; -import com.deepgram.core.WebSocketReadyState; -import com.deepgram.resources.listen.v2.types.ListenV2CloseStream; -import com.deepgram.resources.listen.v2.types.ListenV2Configure; -import com.deepgram.resources.listen.v2.types.ListenV2ConfigureFailure; -import com.deepgram.resources.listen.v2.types.ListenV2ConfigureSuccess; -import com.deepgram.resources.listen.v2.types.ListenV2Connected; -import com.deepgram.resources.listen.v2.types.ListenV2FatalError; -import com.deepgram.resources.listen.v2.types.ListenV2ForceEndTurn; -import com.deepgram.resources.listen.v2.types.ListenV2TurnInfo; -import com.deepgram.resources.listen.v2.types.ListenV2Warning; -import com.fasterxml.jackson.databind.JsonNode; -import com.fasterxml.jackson.databind.ObjectMapper; -import java.util.concurrent.CompletableFuture; -import java.util.concurrent.ScheduledExecutorService; -import java.util.concurrent.atomic.AtomicReference; -import java.util.function.Consumer; -import okhttp3.HttpUrl; -import okhttp3.OkHttpClient; -import okhttp3.Request; -import okhttp3.Response; -import okhttp3.WebSocket; -import okio.ByteString; - -/** - * WebSocket client for the v2 channel. - * Provides real-time bidirectional communication with strongly-typed messages. - */ -public class V2WebSocketClient implements AutoCloseable { - protected final ClientOptions clientOptions; - - private final ObjectMapper objectMapper; - - private final OkHttpClient okHttpClient; - - private ScheduledExecutorService timeoutExecutor; - - private volatile WebSocketReadyState readyState = WebSocketReadyState.CLOSED; - - private volatile Runnable onConnectedHandler; - - private volatile Consumer onDisconnectedHandler; - - private volatile Consumer onErrorHandler; - - private volatile Consumer onMessageHandler; - - private volatile ReconnectingWebSocketListener.ReconnectOptions reconnectOptions; - - private CompletableFuture connectionFuture; - - private ReconnectingWebSocketListener reconnectingListener; - - private final AtomicReference closeStreamSocket = new AtomicReference<>(); - - private volatile Consumer connectedHandler; - - private volatile Consumer turnInfoHandler; - - private volatile Consumer configureSuccessHandler; - - private volatile Consumer configureFailureHandler; - - private volatile Consumer warningHandler; - - private volatile Consumer errorHandler; - - /** - * Creates a new async WebSocket client for the v2 channel. - */ - public V2WebSocketClient(ClientOptions clientOptions) { - this.clientOptions = clientOptions; - this.objectMapper = ObjectMappers.JSON_MAPPER; - this.okHttpClient = clientOptions.httpClient(); - } - - /** - * Establishes the WebSocket connection asynchronously with automatic reconnection. - * @return a CompletableFuture that completes when the connection is established - * @param options connection options including query parameters - */ - public CompletableFuture connect(V2ConnectOptions options) { - connectionFuture = new CompletableFuture<>(); - closeStreamSocket.set(null); - String baseUrl = clientOptions.environment().getProductionURL(); - String fullPath = "/v2/listen"; - if (baseUrl.endsWith("/") && fullPath.startsWith("/")) { - fullPath = fullPath.substring(1); - } else if (!baseUrl.endsWith("/") && !fullPath.startsWith("/")) { - fullPath = "/" + fullPath; - } - // OkHttp's HttpUrl only supports http/https schemes; convert wss/ws for URL parsing - if (baseUrl.startsWith("wss://")) { - baseUrl = "https://" + baseUrl.substring(6); - } else if (baseUrl.startsWith("ws://")) { - baseUrl = "http://" + baseUrl.substring(5); - } - HttpUrl parsedUrl = HttpUrl.parse(baseUrl + fullPath); - if (parsedUrl == null) { - throw new IllegalArgumentException("Invalid WebSocket URL: " + baseUrl + fullPath); - } - HttpUrl.Builder urlBuilder = parsedUrl.newBuilder(); - urlBuilder.addQueryParameter("model", String.valueOf(options.getModel())); - if (options.getEncoding() != null && options.getEncoding().isPresent()) { - urlBuilder.addQueryParameter( - "encoding", String.valueOf(options.getEncoding().get())); - } - if (options.getSampleRate() != null && options.getSampleRate().isPresent()) { - urlBuilder.addQueryParameter( - "sample_rate", String.valueOf(options.getSampleRate().get())); - } - if (options.getEagerEotThreshold() != null - && options.getEagerEotThreshold().isPresent()) { - urlBuilder.addQueryParameter( - "eager_eot_threshold", - String.valueOf(options.getEagerEotThreshold().get())); - } - if (options.getEotThreshold() != null && options.getEotThreshold().isPresent()) { - urlBuilder.addQueryParameter( - "eot_threshold", String.valueOf(options.getEotThreshold().get())); - } - if (options.getEotTimeoutMs() != null && options.getEotTimeoutMs().isPresent()) { - urlBuilder.addQueryParameter( - "eot_timeout_ms", String.valueOf(options.getEotTimeoutMs().get())); - } - if (options.getKeyterm() != null && options.getKeyterm().isPresent()) { - QueryStringMapper.addQueryParameter( - urlBuilder, "keyterm", options.getKeyterm().get().get(), true); - } - if (options.getLanguageHint() != null && options.getLanguageHint().isPresent()) { - QueryStringMapper.addQueryParameter( - urlBuilder, "language_hint", options.getLanguageHint().get().get(), true); - } - if (options.getProfanityFilter() != null && options.getProfanityFilter().isPresent()) { - urlBuilder.addQueryParameter( - "profanity_filter", - String.valueOf(options.getProfanityFilter().get())); - } - if (options.getNumerals() != null && options.getNumerals().isPresent()) { - urlBuilder.addQueryParameter( - "numerals", String.valueOf(options.getNumerals().get())); - } - if (options.getRedact() != null && options.getRedact().isPresent()) { - urlBuilder.addQueryParameter( - "redact", String.valueOf(options.getRedact().get())); - } - if (options.getMipOptOut() != null && options.getMipOptOut().isPresent()) { - urlBuilder.addQueryParameter( - "mip_opt_out", String.valueOf(options.getMipOptOut().get())); - } - if (options.getTag() != null && options.getTag().isPresent()) { - QueryStringMapper.addQueryParameter(urlBuilder, "tag", options.getTag().get().get(), true); - } - if (options.getAdditionalProperties() != null) { - options.getAdditionalProperties().forEach((key, value) -> { - if (value != null) { - QueryStringMapper.addQueryParameter(urlBuilder, key, value, true); - } - }); - } - Request.Builder requestBuilder = new Request.Builder().url(urlBuilder.build()); - clientOptions.headers((RequestOptions) null).forEach(requestBuilder::addHeader); - final Request request = requestBuilder.build(); - this.readyState = WebSocketReadyState.CONNECTING; - ReconnectingWebSocketListener.ReconnectOptions reconnectOpts = this.reconnectOptions != null - ? this.reconnectOptions - : ReconnectingWebSocketListener.ReconnectOptions.builder().build(); - this.reconnectingListener = - new ReconnectingWebSocketListener( - reconnectOpts, - () -> { - if (clientOptions.isClosed()) { - throw new IllegalStateException("root client has been closed"); - } - if (clientOptions.webSocketFactory().isPresent()) { - return clientOptions - .webSocketFactory() - .get() - .create(request, this.reconnectingListener); - } else { - return okHttpClient.newWebSocket(request, this.reconnectingListener); - } - }, - clientOptions::isClosed) { - @Override - protected void onWebSocketOpen(WebSocket webSocket, Response response) { - closeStreamSocket.set(null); - readyState = WebSocketReadyState.OPEN; - if (onConnectedHandler != null) { - onConnectedHandler.run(); - } - connectionFuture.complete(null); - } - - @Override - protected void onWebSocketMessage(WebSocket webSocket, String text) { - handleIncomingMessage(text); - } - - @Override - protected void onWebSocketBinaryMessage(WebSocket webSocket, ByteString bytes) {} - - @Override - protected void onWebSocketFailure(WebSocket webSocket, Throwable t, Response response) { - readyState = WebSocketReadyState.CLOSED; - if (onErrorHandler != null) { - onErrorHandler.accept(new RuntimeException(t)); - } - connectionFuture.completeExceptionally(t); - } - - @Override - protected void onWebSocketClosed(WebSocket webSocket, int code, String reason) { - readyState = WebSocketReadyState.CLOSED; - if (onDisconnectedHandler != null) { - onDisconnectedHandler.accept(new DisconnectReason(code, reason)); - } - } - - @Override - protected boolean shouldReconnectAfterClose(WebSocket webSocket, int code, String reason) { - return super.shouldReconnectAfterClose(webSocket, code, reason) - && (code != 1005 || closeStreamSocket.get() != webSocket); - } - }; - clientOptions.registerWebSocket(this); - reconnectingListener.connect(); - return connectionFuture; - } - - /** - * Disconnects the WebSocket connection and releases resources. - */ - public void disconnect() { - clientOptions.unregisterWebSocket(this); - if (reconnectingListener != null) { - reconnectingListener.disconnect(); - } - if (timeoutExecutor != null) { - timeoutExecutor.shutdownNow(); - timeoutExecutor = null; - } - } - - /** - * Gets the current state of the WebSocket connection. - * - * This provides the actual connection state, similar to the W3C WebSocket API. - * - * @return the current WebSocket ready state - */ - public WebSocketReadyState getReadyState() { - return readyState; - } - - /** - * Sends a ListenV2Media message to the server asynchronously. - * @param message the message to send - * @return a CompletableFuture that completes when the message is sent - */ - public CompletableFuture sendMedia(ByteString message) { - CompletableFuture future = new CompletableFuture<>(); - try { - assertSocketIsOpen(); - // Use reconnecting listener's sendBinary method which handles queuing - reconnectingListener.sendBinary(message); - future.complete(null); - } catch (Exception e) { - future.completeExceptionally(new RuntimeException("Failed to send binary data", e)); - } - return future; - } - - /** - * Sends a ListenV2CloseStream message to the server asynchronously. A queued CloseStream does - * not suppress reconnects because it might never reach the server. - * @param message the message to send - * @return a CompletableFuture that completes when the message is sent - */ - public CompletableFuture sendCloseStream(ListenV2CloseStream message) { - return sendMessage(message, closeStreamSocket::set); - } - - /** - * Sends a ListenV2ForceEndTurn message to the server asynchronously. - * @param message the message to send - * @return a CompletableFuture that completes when the message is sent - */ - public CompletableFuture sendForceEndTurn(ListenV2ForceEndTurn message) { - return sendMessage(message); - } - - /** - * Sends a ListenV2Configure message to the server asynchronously. - * @param message the message to send - * @return a CompletableFuture that completes when the message is sent - */ - public CompletableFuture sendConfigure(ListenV2Configure message) { - return sendMessage(message); - } - - /** - * Registers a handler for ListenV2Connected messages from the server. - * @param handler the handler to invoke when a message is received - */ - public void onConnected(Consumer handler) { - this.connectedHandler = handler; - } - - /** - * Registers a handler for ListenV2TurnInfo messages from the server. - * @param handler the handler to invoke when a message is received - */ - public void onTurnInfo(Consumer handler) { - this.turnInfoHandler = handler; - } - - /** - * Registers a handler for ListenV2ConfigureSuccess messages from the server. - * @param handler the handler to invoke when a message is received - */ - public void onConfigureSuccess(Consumer handler) { - this.configureSuccessHandler = handler; - } - - /** - * Registers a handler for ListenV2ConfigureFailure messages from the server. - * @param handler the handler to invoke when a message is received - */ - public void onConfigureFailure(Consumer handler) { - this.configureFailureHandler = handler; - } - - /** - * Registers a handler for ListenV2Warning messages from the server. - * @param handler the handler to invoke when a message is received - */ - public void onWarning(Consumer handler) { - this.warningHandler = handler; - } - - /** - * Registers a handler for ListenV2FatalError messages from the server. - * @param handler the handler to invoke when a message is received - */ - public void onErrorMessage(Consumer handler) { - this.errorHandler = handler; - } - - /** - * Registers a handler called when the connection is established. - * @param handler the handler to invoke when connected - */ - public void onConnected(Runnable handler) { - this.onConnectedHandler = handler; - } - - /** - * Registers a handler called when the connection is closed. - * @param handler the handler to invoke when disconnected - */ - public void onDisconnected(Consumer handler) { - this.onDisconnectedHandler = handler; - } - - /** - * Registers a handler called when an error occurs. - * @param handler the handler to invoke on error - */ - public void onError(Consumer handler) { - this.onErrorHandler = handler; - } - - /** - * Registers a handler called for every incoming text message. - * The handler receives the raw JSON string before type-specific dispatch. - * @param handler the handler to invoke with the raw message JSON - */ - public void onMessage(Consumer handler) { - this.onMessageHandler = handler; - } - - /** - * Configures reconnection behavior. Must be called before {@link #connect}. - * - * @param options the reconnection options (backoff, retries, queue size) - */ - public void reconnectOptions(ReconnectingWebSocketListener.ReconnectOptions options) { - this.reconnectOptions = options; - } - - /** - * Closes this WebSocket client, releasing all resources. - * Equivalent to calling {@link #disconnect()}. - */ - @Override - public void close() { - disconnect(); - } - - /** - * Ensures the WebSocket is connected and ready to send messages. - * @throws IllegalStateException if the socket is not connected or not open - */ - private void assertSocketIsOpen() { - if (reconnectingListener.getWebSocket() == null) { - throw new IllegalStateException("WebSocket is not connected. Call connect() first."); - } - if (readyState != WebSocketReadyState.OPEN) { - throw new IllegalStateException("WebSocket is not open. Current state: " + readyState); - } - } - - private CompletableFuture sendMessage(Object body) { - return sendMessage(body, null); - } - - private CompletableFuture sendMessage(Object body, Consumer onSent) { - CompletableFuture future = new CompletableFuture<>(); - try { - assertSocketIsOpen(); - String json = objectMapper.writeValueAsString(body); - // Use reconnecting listener's send method which handles queuing - reconnectingListener.send(json, onSent); - future.complete(null); - } catch (IllegalStateException e) { - future.completeExceptionally(e); - } catch (Exception e) { - future.completeExceptionally(new RuntimeException("Failed to send message", e)); - } - return future; - } - - private void handleIncomingMessage(String json) { - try { - if (onMessageHandler != null) { - onMessageHandler.accept(json); - } - JsonNode node = objectMapper.readTree(json); - if (node == null || node.isNull()) { - throw new IllegalArgumentException("Received null or invalid JSON message"); - } - if (node.has("request_id") - && node.has("sequence_id") - && node.has("event") - && node.has("turn_index") - && node.has("audio_window_start") - && node.has("audio_window_end") - && node.has("transcript") - && node.has("words") - && node.has("end_of_turn_confidence") - && "TurnInfo".equals(node.path("type").asText())) { - ListenV2TurnInfo turnInfoHandlerEvent = null; - try { - turnInfoHandlerEvent = objectMapper.treeToValue(node, ListenV2TurnInfo.class); - } catch (Exception e) { - } - if (turnInfoHandlerEvent != null) { - if (turnInfoHandler != null) { - turnInfoHandler.accept(turnInfoHandlerEvent); - } - return; - } - } - if (node.has("request_id") - && node.has("thresholds") - && node.has("keyterms") - && node.has("sequence_id") - && "ConfigureSuccess".equals(node.path("type").asText())) { - ListenV2ConfigureSuccess configureSuccessHandlerEvent = null; - try { - configureSuccessHandlerEvent = objectMapper.treeToValue(node, ListenV2ConfigureSuccess.class); - } catch (Exception e) { - } - if (configureSuccessHandlerEvent != null) { - if (configureSuccessHandler != null) { - configureSuccessHandler.accept(configureSuccessHandlerEvent); - } - return; - } - } - if (node.has("request_id") - && node.has("sequence_id") - && node.has("code") - && node.has("description") - && "Warning".equals(node.path("type").asText())) { - ListenV2Warning warningHandlerEvent = null; - try { - warningHandlerEvent = objectMapper.treeToValue(node, ListenV2Warning.class); - } catch (Exception e) { - } - if (warningHandlerEvent != null) { - if (warningHandler != null) { - warningHandler.accept(warningHandlerEvent); - } - return; - } - } - if (node.has("sequence_id") - && node.has("code") - && node.has("description") - && "Error".equals(node.path("type").asText())) { - ListenV2FatalError errorHandlerEvent = null; - try { - errorHandlerEvent = objectMapper.treeToValue(node, ListenV2FatalError.class); - } catch (Exception e) { - } - if (errorHandlerEvent != null) { - if (errorHandler != null) { - errorHandler.accept(errorHandlerEvent); - } - return; - } - } - if (node.has("request_id") - && node.has("sequence_id") - && "Connected".equals(node.path("type").asText())) { - ListenV2Connected connectedHandlerEvent = null; - try { - connectedHandlerEvent = objectMapper.treeToValue(node, ListenV2Connected.class); - } catch (Exception e) { - } - if (connectedHandlerEvent != null) { - if (connectedHandler != null) { - connectedHandler.accept(connectedHandlerEvent); - } - return; - } - } - if (node.has("request_id") - && node.has("sequence_id") - && "ConfigureFailure".equals(node.path("type").asText())) { - ListenV2ConfigureFailure configureFailureHandlerEvent = null; - try { - configureFailureHandlerEvent = objectMapper.treeToValue(node, ListenV2ConfigureFailure.class); - } catch (Exception e) { - } - if (configureFailureHandlerEvent != null) { - if (configureFailureHandler != null) { - configureFailureHandler.accept(configureFailureHandlerEvent); - } - return; - } - } - // The raw frame is already delivered to onMessage(String), so ignore unknown typed - // frames for forward compatibility with newly added server control messages. - } catch (Exception e) { - if (onErrorHandler != null) { - onErrorHandler.accept(e); - } - } - } -} diff --git a/src/main/java/com/deepgram/resources/speak/v1/websocket/V1WebSocketClient.java b/src/main/java/com/deepgram/resources/speak/v1/websocket/V1WebSocketClient.java index 6dd298a..e6824a3 100644 --- a/src/main/java/com/deepgram/resources/speak/v1/websocket/V1WebSocketClient.java +++ b/src/main/java/com/deepgram/resources/speak/v1/websocket/V1WebSocketClient.java @@ -6,6 +6,7 @@ import com.deepgram.core.ClientOptions; import com.deepgram.core.DisconnectReason; import com.deepgram.core.ObjectMappers; +import com.deepgram.core.QueryStringMapper; import com.deepgram.core.ReconnectingWebSocketListener; import com.deepgram.core.RequestOptions; import com.deepgram.core.WebSocketReadyState; @@ -122,6 +123,13 @@ public CompletableFuture connect(V1ConnectOptions options) { urlBuilder.addQueryParameter( "speed", String.valueOf(options.getSpeed().get())); } + if (options.getAdditionalProperties() != null) { + options.getAdditionalProperties().forEach((key, value) -> { + if (value != null) { + QueryStringMapper.addQueryParameter(urlBuilder, key, value, true); + } + }); + } Request.Builder requestBuilder = new Request.Builder().url(urlBuilder.build()); clientOptions.headers((RequestOptions) null).forEach(requestBuilder::addHeader); final Request request = requestBuilder.build(); diff --git a/src/main/java/com/deepgram/resources/speak/v1/websocket/V1WebSocketClient.java.bak b/src/main/java/com/deepgram/resources/speak/v1/websocket/V1WebSocketClient.java.bak deleted file mode 100644 index e6824a3..0000000 --- a/src/main/java/com/deepgram/resources/speak/v1/websocket/V1WebSocketClient.java.bak +++ /dev/null @@ -1,467 +0,0 @@ -/** - * This file was auto-generated by Fern from our API Definition. - */ -package com.deepgram.resources.speak.v1.websocket; - -import com.deepgram.core.ClientOptions; -import com.deepgram.core.DisconnectReason; -import com.deepgram.core.ObjectMappers; -import com.deepgram.core.QueryStringMapper; -import com.deepgram.core.ReconnectingWebSocketListener; -import com.deepgram.core.RequestOptions; -import com.deepgram.core.WebSocketReadyState; -import com.deepgram.resources.speak.v1.types.SpeakV1Clear; -import com.deepgram.resources.speak.v1.types.SpeakV1Cleared; -import com.deepgram.resources.speak.v1.types.SpeakV1Close; -import com.deepgram.resources.speak.v1.types.SpeakV1Flush; -import com.deepgram.resources.speak.v1.types.SpeakV1Flushed; -import com.deepgram.resources.speak.v1.types.SpeakV1Metadata; -import com.deepgram.resources.speak.v1.types.SpeakV1Text; -import com.deepgram.resources.speak.v1.types.SpeakV1Warning; -import com.fasterxml.jackson.databind.JsonNode; -import com.fasterxml.jackson.databind.ObjectMapper; -import java.util.concurrent.CompletableFuture; -import java.util.concurrent.ScheduledExecutorService; -import java.util.function.Consumer; -import okhttp3.HttpUrl; -import okhttp3.OkHttpClient; -import okhttp3.Request; -import okhttp3.Response; -import okhttp3.WebSocket; -import okio.ByteString; - -/** - * WebSocket client for the v1 channel. - * Provides real-time bidirectional communication with strongly-typed messages. - */ -public class V1WebSocketClient implements AutoCloseable { - protected final ClientOptions clientOptions; - - private final ObjectMapper objectMapper; - - private final OkHttpClient okHttpClient; - - private ScheduledExecutorService timeoutExecutor; - - private volatile WebSocketReadyState readyState = WebSocketReadyState.CLOSED; - - private volatile Runnable onConnectedHandler; - - private volatile Consumer onDisconnectedHandler; - - private volatile Consumer onErrorHandler; - - private volatile Consumer onMessageHandler; - - private volatile ReconnectingWebSocketListener.ReconnectOptions reconnectOptions; - - private CompletableFuture connectionFuture; - - private ReconnectingWebSocketListener reconnectingListener; - - private volatile Consumer speakV1AudioHandler; - - private volatile Consumer metadataHandler; - - private volatile Consumer flushedHandler; - - private volatile Consumer clearedHandler; - - private volatile Consumer warningHandler; - - /** - * Creates a new async WebSocket client for the v1 channel. - */ - public V1WebSocketClient(ClientOptions clientOptions) { - this.clientOptions = clientOptions; - this.objectMapper = ObjectMappers.JSON_MAPPER; - this.okHttpClient = clientOptions.httpClient(); - } - - /** - * Establishes the WebSocket connection asynchronously with automatic reconnection. - * @return a CompletableFuture that completes when the connection is established - * @param options connection options including query parameters - */ - public CompletableFuture connect(V1ConnectOptions options) { - connectionFuture = new CompletableFuture<>(); - String baseUrl = clientOptions.environment().getProductionURL(); - String fullPath = "/v1/speak"; - if (baseUrl.endsWith("/") && fullPath.startsWith("/")) { - fullPath = fullPath.substring(1); - } else if (!baseUrl.endsWith("/") && !fullPath.startsWith("/")) { - fullPath = "/" + fullPath; - } - // OkHttp's HttpUrl only supports http/https schemes; convert wss/ws for URL parsing - if (baseUrl.startsWith("wss://")) { - baseUrl = "https://" + baseUrl.substring(6); - } else if (baseUrl.startsWith("ws://")) { - baseUrl = "http://" + baseUrl.substring(5); - } - HttpUrl parsedUrl = HttpUrl.parse(baseUrl + fullPath); - if (parsedUrl == null) { - throw new IllegalArgumentException("Invalid WebSocket URL: " + baseUrl + fullPath); - } - HttpUrl.Builder urlBuilder = parsedUrl.newBuilder(); - if (options.getEncoding() != null && options.getEncoding().isPresent()) { - urlBuilder.addQueryParameter( - "encoding", String.valueOf(options.getEncoding().get())); - } - if (options.getMipOptOut() != null && options.getMipOptOut().isPresent()) { - urlBuilder.addQueryParameter( - "mip_opt_out", String.valueOf(options.getMipOptOut().get())); - } - if (options.getModel() != null && options.getModel().isPresent()) { - urlBuilder.addQueryParameter( - "model", String.valueOf(options.getModel().get())); - } - if (options.getSampleRate() != null && options.getSampleRate().isPresent()) { - urlBuilder.addQueryParameter( - "sample_rate", String.valueOf(options.getSampleRate().get())); - } - if (options.getSpeed() != null && options.getSpeed().isPresent()) { - urlBuilder.addQueryParameter( - "speed", String.valueOf(options.getSpeed().get())); - } - if (options.getAdditionalProperties() != null) { - options.getAdditionalProperties().forEach((key, value) -> { - if (value != null) { - QueryStringMapper.addQueryParameter(urlBuilder, key, value, true); - } - }); - } - Request.Builder requestBuilder = new Request.Builder().url(urlBuilder.build()); - clientOptions.headers((RequestOptions) null).forEach(requestBuilder::addHeader); - final Request request = requestBuilder.build(); - this.readyState = WebSocketReadyState.CONNECTING; - ReconnectingWebSocketListener.ReconnectOptions reconnectOpts = this.reconnectOptions != null - ? this.reconnectOptions - : ReconnectingWebSocketListener.ReconnectOptions.builder().build(); - this.reconnectingListener = - new ReconnectingWebSocketListener( - reconnectOpts, - () -> { - if (clientOptions.isClosed()) { - throw new IllegalStateException("root client has been closed"); - } - if (clientOptions.webSocketFactory().isPresent()) { - return clientOptions - .webSocketFactory() - .get() - .create(request, this.reconnectingListener); - } else { - return okHttpClient.newWebSocket(request, this.reconnectingListener); - } - }, - clientOptions::isClosed) { - @Override - protected void onWebSocketOpen(WebSocket webSocket, Response response) { - readyState = WebSocketReadyState.OPEN; - if (onConnectedHandler != null) { - onConnectedHandler.run(); - } - connectionFuture.complete(null); - } - - @Override - protected void onWebSocketMessage(WebSocket webSocket, String text) { - handleIncomingMessage(text); - } - - @Override - protected void onWebSocketBinaryMessage(WebSocket webSocket, ByteString bytes) { - if (speakV1AudioHandler != null) { - speakV1AudioHandler.accept(bytes); - } - } - - @Override - protected void onWebSocketFailure(WebSocket webSocket, Throwable t, Response response) { - readyState = WebSocketReadyState.CLOSED; - if (onErrorHandler != null) { - onErrorHandler.accept(new RuntimeException(t)); - } - connectionFuture.completeExceptionally(t); - } - - @Override - protected void onWebSocketClosed(WebSocket webSocket, int code, String reason) { - readyState = WebSocketReadyState.CLOSED; - if (onDisconnectedHandler != null) { - onDisconnectedHandler.accept(new DisconnectReason(code, reason)); - } - } - }; - clientOptions.registerWebSocket(this); - reconnectingListener.connect(); - return connectionFuture; - } - - /** - * Establishes the WebSocket connection asynchronously with default options. - * @return a CompletableFuture that completes when the connection is established - */ - public CompletableFuture connect() { - return connect(V1ConnectOptions.builder().build()); - } - - /** - * Disconnects the WebSocket connection and releases resources. - */ - public void disconnect() { - clientOptions.unregisterWebSocket(this); - if (reconnectingListener != null) { - reconnectingListener.disconnect(); - } - if (timeoutExecutor != null) { - timeoutExecutor.shutdownNow(); - timeoutExecutor = null; - } - } - - /** - * Gets the current state of the WebSocket connection. - * - * This provides the actual connection state, similar to the W3C WebSocket API. - * - * @return the current WebSocket ready state - */ - public WebSocketReadyState getReadyState() { - return readyState; - } - - /** - * Sends a SpeakV1Text message to the server asynchronously. - * @param message the message to send - * @return a CompletableFuture that completes when the message is sent - */ - public CompletableFuture sendText(SpeakV1Text message) { - return sendMessage(message); - } - - /** - * Sends a SpeakV1Flush message to the server asynchronously. - * @param message the message to send - * @return a CompletableFuture that completes when the message is sent - */ - public CompletableFuture sendFlush(SpeakV1Flush message) { - return sendMessage(message); - } - - /** - * Sends a SpeakV1Clear message to the server asynchronously. - * @param message the message to send - * @return a CompletableFuture that completes when the message is sent - */ - public CompletableFuture sendClear(SpeakV1Clear message) { - return sendMessage(message); - } - - /** - * Sends a SpeakV1Close message to the server asynchronously. - * @param message the message to send - * @return a CompletableFuture that completes when the message is sent - */ - public CompletableFuture sendClose(SpeakV1Close message) { - return sendMessage(message); - } - - /** - * Registers a handler for SpeakV1Audio messages from the server. - * @param handler the handler to invoke when a message is received - */ - public void onSpeakV1Audio(Consumer handler) { - this.speakV1AudioHandler = handler; - } - - /** - * Registers a handler for SpeakV1Metadata messages from the server. - * @param handler the handler to invoke when a message is received - */ - public void onMetadata(Consumer handler) { - this.metadataHandler = handler; - } - - /** - * Registers a handler for SpeakV1Flushed messages from the server. - * @param handler the handler to invoke when a message is received - */ - public void onFlushed(Consumer handler) { - this.flushedHandler = handler; - } - - /** - * Registers a handler for SpeakV1Cleared messages from the server. - * @param handler the handler to invoke when a message is received - */ - public void onCleared(Consumer handler) { - this.clearedHandler = handler; - } - - /** - * Registers a handler for SpeakV1Warning messages from the server. - * @param handler the handler to invoke when a message is received - */ - public void onWarning(Consumer handler) { - this.warningHandler = handler; - } - - /** - * Registers a handler called when the connection is established. - * @param handler the handler to invoke when connected - */ - public void onConnected(Runnable handler) { - this.onConnectedHandler = handler; - } - - /** - * Registers a handler called when the connection is closed. - * @param handler the handler to invoke when disconnected - */ - public void onDisconnected(Consumer handler) { - this.onDisconnectedHandler = handler; - } - - /** - * Registers a handler called when an error occurs. - * @param handler the handler to invoke on error - */ - public void onError(Consumer handler) { - this.onErrorHandler = handler; - } - - /** - * Registers a handler called for every incoming text message. - * The handler receives the raw JSON string before type-specific dispatch. - * @param handler the handler to invoke with the raw message JSON - */ - public void onMessage(Consumer handler) { - this.onMessageHandler = handler; - } - - /** - * Configures reconnection behavior. Must be called before {@link #connect}. - * - * @param options the reconnection options (backoff, retries, queue size) - */ - public void reconnectOptions(ReconnectingWebSocketListener.ReconnectOptions options) { - this.reconnectOptions = options; - } - - /** - * Closes this WebSocket client, releasing all resources. - * Equivalent to calling {@link #disconnect()}. - */ - @Override - public void close() { - disconnect(); - } - - /** - * Ensures the WebSocket is connected and ready to send messages. - * @throws IllegalStateException if the socket is not connected or not open - */ - private void assertSocketIsOpen() { - if (reconnectingListener.getWebSocket() == null) { - throw new IllegalStateException("WebSocket is not connected. Call connect() first."); - } - if (readyState != WebSocketReadyState.OPEN) { - throw new IllegalStateException("WebSocket is not open. Current state: " + readyState); - } - } - - private CompletableFuture sendMessage(Object body) { - CompletableFuture future = new CompletableFuture<>(); - try { - assertSocketIsOpen(); - String json = objectMapper.writeValueAsString(body); - // Use reconnecting listener's send method which handles queuing - reconnectingListener.send(json); - future.complete(null); - } catch (IllegalStateException e) { - future.completeExceptionally(e); - } catch (Exception e) { - future.completeExceptionally(new RuntimeException("Failed to send message", e)); - } - return future; - } - - private void handleIncomingMessage(String json) { - try { - if (onMessageHandler != null) { - onMessageHandler.accept(json); - } - JsonNode node = objectMapper.readTree(json); - if (node == null || node.isNull()) { - throw new IllegalArgumentException("Received null or invalid JSON message"); - } - if (node.has("request_id") - && node.has("model_name") - && node.has("model_version") - && node.has("model_uuid") - && "Metadata".equals(node.path("type").asText())) { - SpeakV1Metadata metadataHandlerEvent = null; - try { - metadataHandlerEvent = objectMapper.treeToValue(node, SpeakV1Metadata.class); - } catch (Exception e) { - } - if (metadataHandlerEvent != null) { - if (metadataHandler != null) { - metadataHandler.accept(metadataHandlerEvent); - } - return; - } - } - if (node.has("description") - && node.has("code") - && "Warning".equals(node.path("type").asText())) { - SpeakV1Warning warningHandlerEvent = null; - try { - warningHandlerEvent = objectMapper.treeToValue(node, SpeakV1Warning.class); - } catch (Exception e) { - } - if (warningHandlerEvent != null) { - if (warningHandler != null) { - warningHandler.accept(warningHandlerEvent); - } - return; - } - } - if (node.has("type") && node.has("sequence_id")) { - SpeakV1Flushed flushedHandlerEvent = null; - try { - flushedHandlerEvent = objectMapper.treeToValue(node, SpeakV1Flushed.class); - } catch (Exception e) { - } - if (flushedHandlerEvent != null) { - if (flushedHandler != null) { - flushedHandler.accept(flushedHandlerEvent); - } - return; - } - } - if (node.has("type") && node.has("sequence_id")) { - SpeakV1Cleared clearedHandlerEvent = null; - try { - clearedHandlerEvent = objectMapper.treeToValue(node, SpeakV1Cleared.class); - } catch (Exception e) { - } - if (clearedHandlerEvent != null) { - if (clearedHandler != null) { - clearedHandler.accept(clearedHandlerEvent); - } - return; - } - } - if (onErrorHandler != null) { - onErrorHandler.accept(new RuntimeException( - "Unrecognized WebSocket message: " + json.substring(0, Math.min(200, json.length())) - + "... Update your SDK version to support new message types.")); - } - } catch (Exception e) { - if (onErrorHandler != null) { - onErrorHandler.accept(e); - } - } - } -} diff --git a/src/main/java/com/deepgram/resources/speak/v2/types/SpeakV2Close.java b/src/main/java/com/deepgram/resources/speak/v2/types/SpeakV2Close.java index 2e96a5c..7fecd79 100644 --- a/src/main/java/com/deepgram/resources/speak/v2/types/SpeakV2Close.java +++ b/src/main/java/com/deepgram/resources/speak/v2/types/SpeakV2Close.java @@ -36,6 +36,11 @@ public boolean equals(Object other) { return other instanceof SpeakV2Close; } + @java.lang.Override + public int hashCode() { + return getClass().hashCode(); + } + @JsonAnyGetter public Map getAdditionalProperties() { return this.additionalProperties; diff --git a/src/main/java/com/deepgram/resources/speak/v2/types/SpeakV2Close.java.bak b/src/main/java/com/deepgram/resources/speak/v2/types/SpeakV2Close.java.bak deleted file mode 100644 index 7fecd79..0000000 --- a/src/main/java/com/deepgram/resources/speak/v2/types/SpeakV2Close.java.bak +++ /dev/null @@ -1,83 +0,0 @@ -/** - * This file was auto-generated by Fern from our API Definition. - */ -package com.deepgram.resources.speak.v2.types; - -import com.deepgram.core.ObjectMappers; -import com.fasterxml.jackson.annotation.JsonAnyGetter; -import com.fasterxml.jackson.annotation.JsonAnySetter; -import com.fasterxml.jackson.annotation.JsonIgnoreProperties; -import com.fasterxml.jackson.annotation.JsonInclude; -import com.fasterxml.jackson.annotation.JsonProperty; -import com.fasterxml.jackson.databind.annotation.JsonDeserialize; -import java.util.HashMap; -import java.util.Map; - -@JsonInclude(JsonInclude.Include.NON_ABSENT) -@JsonDeserialize(builder = SpeakV2Close.Builder.class) -public final class SpeakV2Close { - private final Map additionalProperties; - - private SpeakV2Close(Map additionalProperties) { - this.additionalProperties = additionalProperties; - } - - /** - * @return Message type identifier - */ - @JsonProperty("type") - public String getType() { - return "Close"; - } - - @java.lang.Override - public boolean equals(Object other) { - if (this == other) return true; - return other instanceof SpeakV2Close; - } - - @java.lang.Override - public int hashCode() { - return getClass().hashCode(); - } - - @JsonAnyGetter - public Map getAdditionalProperties() { - return this.additionalProperties; - } - - @java.lang.Override - public String toString() { - return ObjectMappers.stringify(this); - } - - public static Builder builder() { - return new Builder(); - } - - @JsonIgnoreProperties(ignoreUnknown = true) - public static final class Builder { - @JsonAnySetter - private Map additionalProperties = new HashMap<>(); - - private Builder() {} - - public Builder from(SpeakV2Close other) { - return this; - } - - public SpeakV2Close build() { - return new SpeakV2Close(additionalProperties); - } - - public Builder additionalProperty(String key, Object value) { - this.additionalProperties.put(key, value); - return this; - } - - public Builder additionalProperties(Map additionalProperties) { - this.additionalProperties.putAll(additionalProperties); - return this; - } - } -} diff --git a/src/main/java/com/deepgram/resources/speak/v2/types/SpeakV2Flush.java b/src/main/java/com/deepgram/resources/speak/v2/types/SpeakV2Flush.java index acd717d..130da0d 100644 --- a/src/main/java/com/deepgram/resources/speak/v2/types/SpeakV2Flush.java +++ b/src/main/java/com/deepgram/resources/speak/v2/types/SpeakV2Flush.java @@ -36,6 +36,11 @@ public boolean equals(Object other) { return other instanceof SpeakV2Flush; } + @java.lang.Override + public int hashCode() { + return getClass().hashCode(); + } + @JsonAnyGetter public Map getAdditionalProperties() { return this.additionalProperties; diff --git a/src/main/java/com/deepgram/resources/speak/v2/types/SpeakV2Flush.java.bak b/src/main/java/com/deepgram/resources/speak/v2/types/SpeakV2Flush.java.bak deleted file mode 100644 index 130da0d..0000000 --- a/src/main/java/com/deepgram/resources/speak/v2/types/SpeakV2Flush.java.bak +++ /dev/null @@ -1,83 +0,0 @@ -/** - * This file was auto-generated by Fern from our API Definition. - */ -package com.deepgram.resources.speak.v2.types; - -import com.deepgram.core.ObjectMappers; -import com.fasterxml.jackson.annotation.JsonAnyGetter; -import com.fasterxml.jackson.annotation.JsonAnySetter; -import com.fasterxml.jackson.annotation.JsonIgnoreProperties; -import com.fasterxml.jackson.annotation.JsonInclude; -import com.fasterxml.jackson.annotation.JsonProperty; -import com.fasterxml.jackson.databind.annotation.JsonDeserialize; -import java.util.HashMap; -import java.util.Map; - -@JsonInclude(JsonInclude.Include.NON_ABSENT) -@JsonDeserialize(builder = SpeakV2Flush.Builder.class) -public final class SpeakV2Flush { - private final Map additionalProperties; - - private SpeakV2Flush(Map additionalProperties) { - this.additionalProperties = additionalProperties; - } - - /** - * @return Message type identifier - */ - @JsonProperty("type") - public String getType() { - return "Flush"; - } - - @java.lang.Override - public boolean equals(Object other) { - if (this == other) return true; - return other instanceof SpeakV2Flush; - } - - @java.lang.Override - public int hashCode() { - return getClass().hashCode(); - } - - @JsonAnyGetter - public Map getAdditionalProperties() { - return this.additionalProperties; - } - - @java.lang.Override - public String toString() { - return ObjectMappers.stringify(this); - } - - public static Builder builder() { - return new Builder(); - } - - @JsonIgnoreProperties(ignoreUnknown = true) - public static final class Builder { - @JsonAnySetter - private Map additionalProperties = new HashMap<>(); - - private Builder() {} - - public Builder from(SpeakV2Flush other) { - return this; - } - - public SpeakV2Flush build() { - return new SpeakV2Flush(additionalProperties); - } - - public Builder additionalProperty(String key, Object value) { - this.additionalProperties.put(key, value); - return this; - } - - public Builder additionalProperties(Map additionalProperties) { - this.additionalProperties.putAll(additionalProperties); - return this; - } - } -} diff --git a/src/main/java/com/deepgram/resources/speak/v2/websocket/V2WebSocketClient.java b/src/main/java/com/deepgram/resources/speak/v2/websocket/V2WebSocketClient.java index 07f6c36..b4e01f7 100644 --- a/src/main/java/com/deepgram/resources/speak/v2/websocket/V2WebSocketClient.java +++ b/src/main/java/com/deepgram/resources/speak/v2/websocket/V2WebSocketClient.java @@ -6,6 +6,7 @@ import com.deepgram.core.ClientOptions; import com.deepgram.core.DisconnectReason; import com.deepgram.core.ObjectMappers; +import com.deepgram.core.QueryStringMapper; import com.deepgram.core.ReconnectingWebSocketListener; import com.deepgram.core.RequestOptions; import com.deepgram.core.WebSocketReadyState; @@ -143,7 +144,14 @@ public CompletableFuture connect(V2ConnectOptions options) { "mip_opt_out", String.valueOf(options.getMipOptOut().get())); } if (options.getTag() != null && options.getTag().isPresent()) { - urlBuilder.addQueryParameter("tag", String.valueOf(options.getTag().get())); + QueryStringMapper.addQueryParameter(urlBuilder, "tag", options.getTag().get().get(), true); + } + if (options.getAdditionalProperties() != null) { + options.getAdditionalProperties().forEach((key, value) -> { + if (value != null) { + QueryStringMapper.addQueryParameter(urlBuilder, key, value, true); + } + }); } Request.Builder requestBuilder = new Request.Builder().url(urlBuilder.build()); clientOptions.headers((RequestOptions) null).forEach(requestBuilder::addHeader); @@ -611,11 +619,8 @@ private void handleIncomingMessage(String json) { return; } } - if (onErrorHandler != null) { - onErrorHandler.accept(new RuntimeException( - "Unrecognized WebSocket message: " + json.substring(0, Math.min(200, json.length())) - + "... Update your SDK version to support new message types.")); - } + // The raw frame is already delivered to onMessage(String), so ignore unknown typed + // frames for forward compatibility with newly added server control messages. } catch (Exception e) { if (onErrorHandler != null) { onErrorHandler.accept(e); diff --git a/src/main/java/com/deepgram/resources/speak/v2/websocket/V2WebSocketClient.java.bak b/src/main/java/com/deepgram/resources/speak/v2/websocket/V2WebSocketClient.java.bak deleted file mode 100644 index b4e01f7..0000000 --- a/src/main/java/com/deepgram/resources/speak/v2/websocket/V2WebSocketClient.java.bak +++ /dev/null @@ -1,630 +0,0 @@ -/** - * This file was auto-generated by Fern from our API Definition. - */ -package com.deepgram.resources.speak.v2.websocket; - -import com.deepgram.core.ClientOptions; -import com.deepgram.core.DisconnectReason; -import com.deepgram.core.ObjectMappers; -import com.deepgram.core.QueryStringMapper; -import com.deepgram.core.ReconnectingWebSocketListener; -import com.deepgram.core.RequestOptions; -import com.deepgram.core.WebSocketReadyState; -import com.deepgram.resources.speak.v2.types.SpeakV2Close; -import com.deepgram.resources.speak.v2.types.SpeakV2Configure; -import com.deepgram.resources.speak.v2.types.SpeakV2ConfigureFailure; -import com.deepgram.resources.speak.v2.types.SpeakV2ConfigureSuccess; -import com.deepgram.resources.speak.v2.types.SpeakV2Connected; -import com.deepgram.resources.speak.v2.types.SpeakV2Error; -import com.deepgram.resources.speak.v2.types.SpeakV2Flush; -import com.deepgram.resources.speak.v2.types.SpeakV2Flushed; -import com.deepgram.resources.speak.v2.types.SpeakV2Interrupt; -import com.deepgram.resources.speak.v2.types.SpeakV2SessionMetadata; -import com.deepgram.resources.speak.v2.types.SpeakV2Speak; -import com.deepgram.resources.speak.v2.types.SpeakV2SpeechInterrupted; -import com.deepgram.resources.speak.v2.types.SpeakV2SpeechMetadata; -import com.deepgram.resources.speak.v2.types.SpeakV2SpeechStarted; -import com.deepgram.resources.speak.v2.types.SpeakV2Warning; -import com.fasterxml.jackson.databind.JsonNode; -import com.fasterxml.jackson.databind.ObjectMapper; -import java.util.concurrent.CompletableFuture; -import java.util.concurrent.ScheduledExecutorService; -import java.util.function.Consumer; -import okhttp3.HttpUrl; -import okhttp3.OkHttpClient; -import okhttp3.Request; -import okhttp3.Response; -import okhttp3.WebSocket; -import okio.ByteString; - -/** - * WebSocket client for the v2 channel. - * Provides real-time bidirectional communication with strongly-typed messages. - */ -public class V2WebSocketClient implements AutoCloseable { - protected final ClientOptions clientOptions; - - private final ObjectMapper objectMapper; - - private final OkHttpClient okHttpClient; - - private ScheduledExecutorService timeoutExecutor; - - private volatile WebSocketReadyState readyState = WebSocketReadyState.CLOSED; - - private volatile Runnable onConnectedHandler; - - private volatile Consumer onDisconnectedHandler; - - private volatile Consumer onErrorHandler; - - private volatile Consumer onMessageHandler; - - private volatile ReconnectingWebSocketListener.ReconnectOptions reconnectOptions; - - private CompletableFuture connectionFuture; - - private ReconnectingWebSocketListener reconnectingListener; - - private volatile Consumer speakV2AudioHandler; - - private volatile Consumer connectedHandler; - - private volatile Consumer speechStartedHandler; - - private volatile Consumer speechMetadataHandler; - - private volatile Consumer speechInterruptedHandler; - - private volatile Consumer flushedHandler; - - private volatile Consumer sessionMetadataHandler; - - private volatile Consumer configureSuccessHandler; - - private volatile Consumer configureFailureHandler; - - private volatile Consumer warningHandler; - - private volatile Consumer errorHandler; - - /** - * Creates a new async WebSocket client for the v2 channel. - */ - public V2WebSocketClient(ClientOptions clientOptions) { - this.clientOptions = clientOptions; - this.objectMapper = ObjectMappers.JSON_MAPPER; - this.okHttpClient = clientOptions.httpClient(); - } - - /** - * Establishes the WebSocket connection asynchronously with automatic reconnection. - * @return a CompletableFuture that completes when the connection is established - * @param options connection options including query parameters - */ - public CompletableFuture connect(V2ConnectOptions options) { - connectionFuture = new CompletableFuture<>(); - String baseUrl = clientOptions.environment().getProductionURL(); - String fullPath = "/v2/speak"; - if (baseUrl.endsWith("/") && fullPath.startsWith("/")) { - fullPath = fullPath.substring(1); - } else if (!baseUrl.endsWith("/") && !fullPath.startsWith("/")) { - fullPath = "/" + fullPath; - } - // OkHttp's HttpUrl only supports http/https schemes; convert wss/ws for URL parsing - if (baseUrl.startsWith("wss://")) { - baseUrl = "https://" + baseUrl.substring(6); - } else if (baseUrl.startsWith("ws://")) { - baseUrl = "http://" + baseUrl.substring(5); - } - HttpUrl parsedUrl = HttpUrl.parse(baseUrl + fullPath); - if (parsedUrl == null) { - throw new IllegalArgumentException("Invalid WebSocket URL: " + baseUrl + fullPath); - } - HttpUrl.Builder urlBuilder = parsedUrl.newBuilder(); - urlBuilder.addQueryParameter("model", String.valueOf(options.getModel())); - if (options.getEncoding() != null && options.getEncoding().isPresent()) { - urlBuilder.addQueryParameter( - "encoding", String.valueOf(options.getEncoding().get())); - } - if (options.getSampleRate() != null && options.getSampleRate().isPresent()) { - urlBuilder.addQueryParameter( - "sample_rate", String.valueOf(options.getSampleRate().get())); - } - if (options.getSpeed() != null && options.getSpeed().isPresent()) { - urlBuilder.addQueryParameter( - "speed", String.valueOf(options.getSpeed().get())); - } - if (options.getExpressivity() != null && options.getExpressivity().isPresent()) { - urlBuilder.addQueryParameter( - "expressivity", String.valueOf(options.getExpressivity().get())); - } - if (options.getMipOptOut() != null && options.getMipOptOut().isPresent()) { - urlBuilder.addQueryParameter( - "mip_opt_out", String.valueOf(options.getMipOptOut().get())); - } - if (options.getTag() != null && options.getTag().isPresent()) { - QueryStringMapper.addQueryParameter(urlBuilder, "tag", options.getTag().get().get(), true); - } - if (options.getAdditionalProperties() != null) { - options.getAdditionalProperties().forEach((key, value) -> { - if (value != null) { - QueryStringMapper.addQueryParameter(urlBuilder, key, value, true); - } - }); - } - Request.Builder requestBuilder = new Request.Builder().url(urlBuilder.build()); - clientOptions.headers((RequestOptions) null).forEach(requestBuilder::addHeader); - final Request request = requestBuilder.build(); - this.readyState = WebSocketReadyState.CONNECTING; - ReconnectingWebSocketListener.ReconnectOptions reconnectOpts = this.reconnectOptions != null - ? this.reconnectOptions - : ReconnectingWebSocketListener.ReconnectOptions.builder().build(); - this.reconnectingListener = - new ReconnectingWebSocketListener( - reconnectOpts, - () -> { - if (clientOptions.isClosed()) { - throw new IllegalStateException("root client has been closed"); - } - if (clientOptions.webSocketFactory().isPresent()) { - return clientOptions - .webSocketFactory() - .get() - .create(request, this.reconnectingListener); - } else { - return okHttpClient.newWebSocket(request, this.reconnectingListener); - } - }, - clientOptions::isClosed) { - @Override - protected void onWebSocketOpen(WebSocket webSocket, Response response) { - readyState = WebSocketReadyState.OPEN; - if (onConnectedHandler != null) { - onConnectedHandler.run(); - } - connectionFuture.complete(null); - } - - @Override - protected void onWebSocketMessage(WebSocket webSocket, String text) { - handleIncomingMessage(text); - } - - @Override - protected void onWebSocketBinaryMessage(WebSocket webSocket, ByteString bytes) { - if (speakV2AudioHandler != null) { - speakV2AudioHandler.accept(bytes); - } - } - - @Override - protected void onWebSocketFailure(WebSocket webSocket, Throwable t, Response response) { - readyState = WebSocketReadyState.CLOSED; - if (onErrorHandler != null) { - onErrorHandler.accept(new RuntimeException(t)); - } - connectionFuture.completeExceptionally(t); - } - - @Override - protected void onWebSocketClosed(WebSocket webSocket, int code, String reason) { - readyState = WebSocketReadyState.CLOSED; - if (onDisconnectedHandler != null) { - onDisconnectedHandler.accept(new DisconnectReason(code, reason)); - } - } - }; - clientOptions.registerWebSocket(this); - reconnectingListener.connect(); - return connectionFuture; - } - - /** - * Disconnects the WebSocket connection and releases resources. - */ - public void disconnect() { - clientOptions.unregisterWebSocket(this); - if (reconnectingListener != null) { - reconnectingListener.disconnect(); - } - if (timeoutExecutor != null) { - timeoutExecutor.shutdownNow(); - timeoutExecutor = null; - } - } - - /** - * Gets the current state of the WebSocket connection. - * - * This provides the actual connection state, similar to the W3C WebSocket API. - * - * @return the current WebSocket ready state - */ - public WebSocketReadyState getReadyState() { - return readyState; - } - - /** - * Sends a SpeakV2Speak message to the server asynchronously. - * @param message the message to send - * @return a CompletableFuture that completes when the message is sent - */ - public CompletableFuture sendSpeak(SpeakV2Speak message) { - return sendMessage(message); - } - - /** - * Sends a SpeakV2Flush message to the server asynchronously. - * @param message the message to send - * @return a CompletableFuture that completes when the message is sent - */ - public CompletableFuture sendFlush(SpeakV2Flush message) { - return sendMessage(message); - } - - /** - * Sends a SpeakV2Interrupt message to the server asynchronously. - * @param message the message to send - * @return a CompletableFuture that completes when the message is sent - */ - public CompletableFuture sendInterrupt(SpeakV2Interrupt message) { - return sendMessage(message); - } - - /** - * Sends a SpeakV2Configure message to the server asynchronously. - * @param message the message to send - * @return a CompletableFuture that completes when the message is sent - */ - public CompletableFuture sendConfigure(SpeakV2Configure message) { - return sendMessage(message); - } - - /** - * Sends a SpeakV2Close message to the server asynchronously. - * @param message the message to send - * @return a CompletableFuture that completes when the message is sent - */ - public CompletableFuture sendClose(SpeakV2Close message) { - return sendMessage(message); - } - - /** - * Registers a handler for SpeakV2Audio messages from the server. - * @param handler the handler to invoke when a message is received - */ - public void onSpeakV2Audio(Consumer handler) { - this.speakV2AudioHandler = handler; - } - - /** - * Registers a handler for SpeakV2Connected messages from the server. - * @param handler the handler to invoke when a message is received - */ - public void onConnected(Consumer handler) { - this.connectedHandler = handler; - } - - /** - * Registers a handler for SpeakV2SpeechStarted messages from the server. - * @param handler the handler to invoke when a message is received - */ - public void onSpeechStarted(Consumer handler) { - this.speechStartedHandler = handler; - } - - /** - * Registers a handler for SpeakV2SpeechMetadata messages from the server. - * @param handler the handler to invoke when a message is received - */ - public void onSpeechMetadata(Consumer handler) { - this.speechMetadataHandler = handler; - } - - /** - * Registers a handler for SpeakV2SpeechInterrupted messages from the server. - * @param handler the handler to invoke when a message is received - */ - public void onSpeechInterrupted(Consumer handler) { - this.speechInterruptedHandler = handler; - } - - /** - * Registers a handler for SpeakV2Flushed messages from the server. - * @param handler the handler to invoke when a message is received - */ - public void onFlushed(Consumer handler) { - this.flushedHandler = handler; - } - - /** - * Registers a handler for SpeakV2SessionMetadata messages from the server. - * @param handler the handler to invoke when a message is received - */ - public void onSessionMetadata(Consumer handler) { - this.sessionMetadataHandler = handler; - } - - /** - * Registers a handler for SpeakV2ConfigureSuccess messages from the server. - * @param handler the handler to invoke when a message is received - */ - public void onConfigureSuccess(Consumer handler) { - this.configureSuccessHandler = handler; - } - - /** - * Registers a handler for SpeakV2ConfigureFailure messages from the server. - * @param handler the handler to invoke when a message is received - */ - public void onConfigureFailure(Consumer handler) { - this.configureFailureHandler = handler; - } - - /** - * Registers a handler for SpeakV2Warning messages from the server. - * @param handler the handler to invoke when a message is received - */ - public void onWarning(Consumer handler) { - this.warningHandler = handler; - } - - /** - * Registers a handler for SpeakV2Error messages from the server. - * @param handler the handler to invoke when a message is received - */ - public void onErrorMessage(Consumer handler) { - this.errorHandler = handler; - } - - /** - * Registers a handler called when the connection is established. - * @param handler the handler to invoke when connected - */ - public void onConnected(Runnable handler) { - this.onConnectedHandler = handler; - } - - /** - * Registers a handler called when the connection is closed. - * @param handler the handler to invoke when disconnected - */ - public void onDisconnected(Consumer handler) { - this.onDisconnectedHandler = handler; - } - - /** - * Registers a handler called when an error occurs. - * @param handler the handler to invoke on error - */ - public void onError(Consumer handler) { - this.onErrorHandler = handler; - } - - /** - * Registers a handler called for every incoming text message. - * The handler receives the raw JSON string before type-specific dispatch. - * @param handler the handler to invoke with the raw message JSON - */ - public void onMessage(Consumer handler) { - this.onMessageHandler = handler; - } - - /** - * Configures reconnection behavior. Must be called before {@link #connect}. - * - * @param options the reconnection options (backoff, retries, queue size) - */ - public void reconnectOptions(ReconnectingWebSocketListener.ReconnectOptions options) { - this.reconnectOptions = options; - } - - /** - * Closes this WebSocket client, releasing all resources. - * Equivalent to calling {@link #disconnect()}. - */ - @Override - public void close() { - disconnect(); - } - - /** - * Ensures the WebSocket is connected and ready to send messages. - * @throws IllegalStateException if the socket is not connected or not open - */ - private void assertSocketIsOpen() { - if (reconnectingListener.getWebSocket() == null) { - throw new IllegalStateException("WebSocket is not connected. Call connect() first."); - } - if (readyState != WebSocketReadyState.OPEN) { - throw new IllegalStateException("WebSocket is not open. Current state: " + readyState); - } - } - - private CompletableFuture sendMessage(Object body) { - CompletableFuture future = new CompletableFuture<>(); - try { - assertSocketIsOpen(); - String json = objectMapper.writeValueAsString(body); - // Use reconnecting listener's send method which handles queuing - reconnectingListener.send(json); - future.complete(null); - } catch (IllegalStateException e) { - future.completeExceptionally(e); - } catch (Exception e) { - future.completeExceptionally(new RuntimeException("Failed to send message", e)); - } - return future; - } - - private void handleIncomingMessage(String json) { - try { - if (onMessageHandler != null) { - onMessageHandler.accept(json); - } - JsonNode node = objectMapper.readTree(json); - if (node == null || node.isNull()) { - throw new IllegalArgumentException("Received null or invalid JSON message"); - } - if (node.has("speech_id") - && node.has("audio_duration_ms") - && node.has("input_character_count") - && node.has("billable_character_count") - && node.has("controls_applied") - && "SpeechMetadata".equals(node.path("type").asText())) { - SpeakV2SpeechMetadata speechMetadataHandlerEvent = null; - try { - speechMetadataHandlerEvent = objectMapper.treeToValue(node, SpeakV2SpeechMetadata.class); - } catch (Exception e) { - } - if (speechMetadataHandlerEvent != null) { - if (speechMetadataHandler != null) { - speechMetadataHandler.accept(speechMetadataHandlerEvent); - } - return; - } - } - if (node.has("request_id") - && node.has("model_name") - && node.has("model_version") - && node.has("model_uuids") - && "Connected".equals(node.path("type").asText())) { - SpeakV2Connected connectedHandlerEvent = null; - try { - connectedHandlerEvent = objectMapper.treeToValue(node, SpeakV2Connected.class); - } catch (Exception e) { - } - if (connectedHandlerEvent != null) { - if (connectedHandler != null) { - connectedHandler.accept(connectedHandlerEvent); - } - return; - } - } - if (node.has("total_audio_duration_ms") - && node.has("total_input_character_count") - && node.has("total_billable_character_count") - && "SessionMetadata".equals(node.path("type").asText())) { - SpeakV2SessionMetadata sessionMetadataHandlerEvent = null; - try { - sessionMetadataHandlerEvent = objectMapper.treeToValue(node, SpeakV2SessionMetadata.class); - } catch (Exception e) { - } - if (sessionMetadataHandlerEvent != null) { - if (sessionMetadataHandler != null) { - sessionMetadataHandler.accept(sessionMetadataHandlerEvent); - } - return; - } - } - if (node.has("audio_played_ms") - && node.has("metadata") - && "SpeechInterrupted".equals(node.path("type").asText())) { - SpeakV2SpeechInterrupted speechInterruptedHandlerEvent = null; - try { - speechInterruptedHandlerEvent = objectMapper.treeToValue(node, SpeakV2SpeechInterrupted.class); - } catch (Exception e) { - } - if (speechInterruptedHandlerEvent != null) { - if (speechInterruptedHandler != null) { - speechInterruptedHandler.accept(speechInterruptedHandlerEvent); - } - return; - } - } - if (node.has("code") - && node.has("description") - && "ConfigureFailure".equals(node.path("type").asText())) { - SpeakV2ConfigureFailure configureFailureHandlerEvent = null; - try { - configureFailureHandlerEvent = objectMapper.treeToValue(node, SpeakV2ConfigureFailure.class); - } catch (Exception e) { - } - if (configureFailureHandlerEvent != null) { - if (configureFailureHandler != null) { - configureFailureHandler.accept(configureFailureHandlerEvent); - } - return; - } - } - if (node.has("code") - && node.has("description") - && "Warning".equals(node.path("type").asText())) { - SpeakV2Warning warningHandlerEvent = null; - try { - warningHandlerEvent = objectMapper.treeToValue(node, SpeakV2Warning.class); - } catch (Exception e) { - } - if (warningHandlerEvent != null) { - if (warningHandler != null) { - warningHandler.accept(warningHandlerEvent); - } - return; - } - } - if (node.has("code") - && node.has("description") - && "Error".equals(node.path("type").asText())) { - SpeakV2Error errorHandlerEvent = null; - try { - errorHandlerEvent = objectMapper.treeToValue(node, SpeakV2Error.class); - } catch (Exception e) { - } - if (errorHandlerEvent != null) { - if (errorHandler != null) { - errorHandler.accept(errorHandlerEvent); - } - return; - } - } - if (node.has("speech_id") - && "SpeechStarted".equals(node.path("type").asText())) { - SpeakV2SpeechStarted speechStartedHandlerEvent = null; - try { - speechStartedHandlerEvent = objectMapper.treeToValue(node, SpeakV2SpeechStarted.class); - } catch (Exception e) { - } - if (speechStartedHandlerEvent != null) { - if (speechStartedHandler != null) { - speechStartedHandler.accept(speechStartedHandlerEvent); - } - return; - } - } - if (node.has("speech_id") && "Flushed".equals(node.path("type").asText())) { - SpeakV2Flushed flushedHandlerEvent = null; - try { - flushedHandlerEvent = objectMapper.treeToValue(node, SpeakV2Flushed.class); - } catch (Exception e) { - } - if (flushedHandlerEvent != null) { - if (flushedHandler != null) { - flushedHandler.accept(flushedHandlerEvent); - } - return; - } - } - if (node.has("applied") - && "ConfigureSuccess".equals(node.path("type").asText())) { - SpeakV2ConfigureSuccess configureSuccessHandlerEvent = null; - try { - configureSuccessHandlerEvent = objectMapper.treeToValue(node, SpeakV2ConfigureSuccess.class); - } catch (Exception e) { - } - if (configureSuccessHandlerEvent != null) { - if (configureSuccessHandler != null) { - configureSuccessHandler.accept(configureSuccessHandlerEvent); - } - return; - } - } - // The raw frame is already delivered to onMessage(String), so ignore unknown typed - // frames for forward compatibility with newly added server control messages. - } catch (Exception e) { - if (onErrorHandler != null) { - onErrorHandler.accept(e); - } - } - } -} diff --git a/src/main/java/com/deepgram/types/DeepgramModel.java b/src/main/java/com/deepgram/types/DeepgramModel.java index fc7c08c..6444d7f 100644 --- a/src/main/java/com/deepgram/types/DeepgramModel.java +++ b/src/main/java/com/deepgram/types/DeepgramModel.java @@ -13,6 +13,8 @@ public final class DeepgramModel { public static final DeepgramModel FLUX_KELSEY_EN = new DeepgramModel(Value.FLUX_KELSEY_EN, "flux-kelsey-en"); + 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"); public static final DeepgramModel AURA2HARMONIA_EN = @@ -251,6 +253,8 @@ public T visit(Visitor visitor) { return visitor.visitAura2HeraEn(); case FLUX_KELSEY_EN: return visitor.visitFluxKelseyEn(); + case FLUX_RENEE_EN: + return visitor.visitFluxReneeEn(); case FLUX_SIENNA_EN: return visitor.visitFluxSiennaEn(); case AURA2HARMONIA_EN: @@ -458,6 +462,8 @@ public static DeepgramModel valueOf(String value) { return AURA2HERA_EN; case "flux-kelsey-en": return FLUX_KELSEY_EN; + case "flux-renee-en": + return FLUX_RENEE_EN; case "flux-sienna-en": return FLUX_SIENNA_EN; case "aura-2-harmonia-en": @@ -840,6 +846,8 @@ public enum Value { FLUX_PRIYA_EN, + FLUX_RENEE_EN, + FLUX_RUFUS_EN, FLUX_SEAN_EN, @@ -1042,6 +1050,8 @@ public interface Visitor { T visitFluxPriyaEn(); + T visitFluxReneeEn(); + T visitFluxRufusEn(); T visitFluxSeanEn(); diff --git a/src/main/java/com/deepgram/types/DeepgramModel.java.bak b/src/main/java/com/deepgram/types/DeepgramModel.java.bak deleted file mode 100644 index 6444d7f..0000000 --- a/src/main/java/com/deepgram/types/DeepgramModel.java.bak +++ /dev/null @@ -1,1071 +0,0 @@ -/** - * This file was auto-generated by Fern from our API Definition. - */ -package com.deepgram.types; - -import com.fasterxml.jackson.annotation.JsonCreator; -import com.fasterxml.jackson.annotation.JsonValue; - -public final class DeepgramModel { - public static final DeepgramModel AURA2SIRIO_ES = new DeepgramModel(Value.AURA2SIRIO_ES, "aura-2-sirio-es"); - - public static final DeepgramModel AURA2HERA_EN = new DeepgramModel(Value.AURA2HERA_EN, "aura-2-hera-en"); - - public static final DeepgramModel FLUX_KELSEY_EN = new DeepgramModel(Value.FLUX_KELSEY_EN, "flux-kelsey-en"); - - 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"); - - public static final DeepgramModel AURA2HARMONIA_EN = - new DeepgramModel(Value.AURA2HARMONIA_EN, "aura-2-harmonia-en"); - - public static final DeepgramModel AURA2CARINA_ES = new DeepgramModel(Value.AURA2CARINA_ES, "aura-2-carina-es"); - - public static final DeepgramModel FLUX_MEENA_EN = new DeepgramModel(Value.FLUX_MEENA_EN, "flux-meena-en"); - - public static final DeepgramModel FLUX_MEGHAN_EN = new DeepgramModel(Value.FLUX_MEGHAN_EN, "flux-meghan-en"); - - public static final DeepgramModel AURA_ZEUS_EN = new DeepgramModel(Value.AURA_ZEUS_EN, "aura-zeus-en"); - - public static final DeepgramModel AURA2HERMES_EN = new DeepgramModel(Value.AURA2HERMES_EN, "aura-2-hermes-en"); - - public static final DeepgramModel FLUX_CONOR_EN = new DeepgramModel(Value.FLUX_CONOR_EN, "flux-conor-en"); - - public static final DeepgramModel AURA2NEPTUNE_EN = new DeepgramModel(Value.AURA2NEPTUNE_EN, "aura-2-neptune-en"); - - public static final DeepgramModel FLUX_BROOKE_EN = new DeepgramModel(Value.FLUX_BROOKE_EN, "flux-brooke-en"); - - public static final DeepgramModel FLUX_RUFUS_EN = new DeepgramModel(Value.FLUX_RUFUS_EN, "flux-rufus-en"); - - public static final DeepgramModel FLUX_WES_EN = new DeepgramModel(Value.FLUX_WES_EN, "flux-wes-en"); - - public static final DeepgramModel AURA2CALLISTA_EN = - new DeepgramModel(Value.AURA2CALLISTA_EN, "aura-2-callista-en"); - - public static final DeepgramModel AURA2AURORA_EN = new DeepgramModel(Value.AURA2AURORA_EN, "aura-2-aurora-en"); - - public static final DeepgramModel AURA2OPHELIA_EN = new DeepgramModel(Value.AURA2OPHELIA_EN, "aura-2-ophelia-en"); - - public static final DeepgramModel FLUX_BRUCE_EN = new DeepgramModel(Value.FLUX_BRUCE_EN, "flux-bruce-en"); - - public static final DeepgramModel AURA2APOLLO_EN = new DeepgramModel(Value.AURA2APOLLO_EN, "aura-2-apollo-en"); - - public static final DeepgramModel AURA2DELIA_EN = new DeepgramModel(Value.AURA2DELIA_EN, "aura-2-delia-en"); - - public static final DeepgramModel AURA2MARS_EN = new DeepgramModel(Value.AURA2MARS_EN, "aura-2-mars-en"); - - public static final DeepgramModel AURA_HERA_EN = new DeepgramModel(Value.AURA_HERA_EN, "aura-hera-en"); - - public static final DeepgramModel FLUX_MAEVE_EN = new DeepgramModel(Value.FLUX_MAEVE_EN, "flux-maeve-en"); - - public static final DeepgramModel FLUX_MARCUS_EN = new DeepgramModel(Value.FLUX_MARCUS_EN, "flux-marcus-en"); - - public static final DeepgramModel AURA2ARIES_EN = new DeepgramModel(Value.AURA2ARIES_EN, "aura-2-aries-en"); - - public static final DeepgramModel FLUX_PAIGE_EN = new DeepgramModel(Value.FLUX_PAIGE_EN, "flux-paige-en"); - - public static final DeepgramModel AURA2JUNO_EN = new DeepgramModel(Value.AURA2JUNO_EN, "aura-2-juno-en"); - - public static final DeepgramModel FLUX_DREW_EN = new DeepgramModel(Value.FLUX_DREW_EN, "flux-drew-en"); - - public static final DeepgramModel AURA2JAVIER_ES = new DeepgramModel(Value.AURA2JAVIER_ES, "aura-2-javier-es"); - - public static final DeepgramModel AURA2ANDROMEDA_EN = - new DeepgramModel(Value.AURA2ANDROMEDA_EN, "aura-2-andromeda-en"); - - public static final DeepgramModel AURA2SATURN_EN = new DeepgramModel(Value.AURA2SATURN_EN, "aura-2-saturn-en"); - - public static final DeepgramModel AURA2IRIS_EN = new DeepgramModel(Value.AURA2IRIS_EN, "aura-2-iris-en"); - - public static final DeepgramModel AURA_ANGUS_EN = new DeepgramModel(Value.AURA_ANGUS_EN, "aura-angus-en"); - - public static final DeepgramModel FLUX_KAI_EN = new DeepgramModel(Value.FLUX_KAI_EN, "flux-kai-en"); - - public static final DeepgramModel FLUX_BRITTANY_EN = new DeepgramModel(Value.FLUX_BRITTANY_EN, "flux-brittany-en"); - - public static final DeepgramModel FLUX_SEAN_EN = new DeepgramModel(Value.FLUX_SEAN_EN, "flux-sean-en"); - - public static final DeepgramModel AURA2HELENA_EN = new DeepgramModel(Value.AURA2HELENA_EN, "aura-2-helena-en"); - - public static final DeepgramModel FLUX_HALEY_EN = new DeepgramModel(Value.FLUX_HALEY_EN, "flux-haley-en"); - - public static final DeepgramModel AURA_STELLA_EN = new DeepgramModel(Value.AURA_STELLA_EN, "aura-stella-en"); - - public static final DeepgramModel FLUX_JACK_EN = new DeepgramModel(Value.FLUX_JACK_EN, "flux-jack-en"); - - public static final DeepgramModel AURA2DRACO_EN = new DeepgramModel(Value.AURA2DRACO_EN, "aura-2-draco-en"); - - public static final DeepgramModel FLUX_PRIYA_EN = new DeepgramModel(Value.FLUX_PRIYA_EN, "flux-priya-en"); - - public static final DeepgramModel AURA_HELIOS_EN = new DeepgramModel(Value.AURA_HELIOS_EN, "aura-helios-en"); - - public static final DeepgramModel AURA2JANUS_EN = new DeepgramModel(Value.AURA2JANUS_EN, "aura-2-janus-en"); - - public static final DeepgramModel AURA2NESTOR_ES = new DeepgramModel(Value.AURA2NESTOR_ES, "aura-2-nestor-es"); - - public static final DeepgramModel AURA2ORION_EN = new DeepgramModel(Value.AURA2ORION_EN, "aura-2-orion-en"); - - public static final DeepgramModel AURA2MINERVA_EN = new DeepgramModel(Value.AURA2MINERVA_EN, "aura-2-minerva-en"); - - public static final DeepgramModel AURA2ALVARO_ES = new DeepgramModel(Value.AURA2ALVARO_ES, "aura-2-alvaro-es"); - - public static final DeepgramModel AURA_PERSEUS_EN = new DeepgramModel(Value.AURA_PERSEUS_EN, "aura-perseus-en"); - - public static final DeepgramModel AURA2VESTA_EN = new DeepgramModel(Value.AURA2VESTA_EN, "aura-2-vesta-en"); - - public static final DeepgramModel FLUX_COLE_EN = new DeepgramModel(Value.FLUX_COLE_EN, "flux-cole-en"); - - public static final DeepgramModel AURA2ZEUS_EN = new DeepgramModel(Value.AURA2ZEUS_EN, "aura-2-zeus-en"); - - public static final DeepgramModel AURA2ORPHEUS_EN = new DeepgramModel(Value.AURA2ORPHEUS_EN, "aura-2-orpheus-en"); - - public static final DeepgramModel AURA2THALIA_EN = new DeepgramModel(Value.AURA2THALIA_EN, "aura-2-thalia-en"); - - public static final DeepgramModel FLUX_NAVEEN_EN = new DeepgramModel(Value.FLUX_NAVEEN_EN, "flux-naveen-en"); - - public static final DeepgramModel AURA2ASTERIA_EN = new DeepgramModel(Value.AURA2ASTERIA_EN, "aura-2-asteria-en"); - - public static final DeepgramModel AURA2SELENA_ES = new DeepgramModel(Value.AURA2SELENA_ES, "aura-2-selena-es"); - - public static final DeepgramModel FLUX_MARCELO_EN = new DeepgramModel(Value.FLUX_MARCELO_EN, "flux-marcelo-en"); - - public static final DeepgramModel FLUX_ELISE_EN = new DeepgramModel(Value.FLUX_ELISE_EN, "flux-elise-en"); - - public static final DeepgramModel FLUX_ALEXIS_EN = new DeepgramModel(Value.FLUX_ALEXIS_EN, "flux-alexis-en"); - - public static final DeepgramModel AURA_LUNA_EN = new DeepgramModel(Value.AURA_LUNA_EN, "aura-luna-en"); - - public static final DeepgramModel AURA_ORPHEUS_EN = new DeepgramModel(Value.AURA_ORPHEUS_EN, "aura-orpheus-en"); - - public static final DeepgramModel AURA2AMALTHEA_EN = - new DeepgramModel(Value.AURA2AMALTHEA_EN, "aura-2-amalthea-en"); - - public static final DeepgramModel FLUX_DONOVAN_EN = new DeepgramModel(Value.FLUX_DONOVAN_EN, "flux-donovan-en"); - - public static final DeepgramModel AURA2SELENE_EN = new DeepgramModel(Value.AURA2SELENE_EN, "aura-2-selene-en"); - - public static final DeepgramModel FLUX_TANNER_EN = new DeepgramModel(Value.FLUX_TANNER_EN, "flux-tanner-en"); - - public static final DeepgramModel FLUX_SHARON_EN = new DeepgramModel(Value.FLUX_SHARON_EN, "flux-sharon-en"); - - public static final DeepgramModel AURA2LUNA_EN = new DeepgramModel(Value.AURA2LUNA_EN, "aura-2-luna-en"); - - public static final DeepgramModel AURA2PHOEBE_EN = new DeepgramModel(Value.AURA2PHOEBE_EN, "aura-2-phoebe-en"); - - public static final DeepgramModel FLUX_HANNAH_EN = new DeepgramModel(Value.FLUX_HANNAH_EN, "flux-hannah-en"); - - public static final DeepgramModel AURA2DIANA_ES = new DeepgramModel(Value.AURA2DIANA_ES, "aura-2-diana-es"); - - public static final DeepgramModel AURA_ORION_EN = new DeepgramModel(Value.AURA_ORION_EN, "aura-orion-en"); - - public static final DeepgramModel AURA2ATHENA_EN = new DeepgramModel(Value.AURA2ATHENA_EN, "aura-2-athena-en"); - - public static final DeepgramModel AURA_ARCAS_EN = new DeepgramModel(Value.AURA_ARCAS_EN, "aura-arcas-en"); - - public static final DeepgramModel AURA2THEIA_EN = new DeepgramModel(Value.AURA2THEIA_EN, "aura-2-theia-en"); - - public static final DeepgramModel FLUX_WADE_EN = new DeepgramModel(Value.FLUX_WADE_EN, "flux-wade-en"); - - public static final DeepgramModel FLUX_HEATHER_EN = new DeepgramModel(Value.FLUX_HEATHER_EN, "flux-heather-en"); - - public static final DeepgramModel FLUX_CLIFF_EN = new DeepgramModel(Value.FLUX_CLIFF_EN, "flux-cliff-en"); - - public static final DeepgramModel AURA2JUPITER_EN = new DeepgramModel(Value.AURA2JUPITER_EN, "aura-2-jupiter-en"); - - public static final DeepgramModel FLUX_KIT_EN = new DeepgramModel(Value.FLUX_KIT_EN, "flux-kit-en"); - - public static final DeepgramModel AURA2AQUILA_ES = new DeepgramModel(Value.AURA2AQUILA_ES, "aura-2-aquila-es"); - - public static final DeepgramModel AURA2CORA_EN = new DeepgramModel(Value.AURA2CORA_EN, "aura-2-cora-en"); - - public static final DeepgramModel AURA2CORDELIA_EN = - new DeepgramModel(Value.AURA2CORDELIA_EN, "aura-2-cordelia-en"); - - public static final DeepgramModel AURA2ATLAS_EN = new DeepgramModel(Value.AURA2ATLAS_EN, "aura-2-atlas-en"); - - public static final DeepgramModel AURA2HYPERION_EN = - new DeepgramModel(Value.AURA2HYPERION_EN, "aura-2-hyperion-en"); - - public static final DeepgramModel AURA2CELESTE_ES = new DeepgramModel(Value.AURA2CELESTE_ES, "aura-2-celeste-es"); - - public static final DeepgramModel AURA2PLUTO_EN = new DeepgramModel(Value.AURA2PLUTO_EN, "aura-2-pluto-en"); - - public static final DeepgramModel FLUX_BREE_EN = new DeepgramModel(Value.FLUX_BREE_EN, "flux-bree-en"); - - public static final DeepgramModel AURA2ARCAS_EN = new DeepgramModel(Value.AURA2ARCAS_EN, "aura-2-arcas-en"); - - public static final DeepgramModel FLUX_COLIN_EN = new DeepgramModel(Value.FLUX_COLIN_EN, "flux-colin-en"); - - public static final DeepgramModel AURA_ATHENA_EN = new DeepgramModel(Value.AURA_ATHENA_EN, "aura-athena-en"); - - public static final DeepgramModel AURA2ODYSSEUS_EN = - new DeepgramModel(Value.AURA2ODYSSEUS_EN, "aura-2-odysseus-en"); - - public static final DeepgramModel AURA2PANDORA_EN = new DeepgramModel(Value.AURA2PANDORA_EN, "aura-2-pandora-en"); - - public static final DeepgramModel FLUX_MILES_EN = new DeepgramModel(Value.FLUX_MILES_EN, "flux-miles-en"); - - public static final DeepgramModel AURA_ASTERIA_EN = new DeepgramModel(Value.AURA_ASTERIA_EN, "aura-asteria-en"); - - public static final DeepgramModel AURA2ELECTRA_EN = new DeepgramModel(Value.AURA2ELECTRA_EN, "aura-2-electra-en"); - - public static final DeepgramModel FLUX_GEMMA_EN = new DeepgramModel(Value.FLUX_GEMMA_EN, "flux-gemma-en"); - - public static final DeepgramModel AURA2ESTRELLA_ES = - new DeepgramModel(Value.AURA2ESTRELLA_ES, "aura-2-estrella-es"); - - private final Value value; - - private final String string; - - DeepgramModel(Value value, String string) { - this.value = value; - this.string = string; - } - - public Value getEnumValue() { - return value; - } - - @java.lang.Override - @JsonValue - public String toString() { - return this.string; - } - - @java.lang.Override - public boolean equals(Object other) { - return (this == other) - || (other instanceof DeepgramModel && this.string.equals(((DeepgramModel) other).string)); - } - - @java.lang.Override - public int hashCode() { - return this.string.hashCode(); - } - - public T visit(Visitor visitor) { - switch (value) { - case AURA2SIRIO_ES: - return visitor.visitAura2SirioEs(); - case AURA2HERA_EN: - return visitor.visitAura2HeraEn(); - case FLUX_KELSEY_EN: - return visitor.visitFluxKelseyEn(); - case FLUX_RENEE_EN: - return visitor.visitFluxReneeEn(); - case FLUX_SIENNA_EN: - return visitor.visitFluxSiennaEn(); - case AURA2HARMONIA_EN: - return visitor.visitAura2HarmoniaEn(); - case AURA2CARINA_ES: - return visitor.visitAura2CarinaEs(); - case FLUX_MEENA_EN: - return visitor.visitFluxMeenaEn(); - case FLUX_MEGHAN_EN: - return visitor.visitFluxMeghanEn(); - case AURA_ZEUS_EN: - return visitor.visitAuraZeusEn(); - case AURA2HERMES_EN: - return visitor.visitAura2HermesEn(); - case FLUX_CONOR_EN: - return visitor.visitFluxConorEn(); - case AURA2NEPTUNE_EN: - return visitor.visitAura2NeptuneEn(); - case FLUX_BROOKE_EN: - return visitor.visitFluxBrookeEn(); - case FLUX_RUFUS_EN: - return visitor.visitFluxRufusEn(); - case FLUX_WES_EN: - return visitor.visitFluxWesEn(); - case AURA2CALLISTA_EN: - return visitor.visitAura2CallistaEn(); - case AURA2AURORA_EN: - return visitor.visitAura2AuroraEn(); - case AURA2OPHELIA_EN: - return visitor.visitAura2OpheliaEn(); - case FLUX_BRUCE_EN: - return visitor.visitFluxBruceEn(); - case AURA2APOLLO_EN: - return visitor.visitAura2ApolloEn(); - case AURA2DELIA_EN: - return visitor.visitAura2DeliaEn(); - case AURA2MARS_EN: - return visitor.visitAura2MarsEn(); - case AURA_HERA_EN: - return visitor.visitAuraHeraEn(); - case FLUX_MAEVE_EN: - return visitor.visitFluxMaeveEn(); - case FLUX_MARCUS_EN: - return visitor.visitFluxMarcusEn(); - case AURA2ARIES_EN: - return visitor.visitAura2AriesEn(); - case FLUX_PAIGE_EN: - return visitor.visitFluxPaigeEn(); - case AURA2JUNO_EN: - return visitor.visitAura2JunoEn(); - case FLUX_DREW_EN: - return visitor.visitFluxDrewEn(); - case AURA2JAVIER_ES: - return visitor.visitAura2JavierEs(); - case AURA2ANDROMEDA_EN: - return visitor.visitAura2AndromedaEn(); - case AURA2SATURN_EN: - return visitor.visitAura2SaturnEn(); - case AURA2IRIS_EN: - return visitor.visitAura2IrisEn(); - case AURA_ANGUS_EN: - return visitor.visitAuraAngusEn(); - case FLUX_KAI_EN: - return visitor.visitFluxKaiEn(); - case FLUX_BRITTANY_EN: - return visitor.visitFluxBrittanyEn(); - case FLUX_SEAN_EN: - return visitor.visitFluxSeanEn(); - case AURA2HELENA_EN: - return visitor.visitAura2HelenaEn(); - case FLUX_HALEY_EN: - return visitor.visitFluxHaleyEn(); - case AURA_STELLA_EN: - return visitor.visitAuraStellaEn(); - case FLUX_JACK_EN: - return visitor.visitFluxJackEn(); - case AURA2DRACO_EN: - return visitor.visitAura2DracoEn(); - case FLUX_PRIYA_EN: - return visitor.visitFluxPriyaEn(); - case AURA_HELIOS_EN: - return visitor.visitAuraHeliosEn(); - case AURA2JANUS_EN: - return visitor.visitAura2JanusEn(); - case AURA2NESTOR_ES: - return visitor.visitAura2NestorEs(); - case AURA2ORION_EN: - return visitor.visitAura2OrionEn(); - case AURA2MINERVA_EN: - return visitor.visitAura2MinervaEn(); - case AURA2ALVARO_ES: - return visitor.visitAura2AlvaroEs(); - case AURA_PERSEUS_EN: - return visitor.visitAuraPerseusEn(); - case AURA2VESTA_EN: - return visitor.visitAura2VestaEn(); - case FLUX_COLE_EN: - return visitor.visitFluxColeEn(); - case AURA2ZEUS_EN: - return visitor.visitAura2ZeusEn(); - case AURA2ORPHEUS_EN: - return visitor.visitAura2OrpheusEn(); - case AURA2THALIA_EN: - return visitor.visitAura2ThaliaEn(); - case FLUX_NAVEEN_EN: - return visitor.visitFluxNaveenEn(); - case AURA2ASTERIA_EN: - return visitor.visitAura2AsteriaEn(); - case AURA2SELENA_ES: - return visitor.visitAura2SelenaEs(); - case FLUX_MARCELO_EN: - return visitor.visitFluxMarceloEn(); - case FLUX_ELISE_EN: - return visitor.visitFluxEliseEn(); - case FLUX_ALEXIS_EN: - return visitor.visitFluxAlexisEn(); - case AURA_LUNA_EN: - return visitor.visitAuraLunaEn(); - case AURA_ORPHEUS_EN: - return visitor.visitAuraOrpheusEn(); - case AURA2AMALTHEA_EN: - return visitor.visitAura2AmaltheaEn(); - case FLUX_DONOVAN_EN: - return visitor.visitFluxDonovanEn(); - case AURA2SELENE_EN: - return visitor.visitAura2SeleneEn(); - case FLUX_TANNER_EN: - return visitor.visitFluxTannerEn(); - case FLUX_SHARON_EN: - return visitor.visitFluxSharonEn(); - case AURA2LUNA_EN: - return visitor.visitAura2LunaEn(); - case AURA2PHOEBE_EN: - return visitor.visitAura2PhoebeEn(); - case FLUX_HANNAH_EN: - return visitor.visitFluxHannahEn(); - case AURA2DIANA_ES: - return visitor.visitAura2DianaEs(); - case AURA_ORION_EN: - return visitor.visitAuraOrionEn(); - case AURA2ATHENA_EN: - return visitor.visitAura2AthenaEn(); - case AURA_ARCAS_EN: - return visitor.visitAuraArcasEn(); - case AURA2THEIA_EN: - return visitor.visitAura2TheiaEn(); - case FLUX_WADE_EN: - return visitor.visitFluxWadeEn(); - case FLUX_HEATHER_EN: - return visitor.visitFluxHeatherEn(); - case FLUX_CLIFF_EN: - return visitor.visitFluxCliffEn(); - case AURA2JUPITER_EN: - return visitor.visitAura2JupiterEn(); - case FLUX_KIT_EN: - return visitor.visitFluxKitEn(); - case AURA2AQUILA_ES: - return visitor.visitAura2AquilaEs(); - case AURA2CORA_EN: - return visitor.visitAura2CoraEn(); - case AURA2CORDELIA_EN: - return visitor.visitAura2CordeliaEn(); - case AURA2ATLAS_EN: - return visitor.visitAura2AtlasEn(); - case AURA2HYPERION_EN: - return visitor.visitAura2HyperionEn(); - case AURA2CELESTE_ES: - return visitor.visitAura2CelesteEs(); - case AURA2PLUTO_EN: - return visitor.visitAura2PlutoEn(); - case FLUX_BREE_EN: - return visitor.visitFluxBreeEn(); - case AURA2ARCAS_EN: - return visitor.visitAura2ArcasEn(); - case FLUX_COLIN_EN: - return visitor.visitFluxColinEn(); - case AURA_ATHENA_EN: - return visitor.visitAuraAthenaEn(); - case AURA2ODYSSEUS_EN: - return visitor.visitAura2OdysseusEn(); - case AURA2PANDORA_EN: - return visitor.visitAura2PandoraEn(); - case FLUX_MILES_EN: - return visitor.visitFluxMilesEn(); - case AURA_ASTERIA_EN: - return visitor.visitAuraAsteriaEn(); - case AURA2ELECTRA_EN: - return visitor.visitAura2ElectraEn(); - case FLUX_GEMMA_EN: - return visitor.visitFluxGemmaEn(); - case AURA2ESTRELLA_ES: - return visitor.visitAura2EstrellaEs(); - case UNKNOWN: - default: - return visitor.visitUnknown(string); - } - } - - @JsonCreator(mode = JsonCreator.Mode.DELEGATING) - public static DeepgramModel valueOf(String value) { - switch (value) { - case "aura-2-sirio-es": - return AURA2SIRIO_ES; - case "aura-2-hera-en": - return AURA2HERA_EN; - case "flux-kelsey-en": - return FLUX_KELSEY_EN; - case "flux-renee-en": - return FLUX_RENEE_EN; - case "flux-sienna-en": - return FLUX_SIENNA_EN; - case "aura-2-harmonia-en": - return AURA2HARMONIA_EN; - case "aura-2-carina-es": - return AURA2CARINA_ES; - case "flux-meena-en": - return FLUX_MEENA_EN; - case "flux-meghan-en": - return FLUX_MEGHAN_EN; - case "aura-zeus-en": - return AURA_ZEUS_EN; - case "aura-2-hermes-en": - return AURA2HERMES_EN; - case "flux-conor-en": - return FLUX_CONOR_EN; - case "aura-2-neptune-en": - return AURA2NEPTUNE_EN; - case "flux-brooke-en": - return FLUX_BROOKE_EN; - case "flux-rufus-en": - return FLUX_RUFUS_EN; - case "flux-wes-en": - return FLUX_WES_EN; - case "aura-2-callista-en": - return AURA2CALLISTA_EN; - case "aura-2-aurora-en": - return AURA2AURORA_EN; - case "aura-2-ophelia-en": - return AURA2OPHELIA_EN; - case "flux-bruce-en": - return FLUX_BRUCE_EN; - case "aura-2-apollo-en": - return AURA2APOLLO_EN; - case "aura-2-delia-en": - return AURA2DELIA_EN; - case "aura-2-mars-en": - return AURA2MARS_EN; - case "aura-hera-en": - return AURA_HERA_EN; - case "flux-maeve-en": - return FLUX_MAEVE_EN; - case "flux-marcus-en": - return FLUX_MARCUS_EN; - case "aura-2-aries-en": - return AURA2ARIES_EN; - case "flux-paige-en": - return FLUX_PAIGE_EN; - case "aura-2-juno-en": - return AURA2JUNO_EN; - case "flux-drew-en": - return FLUX_DREW_EN; - case "aura-2-javier-es": - return AURA2JAVIER_ES; - case "aura-2-andromeda-en": - return AURA2ANDROMEDA_EN; - case "aura-2-saturn-en": - return AURA2SATURN_EN; - case "aura-2-iris-en": - return AURA2IRIS_EN; - case "aura-angus-en": - return AURA_ANGUS_EN; - case "flux-kai-en": - return FLUX_KAI_EN; - case "flux-brittany-en": - return FLUX_BRITTANY_EN; - case "flux-sean-en": - return FLUX_SEAN_EN; - case "aura-2-helena-en": - return AURA2HELENA_EN; - case "flux-haley-en": - return FLUX_HALEY_EN; - case "aura-stella-en": - return AURA_STELLA_EN; - case "flux-jack-en": - return FLUX_JACK_EN; - case "aura-2-draco-en": - return AURA2DRACO_EN; - case "flux-priya-en": - return FLUX_PRIYA_EN; - case "aura-helios-en": - return AURA_HELIOS_EN; - case "aura-2-janus-en": - return AURA2JANUS_EN; - case "aura-2-nestor-es": - return AURA2NESTOR_ES; - case "aura-2-orion-en": - return AURA2ORION_EN; - case "aura-2-minerva-en": - return AURA2MINERVA_EN; - case "aura-2-alvaro-es": - return AURA2ALVARO_ES; - case "aura-perseus-en": - return AURA_PERSEUS_EN; - case "aura-2-vesta-en": - return AURA2VESTA_EN; - case "flux-cole-en": - return FLUX_COLE_EN; - case "aura-2-zeus-en": - return AURA2ZEUS_EN; - case "aura-2-orpheus-en": - return AURA2ORPHEUS_EN; - case "aura-2-thalia-en": - return AURA2THALIA_EN; - case "flux-naveen-en": - return FLUX_NAVEEN_EN; - case "aura-2-asteria-en": - return AURA2ASTERIA_EN; - case "aura-2-selena-es": - return AURA2SELENA_ES; - case "flux-marcelo-en": - return FLUX_MARCELO_EN; - case "flux-elise-en": - return FLUX_ELISE_EN; - case "flux-alexis-en": - return FLUX_ALEXIS_EN; - case "aura-luna-en": - return AURA_LUNA_EN; - case "aura-orpheus-en": - return AURA_ORPHEUS_EN; - case "aura-2-amalthea-en": - return AURA2AMALTHEA_EN; - case "flux-donovan-en": - return FLUX_DONOVAN_EN; - case "aura-2-selene-en": - return AURA2SELENE_EN; - case "flux-tanner-en": - return FLUX_TANNER_EN; - case "flux-sharon-en": - return FLUX_SHARON_EN; - case "aura-2-luna-en": - return AURA2LUNA_EN; - case "aura-2-phoebe-en": - return AURA2PHOEBE_EN; - case "flux-hannah-en": - return FLUX_HANNAH_EN; - case "aura-2-diana-es": - return AURA2DIANA_ES; - case "aura-orion-en": - return AURA_ORION_EN; - case "aura-2-athena-en": - return AURA2ATHENA_EN; - case "aura-arcas-en": - return AURA_ARCAS_EN; - case "aura-2-theia-en": - return AURA2THEIA_EN; - case "flux-wade-en": - return FLUX_WADE_EN; - case "flux-heather-en": - return FLUX_HEATHER_EN; - case "flux-cliff-en": - return FLUX_CLIFF_EN; - case "aura-2-jupiter-en": - return AURA2JUPITER_EN; - case "flux-kit-en": - return FLUX_KIT_EN; - case "aura-2-aquila-es": - return AURA2AQUILA_ES; - case "aura-2-cora-en": - return AURA2CORA_EN; - case "aura-2-cordelia-en": - return AURA2CORDELIA_EN; - case "aura-2-atlas-en": - return AURA2ATLAS_EN; - case "aura-2-hyperion-en": - return AURA2HYPERION_EN; - case "aura-2-celeste-es": - return AURA2CELESTE_ES; - case "aura-2-pluto-en": - return AURA2PLUTO_EN; - case "flux-bree-en": - return FLUX_BREE_EN; - case "aura-2-arcas-en": - return AURA2ARCAS_EN; - case "flux-colin-en": - return FLUX_COLIN_EN; - case "aura-athena-en": - return AURA_ATHENA_EN; - case "aura-2-odysseus-en": - return AURA2ODYSSEUS_EN; - case "aura-2-pandora-en": - return AURA2PANDORA_EN; - case "flux-miles-en": - return FLUX_MILES_EN; - case "aura-asteria-en": - return AURA_ASTERIA_EN; - case "aura-2-electra-en": - return AURA2ELECTRA_EN; - case "flux-gemma-en": - return FLUX_GEMMA_EN; - case "aura-2-estrella-es": - return AURA2ESTRELLA_ES; - default: - return new DeepgramModel(Value.UNKNOWN, value); - } - } - - public enum Value { - AURA_ASTERIA_EN, - - AURA_LUNA_EN, - - AURA_STELLA_EN, - - AURA_ATHENA_EN, - - AURA_HERA_EN, - - AURA_ORION_EN, - - AURA_ARCAS_EN, - - AURA_PERSEUS_EN, - - AURA_ANGUS_EN, - - AURA_ORPHEUS_EN, - - AURA_HELIOS_EN, - - AURA_ZEUS_EN, - - AURA2AMALTHEA_EN, - - AURA2ANDROMEDA_EN, - - AURA2APOLLO_EN, - - AURA2ARCAS_EN, - - AURA2ARIES_EN, - - AURA2ASTERIA_EN, - - AURA2ATHENA_EN, - - AURA2ATLAS_EN, - - AURA2AURORA_EN, - - AURA2CALLISTA_EN, - - AURA2CORA_EN, - - AURA2CORDELIA_EN, - - AURA2DELIA_EN, - - AURA2DRACO_EN, - - AURA2ELECTRA_EN, - - AURA2HARMONIA_EN, - - AURA2HELENA_EN, - - AURA2HERA_EN, - - AURA2HERMES_EN, - - AURA2HYPERION_EN, - - AURA2IRIS_EN, - - AURA2JANUS_EN, - - AURA2JUNO_EN, - - AURA2JUPITER_EN, - - AURA2LUNA_EN, - - AURA2MARS_EN, - - AURA2MINERVA_EN, - - AURA2NEPTUNE_EN, - - AURA2ODYSSEUS_EN, - - AURA2OPHELIA_EN, - - AURA2ORION_EN, - - AURA2ORPHEUS_EN, - - AURA2PANDORA_EN, - - AURA2PHOEBE_EN, - - AURA2PLUTO_EN, - - AURA2SATURN_EN, - - AURA2SELENE_EN, - - AURA2THALIA_EN, - - AURA2THEIA_EN, - - AURA2VESTA_EN, - - AURA2ZEUS_EN, - - AURA2SIRIO_ES, - - AURA2NESTOR_ES, - - AURA2CARINA_ES, - - AURA2CELESTE_ES, - - AURA2ALVARO_ES, - - AURA2DIANA_ES, - - AURA2AQUILA_ES, - - AURA2SELENA_ES, - - AURA2ESTRELLA_ES, - - AURA2JAVIER_ES, - - FLUX_ALEXIS_EN, - - FLUX_BREE_EN, - - FLUX_BRITTANY_EN, - - FLUX_BROOKE_EN, - - FLUX_BRUCE_EN, - - FLUX_CLIFF_EN, - - FLUX_COLE_EN, - - FLUX_COLIN_EN, - - FLUX_CONOR_EN, - - FLUX_DONOVAN_EN, - - FLUX_DREW_EN, - - FLUX_ELISE_EN, - - FLUX_GEMMA_EN, - - FLUX_HALEY_EN, - - FLUX_HANNAH_EN, - - FLUX_HEATHER_EN, - - FLUX_JACK_EN, - - FLUX_KAI_EN, - - FLUX_KELSEY_EN, - - FLUX_KIT_EN, - - FLUX_MAEVE_EN, - - FLUX_MARCELO_EN, - - FLUX_MARCUS_EN, - - FLUX_MEENA_EN, - - FLUX_MEGHAN_EN, - - FLUX_MILES_EN, - - FLUX_NAVEEN_EN, - - FLUX_PAIGE_EN, - - FLUX_PRIYA_EN, - - FLUX_RENEE_EN, - - FLUX_RUFUS_EN, - - FLUX_SEAN_EN, - - FLUX_SHARON_EN, - - FLUX_SIENNA_EN, - - FLUX_TANNER_EN, - - FLUX_WADE_EN, - - FLUX_WES_EN, - - UNKNOWN - } - - public interface Visitor { - T visitAuraAsteriaEn(); - - T visitAuraLunaEn(); - - T visitAuraStellaEn(); - - T visitAuraAthenaEn(); - - T visitAuraHeraEn(); - - T visitAuraOrionEn(); - - T visitAuraArcasEn(); - - T visitAuraPerseusEn(); - - T visitAuraAngusEn(); - - T visitAuraOrpheusEn(); - - T visitAuraHeliosEn(); - - T visitAuraZeusEn(); - - T visitAura2AmaltheaEn(); - - T visitAura2AndromedaEn(); - - T visitAura2ApolloEn(); - - T visitAura2ArcasEn(); - - T visitAura2AriesEn(); - - T visitAura2AsteriaEn(); - - T visitAura2AthenaEn(); - - T visitAura2AtlasEn(); - - T visitAura2AuroraEn(); - - T visitAura2CallistaEn(); - - T visitAura2CoraEn(); - - T visitAura2CordeliaEn(); - - T visitAura2DeliaEn(); - - T visitAura2DracoEn(); - - T visitAura2ElectraEn(); - - T visitAura2HarmoniaEn(); - - T visitAura2HelenaEn(); - - T visitAura2HeraEn(); - - T visitAura2HermesEn(); - - T visitAura2HyperionEn(); - - T visitAura2IrisEn(); - - T visitAura2JanusEn(); - - T visitAura2JunoEn(); - - T visitAura2JupiterEn(); - - T visitAura2LunaEn(); - - T visitAura2MarsEn(); - - T visitAura2MinervaEn(); - - T visitAura2NeptuneEn(); - - T visitAura2OdysseusEn(); - - T visitAura2OpheliaEn(); - - T visitAura2OrionEn(); - - T visitAura2OrpheusEn(); - - T visitAura2PandoraEn(); - - T visitAura2PhoebeEn(); - - T visitAura2PlutoEn(); - - T visitAura2SaturnEn(); - - T visitAura2SeleneEn(); - - T visitAura2ThaliaEn(); - - T visitAura2TheiaEn(); - - T visitAura2VestaEn(); - - T visitAura2ZeusEn(); - - T visitAura2SirioEs(); - - T visitAura2NestorEs(); - - T visitAura2CarinaEs(); - - T visitAura2CelesteEs(); - - T visitAura2AlvaroEs(); - - T visitAura2DianaEs(); - - T visitAura2AquilaEs(); - - T visitAura2SelenaEs(); - - T visitAura2EstrellaEs(); - - T visitAura2JavierEs(); - - T visitFluxAlexisEn(); - - T visitFluxBreeEn(); - - T visitFluxBrittanyEn(); - - T visitFluxBrookeEn(); - - T visitFluxBruceEn(); - - T visitFluxCliffEn(); - - T visitFluxColeEn(); - - T visitFluxColinEn(); - - T visitFluxConorEn(); - - T visitFluxDonovanEn(); - - T visitFluxDrewEn(); - - T visitFluxEliseEn(); - - T visitFluxGemmaEn(); - - T visitFluxHaleyEn(); - - T visitFluxHannahEn(); - - T visitFluxHeatherEn(); - - T visitFluxJackEn(); - - T visitFluxKaiEn(); - - T visitFluxKelseyEn(); - - T visitFluxKitEn(); - - T visitFluxMaeveEn(); - - T visitFluxMarceloEn(); - - T visitFluxMarcusEn(); - - T visitFluxMeenaEn(); - - T visitFluxMeghanEn(); - - T visitFluxMilesEn(); - - T visitFluxNaveenEn(); - - T visitFluxPaigeEn(); - - T visitFluxPriyaEn(); - - T visitFluxReneeEn(); - - T visitFluxRufusEn(); - - T visitFluxSeanEn(); - - T visitFluxSharonEn(); - - T visitFluxSiennaEn(); - - T visitFluxTannerEn(); - - T visitFluxWadeEn(); - - T visitFluxWesEn(); - - T visitUnknown(String unknownType); - } -} diff --git a/src/test/java/com/deepgram/AgentV1ControlFrameWireTest.java b/src/test/java/com/deepgram/AgentV1ControlFrameWireTest.java index 4404a34..dcc20b0 100644 --- a/src/test/java/com/deepgram/AgentV1ControlFrameWireTest.java +++ b/src/test/java/com/deepgram/AgentV1ControlFrameWireTest.java @@ -3,9 +3,13 @@ import static org.assertj.core.api.Assertions.assertThat; import com.deepgram.core.Environment; +import com.deepgram.core.ObjectMappers; +import com.deepgram.resources.agent.v1.types.AgentV1CustomFromThinkProvider; +import com.deepgram.resources.agent.v1.types.AgentV1CustomToThinkProvider; import com.deepgram.resources.agent.v1.types.AgentV1FunctionCallCancelled; import com.deepgram.resources.agent.v1.types.AgentV1ForceEndTurn; import com.deepgram.resources.agent.v1.websocket.V1WebSocketClient; +import java.util.Map; import java.util.concurrent.BlockingQueue; import java.util.concurrent.CountDownLatch; import java.util.concurrent.LinkedBlockingQueue; @@ -97,4 +101,69 @@ public void onClosing(WebSocket webSocket, int code, String reason) { ws.disconnect(); } } + + @Test + void sendsCustomPayloadToThinkProvider() throws Exception { + BlockingQueue received = new LinkedBlockingQueue<>(); + server.enqueue(new MockResponse().withWebSocketUpgrade(new WebSocketListener() { + @Override + public void onMessage(WebSocket webSocket, String text) { + received.add(text); + } + + @Override + public void onClosing(WebSocket webSocket, int code, String reason) { + webSocket.close(code, reason); + } + })); + + V1WebSocketClient ws = client.agent().v1().v1WebSocket(); + try { + ws.connect().get(5, TimeUnit.SECONDS); + ws.sendCustomToThinkProvider(AgentV1CustomToThinkProvider.builder() + .content(Map.of("action", "lookup", "query", "weather")) + .build()) + .get(5, TimeUnit.SECONDS); + + String frame = received.poll(5, TimeUnit.SECONDS); + assertThat(frame).isNotNull(); + assertThat(ObjectMappers.JSON_MAPPER.readTree(frame).path("type").asText()) + .isEqualTo("__customToThinkProvider"); + assertThat(ObjectMappers.JSON_MAPPER.readTree(frame).path("content").path("action").asText()) + .isEqualTo("lookup"); + } finally { + ws.disconnect(); + } + } + + @Test + void dispatchesCustomPayloadFromThinkProvider() throws Exception { + CountDownLatch received = new CountDownLatch(1); + AtomicReference custom = new AtomicReference<>(); + server.enqueue(new MockResponse().withWebSocketUpgrade(new WebSocketListener() { + @Override + public void onOpen(WebSocket webSocket, okhttp3.Response response) { + webSocket.send("{\"type\":\"__customFromThinkProvider\",\"content\":{\"decision\":\"continue\"}}"); + } + + @Override + public void onClosing(WebSocket webSocket, int code, String reason) { + webSocket.close(code, reason); + } + })); + + V1WebSocketClient ws = client.agent().v1().v1WebSocket(); + ws.onCustomFromThinkProvider(event -> { + custom.set(event); + received.countDown(); + }); + try { + ws.connect().get(5, TimeUnit.SECONDS); + + assertThat(received.await(5, TimeUnit.SECONDS)).isTrue(); + assertThat(custom.get().getContent()).isEqualTo(Map.of("decision", "continue")); + } finally { + ws.disconnect(); + } + } } diff --git a/src/test/java/com/deepgram/ListenV1ConfigureErrorWebSocketTest.java b/src/test/java/com/deepgram/ListenV1ConfigureErrorWebSocketTest.java new file mode 100644 index 0000000..27429fd --- /dev/null +++ b/src/test/java/com/deepgram/ListenV1ConfigureErrorWebSocketTest.java @@ -0,0 +1,104 @@ +package com.deepgram; + +import static org.assertj.core.api.Assertions.assertThat; + +import com.deepgram.core.Environment; +import com.deepgram.core.ObjectMappers; +import com.deepgram.resources.listen.v1.types.ListenV1Configure; +import com.deepgram.resources.listen.v1.types.ListenV1Error; +import com.deepgram.resources.listen.v1.websocket.V1ConnectOptions; +import com.deepgram.resources.listen.v1.websocket.V1WebSocketClient; +import com.deepgram.types.ListenV1Model; +import java.util.List; +import java.util.Map; +import java.util.concurrent.BlockingQueue; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.LinkedBlockingQueue; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.atomic.AtomicReference; +import okhttp3.WebSocket; +import okhttp3.WebSocketListener; +import okhttp3.mockwebserver.MockResponse; +import okhttp3.mockwebserver.MockWebServer; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +class ListenV1ConfigureErrorWebSocketTest { + private MockWebServer server; + private DeepgramClient client; + + @BeforeEach + void setUp() throws Exception { + server = new MockWebServer(); + server.start(); + String base = server.url("/").toString().replaceAll("/$", ""); + Environment env = Environment.custom() + .base(base) + .production(base) + .agent(base) + .agentRest(base) + .build(); + client = DeepgramClient.builder().apiKey("test").environment(env).build(); + } + + @AfterEach + void tearDown() throws Exception { + server.shutdown(); + } + + @Test + void sendsConfigureAndDispatchesConfigureError() throws Exception { + BlockingQueue received = new LinkedBlockingQueue<>(); + server.enqueue(new MockResponse().withWebSocketUpgrade(new WebSocketListener() { + @Override + public void onMessage(WebSocket webSocket, String text) { + received.add(text); + webSocket.send("{\"type\":\"Error\",\"variant\":\"InvalidConfigureMessage\"," + + "\"description\":\"keyterms unavailable\",\"code\":\"KeytermsNotSupported\"}"); + } + + @Override + public void onClosing(WebSocket webSocket, int code, String reason) { + webSocket.close(code, reason); + } + })); + + CountDownLatch errorReceived = new CountDownLatch(1); + AtomicInteger genericErrorCount = new AtomicInteger(); + AtomicReference error = new AtomicReference<>(); + V1WebSocketClient ws = client.listen().v1().v1WebSocket(); + ws.onErrorMessage(event -> { + error.set(event); + errorReceived.countDown(); + }); + ws.onError(event -> genericErrorCount.incrementAndGet()); + try { + ws.connect(V1ConnectOptions.builder().model(ListenV1Model.NOVA3).build()) + .get(5, TimeUnit.SECONDS); + ws.sendConfigure(ListenV1Configure.builder() + .keyterms(List.of("Deepgram", "Flux")) + .features(Map.of("numerals", true)) + .build()) + .get(5, TimeUnit.SECONDS); + + String frame = received.poll(5, TimeUnit.SECONDS); + assertThat(frame).isNotNull(); + assertThat(ObjectMappers.JSON_MAPPER.readTree(frame).path("type").asText()).isEqualTo("Configure"); + assertThat(ObjectMappers.JSON_MAPPER.readTree(frame).path("keyterms").get(0).asText()) + .isEqualTo("Deepgram"); + assertThat(ObjectMappers.JSON_MAPPER.readTree(frame).path("keyterms").get(1).asText()) + .isEqualTo("Flux"); + assertThat(ObjectMappers.JSON_MAPPER.readTree(frame).path("features").path("numerals").asBoolean()) + .isTrue(); + + assertThat(errorReceived.await(5, TimeUnit.SECONDS)).isTrue(); + assertThat(error.get().getVariant()).isEqualTo("InvalidConfigureMessage"); + assertThat(error.get().getCode()).contains("KeytermsNotSupported"); + assertThat(genericErrorCount).hasValue(0); + } finally { + ws.disconnect(); + } + } +} From 01cc7375f3b78ccbb16e83fd09623ef73ed481d0 Mon Sep 17 00:00:00 2001 From: Greg Holmes Date: Wed, 7 Oct 2026 12:29:50 +0100 Subject: [PATCH 5/7] test: cover regenerated websocket controls --- examples/listen/LiveReconfigure.java | 83 +++++++++++++++++++ .../deepgram/AgentV1ControlFrameWireTest.java | 45 +++++++++- .../ListenV1ConfigureErrorWebSocketTest.java | 58 ++++++++++++- 3 files changed, 181 insertions(+), 5 deletions(-) create mode 100644 examples/listen/LiveReconfigure.java diff --git a/examples/listen/LiveReconfigure.java b/examples/listen/LiveReconfigure.java new file mode 100644 index 0000000..7e0d437 --- /dev/null +++ b/examples/listen/LiveReconfigure.java @@ -0,0 +1,83 @@ +import com.deepgram.DeepgramClient; +import com.deepgram.resources.listen.v1.types.ListenV1CloseStream; +import com.deepgram.resources.listen.v1.types.ListenV1CloseStreamType; +import com.deepgram.resources.listen.v1.types.ListenV1Configure; +import com.deepgram.resources.listen.v1.types.ListenV1Error; +import com.deepgram.resources.listen.v1.types.ListenV1ResultsChannelAlternativesItem; +import com.deepgram.resources.listen.v1.websocket.V1ConnectOptions; +import com.deepgram.resources.listen.v1.websocket.V1WebSocketClient; +import com.deepgram.types.ListenV1Model; +import java.util.List; +import java.util.Map; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; + +/** + * Reconfigures an active Nova-3 Listen V1 stream without reconnecting. + * + *

Usage: {@code DEEPGRAM_API_KEY=... java LiveReconfigure} + */ +public class LiveReconfigure { + public static void main(String[] args) { + String apiKey = System.getenv("DEEPGRAM_API_KEY"); + if (apiKey == null || apiKey.isEmpty()) { + System.err.println("DEEPGRAM_API_KEY environment variable is required"); + System.exit(1); + } + + DeepgramClient client = DeepgramClient.builder().apiKey(apiKey).build(); + V1WebSocketClient wsClient = client.listen().v1().v1WebSocket(); + CountDownLatch closeLatch = new CountDownLatch(1); + + try { + wsClient.onConnected(() -> System.out.println("Connected to Deepgram")); + wsClient.onResults(result -> { + if (result.getChannel() != null + && result.getChannel().getAlternatives() != null + && !result.getChannel().getAlternatives().isEmpty()) { + ListenV1ResultsChannelAlternativesItem alternative = + result.getChannel().getAlternatives().get(0); + if (alternative.getTranscript() != null + && !alternative.getTranscript().isEmpty()) { + System.out.println(alternative.getTranscript()); + } + } + }); + wsClient.onErrorMessage(LiveReconfigure::printListenError); + wsClient.onError(error -> System.err.println("WebSocket error occurred.")); + wsClient.onDisconnected(reason -> { + System.out.println("Connection closed."); + closeLatch.countDown(); + }); + + wsClient.connect(V1ConnectOptions.builder() + .model(ListenV1Model.NOVA3) + .build()) + .get(10, TimeUnit.SECONDS); + + wsClient.sendConfigure(ListenV1Configure.builder() + .keyterms(List.of("Deepgram", "Nova-3")) + .features(Map.of("numerals", true)) + .build()) + .get(10, TimeUnit.SECONDS); + System.out.println("Sent live reconfiguration for keyterms and numerals."); + + // Stream audio after this point, for example: wsClient.sendMedia(audioChunk); + wsClient.sendCloseStream(ListenV1CloseStream.builder() + .type(ListenV1CloseStreamType.CLOSE_STREAM) + .build()) + .get(10, TimeUnit.SECONDS); + closeLatch.await(15, TimeUnit.SECONDS); + } catch (Exception e) { + System.err.println("Unable to run the live reconfiguration example."); + } finally { + wsClient.close(); + client.close(); + } + } + + private static void printListenError(ListenV1Error error) { + System.err.println("Listen error (" + error.getVariant() + "): " + error.getDescription()); + error.getCode().ifPresent(code -> System.err.println("Error code: " + code)); + } +} diff --git a/src/test/java/com/deepgram/AgentV1ControlFrameWireTest.java b/src/test/java/com/deepgram/AgentV1ControlFrameWireTest.java index dcc20b0..1e5b7f3 100644 --- a/src/test/java/com/deepgram/AgentV1ControlFrameWireTest.java +++ b/src/test/java/com/deepgram/AgentV1ControlFrameWireTest.java @@ -9,11 +9,14 @@ import com.deepgram.resources.agent.v1.types.AgentV1FunctionCallCancelled; import com.deepgram.resources.agent.v1.types.AgentV1ForceEndTurn; import com.deepgram.resources.agent.v1.websocket.V1WebSocketClient; +import java.util.LinkedHashMap; +import java.util.List; import java.util.Map; import java.util.concurrent.BlockingQueue; import java.util.concurrent.CountDownLatch; import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicReference; import okhttp3.WebSocket; import okhttp3.WebSocketListener; @@ -120,8 +123,15 @@ public void onClosing(WebSocket webSocket, int code, String reason) { V1WebSocketClient ws = client.agent().v1().v1WebSocket(); try { ws.connect().get(5, TimeUnit.SECONDS); + Map content = new LinkedHashMap<>(); + content.put("action", "lookup"); + content.put("arguments", Map.of("city", "Seattle", "units", "metric")); + content.put("candidates", List.of("weather", 7, true)); + content.put("attempt", 2); + content.put("enabled", false); + content.put("empty", null); ws.sendCustomToThinkProvider(AgentV1CustomToThinkProvider.builder() - .content(Map.of("action", "lookup", "query", "weather")) + .content(content) .build()) .get(5, TimeUnit.SECONDS); @@ -131,6 +141,18 @@ public void onClosing(WebSocket webSocket, int code, String reason) { .isEqualTo("__customToThinkProvider"); assertThat(ObjectMappers.JSON_MAPPER.readTree(frame).path("content").path("action").asText()) .isEqualTo("lookup"); + assertThat(ObjectMappers.JSON_MAPPER.readTree(frame).path("content").path("arguments").path("city").asText()) + .isEqualTo("Seattle"); + assertThat(ObjectMappers.JSON_MAPPER.readTree(frame).path("content").path("candidates").get(1).asInt()) + .isEqualTo(7); + assertThat(ObjectMappers.JSON_MAPPER.readTree(frame).path("content").path("candidates").get(2).asBoolean()) + .isTrue(); + assertThat(ObjectMappers.JSON_MAPPER.readTree(frame).path("content").path("attempt").asInt()) + .isEqualTo(2); + assertThat(ObjectMappers.JSON_MAPPER.readTree(frame).path("content").path("enabled").asBoolean()) + .isFalse(); + assertThat(ObjectMappers.JSON_MAPPER.readTree(frame).path("content").path("empty").isNull()) + .isTrue(); } finally { ws.disconnect(); } @@ -140,10 +162,13 @@ public void onClosing(WebSocket webSocket, int code, String reason) { void dispatchesCustomPayloadFromThinkProvider() throws Exception { CountDownLatch received = new CountDownLatch(1); AtomicReference custom = new AtomicReference<>(); + AtomicInteger genericErrorCount = new AtomicInteger(); server.enqueue(new MockResponse().withWebSocketUpgrade(new WebSocketListener() { @Override public void onOpen(WebSocket webSocket, okhttp3.Response response) { - webSocket.send("{\"type\":\"__customFromThinkProvider\",\"content\":{\"decision\":\"continue\"}}"); + webSocket.send("{\"type\":\"__customFromThinkProvider\",\"content\":{" + + "\"decision\":\"continue\",\"arguments\":{\"city\":\"Seattle\"}," + + "\"candidates\":[\"weather\",7,true],\"attempt\":2,\"enabled\":false,\"empty\":null}}"); } @Override @@ -157,11 +182,25 @@ public void onClosing(WebSocket webSocket, int code, String reason) { custom.set(event); received.countDown(); }); + ws.onError(event -> genericErrorCount.incrementAndGet()); try { ws.connect().get(5, TimeUnit.SECONDS); assertThat(received.await(5, TimeUnit.SECONDS)).isTrue(); - assertThat(custom.get().getContent()).isEqualTo(Map.of("decision", "continue")); + assertThat(custom.get().getContent()).isInstanceOf(Map.class); + Map content = (Map) custom.get().getContent(); + assertThat(content.get("decision")).isEqualTo("continue"); + Map arguments = (Map) content.get("arguments"); + assertThat(arguments.get("city")).isEqualTo("Seattle"); + List candidates = (List) content.get("candidates"); + assertThat(candidates).hasSize(3); + assertThat(candidates.get(0)).isEqualTo("weather"); + assertThat(candidates.get(1)).isEqualTo(7); + assertThat(candidates.get(2)).isEqualTo(true); + assertThat(content.get("attempt")).isEqualTo(2); + assertThat(content.get("enabled")).isEqualTo(false); + assertThat(content.get("empty")).isNull(); + assertThat(genericErrorCount).hasValue(0); } finally { ws.disconnect(); } diff --git a/src/test/java/com/deepgram/ListenV1ConfigureErrorWebSocketTest.java b/src/test/java/com/deepgram/ListenV1ConfigureErrorWebSocketTest.java index 27429fd..83d4a13 100644 --- a/src/test/java/com/deepgram/ListenV1ConfigureErrorWebSocketTest.java +++ b/src/test/java/com/deepgram/ListenV1ConfigureErrorWebSocketTest.java @@ -79,7 +79,7 @@ public void onClosing(WebSocket webSocket, int code, String reason) { .get(5, TimeUnit.SECONDS); ws.sendConfigure(ListenV1Configure.builder() .keyterms(List.of("Deepgram", "Flux")) - .features(Map.of("numerals", true)) + .features(Map.of("numerals", false)) .build()) .get(5, TimeUnit.SECONDS); @@ -91,12 +91,66 @@ public void onClosing(WebSocket webSocket, int code, String reason) { assertThat(ObjectMappers.JSON_MAPPER.readTree(frame).path("keyterms").get(1).asText()) .isEqualTo("Flux"); assertThat(ObjectMappers.JSON_MAPPER.readTree(frame).path("features").path("numerals").asBoolean()) - .isTrue(); + .isFalse(); assertThat(errorReceived.await(5, TimeUnit.SECONDS)).isTrue(); assertThat(error.get().getVariant()).isEqualTo("InvalidConfigureMessage"); assertThat(error.get().getCode()).contains("KeytermsNotSupported"); assertThat(genericErrorCount).hasValue(0); + + ws.sendConfigure(ListenV1Configure.builder().keyterms(List.of()).build()) + .get(5, TimeUnit.SECONDS); + frame = received.poll(5, TimeUnit.SECONDS); + assertThat(frame).isNotNull(); + assertThat(ObjectMappers.JSON_MAPPER.readTree(frame).path("keyterms").isArray()).isTrue(); + assertThat(ObjectMappers.JSON_MAPPER.readTree(frame).path("keyterms").isEmpty()).isTrue(); + + ws.sendConfigure(ListenV1Configure.builder().build()).get(5, TimeUnit.SECONDS); + frame = received.poll(5, TimeUnit.SECONDS); + assertThat(frame).isNotNull(); + assertThat(ObjectMappers.JSON_MAPPER.readTree(frame).size()).isEqualTo(1); + assertThat(ObjectMappers.JSON_MAPPER.readTree(frame).path("type").asText()).isEqualTo("Configure"); + } finally { + ws.disconnect(); + } + } + + @Test + void dispatchesSchemaErrorWithMessageAndNoCode() throws Exception { + server.enqueue(new MockResponse().withWebSocketUpgrade(new WebSocketListener() { + @Override + public void onMessage(WebSocket webSocket, String text) { + webSocket.send("{\"type\":\"Error\",\"variant\":\"SchemaError\"," + + "\"description\":\"could not parse control frame\"," + + "\"message\":\"{\\\"type\\\":\\\"Configure\\\"}\"}"); + } + + @Override + public void onClosing(WebSocket webSocket, int code, String reason) { + webSocket.close(code, reason); + } + })); + + CountDownLatch errorReceived = new CountDownLatch(1); + AtomicInteger genericErrorCount = new AtomicInteger(); + AtomicReference error = new AtomicReference<>(); + V1WebSocketClient ws = client.listen().v1().v1WebSocket(); + ws.onErrorMessage(event -> { + error.set(event); + errorReceived.countDown(); + }); + ws.onError(event -> genericErrorCount.incrementAndGet()); + try { + ws.connect(V1ConnectOptions.builder().model(ListenV1Model.NOVA3).build()) + .get(5, TimeUnit.SECONDS); + ws.sendConfigure(ListenV1Configure.builder().build()).get(5, TimeUnit.SECONDS); + + assertThat(errorReceived.await(5, TimeUnit.SECONDS)).isTrue(); + assertThat(error.get().getVariant()).isEqualTo("SchemaError"); + assertThat(error.get().getDescription()).isEqualTo("could not parse control frame"); + assertThat(error.get().getMessage()).contains("{\"type\":\"Configure\"}"); + assertThat(error.get().getCode()).isEmpty(); + assertThat(genericErrorCount).hasValue(0); } finally { ws.disconnect(); } From 02cf2e880a2b6dd9f908598a9e0b85698edd12e2 Mon Sep 17 00:00:00 2001 From: Greg Holmes Date: Thu, 8 Oct 2026 11:47:43 +0100 Subject: [PATCH 6/7] fix: restore listen v1 error fallback --- .fern/metadata.json | 4 +-- AGENTS.md | 4 +-- README.md | 14 ++++++++ examples/listen/LiveReconfigure.java | 4 +-- .../java/com/deepgram/core/ClientOptions.java | 13 +++---- .../v1/websocket/V1WebSocketClient.java | 3 ++ .../ListenV1ConfigureErrorWebSocketTest.java | 34 +++++++++++++++++++ 7 files changed, 64 insertions(+), 12 deletions(-) diff --git a/.fern/metadata.json b/.fern/metadata.json index 1cb7167..10d57bd 100644 --- a/.fern/metadata.json +++ b/.fern/metadata.json @@ -15,5 +15,5 @@ "originGitCommit": "9dcbd3d5e8d36420319e1b33988613ad2cb2be10", "originGitCommitIsDirty": true, "invokedBy": "manual", - "sdkVersion": "0.11.1" -} \ No newline at end of file + "sdkVersion": "0.11.0" +} diff --git a/AGENTS.md b/AGENTS.md index bcef6fd..fc62e58 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -47,10 +47,10 @@ How to identify: Current temporarily frozen files: -- `src/main/java/com/deepgram/core/ClientOptions.java` - preserves release-please version markers and correct SDK header constants that Fern currently overwrites; use the standard `.bak` swap/restore workflow during regen review. Since generator 4.18.0 Fern emits a `getSdkVersion()` helper reading `Package.getImplementationVersion()` instead of a literal. That *does* resolve in the published artifact (CI publishes via `mvn deploy -P release`, and `pom.xml`'s maven-jar-plugin sets `addDefaultImplementationEntries=true`, so the JAR manifest carries `Implementation-Version`), but it resolves to `null` under Gradle and in tests, where it silently falls back to a hardcoded literal the generator does not keep current. We keep the explicit literals because they are correct in every context and because `.github/release-please-config.json` already lists this file in `extra-files`, so release-please bumps it alongside `pom.xml`, `build.gradle`, and `.fern/metadata.json`. Fern also emits `User-Agent` with a `com.deepgram.` prefix while leaving `X-Fern-SDK-Name` on the `com.deepgram:` Maven-coordinate form; we keep both on the colon form. Fern 4.22.1 tracks and closes child WebSockets before shutting down SDK-owned OkHttp resources; the permanent custom client wrappers inherit it, so do not add wrapper-local lifecycle code and retain Fern's lifecycle implementation during reconciliation. +- `src/main/java/com/deepgram/core/ClientOptions.java` - preserves release-please version markers, correct SDK header constants, and the accurate `close()` lifecycle Javadoc that Fern currently overwrites; use the standard `.bak` swap/restore workflow during regen review. Since generator 4.18.0 Fern emits a `getSdkVersion()` helper reading `Package.getImplementationVersion()` instead of a literal. That *does* resolve in the published artifact (CI publishes via `mvn deploy -P release`, and `pom.xml`'s maven-jar-plugin sets `addDefaultImplementationEntries=true`, so the JAR manifest carries `Implementation-Version`), but it resolves to `null` under Gradle and in tests, where it silently falls back to a hardcoded literal the generator does not keep current. We keep the explicit literals because they are correct in every context and because `.github/release-please-config.json` already lists this file in `extra-files`, so release-please bumps it alongside `pom.xml`, `build.gradle`, and `.fern/metadata.json`. Fern also emits `User-Agent` with a `com.deepgram.` prefix while leaving `X-Fern-SDK-Name` on the `com.deepgram:` Maven-coordinate form; we keep both on the colon form. Fern 4.22.1 tracks and closes child WebSockets before shutting down SDK-owned OkHttp resources; the permanent custom client wrappers inherit it, so do not add wrapper-local lifecycle code and retain Fern's lifecycle implementation during reconciliation. - `src/main/java/com/deepgram/core/ReconnectingWebSocketListener.java` - carries bug fixes for `maxRetries(0)` semantics ("connect once, don't retry") and a configurable `connectionTimeoutMs` field (was hardcoded 4000ms), plus an `applyOptionsOverride(...)` hook used by `TransportWebSocketFactory` to apply per-transport reconnect policy; pull this back out once the fixes are upstreamed into the Fern generator. Use the standard `.bak` swap/restore workflow during regen review. - `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. 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). +- `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` 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). - 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). diff --git a/README.md b/README.md index bab5073..21666a9 100644 --- a/README.md +++ b/README.md @@ -259,11 +259,14 @@ Stream audio for real-time speech-to-text. import com.deepgram.DeepgramClient; import com.deepgram.resources.listen.v1.types.ListenV1CloseStream; import com.deepgram.resources.listen.v1.types.ListenV1CloseStreamType; +import com.deepgram.resources.listen.v1.types.ListenV1Configure; import com.deepgram.resources.listen.v1.websocket.V1WebSocketClient; import com.deepgram.resources.listen.v1.websocket.V1ConnectOptions; import com.deepgram.types.ListenV1Model; import java.nio.file.Files; import java.nio.file.Path; +import java.util.List; +import java.util.Map; import java.util.concurrent.TimeUnit; import okio.ByteString; @@ -288,12 +291,23 @@ ws.onError(error -> { System.err.println("Error: " + error.getMessage()); }); +ws.onErrorMessage(error -> { + System.err.println("Server error: " + error.getVariant() + ": " + error.getDescription()); +}); + // Connect with options (model is required) ws.connect(V1ConnectOptions.builder() .model(ListenV1Model.NOVA3) .build()) .get(10, TimeUnit.SECONDS); +// Update Nova-3 keyterms and numerals without reconnecting. +ws.sendConfigure(ListenV1Configure.builder() + .keyterms(List.of("Deepgram")) + .features(Map.of("numerals", true)) + .build()) + .get(5, TimeUnit.SECONDS); + ws.sendMedia(ByteString.of(audioBytes)); ws.sendCloseStream(ListenV1CloseStream.builder() .type(ListenV1CloseStreamType.CLOSE_STREAM) diff --git a/examples/listen/LiveReconfigure.java b/examples/listen/LiveReconfigure.java index 7e0d437..da6d4e5 100644 --- a/examples/listen/LiveReconfigure.java +++ b/examples/listen/LiveReconfigure.java @@ -44,7 +44,7 @@ public static void main(String[] args) { } }); wsClient.onErrorMessage(LiveReconfigure::printListenError); - wsClient.onError(error -> System.err.println("WebSocket error occurred.")); + wsClient.onError(error -> System.err.println("WebSocket error occurred: " + error.getMessage())); wsClient.onDisconnected(reason -> { System.out.println("Connection closed."); closeLatch.countDown(); @@ -69,7 +69,7 @@ public static void main(String[] args) { .get(10, TimeUnit.SECONDS); closeLatch.await(15, TimeUnit.SECONDS); } catch (Exception e) { - System.err.println("Unable to run the live reconfiguration example."); + System.err.println("Unable to run the live reconfiguration example: " + e.getMessage()); } finally { wsClient.close(); client.close(); diff --git a/src/main/java/com/deepgram/core/ClientOptions.java b/src/main/java/com/deepgram/core/ClientOptions.java index 36be71d..4d7a26a 100644 --- a/src/main/java/com/deepgram/core/ClientOptions.java +++ b/src/main/java/com/deepgram/core/ClientOptions.java @@ -63,10 +63,10 @@ private ClientOptions( this.headers.putAll(headers); this.headers.putAll(new HashMap() { { - put("User-Agent", "com.deepgram:deepgram-java-sdk/0.11.1"); // x-release-please-version + put("User-Agent", "com.deepgram:deepgram-java-sdk/0.11.0"); // x-release-please-version put("X-Fern-Language", "JAVA"); put("X-Fern-SDK-Name", "com.deepgram:deepgram-java-sdk"); - put("X-Fern-SDK-Version", "0.11.1"); // x-release-please-version + put("X-Fern-SDK-Version", "0.11.0"); // x-release-please-version } }); this.headerSuppliers = headerSuppliers; @@ -144,10 +144,11 @@ public Optional retryJitterFactor() { * OkHttpClient itself; an OkHttpClient supplied via httpClient is left running, since the * caller owns its lifecycle. *

- * In-flight calls are not cancelled or awaited, and any request issued after this method - * returns fails with a {@code RejectedExecutionException}. Options derived from this one via - * {@code Builder.from(...)} share the same dispatcher and connection pool, so closing either - * releases them for both. Calling this method more than once has no further effect. + * 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. *

* 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/websocket/V1WebSocketClient.java b/src/main/java/com/deepgram/resources/listen/v1/websocket/V1WebSocketClient.java index d87d076..93f56c5 100644 --- a/src/main/java/com/deepgram/resources/listen/v1/websocket/V1WebSocketClient.java +++ b/src/main/java/com/deepgram/resources/listen/v1/websocket/V1WebSocketClient.java @@ -572,6 +572,9 @@ private void handleIncomingMessage(String json) { if (errorHandlerEvent != null) { if (errorHandler != null) { errorHandler.accept(errorHandlerEvent); + } else if (onErrorHandler != null) { + onErrorHandler.accept(new RuntimeException( + errorHandlerEvent.getVariant() + ": " + errorHandlerEvent.getDescription())); } return; } diff --git a/src/test/java/com/deepgram/ListenV1ConfigureErrorWebSocketTest.java b/src/test/java/com/deepgram/ListenV1ConfigureErrorWebSocketTest.java index 83d4a13..57e239e 100644 --- a/src/test/java/com/deepgram/ListenV1ConfigureErrorWebSocketTest.java +++ b/src/test/java/com/deepgram/ListenV1ConfigureErrorWebSocketTest.java @@ -155,4 +155,38 @@ public void onClosing(WebSocket webSocket, int code, String reason) { ws.disconnect(); } } + + @Test + void dispatchesServerErrorsToGenericHandlerWithoutTypedHandler() throws Exception { + server.enqueue(new MockResponse().withWebSocketUpgrade(new WebSocketListener() { + @Override + public void onMessage(WebSocket webSocket, String text) { + webSocket.send("{\"type\":\"Error\",\"variant\":\"SchemaError\"," + + "\"description\":\"could not parse control frame\"}"); + } + + @Override + public void onClosing(WebSocket webSocket, int code, String reason) { + webSocket.close(code, reason); + } + })); + + CountDownLatch errorReceived = new CountDownLatch(1); + AtomicReference error = new AtomicReference<>(); + V1WebSocketClient ws = client.listen().v1().v1WebSocket(); + ws.onError(event -> { + error.set(event); + errorReceived.countDown(); + }); + try { + ws.connect(V1ConnectOptions.builder().model(ListenV1Model.NOVA3).build()) + .get(5, TimeUnit.SECONDS); + ws.sendConfigure(ListenV1Configure.builder().build()).get(5, TimeUnit.SECONDS); + + assertThat(errorReceived.await(5, TimeUnit.SECONDS)).isTrue(); + assertThat(error.get()).hasMessage("SchemaError: could not parse control frame"); + } finally { + ws.disconnect(); + } + } } From 127b43209d86d069aa425547cc7460d77f4ca24e Mon Sep 17 00:00:00 2001 From: Corey Weathers Date: Thu, 8 Oct 2026 07:02:49 -0400 Subject: [PATCH 7/7] fix(listen): carry server errors to onError as ListenV1ErrorException and document the keyterm limit Hand the generic onError handler a ListenV1ErrorException that carries the typed ListenV1Error (variant, description, optional code) instead of a bare RuntimeException, register the new hand-written class in .fernignore and AGENTS.md, and note in the README snippet and LiveReconfigure header that an over-limit keyterm update currently stops transcription without an Error. --- .fernignore | 5 +++ AGENTS.md | 3 +- README.md | 4 ++- examples/listen/LiveReconfigure.java | 3 ++ .../v1/websocket/ListenV1ErrorException.java | 31 +++++++++++++++++++ .../v1/websocket/V1WebSocketClient.java | 3 +- .../ListenV1ConfigureErrorWebSocketTest.java | 9 +++++- 7 files changed, 53 insertions(+), 5 deletions(-) create mode 100644 src/main/java/com/deepgram/resources/listen/v1/websocket/ListenV1ErrorException.java diff --git a/.fernignore b/.fernignore index ef6a835..550252f 100644 --- a/.fernignore +++ b/.fernignore @@ -21,6 +21,11 @@ src/main/java/com/deepgram/core/ClientOptions.java # Transport abstraction (pluggable transport for SageMaker, etc.) src/main/java/com/deepgram/core/transport/ +# Hand-written exception that carries a Listen V1 server Error to the generic onError handler +# (thrown by the frozen listen v1 V1WebSocketClient below when no onErrorMessage handler is set). +# No Fern equivalent; the generator would delete it. +src/main/java/com/deepgram/resources/listen/v1/websocket/ListenV1ErrorException.java + # Bug fixes for maxRetries(0) semantics ("connect once, don't retry") and a # configurable connectionTimeoutMs on ReconnectOptions (was hardcoded 4000ms). # Pull this back out once the fixes are upstreamed into the Fern generator. diff --git a/AGENTS.md b/AGENTS.md index fc62e58..7f6717e 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -26,6 +26,7 @@ Current permanently frozen files: - `src/main/java/com/deepgram/DeepgramClient.java`, `src/main/java/com/deepgram/AsyncDeepgramClient.java`, `src/main/java/com/deepgram/DeepgramClientBuilder.java`, `src/main/java/com/deepgram/AsyncDeepgramClientBuilder.java` - custom wrapper entrypoints that add Bearer auth, session ID support, and custom transport behavior on top of Fern's generated API client - `src/main/java/com/deepgram/core/transport/` - hand-written transport abstraction +- `src/main/java/com/deepgram/resources/listen/v1/websocket/ListenV1ErrorException.java` - hand-written exception carrying a Listen V1 server `Error` (`ListenV1Error`) to the generic `onError` handler when no `onErrorMessage` handler is registered; thrown by the frozen listen v1 `V1WebSocketClient`. No Fern equivalent - `build.gradle`, `settings.gradle`, `gradle/`, `gradlew`, `gradlew.bat`, `pom.xml`, `Makefile` - build and project configuration - `README.md`, `CHANGELOG.md`, `CONTRIBUTING.md`, `LICENSE`, `docs/` - docs - `src/test/` - manually maintained tests @@ -50,7 +51,7 @@ Current temporarily frozen files: - `src/main/java/com/deepgram/core/ClientOptions.java` - preserves release-please version markers, correct SDK header constants, and the accurate `close()` lifecycle Javadoc that Fern currently overwrites; use the standard `.bak` swap/restore workflow during regen review. Since generator 4.18.0 Fern emits a `getSdkVersion()` helper reading `Package.getImplementationVersion()` instead of a literal. That *does* resolve in the published artifact (CI publishes via `mvn deploy -P release`, and `pom.xml`'s maven-jar-plugin sets `addDefaultImplementationEntries=true`, so the JAR manifest carries `Implementation-Version`), but it resolves to `null` under Gradle and in tests, where it silently falls back to a hardcoded literal the generator does not keep current. We keep the explicit literals because they are correct in every context and because `.github/release-please-config.json` already lists this file in `extra-files`, so release-please bumps it alongside `pom.xml`, `build.gradle`, and `.fern/metadata.json`. Fern also emits `User-Agent` with a `com.deepgram.` prefix while leaving `X-Fern-SDK-Name` on the `com.deepgram:` Maven-coordinate form; we keep both on the colon form. Fern 4.22.1 tracks and closes child WebSockets before shutting down SDK-owned OkHttp resources; the permanent custom client wrappers inherit it, so do not add wrapper-local lifecycle code and retain Fern's lifecycle implementation during reconciliation. - `src/main/java/com/deepgram/core/ReconnectingWebSocketListener.java` - carries bug fixes for `maxRetries(0)` semantics ("connect once, don't retry") and a configurable `connectionTimeoutMs` field (was hardcoded 4000ms), plus an `applyOptionsOverride(...)` hook used by `TransportWebSocketFactory` to apply per-transport reconnect policy; pull this back out once the fixes are upstreamed into the Fern generator. Use the standard `.bak` swap/restore workflow during regen review. - `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` 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). +- `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). - 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). diff --git a/README.md b/README.md index 21666a9..3940e28 100644 --- a/README.md +++ b/README.md @@ -301,7 +301,9 @@ ws.connect(V1ConnectOptions.builder() .build()) .get(10, TimeUnit.SECONDS); -// Update Nova-3 keyterms and numerals without reconnecting. +// 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. ws.sendConfigure(ListenV1Configure.builder() .keyterms(List.of("Deepgram")) .features(Map.of("numerals", true)) diff --git a/examples/listen/LiveReconfigure.java b/examples/listen/LiveReconfigure.java index da6d4e5..6da7fcc 100644 --- a/examples/listen/LiveReconfigure.java +++ b/examples/listen/LiveReconfigure.java @@ -15,6 +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. + * *

Usage: {@code DEEPGRAM_API_KEY=... java LiveReconfigure} */ public class LiveReconfigure { diff --git a/src/main/java/com/deepgram/resources/listen/v1/websocket/ListenV1ErrorException.java b/src/main/java/com/deepgram/resources/listen/v1/websocket/ListenV1ErrorException.java new file mode 100644 index 0000000..88a946e --- /dev/null +++ b/src/main/java/com/deepgram/resources/listen/v1/websocket/ListenV1ErrorException.java @@ -0,0 +1,31 @@ +package com.deepgram.resources.listen.v1.websocket; + +import com.deepgram.core.DeepgramApiException; +import com.deepgram.resources.listen.v1.types.ListenV1Error; + +/** + * Carries a Listen V1 server {@code Error} message to the generic {@code onError} handler when no + * {@code onErrorMessage} handler is registered, so callers on the generic path can still read the + * typed {@link ListenV1Error} fields. + */ +public final class ListenV1ErrorException extends DeepgramApiException { + private final ListenV1Error error; + + public ListenV1ErrorException(ListenV1Error error) { + super(buildMessage(error)); + this.error = error; + } + + /** The server error message as received, with {@code variant}, {@code description}, and optional {@code code}. */ + public ListenV1Error getError() { + return error; + } + + private static String buildMessage(ListenV1Error error) { + String message = error.getVariant() + ": " + error.getDescription(); + if (error.getCode().isPresent()) { + message += " (" + error.getCode().get() + ")"; + } + return message; + } +} diff --git a/src/main/java/com/deepgram/resources/listen/v1/websocket/V1WebSocketClient.java b/src/main/java/com/deepgram/resources/listen/v1/websocket/V1WebSocketClient.java index 93f56c5..e7cd50c 100644 --- a/src/main/java/com/deepgram/resources/listen/v1/websocket/V1WebSocketClient.java +++ b/src/main/java/com/deepgram/resources/listen/v1/websocket/V1WebSocketClient.java @@ -573,8 +573,7 @@ private void handleIncomingMessage(String json) { if (errorHandler != null) { errorHandler.accept(errorHandlerEvent); } else if (onErrorHandler != null) { - onErrorHandler.accept(new RuntimeException( - errorHandlerEvent.getVariant() + ": " + errorHandlerEvent.getDescription())); + onErrorHandler.accept(new ListenV1ErrorException(errorHandlerEvent)); } return; } diff --git a/src/test/java/com/deepgram/ListenV1ConfigureErrorWebSocketTest.java b/src/test/java/com/deepgram/ListenV1ConfigureErrorWebSocketTest.java index 57e239e..3e89b97 100644 --- a/src/test/java/com/deepgram/ListenV1ConfigureErrorWebSocketTest.java +++ b/src/test/java/com/deepgram/ListenV1ConfigureErrorWebSocketTest.java @@ -7,6 +7,7 @@ import com.deepgram.resources.listen.v1.types.ListenV1Configure; import com.deepgram.resources.listen.v1.types.ListenV1Error; import com.deepgram.resources.listen.v1.websocket.V1ConnectOptions; +import com.deepgram.resources.listen.v1.websocket.ListenV1ErrorException; import com.deepgram.resources.listen.v1.websocket.V1WebSocketClient; import com.deepgram.types.ListenV1Model; import java.util.List; @@ -184,7 +185,13 @@ public void onClosing(WebSocket webSocket, int code, String reason) { ws.sendConfigure(ListenV1Configure.builder().build()).get(5, TimeUnit.SECONDS); assertThat(errorReceived.await(5, TimeUnit.SECONDS)).isTrue(); - assertThat(error.get()).hasMessage("SchemaError: could not parse control frame"); + assertThat(error.get()) + .isInstanceOf(ListenV1ErrorException.class) + .hasMessage("SchemaError: could not parse control frame"); + ListenV1Error serverError = ((ListenV1ErrorException) error.get()).getError(); + assertThat(serverError.getVariant()).isEqualTo("SchemaError"); + assertThat(serverError.getDescription()).isEqualTo("could not parse control frame"); + assertThat(serverError.getCode()).isEmpty(); } finally { ws.disconnect(); }