From b6b7d49cad020afd3c5517754459ba0991398b2e Mon Sep 17 00:00:00 2001 From: Greg Holmes Date: Tue, 11 Aug 2026 14:05:54 +0100 Subject: [PATCH 01/14] chore: unfreeze files pending regen --- .fernignore | 34 +- .../com/deepgram/core/ClientOptions.java.bak | 241 ++++++++ .../ReconnectingWebSocketListener.java.bak | 545 +++++++++++++++++ .../v1/types/AgentV1AgentAudioDone.java.bak | 86 +++ .../agent/v1/types/AgentV1KeepAlive.java.bak | 86 +++ .../v1/types/AgentV1ListenUpdated.java.bak | 86 +++ .../v1/types/AgentV1PromptUpdated.java.bak | 86 +++ .../v1/types/AgentV1SettingsApplied.java.bak | 86 +++ .../v1/types/AgentV1SpeakUpdated.java.bak | 86 +++ .../v1/types/AgentV1ThinkUpdated.java.bak | 86 +++ .../types/AgentV1UserStartedSpeaking.java.bak | 86 +++ .../v1/websocket/V1WebSocketClient.java.bak | 555 ++++++++++++++++++ .../v2/types/ListenV2CloseStream.java.bak | 86 +++ .../v2/websocket/V2WebSocketClient.java.bak | 504 ++++++++++++++++ .../v1/websocket/V1WebSocketClient.java.bak | 458 +++++++++++++++ .../speak/v2/types/SpeakV2Close.java.bak | 86 +++ .../speak/v2/types/SpeakV2Flush.java.bak | 86 +++ .../v2/websocket/V2WebSocketClient.java.bak | 524 +++++++++++++++++ 18 files changed, 3790 insertions(+), 17 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/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/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/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/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 diff --git a/.fernignore b/.fernignore index ce1762c..a6fd951 100644 --- a/.fernignore +++ b/.fernignore @@ -14,7 +14,7 @@ src/main/java/com/deepgram/AsyncDeepgramClientBuilder.java # Contains User-Agent, X-Fern-SDK-Name, and X-Fern-SDK-Version headers # with // x-release-please-version comments for automated version bumps. # Fern regen overwrites these with incorrect SDK names and strips the markers. -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/ @@ -22,7 +22,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 @@ -32,8 +32,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, @@ -47,24 +47,24 @@ 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 # 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/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/listen/v2/types/ListenV2CloseStream.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 # 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..021caba --- /dev/null +++ b/src/main/java/com/deepgram/core/ClientOptions.java.bak @@ -0,0 +1,241 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ +package com.deepgram.core; + +import java.util.HashMap; +import java.util.Map; +import java.util.Optional; +import java.util.concurrent.TimeUnit; +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 int timeout; + + private final int maxRetries; + + private final Optional webSocketFactory; + + private final Optional logging; + + private ClientOptions( + Environment environment, + Map headers, + Map> headerSuppliers, + OkHttpClient httpClient, + int timeout, + int maxRetries, + 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.7.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", "0.7.1"); // x-release-please-version + } + }); + this.headerSuppliers = headerSuppliers; + this.httpClient = httpClient; + this.timeout = timeout; + this.maxRetries = maxRetries; + 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 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 timeout = Optional.empty(); + + private OkHttpClient httpClient = null; + + 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) { + 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; + } + + public Builder httpClient(OkHttpClient httpClient) { + this.httpClient = httpClient; + 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)); + } + + Logger logger = Logger.from(this.logging); + httpClientBuilder.addInterceptor(new LoggingInterceptor(logger)); + + this.httpClient = httpClientBuilder.build(); + this.timeout = Optional.of(httpClient.callTimeoutMillis() / 1000); + + return new ClientOptions( + environment, + headers, + headerSuppliers, + httpClient, + this.timeout.get(), + this.maxRetries, + 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.headers.putAll(clientOptions.headers); + builder.headerSuppliers.putAll(clientOptions.headerSuppliers); + builder.maxRetries = clientOptions.maxRetries(); + 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..a533068 --- /dev/null +++ b/src/main/java/com/deepgram/core/ReconnectingWebSocketListener.java.bak @@ -0,0 +1,545 @@ +/** + * 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.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 { + // Overridable options are held behind a single volatile reference (not five separate volatile + // fields) so {@link #applyOptionsOverride} swaps them atomically — a reader that snapshots the + // reference once sees a mutually-consistent set (e.g. getNextDelay() cannot observe a new min + // paired with an old max). Rewiring is used by {@code TransportWebSocketFactory} to honour + // {@code DeepgramTransportFactory.reconnectOptions()} without editing the generated WS clients. + private volatile ReconnectOptions activeOptions; + + // maxEnqueuedMessages is fixed at construction (the queue is sized once) and intentionally not + // overridable, so it stays a separate final field rather than being read from 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; + + /** + * 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.activeOptions = options; + this.maxEnqueuedMessages = options.maxEnqueuedMessages; + this.connectionSupplier = connectionSupplier; + } + + /** + * Replaces the option-derived parameters on this listener at runtime. Used by + * {@code TransportWebSocketFactory} to apply {@code DeepgramTransportFactory.reconnectOptions()} + * without requiring edits to the generated per-resource WebSocket clients. {@code maxEnqueuedMessages} + * is intentionally not overridden — the message queue is sized at construction. + * + *

Thread-safety: the override is a single atomic write to a {@code volatile} reference, so a + * reader that snapshots {@link #activeOptions} once sees a mutually-consistent set of values + * (never a new min paired with an old max). The initial connect() call may have already started + * before the override lands, so for the very first attempt the original options apply; the + * override takes effect from the next attempt onwards. For the SageMaker storm-suppression case + * ({@code maxRetries(0)}) this is fine because the initial attempt's gate + * ({@code retryCount > maxRetries} with {@code retryCount=0}) always passes regardless. + * + * @param options replacement options; {@code null} is a no-op. + */ + public void applyOptionsOverride(ReconnectOptions options) { + if (options == null) { + return; + } + 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 — the initial attempt always proceeds. + * {@code maxRetries(0)} means "connect once, don't retry" (not "refuse to connect"). + * + * Error handling: + * - TimeoutException: Includes retry attempt context + * - InterruptedException: Preserves thread interruption status + * - ExecutionException: Extracts actual cause and adds context + */ + public void connect() { + if (!connectLock.compareAndSet(false, true)) { + return; + } + // Snapshot the overridable options once so the gate and timeout below use a consistent set. + ReconnectOptions opts = this.activeOptions; + // retryCount is incremented inside scheduleReconnect() before re-entering connect(), + // so on the initial call retryCount == 0 and we always proceed. The cap applies to + // retries only — maxRetries(0) blocks retries but allows the initial attempt. + if (retryCount.get() > opts.maxRetries) { + connectLock.set(false); + return; + } + try { + CompletableFuture connectionFuture = CompletableFuture.supplyAsync(connectionSupplier); + try { + webSocket = connectionFuture.get(opts.connectionTimeoutMs, MILLISECONDS); + } catch (TimeoutException e) { + connectionFuture.cancel(true); + TimeoutException timeoutError = + new TimeoutException("WebSocket connection timeout after " + opts.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) { + WebSocket ws = webSocket; + if (ws != null) { + boolean sent = ws.send(message); + 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) { + WebSocket ws = webSocket; + if (ws != null) { + boolean sent = ws.send(data); + 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(); + } + } + + /** + * @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 (code != 1000 && shouldReconnect.get()) { + scheduleReconnect(); + } + } + + /** + * 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() { + // Single volatile read → a consistent (min, growFactor, max) snapshot even if + // applyOptionsOverride swaps the options concurrently. + ReconnectOptions opts = this.activeOptions; + if (retryCount.get() == 1) { + return opts.minReconnectionDelayMs; + } + long delay = (long) (opts.minReconnectionDelayMs * Math.pow(opts.reconnectionDelayGrowFactor, retryCount.get() - 1)); + return Math.min(delay, opts.maxReconnectionDelayMs); + } + + /** + * Schedules a reconnection attempt with appropriate delay. + * Increments retry count and uses exponential backoff. + */ + private void scheduleReconnect() { + 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}. + * Each call to {@link ReconnectingWebSocketListener#connect()} will wait at most + * this long for the underlying WebSocket factory to produce a connected socket. + */ + 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 + * - connectionTimeoutMs is positive + * + * @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..41af522 --- /dev/null +++ b/src/main/java/com/deepgram/resources/agent/v1/types/AgentV1AgentAudioDone.java.bak @@ -0,0 +1,86 @@ +/** + * 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() { + // Manual patch: Fern generates equals() (all instances of this fields-less message + // are equal) but no hashCode(), violating the Object contract. Mirror equals() with a + // type-based constant hash. Remove once Fern emits a consistent equals/hashCode pair. + 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/AgentV1KeepAlive.java.bak b/src/main/java/com/deepgram/resources/agent/v1/types/AgentV1KeepAlive.java.bak new file mode 100644 index 0000000..083578b --- /dev/null +++ b/src/main/java/com/deepgram/resources/agent/v1/types/AgentV1KeepAlive.java.bak @@ -0,0 +1,86 @@ +/** + * 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() { + // Manual patch: Fern generates equals() (all instances of this fields-less message + // are equal) but no hashCode(), violating the Object contract. Mirror equals() with a + // type-based constant hash. Remove once Fern emits a consistent equals/hashCode pair. + 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..6018473 --- /dev/null +++ b/src/main/java/com/deepgram/resources/agent/v1/types/AgentV1ListenUpdated.java.bak @@ -0,0 +1,86 @@ +/** + * 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() { + // Manual patch: Fern generates equals() (all instances of this fields-less message + // are equal) but no hashCode(), violating the Object contract. Mirror equals() with a + // type-based constant hash. Remove once Fern emits a consistent equals/hashCode pair. + 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..6edec37 --- /dev/null +++ b/src/main/java/com/deepgram/resources/agent/v1/types/AgentV1PromptUpdated.java.bak @@ -0,0 +1,86 @@ +/** + * 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() { + // Manual patch: Fern generates equals() (all instances of this fields-less message + // are equal) but no hashCode(), violating the Object contract. Mirror equals() with a + // type-based constant hash. Remove once Fern emits a consistent equals/hashCode pair. + 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/AgentV1SettingsApplied.java.bak b/src/main/java/com/deepgram/resources/agent/v1/types/AgentV1SettingsApplied.java.bak new file mode 100644 index 0000000..fb23c60 --- /dev/null +++ b/src/main/java/com/deepgram/resources/agent/v1/types/AgentV1SettingsApplied.java.bak @@ -0,0 +1,86 @@ +/** + * 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() { + // Manual patch: Fern generates equals() (all instances of this fields-less message + // are equal) but no hashCode(), violating the Object contract. Mirror equals() with a + // type-based constant hash. Remove once Fern emits a consistent equals/hashCode pair. + 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..7619157 --- /dev/null +++ b/src/main/java/com/deepgram/resources/agent/v1/types/AgentV1SpeakUpdated.java.bak @@ -0,0 +1,86 @@ +/** + * 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() { + // Manual patch: Fern generates equals() (all instances of this fields-less message + // are equal) but no hashCode(), violating the Object contract. Mirror equals() with a + // type-based constant hash. Remove once Fern emits a consistent equals/hashCode pair. + 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..2f2a981 --- /dev/null +++ b/src/main/java/com/deepgram/resources/agent/v1/types/AgentV1ThinkUpdated.java.bak @@ -0,0 +1,86 @@ +/** + * 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() { + // Manual patch: Fern generates equals() (all instances of this fields-less message + // are equal) but no hashCode(), violating the Object contract. Mirror equals() with a + // type-based constant hash. Remove once Fern emits a consistent equals/hashCode pair. + 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/AgentV1UserStartedSpeaking.java.bak b/src/main/java/com/deepgram/resources/agent/v1/types/AgentV1UserStartedSpeaking.java.bak new file mode 100644 index 0000000..7c3fc38 --- /dev/null +++ b/src/main/java/com/deepgram/resources/agent/v1/types/AgentV1UserStartedSpeaking.java.bak @@ -0,0 +1,86 @@ +/** + * 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() { + // Manual patch: Fern generates equals() (all instances of this fields-less message + // are equal) but no hashCode(), violating the Object contract. Mirror equals() with a + // type-based constant hash. Remove once Fern emits a consistent equals/hashCode pair. + 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..ffa4953 --- /dev/null +++ b/src/main/java/com/deepgram/resources/listen/v1/websocket/V1WebSocketClient.java.bak @@ -0,0 +1,555 @@ +/** + * 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()) { + // Array-valued query params (String | List unions) must serialize as repeated + // params (extra=a&extra=b), not a stringified list. The generated streaming template + // uses String.valueOf(...), which mangles a List into "[a, b]"; route these through + // QueryStringMapper (arraysAsRepeats=true) so the wire format matches the REST path. + 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())); + } + // Escape hatch: emit caller-supplied additionalProperties (e.g. no_delay) as query params. + // The generated template only serializes the typed options and drops these otherwise. + // ConnectOptions is request-only (never deserialized), so this map holds only what the + // caller set via the builder. Routed through QueryStringMapper to match the REST path. + 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.webSocketFactory().isPresent()) { + return clientOptions.webSocketFactory().get().create(request, this.reconnectingListener); + } else { + return okHttpClient.newWebSocket(request, this.reconnectingListener); + } + }) { + @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)); + } + } + }; + reconnectingListener.connect(); + return connectionFuture; + } + + /** + * Disconnects the WebSocket connection and releases resources. + */ + public void disconnect() { + 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..e508681 --- /dev/null +++ b/src/main/java/com/deepgram/resources/listen/v2/types/ListenV2CloseStream.java.bak @@ -0,0 +1,86 @@ +/** + * 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() { + // Manual patch: Fern generates equals() (all instances of this fields-less message + // are equal) but no hashCode(), violating the Object contract. Mirror equals() with a + // type-based constant hash. Remove once Fern emits a consistent equals/hashCode pair. + 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/websocket/V2WebSocketClient.java.bak b/src/main/java/com/deepgram/resources/listen/v2/websocket/V2WebSocketClient.java.bak new file mode 100644 index 0000000..f47ee22 --- /dev/null +++ b/src/main/java/com/deepgram/resources/listen/v2/websocket/V2WebSocketClient.java.bak @@ -0,0 +1,504 @@ +/** + * 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.ListenV2TurnInfo; +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 connectedHandler; + + private volatile Consumer turnInfoHandler; + + private volatile Consumer configureSuccessHandler; + + private volatile Consumer configureFailureHandler; + + 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/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()) { + // Array-valued query params (String | List unions) must serialize as repeated + // params (keyterm=a&keyterm=b), not a stringified list. The generated streaming template + // uses String.valueOf(...), which mangles a List into "[a, b]"; route these through + // QueryStringMapper (arraysAsRepeats=true) so the wire format matches the REST path. + 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.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); + } + // Escape hatch: emit caller-supplied additionalProperties (e.g. no_delay) as query params. + // The generated template only serializes the typed options and drops these otherwise. + // ConnectOptions is request-only (never deserialized), so this map holds only what the + // caller set via the builder. Routed through QueryStringMapper to match the REST path. + 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.webSocketFactory().isPresent()) { + return clientOptions.webSocketFactory().get().create(request, this.reconnectingListener); + } else { + return okHttpClient.newWebSocket(request, this.reconnectingListener); + } + }) { + @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)); + } + } + }; + reconnectingListener.connect(); + return connectionFuture; + } + + /** + * Disconnects the WebSocket connection and releases resources. + */ + public void disconnect() { + 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. + * @param message the message to send + * @return a CompletableFuture that completes when the message is sent + */ + public CompletableFuture sendCloseStream(ListenV2CloseStream 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 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) { + 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("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("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; + } + } + // Unrecognized message type: forward-compatible no-op. The raw frame was + // already delivered to onMessage(String) above, so a newer server adding a + // benign control frame (e.g. a GA addition to this endpoint) must not surface + // as a fatal error to deployed clients. Routing it to onError would make a + // harmless frame look fatal; mirrors the speak v2 client and the JS/Python SDKs. + } 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..8033a55 --- /dev/null +++ b/src/main/java/com/deepgram/resources/speak/v1/websocket/V1WebSocketClient.java.bak @@ -0,0 +1,458 @@ +/** + * 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())); + } + // Escape hatch: emit caller-supplied additionalProperties (e.g. no_delay) as query params. + // The generated template only serializes the typed options and drops these otherwise. + // ConnectOptions is request-only (never deserialized), so this map holds only what the + // caller set via the builder. Routed through QueryStringMapper to match the REST path. + 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.webSocketFactory().isPresent()) { + return clientOptions.webSocketFactory().get().create(request, this.reconnectingListener); + } else { + return okHttpClient.newWebSocket(request, this.reconnectingListener); + } + }) { + @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)); + } + } + }; + 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() { + 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..51fef19 --- /dev/null +++ b/src/main/java/com/deepgram/resources/speak/v2/types/SpeakV2Close.java.bak @@ -0,0 +1,86 @@ +/** + * 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() { + // Manual patch: Fern generates equals() (all instances of this fields-less message + // are equal) but no hashCode(), violating the Object contract. Mirror equals() with a + // type-based constant hash. Remove once Fern emits a consistent equals/hashCode pair. + 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..e122400 --- /dev/null +++ b/src/main/java/com/deepgram/resources/speak/v2/types/SpeakV2Flush.java.bak @@ -0,0 +1,86 @@ +/** + * 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() { + // Manual patch: Fern generates equals() (all instances of this fields-less message + // are equal) but no hashCode(), violating the Object contract. Mirror equals() with a + // type-based constant hash. Remove once Fern emits a consistent equals/hashCode pair. + 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..44426c4 --- /dev/null +++ b/src/main/java/com/deepgram/resources/speak/v2/websocket/V2WebSocketClient.java.bak @@ -0,0 +1,524 @@ +/** + * 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.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.SpeakV2SessionMetadata; +import com.deepgram.resources.speak.v2.types.SpeakV2Speak; +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 flushedHandler; + + private volatile Consumer sessionMetadataHandler; + + 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.getMipOptOut() != null && options.getMipOptOut().isPresent()) { + urlBuilder.addQueryParameter( + "mip_opt_out", String.valueOf(options.getMipOptOut().get())); + } + if (options.getTag() != null && options.getTag().isPresent()) { + // Array-valued query params (String | List unions) must serialize as repeated + // params (tag=a&tag=b), not a stringified list. The generated streaming template uses + // String.valueOf(...), which mangles a List into "[a, b]"; route through + // QueryStringMapper (arraysAsRepeats=true) so the wire format matches the REST path. + QueryStringMapper.addQueryParameter( + urlBuilder, "tag", options.getTag().get().get(), true); + } + // Escape hatch: emit caller-supplied additionalProperties (e.g. no_delay) as query params. + // The generated template only serializes the typed options and drops these otherwise. + // ConnectOptions is request-only (never deserialized), so this map holds only what the + // caller set via the builder. Routed through QueryStringMapper to match the REST path. + 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.webSocketFactory().isPresent()) { + return clientOptions.webSocketFactory().get().create(request, this.reconnectingListener); + } else { + return okHttpClient.newWebSocket(request, this.reconnectingListener); + } + }) { + @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)); + } + } + }; + reconnectingListener.connect(); + return connectionFuture; + } + + /** + * Disconnects the WebSocket connection and releases resources. + */ + public void disconnect() { + 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 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 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 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("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; + } + } + // Unrecognized message type: forward-compatible no-op. The raw frame was + // already delivered to onMessage(String) above, so a newer server adding a + // benign control frame (e.g. a GA addition to this endpoint) must not surface + // as a fatal error to deployed clients. Routing it to onError would make a + // harmless frame look fatal to a voice agent; mirrors the JS/Python SDKs. + } catch (Exception e) { + if (onErrorHandler != null) { + onErrorHandler.accept(e); + } + } + } +} From f937fbe174ef725572034935e651601e94f9c903 Mon Sep 17 00:00:00 2001 From: Greg Holmes Date: Tue, 11 Aug 2026 14:07:55 +0100 Subject: [PATCH 02/14] docs: sync AGENTS.md frozen-file list with .fernignore (v1 WS clients, streaming query-param patches) --- AGENTS.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/AGENTS.md b/AGENTS.md index 8b3a389..a4b559b 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -49,7 +49,8 @@ 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 - `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`. 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/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). - 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/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}.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). ### Prepare repo for regeneration From a248b527efbb967d9627101ccfd671b75688bd2b Mon Sep 17 00:00:00 2001 From: "fern-api[bot]" <115122769+fern-api[bot]@users.noreply.github.com> Date: Tue, 11 Aug 2026 13:12:04 +0000 Subject: [PATCH 03/14] SDK regeneration --- .fern/metadata.json | 9 +- .../AsyncDeepgramApiClientBuilder.java | 43 ++- .../deepgram/DeepgramApiClientBuilder.java | 43 ++- .../java/com/deepgram/core/ClientOptions.java | 80 ++++- .../java/com/deepgram/core/ConsoleLogger.java | 1 + .../deepgram/core/DateTimeDeserializer.java | 15 +- .../core/ReconnectingWebSocketListener.java | 93 ++---- .../com/deepgram/core/RequestOptions.java | 16 + .../ResponseDecompressionInterceptor.java | 56 ++++ .../com/deepgram/core/RetryInterceptor.java | 89 +++++- .../think/models/AsyncRawModelsClient.java | 13 + .../think/models/RawModelsClient.java | 12 + .../agent/v1/types/AgentV1AgentAudioDone.java | 8 - .../v1/types/AgentV1AgentStartedSpeaking.java | 3 - .../agent/v1/types/AgentV1AgentThinking.java | 1 - .../v1/types/AgentV1ConversationText.java | 2 - .../agent/v1/types/AgentV1Error.java | 2 - ...entV1FunctionCallRequestFunctionsItem.java | 4 - ...HistoryFunctionCallsFunctionCallsItem.java | 5 - .../v1/types/AgentV1InjectAgentMessage.java | 1 - .../v1/types/AgentV1InjectUserMessage.java | 1 - .../v1/types/AgentV1InjectionRefused.java | 1 - .../agent/v1/types/AgentV1KeepAlive.java | 8 - .../agent/v1/types/AgentV1LatencyReport.java | 34 ++- .../agent/v1/types/AgentV1ListenUpdated.java | 8 - .../agent/v1/types/AgentV1PromptUpdated.java | 8 - .../AgentV1ReceiveFunctionCallResponse.java | 2 - .../AgentV1SendFunctionCallResponse.java | 2 - ...gesItemFunctionCallsFunctionCallsItem.java | 5 - .../v1/types/AgentV1SettingsApplied.java | 8 - .../v1/types/AgentV1SettingsAudioInput.java | 2 - .../agent/v1/types/AgentV1SpeakUpdated.java | 8 - .../agent/v1/types/AgentV1ThinkUpdated.java | 8 - .../agent/v1/types/AgentV1UpdateListen.java | 7 +- .../v1/types/AgentV1UpdateListenListen.java | 14 +- .../AgentV1UpdateListenListenProvider.java | 224 ++++++++++++++ .../agent/v1/types/AgentV1UpdatePrompt.java | 1 - .../v1/types/AgentV1UserStartedSpeaking.java | 8 - .../agent/v1/types/AgentV1Warning.java | 2 - .../agent/v1/types/AgentV1Welcome.java | 1 - .../v1/types/ConversationHistoryMessage.java | 2 - .../auth/v1/tokens/AsyncRawTokensClient.java | 13 + .../auth/v1/tokens/RawTokensClient.java | 12 + .../listen/v1/media/AsyncRawMediaClient.java | 25 ++ .../listen/v1/media/RawMediaClient.java | 23 ++ .../v1/media/requests/ListenV1RequestUrl.java | 16 +- .../MediaTranscribeRequestOctetStream.java | 16 +- .../listen/v1/types/ListenV1CloseStream.java | 1 - .../listen/v1/types/ListenV1Finalize.java | 1 - .../listen/v1/types/ListenV1KeepAlive.java | 1 - .../listen/v1/types/ListenV1Metadata.java | 6 - .../listen/v1/types/ListenV1Results.java | 2 - ...istenV1ResultsChannelAlternativesItem.java | 2 - ...sultsChannelAlternativesItemWordsItem.java | 4 - .../v1/types/ListenV1ResultsEntitiesItem.java | 6 - .../v1/types/ListenV1ResultsMetadata.java | 2 - .../ListenV1ResultsMetadataModelInfo.java | 3 - .../v1/types/ListenV1SpeechStarted.java | 1 - .../listen/v1/types/ListenV1UtteranceEnd.java | 1 - .../listen/v1/websocket/V1ConnectOptions.java | 1 - .../v1/websocket/V1WebSocketClient.java | 39 +-- .../listen/v2/types/ListenV2CloseStream.java | 8 - .../v2/types/ListenV2ConfigureFailure.java | 4 - .../v2/types/ListenV2ConfigureSuccess.java | 6 - .../listen/v2/types/ListenV2Connected.java | 4 - .../listen/v2/types/ListenV2FatalError.java | 5 - .../listen/v2/types/ListenV2TurnInfo.java | 15 - .../v2/types/ListenV2TurnInfoWordsItem.java | 2 - .../listen/v2/websocket/V2ConnectOptions.java | 33 ++ .../v2/websocket/V2WebSocketClient.java | 41 +-- .../v1/models/AsyncRawModelsClient.java | 25 ++ .../manage/v1/models/RawModelsClient.java | 23 ++ .../v1/projects/AsyncRawProjectsClient.java | 61 ++++ .../manage/v1/projects/RawProjectsClient.java | 56 ++++ .../balances/AsyncRawBalancesClient.java | 25 ++ .../billing/balances/RawBalancesClient.java | 23 ++ .../breakdown/AsyncRawBreakdownClient.java | 13 + .../billing/breakdown/RawBreakdownClient.java | 12 + .../billing/fields/AsyncRawFieldsClient.java | 13 + .../billing/fields/RawFieldsClient.java | 12 + .../purchases/AsyncRawPurchasesClient.java | 13 + .../billing/purchases/RawPurchasesClient.java | 12 + .../v1/projects/keys/AsyncRawKeysClient.java | 49 +++ .../v1/projects/keys/RawKeysClient.java | 45 +++ .../members/AsyncRawMembersClient.java | 25 ++ .../v1/projects/members/RawMembersClient.java | 23 ++ .../invites/AsyncRawInvitesClient.java | 37 +++ .../members/invites/RawInvitesClient.java | 34 +++ .../CreateProjectInviteV1Request.java | 2 - .../members/scopes/AsyncRawScopesClient.java | 25 ++ .../members/scopes/RawScopesClient.java | 23 ++ .../UpdateProjectMemberScopesV1Request.java | 1 - .../projects/models/AsyncRawModelsClient.java | 25 ++ .../v1/projects/models/RawModelsClient.java | 23 ++ .../requests/AsyncRawRequestsClient.java | 25 ++ .../projects/requests/RawRequestsClient.java | 23 ++ .../projects/usage/AsyncRawUsageClient.java | 13 + .../v1/projects/usage/RawUsageClient.java | 12 + .../breakdown/AsyncRawBreakdownClient.java | 13 + .../usage/breakdown/RawBreakdownClient.java | 12 + .../usage/fields/AsyncRawFieldsClient.java | 13 + .../usage/fields/RawFieldsClient.java | 12 + .../read/v1/text/AsyncRawTextClient.java | 13 + .../resources/read/v1/text/RawTextClient.java | 12 + ...AsyncRawDistributionCredentialsClient.java | 49 +++ .../RawDistributionCredentialsClient.java | 45 +++ .../speak/v1/audio/AsyncRawAudioClient.java | 13 + .../speak/v1/audio/RawAudioClient.java | 12 + .../v1/audio/requests/SpeakV1Request.java | 1 - .../speak/v1/types/SpeakV1Clear.java | 1 - .../speak/v1/types/SpeakV1Cleared.java | 2 - .../speak/v1/types/SpeakV1Close.java | 1 - .../speak/v1/types/SpeakV1Flush.java | 1 - .../speak/v1/types/SpeakV1Flushed.java | 2 - .../speak/v1/types/SpeakV1Metadata.java | 4 - .../resources/speak/v1/types/SpeakV1Text.java | 1 - .../speak/v1/types/SpeakV1Warning.java | 2 - .../speak/v1/websocket/V1WebSocketClient.java | 12 - .../speak/v2/audio/AsyncRawAudioClient.java | 21 ++ .../speak/v2/audio/RawAudioClient.java | 20 ++ .../v2/audio/requests/SpeakV2Request.java | 98 +++++- .../speak/v2/types/SpeakV2Close.java | 8 - .../speak/v2/types/SpeakV2Configure.java | 113 +++++++ .../v2/types/SpeakV2ConfigureFailure.java | 260 ++++++++++++++++ .../v2/types/SpeakV2ConfigureFailureCode.java | 108 +++++++ .../v2/types/SpeakV2ConfigureSuccess.java | 136 +++++++++ .../types/SpeakV2ConfigureSuccessApplied.java | 105 +++++++ .../speak/v2/types/SpeakV2Connected.java | 3 - .../speak/v2/types/SpeakV2Error.java | 2 - .../speak/v2/types/SpeakV2ErrorCode.java | 10 + .../speak/v2/types/SpeakV2Flush.java | 8 - .../speak/v2/types/SpeakV2Flushed.java | 1 - .../speak/v2/types/SpeakV2Interrupt.java | 122 ++++++++ .../types/SpeakV2InterruptPlaybackOffset.java | 135 +++++++++ .../v2/types/SpeakV2SessionMetadata.java | 9 +- .../speak/v2/types/SpeakV2Speak.java | 7 +- .../v2/types/SpeakV2SpeechInterrupted.java | 261 ++++++++++++++++ .../SpeakV2SpeechInterruptedMetadata.java | 283 ++++++++++++++++++ ...echInterruptedMetadataControlsApplied.java | 200 +++++++++++++ .../speak/v2/types/SpeakV2SpeechMetadata.java | 11 +- .../SpeakV2SpeechMetadataControlsApplied.java | 63 +++- .../speak/v2/types/SpeakV2SpeechStarted.java | 1 - .../speak/v2/types/SpeakV2Warning.java | 17 +- .../speak/v2/websocket/V2ConnectOptions.java | 66 +++- .../speak/v2/websocket/V2WebSocketClient.java | 134 +++++++-- .../AsyncRawConfigurationsClient.java | 61 ++++ .../RawConfigurationsClient.java | 56 ++++ .../CreateAgentConfigurationV1Request.java | 1 - .../variables/AsyncRawVariablesClient.java | 61 ++++ .../variables/RawVariablesClient.java | 56 ++++ .../CreateAgentVariableV1Request.java | 1 - .../deepgram/types/AgentConfigurationV1.java | 1 - ...gentThinkModelsV1ResponseModelsItemId.java | 2 - ...entThinkModelsV1ResponseModelsItemOne.java | 2 - ...tThinkModelsV1ResponseModelsItemThree.java | 1 - ...entThinkModelsV1ResponseModelsItemTwo.java | 2 - ...ntThinkModelsV1ResponseModelsItemZero.java | 2 - .../com/deepgram/types/AgentVariableV1.java | 2 - .../java/com/deepgram/types/Anthropic.java | 1 - .../types/AwsBedrockThinkProvider.java | 1 - .../deepgram/types/AwsPollySpeakProvider.java | 2 - .../types/BillingBreakdownV1Response.java | 2 - .../BillingBreakdownV1ResponseResolution.java | 2 - ...BillingBreakdownV1ResponseResultsItem.java | 1 - .../java/com/deepgram/types/Cartesia.java | 1 - .../types/CartesiaSpeakProviderVoice.java | 2 - .../CreateAgentConfigurationV1Response.java | 1 - ...ialsV1ResponseDistributionCredentials.java | 3 - ...stributionCredentialsV1ResponseMember.java | 2 - .../java/com/deepgram/types/Deepgram.java | 15 +- .../types/DeepgramListenProviderV2.java | 1 - .../types/DeepgramSpeakProviderModel.java | 132 ++++++++ .../types/ElevenLabsSpeakProvider.java | 1 - ...ialsV1ResponseDistributionCredentials.java | 3 - ...stributionCredentialsV1ResponseMember.java | 2 - src/main/java/com/deepgram/types/Google.java | 25 +- .../types/GoogleThinkProviderVersion.java | 96 ++++++ .../com/deepgram/types/GrantV1Response.java | 1 - ...redentialsItemDistributionCredentials.java | 3 - ...onseDistributionCredentialsItemMember.java | 2 - .../types/ListenV1AcceptedResponse.java | 1 - .../com/deepgram/types/ListenV2Redact.java | 84 ++++++ .../deepgram/types/OpenAiSpeakProvider.java | 2 - .../deepgram/types/OpenAiThinkProvider.java | 1 - .../com/deepgram/types/ReadV1RequestText.java | 1 - .../com/deepgram/types/ReadV1RequestUrl.java | 1 - .../types/SpeakV2AcceptedResponse.java | 1 - .../types/UsageBreakdownV1Response.java | 2 - .../UsageBreakdownV1ResponseResolution.java | 2 - .../UsageBreakdownV1ResponseResultsItem.java | 7 - 190 files changed, 4333 insertions(+), 555 deletions(-) create mode 100644 src/main/java/com/deepgram/core/ResponseDecompressionInterceptor.java create mode 100644 src/main/java/com/deepgram/resources/agent/v1/types/AgentV1UpdateListenListenProvider.java create mode 100644 src/main/java/com/deepgram/resources/speak/v2/types/SpeakV2Configure.java create mode 100644 src/main/java/com/deepgram/resources/speak/v2/types/SpeakV2ConfigureFailure.java create mode 100644 src/main/java/com/deepgram/resources/speak/v2/types/SpeakV2ConfigureFailureCode.java create mode 100644 src/main/java/com/deepgram/resources/speak/v2/types/SpeakV2ConfigureSuccess.java create mode 100644 src/main/java/com/deepgram/resources/speak/v2/types/SpeakV2ConfigureSuccessApplied.java create mode 100644 src/main/java/com/deepgram/resources/speak/v2/types/SpeakV2Interrupt.java create mode 100644 src/main/java/com/deepgram/resources/speak/v2/types/SpeakV2InterruptPlaybackOffset.java create mode 100644 src/main/java/com/deepgram/resources/speak/v2/types/SpeakV2SpeechInterrupted.java create mode 100644 src/main/java/com/deepgram/resources/speak/v2/types/SpeakV2SpeechInterruptedMetadata.java create mode 100644 src/main/java/com/deepgram/resources/speak/v2/types/SpeakV2SpeechInterruptedMetadataControlsApplied.java create mode 100644 src/main/java/com/deepgram/types/GoogleThinkProviderVersion.java create mode 100644 src/main/java/com/deepgram/types/ListenV2Redact.java diff --git a/.fern/metadata.json b/.fern/metadata.json index 9e4e19b..72d7e7c 100644 --- a/.fern/metadata.json +++ b/.fern/metadata.json @@ -1,7 +1,7 @@ { "cliVersion": "5.44.6", "generatorName": "fernapi/fern-java-sdk", - "generatorVersion": "4.10.1", + "generatorVersion": "4.16.0", "generatorConfig": { "package-prefix": "com.deepgram", "base-api-exception-class-name": "DeepgramHttpException", @@ -9,10 +9,11 @@ "client": { "class-name": "DeepgramClient" }, - "enable-wire-tests": true + "enable-wire-tests": true, + "runtime-version": true }, - "originGitCommit": "ff8fd2b74fdd5c081e9f111d59d3619f7e286dd8", + "originGitCommit": "03f06776bbb692c49f8d76c4230bca7f4bc4bca7", "originGitCommitIsDirty": true, "invokedBy": "manual", - "sdkVersion": "0.7.1" + "sdkVersion": "0.7.2" } \ No newline at end of file diff --git a/src/main/java/com/deepgram/AsyncDeepgramApiClientBuilder.java b/src/main/java/com/deepgram/AsyncDeepgramApiClientBuilder.java index 35f7792..a7fe4c9 100644 --- a/src/main/java/com/deepgram/AsyncDeepgramApiClientBuilder.java +++ b/src/main/java/com/deepgram/AsyncDeepgramApiClientBuilder.java @@ -16,6 +16,12 @@ public class AsyncDeepgramApiClientBuilder { private Optional maxRetries = Optional.empty(); + private Optional initialRetryDelayMillis = Optional.empty(); + + private Optional maxRetryDelayMillis = Optional.empty(); + + private Optional retryJitterFactor = Optional.empty(); + private final Map customHeaders = new HashMap<>(); private String apiKey = System.getenv("DEEPGRAM_API_KEY"); @@ -56,6 +62,30 @@ public AsyncDeepgramApiClientBuilder maxRetries(int maxRetries) { return this; } + /** + * Sets the initial delay (in milliseconds) used for exponential backoff between retries. Defaults to 1000 milliseconds. + */ + public AsyncDeepgramApiClientBuilder initialRetryDelayMillis(long initialRetryDelayMillis) { + this.initialRetryDelayMillis = Optional.of(initialRetryDelayMillis); + return this; + } + + /** + * Sets the maximum delay (in milliseconds) between retries. Defaults to 60000 milliseconds. + */ + public AsyncDeepgramApiClientBuilder maxRetryDelayMillis(long maxRetryDelayMillis) { + this.maxRetryDelayMillis = Optional.of(maxRetryDelayMillis); + return this; + } + + /** + * Sets the jitter factor (between 0 and 1) applied to retry delays. Defaults to 0.2. + */ + public AsyncDeepgramApiClientBuilder retryJitterFactor(double retryJitterFactor) { + this.retryJitterFactor = Optional.of(retryJitterFactor); + return this; + } + /** * Sets the underlying OkHttp client */ @@ -126,7 +156,9 @@ protected void setEnvironment(ClientOptions.Builder builder) { * } */ protected void setAuthentication(ClientOptions.Builder builder) { - builder.addHeader("Authorization", "Token " + this.apiKey); + if (this.apiKey != null) { + builder.addHeader("Authorization", "Token " + this.apiKey); + } } /** @@ -151,6 +183,15 @@ protected void setRetries(ClientOptions.Builder builder) { if (this.maxRetries.isPresent()) { builder.maxRetries(this.maxRetries.get()); } + if (this.initialRetryDelayMillis.isPresent()) { + builder.initialRetryDelayMillis(this.initialRetryDelayMillis.get()); + } + if (this.maxRetryDelayMillis.isPresent()) { + builder.maxRetryDelayMillis(this.maxRetryDelayMillis.get()); + } + if (this.retryJitterFactor.isPresent()) { + builder.retryJitterFactor(this.retryJitterFactor.get()); + } } /** diff --git a/src/main/java/com/deepgram/DeepgramApiClientBuilder.java b/src/main/java/com/deepgram/DeepgramApiClientBuilder.java index b6060d1..5d61fbd 100644 --- a/src/main/java/com/deepgram/DeepgramApiClientBuilder.java +++ b/src/main/java/com/deepgram/DeepgramApiClientBuilder.java @@ -16,6 +16,12 @@ public class DeepgramApiClientBuilder { private Optional maxRetries = Optional.empty(); + private Optional initialRetryDelayMillis = Optional.empty(); + + private Optional maxRetryDelayMillis = Optional.empty(); + + private Optional retryJitterFactor = Optional.empty(); + private final Map customHeaders = new HashMap<>(); private String apiKey = System.getenv("DEEPGRAM_API_KEY"); @@ -56,6 +62,30 @@ public DeepgramApiClientBuilder maxRetries(int maxRetries) { return this; } + /** + * Sets the initial delay (in milliseconds) used for exponential backoff between retries. Defaults to 1000 milliseconds. + */ + public DeepgramApiClientBuilder initialRetryDelayMillis(long initialRetryDelayMillis) { + this.initialRetryDelayMillis = Optional.of(initialRetryDelayMillis); + return this; + } + + /** + * Sets the maximum delay (in milliseconds) between retries. Defaults to 60000 milliseconds. + */ + public DeepgramApiClientBuilder maxRetryDelayMillis(long maxRetryDelayMillis) { + this.maxRetryDelayMillis = Optional.of(maxRetryDelayMillis); + return this; + } + + /** + * Sets the jitter factor (between 0 and 1) applied to retry delays. Defaults to 0.2. + */ + public DeepgramApiClientBuilder retryJitterFactor(double retryJitterFactor) { + this.retryJitterFactor = Optional.of(retryJitterFactor); + return this; + } + /** * Sets the underlying OkHttp client */ @@ -126,7 +156,9 @@ protected void setEnvironment(ClientOptions.Builder builder) { * } */ protected void setAuthentication(ClientOptions.Builder builder) { - builder.addHeader("Authorization", "Token " + this.apiKey); + if (this.apiKey != null) { + builder.addHeader("Authorization", "Token " + this.apiKey); + } } /** @@ -151,6 +183,15 @@ protected void setRetries(ClientOptions.Builder builder) { if (this.maxRetries.isPresent()) { builder.maxRetries(this.maxRetries.get()); } + if (this.initialRetryDelayMillis.isPresent()) { + builder.initialRetryDelayMillis(this.initialRetryDelayMillis.get()); + } + if (this.maxRetryDelayMillis.isPresent()) { + builder.maxRetryDelayMillis(this.maxRetryDelayMillis.get()); + } + if (this.retryJitterFactor.isPresent()) { + builder.retryJitterFactor(this.retryJitterFactor.get()); + } } /** diff --git a/src/main/java/com/deepgram/core/ClientOptions.java b/src/main/java/com/deepgram/core/ClientOptions.java index 021caba..e2a76ff 100644 --- a/src/main/java/com/deepgram/core/ClientOptions.java +++ b/src/main/java/com/deepgram/core/ClientOptions.java @@ -23,6 +23,12 @@ public final class ClientOptions { private final int maxRetries; + private final Optional initialRetryDelayMillis; + + private final Optional maxRetryDelayMillis; + + private final Optional retryJitterFactor; + private final Optional webSocketFactory; private final Optional logging; @@ -34,6 +40,9 @@ private ClientOptions( OkHttpClient httpClient, int timeout, int maxRetries, + Optional initialRetryDelayMillis, + Optional maxRetryDelayMillis, + Optional retryJitterFactor, Optional webSocketFactory, Optional logging) { this.environment = environment; @@ -41,16 +50,19 @@ private ClientOptions( this.headers.putAll(headers); this.headers.putAll(new HashMap() { { - put("User-Agent", "com.deepgram:deepgram-java-sdk/0.7.1"); // 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.7.1"); // x-release-please-version + put("X-Fern-SDK-Version", getSdkVersion()); } }); this.headerSuppliers = headerSuppliers; this.httpClient = httpClient; this.timeout = timeout; this.maxRetries = maxRetries; + this.initialRetryDelayMillis = initialRetryDelayMillis; + this.maxRetryDelayMillis = maxRetryDelayMillis; + this.retryJitterFactor = retryJitterFactor; this.webSocketFactory = webSocketFactory; this.logging = logging; } @@ -70,6 +82,11 @@ public Map headers(RequestOptions requestOptions) { return values; } + private static String getSdkVersion() { + String version = ClientOptions.class.getPackage().getImplementationVersion(); + return version != null ? version : "0.7.2"; + } + public int timeout(RequestOptions requestOptions) { if (requestOptions == null) { return this.timeout; @@ -98,6 +115,18 @@ public int maxRetries() { return this.maxRetries; } + public Optional initialRetryDelayMillis() { + return this.initialRetryDelayMillis; + } + + public Optional maxRetryDelayMillis() { + return this.maxRetryDelayMillis; + } + + public Optional retryJitterFactor() { + return this.retryJitterFactor; + } + public Optional webSocketFactory() { return this.webSocketFactory; } @@ -119,6 +148,12 @@ public static class Builder { 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; @@ -133,7 +168,9 @@ public Builder environment(Environment environment) { } public Builder addHeader(String key, String value) { - this.headers.put(key, value); + if (value != null) { + this.headers.put(key, value); + } return this; } @@ -166,6 +203,30 @@ public Builder maxRetries(int 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; + } + public Builder httpClient(OkHttpClient httpClient) { this.httpClient = httpClient; return this; @@ -203,11 +264,16 @@ public ClientOptions build() { .connectTimeout(0, TimeUnit.SECONDS) .writeTimeout(0, TimeUnit.SECONDS) .readTimeout(0, TimeUnit.SECONDS) - .addInterceptor(new RetryInterceptor(this.maxRetries)); + .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); @@ -219,6 +285,9 @@ public ClientOptions build() { httpClient, this.timeout.get(), this.maxRetries, + this.initialRetryDelayMillis, + this.maxRetryDelayMillis, + this.retryJitterFactor, this.webSocketFactory, this.logging); } @@ -234,6 +303,9 @@ public static Builder from(ClientOptions clientOptions) { 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/ConsoleLogger.java b/src/main/java/com/deepgram/core/ConsoleLogger.java index a0b46a9..c96f3ac 100644 --- a/src/main/java/com/deepgram/core/ConsoleLogger.java +++ b/src/main/java/com/deepgram/core/ConsoleLogger.java @@ -23,6 +23,7 @@ public String format(java.util.logging.LogRecord record) { return record.getLevel() + " - " + record.getMessage() + System.lineSeparator(); } }); + handler.setLevel(Level.ALL); logger.addHandler(handler); logger.setUseParentHandlers(false); logger.setLevel(Level.ALL); diff --git a/src/main/java/com/deepgram/core/DateTimeDeserializer.java b/src/main/java/com/deepgram/core/DateTimeDeserializer.java index 0a8e0b6..d7f66b5 100644 --- a/src/main/java/com/deepgram/core/DateTimeDeserializer.java +++ b/src/main/java/com/deepgram/core/DateTimeDeserializer.java @@ -19,7 +19,8 @@ import java.time.temporal.TemporalQueries; /** - * Custom deserializer that handles converting ISO8601 dates into {@link OffsetDateTime} objects. + * Custom deserializer that handles converting date-time strings into {@link OffsetDateTime} objects. + * Supports ISO 8601 format, space-separated variants, and RFC 1123 (RFC 2822) format. */ class DateTimeDeserializer extends JsonDeserializer { private static final SimpleModule MODULE; @@ -48,9 +49,15 @@ public OffsetDateTime deserialize(JsonParser parser, DeserializationContext cont try { temporal = DateTimeFormatter.ISO_DATE_TIME.parseBest(value, OffsetDateTime::from, LocalDateTime::from); } catch (DateTimeParseException e) { - // Fall back to space-separated format (e.g. "2025-02-15 10:30:00+00:00"). - temporal = DateTimeFormatter.ISO_DATE_TIME.parseBest( - value.replace(' ', 'T'), OffsetDateTime::from, LocalDateTime::from); + try { + // Fall back to space-separated format (e.g. "2025-02-15 10:30:00+00:00"). + temporal = DateTimeFormatter.ISO_DATE_TIME.parseBest( + value.replace(' ', 'T'), OffsetDateTime::from, LocalDateTime::from); + } catch (DateTimeParseException e2) { + // Fall back to RFC 1123 format (e.g. "Thu, 07 May 2026 14:23:38 +0000"). + temporal = DateTimeFormatter.RFC_1123_DATE_TIME.parseBest( + value, OffsetDateTime::from, LocalDateTime::from); + } } if (temporal.query(TemporalQueries.offset()) == null) { diff --git a/src/main/java/com/deepgram/core/ReconnectingWebSocketListener.java b/src/main/java/com/deepgram/core/ReconnectingWebSocketListener.java index a533068..0ca455a 100644 --- a/src/main/java/com/deepgram/core/ReconnectingWebSocketListener.java +++ b/src/main/java/com/deepgram/core/ReconnectingWebSocketListener.java @@ -25,15 +25,14 @@ * Provides production-ready resilience for WebSocket connections. */ public abstract class ReconnectingWebSocketListener extends WebSocketListener { - // Overridable options are held behind a single volatile reference (not five separate volatile - // fields) so {@link #applyOptionsOverride} swaps them atomically — a reader that snapshots the - // reference once sees a mutually-consistent set (e.g. getNextDelay() cannot observe a new min - // paired with an old max). Rewiring is used by {@code TransportWebSocketFactory} to honour - // {@code DeepgramTransportFactory.reconnectOptions()} without editing the generated WS clients. - private volatile ReconnectOptions activeOptions; - - // maxEnqueuedMessages is fixed at construction (the queue is sized once) and intentionally not - // overridable, so it stays a separate final field rather than being read from activeOptions. + private final long minReconnectionDelayMs; + + private final long maxReconnectionDelayMs; + + private final double reconnectionDelayGrowFactor; + + private final int maxRetries; + private final int maxEnqueuedMessages; private final AtomicInteger retryCount = new AtomicInteger(0); @@ -62,42 +61,21 @@ public abstract class ReconnectingWebSocketListener extends WebSocketListener { */ public ReconnectingWebSocketListener( ReconnectingWebSocketListener.ReconnectOptions options, Supplier connectionSupplier) { - 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; } - /** - * Replaces the option-derived parameters on this listener at runtime. Used by - * {@code TransportWebSocketFactory} to apply {@code DeepgramTransportFactory.reconnectOptions()} - * without requiring edits to the generated per-resource WebSocket clients. {@code maxEnqueuedMessages} - * is intentionally not overridden — the message queue is sized at construction. - * - *

Thread-safety: the override is a single atomic write to a {@code volatile} reference, so a - * reader that snapshots {@link #activeOptions} once sees a mutually-consistent set of values - * (never a new min paired with an old max). The initial connect() call may have already started - * before the override lands, so for the very first attempt the original options apply; the - * override takes effect from the next attempt onwards. For the SageMaker storm-suppression case - * ({@code maxRetries(0)}) this is fine because the initial attempt's gate - * ({@code retryCount > maxRetries} with {@code retryCount=0}) always passes regardless. - * - * @param options replacement options; {@code null} is a no-op. - */ - public void applyOptionsOverride(ReconnectOptions options) { - if (options == null) { - return; - } - 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 — the initial attempt always proceeds. - * {@code maxRetries(0)} means "connect once, don't retry" (not "refuse to connect"). + * - Retry count not incremented for initial connection attempt * * Error handling: * - TimeoutException: Includes retry attempt context @@ -108,26 +86,20 @@ public void connect() { if (!connectLock.compareAndSet(false, true)) { return; } - // Snapshot the overridable options once so the gate and timeout below use a consistent set. - ReconnectOptions opts = this.activeOptions; - // retryCount is incremented inside scheduleReconnect() before re-entering connect(), - // so on the initial call retryCount == 0 and we always proceed. The cap applies to - // retries only — maxRetries(0) blocks retries but allows the initial attempt. - if (retryCount.get() > opts.maxRetries) { + if (retryCount.get() >= maxRetries) { connectLock.set(false); return; } try { CompletableFuture connectionFuture = CompletableFuture.supplyAsync(connectionSupplier); try { - webSocket = connectionFuture.get(opts.connectionTimeoutMs, MILLISECONDS); + webSocket = connectionFuture.get(4000, MILLISECONDS); } catch (TimeoutException e) { connectionFuture.cancel(true); TimeoutException timeoutError = - new TimeoutException("WebSocket connection timeout after " + opts.connectionTimeoutMs - + " milliseconds" + new TimeoutException("WebSocket connection timeout after " + 4000 + " milliseconds" + (retryCount.get() > 0 - ? " (retry attempt #" + retryCount.get() + ")" + ? " (retry attempt #" + retryCount.get() : " (initial connection attempt)")); onWebSocketFailure(null, timeoutError, null); if (shouldReconnect.get()) { @@ -342,14 +314,11 @@ public void onClosed(WebSocket webSocket, int code, String reason) { * - 2+ = exponential backoff up to maxReconnectionDelayMs */ private long getNextDelay() { - // Single volatile read → a consistent (min, growFactor, max) snapshot even if - // applyOptionsOverride swaps the options concurrently. - ReconnectOptions opts = this.activeOptions; if (retryCount.get() == 1) { - return opts.minReconnectionDelayMs; + return minReconnectionDelayMs; } - long delay = (long) (opts.minReconnectionDelayMs * Math.pow(opts.reconnectionDelayGrowFactor, retryCount.get() - 1)); - return Math.min(delay, opts.maxReconnectionDelayMs); + long delay = (long) (minReconnectionDelayMs * Math.pow(reconnectionDelayGrowFactor, retryCount.get() - 1)); + return Math.min(delay, maxReconnectionDelayMs); } /** @@ -430,15 +399,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() { @@ -456,15 +422,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) { @@ -492,16 +455,6 @@ public Builder maxEnqueuedMessages(int maxEnqueuedMessages) { return this; } - /** - * Sets the per-attempt connection timeout in milliseconds. Defaults to {@code 4000}. - * Each call to {@link ReconnectingWebSocketListener#connect()} will wait at most - * this long for the underlying WebSocket factory to produce a connected socket. - */ - public Builder connectionTimeoutMs(long connectionTimeoutMs) { - this.connectionTimeoutMs = connectionTimeoutMs; - return this; - } - /** * Builds the ReconnectOptions with validation. * @@ -510,7 +463,6 @@ public Builder connectionTimeoutMs(long connectionTimeoutMs) { * - minReconnectionDelayMs <= maxReconnectionDelayMs * - reconnectionDelayGrowFactor >= 1.0 * - maxRetries and maxEnqueuedMessages are non-negative - * - connectionTimeoutMs is positive * * @return The validated ReconnectOptions instance * @throws IllegalArgumentException if configuration is invalid @@ -535,9 +487,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/core/RequestOptions.java b/src/main/java/com/deepgram/core/RequestOptions.java index d0e615a..0e70641 100644 --- a/src/main/java/com/deepgram/core/RequestOptions.java +++ b/src/main/java/com/deepgram/core/RequestOptions.java @@ -16,6 +16,8 @@ public final class RequestOptions { private final TimeUnit timeoutTimeUnit; + private final Optional maxRetries; + private final Map headers; private final Map> headerSuppliers; @@ -28,6 +30,7 @@ private RequestOptions( String apiKey, Optional timeout, TimeUnit timeoutTimeUnit, + Optional maxRetries, Map headers, Map> headerSuppliers, Map queryParameters, @@ -35,6 +38,7 @@ private RequestOptions( this.apiKey = apiKey; this.timeout = timeout; this.timeoutTimeUnit = timeoutTimeUnit; + this.maxRetries = maxRetries; this.headers = headers; this.headerSuppliers = headerSuppliers; this.queryParameters = queryParameters; @@ -49,6 +53,10 @@ public TimeUnit getTimeoutTimeUnit() { return timeoutTimeUnit; } + public Optional getMaxRetries() { + return maxRetries; + } + public Map getHeaders() { Map headers = new HashMap<>(); if (this.apiKey != null) { @@ -80,6 +88,8 @@ public static class Builder { private TimeUnit timeoutTimeUnit = TimeUnit.SECONDS; + private Optional maxRetries = Optional.empty(); + private final Map headers = new HashMap<>(); private final Map> headerSuppliers = new HashMap<>(); @@ -104,6 +114,11 @@ public Builder timeout(Integer timeout, TimeUnit timeoutTimeUnit) { return this; } + public Builder maxRetries(Integer maxRetries) { + this.maxRetries = Optional.of(maxRetries); + return this; + } + public Builder addHeader(String key, String value) { this.headers.put(key, value); return this; @@ -129,6 +144,7 @@ public RequestOptions build() { apiKey, timeout, timeoutTimeUnit, + maxRetries, headers, headerSuppliers, queryParameters, diff --git a/src/main/java/com/deepgram/core/ResponseDecompressionInterceptor.java b/src/main/java/com/deepgram/core/ResponseDecompressionInterceptor.java new file mode 100644 index 0000000..f49b998 --- /dev/null +++ b/src/main/java/com/deepgram/core/ResponseDecompressionInterceptor.java @@ -0,0 +1,56 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ +package com.deepgram.core; + +import java.io.IOException; +import java.util.zip.Inflater; +import okhttp3.Interceptor; +import okhttp3.Response; +import okhttp3.ResponseBody; +import okio.GzipSource; +import okio.InflaterSource; +import okio.Okio; +import okio.Source; + +/** + * Decompresses gzip and deflate encoded response bodies. OkHttp only performs + * transparent decompression when it adds the Accept-Encoding header itself, so + * responses to requests with an explicitly set Accept-Encoding header would + * otherwise be returned as raw compressed bytes. + */ +public class ResponseDecompressionInterceptor implements Interceptor { + + @Override + public Response intercept(Chain chain) throws IOException { + return decompress(chain.proceed(chain.request())); + } + + private Response decompress(Response response) { + ResponseBody body = response.body(); + if (body == null) { + return response; + } + String encoding = response.header("Content-Encoding"); + if (encoding == null) { + return response; + } + Source decompressedSource; + if (encoding.equalsIgnoreCase("gzip") || encoding.equalsIgnoreCase("x-gzip")) { + decompressedSource = new GzipSource(body.source()); + } else if (encoding.equalsIgnoreCase("deflate")) { + decompressedSource = new InflaterSource(body.source(), new Inflater()); + } else { + return response; + } + ResponseBody decompressedBody = ResponseBody.create(body.contentType(), -1L, Okio.buffer(decompressedSource)); + return response.newBuilder() + .headers(response.headers() + .newBuilder() + .removeAll("Content-Encoding") + .removeAll("Content-Length") + .build()) + .body(decompressedBody) + .build(); + } +} diff --git a/src/main/java/com/deepgram/core/RetryInterceptor.java b/src/main/java/com/deepgram/core/RetryInterceptor.java index 91e8e26..a0959bb 100644 --- a/src/main/java/com/deepgram/core/RetryInterceptor.java +++ b/src/main/java/com/deepgram/core/RetryInterceptor.java @@ -11,34 +11,71 @@ import java.util.Optional; import java.util.Random; import okhttp3.Interceptor; +import okhttp3.Request; import okhttp3.Response; public class RetryInterceptor implements Interceptor { - private static final Duration INITIAL_RETRY_DELAY = Duration.ofMillis(1000); - private static final Duration MAX_RETRY_DELAY = Duration.ofMillis(60000); - private static final double JITTER_FACTOR = 0.2; + private static final Duration DEFAULT_INITIAL_RETRY_DELAY = Duration.ofMillis(1000); + private static final Duration DEFAULT_MAX_RETRY_DELAY = Duration.ofMillis(60000); + private static final double DEFAULT_JITTER_FACTOR = 0.2; private final int maxRetries; + private final Duration initialRetryDelay; + private final Duration maxRetryDelay; + private final double jitterFactor; private final Random random = new Random(); public RetryInterceptor(int maxRetries) { + this(maxRetries, Optional.empty(), Optional.empty(), Optional.empty()); + } + + public RetryInterceptor( + int maxRetries, + Optional initialRetryDelayMillis, + Optional maxRetryDelayMillis, + Optional jitterFactor) { + initialRetryDelayMillis.ifPresent(delay -> { + if (delay < 0) { + throw new IllegalArgumentException("initialRetryDelayMillis must be non-negative"); + } + }); + maxRetryDelayMillis.ifPresent(delay -> { + if (delay < 0) { + throw new IllegalArgumentException("maxRetryDelayMillis must be non-negative"); + } + }); + jitterFactor.ifPresent(factor -> { + if (factor < 0 || factor > 1) { + throw new IllegalArgumentException("jitterFactor must be between 0 and 1"); + } + }); this.maxRetries = maxRetries; + this.initialRetryDelay = initialRetryDelayMillis.map(Duration::ofMillis).orElse(DEFAULT_INITIAL_RETRY_DELAY); + this.maxRetryDelay = maxRetryDelayMillis.map(Duration::ofMillis).orElse(DEFAULT_MAX_RETRY_DELAY); + this.jitterFactor = jitterFactor.orElse(DEFAULT_JITTER_FACTOR); } @Override public Response intercept(Chain chain) throws IOException { - Response response = chain.proceed(chain.request()); + Request request = chain.request(); + int effectiveMaxRetries = resolveMaxRetries(request); + Response response = chain.proceed(request); if (shouldRetry(response.code())) { - return retryChain(response, chain); + return retryChain(response, chain, effectiveMaxRetries); } return response; } - private Response retryChain(Response response, Chain chain) throws IOException { - ExponentialBackoff backoff = new ExponentialBackoff(this.maxRetries); + private int resolveMaxRetries(Request request) { + MaxRetriesOverride override = request.tag(MaxRetriesOverride.class); + return override != null ? override.getValue() : this.maxRetries; + } + + private Response retryChain(Response response, Chain chain, int maxRetries) throws IOException { + ExponentialBackoff backoff = new ExponentialBackoff(maxRetries); Optional nextBackoff = backoff.nextBackoff(response); while (nextBackoff.isPresent()) { try { @@ -70,7 +107,7 @@ private Duration getRetryDelayFromHeaders(Response response, int retryAttempt) { Optional secondsDelay = tryParseLong(retryAfter) .map(seconds -> seconds * 1000) .filter(delayMs -> delayMs > 0) - .map(delayMs -> Math.min(delayMs, MAX_RETRY_DELAY.toMillis())) + .map(delayMs -> Math.min(delayMs, maxRetryDelay.toMillis())) .map(Duration::ofMillis); if (secondsDelay.isPresent()) { return secondsDelay.get(); @@ -80,7 +117,7 @@ private Duration getRetryDelayFromHeaders(Response response, int retryAttempt) { Optional dateDelay = tryParseHttpDate(retryAfter) .map(resetTime -> resetTime.toInstant().toEpochMilli() - System.currentTimeMillis()) .filter(delayMs -> delayMs > 0) - .map(delayMs -> Math.min(delayMs, MAX_RETRY_DELAY.toMillis())) + .map(delayMs -> Math.min(delayMs, maxRetryDelay.toMillis())) .map(Duration::ofMillis); if (dateDelay.isPresent()) { return dateDelay.get(); @@ -94,7 +131,7 @@ private Duration getRetryDelayFromHeaders(Response response, int retryAttempt) { Optional rateLimitDelay = tryParseLong(rateLimitReset) .map(resetTimeSeconds -> (resetTimeSeconds * 1000) - System.currentTimeMillis()) .filter(delayMs -> delayMs > 0) - .map(delayMs -> Math.min(delayMs, MAX_RETRY_DELAY.toMillis())) + .map(delayMs -> Math.min(delayMs, maxRetryDelay.toMillis())) .map(this::addPositiveJitter) .map(Duration::ofMillis); if (rateLimitDelay.isPresent()) { @@ -103,8 +140,15 @@ private Duration getRetryDelayFromHeaders(Response response, int retryAttempt) { } // Fall back to exponential backoff, with symmetric jitter - long baseDelay = INITIAL_RETRY_DELAY.toMillis() * (1L << retryAttempt); // 2^retryAttempt - long cappedDelay = Math.min(baseDelay, MAX_RETRY_DELAY.toMillis()); + long initialDelayMillis = initialRetryDelay.toMillis(); + long maxDelayMillis = maxRetryDelay.toMillis(); + long cappedDelay; + if (retryAttempt >= Long.SIZE - 1 || initialDelayMillis > (maxDelayMillis >> retryAttempt)) { + // initialDelayMillis * 2^retryAttempt would exceed maxDelayMillis (or overflow) + cappedDelay = maxDelayMillis; + } else { + cappedDelay = Math.min(initialDelayMillis << retryAttempt, maxDelayMillis); // 2^retryAttempt + } return Duration.ofMillis(addSymmetricJitter(cappedDelay)); } @@ -141,7 +185,7 @@ private Optional tryParseHttpDate(String value) { * Used for X-RateLimit-Reset header delays. */ private long addPositiveJitter(long delayMs) { - double jitterMultiplier = 1.0 + (random.nextDouble() * JITTER_FACTOR); + double jitterMultiplier = 1.0 + (random.nextDouble() * jitterFactor); return (long) (delayMs * jitterMultiplier); } @@ -150,7 +194,7 @@ private long addPositiveJitter(long delayMs) { * Used for exponential backoff delays. */ private long addSymmetricJitter(long delayMs) { - double jitterMultiplier = 1.0 + ((random.nextDouble() - 0.5) * JITTER_FACTOR); + double jitterMultiplier = 1.0 + ((random.nextDouble() - 0.5) * jitterFactor); return (long) (delayMs * jitterMultiplier); } @@ -158,6 +202,23 @@ private static boolean shouldRetry(int statusCode) { return statusCode == 408 || statusCode == 429 || statusCode >= 500; } + /** + * Per-request override carried on the OkHttp {@link Request} as a tag. + * When present, the interceptor uses this value instead of the client-wide + * {@code maxRetries} configured at construction time. + */ + public static final class MaxRetriesOverride { + private final int value; + + public MaxRetriesOverride(int value) { + this.value = value; + } + + public int getValue() { + return value; + } + } + private final class ExponentialBackoff { private final int maxNumRetries; diff --git a/src/main/java/com/deepgram/resources/agent/v1/settings/think/models/AsyncRawModelsClient.java b/src/main/java/com/deepgram/resources/agent/v1/settings/think/models/AsyncRawModelsClient.java index cb35a9a..a2c304c 100644 --- a/src/main/java/com/deepgram/resources/agent/v1/settings/think/models/AsyncRawModelsClient.java +++ b/src/main/java/com/deepgram/resources/agent/v1/settings/think/models/AsyncRawModelsClient.java @@ -9,6 +9,7 @@ import com.deepgram.core.DeepgramHttpException; import com.deepgram.core.ObjectMappers; import com.deepgram.core.RequestOptions; +import com.deepgram.core.RetryInterceptor; import com.deepgram.errors.BadRequestError; import com.deepgram.types.AgentThinkModelsV1Response; import com.fasterxml.jackson.core.JsonProcessingException; @@ -60,6 +61,15 @@ public CompletableFuture> li if (requestOptions != null && requestOptions.getTimeout().isPresent()) { client = clientOptions.httpClientWithTimeout(requestOptions); } + if (requestOptions != null && requestOptions.getMaxRetries().isPresent()) { + okhttpRequest = okhttpRequest + .newBuilder() + .tag( + RetryInterceptor.MaxRetriesOverride.class, + new RetryInterceptor.MaxRetriesOverride( + requestOptions.getMaxRetries().get())) + .build(); + } CompletableFuture> future = new CompletableFuture<>(); client.newCall(okhttpRequest).enqueue(new Callback() { @Override @@ -86,6 +96,9 @@ public void onResponse(@NotNull Call call, @NotNull Response response) throws IO future.completeExceptionally(new DeepgramHttpException( "Error with status code " + response.code(), response.code(), errorBody, response)); return; + } catch (JsonProcessingException e) { + future.completeExceptionally( + new DeepgramApiException("Failed to deserialize response: " + e.getMessage(), e)); } catch (IOException e) { future.completeExceptionally(new DeepgramApiException("Network error executing HTTP request", e)); } diff --git a/src/main/java/com/deepgram/resources/agent/v1/settings/think/models/RawModelsClient.java b/src/main/java/com/deepgram/resources/agent/v1/settings/think/models/RawModelsClient.java index 51a486f..f22e179 100644 --- a/src/main/java/com/deepgram/resources/agent/v1/settings/think/models/RawModelsClient.java +++ b/src/main/java/com/deepgram/resources/agent/v1/settings/think/models/RawModelsClient.java @@ -9,6 +9,7 @@ import com.deepgram.core.DeepgramHttpException; import com.deepgram.core.ObjectMappers; import com.deepgram.core.RequestOptions; +import com.deepgram.core.RetryInterceptor; import com.deepgram.errors.BadRequestError; import com.deepgram.types.AgentThinkModelsV1Response; import com.fasterxml.jackson.core.JsonProcessingException; @@ -56,6 +57,15 @@ public DeepgramApiHttpResponse list(RequestOptions r if (requestOptions != null && requestOptions.getTimeout().isPresent()) { client = clientOptions.httpClientWithTimeout(requestOptions); } + if (requestOptions != null && requestOptions.getMaxRetries().isPresent()) { + okhttpRequest = okhttpRequest + .newBuilder() + .tag( + RetryInterceptor.MaxRetriesOverride.class, + new RetryInterceptor.MaxRetriesOverride( + requestOptions.getMaxRetries().get())) + .build(); + } try (Response response = client.newCall(okhttpRequest).execute()) { ResponseBody responseBody = response.body(); String responseBodyString = responseBody != null ? responseBody.string() : "{}"; @@ -75,6 +85,8 @@ public DeepgramApiHttpResponse list(RequestOptions r Object errorBody = ObjectMappers.parseErrorBody(responseBodyString); throw new DeepgramHttpException( "Error with status code " + response.code(), response.code(), errorBody, response); + } catch (JsonProcessingException e) { + throw new DeepgramApiException("Failed to deserialize response: " + e.getMessage(), e); } catch (IOException e) { throw new DeepgramApiException("Network error executing HTTP request", e); } 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 41af522..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,14 +36,6 @@ public boolean equals(Object other) { return other instanceof AgentV1AgentAudioDone; } - @java.lang.Override - public int hashCode() { - // Manual patch: Fern generates equals() (all instances of this fields-less message - // are equal) but no hashCode(), violating the Object contract. Mirror equals() with a - // type-based constant hash. Remove once Fern emits a consistent equals/hashCode pair. - return getClass().hashCode(); - } - @JsonAnyGetter public Map getAdditionalProperties() { return this.additionalProperties; diff --git a/src/main/java/com/deepgram/resources/agent/v1/types/AgentV1AgentStartedSpeaking.java b/src/main/java/com/deepgram/resources/agent/v1/types/AgentV1AgentStartedSpeaking.java index 65c5d4e..42d8e69 100644 --- a/src/main/java/com/deepgram/resources/agent/v1/types/AgentV1AgentStartedSpeaking.java +++ b/src/main/java/com/deepgram/resources/agent/v1/types/AgentV1AgentStartedSpeaking.java @@ -148,7 +148,6 @@ public Builder from(AgentV1AgentStartedSpeaking other) { } /** - *

Seconds from receiving the user's utterance to producing the agent's reply

*

Seconds from receiving the user's utterance to producing the agent's reply

* @return Reference to {@code this} so that method calls can be chained together. */ @@ -160,7 +159,6 @@ public TtsLatencyStage totalLatency(float totalLatency) { } /** - *

The portion of total latency attributable to text-to-speech

*

The portion of total latency attributable to text-to-speech

* @return Reference to {@code this} so that method calls can be chained together. */ @@ -172,7 +170,6 @@ public TttLatencyStage ttsLatency(float ttsLatency) { } /** - *

The portion of total latency attributable to text-to-text (usually an LLM)

*

The portion of total latency attributable to text-to-text (usually an LLM)

* @return Reference to {@code this} so that method calls can be chained together. */ diff --git a/src/main/java/com/deepgram/resources/agent/v1/types/AgentV1AgentThinking.java b/src/main/java/com/deepgram/resources/agent/v1/types/AgentV1AgentThinking.java index b98b6b5..60b37dd 100644 --- a/src/main/java/com/deepgram/resources/agent/v1/types/AgentV1AgentThinking.java +++ b/src/main/java/com/deepgram/resources/agent/v1/types/AgentV1AgentThinking.java @@ -106,7 +106,6 @@ public Builder from(AgentV1AgentThinking other) { } /** - *

The text of the agent's thought process

*

The text of the agent's thought process

* @return Reference to {@code this} so that method calls can be chained together. */ diff --git a/src/main/java/com/deepgram/resources/agent/v1/types/AgentV1ConversationText.java b/src/main/java/com/deepgram/resources/agent/v1/types/AgentV1ConversationText.java index a15a427..6533a48 100644 --- a/src/main/java/com/deepgram/resources/agent/v1/types/AgentV1ConversationText.java +++ b/src/main/java/com/deepgram/resources/agent/v1/types/AgentV1ConversationText.java @@ -180,7 +180,6 @@ public Builder from(AgentV1ConversationText other) { } /** - *

Identifies who spoke the statement

*

Identifies who spoke the statement

* @return Reference to {@code this} so that method calls can be chained together. */ @@ -192,7 +191,6 @@ public ContentStage role(@NotNull AgentV1ConversationTextRole role) { } /** - *

The actual statement that was spoken

*

The actual statement that was spoken

* @return Reference to {@code this} so that method calls can be chained together. */ diff --git a/src/main/java/com/deepgram/resources/agent/v1/types/AgentV1Error.java b/src/main/java/com/deepgram/resources/agent/v1/types/AgentV1Error.java index aa97cc9..7ac10b8 100644 --- a/src/main/java/com/deepgram/resources/agent/v1/types/AgentV1Error.java +++ b/src/main/java/com/deepgram/resources/agent/v1/types/AgentV1Error.java @@ -127,7 +127,6 @@ public Builder from(AgentV1Error other) { } /** - *

A description of what went wrong

*

A description of what went wrong

* @return Reference to {@code this} so that method calls can be chained together. */ @@ -139,7 +138,6 @@ public CodeStage description(@NotNull String description) { } /** - *

Error code identifying the type of error

*

Error code identifying the type of error

* @return Reference to {@code this} so that method calls can be chained together. */ diff --git a/src/main/java/com/deepgram/resources/agent/v1/types/AgentV1FunctionCallRequestFunctionsItem.java b/src/main/java/com/deepgram/resources/agent/v1/types/AgentV1FunctionCallRequestFunctionsItem.java index d683b72..15267f3 100644 --- a/src/main/java/com/deepgram/resources/agent/v1/types/AgentV1FunctionCallRequestFunctionsItem.java +++ b/src/main/java/com/deepgram/resources/agent/v1/types/AgentV1FunctionCallRequestFunctionsItem.java @@ -195,7 +195,6 @@ public Builder from(AgentV1FunctionCallRequestFunctionsItem other) { } /** - *

Unique identifier for the function call

*

Unique identifier for the function call

* @return Reference to {@code this} so that method calls can be chained together. */ @@ -207,7 +206,6 @@ public NameStage id(@NotNull String id) { } /** - *

The name of the function to call

*

The name of the function to call

* @return Reference to {@code this} so that method calls can be chained together. */ @@ -219,7 +217,6 @@ public ArgumentsStage name(@NotNull String name) { } /** - *

JSON string containing the function arguments

*

JSON string containing the function arguments

* @return Reference to {@code this} so that method calls can be chained together. */ @@ -231,7 +228,6 @@ public ClientSideStage arguments(@NotNull String arguments) { } /** - *

Whether the function should be executed client-side

*

Whether the function should be executed client-side

* @return Reference to {@code this} so that method calls can be chained together. */ diff --git a/src/main/java/com/deepgram/resources/agent/v1/types/AgentV1HistoryFunctionCallsFunctionCallsItem.java b/src/main/java/com/deepgram/resources/agent/v1/types/AgentV1HistoryFunctionCallsFunctionCallsItem.java index 0a04bbf..4a45de4 100644 --- a/src/main/java/com/deepgram/resources/agent/v1/types/AgentV1HistoryFunctionCallsFunctionCallsItem.java +++ b/src/main/java/com/deepgram/resources/agent/v1/types/AgentV1HistoryFunctionCallsFunctionCallsItem.java @@ -219,7 +219,6 @@ public Builder from(AgentV1HistoryFunctionCallsFunctionCallsItem other) { } /** - *

Unique identifier for the function call

*

Unique identifier for the function call

* @return Reference to {@code this} so that method calls can be chained together. */ @@ -231,7 +230,6 @@ public NameStage id(@NotNull String id) { } /** - *

Name of the function called

*

Name of the function called

* @return Reference to {@code this} so that method calls can be chained together. */ @@ -243,7 +241,6 @@ public ClientSideStage name(@NotNull String name) { } /** - *

Indicates if the call was client-side or server-side

*

Indicates if the call was client-side or server-side

* @return Reference to {@code this} so that method calls can be chained together. */ @@ -255,7 +252,6 @@ public ArgumentsStage clientSide(boolean clientSide) { } /** - *

Arguments passed to the function

*

Arguments passed to the function

* @return Reference to {@code this} so that method calls can be chained together. */ @@ -267,7 +263,6 @@ public ResponseStage arguments(@NotNull String arguments) { } /** - *

Response from the function call

*

Response from the function call

* @return Reference to {@code this} so that method calls can be chained together. */ diff --git a/src/main/java/com/deepgram/resources/agent/v1/types/AgentV1InjectAgentMessage.java b/src/main/java/com/deepgram/resources/agent/v1/types/AgentV1InjectAgentMessage.java index 88be5ed..872f4fb 100644 --- a/src/main/java/com/deepgram/resources/agent/v1/types/AgentV1InjectAgentMessage.java +++ b/src/main/java/com/deepgram/resources/agent/v1/types/AgentV1InjectAgentMessage.java @@ -142,7 +142,6 @@ public Builder from(AgentV1InjectAgentMessage other) { } /** - *

The statement that the agent should say

*

The statement that the agent should say

* @return Reference to {@code this} so that method calls can be chained together. */ diff --git a/src/main/java/com/deepgram/resources/agent/v1/types/AgentV1InjectUserMessage.java b/src/main/java/com/deepgram/resources/agent/v1/types/AgentV1InjectUserMessage.java index 38696b6..f6d0aaf 100644 --- a/src/main/java/com/deepgram/resources/agent/v1/types/AgentV1InjectUserMessage.java +++ b/src/main/java/com/deepgram/resources/agent/v1/types/AgentV1InjectUserMessage.java @@ -106,7 +106,6 @@ public Builder from(AgentV1InjectUserMessage other) { } /** - *

The specific phrase or statement the agent should respond to

*

The specific phrase or statement the agent should respond to

* @return Reference to {@code this} so that method calls can be chained together. */ diff --git a/src/main/java/com/deepgram/resources/agent/v1/types/AgentV1InjectionRefused.java b/src/main/java/com/deepgram/resources/agent/v1/types/AgentV1InjectionRefused.java index 8a2250d..de789a2 100644 --- a/src/main/java/com/deepgram/resources/agent/v1/types/AgentV1InjectionRefused.java +++ b/src/main/java/com/deepgram/resources/agent/v1/types/AgentV1InjectionRefused.java @@ -106,7 +106,6 @@ public Builder from(AgentV1InjectionRefused other) { } /** - *

Details about why the injection was refused

*

Details about why the injection was refused

* @return Reference to {@code this} so that method calls can be chained together. */ 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 083578b..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,14 +36,6 @@ public boolean equals(Object other) { return other instanceof AgentV1KeepAlive; } - @java.lang.Override - public int hashCode() { - // Manual patch: Fern generates equals() (all instances of this fields-less message - // are equal) but no hashCode(), violating the Object contract. Mirror equals() with a - // type-based constant hash. Remove once Fern emits a consistent equals/hashCode pair. - return getClass().hashCode(); - } - @JsonAnyGetter public Map getAdditionalProperties() { return this.additionalProperties; diff --git a/src/main/java/com/deepgram/resources/agent/v1/types/AgentV1LatencyReport.java b/src/main/java/com/deepgram/resources/agent/v1/types/AgentV1LatencyReport.java index f6efcc4..39aecfb 100644 --- a/src/main/java/com/deepgram/resources/agent/v1/types/AgentV1LatencyReport.java +++ b/src/main/java/com/deepgram/resources/agent/v1/types/AgentV1LatencyReport.java @@ -20,6 +20,8 @@ @JsonInclude(JsonInclude.Include.NON_ABSENT) @JsonDeserialize(builder = AgentV1LatencyReport.Builder.class) public final class AgentV1LatencyReport { + private final Optional sttLatency; + private final Optional tttTokenLatency; private final Optional tttTextLatency; @@ -35,6 +37,7 @@ public final class AgentV1LatencyReport { private final Map additionalProperties; private AgentV1LatencyReport( + Optional sttLatency, Optional tttTokenLatency, Optional tttTextLatency, Optional tttToolLatency, @@ -42,6 +45,7 @@ private AgentV1LatencyReport( Optional ttsLatency, Optional totalLatency, Map additionalProperties) { + this.sttLatency = sttLatency; this.tttTokenLatency = tttTokenLatency; this.tttTextLatency = tttTextLatency; this.tttToolLatency = tttToolLatency; @@ -59,6 +63,14 @@ public String getType() { return "LatencyReport"; } + /** + * @return Speech-to-text: time from audio received to transcript produced, in seconds + */ + @JsonProperty("stt_latency") + public Optional getSttLatency() { + return sttLatency; + } + /** * @return Time to first token of any type (text, tool call, or thinking), in seconds */ @@ -119,7 +131,8 @@ public Map getAdditionalProperties() { } private boolean equalTo(AgentV1LatencyReport other) { - return tttTokenLatency.equals(other.tttTokenLatency) + return sttLatency.equals(other.sttLatency) + && tttTokenLatency.equals(other.tttTokenLatency) && tttTextLatency.equals(other.tttTextLatency) && tttToolLatency.equals(other.tttToolLatency) && tttThinkingLatency.equals(other.tttThinkingLatency) @@ -130,6 +143,7 @@ private boolean equalTo(AgentV1LatencyReport other) { @java.lang.Override public int hashCode() { return Objects.hash( + this.sttLatency, this.tttTokenLatency, this.tttTextLatency, this.tttToolLatency, @@ -149,6 +163,8 @@ public static Builder builder() { @JsonIgnoreProperties(ignoreUnknown = true) public static final class Builder { + private Optional sttLatency = Optional.empty(); + private Optional tttTokenLatency = Optional.empty(); private Optional tttTextLatency = Optional.empty(); @@ -167,6 +183,7 @@ public static final class Builder { private Builder() {} public Builder from(AgentV1LatencyReport other) { + sttLatency(other.getSttLatency()); tttTokenLatency(other.getTttTokenLatency()); tttTextLatency(other.getTttTextLatency()); tttToolLatency(other.getTttToolLatency()); @@ -176,6 +193,20 @@ public Builder from(AgentV1LatencyReport other) { return this; } + /** + *

Speech-to-text: time from audio received to transcript produced, in seconds

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

Time to first token of any type (text, tool call, or thinking), in seconds

*/ @@ -262,6 +293,7 @@ public Builder totalLatency(Float totalLatency) { public AgentV1LatencyReport build() { return new AgentV1LatencyReport( + sttLatency, tttTokenLatency, tttTextLatency, tttToolLatency, 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 6018473..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,14 +36,6 @@ public boolean equals(Object other) { return other instanceof AgentV1ListenUpdated; } - @java.lang.Override - public int hashCode() { - // Manual patch: Fern generates equals() (all instances of this fields-less message - // are equal) but no hashCode(), violating the Object contract. Mirror equals() with a - // type-based constant hash. Remove once Fern emits a consistent equals/hashCode pair. - 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 6edec37..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,14 +36,6 @@ public boolean equals(Object other) { return other instanceof AgentV1PromptUpdated; } - @java.lang.Override - public int hashCode() { - // Manual patch: Fern generates equals() (all instances of this fields-less message - // are equal) but no hashCode(), violating the Object contract. Mirror equals() with a - // type-based constant hash. Remove once Fern emits a consistent equals/hashCode pair. - return getClass().hashCode(); - } - @JsonAnyGetter public Map getAdditionalProperties() { return this.additionalProperties; diff --git a/src/main/java/com/deepgram/resources/agent/v1/types/AgentV1ReceiveFunctionCallResponse.java b/src/main/java/com/deepgram/resources/agent/v1/types/AgentV1ReceiveFunctionCallResponse.java index 17e67d8..1ee16f6 100644 --- a/src/main/java/com/deepgram/resources/agent/v1/types/AgentV1ReceiveFunctionCallResponse.java +++ b/src/main/java/com/deepgram/resources/agent/v1/types/AgentV1ReceiveFunctionCallResponse.java @@ -160,7 +160,6 @@ public Builder from(AgentV1ReceiveFunctionCallResponse other) { } /** - *

The name of the function being called

*

The name of the function being called

* @return Reference to {@code this} so that method calls can be chained together. */ @@ -172,7 +171,6 @@ public ContentStage name(@NotNull String name) { } /** - *

The content or result of the function call

*

The content or result of the function call

* @return Reference to {@code this} so that method calls can be chained together. */ diff --git a/src/main/java/com/deepgram/resources/agent/v1/types/AgentV1SendFunctionCallResponse.java b/src/main/java/com/deepgram/resources/agent/v1/types/AgentV1SendFunctionCallResponse.java index e89546b..4562fa9 100644 --- a/src/main/java/com/deepgram/resources/agent/v1/types/AgentV1SendFunctionCallResponse.java +++ b/src/main/java/com/deepgram/resources/agent/v1/types/AgentV1SendFunctionCallResponse.java @@ -159,7 +159,6 @@ public Builder from(AgentV1SendFunctionCallResponse other) { } /** - *

The name of the function being called

*

The name of the function being called

* @return Reference to {@code this} so that method calls can be chained together. */ @@ -171,7 +170,6 @@ public ContentStage name(@NotNull String name) { } /** - *

The content or result of the function call

*

The content or result of the function call

* @return Reference to {@code this} so that method calls can be chained together. */ diff --git a/src/main/java/com/deepgram/resources/agent/v1/types/AgentV1SettingsAgentContextContextMessagesItemFunctionCallsFunctionCallsItem.java b/src/main/java/com/deepgram/resources/agent/v1/types/AgentV1SettingsAgentContextContextMessagesItemFunctionCallsFunctionCallsItem.java index 441f770..d563b7e 100644 --- a/src/main/java/com/deepgram/resources/agent/v1/types/AgentV1SettingsAgentContextContextMessagesItemFunctionCallsFunctionCallsItem.java +++ b/src/main/java/com/deepgram/resources/agent/v1/types/AgentV1SettingsAgentContextContextMessagesItemFunctionCallsFunctionCallsItem.java @@ -219,7 +219,6 @@ public Builder from(AgentV1SettingsAgentContextContextMessagesItemFunctionCallsF } /** - *

Unique identifier for the function call

*

Unique identifier for the function call

* @return Reference to {@code this} so that method calls can be chained together. */ @@ -231,7 +230,6 @@ public NameStage id(@NotNull String id) { } /** - *

Name of the function called

*

Name of the function called

* @return Reference to {@code this} so that method calls can be chained together. */ @@ -243,7 +241,6 @@ public ClientSideStage name(@NotNull String name) { } /** - *

Indicates if the call was client-side or server-side

*

Indicates if the call was client-side or server-side

* @return Reference to {@code this} so that method calls can be chained together. */ @@ -255,7 +252,6 @@ public ArgumentsStage clientSide(boolean clientSide) { } /** - *

Arguments passed to the function

*

Arguments passed to the function

* @return Reference to {@code this} so that method calls can be chained together. */ @@ -267,7 +263,6 @@ public ResponseStage arguments(@NotNull String arguments) { } /** - *

Response from the function call

*

Response from the function call

* @return Reference to {@code this} so that method calls can be chained together. */ 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 fb23c60..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,14 +36,6 @@ public boolean equals(Object other) { return other instanceof AgentV1SettingsApplied; } - @java.lang.Override - public int hashCode() { - // Manual patch: Fern generates equals() (all instances of this fields-less message - // are equal) but no hashCode(), violating the Object contract. Mirror equals() with a - // type-based constant hash. Remove once Fern emits a consistent equals/hashCode pair. - return getClass().hashCode(); - } - @JsonAnyGetter public Map getAdditionalProperties() { return this.additionalProperties; diff --git a/src/main/java/com/deepgram/resources/agent/v1/types/AgentV1SettingsAudioInput.java b/src/main/java/com/deepgram/resources/agent/v1/types/AgentV1SettingsAudioInput.java index 714c93d..9e72cc6 100644 --- a/src/main/java/com/deepgram/resources/agent/v1/types/AgentV1SettingsAudioInput.java +++ b/src/main/java/com/deepgram/resources/agent/v1/types/AgentV1SettingsAudioInput.java @@ -120,7 +120,6 @@ public Builder from(AgentV1SettingsAudioInput other) { } /** - *

Audio encoding format

*

Audio encoding format

* @return Reference to {@code this} so that method calls can be chained together. */ @@ -132,7 +131,6 @@ public SampleRateStage encoding(@NotNull AgentV1SettingsAudioInputEncoding encod } /** - *

Sample rate in Hz. Common values are 16000, 24000, 44100, 48000

*

Sample rate in Hz. Common values are 16000, 24000, 44100, 48000

* @return Reference to {@code this} so that method calls can be chained together. */ 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 7619157..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,14 +36,6 @@ public boolean equals(Object other) { return other instanceof AgentV1SpeakUpdated; } - @java.lang.Override - public int hashCode() { - // Manual patch: Fern generates equals() (all instances of this fields-less message - // are equal) but no hashCode(), violating the Object contract. Mirror equals() with a - // type-based constant hash. Remove once Fern emits a consistent equals/hashCode pair. - 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 2f2a981..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,14 +36,6 @@ public boolean equals(Object other) { return other instanceof AgentV1ThinkUpdated; } - @java.lang.Override - public int hashCode() { - // Manual patch: Fern generates equals() (all instances of this fields-less message - // are equal) but no hashCode(), violating the Object contract. Mirror equals() with a - // type-based constant hash. Remove once Fern emits a consistent equals/hashCode pair. - return getClass().hashCode(); - } - @JsonAnyGetter public Map getAdditionalProperties() { return this.additionalProperties; diff --git a/src/main/java/com/deepgram/resources/agent/v1/types/AgentV1UpdateListen.java b/src/main/java/com/deepgram/resources/agent/v1/types/AgentV1UpdateListen.java index 432d72b..f1b18d9 100644 --- a/src/main/java/com/deepgram/resources/agent/v1/types/AgentV1UpdateListen.java +++ b/src/main/java/com/deepgram/resources/agent/v1/types/AgentV1UpdateListen.java @@ -37,7 +37,7 @@ public String getType() { } /** - * @return Listen configuration to update. Contains a provider object with the same schema as Settings. The provider identity (type, version, model) is required and must match the current session. + * @return Listen configuration to update. Contains a provider object with the same schema as Settings. The model and language can be changed mid-session. Keyterms can only be updated mid-session for Flux models. */ @JsonProperty("listen") public AgentV1UpdateListenListen getListen() { @@ -75,7 +75,7 @@ public static ListenStage builder() { public interface ListenStage { /** - *

Listen configuration to update. Contains a provider object with the same schema as Settings. The provider identity (type, version, model) is required and must match the current session.

+ *

Listen configuration to update. Contains a provider object with the same schema as Settings. The model and language can be changed mid-session. Keyterms can only be updated mid-session for Flux models.

*/ _FinalStage listen(@NotNull AgentV1UpdateListenListen listen); @@ -106,8 +106,7 @@ public Builder from(AgentV1UpdateListen other) { } /** - *

Listen configuration to update. Contains a provider object with the same schema as Settings. The provider identity (type, version, model) is required and must match the current session.

- *

Listen configuration to update. Contains a provider object with the same schema as Settings. The provider identity (type, version, model) is required and must match the current session.

+ *

Listen configuration to update. Contains a provider object with the same schema as Settings. The model and language can be changed mid-session. Keyterms can only be updated mid-session for Flux models.

* @return Reference to {@code this} so that method calls can be chained together. */ @java.lang.Override diff --git a/src/main/java/com/deepgram/resources/agent/v1/types/AgentV1UpdateListenListen.java b/src/main/java/com/deepgram/resources/agent/v1/types/AgentV1UpdateListenListen.java index c65548a..0acddc6 100644 --- a/src/main/java/com/deepgram/resources/agent/v1/types/AgentV1UpdateListenListen.java +++ b/src/main/java/com/deepgram/resources/agent/v1/types/AgentV1UpdateListenListen.java @@ -4,7 +4,6 @@ package com.deepgram.resources.agent.v1.types; import com.deepgram.core.ObjectMappers; -import com.deepgram.types.DeepgramListenProviderV2; import com.fasterxml.jackson.annotation.JsonAnyGetter; import com.fasterxml.jackson.annotation.JsonAnySetter; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; @@ -20,17 +19,18 @@ @JsonInclude(JsonInclude.Include.NON_ABSENT) @JsonDeserialize(builder = AgentV1UpdateListenListen.Builder.class) public final class AgentV1UpdateListenListen { - private final DeepgramListenProviderV2 provider; + private final AgentV1UpdateListenListenProvider provider; private final Map additionalProperties; - private AgentV1UpdateListenListen(DeepgramListenProviderV2 provider, Map additionalProperties) { + private AgentV1UpdateListenListen( + AgentV1UpdateListenListenProvider provider, Map additionalProperties) { this.provider = provider; this.additionalProperties = additionalProperties; } @JsonProperty("provider") - public DeepgramListenProviderV2 getProvider() { + public AgentV1UpdateListenListenProvider getProvider() { return provider; } @@ -64,7 +64,7 @@ public static ProviderStage builder() { } public interface ProviderStage { - _FinalStage provider(@NotNull DeepgramListenProviderV2 provider); + _FinalStage provider(@NotNull AgentV1UpdateListenListenProvider provider); Builder from(AgentV1UpdateListenListen other); } @@ -79,7 +79,7 @@ public interface _FinalStage { @JsonIgnoreProperties(ignoreUnknown = true) public static final class Builder implements ProviderStage, _FinalStage { - private DeepgramListenProviderV2 provider; + private AgentV1UpdateListenListenProvider provider; @JsonAnySetter private Map additionalProperties = new HashMap<>(); @@ -94,7 +94,7 @@ public Builder from(AgentV1UpdateListenListen other) { @java.lang.Override @JsonSetter("provider") - public _FinalStage provider(@NotNull DeepgramListenProviderV2 provider) { + public _FinalStage provider(@NotNull AgentV1UpdateListenListenProvider provider) { this.provider = Objects.requireNonNull(provider, "provider must not be null"); 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 new file mode 100644 index 0000000..e693a04 --- /dev/null +++ b/src/main/java/com/deepgram/resources/agent/v1/types/AgentV1UpdateListenListenProvider.java @@ -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 = _UnknownValue.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/AgentV1UpdatePrompt.java b/src/main/java/com/deepgram/resources/agent/v1/types/AgentV1UpdatePrompt.java index adbd18f..d3ee642 100644 --- a/src/main/java/com/deepgram/resources/agent/v1/types/AgentV1UpdatePrompt.java +++ b/src/main/java/com/deepgram/resources/agent/v1/types/AgentV1UpdatePrompt.java @@ -106,7 +106,6 @@ public Builder from(AgentV1UpdatePrompt other) { } /** - *

The new system prompt to be used by the agent

*

The new system prompt to be used by the agent

* @return Reference to {@code this} so that method calls can be chained together. */ 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 7c3fc38..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,14 +36,6 @@ public boolean equals(Object other) { return other instanceof AgentV1UserStartedSpeaking; } - @java.lang.Override - public int hashCode() { - // Manual patch: Fern generates equals() (all instances of this fields-less message - // are equal) but no hashCode(), violating the Object contract. Mirror equals() with a - // type-based constant hash. Remove once Fern emits a consistent equals/hashCode pair. - return getClass().hashCode(); - } - @JsonAnyGetter public Map getAdditionalProperties() { return this.additionalProperties; diff --git a/src/main/java/com/deepgram/resources/agent/v1/types/AgentV1Warning.java b/src/main/java/com/deepgram/resources/agent/v1/types/AgentV1Warning.java index f151d6d..fd2d7ea 100644 --- a/src/main/java/com/deepgram/resources/agent/v1/types/AgentV1Warning.java +++ b/src/main/java/com/deepgram/resources/agent/v1/types/AgentV1Warning.java @@ -127,7 +127,6 @@ public Builder from(AgentV1Warning other) { } /** - *

Description of the warning

*

Description of the warning

* @return Reference to {@code this} so that method calls can be chained together. */ @@ -139,7 +138,6 @@ public CodeStage description(@NotNull String description) { } /** - *

Warning code identifier

*

Warning code identifier

* @return Reference to {@code this} so that method calls can be chained together. */ diff --git a/src/main/java/com/deepgram/resources/agent/v1/types/AgentV1Welcome.java b/src/main/java/com/deepgram/resources/agent/v1/types/AgentV1Welcome.java index 1303bea..0c24b59 100644 --- a/src/main/java/com/deepgram/resources/agent/v1/types/AgentV1Welcome.java +++ b/src/main/java/com/deepgram/resources/agent/v1/types/AgentV1Welcome.java @@ -106,7 +106,6 @@ public Builder from(AgentV1Welcome other) { } /** - *

Unique identifier for the request

*

Unique identifier for the request

* @return Reference to {@code this} so that method calls can be chained together. */ diff --git a/src/main/java/com/deepgram/resources/agent/v1/types/ConversationHistoryMessage.java b/src/main/java/com/deepgram/resources/agent/v1/types/ConversationHistoryMessage.java index b6951a6..8417eeb 100644 --- a/src/main/java/com/deepgram/resources/agent/v1/types/ConversationHistoryMessage.java +++ b/src/main/java/com/deepgram/resources/agent/v1/types/ConversationHistoryMessage.java @@ -130,7 +130,6 @@ public Builder from(ConversationHistoryMessage other) { } /** - *

Identifies who spoke the statement

*

Identifies who spoke the statement

* @return Reference to {@code this} so that method calls can be chained together. */ @@ -142,7 +141,6 @@ public ContentStage role(@NotNull AgentV1SettingsAgentContextContextMessagesItem } /** - *

The actual statement that was spoken

*

The actual statement that was spoken

* @return Reference to {@code this} so that method calls can be chained together. */ diff --git a/src/main/java/com/deepgram/resources/auth/v1/tokens/AsyncRawTokensClient.java b/src/main/java/com/deepgram/resources/auth/v1/tokens/AsyncRawTokensClient.java index 784132c..1d7875a 100644 --- a/src/main/java/com/deepgram/resources/auth/v1/tokens/AsyncRawTokensClient.java +++ b/src/main/java/com/deepgram/resources/auth/v1/tokens/AsyncRawTokensClient.java @@ -10,6 +10,7 @@ import com.deepgram.core.MediaTypes; import com.deepgram.core.ObjectMappers; import com.deepgram.core.RequestOptions; +import com.deepgram.core.RetryInterceptor; import com.deepgram.errors.BadRequestError; import com.deepgram.resources.auth.v1.tokens.requests.GrantV1Request; import com.deepgram.types.GrantV1Response; @@ -86,6 +87,15 @@ public CompletableFuture> grant( if (requestOptions != null && requestOptions.getTimeout().isPresent()) { client = clientOptions.httpClientWithTimeout(requestOptions); } + if (requestOptions != null && requestOptions.getMaxRetries().isPresent()) { + okhttpRequest = okhttpRequest + .newBuilder() + .tag( + RetryInterceptor.MaxRetriesOverride.class, + new RetryInterceptor.MaxRetriesOverride( + requestOptions.getMaxRetries().get())) + .build(); + } CompletableFuture> future = new CompletableFuture<>(); client.newCall(okhttpRequest).enqueue(new Callback() { @Override @@ -111,6 +121,9 @@ public void onResponse(@NotNull Call call, @NotNull Response response) throws IO future.completeExceptionally(new DeepgramHttpException( "Error with status code " + response.code(), response.code(), errorBody, response)); return; + } catch (JsonProcessingException e) { + future.completeExceptionally( + new DeepgramApiException("Failed to deserialize response: " + e.getMessage(), e)); } catch (IOException e) { future.completeExceptionally(new DeepgramApiException("Network error executing HTTP request", e)); } diff --git a/src/main/java/com/deepgram/resources/auth/v1/tokens/RawTokensClient.java b/src/main/java/com/deepgram/resources/auth/v1/tokens/RawTokensClient.java index a8bfeeb..6f99ea4 100644 --- a/src/main/java/com/deepgram/resources/auth/v1/tokens/RawTokensClient.java +++ b/src/main/java/com/deepgram/resources/auth/v1/tokens/RawTokensClient.java @@ -10,6 +10,7 @@ import com.deepgram.core.MediaTypes; import com.deepgram.core.ObjectMappers; import com.deepgram.core.RequestOptions; +import com.deepgram.core.RetryInterceptor; import com.deepgram.errors.BadRequestError; import com.deepgram.resources.auth.v1.tokens.requests.GrantV1Request; import com.deepgram.types.GrantV1Response; @@ -81,6 +82,15 @@ public DeepgramApiHttpResponse grant(GrantV1Request request, Re if (requestOptions != null && requestOptions.getTimeout().isPresent()) { client = clientOptions.httpClientWithTimeout(requestOptions); } + if (requestOptions != null && requestOptions.getMaxRetries().isPresent()) { + okhttpRequest = okhttpRequest + .newBuilder() + .tag( + RetryInterceptor.MaxRetriesOverride.class, + new RetryInterceptor.MaxRetriesOverride( + requestOptions.getMaxRetries().get())) + .build(); + } try (Response response = client.newCall(okhttpRequest).execute()) { ResponseBody responseBody = response.body(); String responseBodyString = responseBody != null ? responseBody.string() : "{}"; @@ -99,6 +109,8 @@ public DeepgramApiHttpResponse grant(GrantV1Request request, Re Object errorBody = ObjectMappers.parseErrorBody(responseBodyString); throw new DeepgramHttpException( "Error with status code " + response.code(), response.code(), errorBody, response); + } catch (JsonProcessingException e) { + throw new DeepgramApiException("Failed to deserialize response: " + e.getMessage(), e); } catch (IOException e) { throw new DeepgramApiException("Network error executing HTTP request", e); } diff --git a/src/main/java/com/deepgram/resources/listen/v1/media/AsyncRawMediaClient.java b/src/main/java/com/deepgram/resources/listen/v1/media/AsyncRawMediaClient.java index a186d87..203d243 100644 --- a/src/main/java/com/deepgram/resources/listen/v1/media/AsyncRawMediaClient.java +++ b/src/main/java/com/deepgram/resources/listen/v1/media/AsyncRawMediaClient.java @@ -12,6 +12,7 @@ import com.deepgram.core.ObjectMappers; import com.deepgram.core.QueryStringMapper; import com.deepgram.core.RequestOptions; +import com.deepgram.core.RetryInterceptor; import com.deepgram.errors.BadRequestError; import com.deepgram.resources.listen.v1.media.requests.ListenV1RequestUrl; import com.deepgram.resources.listen.v1.media.requests.MediaTranscribeRequestOctetStream; @@ -225,6 +226,15 @@ public CompletableFuture> trans if (requestOptions != null && requestOptions.getTimeout().isPresent()) { client = clientOptions.httpClientWithTimeout(requestOptions); } + if (requestOptions != null && requestOptions.getMaxRetries().isPresent()) { + okhttpRequest = okhttpRequest + .newBuilder() + .tag( + RetryInterceptor.MaxRetriesOverride.class, + new RetryInterceptor.MaxRetriesOverride( + requestOptions.getMaxRetries().get())) + .build(); + } CompletableFuture> future = new CompletableFuture<>(); client.newCall(okhttpRequest).enqueue(new Callback() { @Override @@ -250,6 +260,9 @@ public void onResponse(@NotNull Call call, @NotNull Response response) throws IO future.completeExceptionally(new DeepgramHttpException( "Error with status code " + response.code(), response.code(), errorBody, response)); return; + } catch (JsonProcessingException e) { + future.completeExceptionally( + new DeepgramApiException("Failed to deserialize response: " + e.getMessage(), e)); } catch (IOException e) { future.completeExceptionally(new DeepgramApiException("Network error executing HTTP request", e)); } @@ -461,6 +474,15 @@ public CompletableFuture> trans if (requestOptions != null && requestOptions.getTimeout().isPresent()) { client = clientOptions.httpClientWithTimeout(requestOptions); } + if (requestOptions != null && requestOptions.getMaxRetries().isPresent()) { + okhttpRequest = okhttpRequest + .newBuilder() + .tag( + RetryInterceptor.MaxRetriesOverride.class, + new RetryInterceptor.MaxRetriesOverride( + requestOptions.getMaxRetries().get())) + .build(); + } CompletableFuture> future = new CompletableFuture<>(); client.newCall(okhttpRequest).enqueue(new Callback() { @Override @@ -486,6 +508,9 @@ public void onResponse(@NotNull Call call, @NotNull Response response) throws IO future.completeExceptionally(new DeepgramHttpException( "Error with status code " + response.code(), response.code(), errorBody, response)); return; + } catch (JsonProcessingException e) { + future.completeExceptionally( + new DeepgramApiException("Failed to deserialize response: " + e.getMessage(), e)); } catch (IOException e) { future.completeExceptionally(new DeepgramApiException("Network error executing HTTP request", e)); } diff --git a/src/main/java/com/deepgram/resources/listen/v1/media/RawMediaClient.java b/src/main/java/com/deepgram/resources/listen/v1/media/RawMediaClient.java index f263224..b0114e5 100644 --- a/src/main/java/com/deepgram/resources/listen/v1/media/RawMediaClient.java +++ b/src/main/java/com/deepgram/resources/listen/v1/media/RawMediaClient.java @@ -12,6 +12,7 @@ import com.deepgram.core.ObjectMappers; import com.deepgram.core.QueryStringMapper; import com.deepgram.core.RequestOptions; +import com.deepgram.core.RetryInterceptor; import com.deepgram.errors.BadRequestError; import com.deepgram.resources.listen.v1.media.requests.ListenV1RequestUrl; import com.deepgram.resources.listen.v1.media.requests.MediaTranscribeRequestOctetStream; @@ -220,6 +221,15 @@ public DeepgramApiHttpResponse transcribeUrl( if (requestOptions != null && requestOptions.getTimeout().isPresent()) { client = clientOptions.httpClientWithTimeout(requestOptions); } + if (requestOptions != null && requestOptions.getMaxRetries().isPresent()) { + okhttpRequest = okhttpRequest + .newBuilder() + .tag( + RetryInterceptor.MaxRetriesOverride.class, + new RetryInterceptor.MaxRetriesOverride( + requestOptions.getMaxRetries().get())) + .build(); + } try (Response response = client.newCall(okhttpRequest).execute()) { ResponseBody responseBody = response.body(); String responseBodyString = responseBody != null ? responseBody.string() : "{}"; @@ -239,6 +249,8 @@ public DeepgramApiHttpResponse transcribeUrl( Object errorBody = ObjectMappers.parseErrorBody(responseBodyString); throw new DeepgramHttpException( "Error with status code " + response.code(), response.code(), errorBody, response); + } catch (JsonProcessingException e) { + throw new DeepgramApiException("Failed to deserialize response: " + e.getMessage(), e); } catch (IOException e) { throw new DeepgramApiException("Network error executing HTTP request", e); } @@ -440,6 +452,15 @@ public DeepgramApiHttpResponse transcribeFile( if (requestOptions != null && requestOptions.getTimeout().isPresent()) { client = clientOptions.httpClientWithTimeout(requestOptions); } + if (requestOptions != null && requestOptions.getMaxRetries().isPresent()) { + okhttpRequest = okhttpRequest + .newBuilder() + .tag( + RetryInterceptor.MaxRetriesOverride.class, + new RetryInterceptor.MaxRetriesOverride( + requestOptions.getMaxRetries().get())) + .build(); + } try (Response response = client.newCall(okhttpRequest).execute()) { ResponseBody responseBody = response.body(); String responseBodyString = responseBody != null ? responseBody.string() : "{}"; @@ -459,6 +480,8 @@ public DeepgramApiHttpResponse transcribeFile( Object errorBody = ObjectMappers.parseErrorBody(responseBodyString); throw new DeepgramHttpException( "Error with status code " + response.code(), response.code(), errorBody, response); + } catch (JsonProcessingException e) { + throw new DeepgramApiException("Failed to deserialize response: " + e.getMessage(), e); } catch (IOException e) { throw new DeepgramApiException("Network error executing HTTP request", e); } diff --git a/src/main/java/com/deepgram/resources/listen/v1/media/requests/ListenV1RequestUrl.java b/src/main/java/com/deepgram/resources/listen/v1/media/requests/ListenV1RequestUrl.java index d456f3a..196ad4b 100644 --- a/src/main/java/com/deepgram/resources/listen/v1/media/requests/ListenV1RequestUrl.java +++ b/src/main/java/com/deepgram/resources/listen/v1/media/requests/ListenV1RequestUrl.java @@ -224,7 +224,9 @@ public Optional> getCustomIntent() { } /** - * @return Key term prompting can boost or suppress specialized terminology and brands. Only compatible with Nova-3 + * @return Key term prompting improves recognition of specialized terminology and brands. Only compatible with Nova-3. + *

keyterm accepts plain terms only. Unlike the legacy keywords feature, it does not support weights or intensifiers. Appending one (for example, keyterm=term:0.15) is not rejected—the weight is silently ignored and the entire value is treated as a literal keyterm.

+ *

To boost multiple separate keyterms, repeat the keyterm parameter (for example, keyterm=term1&keyterm=term2). To boost one multi-word phrase as a single keyterm, join the words with %20 or + (for example, keyterm=customer%20service). Do not separate keyterms with commas, semicolons, or line breaks.

*/ @JsonIgnore public Optional> getKeyterm() { @@ -646,7 +648,9 @@ public interface _FinalStage { _FinalStage customIntent(String customIntent); /** - *

Key term prompting can boost or suppress specialized terminology and brands. Only compatible with Nova-3

+ *

Key term prompting improves recognition of specialized terminology and brands. Only compatible with Nova-3.

+ *

keyterm accepts plain terms only. Unlike the legacy keywords feature, it does not support weights or intensifiers. Appending one (for example, keyterm=term:0.15) is not rejected—the weight is silently ignored and the entire value is treated as a literal keyterm.

+ *

To boost multiple separate keyterms, repeat the keyterm parameter (for example, keyterm=term1&keyterm=term2). To boost one multi-word phrase as a single keyterm, join the words with %20 or + (for example, keyterm=customer%20service). Do not separate keyterms with commas, semicolons, or line breaks.

*/ _FinalStage keyterm(Optional> keyterm); @@ -1683,7 +1687,9 @@ public _FinalStage keyterm(String keyterm) { } /** - *

Key term prompting can boost or suppress specialized terminology and brands. Only compatible with Nova-3

+ *

Key term prompting improves recognition of specialized terminology and brands. Only compatible with Nova-3.

+ *

keyterm accepts plain terms only. Unlike the legacy keywords feature, it does not support weights or intensifiers. Appending one (for example, keyterm=term:0.15) is not rejected—the weight is silently ignored and the entire value is treated as a literal keyterm.

+ *

To boost multiple separate keyterms, repeat the keyterm parameter (for example, keyterm=term1&keyterm=term2). To boost one multi-word phrase as a single keyterm, join the words with %20 or + (for example, keyterm=customer%20service). Do not separate keyterms with commas, semicolons, or line breaks.

* @return Reference to {@code this} so that method calls can be chained together. */ @java.lang.Override @@ -1693,7 +1699,9 @@ public _FinalStage keyterm(List keyterm) { } /** - *

Key term prompting can boost or suppress specialized terminology and brands. Only compatible with Nova-3

+ *

Key term prompting improves recognition of specialized terminology and brands. Only compatible with Nova-3.

+ *

keyterm accepts plain terms only. Unlike the legacy keywords feature, it does not support weights or intensifiers. Appending one (for example, keyterm=term:0.15) is not rejected—the weight is silently ignored and the entire value is treated as a literal keyterm.

+ *

To boost multiple separate keyterms, repeat the keyterm parameter (for example, keyterm=term1&keyterm=term2). To boost one multi-word phrase as a single keyterm, join the words with %20 or + (for example, keyterm=customer%20service). Do not separate keyterms with commas, semicolons, or line breaks.

*/ @java.lang.Override @JsonSetter(value = "keyterm", nulls = Nulls.SKIP) diff --git a/src/main/java/com/deepgram/resources/listen/v1/media/requests/MediaTranscribeRequestOctetStream.java b/src/main/java/com/deepgram/resources/listen/v1/media/requests/MediaTranscribeRequestOctetStream.java index bbf624f..9286eb1 100644 --- a/src/main/java/com/deepgram/resources/listen/v1/media/requests/MediaTranscribeRequestOctetStream.java +++ b/src/main/java/com/deepgram/resources/listen/v1/media/requests/MediaTranscribeRequestOctetStream.java @@ -223,7 +223,9 @@ public Optional> getCustomIntent() { } /** - * @return Key term prompting can boost or suppress specialized terminology and brands. Only compatible with Nova-3 + * @return Key term prompting improves recognition of specialized terminology and brands. Only compatible with Nova-3. + *

keyterm accepts plain terms only. Unlike the legacy keywords feature, it does not support weights or intensifiers. Appending one (for example, keyterm=term:0.15) is not rejected—the weight is silently ignored and the entire value is treated as a literal keyterm.

+ *

To boost multiple separate keyterms, repeat the keyterm parameter (for example, keyterm=term1&keyterm=term2). To boost one multi-word phrase as a single keyterm, join the words with %20 or + (for example, keyterm=customer%20service). Do not separate keyterms with commas, semicolons, or line breaks.

*/ @JsonProperty("keyterm") public Optional> getKeyterm() { @@ -645,7 +647,9 @@ public interface _FinalStage { _FinalStage customIntent(String customIntent); /** - *

Key term prompting can boost or suppress specialized terminology and brands. Only compatible with Nova-3

+ *

Key term prompting improves recognition of specialized terminology and brands. Only compatible with Nova-3.

+ *

keyterm accepts plain terms only. Unlike the legacy keywords feature, it does not support weights or intensifiers. Appending one (for example, keyterm=term:0.15) is not rejected—the weight is silently ignored and the entire value is treated as a literal keyterm.

+ *

To boost multiple separate keyterms, repeat the keyterm parameter (for example, keyterm=term1&keyterm=term2). To boost one multi-word phrase as a single keyterm, join the words with %20 or + (for example, keyterm=customer%20service). Do not separate keyterms with commas, semicolons, or line breaks.

*/ _FinalStage keyterm(Optional> keyterm); @@ -1682,7 +1686,9 @@ public _FinalStage keyterm(String keyterm) { } /** - *

Key term prompting can boost or suppress specialized terminology and brands. Only compatible with Nova-3

+ *

Key term prompting improves recognition of specialized terminology and brands. Only compatible with Nova-3.

+ *

keyterm accepts plain terms only. Unlike the legacy keywords feature, it does not support weights or intensifiers. Appending one (for example, keyterm=term:0.15) is not rejected—the weight is silently ignored and the entire value is treated as a literal keyterm.

+ *

To boost multiple separate keyterms, repeat the keyterm parameter (for example, keyterm=term1&keyterm=term2). To boost one multi-word phrase as a single keyterm, join the words with %20 or + (for example, keyterm=customer%20service). Do not separate keyterms with commas, semicolons, or line breaks.

* @return Reference to {@code this} so that method calls can be chained together. */ @java.lang.Override @@ -1692,7 +1698,9 @@ public _FinalStage keyterm(List keyterm) { } /** - *

Key term prompting can boost or suppress specialized terminology and brands. Only compatible with Nova-3

+ *

Key term prompting improves recognition of specialized terminology and brands. Only compatible with Nova-3.

+ *

keyterm accepts plain terms only. Unlike the legacy keywords feature, it does not support weights or intensifiers. Appending one (for example, keyterm=term:0.15) is not rejected—the weight is silently ignored and the entire value is treated as a literal keyterm.

+ *

To boost multiple separate keyterms, repeat the keyterm parameter (for example, keyterm=term1&keyterm=term2). To boost one multi-word phrase as a single keyterm, join the words with %20 or + (for example, keyterm=customer%20service). Do not separate keyterms with commas, semicolons, or line breaks.

*/ @java.lang.Override @JsonSetter(value = "keyterm", nulls = Nulls.SKIP) diff --git a/src/main/java/com/deepgram/resources/listen/v1/types/ListenV1CloseStream.java b/src/main/java/com/deepgram/resources/listen/v1/types/ListenV1CloseStream.java index 5731273..e3302a4 100644 --- a/src/main/java/com/deepgram/resources/listen/v1/types/ListenV1CloseStream.java +++ b/src/main/java/com/deepgram/resources/listen/v1/types/ListenV1CloseStream.java @@ -98,7 +98,6 @@ public Builder from(ListenV1CloseStream other) { } /** - *

Message type identifier

*

Message type identifier

* @return Reference to {@code this} so that method calls can be chained together. */ diff --git a/src/main/java/com/deepgram/resources/listen/v1/types/ListenV1Finalize.java b/src/main/java/com/deepgram/resources/listen/v1/types/ListenV1Finalize.java index c1bac7f..93a6b93 100644 --- a/src/main/java/com/deepgram/resources/listen/v1/types/ListenV1Finalize.java +++ b/src/main/java/com/deepgram/resources/listen/v1/types/ListenV1Finalize.java @@ -98,7 +98,6 @@ public Builder from(ListenV1Finalize other) { } /** - *

Message type identifier

*

Message type identifier

* @return Reference to {@code this} so that method calls can be chained together. */ diff --git a/src/main/java/com/deepgram/resources/listen/v1/types/ListenV1KeepAlive.java b/src/main/java/com/deepgram/resources/listen/v1/types/ListenV1KeepAlive.java index 2cfb71e..c5342c1 100644 --- a/src/main/java/com/deepgram/resources/listen/v1/types/ListenV1KeepAlive.java +++ b/src/main/java/com/deepgram/resources/listen/v1/types/ListenV1KeepAlive.java @@ -98,7 +98,6 @@ public Builder from(ListenV1KeepAlive other) { } /** - *

Message type identifier

*

Message type identifier

* @return Reference to {@code this} so that method calls can be chained together. */ diff --git a/src/main/java/com/deepgram/resources/listen/v1/types/ListenV1Metadata.java b/src/main/java/com/deepgram/resources/listen/v1/types/ListenV1Metadata.java index b11bbe6..95dec03 100644 --- a/src/main/java/com/deepgram/resources/listen/v1/types/ListenV1Metadata.java +++ b/src/main/java/com/deepgram/resources/listen/v1/types/ListenV1Metadata.java @@ -231,7 +231,6 @@ public Builder from(ListenV1Metadata other) { } /** - *

The transaction key

*

The transaction key

* @return Reference to {@code this} so that method calls can be chained together. */ @@ -243,7 +242,6 @@ public RequestIdStage transactionKey(@NotNull String transactionKey) { } /** - *

The request ID

*

The request ID

* @return Reference to {@code this} so that method calls can be chained together. */ @@ -255,7 +253,6 @@ public Sha256Stage requestId(@NotNull String requestId) { } /** - *

The sha256

*

The sha256

* @return Reference to {@code this} so that method calls can be chained together. */ @@ -267,7 +264,6 @@ public CreatedStage sha256(@NotNull String sha256) { } /** - *

The created

*

The created

* @return Reference to {@code this} so that method calls can be chained together. */ @@ -279,7 +275,6 @@ public DurationStage created(@NotNull String created) { } /** - *

The duration

*

The duration

* @return Reference to {@code this} so that method calls can be chained together. */ @@ -291,7 +286,6 @@ public ChannelsStage duration(double duration) { } /** - *

The channels

*

The channels

* @return Reference to {@code this} so that method calls can be chained together. */ diff --git a/src/main/java/com/deepgram/resources/listen/v1/types/ListenV1Results.java b/src/main/java/com/deepgram/resources/listen/v1/types/ListenV1Results.java index 242edde..9a61ea6 100644 --- a/src/main/java/com/deepgram/resources/listen/v1/types/ListenV1Results.java +++ b/src/main/java/com/deepgram/resources/listen/v1/types/ListenV1Results.java @@ -295,7 +295,6 @@ public Builder from(ListenV1Results other) { } /** - *

The duration of the transcription

*

The duration of the transcription

* @return Reference to {@code this} so that method calls can be chained together. */ @@ -307,7 +306,6 @@ public StartStage duration(double duration) { } /** - *

The start time of the transcription

*

The start time of the transcription

* @return Reference to {@code this} so that method calls can be chained together. */ diff --git a/src/main/java/com/deepgram/resources/listen/v1/types/ListenV1ResultsChannelAlternativesItem.java b/src/main/java/com/deepgram/resources/listen/v1/types/ListenV1ResultsChannelAlternativesItem.java index fd5ff33..928064e 100644 --- a/src/main/java/com/deepgram/resources/listen/v1/types/ListenV1ResultsChannelAlternativesItem.java +++ b/src/main/java/com/deepgram/resources/listen/v1/types/ListenV1ResultsChannelAlternativesItem.java @@ -164,7 +164,6 @@ public Builder from(ListenV1ResultsChannelAlternativesItem other) { } /** - *

The transcript of the transcription

*

The transcript of the transcription

* @return Reference to {@code this} so that method calls can be chained together. */ @@ -176,7 +175,6 @@ public ConfidenceStage transcript(@NotNull String transcript) { } /** - *

The confidence of the transcription

*

The confidence of the transcription

* @return Reference to {@code this} so that method calls can be chained together. */ diff --git a/src/main/java/com/deepgram/resources/listen/v1/types/ListenV1ResultsChannelAlternativesItemWordsItem.java b/src/main/java/com/deepgram/resources/listen/v1/types/ListenV1ResultsChannelAlternativesItemWordsItem.java index 159504a..c07c561 100644 --- a/src/main/java/com/deepgram/resources/listen/v1/types/ListenV1ResultsChannelAlternativesItemWordsItem.java +++ b/src/main/java/com/deepgram/resources/listen/v1/types/ListenV1ResultsChannelAlternativesItemWordsItem.java @@ -242,7 +242,6 @@ public Builder from(ListenV1ResultsChannelAlternativesItemWordsItem other) { } /** - *

The word of the transcription

*

The word of the transcription

* @return Reference to {@code this} so that method calls can be chained together. */ @@ -254,7 +253,6 @@ public StartStage word(@NotNull String word) { } /** - *

The start time of the word

*

The start time of the word

* @return Reference to {@code this} so that method calls can be chained together. */ @@ -266,7 +264,6 @@ public EndStage start(double start) { } /** - *

The end time of the word

*

The end time of the word

* @return Reference to {@code this} so that method calls can be chained together. */ @@ -278,7 +275,6 @@ public ConfidenceStage end(double end) { } /** - *

The confidence of the word

*

The confidence of the word

* @return Reference to {@code this} so that method calls can be chained together. */ diff --git a/src/main/java/com/deepgram/resources/listen/v1/types/ListenV1ResultsEntitiesItem.java b/src/main/java/com/deepgram/resources/listen/v1/types/ListenV1ResultsEntitiesItem.java index ff5c10e..29b8692 100644 --- a/src/main/java/com/deepgram/resources/listen/v1/types/ListenV1ResultsEntitiesItem.java +++ b/src/main/java/com/deepgram/resources/listen/v1/types/ListenV1ResultsEntitiesItem.java @@ -222,7 +222,6 @@ public Builder from(ListenV1ResultsEntitiesItem other) { } /** - *

The type/category of the entity (e.g., NAME, PHONE_NUMBER, EMAIL_ADDRESS, ORGANIZATION, CARDINAL)

*

The type/category of the entity (e.g., NAME, PHONE_NUMBER, EMAIL_ADDRESS, ORGANIZATION, CARDINAL)

* @return Reference to {@code this} so that method calls can be chained together. */ @@ -234,7 +233,6 @@ public ValueStage label(@NotNull String label) { } /** - *

The formatted text representation of the entity

*

The formatted text representation of the entity

* @return Reference to {@code this} so that method calls can be chained together. */ @@ -246,7 +244,6 @@ public RawValueStage value(@NotNull String value) { } /** - *

The original spoken text of the entity (present when formatting is enabled)

*

The original spoken text of the entity (present when formatting is enabled)

* @return Reference to {@code this} so that method calls can be chained together. */ @@ -258,7 +255,6 @@ public ConfidenceStage rawValue(@NotNull String rawValue) { } /** - *

The confidence score of the entity detection

*

The confidence score of the entity detection

* @return Reference to {@code this} so that method calls can be chained together. */ @@ -270,7 +266,6 @@ public StartWordStage confidence(double confidence) { } /** - *

The index of the first word of the entity in the transcript (inclusive)

*

The index of the first word of the entity in the transcript (inclusive)

* @return Reference to {@code this} so that method calls can be chained together. */ @@ -282,7 +277,6 @@ public EndWordStage startWord(int startWord) { } /** - *

The index of the last word of the entity in the transcript (exclusive)

*

The index of the last word of the entity in the transcript (exclusive)

* @return Reference to {@code this} so that method calls can be chained together. */ diff --git a/src/main/java/com/deepgram/resources/listen/v1/types/ListenV1ResultsMetadata.java b/src/main/java/com/deepgram/resources/listen/v1/types/ListenV1ResultsMetadata.java index df504c7..a7f01b5 100644 --- a/src/main/java/com/deepgram/resources/listen/v1/types/ListenV1ResultsMetadata.java +++ b/src/main/java/com/deepgram/resources/listen/v1/types/ListenV1ResultsMetadata.java @@ -140,7 +140,6 @@ public Builder from(ListenV1ResultsMetadata other) { } /** - *

The request ID

*

The request ID

* @return Reference to {@code this} so that method calls can be chained together. */ @@ -159,7 +158,6 @@ public ModelUuidStage modelInfo(@NotNull ListenV1ResultsMetadataModelInfo modelI } /** - *

The model UUID

*

The model UUID

* @return Reference to {@code this} so that method calls can be chained together. */ diff --git a/src/main/java/com/deepgram/resources/listen/v1/types/ListenV1ResultsMetadataModelInfo.java b/src/main/java/com/deepgram/resources/listen/v1/types/ListenV1ResultsMetadataModelInfo.java index 732cd94..46d7a14 100644 --- a/src/main/java/com/deepgram/resources/listen/v1/types/ListenV1ResultsMetadataModelInfo.java +++ b/src/main/java/com/deepgram/resources/listen/v1/types/ListenV1ResultsMetadataModelInfo.java @@ -141,7 +141,6 @@ public Builder from(ListenV1ResultsMetadataModelInfo other) { } /** - *

The name of the model

*

The name of the model

* @return Reference to {@code this} so that method calls can be chained together. */ @@ -153,7 +152,6 @@ public VersionStage name(@NotNull String name) { } /** - *

The version of the model

*

The version of the model

* @return Reference to {@code this} so that method calls can be chained together. */ @@ -165,7 +163,6 @@ public ArchStage version(@NotNull String version) { } /** - *

The arch of the model

*

The arch of the model

* @return Reference to {@code this} so that method calls can be chained together. */ diff --git a/src/main/java/com/deepgram/resources/listen/v1/types/ListenV1SpeechStarted.java b/src/main/java/com/deepgram/resources/listen/v1/types/ListenV1SpeechStarted.java index ce614e3..baf4abb 100644 --- a/src/main/java/com/deepgram/resources/listen/v1/types/ListenV1SpeechStarted.java +++ b/src/main/java/com/deepgram/resources/listen/v1/types/ListenV1SpeechStarted.java @@ -131,7 +131,6 @@ public Builder from(ListenV1SpeechStarted other) { } /** - *

The timestamp

*

The timestamp

* @return Reference to {@code this} so that method calls can be chained together. */ diff --git a/src/main/java/com/deepgram/resources/listen/v1/types/ListenV1UtteranceEnd.java b/src/main/java/com/deepgram/resources/listen/v1/types/ListenV1UtteranceEnd.java index df76d55..7a9f0f9 100644 --- a/src/main/java/com/deepgram/resources/listen/v1/types/ListenV1UtteranceEnd.java +++ b/src/main/java/com/deepgram/resources/listen/v1/types/ListenV1UtteranceEnd.java @@ -131,7 +131,6 @@ public Builder from(ListenV1UtteranceEnd other) { } /** - *

The last word end

*

The last word end

* @return Reference to {@code this} so that method calls can be chained together. */ diff --git a/src/main/java/com/deepgram/resources/listen/v1/websocket/V1ConnectOptions.java b/src/main/java/com/deepgram/resources/listen/v1/websocket/V1ConnectOptions.java index 77822f1..700f2cd 100644 --- a/src/main/java/com/deepgram/resources/listen/v1/websocket/V1ConnectOptions.java +++ b/src/main/java/com/deepgram/resources/listen/v1/websocket/V1ConnectOptions.java @@ -648,7 +648,6 @@ public Builder from(V1ConnectOptions other) { } /** - *

AI model to use for the transcription

*

AI model to use for the transcription

* @return Reference to {@code this} so that method calls can be chained together. */ 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 ffa4953..4952808 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,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; @@ -139,12 +138,8 @@ public CompletableFuture connect(V1ConnectOptions options) { "endpointing", String.valueOf(options.getEndpointing().get())); } if (options.getExtra() != null && options.getExtra().isPresent()) { - // Array-valued query params (String | List unions) must serialize as repeated - // params (extra=a&extra=b), not a stringified list. The generated streaming template - // uses String.valueOf(...), which mangles a List into "[a, b]"; route these through - // QueryStringMapper (arraysAsRepeats=true) so the wire format matches the REST path. - 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( @@ -152,12 +147,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( @@ -190,24 +185,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( @@ -222,17 +216,6 @@ public CompletableFuture connect(V1ConnectOptions options) { urlBuilder.addQueryParameter( "version", String.valueOf(options.getVersion().get())); } - // Escape hatch: emit caller-supplied additionalProperties (e.g. no_delay) as query params. - // The generated template only serializes the typed options and drops these otherwise. - // ConnectOptions is request-only (never deserialized), so this map holds only what the - // caller set via the builder. Routed through QueryStringMapper to match the REST path. - 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/v2/types/ListenV2CloseStream.java b/src/main/java/com/deepgram/resources/listen/v2/types/ListenV2CloseStream.java index e508681..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,14 +36,6 @@ public boolean equals(Object other) { return other instanceof ListenV2CloseStream; } - @java.lang.Override - public int hashCode() { - // Manual patch: Fern generates equals() (all instances of this fields-less message - // are equal) but no hashCode(), violating the Object contract. Mirror equals() with a - // type-based constant hash. Remove once Fern emits a consistent equals/hashCode pair. - return getClass().hashCode(); - } - @JsonAnyGetter public Map getAdditionalProperties() { return this.additionalProperties; diff --git a/src/main/java/com/deepgram/resources/listen/v2/types/ListenV2ConfigureFailure.java b/src/main/java/com/deepgram/resources/listen/v2/types/ListenV2ConfigureFailure.java index 5f482fb..478307b 100644 --- a/src/main/java/com/deepgram/resources/listen/v2/types/ListenV2ConfigureFailure.java +++ b/src/main/java/com/deepgram/resources/listen/v2/types/ListenV2ConfigureFailure.java @@ -131,7 +131,6 @@ public Builder from(ListenV2ConfigureFailure other) { } /** - *

The unique identifier of the request

*

The unique identifier of the request

* @return Reference to {@code this} so that method calls can be chained together. */ @@ -143,9 +142,6 @@ public SequenceIdStage requestId(@NotNull String requestId) { } /** - *

Starts at 0 and increments for each message the server sends - * to the client. This includes messages of other types, like - * TurnInfo messages.

*

Starts at 0 and increments for each message the server sends * to the client. This includes messages of other types, like * TurnInfo messages.

diff --git a/src/main/java/com/deepgram/resources/listen/v2/types/ListenV2ConfigureSuccess.java b/src/main/java/com/deepgram/resources/listen/v2/types/ListenV2ConfigureSuccess.java index 1319c9e..ca93f2b 100644 --- a/src/main/java/com/deepgram/resources/listen/v2/types/ListenV2ConfigureSuccess.java +++ b/src/main/java/com/deepgram/resources/listen/v2/types/ListenV2ConfigureSuccess.java @@ -205,7 +205,6 @@ public Builder from(ListenV2ConfigureSuccess other) { } /** - *

The unique identifier of the request

*

The unique identifier of the request

* @return Reference to {@code this} so that method calls can be chained together. */ @@ -217,8 +216,6 @@ public ThresholdsStage requestId(@NotNull String requestId) { } /** - *

Updates each parameter, if it is supplied. If a particular threshold parameter - * is not supplied, the configuration continues using the currently configured value.

*

Updates each parameter, if it is supplied. If a particular threshold parameter * is not supplied, the configuration continues using the currently configured value.

* @return Reference to {@code this} so that method calls can be chained together. @@ -238,9 +235,6 @@ public SequenceIdStage keyterms(@NotNull ListenV2Keyterm keyterms) { } /** - *

Starts at 0 and increments for each message the server sends - * to the client. This includes messages of other types, like - * TurnInfo messages.

*

Starts at 0 and increments for each message the server sends * to the client. This includes messages of other types, like * TurnInfo messages.

diff --git a/src/main/java/com/deepgram/resources/listen/v2/types/ListenV2Connected.java b/src/main/java/com/deepgram/resources/listen/v2/types/ListenV2Connected.java index 56e09a8..14a9b64 100644 --- a/src/main/java/com/deepgram/resources/listen/v2/types/ListenV2Connected.java +++ b/src/main/java/com/deepgram/resources/listen/v2/types/ListenV2Connected.java @@ -131,7 +131,6 @@ public Builder from(ListenV2Connected other) { } /** - *

The unique identifier of the request

*

The unique identifier of the request

* @return Reference to {@code this} so that method calls can be chained together. */ @@ -143,9 +142,6 @@ public SequenceIdStage requestId(@NotNull String requestId) { } /** - *

Starts at 0 and increments for each message the server sends - * to the client. This includes messages of other types, like - * TurnInfo messages.

*

Starts at 0 and increments for each message the server sends * to the client. This includes messages of other types, like * TurnInfo messages.

diff --git a/src/main/java/com/deepgram/resources/listen/v2/types/ListenV2FatalError.java b/src/main/java/com/deepgram/resources/listen/v2/types/ListenV2FatalError.java index 1bd208b..fcf3c0b 100644 --- a/src/main/java/com/deepgram/resources/listen/v2/types/ListenV2FatalError.java +++ b/src/main/java/com/deepgram/resources/listen/v2/types/ListenV2FatalError.java @@ -153,9 +153,6 @@ public Builder from(ListenV2FatalError other) { } /** - *

Starts at 0 and increments for each message the server sends - * to the client. This includes messages of other types, like - * Connected messages.

*

Starts at 0 and increments for each message the server sends * to the client. This includes messages of other types, like * Connected messages.

@@ -169,7 +166,6 @@ public CodeStage sequenceId(int sequenceId) { } /** - *

A string code describing the error, e.g. INTERNAL_SERVER_ERROR

*

A string code describing the error, e.g. INTERNAL_SERVER_ERROR

* @return Reference to {@code this} so that method calls can be chained together. */ @@ -181,7 +177,6 @@ public DescriptionStage code(@NotNull String code) { } /** - *

Prose description of the error

*

Prose description of the error

* @return Reference to {@code this} so that method calls can be chained together. */ diff --git a/src/main/java/com/deepgram/resources/listen/v2/types/ListenV2TurnInfo.java b/src/main/java/com/deepgram/resources/listen/v2/types/ListenV2TurnInfo.java index 5cb7195..9e1d149 100644 --- a/src/main/java/com/deepgram/resources/listen/v2/types/ListenV2TurnInfo.java +++ b/src/main/java/com/deepgram/resources/listen/v2/types/ListenV2TurnInfo.java @@ -381,7 +381,6 @@ public Builder from(ListenV2TurnInfo other) { } /** - *

The unique identifier of the request

*

The unique identifier of the request

* @return Reference to {@code this} so that method calls can be chained together. */ @@ -393,7 +392,6 @@ public SequenceIdStage requestId(@NotNull String requestId) { } /** - *

Starts at 0 and increments for each message the server sends to the client. This includes messages of other types, like Connected messages.

*

Starts at 0 and increments for each message the server sends to the client. This includes messages of other types, like Connected messages.

* @return Reference to {@code this} so that method calls can be chained together. */ @@ -405,14 +403,6 @@ public EventStage sequenceId(int sequenceId) { } /** - *

The type of event being reported.

- *
    - *
  • Update - Additional audio has been transcribed, but the turn state hasn't changed
  • - *
  • StartOfTurn - The user has begun speaking for the first time in the turn
  • - *
  • EagerEndOfTurn - The system has moderate confidence that the user has finished speaking for the turn. This is an opportunity to begin preparing an agent reply
  • - *
  • TurnResumed - The system detected that speech had ended and therefore sent an EagerEndOfTurn event, but speech is actually continuing for this turn
  • - *
  • EndOfTurn - The user has finished speaking for the turn
  • - *
*

The type of event being reported.

*
    *
  • Update - Additional audio has been transcribed, but the turn state hasn't changed
  • @@ -431,7 +421,6 @@ public TurnIndexStage event(@NotNull ListenV2TurnInfoEvent event) { } /** - *

    The index of the current turn

    *

    The index of the current turn

    * @return Reference to {@code this} so that method calls can be chained together. */ @@ -443,7 +432,6 @@ public AudioWindowStartStage turnIndex(int turnIndex) { } /** - *

    Start time in seconds of the audio range that was transcribed

    *

    Start time in seconds of the audio range that was transcribed

    * @return Reference to {@code this} so that method calls can be chained together. */ @@ -455,7 +443,6 @@ public AudioWindowEndStage audioWindowStart(float audioWindowStart) { } /** - *

    End time in seconds of the audio range that was transcribed

    *

    End time in seconds of the audio range that was transcribed

    * @return Reference to {@code this} so that method calls can be chained together. */ @@ -467,7 +454,6 @@ public TranscriptStage audioWindowEnd(float audioWindowEnd) { } /** - *

    Text that was said over the course of the current turn

    *

    Text that was said over the course of the current turn

    * @return Reference to {@code this} so that method calls can be chained together. */ @@ -479,7 +465,6 @@ public EndOfTurnConfidenceStage transcript(@NotNull String transcript) { } /** - *

    Confidence that no more speech is coming in this turn

    *

    Confidence that no more speech is coming in this turn

    * @return Reference to {@code this} so that method calls can be chained together. */ diff --git a/src/main/java/com/deepgram/resources/listen/v2/types/ListenV2TurnInfoWordsItem.java b/src/main/java/com/deepgram/resources/listen/v2/types/ListenV2TurnInfoWordsItem.java index 35e7f79..aa7396f 100644 --- a/src/main/java/com/deepgram/resources/listen/v2/types/ListenV2TurnInfoWordsItem.java +++ b/src/main/java/com/deepgram/resources/listen/v2/types/ListenV2TurnInfoWordsItem.java @@ -171,7 +171,6 @@ public Builder from(ListenV2TurnInfoWordsItem other) { } /** - *

    The individual punctuated, properly-cased word from the transcript

    *

    The individual punctuated, properly-cased word from the transcript

    * @return Reference to {@code this} so that method calls can be chained together. */ @@ -183,7 +182,6 @@ public ConfidenceStage word(@NotNull String word) { } /** - *

    Confidence that this word was transcribed correctly

    *

    Confidence that this word was transcribed correctly

    * @return Reference to {@code this} so that method calls can be chained together. */ diff --git a/src/main/java/com/deepgram/resources/listen/v2/websocket/V2ConnectOptions.java b/src/main/java/com/deepgram/resources/listen/v2/websocket/V2ConnectOptions.java index d14542a..5cc78b9 100644 --- a/src/main/java/com/deepgram/resources/listen/v2/websocket/V2ConnectOptions.java +++ b/src/main/java/com/deepgram/resources/listen/v2/websocket/V2ConnectOptions.java @@ -14,6 +14,7 @@ import com.deepgram.types.ListenV2Model; import com.deepgram.types.ListenV2Numerals; import com.deepgram.types.ListenV2ProfanityFilter; +import com.deepgram.types.ListenV2Redact; import com.deepgram.types.ListenV2SampleRate; import com.deepgram.types.ListenV2Tag; import com.fasterxml.jackson.annotation.JsonAnyGetter; @@ -53,6 +54,8 @@ public final class V2ConnectOptions { private final Optional numerals; + private final Optional redact; + private final Optional mipOptOut; private final Optional tag; @@ -70,6 +73,7 @@ private V2ConnectOptions( Optional languageHint, Optional profanityFilter, Optional numerals, + Optional redact, Optional mipOptOut, Optional tag, Map additionalProperties) { @@ -83,6 +87,7 @@ private V2ConnectOptions( this.languageHint = languageHint; this.profanityFilter = profanityFilter; this.numerals = numerals; + this.redact = redact; this.mipOptOut = mipOptOut; this.tag = tag; this.additionalProperties = additionalProperties; @@ -138,6 +143,11 @@ public Optional getNumerals() { return numerals; } + @JsonProperty("redact") + public Optional getRedact() { + return redact; + } + @JsonProperty("mip_opt_out") public Optional getMipOptOut() { return mipOptOut; @@ -170,6 +180,7 @@ private boolean equalTo(V2ConnectOptions other) { && languageHint.equals(other.languageHint) && profanityFilter.equals(other.profanityFilter) && numerals.equals(other.numerals) + && redact.equals(other.redact) && mipOptOut.equals(other.mipOptOut) && tag.equals(other.tag); } @@ -187,6 +198,7 @@ public int hashCode() { this.languageHint, this.profanityFilter, this.numerals, + this.redact, this.mipOptOut, this.tag); } @@ -249,6 +261,10 @@ public interface _FinalStage { _FinalStage numerals(ListenV2Numerals numerals); + _FinalStage redact(Optional redact); + + _FinalStage redact(ListenV2Redact redact); + _FinalStage mipOptOut(Optional mipOptOut); _FinalStage mipOptOut(ListenV2MipOptOut mipOptOut); @@ -266,6 +282,8 @@ public static final class Builder implements ModelStage, _FinalStage { private Optional mipOptOut = Optional.empty(); + private Optional redact = Optional.empty(); + private Optional numerals = Optional.empty(); private Optional profanityFilter = Optional.empty(); @@ -301,6 +319,7 @@ public Builder from(V2ConnectOptions other) { languageHint(other.getLanguageHint()); profanityFilter(other.getProfanityFilter()); numerals(other.getNumerals()); + redact(other.getRedact()); mipOptOut(other.getMipOptOut()); tag(other.getTag()); return this; @@ -339,6 +358,19 @@ public _FinalStage mipOptOut(Optional mipOptOut) { return this; } + @java.lang.Override + public _FinalStage redact(ListenV2Redact redact) { + this.redact = Optional.ofNullable(redact); + return this; + } + + @java.lang.Override + @JsonSetter(value = "redact", nulls = Nulls.SKIP) + public _FinalStage redact(Optional redact) { + this.redact = redact; + return this; + } + @java.lang.Override public _FinalStage numerals(ListenV2Numerals numerals) { this.numerals = Optional.ofNullable(numerals); @@ -469,6 +501,7 @@ public V2ConnectOptions build() { languageHint, profanityFilter, numerals, + redact, mipOptOut, tag, 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 f47ee22..0d07ce5 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; @@ -126,16 +125,12 @@ public CompletableFuture connect(V2ConnectOptions options) { "eot_timeout_ms", String.valueOf(options.getEotTimeoutMs().get())); } if (options.getKeyterm() != null && options.getKeyterm().isPresent()) { - // Array-valued query params (String | List unions) must serialize as repeated - // params (keyterm=a&keyterm=b), not a stringified list. The generated streaming template - // uses String.valueOf(...), which mangles a List into "[a, b]"; route these through - // QueryStringMapper (arraysAsRepeats=true) so the wire format matches the REST path. - 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( @@ -146,24 +141,16 @@ public CompletableFuture connect(V2ConnectOptions options) { 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); - } - // Escape hatch: emit caller-supplied additionalProperties (e.g. no_delay) as query params. - // The generated template only serializes the typed options and drops these otherwise. - // ConnectOptions is request-only (never deserialized), so this map holds only what the - // caller set via the builder. Routed through QueryStringMapper to match the REST path. - 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); @@ -490,11 +477,11 @@ private void handleIncomingMessage(String json) { return; } } - // Unrecognized message type: forward-compatible no-op. The raw frame was - // already delivered to onMessage(String) above, so a newer server adding a - // benign control frame (e.g. a GA addition to this endpoint) must not surface - // as a fatal error to deployed clients. Routing it to onError would make a - // harmless frame look fatal; mirrors the speak v2 client and the JS/Python SDKs. + 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/manage/v1/models/AsyncRawModelsClient.java b/src/main/java/com/deepgram/resources/manage/v1/models/AsyncRawModelsClient.java index e338abc..d545d48 100644 --- a/src/main/java/com/deepgram/resources/manage/v1/models/AsyncRawModelsClient.java +++ b/src/main/java/com/deepgram/resources/manage/v1/models/AsyncRawModelsClient.java @@ -10,6 +10,7 @@ import com.deepgram.core.ObjectMappers; import com.deepgram.core.QueryStringMapper; import com.deepgram.core.RequestOptions; +import com.deepgram.core.RetryInterceptor; import com.deepgram.errors.BadRequestError; import com.deepgram.resources.manage.v1.models.requests.ModelsListRequest; import com.deepgram.types.GetModelV1Response; @@ -82,6 +83,15 @@ public CompletableFuture> list( if (requestOptions != null && requestOptions.getTimeout().isPresent()) { client = clientOptions.httpClientWithTimeout(requestOptions); } + if (requestOptions != null && requestOptions.getMaxRetries().isPresent()) { + okhttpRequest = okhttpRequest + .newBuilder() + .tag( + RetryInterceptor.MaxRetriesOverride.class, + new RetryInterceptor.MaxRetriesOverride( + requestOptions.getMaxRetries().get())) + .build(); + } CompletableFuture> future = new CompletableFuture<>(); client.newCall(okhttpRequest).enqueue(new Callback() { @Override @@ -107,6 +117,9 @@ public void onResponse(@NotNull Call call, @NotNull Response response) throws IO future.completeExceptionally(new DeepgramHttpException( "Error with status code " + response.code(), response.code(), errorBody, response)); return; + } catch (JsonProcessingException e) { + future.completeExceptionally( + new DeepgramApiException("Failed to deserialize response: " + e.getMessage(), e)); } catch (IOException e) { future.completeExceptionally(new DeepgramApiException("Network error executing HTTP request", e)); } @@ -151,6 +164,15 @@ public CompletableFuture> get( if (requestOptions != null && requestOptions.getTimeout().isPresent()) { client = clientOptions.httpClientWithTimeout(requestOptions); } + if (requestOptions != null && requestOptions.getMaxRetries().isPresent()) { + okhttpRequest = okhttpRequest + .newBuilder() + .tag( + RetryInterceptor.MaxRetriesOverride.class, + new RetryInterceptor.MaxRetriesOverride( + requestOptions.getMaxRetries().get())) + .build(); + } CompletableFuture> future = new CompletableFuture<>(); client.newCall(okhttpRequest).enqueue(new Callback() { @Override @@ -176,6 +198,9 @@ public void onResponse(@NotNull Call call, @NotNull Response response) throws IO future.completeExceptionally(new DeepgramHttpException( "Error with status code " + response.code(), response.code(), errorBody, response)); return; + } catch (JsonProcessingException e) { + future.completeExceptionally( + new DeepgramApiException("Failed to deserialize response: " + e.getMessage(), e)); } catch (IOException e) { future.completeExceptionally(new DeepgramApiException("Network error executing HTTP request", e)); } diff --git a/src/main/java/com/deepgram/resources/manage/v1/models/RawModelsClient.java b/src/main/java/com/deepgram/resources/manage/v1/models/RawModelsClient.java index fa32fb9..84d1025 100644 --- a/src/main/java/com/deepgram/resources/manage/v1/models/RawModelsClient.java +++ b/src/main/java/com/deepgram/resources/manage/v1/models/RawModelsClient.java @@ -10,6 +10,7 @@ import com.deepgram.core.ObjectMappers; import com.deepgram.core.QueryStringMapper; import com.deepgram.core.RequestOptions; +import com.deepgram.core.RetryInterceptor; import com.deepgram.errors.BadRequestError; import com.deepgram.resources.manage.v1.models.requests.ModelsListRequest; import com.deepgram.types.GetModelV1Response; @@ -78,6 +79,15 @@ public DeepgramApiHttpResponse list( if (requestOptions != null && requestOptions.getTimeout().isPresent()) { client = clientOptions.httpClientWithTimeout(requestOptions); } + if (requestOptions != null && requestOptions.getMaxRetries().isPresent()) { + okhttpRequest = okhttpRequest + .newBuilder() + .tag( + RetryInterceptor.MaxRetriesOverride.class, + new RetryInterceptor.MaxRetriesOverride( + requestOptions.getMaxRetries().get())) + .build(); + } try (Response response = client.newCall(okhttpRequest).execute()) { ResponseBody responseBody = response.body(); String responseBodyString = responseBody != null ? responseBody.string() : "{}"; @@ -96,6 +106,8 @@ public DeepgramApiHttpResponse list( Object errorBody = ObjectMappers.parseErrorBody(responseBodyString); throw new DeepgramHttpException( "Error with status code " + response.code(), response.code(), errorBody, response); + } catch (JsonProcessingException e) { + throw new DeepgramApiException("Failed to deserialize response: " + e.getMessage(), e); } catch (IOException e) { throw new DeepgramApiException("Network error executing HTTP request", e); } @@ -131,6 +143,15 @@ public DeepgramApiHttpResponse get(String modelId, RequestOp if (requestOptions != null && requestOptions.getTimeout().isPresent()) { client = clientOptions.httpClientWithTimeout(requestOptions); } + if (requestOptions != null && requestOptions.getMaxRetries().isPresent()) { + okhttpRequest = okhttpRequest + .newBuilder() + .tag( + RetryInterceptor.MaxRetriesOverride.class, + new RetryInterceptor.MaxRetriesOverride( + requestOptions.getMaxRetries().get())) + .build(); + } try (Response response = client.newCall(okhttpRequest).execute()) { ResponseBody responseBody = response.body(); String responseBodyString = responseBody != null ? responseBody.string() : "{}"; @@ -149,6 +170,8 @@ public DeepgramApiHttpResponse get(String modelId, RequestOp Object errorBody = ObjectMappers.parseErrorBody(responseBodyString); throw new DeepgramHttpException( "Error with status code " + response.code(), response.code(), errorBody, response); + } catch (JsonProcessingException e) { + throw new DeepgramApiException("Failed to deserialize response: " + e.getMessage(), e); } catch (IOException e) { throw new DeepgramApiException("Network error executing HTTP request", e); } diff --git a/src/main/java/com/deepgram/resources/manage/v1/projects/AsyncRawProjectsClient.java b/src/main/java/com/deepgram/resources/manage/v1/projects/AsyncRawProjectsClient.java index 4bc3e68..fd1eb5d 100644 --- a/src/main/java/com/deepgram/resources/manage/v1/projects/AsyncRawProjectsClient.java +++ b/src/main/java/com/deepgram/resources/manage/v1/projects/AsyncRawProjectsClient.java @@ -11,6 +11,7 @@ import com.deepgram.core.ObjectMappers; import com.deepgram.core.QueryStringMapper; import com.deepgram.core.RequestOptions; +import com.deepgram.core.RetryInterceptor; import com.deepgram.errors.BadRequestError; import com.deepgram.resources.manage.v1.projects.requests.ProjectsGetRequest; import com.deepgram.resources.manage.v1.projects.requests.UpdateProjectV1Request; @@ -69,6 +70,15 @@ public CompletableFuture> list(R if (requestOptions != null && requestOptions.getTimeout().isPresent()) { client = clientOptions.httpClientWithTimeout(requestOptions); } + if (requestOptions != null && requestOptions.getMaxRetries().isPresent()) { + okhttpRequest = okhttpRequest + .newBuilder() + .tag( + RetryInterceptor.MaxRetriesOverride.class, + new RetryInterceptor.MaxRetriesOverride( + requestOptions.getMaxRetries().get())) + .build(); + } CompletableFuture> future = new CompletableFuture<>(); client.newCall(okhttpRequest).enqueue(new Callback() { @Override @@ -94,6 +104,9 @@ public void onResponse(@NotNull Call call, @NotNull Response response) throws IO future.completeExceptionally(new DeepgramHttpException( "Error with status code " + response.code(), response.code(), errorBody, response)); return; + } catch (JsonProcessingException e) { + future.completeExceptionally( + new DeepgramApiException("Failed to deserialize response: " + e.getMessage(), e)); } catch (IOException e) { future.completeExceptionally(new DeepgramApiException("Network error executing HTTP request", e)); } @@ -162,6 +175,15 @@ public CompletableFuture> get( if (requestOptions != null && requestOptions.getTimeout().isPresent()) { client = clientOptions.httpClientWithTimeout(requestOptions); } + if (requestOptions != null && requestOptions.getMaxRetries().isPresent()) { + okhttpRequest = okhttpRequest + .newBuilder() + .tag( + RetryInterceptor.MaxRetriesOverride.class, + new RetryInterceptor.MaxRetriesOverride( + requestOptions.getMaxRetries().get())) + .build(); + } CompletableFuture> future = new CompletableFuture<>(); client.newCall(okhttpRequest).enqueue(new Callback() { @Override @@ -187,6 +209,9 @@ public void onResponse(@NotNull Call call, @NotNull Response response) throws IO future.completeExceptionally(new DeepgramHttpException( "Error with status code " + response.code(), response.code(), errorBody, response)); return; + } catch (JsonProcessingException e) { + future.completeExceptionally( + new DeepgramApiException("Failed to deserialize response: " + e.getMessage(), e)); } catch (IOException e) { future.completeExceptionally(new DeepgramApiException("Network error executing HTTP request", e)); } @@ -231,6 +256,15 @@ public CompletableFuture> delet if (requestOptions != null && requestOptions.getTimeout().isPresent()) { client = clientOptions.httpClientWithTimeout(requestOptions); } + if (requestOptions != null && requestOptions.getMaxRetries().isPresent()) { + okhttpRequest = okhttpRequest + .newBuilder() + .tag( + RetryInterceptor.MaxRetriesOverride.class, + new RetryInterceptor.MaxRetriesOverride( + requestOptions.getMaxRetries().get())) + .build(); + } CompletableFuture> future = new CompletableFuture<>(); client.newCall(okhttpRequest).enqueue(new Callback() { @Override @@ -256,6 +290,9 @@ public void onResponse(@NotNull Call call, @NotNull Response response) throws IO future.completeExceptionally(new DeepgramHttpException( "Error with status code " + response.code(), response.code(), errorBody, response)); return; + } catch (JsonProcessingException e) { + future.completeExceptionally( + new DeepgramApiException("Failed to deserialize response: " + e.getMessage(), e)); } catch (IOException e) { future.completeExceptionally(new DeepgramApiException("Network error executing HTTP request", e)); } @@ -324,6 +361,15 @@ public CompletableFuture> updat if (requestOptions != null && requestOptions.getTimeout().isPresent()) { client = clientOptions.httpClientWithTimeout(requestOptions); } + if (requestOptions != null && requestOptions.getMaxRetries().isPresent()) { + okhttpRequest = okhttpRequest + .newBuilder() + .tag( + RetryInterceptor.MaxRetriesOverride.class, + new RetryInterceptor.MaxRetriesOverride( + requestOptions.getMaxRetries().get())) + .build(); + } CompletableFuture> future = new CompletableFuture<>(); client.newCall(okhttpRequest).enqueue(new Callback() { @Override @@ -349,6 +395,9 @@ public void onResponse(@NotNull Call call, @NotNull Response response) throws IO future.completeExceptionally(new DeepgramHttpException( "Error with status code " + response.code(), response.code(), errorBody, response)); return; + } catch (JsonProcessingException e) { + future.completeExceptionally( + new DeepgramApiException("Failed to deserialize response: " + e.getMessage(), e)); } catch (IOException e) { future.completeExceptionally(new DeepgramApiException("Network error executing HTTP request", e)); } @@ -394,6 +443,15 @@ public CompletableFuture> leave( if (requestOptions != null && requestOptions.getTimeout().isPresent()) { client = clientOptions.httpClientWithTimeout(requestOptions); } + if (requestOptions != null && requestOptions.getMaxRetries().isPresent()) { + okhttpRequest = okhttpRequest + .newBuilder() + .tag( + RetryInterceptor.MaxRetriesOverride.class, + new RetryInterceptor.MaxRetriesOverride( + requestOptions.getMaxRetries().get())) + .build(); + } CompletableFuture> future = new CompletableFuture<>(); client.newCall(okhttpRequest).enqueue(new Callback() { @Override @@ -419,6 +477,9 @@ public void onResponse(@NotNull Call call, @NotNull Response response) throws IO future.completeExceptionally(new DeepgramHttpException( "Error with status code " + response.code(), response.code(), errorBody, response)); return; + } catch (JsonProcessingException e) { + future.completeExceptionally( + new DeepgramApiException("Failed to deserialize response: " + e.getMessage(), e)); } catch (IOException e) { future.completeExceptionally(new DeepgramApiException("Network error executing HTTP request", e)); } diff --git a/src/main/java/com/deepgram/resources/manage/v1/projects/RawProjectsClient.java b/src/main/java/com/deepgram/resources/manage/v1/projects/RawProjectsClient.java index 04a7fec..66a7d57 100644 --- a/src/main/java/com/deepgram/resources/manage/v1/projects/RawProjectsClient.java +++ b/src/main/java/com/deepgram/resources/manage/v1/projects/RawProjectsClient.java @@ -11,6 +11,7 @@ import com.deepgram.core.ObjectMappers; import com.deepgram.core.QueryStringMapper; import com.deepgram.core.RequestOptions; +import com.deepgram.core.RetryInterceptor; import com.deepgram.errors.BadRequestError; import com.deepgram.resources.manage.v1.projects.requests.ProjectsGetRequest; import com.deepgram.resources.manage.v1.projects.requests.UpdateProjectV1Request; @@ -65,6 +66,15 @@ public DeepgramApiHttpResponse list(RequestOptions reque if (requestOptions != null && requestOptions.getTimeout().isPresent()) { client = clientOptions.httpClientWithTimeout(requestOptions); } + if (requestOptions != null && requestOptions.getMaxRetries().isPresent()) { + okhttpRequest = okhttpRequest + .newBuilder() + .tag( + RetryInterceptor.MaxRetriesOverride.class, + new RetryInterceptor.MaxRetriesOverride( + requestOptions.getMaxRetries().get())) + .build(); + } try (Response response = client.newCall(okhttpRequest).execute()) { ResponseBody responseBody = response.body(); String responseBodyString = responseBody != null ? responseBody.string() : "{}"; @@ -84,6 +94,8 @@ public DeepgramApiHttpResponse list(RequestOptions reque Object errorBody = ObjectMappers.parseErrorBody(responseBodyString); throw new DeepgramHttpException( "Error with status code " + response.code(), response.code(), errorBody, response); + } catch (JsonProcessingException e) { + throw new DeepgramApiException("Failed to deserialize response: " + e.getMessage(), e); } catch (IOException e) { throw new DeepgramApiException("Network error executing HTTP request", e); } @@ -142,6 +154,15 @@ public DeepgramApiHttpResponse get( if (requestOptions != null && requestOptions.getTimeout().isPresent()) { client = clientOptions.httpClientWithTimeout(requestOptions); } + if (requestOptions != null && requestOptions.getMaxRetries().isPresent()) { + okhttpRequest = okhttpRequest + .newBuilder() + .tag( + RetryInterceptor.MaxRetriesOverride.class, + new RetryInterceptor.MaxRetriesOverride( + requestOptions.getMaxRetries().get())) + .build(); + } try (Response response = client.newCall(okhttpRequest).execute()) { ResponseBody responseBody = response.body(); String responseBodyString = responseBody != null ? responseBody.string() : "{}"; @@ -160,6 +181,8 @@ public DeepgramApiHttpResponse get( Object errorBody = ObjectMappers.parseErrorBody(responseBodyString); throw new DeepgramHttpException( "Error with status code " + response.code(), response.code(), errorBody, response); + } catch (JsonProcessingException e) { + throw new DeepgramApiException("Failed to deserialize response: " + e.getMessage(), e); } catch (IOException e) { throw new DeepgramApiException("Network error executing HTTP request", e); } @@ -195,6 +218,15 @@ public DeepgramApiHttpResponse delete(String projectId, if (requestOptions != null && requestOptions.getTimeout().isPresent()) { client = clientOptions.httpClientWithTimeout(requestOptions); } + if (requestOptions != null && requestOptions.getMaxRetries().isPresent()) { + okhttpRequest = okhttpRequest + .newBuilder() + .tag( + RetryInterceptor.MaxRetriesOverride.class, + new RetryInterceptor.MaxRetriesOverride( + requestOptions.getMaxRetries().get())) + .build(); + } try (Response response = client.newCall(okhttpRequest).execute()) { ResponseBody responseBody = response.body(); String responseBodyString = responseBody != null ? responseBody.string() : "{}"; @@ -214,6 +246,8 @@ public DeepgramApiHttpResponse delete(String projectId, Object errorBody = ObjectMappers.parseErrorBody(responseBodyString); throw new DeepgramHttpException( "Error with status code " + response.code(), response.code(), errorBody, response); + } catch (JsonProcessingException e) { + throw new DeepgramApiException("Failed to deserialize response: " + e.getMessage(), e); } catch (IOException e) { throw new DeepgramApiException("Network error executing HTTP request", e); } @@ -272,6 +306,15 @@ public DeepgramApiHttpResponse update( if (requestOptions != null && requestOptions.getTimeout().isPresent()) { client = clientOptions.httpClientWithTimeout(requestOptions); } + if (requestOptions != null && requestOptions.getMaxRetries().isPresent()) { + okhttpRequest = okhttpRequest + .newBuilder() + .tag( + RetryInterceptor.MaxRetriesOverride.class, + new RetryInterceptor.MaxRetriesOverride( + requestOptions.getMaxRetries().get())) + .build(); + } try (Response response = client.newCall(okhttpRequest).execute()) { ResponseBody responseBody = response.body(); String responseBodyString = responseBody != null ? responseBody.string() : "{}"; @@ -291,6 +334,8 @@ public DeepgramApiHttpResponse update( Object errorBody = ObjectMappers.parseErrorBody(responseBodyString); throw new DeepgramHttpException( "Error with status code " + response.code(), response.code(), errorBody, response); + } catch (JsonProcessingException e) { + throw new DeepgramApiException("Failed to deserialize response: " + e.getMessage(), e); } catch (IOException e) { throw new DeepgramApiException("Network error executing HTTP request", e); } @@ -327,6 +372,15 @@ public DeepgramApiHttpResponse leave(String projectId, R if (requestOptions != null && requestOptions.getTimeout().isPresent()) { client = clientOptions.httpClientWithTimeout(requestOptions); } + if (requestOptions != null && requestOptions.getMaxRetries().isPresent()) { + okhttpRequest = okhttpRequest + .newBuilder() + .tag( + RetryInterceptor.MaxRetriesOverride.class, + new RetryInterceptor.MaxRetriesOverride( + requestOptions.getMaxRetries().get())) + .build(); + } try (Response response = client.newCall(okhttpRequest).execute()) { ResponseBody responseBody = response.body(); String responseBodyString = responseBody != null ? responseBody.string() : "{}"; @@ -346,6 +400,8 @@ public DeepgramApiHttpResponse leave(String projectId, R Object errorBody = ObjectMappers.parseErrorBody(responseBodyString); throw new DeepgramHttpException( "Error with status code " + response.code(), response.code(), errorBody, response); + } catch (JsonProcessingException e) { + throw new DeepgramApiException("Failed to deserialize response: " + e.getMessage(), e); } catch (IOException e) { throw new DeepgramApiException("Network error executing HTTP request", e); } diff --git a/src/main/java/com/deepgram/resources/manage/v1/projects/billing/balances/AsyncRawBalancesClient.java b/src/main/java/com/deepgram/resources/manage/v1/projects/billing/balances/AsyncRawBalancesClient.java index accb8f7..653f007 100644 --- a/src/main/java/com/deepgram/resources/manage/v1/projects/billing/balances/AsyncRawBalancesClient.java +++ b/src/main/java/com/deepgram/resources/manage/v1/projects/billing/balances/AsyncRawBalancesClient.java @@ -9,6 +9,7 @@ import com.deepgram.core.DeepgramHttpException; import com.deepgram.core.ObjectMappers; import com.deepgram.core.RequestOptions; +import com.deepgram.core.RetryInterceptor; import com.deepgram.errors.BadRequestError; import com.deepgram.types.GetProjectBalanceV1Response; import com.deepgram.types.ListProjectBalancesV1Response; @@ -64,6 +65,15 @@ public CompletableFuture> if (requestOptions != null && requestOptions.getTimeout().isPresent()) { client = clientOptions.httpClientWithTimeout(requestOptions); } + if (requestOptions != null && requestOptions.getMaxRetries().isPresent()) { + okhttpRequest = okhttpRequest + .newBuilder() + .tag( + RetryInterceptor.MaxRetriesOverride.class, + new RetryInterceptor.MaxRetriesOverride( + requestOptions.getMaxRetries().get())) + .build(); + } CompletableFuture> future = new CompletableFuture<>(); client.newCall(okhttpRequest).enqueue(new Callback() { @Override @@ -90,6 +100,9 @@ public void onResponse(@NotNull Call call, @NotNull Response response) throws IO future.completeExceptionally(new DeepgramHttpException( "Error with status code " + response.code(), response.code(), errorBody, response)); return; + } catch (JsonProcessingException e) { + future.completeExceptionally( + new DeepgramApiException("Failed to deserialize response: " + e.getMessage(), e)); } catch (IOException e) { future.completeExceptionally(new DeepgramApiException("Network error executing HTTP request", e)); } @@ -137,6 +150,15 @@ public CompletableFuture> g if (requestOptions != null && requestOptions.getTimeout().isPresent()) { client = clientOptions.httpClientWithTimeout(requestOptions); } + if (requestOptions != null && requestOptions.getMaxRetries().isPresent()) { + okhttpRequest = okhttpRequest + .newBuilder() + .tag( + RetryInterceptor.MaxRetriesOverride.class, + new RetryInterceptor.MaxRetriesOverride( + requestOptions.getMaxRetries().get())) + .build(); + } CompletableFuture> future = new CompletableFuture<>(); client.newCall(okhttpRequest).enqueue(new Callback() { @Override @@ -163,6 +185,9 @@ public void onResponse(@NotNull Call call, @NotNull Response response) throws IO future.completeExceptionally(new DeepgramHttpException( "Error with status code " + response.code(), response.code(), errorBody, response)); return; + } catch (JsonProcessingException e) { + future.completeExceptionally( + new DeepgramApiException("Failed to deserialize response: " + e.getMessage(), e)); } catch (IOException e) { future.completeExceptionally(new DeepgramApiException("Network error executing HTTP request", e)); } diff --git a/src/main/java/com/deepgram/resources/manage/v1/projects/billing/balances/RawBalancesClient.java b/src/main/java/com/deepgram/resources/manage/v1/projects/billing/balances/RawBalancesClient.java index 8564529..9f6747c 100644 --- a/src/main/java/com/deepgram/resources/manage/v1/projects/billing/balances/RawBalancesClient.java +++ b/src/main/java/com/deepgram/resources/manage/v1/projects/billing/balances/RawBalancesClient.java @@ -9,6 +9,7 @@ import com.deepgram.core.DeepgramHttpException; import com.deepgram.core.ObjectMappers; import com.deepgram.core.RequestOptions; +import com.deepgram.core.RetryInterceptor; import com.deepgram.errors.BadRequestError; import com.deepgram.types.GetProjectBalanceV1Response; import com.deepgram.types.ListProjectBalancesV1Response; @@ -60,6 +61,15 @@ public DeepgramApiHttpResponse list( if (requestOptions != null && requestOptions.getTimeout().isPresent()) { client = clientOptions.httpClientWithTimeout(requestOptions); } + if (requestOptions != null && requestOptions.getMaxRetries().isPresent()) { + okhttpRequest = okhttpRequest + .newBuilder() + .tag( + RetryInterceptor.MaxRetriesOverride.class, + new RetryInterceptor.MaxRetriesOverride( + requestOptions.getMaxRetries().get())) + .build(); + } try (Response response = client.newCall(okhttpRequest).execute()) { ResponseBody responseBody = response.body(); String responseBodyString = responseBody != null ? responseBody.string() : "{}"; @@ -79,6 +89,8 @@ public DeepgramApiHttpResponse list( Object errorBody = ObjectMappers.parseErrorBody(responseBodyString); throw new DeepgramHttpException( "Error with status code " + response.code(), response.code(), errorBody, response); + } catch (JsonProcessingException e) { + throw new DeepgramApiException("Failed to deserialize response: " + e.getMessage(), e); } catch (IOException e) { throw new DeepgramApiException("Network error executing HTTP request", e); } @@ -117,6 +129,15 @@ public DeepgramApiHttpResponse get( if (requestOptions != null && requestOptions.getTimeout().isPresent()) { client = clientOptions.httpClientWithTimeout(requestOptions); } + if (requestOptions != null && requestOptions.getMaxRetries().isPresent()) { + okhttpRequest = okhttpRequest + .newBuilder() + .tag( + RetryInterceptor.MaxRetriesOverride.class, + new RetryInterceptor.MaxRetriesOverride( + requestOptions.getMaxRetries().get())) + .build(); + } try (Response response = client.newCall(okhttpRequest).execute()) { ResponseBody responseBody = response.body(); String responseBodyString = responseBody != null ? responseBody.string() : "{}"; @@ -136,6 +157,8 @@ public DeepgramApiHttpResponse get( Object errorBody = ObjectMappers.parseErrorBody(responseBodyString); throw new DeepgramHttpException( "Error with status code " + response.code(), response.code(), errorBody, response); + } catch (JsonProcessingException e) { + throw new DeepgramApiException("Failed to deserialize response: " + e.getMessage(), e); } catch (IOException e) { throw new DeepgramApiException("Network error executing HTTP request", e); } diff --git a/src/main/java/com/deepgram/resources/manage/v1/projects/billing/breakdown/AsyncRawBreakdownClient.java b/src/main/java/com/deepgram/resources/manage/v1/projects/billing/breakdown/AsyncRawBreakdownClient.java index cb3f70d..129fe40 100644 --- a/src/main/java/com/deepgram/resources/manage/v1/projects/billing/breakdown/AsyncRawBreakdownClient.java +++ b/src/main/java/com/deepgram/resources/manage/v1/projects/billing/breakdown/AsyncRawBreakdownClient.java @@ -10,6 +10,7 @@ import com.deepgram.core.ObjectMappers; import com.deepgram.core.QueryStringMapper; import com.deepgram.core.RequestOptions; +import com.deepgram.core.RetryInterceptor; import com.deepgram.errors.BadRequestError; import com.deepgram.resources.manage.v1.projects.billing.breakdown.requests.BreakdownListRequest; import com.deepgram.types.BillingBreakdownV1Response; @@ -108,6 +109,15 @@ public CompletableFuture> li if (requestOptions != null && requestOptions.getTimeout().isPresent()) { client = clientOptions.httpClientWithTimeout(requestOptions); } + if (requestOptions != null && requestOptions.getMaxRetries().isPresent()) { + okhttpRequest = okhttpRequest + .newBuilder() + .tag( + RetryInterceptor.MaxRetriesOverride.class, + new RetryInterceptor.MaxRetriesOverride( + requestOptions.getMaxRetries().get())) + .build(); + } CompletableFuture> future = new CompletableFuture<>(); client.newCall(okhttpRequest).enqueue(new Callback() { @Override @@ -134,6 +144,9 @@ public void onResponse(@NotNull Call call, @NotNull Response response) throws IO future.completeExceptionally(new DeepgramHttpException( "Error with status code " + response.code(), response.code(), errorBody, response)); return; + } catch (JsonProcessingException e) { + future.completeExceptionally( + new DeepgramApiException("Failed to deserialize response: " + e.getMessage(), e)); } catch (IOException e) { future.completeExceptionally(new DeepgramApiException("Network error executing HTTP request", e)); } diff --git a/src/main/java/com/deepgram/resources/manage/v1/projects/billing/breakdown/RawBreakdownClient.java b/src/main/java/com/deepgram/resources/manage/v1/projects/billing/breakdown/RawBreakdownClient.java index 333f331..75e687b 100644 --- a/src/main/java/com/deepgram/resources/manage/v1/projects/billing/breakdown/RawBreakdownClient.java +++ b/src/main/java/com/deepgram/resources/manage/v1/projects/billing/breakdown/RawBreakdownClient.java @@ -10,6 +10,7 @@ import com.deepgram.core.ObjectMappers; import com.deepgram.core.QueryStringMapper; import com.deepgram.core.RequestOptions; +import com.deepgram.core.RetryInterceptor; import com.deepgram.errors.BadRequestError; import com.deepgram.resources.manage.v1.projects.billing.breakdown.requests.BreakdownListRequest; import com.deepgram.types.BillingBreakdownV1Response; @@ -102,6 +103,15 @@ public DeepgramApiHttpResponse list( if (requestOptions != null && requestOptions.getTimeout().isPresent()) { client = clientOptions.httpClientWithTimeout(requestOptions); } + if (requestOptions != null && requestOptions.getMaxRetries().isPresent()) { + okhttpRequest = okhttpRequest + .newBuilder() + .tag( + RetryInterceptor.MaxRetriesOverride.class, + new RetryInterceptor.MaxRetriesOverride( + requestOptions.getMaxRetries().get())) + .build(); + } try (Response response = client.newCall(okhttpRequest).execute()) { ResponseBody responseBody = response.body(); String responseBodyString = responseBody != null ? responseBody.string() : "{}"; @@ -121,6 +131,8 @@ public DeepgramApiHttpResponse list( Object errorBody = ObjectMappers.parseErrorBody(responseBodyString); throw new DeepgramHttpException( "Error with status code " + response.code(), response.code(), errorBody, response); + } catch (JsonProcessingException e) { + throw new DeepgramApiException("Failed to deserialize response: " + e.getMessage(), e); } catch (IOException e) { throw new DeepgramApiException("Network error executing HTTP request", e); } diff --git a/src/main/java/com/deepgram/resources/manage/v1/projects/billing/fields/AsyncRawFieldsClient.java b/src/main/java/com/deepgram/resources/manage/v1/projects/billing/fields/AsyncRawFieldsClient.java index 56ee924..354ac20 100644 --- a/src/main/java/com/deepgram/resources/manage/v1/projects/billing/fields/AsyncRawFieldsClient.java +++ b/src/main/java/com/deepgram/resources/manage/v1/projects/billing/fields/AsyncRawFieldsClient.java @@ -10,6 +10,7 @@ import com.deepgram.core.ObjectMappers; import com.deepgram.core.QueryStringMapper; import com.deepgram.core.RequestOptions; +import com.deepgram.core.RetryInterceptor; import com.deepgram.errors.BadRequestError; import com.deepgram.resources.manage.v1.projects.billing.fields.requests.FieldsListRequest; import com.deepgram.types.ListBillingFieldsV1Response; @@ -89,6 +90,15 @@ public CompletableFuture> l if (requestOptions != null && requestOptions.getTimeout().isPresent()) { client = clientOptions.httpClientWithTimeout(requestOptions); } + if (requestOptions != null && requestOptions.getMaxRetries().isPresent()) { + okhttpRequest = okhttpRequest + .newBuilder() + .tag( + RetryInterceptor.MaxRetriesOverride.class, + new RetryInterceptor.MaxRetriesOverride( + requestOptions.getMaxRetries().get())) + .build(); + } CompletableFuture> future = new CompletableFuture<>(); client.newCall(okhttpRequest).enqueue(new Callback() { @Override @@ -115,6 +125,9 @@ public void onResponse(@NotNull Call call, @NotNull Response response) throws IO future.completeExceptionally(new DeepgramHttpException( "Error with status code " + response.code(), response.code(), errorBody, response)); return; + } catch (JsonProcessingException e) { + future.completeExceptionally( + new DeepgramApiException("Failed to deserialize response: " + e.getMessage(), e)); } catch (IOException e) { future.completeExceptionally(new DeepgramApiException("Network error executing HTTP request", e)); } diff --git a/src/main/java/com/deepgram/resources/manage/v1/projects/billing/fields/RawFieldsClient.java b/src/main/java/com/deepgram/resources/manage/v1/projects/billing/fields/RawFieldsClient.java index 4b8ad14..22b9486 100644 --- a/src/main/java/com/deepgram/resources/manage/v1/projects/billing/fields/RawFieldsClient.java +++ b/src/main/java/com/deepgram/resources/manage/v1/projects/billing/fields/RawFieldsClient.java @@ -10,6 +10,7 @@ import com.deepgram.core.ObjectMappers; import com.deepgram.core.QueryStringMapper; import com.deepgram.core.RequestOptions; +import com.deepgram.core.RetryInterceptor; import com.deepgram.errors.BadRequestError; import com.deepgram.resources.manage.v1.projects.billing.fields.requests.FieldsListRequest; import com.deepgram.types.ListBillingFieldsV1Response; @@ -83,6 +84,15 @@ public DeepgramApiHttpResponse list( if (requestOptions != null && requestOptions.getTimeout().isPresent()) { client = clientOptions.httpClientWithTimeout(requestOptions); } + if (requestOptions != null && requestOptions.getMaxRetries().isPresent()) { + okhttpRequest = okhttpRequest + .newBuilder() + .tag( + RetryInterceptor.MaxRetriesOverride.class, + new RetryInterceptor.MaxRetriesOverride( + requestOptions.getMaxRetries().get())) + .build(); + } try (Response response = client.newCall(okhttpRequest).execute()) { ResponseBody responseBody = response.body(); String responseBodyString = responseBody != null ? responseBody.string() : "{}"; @@ -102,6 +112,8 @@ public DeepgramApiHttpResponse list( Object errorBody = ObjectMappers.parseErrorBody(responseBodyString); throw new DeepgramHttpException( "Error with status code " + response.code(), response.code(), errorBody, response); + } catch (JsonProcessingException e) { + throw new DeepgramApiException("Failed to deserialize response: " + e.getMessage(), e); } catch (IOException e) { throw new DeepgramApiException("Network error executing HTTP request", e); } diff --git a/src/main/java/com/deepgram/resources/manage/v1/projects/billing/purchases/AsyncRawPurchasesClient.java b/src/main/java/com/deepgram/resources/manage/v1/projects/billing/purchases/AsyncRawPurchasesClient.java index f521f9d..36fc940 100644 --- a/src/main/java/com/deepgram/resources/manage/v1/projects/billing/purchases/AsyncRawPurchasesClient.java +++ b/src/main/java/com/deepgram/resources/manage/v1/projects/billing/purchases/AsyncRawPurchasesClient.java @@ -10,6 +10,7 @@ import com.deepgram.core.ObjectMappers; import com.deepgram.core.QueryStringMapper; import com.deepgram.core.RequestOptions; +import com.deepgram.core.RetryInterceptor; import com.deepgram.errors.BadRequestError; import com.deepgram.resources.manage.v1.projects.billing.purchases.requests.PurchasesListRequest; import com.deepgram.types.ListProjectPurchasesV1Response; @@ -85,6 +86,15 @@ public CompletableFuture if (requestOptions != null && requestOptions.getTimeout().isPresent()) { client = clientOptions.httpClientWithTimeout(requestOptions); } + if (requestOptions != null && requestOptions.getMaxRetries().isPresent()) { + okhttpRequest = okhttpRequest + .newBuilder() + .tag( + RetryInterceptor.MaxRetriesOverride.class, + new RetryInterceptor.MaxRetriesOverride( + requestOptions.getMaxRetries().get())) + .build(); + } CompletableFuture> future = new CompletableFuture<>(); client.newCall(okhttpRequest).enqueue(new Callback() { @Override @@ -111,6 +121,9 @@ public void onResponse(@NotNull Call call, @NotNull Response response) throws IO future.completeExceptionally(new DeepgramHttpException( "Error with status code " + response.code(), response.code(), errorBody, response)); return; + } catch (JsonProcessingException e) { + future.completeExceptionally( + new DeepgramApiException("Failed to deserialize response: " + e.getMessage(), e)); } catch (IOException e) { future.completeExceptionally(new DeepgramApiException("Network error executing HTTP request", e)); } diff --git a/src/main/java/com/deepgram/resources/manage/v1/projects/billing/purchases/RawPurchasesClient.java b/src/main/java/com/deepgram/resources/manage/v1/projects/billing/purchases/RawPurchasesClient.java index 6df387f..6ec1db4 100644 --- a/src/main/java/com/deepgram/resources/manage/v1/projects/billing/purchases/RawPurchasesClient.java +++ b/src/main/java/com/deepgram/resources/manage/v1/projects/billing/purchases/RawPurchasesClient.java @@ -10,6 +10,7 @@ import com.deepgram.core.ObjectMappers; import com.deepgram.core.QueryStringMapper; import com.deepgram.core.RequestOptions; +import com.deepgram.core.RetryInterceptor; import com.deepgram.errors.BadRequestError; import com.deepgram.resources.manage.v1.projects.billing.purchases.requests.PurchasesListRequest; import com.deepgram.types.ListProjectPurchasesV1Response; @@ -81,6 +82,15 @@ public DeepgramApiHttpResponse list( if (requestOptions != null && requestOptions.getTimeout().isPresent()) { client = clientOptions.httpClientWithTimeout(requestOptions); } + if (requestOptions != null && requestOptions.getMaxRetries().isPresent()) { + okhttpRequest = okhttpRequest + .newBuilder() + .tag( + RetryInterceptor.MaxRetriesOverride.class, + new RetryInterceptor.MaxRetriesOverride( + requestOptions.getMaxRetries().get())) + .build(); + } try (Response response = client.newCall(okhttpRequest).execute()) { ResponseBody responseBody = response.body(); String responseBodyString = responseBody != null ? responseBody.string() : "{}"; @@ -100,6 +110,8 @@ public DeepgramApiHttpResponse list( Object errorBody = ObjectMappers.parseErrorBody(responseBodyString); throw new DeepgramHttpException( "Error with status code " + response.code(), response.code(), errorBody, response); + } catch (JsonProcessingException e) { + throw new DeepgramApiException("Failed to deserialize response: " + e.getMessage(), e); } catch (IOException e) { throw new DeepgramApiException("Network error executing HTTP request", e); } diff --git a/src/main/java/com/deepgram/resources/manage/v1/projects/keys/AsyncRawKeysClient.java b/src/main/java/com/deepgram/resources/manage/v1/projects/keys/AsyncRawKeysClient.java index 8d4ce47..985ef3d 100644 --- a/src/main/java/com/deepgram/resources/manage/v1/projects/keys/AsyncRawKeysClient.java +++ b/src/main/java/com/deepgram/resources/manage/v1/projects/keys/AsyncRawKeysClient.java @@ -11,6 +11,7 @@ import com.deepgram.core.ObjectMappers; import com.deepgram.core.QueryStringMapper; import com.deepgram.core.RequestOptions; +import com.deepgram.core.RetryInterceptor; import com.deepgram.errors.BadRequestError; import com.deepgram.resources.manage.v1.projects.keys.requests.KeysListRequest; import com.deepgram.types.CreateKeyV1Request; @@ -91,6 +92,15 @@ public CompletableFuture> lis if (requestOptions != null && requestOptions.getTimeout().isPresent()) { client = clientOptions.httpClientWithTimeout(requestOptions); } + if (requestOptions != null && requestOptions.getMaxRetries().isPresent()) { + okhttpRequest = okhttpRequest + .newBuilder() + .tag( + RetryInterceptor.MaxRetriesOverride.class, + new RetryInterceptor.MaxRetriesOverride( + requestOptions.getMaxRetries().get())) + .build(); + } CompletableFuture> future = new CompletableFuture<>(); client.newCall(okhttpRequest).enqueue(new Callback() { @Override @@ -117,6 +127,9 @@ public void onResponse(@NotNull Call call, @NotNull Response response) throws IO future.completeExceptionally(new DeepgramHttpException( "Error with status code " + response.code(), response.code(), errorBody, response)); return; + } catch (JsonProcessingException e) { + future.completeExceptionally( + new DeepgramApiException("Failed to deserialize response: " + e.getMessage(), e)); } catch (IOException e) { future.completeExceptionally(new DeepgramApiException("Network error executing HTTP request", e)); } @@ -171,6 +184,15 @@ public CompletableFuture> create( if (requestOptions != null && requestOptions.getTimeout().isPresent()) { client = clientOptions.httpClientWithTimeout(requestOptions); } + if (requestOptions != null && requestOptions.getMaxRetries().isPresent()) { + okhttpRequest = okhttpRequest + .newBuilder() + .tag( + RetryInterceptor.MaxRetriesOverride.class, + new RetryInterceptor.MaxRetriesOverride( + requestOptions.getMaxRetries().get())) + .build(); + } CompletableFuture> future = new CompletableFuture<>(); client.newCall(okhttpRequest).enqueue(new Callback() { @Override @@ -196,6 +218,9 @@ public void onResponse(@NotNull Call call, @NotNull Response response) throws IO future.completeExceptionally(new DeepgramHttpException( "Error with status code " + response.code(), response.code(), errorBody, response)); return; + } catch (JsonProcessingException e) { + future.completeExceptionally( + new DeepgramApiException("Failed to deserialize response: " + e.getMessage(), e)); } catch (IOException e) { future.completeExceptionally(new DeepgramApiException("Network error executing HTTP request", e)); } @@ -242,6 +267,15 @@ public CompletableFuture> get( if (requestOptions != null && requestOptions.getTimeout().isPresent()) { client = clientOptions.httpClientWithTimeout(requestOptions); } + if (requestOptions != null && requestOptions.getMaxRetries().isPresent()) { + okhttpRequest = okhttpRequest + .newBuilder() + .tag( + RetryInterceptor.MaxRetriesOverride.class, + new RetryInterceptor.MaxRetriesOverride( + requestOptions.getMaxRetries().get())) + .build(); + } CompletableFuture> future = new CompletableFuture<>(); client.newCall(okhttpRequest).enqueue(new Callback() { @Override @@ -267,6 +301,9 @@ public void onResponse(@NotNull Call call, @NotNull Response response) throws IO future.completeExceptionally(new DeepgramHttpException( "Error with status code " + response.code(), response.code(), errorBody, response)); return; + } catch (JsonProcessingException e) { + future.completeExceptionally( + new DeepgramApiException("Failed to deserialize response: " + e.getMessage(), e)); } catch (IOException e) { future.completeExceptionally(new DeepgramApiException("Network error executing HTTP request", e)); } @@ -314,6 +351,15 @@ public CompletableFuture> de if (requestOptions != null && requestOptions.getTimeout().isPresent()) { client = clientOptions.httpClientWithTimeout(requestOptions); } + if (requestOptions != null && requestOptions.getMaxRetries().isPresent()) { + okhttpRequest = okhttpRequest + .newBuilder() + .tag( + RetryInterceptor.MaxRetriesOverride.class, + new RetryInterceptor.MaxRetriesOverride( + requestOptions.getMaxRetries().get())) + .build(); + } CompletableFuture> future = new CompletableFuture<>(); client.newCall(okhttpRequest).enqueue(new Callback() { @Override @@ -340,6 +386,9 @@ public void onResponse(@NotNull Call call, @NotNull Response response) throws IO future.completeExceptionally(new DeepgramHttpException( "Error with status code " + response.code(), response.code(), errorBody, response)); return; + } catch (JsonProcessingException e) { + future.completeExceptionally( + new DeepgramApiException("Failed to deserialize response: " + e.getMessage(), e)); } catch (IOException e) { future.completeExceptionally(new DeepgramApiException("Network error executing HTTP request", e)); } diff --git a/src/main/java/com/deepgram/resources/manage/v1/projects/keys/RawKeysClient.java b/src/main/java/com/deepgram/resources/manage/v1/projects/keys/RawKeysClient.java index fc2e5f4..a477388 100644 --- a/src/main/java/com/deepgram/resources/manage/v1/projects/keys/RawKeysClient.java +++ b/src/main/java/com/deepgram/resources/manage/v1/projects/keys/RawKeysClient.java @@ -11,6 +11,7 @@ import com.deepgram.core.ObjectMappers; import com.deepgram.core.QueryStringMapper; import com.deepgram.core.RequestOptions; +import com.deepgram.core.RetryInterceptor; import com.deepgram.errors.BadRequestError; import com.deepgram.resources.manage.v1.projects.keys.requests.KeysListRequest; import com.deepgram.types.CreateKeyV1Request; @@ -85,6 +86,15 @@ public DeepgramApiHttpResponse list( if (requestOptions != null && requestOptions.getTimeout().isPresent()) { client = clientOptions.httpClientWithTimeout(requestOptions); } + if (requestOptions != null && requestOptions.getMaxRetries().isPresent()) { + okhttpRequest = okhttpRequest + .newBuilder() + .tag( + RetryInterceptor.MaxRetriesOverride.class, + new RetryInterceptor.MaxRetriesOverride( + requestOptions.getMaxRetries().get())) + .build(); + } try (Response response = client.newCall(okhttpRequest).execute()) { ResponseBody responseBody = response.body(); String responseBodyString = responseBody != null ? responseBody.string() : "{}"; @@ -104,6 +114,8 @@ public DeepgramApiHttpResponse list( Object errorBody = ObjectMappers.parseErrorBody(responseBodyString); throw new DeepgramHttpException( "Error with status code " + response.code(), response.code(), errorBody, response); + } catch (JsonProcessingException e) { + throw new DeepgramApiException("Failed to deserialize response: " + e.getMessage(), e); } catch (IOException e) { throw new DeepgramApiException("Network error executing HTTP request", e); } @@ -149,6 +161,15 @@ public DeepgramApiHttpResponse create( if (requestOptions != null && requestOptions.getTimeout().isPresent()) { client = clientOptions.httpClientWithTimeout(requestOptions); } + if (requestOptions != null && requestOptions.getMaxRetries().isPresent()) { + okhttpRequest = okhttpRequest + .newBuilder() + .tag( + RetryInterceptor.MaxRetriesOverride.class, + new RetryInterceptor.MaxRetriesOverride( + requestOptions.getMaxRetries().get())) + .build(); + } try (Response response = client.newCall(okhttpRequest).execute()) { ResponseBody responseBody = response.body(); String responseBodyString = responseBody != null ? responseBody.string() : "{}"; @@ -167,6 +188,8 @@ public DeepgramApiHttpResponse create( Object errorBody = ObjectMappers.parseErrorBody(responseBodyString); throw new DeepgramHttpException( "Error with status code " + response.code(), response.code(), errorBody, response); + } catch (JsonProcessingException e) { + throw new DeepgramApiException("Failed to deserialize response: " + e.getMessage(), e); } catch (IOException e) { throw new DeepgramApiException("Network error executing HTTP request", e); } @@ -205,6 +228,15 @@ public DeepgramApiHttpResponse get( if (requestOptions != null && requestOptions.getTimeout().isPresent()) { client = clientOptions.httpClientWithTimeout(requestOptions); } + if (requestOptions != null && requestOptions.getMaxRetries().isPresent()) { + okhttpRequest = okhttpRequest + .newBuilder() + .tag( + RetryInterceptor.MaxRetriesOverride.class, + new RetryInterceptor.MaxRetriesOverride( + requestOptions.getMaxRetries().get())) + .build(); + } try (Response response = client.newCall(okhttpRequest).execute()) { ResponseBody responseBody = response.body(); String responseBodyString = responseBody != null ? responseBody.string() : "{}"; @@ -224,6 +256,8 @@ public DeepgramApiHttpResponse get( Object errorBody = ObjectMappers.parseErrorBody(responseBodyString); throw new DeepgramHttpException( "Error with status code " + response.code(), response.code(), errorBody, response); + } catch (JsonProcessingException e) { + throw new DeepgramApiException("Failed to deserialize response: " + e.getMessage(), e); } catch (IOException e) { throw new DeepgramApiException("Network error executing HTTP request", e); } @@ -262,6 +296,15 @@ public DeepgramApiHttpResponse delete( if (requestOptions != null && requestOptions.getTimeout().isPresent()) { client = clientOptions.httpClientWithTimeout(requestOptions); } + if (requestOptions != null && requestOptions.getMaxRetries().isPresent()) { + okhttpRequest = okhttpRequest + .newBuilder() + .tag( + RetryInterceptor.MaxRetriesOverride.class, + new RetryInterceptor.MaxRetriesOverride( + requestOptions.getMaxRetries().get())) + .build(); + } try (Response response = client.newCall(okhttpRequest).execute()) { ResponseBody responseBody = response.body(); String responseBodyString = responseBody != null ? responseBody.string() : "{}"; @@ -281,6 +324,8 @@ public DeepgramApiHttpResponse delete( Object errorBody = ObjectMappers.parseErrorBody(responseBodyString); throw new DeepgramHttpException( "Error with status code " + response.code(), response.code(), errorBody, response); + } catch (JsonProcessingException e) { + throw new DeepgramApiException("Failed to deserialize response: " + e.getMessage(), e); } catch (IOException e) { throw new DeepgramApiException("Network error executing HTTP request", e); } diff --git a/src/main/java/com/deepgram/resources/manage/v1/projects/members/AsyncRawMembersClient.java b/src/main/java/com/deepgram/resources/manage/v1/projects/members/AsyncRawMembersClient.java index c150e33..f9dabda 100644 --- a/src/main/java/com/deepgram/resources/manage/v1/projects/members/AsyncRawMembersClient.java +++ b/src/main/java/com/deepgram/resources/manage/v1/projects/members/AsyncRawMembersClient.java @@ -9,6 +9,7 @@ import com.deepgram.core.DeepgramHttpException; import com.deepgram.core.ObjectMappers; import com.deepgram.core.RequestOptions; +import com.deepgram.core.RetryInterceptor; import com.deepgram.errors.BadRequestError; import com.deepgram.types.DeleteProjectMemberV1Response; import com.deepgram.types.ListProjectMembersV1Response; @@ -64,6 +65,15 @@ public CompletableFuture> if (requestOptions != null && requestOptions.getTimeout().isPresent()) { client = clientOptions.httpClientWithTimeout(requestOptions); } + if (requestOptions != null && requestOptions.getMaxRetries().isPresent()) { + okhttpRequest = okhttpRequest + .newBuilder() + .tag( + RetryInterceptor.MaxRetriesOverride.class, + new RetryInterceptor.MaxRetriesOverride( + requestOptions.getMaxRetries().get())) + .build(); + } CompletableFuture> future = new CompletableFuture<>(); client.newCall(okhttpRequest).enqueue(new Callback() { @Override @@ -90,6 +100,9 @@ public void onResponse(@NotNull Call call, @NotNull Response response) throws IO future.completeExceptionally(new DeepgramHttpException( "Error with status code " + response.code(), response.code(), errorBody, response)); return; + } catch (JsonProcessingException e) { + future.completeExceptionally( + new DeepgramApiException("Failed to deserialize response: " + e.getMessage(), e)); } catch (IOException e) { future.completeExceptionally(new DeepgramApiException("Network error executing HTTP request", e)); } @@ -137,6 +150,15 @@ public CompletableFuture> if (requestOptions != null && requestOptions.getTimeout().isPresent()) { client = clientOptions.httpClientWithTimeout(requestOptions); } + if (requestOptions != null && requestOptions.getMaxRetries().isPresent()) { + okhttpRequest = okhttpRequest + .newBuilder() + .tag( + RetryInterceptor.MaxRetriesOverride.class, + new RetryInterceptor.MaxRetriesOverride( + requestOptions.getMaxRetries().get())) + .build(); + } CompletableFuture> future = new CompletableFuture<>(); client.newCall(okhttpRequest).enqueue(new Callback() { @Override @@ -163,6 +185,9 @@ public void onResponse(@NotNull Call call, @NotNull Response response) throws IO future.completeExceptionally(new DeepgramHttpException( "Error with status code " + response.code(), response.code(), errorBody, response)); return; + } catch (JsonProcessingException e) { + future.completeExceptionally( + new DeepgramApiException("Failed to deserialize response: " + e.getMessage(), e)); } catch (IOException e) { future.completeExceptionally(new DeepgramApiException("Network error executing HTTP request", e)); } diff --git a/src/main/java/com/deepgram/resources/manage/v1/projects/members/RawMembersClient.java b/src/main/java/com/deepgram/resources/manage/v1/projects/members/RawMembersClient.java index ac79ae0..c9dbb22 100644 --- a/src/main/java/com/deepgram/resources/manage/v1/projects/members/RawMembersClient.java +++ b/src/main/java/com/deepgram/resources/manage/v1/projects/members/RawMembersClient.java @@ -9,6 +9,7 @@ import com.deepgram.core.DeepgramHttpException; import com.deepgram.core.ObjectMappers; import com.deepgram.core.RequestOptions; +import com.deepgram.core.RetryInterceptor; import com.deepgram.errors.BadRequestError; import com.deepgram.types.DeleteProjectMemberV1Response; import com.deepgram.types.ListProjectMembersV1Response; @@ -59,6 +60,15 @@ public DeepgramApiHttpResponse list(String project if (requestOptions != null && requestOptions.getTimeout().isPresent()) { client = clientOptions.httpClientWithTimeout(requestOptions); } + if (requestOptions != null && requestOptions.getMaxRetries().isPresent()) { + okhttpRequest = okhttpRequest + .newBuilder() + .tag( + RetryInterceptor.MaxRetriesOverride.class, + new RetryInterceptor.MaxRetriesOverride( + requestOptions.getMaxRetries().get())) + .build(); + } try (Response response = client.newCall(okhttpRequest).execute()) { ResponseBody responseBody = response.body(); String responseBodyString = responseBody != null ? responseBody.string() : "{}"; @@ -78,6 +88,8 @@ public DeepgramApiHttpResponse list(String project Object errorBody = ObjectMappers.parseErrorBody(responseBodyString); throw new DeepgramHttpException( "Error with status code " + response.code(), response.code(), errorBody, response); + } catch (JsonProcessingException e) { + throw new DeepgramApiException("Failed to deserialize response: " + e.getMessage(), e); } catch (IOException e) { throw new DeepgramApiException("Network error executing HTTP request", e); } @@ -116,6 +128,15 @@ public DeepgramApiHttpResponse delete( if (requestOptions != null && requestOptions.getTimeout().isPresent()) { client = clientOptions.httpClientWithTimeout(requestOptions); } + if (requestOptions != null && requestOptions.getMaxRetries().isPresent()) { + okhttpRequest = okhttpRequest + .newBuilder() + .tag( + RetryInterceptor.MaxRetriesOverride.class, + new RetryInterceptor.MaxRetriesOverride( + requestOptions.getMaxRetries().get())) + .build(); + } try (Response response = client.newCall(okhttpRequest).execute()) { ResponseBody responseBody = response.body(); String responseBodyString = responseBody != null ? responseBody.string() : "{}"; @@ -135,6 +156,8 @@ public DeepgramApiHttpResponse delete( Object errorBody = ObjectMappers.parseErrorBody(responseBodyString); throw new DeepgramHttpException( "Error with status code " + response.code(), response.code(), errorBody, response); + } catch (JsonProcessingException e) { + throw new DeepgramApiException("Failed to deserialize response: " + e.getMessage(), e); } catch (IOException e) { throw new DeepgramApiException("Network error executing HTTP request", e); } diff --git a/src/main/java/com/deepgram/resources/manage/v1/projects/members/invites/AsyncRawInvitesClient.java b/src/main/java/com/deepgram/resources/manage/v1/projects/members/invites/AsyncRawInvitesClient.java index 008c633..a91b624 100644 --- a/src/main/java/com/deepgram/resources/manage/v1/projects/members/invites/AsyncRawInvitesClient.java +++ b/src/main/java/com/deepgram/resources/manage/v1/projects/members/invites/AsyncRawInvitesClient.java @@ -10,6 +10,7 @@ import com.deepgram.core.MediaTypes; import com.deepgram.core.ObjectMappers; import com.deepgram.core.RequestOptions; +import com.deepgram.core.RetryInterceptor; import com.deepgram.errors.BadRequestError; import com.deepgram.resources.manage.v1.projects.members.invites.requests.CreateProjectInviteV1Request; import com.deepgram.types.CreateProjectInviteV1Response; @@ -68,6 +69,15 @@ public CompletableFuture> if (requestOptions != null && requestOptions.getTimeout().isPresent()) { client = clientOptions.httpClientWithTimeout(requestOptions); } + if (requestOptions != null && requestOptions.getMaxRetries().isPresent()) { + okhttpRequest = okhttpRequest + .newBuilder() + .tag( + RetryInterceptor.MaxRetriesOverride.class, + new RetryInterceptor.MaxRetriesOverride( + requestOptions.getMaxRetries().get())) + .build(); + } CompletableFuture> future = new CompletableFuture<>(); client.newCall(okhttpRequest).enqueue(new Callback() { @Override @@ -94,6 +104,9 @@ public void onResponse(@NotNull Call call, @NotNull Response response) throws IO future.completeExceptionally(new DeepgramHttpException( "Error with status code " + response.code(), response.code(), errorBody, response)); return; + } catch (JsonProcessingException e) { + future.completeExceptionally( + new DeepgramApiException("Failed to deserialize response: " + e.getMessage(), e)); } catch (IOException e) { future.completeExceptionally(new DeepgramApiException("Network error executing HTTP request", e)); } @@ -148,6 +161,15 @@ public CompletableFuture> if (requestOptions != null && requestOptions.getTimeout().isPresent()) { client = clientOptions.httpClientWithTimeout(requestOptions); } + if (requestOptions != null && requestOptions.getMaxRetries().isPresent()) { + okhttpRequest = okhttpRequest + .newBuilder() + .tag( + RetryInterceptor.MaxRetriesOverride.class, + new RetryInterceptor.MaxRetriesOverride( + requestOptions.getMaxRetries().get())) + .build(); + } CompletableFuture> future = new CompletableFuture<>(); client.newCall(okhttpRequest).enqueue(new Callback() { @Override @@ -174,6 +196,9 @@ public void onResponse(@NotNull Call call, @NotNull Response response) throws IO future.completeExceptionally(new DeepgramHttpException( "Error with status code " + response.code(), response.code(), errorBody, response)); return; + } catch (JsonProcessingException e) { + future.completeExceptionally( + new DeepgramApiException("Failed to deserialize response: " + e.getMessage(), e)); } catch (IOException e) { future.completeExceptionally(new DeepgramApiException("Network error executing HTTP request", e)); } @@ -221,6 +246,15 @@ public CompletableFuture> if (requestOptions != null && requestOptions.getTimeout().isPresent()) { client = clientOptions.httpClientWithTimeout(requestOptions); } + if (requestOptions != null && requestOptions.getMaxRetries().isPresent()) { + okhttpRequest = okhttpRequest + .newBuilder() + .tag( + RetryInterceptor.MaxRetriesOverride.class, + new RetryInterceptor.MaxRetriesOverride( + requestOptions.getMaxRetries().get())) + .build(); + } CompletableFuture> future = new CompletableFuture<>(); client.newCall(okhttpRequest).enqueue(new Callback() { @Override @@ -247,6 +281,9 @@ public void onResponse(@NotNull Call call, @NotNull Response response) throws IO future.completeExceptionally(new DeepgramHttpException( "Error with status code " + response.code(), response.code(), errorBody, response)); return; + } catch (JsonProcessingException e) { + future.completeExceptionally( + new DeepgramApiException("Failed to deserialize response: " + e.getMessage(), e)); } catch (IOException e) { future.completeExceptionally(new DeepgramApiException("Network error executing HTTP request", e)); } diff --git a/src/main/java/com/deepgram/resources/manage/v1/projects/members/invites/RawInvitesClient.java b/src/main/java/com/deepgram/resources/manage/v1/projects/members/invites/RawInvitesClient.java index fdd2bd8..e9421f6 100644 --- a/src/main/java/com/deepgram/resources/manage/v1/projects/members/invites/RawInvitesClient.java +++ b/src/main/java/com/deepgram/resources/manage/v1/projects/members/invites/RawInvitesClient.java @@ -10,6 +10,7 @@ import com.deepgram.core.MediaTypes; import com.deepgram.core.ObjectMappers; import com.deepgram.core.RequestOptions; +import com.deepgram.core.RetryInterceptor; import com.deepgram.errors.BadRequestError; import com.deepgram.resources.manage.v1.projects.members.invites.requests.CreateProjectInviteV1Request; import com.deepgram.types.CreateProjectInviteV1Response; @@ -63,6 +64,15 @@ public DeepgramApiHttpResponse list(String project if (requestOptions != null && requestOptions.getTimeout().isPresent()) { client = clientOptions.httpClientWithTimeout(requestOptions); } + if (requestOptions != null && requestOptions.getMaxRetries().isPresent()) { + okhttpRequest = okhttpRequest + .newBuilder() + .tag( + RetryInterceptor.MaxRetriesOverride.class, + new RetryInterceptor.MaxRetriesOverride( + requestOptions.getMaxRetries().get())) + .build(); + } try (Response response = client.newCall(okhttpRequest).execute()) { ResponseBody responseBody = response.body(); String responseBodyString = responseBody != null ? responseBody.string() : "{}"; @@ -82,6 +92,8 @@ public DeepgramApiHttpResponse list(String project Object errorBody = ObjectMappers.parseErrorBody(responseBodyString); throw new DeepgramHttpException( "Error with status code " + response.code(), response.code(), errorBody, response); + } catch (JsonProcessingException e) { + throw new DeepgramApiException("Failed to deserialize response: " + e.getMessage(), e); } catch (IOException e) { throw new DeepgramApiException("Network error executing HTTP request", e); } @@ -128,6 +140,15 @@ public DeepgramApiHttpResponse create( if (requestOptions != null && requestOptions.getTimeout().isPresent()) { client = clientOptions.httpClientWithTimeout(requestOptions); } + if (requestOptions != null && requestOptions.getMaxRetries().isPresent()) { + okhttpRequest = okhttpRequest + .newBuilder() + .tag( + RetryInterceptor.MaxRetriesOverride.class, + new RetryInterceptor.MaxRetriesOverride( + requestOptions.getMaxRetries().get())) + .build(); + } try (Response response = client.newCall(okhttpRequest).execute()) { ResponseBody responseBody = response.body(); String responseBodyString = responseBody != null ? responseBody.string() : "{}"; @@ -147,6 +168,8 @@ public DeepgramApiHttpResponse create( Object errorBody = ObjectMappers.parseErrorBody(responseBodyString); throw new DeepgramHttpException( "Error with status code " + response.code(), response.code(), errorBody, response); + } catch (JsonProcessingException e) { + throw new DeepgramApiException("Failed to deserialize response: " + e.getMessage(), e); } catch (IOException e) { throw new DeepgramApiException("Network error executing HTTP request", e); } @@ -185,6 +208,15 @@ public DeepgramApiHttpResponse delete( if (requestOptions != null && requestOptions.getTimeout().isPresent()) { client = clientOptions.httpClientWithTimeout(requestOptions); } + if (requestOptions != null && requestOptions.getMaxRetries().isPresent()) { + okhttpRequest = okhttpRequest + .newBuilder() + .tag( + RetryInterceptor.MaxRetriesOverride.class, + new RetryInterceptor.MaxRetriesOverride( + requestOptions.getMaxRetries().get())) + .build(); + } try (Response response = client.newCall(okhttpRequest).execute()) { ResponseBody responseBody = response.body(); String responseBodyString = responseBody != null ? responseBody.string() : "{}"; @@ -204,6 +236,8 @@ public DeepgramApiHttpResponse delete( Object errorBody = ObjectMappers.parseErrorBody(responseBodyString); throw new DeepgramHttpException( "Error with status code " + response.code(), response.code(), errorBody, response); + } catch (JsonProcessingException e) { + throw new DeepgramApiException("Failed to deserialize response: " + e.getMessage(), e); } catch (IOException e) { throw new DeepgramApiException("Network error executing HTTP request", e); } diff --git a/src/main/java/com/deepgram/resources/manage/v1/projects/members/invites/requests/CreateProjectInviteV1Request.java b/src/main/java/com/deepgram/resources/manage/v1/projects/members/invites/requests/CreateProjectInviteV1Request.java index 223785a..aa758fa 100644 --- a/src/main/java/com/deepgram/resources/manage/v1/projects/members/invites/requests/CreateProjectInviteV1Request.java +++ b/src/main/java/com/deepgram/resources/manage/v1/projects/members/invites/requests/CreateProjectInviteV1Request.java @@ -119,7 +119,6 @@ public Builder from(CreateProjectInviteV1Request other) { } /** - *

    The email address of the invitee

    *

    The email address of the invitee

    * @return Reference to {@code this} so that method calls can be chained together. */ @@ -131,7 +130,6 @@ public ScopeStage email(@NotNull String email) { } /** - *

    The scope of the invitee

    *

    The scope of the invitee

    * @return Reference to {@code this} so that method calls can be chained together. */ diff --git a/src/main/java/com/deepgram/resources/manage/v1/projects/members/scopes/AsyncRawScopesClient.java b/src/main/java/com/deepgram/resources/manage/v1/projects/members/scopes/AsyncRawScopesClient.java index 7466c37..5b2bf66 100644 --- a/src/main/java/com/deepgram/resources/manage/v1/projects/members/scopes/AsyncRawScopesClient.java +++ b/src/main/java/com/deepgram/resources/manage/v1/projects/members/scopes/AsyncRawScopesClient.java @@ -10,6 +10,7 @@ import com.deepgram.core.MediaTypes; import com.deepgram.core.ObjectMappers; import com.deepgram.core.RequestOptions; +import com.deepgram.core.RetryInterceptor; import com.deepgram.errors.BadRequestError; import com.deepgram.resources.manage.v1.projects.members.scopes.requests.UpdateProjectMemberScopesV1Request; import com.deepgram.types.ListProjectMemberScopesV1Response; @@ -70,6 +71,15 @@ public CompletableFuture> future = new CompletableFuture<>(); client.newCall(okhttpRequest).enqueue(new Callback() { @@ -97,6 +107,9 @@ public void onResponse(@NotNull Call call, @NotNull Response response) throws IO future.completeExceptionally(new DeepgramHttpException( "Error with status code " + response.code(), response.code(), errorBody, response)); return; + } catch (JsonProcessingException e) { + future.completeExceptionally( + new DeepgramApiException("Failed to deserialize response: " + e.getMessage(), e)); } catch (IOException e) { future.completeExceptionally(new DeepgramApiException("Network error executing HTTP request", e)); } @@ -156,6 +169,15 @@ public CompletableFuture> future = new CompletableFuture<>(); client.newCall(okhttpRequest).enqueue(new Callback() { @@ -183,6 +205,9 @@ public void onResponse(@NotNull Call call, @NotNull Response response) throws IO future.completeExceptionally(new DeepgramHttpException( "Error with status code " + response.code(), response.code(), errorBody, response)); return; + } catch (JsonProcessingException e) { + future.completeExceptionally( + new DeepgramApiException("Failed to deserialize response: " + e.getMessage(), e)); } catch (IOException e) { future.completeExceptionally(new DeepgramApiException("Network error executing HTTP request", e)); } diff --git a/src/main/java/com/deepgram/resources/manage/v1/projects/members/scopes/RawScopesClient.java b/src/main/java/com/deepgram/resources/manage/v1/projects/members/scopes/RawScopesClient.java index c033b81..d3faa38 100644 --- a/src/main/java/com/deepgram/resources/manage/v1/projects/members/scopes/RawScopesClient.java +++ b/src/main/java/com/deepgram/resources/manage/v1/projects/members/scopes/RawScopesClient.java @@ -10,6 +10,7 @@ import com.deepgram.core.MediaTypes; import com.deepgram.core.ObjectMappers; import com.deepgram.core.RequestOptions; +import com.deepgram.core.RetryInterceptor; import com.deepgram.errors.BadRequestError; import com.deepgram.resources.manage.v1.projects.members.scopes.requests.UpdateProjectMemberScopesV1Request; import com.deepgram.types.ListProjectMemberScopesV1Response; @@ -65,6 +66,15 @@ public DeepgramApiHttpResponse list( if (requestOptions != null && requestOptions.getTimeout().isPresent()) { client = clientOptions.httpClientWithTimeout(requestOptions); } + if (requestOptions != null && requestOptions.getMaxRetries().isPresent()) { + okhttpRequest = okhttpRequest + .newBuilder() + .tag( + RetryInterceptor.MaxRetriesOverride.class, + new RetryInterceptor.MaxRetriesOverride( + requestOptions.getMaxRetries().get())) + .build(); + } try (Response response = client.newCall(okhttpRequest).execute()) { ResponseBody responseBody = response.body(); String responseBodyString = responseBody != null ? responseBody.string() : "{}"; @@ -85,6 +95,8 @@ public DeepgramApiHttpResponse list( Object errorBody = ObjectMappers.parseErrorBody(responseBodyString); throw new DeepgramHttpException( "Error with status code " + response.code(), response.code(), errorBody, response); + } catch (JsonProcessingException e) { + throw new DeepgramApiException("Failed to deserialize response: " + e.getMessage(), e); } catch (IOException e) { throw new DeepgramApiException("Network error executing HTTP request", e); } @@ -136,6 +148,15 @@ public DeepgramApiHttpResponse update( if (requestOptions != null && requestOptions.getTimeout().isPresent()) { client = clientOptions.httpClientWithTimeout(requestOptions); } + if (requestOptions != null && requestOptions.getMaxRetries().isPresent()) { + okhttpRequest = okhttpRequest + .newBuilder() + .tag( + RetryInterceptor.MaxRetriesOverride.class, + new RetryInterceptor.MaxRetriesOverride( + requestOptions.getMaxRetries().get())) + .build(); + } try (Response response = client.newCall(okhttpRequest).execute()) { ResponseBody responseBody = response.body(); String responseBodyString = responseBody != null ? responseBody.string() : "{}"; @@ -156,6 +177,8 @@ public DeepgramApiHttpResponse update( Object errorBody = ObjectMappers.parseErrorBody(responseBodyString); throw new DeepgramHttpException( "Error with status code " + response.code(), response.code(), errorBody, response); + } catch (JsonProcessingException e) { + throw new DeepgramApiException("Failed to deserialize response: " + e.getMessage(), e); } catch (IOException e) { throw new DeepgramApiException("Network error executing HTTP request", e); } diff --git a/src/main/java/com/deepgram/resources/manage/v1/projects/members/scopes/requests/UpdateProjectMemberScopesV1Request.java b/src/main/java/com/deepgram/resources/manage/v1/projects/members/scopes/requests/UpdateProjectMemberScopesV1Request.java index 4bbb024..e532862 100644 --- a/src/main/java/com/deepgram/resources/manage/v1/projects/members/scopes/requests/UpdateProjectMemberScopesV1Request.java +++ b/src/main/java/com/deepgram/resources/manage/v1/projects/members/scopes/requests/UpdateProjectMemberScopesV1Request.java @@ -99,7 +99,6 @@ public Builder from(UpdateProjectMemberScopesV1Request other) { } /** - *

    A scope to update

    *

    A scope to update

    * @return Reference to {@code this} so that method calls can be chained together. */ diff --git a/src/main/java/com/deepgram/resources/manage/v1/projects/models/AsyncRawModelsClient.java b/src/main/java/com/deepgram/resources/manage/v1/projects/models/AsyncRawModelsClient.java index d4d3f07..2dd4d6c 100644 --- a/src/main/java/com/deepgram/resources/manage/v1/projects/models/AsyncRawModelsClient.java +++ b/src/main/java/com/deepgram/resources/manage/v1/projects/models/AsyncRawModelsClient.java @@ -10,6 +10,7 @@ import com.deepgram.core.ObjectMappers; import com.deepgram.core.QueryStringMapper; import com.deepgram.core.RequestOptions; +import com.deepgram.core.RetryInterceptor; import com.deepgram.errors.BadRequestError; import com.deepgram.resources.manage.v1.projects.models.requests.ModelsListRequest; import com.deepgram.types.GetModelV1Response; @@ -86,6 +87,15 @@ public CompletableFuture> list( if (requestOptions != null && requestOptions.getTimeout().isPresent()) { client = clientOptions.httpClientWithTimeout(requestOptions); } + if (requestOptions != null && requestOptions.getMaxRetries().isPresent()) { + okhttpRequest = okhttpRequest + .newBuilder() + .tag( + RetryInterceptor.MaxRetriesOverride.class, + new RetryInterceptor.MaxRetriesOverride( + requestOptions.getMaxRetries().get())) + .build(); + } CompletableFuture> future = new CompletableFuture<>(); client.newCall(okhttpRequest).enqueue(new Callback() { @Override @@ -111,6 +121,9 @@ public void onResponse(@NotNull Call call, @NotNull Response response) throws IO future.completeExceptionally(new DeepgramHttpException( "Error with status code " + response.code(), response.code(), errorBody, response)); return; + } catch (JsonProcessingException e) { + future.completeExceptionally( + new DeepgramApiException("Failed to deserialize response: " + e.getMessage(), e)); } catch (IOException e) { future.completeExceptionally(new DeepgramApiException("Network error executing HTTP request", e)); } @@ -157,6 +170,15 @@ public CompletableFuture> get( if (requestOptions != null && requestOptions.getTimeout().isPresent()) { client = clientOptions.httpClientWithTimeout(requestOptions); } + if (requestOptions != null && requestOptions.getMaxRetries().isPresent()) { + okhttpRequest = okhttpRequest + .newBuilder() + .tag( + RetryInterceptor.MaxRetriesOverride.class, + new RetryInterceptor.MaxRetriesOverride( + requestOptions.getMaxRetries().get())) + .build(); + } CompletableFuture> future = new CompletableFuture<>(); client.newCall(okhttpRequest).enqueue(new Callback() { @Override @@ -182,6 +204,9 @@ public void onResponse(@NotNull Call call, @NotNull Response response) throws IO future.completeExceptionally(new DeepgramHttpException( "Error with status code " + response.code(), response.code(), errorBody, response)); return; + } catch (JsonProcessingException e) { + future.completeExceptionally( + new DeepgramApiException("Failed to deserialize response: " + e.getMessage(), e)); } catch (IOException e) { future.completeExceptionally(new DeepgramApiException("Network error executing HTTP request", e)); } diff --git a/src/main/java/com/deepgram/resources/manage/v1/projects/models/RawModelsClient.java b/src/main/java/com/deepgram/resources/manage/v1/projects/models/RawModelsClient.java index ec991bf..48aaf2d 100644 --- a/src/main/java/com/deepgram/resources/manage/v1/projects/models/RawModelsClient.java +++ b/src/main/java/com/deepgram/resources/manage/v1/projects/models/RawModelsClient.java @@ -10,6 +10,7 @@ import com.deepgram.core.ObjectMappers; import com.deepgram.core.QueryStringMapper; import com.deepgram.core.RequestOptions; +import com.deepgram.core.RetryInterceptor; import com.deepgram.errors.BadRequestError; import com.deepgram.resources.manage.v1.projects.models.requests.ModelsListRequest; import com.deepgram.types.GetModelV1Response; @@ -80,6 +81,15 @@ public DeepgramApiHttpResponse list( if (requestOptions != null && requestOptions.getTimeout().isPresent()) { client = clientOptions.httpClientWithTimeout(requestOptions); } + if (requestOptions != null && requestOptions.getMaxRetries().isPresent()) { + okhttpRequest = okhttpRequest + .newBuilder() + .tag( + RetryInterceptor.MaxRetriesOverride.class, + new RetryInterceptor.MaxRetriesOverride( + requestOptions.getMaxRetries().get())) + .build(); + } try (Response response = client.newCall(okhttpRequest).execute()) { ResponseBody responseBody = response.body(); String responseBodyString = responseBody != null ? responseBody.string() : "{}"; @@ -98,6 +108,8 @@ public DeepgramApiHttpResponse list( Object errorBody = ObjectMappers.parseErrorBody(responseBodyString); throw new DeepgramHttpException( "Error with status code " + response.code(), response.code(), errorBody, response); + } catch (JsonProcessingException e) { + throw new DeepgramApiException("Failed to deserialize response: " + e.getMessage(), e); } catch (IOException e) { throw new DeepgramApiException("Network error executing HTTP request", e); } @@ -136,6 +148,15 @@ public DeepgramApiHttpResponse get( if (requestOptions != null && requestOptions.getTimeout().isPresent()) { client = clientOptions.httpClientWithTimeout(requestOptions); } + if (requestOptions != null && requestOptions.getMaxRetries().isPresent()) { + okhttpRequest = okhttpRequest + .newBuilder() + .tag( + RetryInterceptor.MaxRetriesOverride.class, + new RetryInterceptor.MaxRetriesOverride( + requestOptions.getMaxRetries().get())) + .build(); + } try (Response response = client.newCall(okhttpRequest).execute()) { ResponseBody responseBody = response.body(); String responseBodyString = responseBody != null ? responseBody.string() : "{}"; @@ -154,6 +175,8 @@ public DeepgramApiHttpResponse get( Object errorBody = ObjectMappers.parseErrorBody(responseBodyString); throw new DeepgramHttpException( "Error with status code " + response.code(), response.code(), errorBody, response); + } catch (JsonProcessingException e) { + throw new DeepgramApiException("Failed to deserialize response: " + e.getMessage(), e); } catch (IOException e) { throw new DeepgramApiException("Network error executing HTTP request", e); } diff --git a/src/main/java/com/deepgram/resources/manage/v1/projects/requests/AsyncRawRequestsClient.java b/src/main/java/com/deepgram/resources/manage/v1/projects/requests/AsyncRawRequestsClient.java index 0aa178b..67d2a82 100644 --- a/src/main/java/com/deepgram/resources/manage/v1/projects/requests/AsyncRawRequestsClient.java +++ b/src/main/java/com/deepgram/resources/manage/v1/projects/requests/AsyncRawRequestsClient.java @@ -10,6 +10,7 @@ import com.deepgram.core.ObjectMappers; import com.deepgram.core.QueryStringMapper; import com.deepgram.core.RequestOptions; +import com.deepgram.core.RetryInterceptor; import com.deepgram.errors.BadRequestError; import com.deepgram.resources.manage.v1.projects.requests.requests.RequestsListRequest; import com.deepgram.types.GetProjectRequestV1Response; @@ -121,6 +122,15 @@ public CompletableFuture> if (requestOptions != null && requestOptions.getTimeout().isPresent()) { client = clientOptions.httpClientWithTimeout(requestOptions); } + if (requestOptions != null && requestOptions.getMaxRetries().isPresent()) { + okhttpRequest = okhttpRequest + .newBuilder() + .tag( + RetryInterceptor.MaxRetriesOverride.class, + new RetryInterceptor.MaxRetriesOverride( + requestOptions.getMaxRetries().get())) + .build(); + } CompletableFuture> future = new CompletableFuture<>(); client.newCall(okhttpRequest).enqueue(new Callback() { @Override @@ -147,6 +157,9 @@ public void onResponse(@NotNull Call call, @NotNull Response response) throws IO future.completeExceptionally(new DeepgramHttpException( "Error with status code " + response.code(), response.code(), errorBody, response)); return; + } catch (JsonProcessingException e) { + future.completeExceptionally( + new DeepgramApiException("Failed to deserialize response: " + e.getMessage(), e)); } catch (IOException e) { future.completeExceptionally(new DeepgramApiException("Network error executing HTTP request", e)); } @@ -194,6 +207,15 @@ public CompletableFuture> g if (requestOptions != null && requestOptions.getTimeout().isPresent()) { client = clientOptions.httpClientWithTimeout(requestOptions); } + if (requestOptions != null && requestOptions.getMaxRetries().isPresent()) { + okhttpRequest = okhttpRequest + .newBuilder() + .tag( + RetryInterceptor.MaxRetriesOverride.class, + new RetryInterceptor.MaxRetriesOverride( + requestOptions.getMaxRetries().get())) + .build(); + } CompletableFuture> future = new CompletableFuture<>(); client.newCall(okhttpRequest).enqueue(new Callback() { @Override @@ -220,6 +242,9 @@ public void onResponse(@NotNull Call call, @NotNull Response response) throws IO future.completeExceptionally(new DeepgramHttpException( "Error with status code " + response.code(), response.code(), errorBody, response)); return; + } catch (JsonProcessingException e) { + future.completeExceptionally( + new DeepgramApiException("Failed to deserialize response: " + e.getMessage(), e)); } catch (IOException e) { future.completeExceptionally(new DeepgramApiException("Network error executing HTTP request", e)); } diff --git a/src/main/java/com/deepgram/resources/manage/v1/projects/requests/RawRequestsClient.java b/src/main/java/com/deepgram/resources/manage/v1/projects/requests/RawRequestsClient.java index 5a8da14..00412a0 100644 --- a/src/main/java/com/deepgram/resources/manage/v1/projects/requests/RawRequestsClient.java +++ b/src/main/java/com/deepgram/resources/manage/v1/projects/requests/RawRequestsClient.java @@ -10,6 +10,7 @@ import com.deepgram.core.ObjectMappers; import com.deepgram.core.QueryStringMapper; import com.deepgram.core.RequestOptions; +import com.deepgram.core.RetryInterceptor; import com.deepgram.errors.BadRequestError; import com.deepgram.resources.manage.v1.projects.requests.requests.RequestsListRequest; import com.deepgram.types.GetProjectRequestV1Response; @@ -116,6 +117,15 @@ public DeepgramApiHttpResponse list( if (requestOptions != null && requestOptions.getTimeout().isPresent()) { client = clientOptions.httpClientWithTimeout(requestOptions); } + if (requestOptions != null && requestOptions.getMaxRetries().isPresent()) { + okhttpRequest = okhttpRequest + .newBuilder() + .tag( + RetryInterceptor.MaxRetriesOverride.class, + new RetryInterceptor.MaxRetriesOverride( + requestOptions.getMaxRetries().get())) + .build(); + } try (Response response = client.newCall(okhttpRequest).execute()) { ResponseBody responseBody = response.body(); String responseBodyString = responseBody != null ? responseBody.string() : "{}"; @@ -135,6 +145,8 @@ public DeepgramApiHttpResponse list( Object errorBody = ObjectMappers.parseErrorBody(responseBodyString); throw new DeepgramHttpException( "Error with status code " + response.code(), response.code(), errorBody, response); + } catch (JsonProcessingException e) { + throw new DeepgramApiException("Failed to deserialize response: " + e.getMessage(), e); } catch (IOException e) { throw new DeepgramApiException("Network error executing HTTP request", e); } @@ -173,6 +185,15 @@ public DeepgramApiHttpResponse get( if (requestOptions != null && requestOptions.getTimeout().isPresent()) { client = clientOptions.httpClientWithTimeout(requestOptions); } + if (requestOptions != null && requestOptions.getMaxRetries().isPresent()) { + okhttpRequest = okhttpRequest + .newBuilder() + .tag( + RetryInterceptor.MaxRetriesOverride.class, + new RetryInterceptor.MaxRetriesOverride( + requestOptions.getMaxRetries().get())) + .build(); + } try (Response response = client.newCall(okhttpRequest).execute()) { ResponseBody responseBody = response.body(); String responseBodyString = responseBody != null ? responseBody.string() : "{}"; @@ -192,6 +213,8 @@ public DeepgramApiHttpResponse get( Object errorBody = ObjectMappers.parseErrorBody(responseBodyString); throw new DeepgramHttpException( "Error with status code " + response.code(), response.code(), errorBody, response); + } catch (JsonProcessingException e) { + throw new DeepgramApiException("Failed to deserialize response: " + e.getMessage(), e); } catch (IOException e) { throw new DeepgramApiException("Network error executing HTTP request", e); } diff --git a/src/main/java/com/deepgram/resources/manage/v1/projects/usage/AsyncRawUsageClient.java b/src/main/java/com/deepgram/resources/manage/v1/projects/usage/AsyncRawUsageClient.java index af94ec6..8ec476d 100644 --- a/src/main/java/com/deepgram/resources/manage/v1/projects/usage/AsyncRawUsageClient.java +++ b/src/main/java/com/deepgram/resources/manage/v1/projects/usage/AsyncRawUsageClient.java @@ -10,6 +10,7 @@ import com.deepgram.core.ObjectMappers; import com.deepgram.core.QueryStringMapper; import com.deepgram.core.RequestOptions; +import com.deepgram.core.RetryInterceptor; import com.deepgram.errors.BadRequestError; import com.deepgram.resources.manage.v1.projects.usage.requests.UsageGetRequest; import com.deepgram.types.UsageV1Response; @@ -254,6 +255,15 @@ public CompletableFuture> get( if (requestOptions != null && requestOptions.getTimeout().isPresent()) { client = clientOptions.httpClientWithTimeout(requestOptions); } + if (requestOptions != null && requestOptions.getMaxRetries().isPresent()) { + okhttpRequest = okhttpRequest + .newBuilder() + .tag( + RetryInterceptor.MaxRetriesOverride.class, + new RetryInterceptor.MaxRetriesOverride( + requestOptions.getMaxRetries().get())) + .build(); + } CompletableFuture> future = new CompletableFuture<>(); client.newCall(okhttpRequest).enqueue(new Callback() { @Override @@ -279,6 +289,9 @@ public void onResponse(@NotNull Call call, @NotNull Response response) throws IO future.completeExceptionally(new DeepgramHttpException( "Error with status code " + response.code(), response.code(), errorBody, response)); return; + } catch (JsonProcessingException e) { + future.completeExceptionally( + new DeepgramApiException("Failed to deserialize response: " + e.getMessage(), e)); } catch (IOException e) { future.completeExceptionally(new DeepgramApiException("Network error executing HTTP request", e)); } diff --git a/src/main/java/com/deepgram/resources/manage/v1/projects/usage/RawUsageClient.java b/src/main/java/com/deepgram/resources/manage/v1/projects/usage/RawUsageClient.java index 1f85ae8..8a5db02 100644 --- a/src/main/java/com/deepgram/resources/manage/v1/projects/usage/RawUsageClient.java +++ b/src/main/java/com/deepgram/resources/manage/v1/projects/usage/RawUsageClient.java @@ -10,6 +10,7 @@ import com.deepgram.core.ObjectMappers; import com.deepgram.core.QueryStringMapper; import com.deepgram.core.RequestOptions; +import com.deepgram.core.RetryInterceptor; import com.deepgram.errors.BadRequestError; import com.deepgram.resources.manage.v1.projects.usage.requests.UsageGetRequest; import com.deepgram.types.UsageV1Response; @@ -249,6 +250,15 @@ public DeepgramApiHttpResponse get( if (requestOptions != null && requestOptions.getTimeout().isPresent()) { client = clientOptions.httpClientWithTimeout(requestOptions); } + if (requestOptions != null && requestOptions.getMaxRetries().isPresent()) { + okhttpRequest = okhttpRequest + .newBuilder() + .tag( + RetryInterceptor.MaxRetriesOverride.class, + new RetryInterceptor.MaxRetriesOverride( + requestOptions.getMaxRetries().get())) + .build(); + } try (Response response = client.newCall(okhttpRequest).execute()) { ResponseBody responseBody = response.body(); String responseBodyString = responseBody != null ? responseBody.string() : "{}"; @@ -267,6 +277,8 @@ public DeepgramApiHttpResponse get( Object errorBody = ObjectMappers.parseErrorBody(responseBodyString); throw new DeepgramHttpException( "Error with status code " + response.code(), response.code(), errorBody, response); + } catch (JsonProcessingException e) { + throw new DeepgramApiException("Failed to deserialize response: " + e.getMessage(), e); } catch (IOException e) { throw new DeepgramApiException("Network error executing HTTP request", e); } diff --git a/src/main/java/com/deepgram/resources/manage/v1/projects/usage/breakdown/AsyncRawBreakdownClient.java b/src/main/java/com/deepgram/resources/manage/v1/projects/usage/breakdown/AsyncRawBreakdownClient.java index aeaac37..aeb64ba 100644 --- a/src/main/java/com/deepgram/resources/manage/v1/projects/usage/breakdown/AsyncRawBreakdownClient.java +++ b/src/main/java/com/deepgram/resources/manage/v1/projects/usage/breakdown/AsyncRawBreakdownClient.java @@ -10,6 +10,7 @@ import com.deepgram.core.ObjectMappers; import com.deepgram.core.QueryStringMapper; import com.deepgram.core.RequestOptions; +import com.deepgram.core.RetryInterceptor; import com.deepgram.errors.BadRequestError; import com.deepgram.resources.manage.v1.projects.usage.breakdown.requests.BreakdownGetRequest; import com.deepgram.types.UsageBreakdownV1Response; @@ -260,6 +261,15 @@ public CompletableFuture> get( if (requestOptions != null && requestOptions.getTimeout().isPresent()) { client = clientOptions.httpClientWithTimeout(requestOptions); } + if (requestOptions != null && requestOptions.getMaxRetries().isPresent()) { + okhttpRequest = okhttpRequest + .newBuilder() + .tag( + RetryInterceptor.MaxRetriesOverride.class, + new RetryInterceptor.MaxRetriesOverride( + requestOptions.getMaxRetries().get())) + .build(); + } CompletableFuture> future = new CompletableFuture<>(); client.newCall(okhttpRequest).enqueue(new Callback() { @Override @@ -285,6 +295,9 @@ public void onResponse(@NotNull Call call, @NotNull Response response) throws IO future.completeExceptionally(new DeepgramHttpException( "Error with status code " + response.code(), response.code(), errorBody, response)); return; + } catch (JsonProcessingException e) { + future.completeExceptionally( + new DeepgramApiException("Failed to deserialize response: " + e.getMessage(), e)); } catch (IOException e) { future.completeExceptionally(new DeepgramApiException("Network error executing HTTP request", e)); } diff --git a/src/main/java/com/deepgram/resources/manage/v1/projects/usage/breakdown/RawBreakdownClient.java b/src/main/java/com/deepgram/resources/manage/v1/projects/usage/breakdown/RawBreakdownClient.java index 60b4af2..98fcf3a 100644 --- a/src/main/java/com/deepgram/resources/manage/v1/projects/usage/breakdown/RawBreakdownClient.java +++ b/src/main/java/com/deepgram/resources/manage/v1/projects/usage/breakdown/RawBreakdownClient.java @@ -10,6 +10,7 @@ import com.deepgram.core.ObjectMappers; import com.deepgram.core.QueryStringMapper; import com.deepgram.core.RequestOptions; +import com.deepgram.core.RetryInterceptor; import com.deepgram.errors.BadRequestError; import com.deepgram.resources.manage.v1.projects.usage.breakdown.requests.BreakdownGetRequest; import com.deepgram.types.UsageBreakdownV1Response; @@ -254,6 +255,15 @@ public DeepgramApiHttpResponse get( if (requestOptions != null && requestOptions.getTimeout().isPresent()) { client = clientOptions.httpClientWithTimeout(requestOptions); } + if (requestOptions != null && requestOptions.getMaxRetries().isPresent()) { + okhttpRequest = okhttpRequest + .newBuilder() + .tag( + RetryInterceptor.MaxRetriesOverride.class, + new RetryInterceptor.MaxRetriesOverride( + requestOptions.getMaxRetries().get())) + .build(); + } try (Response response = client.newCall(okhttpRequest).execute()) { ResponseBody responseBody = response.body(); String responseBodyString = responseBody != null ? responseBody.string() : "{}"; @@ -273,6 +283,8 @@ public DeepgramApiHttpResponse get( Object errorBody = ObjectMappers.parseErrorBody(responseBodyString); throw new DeepgramHttpException( "Error with status code " + response.code(), response.code(), errorBody, response); + } catch (JsonProcessingException e) { + throw new DeepgramApiException("Failed to deserialize response: " + e.getMessage(), e); } catch (IOException e) { throw new DeepgramApiException("Network error executing HTTP request", e); } diff --git a/src/main/java/com/deepgram/resources/manage/v1/projects/usage/fields/AsyncRawFieldsClient.java b/src/main/java/com/deepgram/resources/manage/v1/projects/usage/fields/AsyncRawFieldsClient.java index 72c7820..c2233f8 100644 --- a/src/main/java/com/deepgram/resources/manage/v1/projects/usage/fields/AsyncRawFieldsClient.java +++ b/src/main/java/com/deepgram/resources/manage/v1/projects/usage/fields/AsyncRawFieldsClient.java @@ -10,6 +10,7 @@ import com.deepgram.core.ObjectMappers; import com.deepgram.core.QueryStringMapper; import com.deepgram.core.RequestOptions; +import com.deepgram.core.RetryInterceptor; import com.deepgram.errors.BadRequestError; import com.deepgram.resources.manage.v1.projects.usage.fields.requests.FieldsListRequest; import com.deepgram.types.UsageFieldsV1Response; @@ -89,6 +90,15 @@ public CompletableFuture> list( if (requestOptions != null && requestOptions.getTimeout().isPresent()) { client = clientOptions.httpClientWithTimeout(requestOptions); } + if (requestOptions != null && requestOptions.getMaxRetries().isPresent()) { + okhttpRequest = okhttpRequest + .newBuilder() + .tag( + RetryInterceptor.MaxRetriesOverride.class, + new RetryInterceptor.MaxRetriesOverride( + requestOptions.getMaxRetries().get())) + .build(); + } CompletableFuture> future = new CompletableFuture<>(); client.newCall(okhttpRequest).enqueue(new Callback() { @Override @@ -114,6 +124,9 @@ public void onResponse(@NotNull Call call, @NotNull Response response) throws IO future.completeExceptionally(new DeepgramHttpException( "Error with status code " + response.code(), response.code(), errorBody, response)); return; + } catch (JsonProcessingException e) { + future.completeExceptionally( + new DeepgramApiException("Failed to deserialize response: " + e.getMessage(), e)); } catch (IOException e) { future.completeExceptionally(new DeepgramApiException("Network error executing HTTP request", e)); } diff --git a/src/main/java/com/deepgram/resources/manage/v1/projects/usage/fields/RawFieldsClient.java b/src/main/java/com/deepgram/resources/manage/v1/projects/usage/fields/RawFieldsClient.java index 9338292..d5e702d 100644 --- a/src/main/java/com/deepgram/resources/manage/v1/projects/usage/fields/RawFieldsClient.java +++ b/src/main/java/com/deepgram/resources/manage/v1/projects/usage/fields/RawFieldsClient.java @@ -10,6 +10,7 @@ import com.deepgram.core.ObjectMappers; import com.deepgram.core.QueryStringMapper; import com.deepgram.core.RequestOptions; +import com.deepgram.core.RetryInterceptor; import com.deepgram.errors.BadRequestError; import com.deepgram.resources.manage.v1.projects.usage.fields.requests.FieldsListRequest; import com.deepgram.types.UsageFieldsV1Response; @@ -83,6 +84,15 @@ public DeepgramApiHttpResponse list( if (requestOptions != null && requestOptions.getTimeout().isPresent()) { client = clientOptions.httpClientWithTimeout(requestOptions); } + if (requestOptions != null && requestOptions.getMaxRetries().isPresent()) { + okhttpRequest = okhttpRequest + .newBuilder() + .tag( + RetryInterceptor.MaxRetriesOverride.class, + new RetryInterceptor.MaxRetriesOverride( + requestOptions.getMaxRetries().get())) + .build(); + } try (Response response = client.newCall(okhttpRequest).execute()) { ResponseBody responseBody = response.body(); String responseBodyString = responseBody != null ? responseBody.string() : "{}"; @@ -101,6 +111,8 @@ public DeepgramApiHttpResponse list( Object errorBody = ObjectMappers.parseErrorBody(responseBodyString); throw new DeepgramHttpException( "Error with status code " + response.code(), response.code(), errorBody, response); + } catch (JsonProcessingException e) { + throw new DeepgramApiException("Failed to deserialize response: " + e.getMessage(), e); } catch (IOException e) { throw new DeepgramApiException("Network error executing HTTP request", e); } diff --git a/src/main/java/com/deepgram/resources/read/v1/text/AsyncRawTextClient.java b/src/main/java/com/deepgram/resources/read/v1/text/AsyncRawTextClient.java index 06d0609..526ca2b 100644 --- a/src/main/java/com/deepgram/resources/read/v1/text/AsyncRawTextClient.java +++ b/src/main/java/com/deepgram/resources/read/v1/text/AsyncRawTextClient.java @@ -11,6 +11,7 @@ import com.deepgram.core.ObjectMappers; import com.deepgram.core.QueryStringMapper; import com.deepgram.core.RequestOptions; +import com.deepgram.core.RetryInterceptor; import com.deepgram.errors.BadRequestError; import com.deepgram.resources.read.v1.text.requests.TextAnalyzeRequest; import com.deepgram.types.ReadV1Request; @@ -136,6 +137,15 @@ public CompletableFuture> analyze( if (requestOptions != null && requestOptions.getTimeout().isPresent()) { client = clientOptions.httpClientWithTimeout(requestOptions); } + if (requestOptions != null && requestOptions.getMaxRetries().isPresent()) { + okhttpRequest = okhttpRequest + .newBuilder() + .tag( + RetryInterceptor.MaxRetriesOverride.class, + new RetryInterceptor.MaxRetriesOverride( + requestOptions.getMaxRetries().get())) + .build(); + } CompletableFuture> future = new CompletableFuture<>(); client.newCall(okhttpRequest).enqueue(new Callback() { @Override @@ -161,6 +171,9 @@ public void onResponse(@NotNull Call call, @NotNull Response response) throws IO future.completeExceptionally(new DeepgramHttpException( "Error with status code " + response.code(), response.code(), errorBody, response)); return; + } catch (JsonProcessingException e) { + future.completeExceptionally( + new DeepgramApiException("Failed to deserialize response: " + e.getMessage(), e)); } catch (IOException e) { future.completeExceptionally(new DeepgramApiException("Network error executing HTTP request", e)); } diff --git a/src/main/java/com/deepgram/resources/read/v1/text/RawTextClient.java b/src/main/java/com/deepgram/resources/read/v1/text/RawTextClient.java index c244be9..daaaf93 100644 --- a/src/main/java/com/deepgram/resources/read/v1/text/RawTextClient.java +++ b/src/main/java/com/deepgram/resources/read/v1/text/RawTextClient.java @@ -11,6 +11,7 @@ import com.deepgram.core.ObjectMappers; import com.deepgram.core.QueryStringMapper; import com.deepgram.core.RequestOptions; +import com.deepgram.core.RetryInterceptor; import com.deepgram.errors.BadRequestError; import com.deepgram.resources.read.v1.text.requests.TextAnalyzeRequest; import com.deepgram.types.ReadV1Request; @@ -130,6 +131,15 @@ public DeepgramApiHttpResponse analyze(TextAnalyzeRequest reques if (requestOptions != null && requestOptions.getTimeout().isPresent()) { client = clientOptions.httpClientWithTimeout(requestOptions); } + if (requestOptions != null && requestOptions.getMaxRetries().isPresent()) { + okhttpRequest = okhttpRequest + .newBuilder() + .tag( + RetryInterceptor.MaxRetriesOverride.class, + new RetryInterceptor.MaxRetriesOverride( + requestOptions.getMaxRetries().get())) + .build(); + } try (Response response = client.newCall(okhttpRequest).execute()) { ResponseBody responseBody = response.body(); String responseBodyString = responseBody != null ? responseBody.string() : "{}"; @@ -148,6 +158,8 @@ public DeepgramApiHttpResponse analyze(TextAnalyzeRequest reques Object errorBody = ObjectMappers.parseErrorBody(responseBodyString); throw new DeepgramHttpException( "Error with status code " + response.code(), response.code(), errorBody, response); + } catch (JsonProcessingException e) { + throw new DeepgramApiException("Failed to deserialize response: " + e.getMessage(), e); } catch (IOException e) { throw new DeepgramApiException("Network error executing HTTP request", e); } diff --git a/src/main/java/com/deepgram/resources/selfhosted/v1/distributioncredentials/AsyncRawDistributionCredentialsClient.java b/src/main/java/com/deepgram/resources/selfhosted/v1/distributioncredentials/AsyncRawDistributionCredentialsClient.java index 3c7cfb6..c24a88f 100644 --- a/src/main/java/com/deepgram/resources/selfhosted/v1/distributioncredentials/AsyncRawDistributionCredentialsClient.java +++ b/src/main/java/com/deepgram/resources/selfhosted/v1/distributioncredentials/AsyncRawDistributionCredentialsClient.java @@ -11,6 +11,7 @@ import com.deepgram.core.ObjectMappers; import com.deepgram.core.QueryStringMapper; import com.deepgram.core.RequestOptions; +import com.deepgram.core.RetryInterceptor; import com.deepgram.errors.BadRequestError; import com.deepgram.resources.selfhosted.v1.distributioncredentials.requests.CreateProjectDistributionCredentialsV1Request; import com.deepgram.types.CreateProjectDistributionCredentialsV1Response; @@ -71,6 +72,15 @@ public CompletableFuture> future = new CompletableFuture<>(); client.newCall(okhttpRequest).enqueue(new Callback() { @@ -98,6 +108,9 @@ public void onResponse(@NotNull Call call, @NotNull Response response) throws IO future.completeExceptionally(new DeepgramHttpException( "Error with status code " + response.code(), response.code(), errorBody, response)); return; + } catch (JsonProcessingException e) { + future.completeExceptionally( + new DeepgramApiException("Failed to deserialize response: " + e.getMessage(), e)); } catch (IOException e) { future.completeExceptionally(new DeepgramApiException("Network error executing HTTP request", e)); } @@ -182,6 +195,15 @@ public CompletableFuture> future = new CompletableFuture<>(); client.newCall(okhttpRequest).enqueue(new Callback() { @@ -209,6 +231,9 @@ public void onResponse(@NotNull Call call, @NotNull Response response) throws IO future.completeExceptionally(new DeepgramHttpException( "Error with status code " + response.code(), response.code(), errorBody, response)); return; + } catch (JsonProcessingException e) { + future.completeExceptionally( + new DeepgramApiException("Failed to deserialize response: " + e.getMessage(), e)); } catch (IOException e) { future.completeExceptionally(new DeepgramApiException("Network error executing HTTP request", e)); } @@ -256,6 +281,15 @@ public CompletableFuture> future = new CompletableFuture<>(); client.newCall(okhttpRequest).enqueue(new Callback() { @@ -283,6 +317,9 @@ public void onResponse(@NotNull Call call, @NotNull Response response) throws IO future.completeExceptionally(new DeepgramHttpException( "Error with status code " + response.code(), response.code(), errorBody, response)); return; + } catch (JsonProcessingException e) { + future.completeExceptionally( + new DeepgramApiException("Failed to deserialize response: " + e.getMessage(), e)); } catch (IOException e) { future.completeExceptionally(new DeepgramApiException("Network error executing HTTP request", e)); } @@ -330,6 +367,15 @@ public CompletableFuture> future = new CompletableFuture<>(); client.newCall(okhttpRequest).enqueue(new Callback() { @@ -357,6 +403,9 @@ public void onResponse(@NotNull Call call, @NotNull Response response) throws IO future.completeExceptionally(new DeepgramHttpException( "Error with status code " + response.code(), response.code(), errorBody, response)); return; + } catch (JsonProcessingException e) { + future.completeExceptionally( + new DeepgramApiException("Failed to deserialize response: " + e.getMessage(), e)); } catch (IOException e) { future.completeExceptionally(new DeepgramApiException("Network error executing HTTP request", e)); } diff --git a/src/main/java/com/deepgram/resources/selfhosted/v1/distributioncredentials/RawDistributionCredentialsClient.java b/src/main/java/com/deepgram/resources/selfhosted/v1/distributioncredentials/RawDistributionCredentialsClient.java index 7e5f038..6af7bd2 100644 --- a/src/main/java/com/deepgram/resources/selfhosted/v1/distributioncredentials/RawDistributionCredentialsClient.java +++ b/src/main/java/com/deepgram/resources/selfhosted/v1/distributioncredentials/RawDistributionCredentialsClient.java @@ -11,6 +11,7 @@ import com.deepgram.core.ObjectMappers; import com.deepgram.core.QueryStringMapper; import com.deepgram.core.RequestOptions; +import com.deepgram.core.RetryInterceptor; import com.deepgram.errors.BadRequestError; import com.deepgram.resources.selfhosted.v1.distributioncredentials.requests.CreateProjectDistributionCredentialsV1Request; import com.deepgram.types.CreateProjectDistributionCredentialsV1Response; @@ -66,6 +67,15 @@ public DeepgramApiHttpResponse lis if (requestOptions != null && requestOptions.getTimeout().isPresent()) { client = clientOptions.httpClientWithTimeout(requestOptions); } + if (requestOptions != null && requestOptions.getMaxRetries().isPresent()) { + okhttpRequest = okhttpRequest + .newBuilder() + .tag( + RetryInterceptor.MaxRetriesOverride.class, + new RetryInterceptor.MaxRetriesOverride( + requestOptions.getMaxRetries().get())) + .build(); + } try (Response response = client.newCall(okhttpRequest).execute()) { ResponseBody responseBody = response.body(); String responseBodyString = responseBody != null ? responseBody.string() : "{}"; @@ -86,6 +96,8 @@ public DeepgramApiHttpResponse lis Object errorBody = ObjectMappers.parseErrorBody(responseBodyString); throw new DeepgramHttpException( "Error with status code " + response.code(), response.code(), errorBody, response); + } catch (JsonProcessingException e) { + throw new DeepgramApiException("Failed to deserialize response: " + e.getMessage(), e); } catch (IOException e) { throw new DeepgramApiException("Network error executing HTTP request", e); } @@ -161,6 +173,15 @@ public DeepgramApiHttpResponse c if (requestOptions != null && requestOptions.getTimeout().isPresent()) { client = clientOptions.httpClientWithTimeout(requestOptions); } + if (requestOptions != null && requestOptions.getMaxRetries().isPresent()) { + okhttpRequest = okhttpRequest + .newBuilder() + .tag( + RetryInterceptor.MaxRetriesOverride.class, + new RetryInterceptor.MaxRetriesOverride( + requestOptions.getMaxRetries().get())) + .build(); + } try (Response response = client.newCall(okhttpRequest).execute()) { ResponseBody responseBody = response.body(); String responseBodyString = responseBody != null ? responseBody.string() : "{}"; @@ -181,6 +202,8 @@ public DeepgramApiHttpResponse c Object errorBody = ObjectMappers.parseErrorBody(responseBodyString); throw new DeepgramHttpException( "Error with status code " + response.code(), response.code(), errorBody, response); + } catch (JsonProcessingException e) { + throw new DeepgramApiException("Failed to deserialize response: " + e.getMessage(), e); } catch (IOException e) { throw new DeepgramApiException("Network error executing HTTP request", e); } @@ -220,6 +243,15 @@ public DeepgramApiHttpResponse get( if (requestOptions != null && requestOptions.getTimeout().isPresent()) { client = clientOptions.httpClientWithTimeout(requestOptions); } + if (requestOptions != null && requestOptions.getMaxRetries().isPresent()) { + okhttpRequest = okhttpRequest + .newBuilder() + .tag( + RetryInterceptor.MaxRetriesOverride.class, + new RetryInterceptor.MaxRetriesOverride( + requestOptions.getMaxRetries().get())) + .build(); + } try (Response response = client.newCall(okhttpRequest).execute()) { ResponseBody responseBody = response.body(); String responseBodyString = responseBody != null ? responseBody.string() : "{}"; @@ -240,6 +272,8 @@ public DeepgramApiHttpResponse get( Object errorBody = ObjectMappers.parseErrorBody(responseBodyString); throw new DeepgramHttpException( "Error with status code " + response.code(), response.code(), errorBody, response); + } catch (JsonProcessingException e) { + throw new DeepgramApiException("Failed to deserialize response: " + e.getMessage(), e); } catch (IOException e) { throw new DeepgramApiException("Network error executing HTTP request", e); } @@ -279,6 +313,15 @@ public DeepgramApiHttpResponse dele if (requestOptions != null && requestOptions.getTimeout().isPresent()) { client = clientOptions.httpClientWithTimeout(requestOptions); } + if (requestOptions != null && requestOptions.getMaxRetries().isPresent()) { + okhttpRequest = okhttpRequest + .newBuilder() + .tag( + RetryInterceptor.MaxRetriesOverride.class, + new RetryInterceptor.MaxRetriesOverride( + requestOptions.getMaxRetries().get())) + .build(); + } try (Response response = client.newCall(okhttpRequest).execute()) { ResponseBody responseBody = response.body(); String responseBodyString = responseBody != null ? responseBody.string() : "{}"; @@ -299,6 +342,8 @@ public DeepgramApiHttpResponse dele Object errorBody = ObjectMappers.parseErrorBody(responseBodyString); throw new DeepgramHttpException( "Error with status code " + response.code(), response.code(), errorBody, response); + } catch (JsonProcessingException e) { + throw new DeepgramApiException("Failed to deserialize response: " + e.getMessage(), e); } catch (IOException e) { throw new DeepgramApiException("Network error executing HTTP request", e); } diff --git a/src/main/java/com/deepgram/resources/speak/v1/audio/AsyncRawAudioClient.java b/src/main/java/com/deepgram/resources/speak/v1/audio/AsyncRawAudioClient.java index 479e78b..a17b66b 100644 --- a/src/main/java/com/deepgram/resources/speak/v1/audio/AsyncRawAudioClient.java +++ b/src/main/java/com/deepgram/resources/speak/v1/audio/AsyncRawAudioClient.java @@ -12,6 +12,7 @@ import com.deepgram.core.QueryStringMapper; import com.deepgram.core.RequestOptions; import com.deepgram.core.ResponseBodyInputStream; +import com.deepgram.core.RetryInterceptor; import com.deepgram.errors.BadRequestError; import com.deepgram.resources.speak.v1.audio.requests.SpeakV1Request; import com.fasterxml.jackson.core.JsonProcessingException; @@ -113,6 +114,15 @@ public CompletableFuture> generate( if (requestOptions != null && requestOptions.getTimeout().isPresent()) { client = clientOptions.httpClientWithTimeout(requestOptions); } + if (requestOptions != null && requestOptions.getMaxRetries().isPresent()) { + okhttpRequest = okhttpRequest + .newBuilder() + .tag( + RetryInterceptor.MaxRetriesOverride.class, + new RetryInterceptor.MaxRetriesOverride( + requestOptions.getMaxRetries().get())) + .build(); + } CompletableFuture> future = new CompletableFuture<>(); client.newCall(okhttpRequest).enqueue(new Callback() { @Override @@ -137,6 +147,9 @@ public void onResponse(@NotNull Call call, @NotNull Response response) throws IO future.completeExceptionally(new DeepgramHttpException( "Error with status code " + response.code(), response.code(), errorBody, response)); return; + } catch (JsonProcessingException e) { + future.completeExceptionally( + new DeepgramApiException("Failed to deserialize response: " + e.getMessage(), e)); } catch (IOException e) { future.completeExceptionally(new DeepgramApiException("Network error executing HTTP request", e)); } diff --git a/src/main/java/com/deepgram/resources/speak/v1/audio/RawAudioClient.java b/src/main/java/com/deepgram/resources/speak/v1/audio/RawAudioClient.java index 377c30e..8e326d6 100644 --- a/src/main/java/com/deepgram/resources/speak/v1/audio/RawAudioClient.java +++ b/src/main/java/com/deepgram/resources/speak/v1/audio/RawAudioClient.java @@ -12,6 +12,7 @@ import com.deepgram.core.QueryStringMapper; import com.deepgram.core.RequestOptions; import com.deepgram.core.ResponseBodyInputStream; +import com.deepgram.core.RetryInterceptor; import com.deepgram.errors.BadRequestError; import com.deepgram.resources.speak.v1.audio.requests.SpeakV1Request; import com.fasterxml.jackson.core.JsonProcessingException; @@ -108,6 +109,15 @@ public DeepgramApiHttpResponse generate(SpeakV1Request request, Req if (requestOptions != null && requestOptions.getTimeout().isPresent()) { client = clientOptions.httpClientWithTimeout(requestOptions); } + if (requestOptions != null && requestOptions.getMaxRetries().isPresent()) { + okhttpRequest = okhttpRequest + .newBuilder() + .tag( + RetryInterceptor.MaxRetriesOverride.class, + new RetryInterceptor.MaxRetriesOverride( + requestOptions.getMaxRetries().get())) + .build(); + } try { Response response = client.newCall(okhttpRequest).execute(); ResponseBody responseBody = response.body(); @@ -126,6 +136,8 @@ public DeepgramApiHttpResponse generate(SpeakV1Request request, Req Object errorBody = ObjectMappers.parseErrorBody(responseBodyString); throw new DeepgramHttpException( "Error with status code " + response.code(), response.code(), errorBody, response); + } catch (JsonProcessingException e) { + throw new DeepgramApiException("Failed to deserialize response: " + e.getMessage(), e); } catch (IOException e) { throw new DeepgramApiException("Network error executing HTTP request", e); } diff --git a/src/main/java/com/deepgram/resources/speak/v1/audio/requests/SpeakV1Request.java b/src/main/java/com/deepgram/resources/speak/v1/audio/requests/SpeakV1Request.java index 75c4629..4bd0456 100644 --- a/src/main/java/com/deepgram/resources/speak/v1/audio/requests/SpeakV1Request.java +++ b/src/main/java/com/deepgram/resources/speak/v1/audio/requests/SpeakV1Request.java @@ -352,7 +352,6 @@ public Builder from(SpeakV1Request other) { } /** - *

    The text content to be converted to speech

    *

    The text content to be converted to speech

    * @return Reference to {@code this} so that method calls can be chained together. */ diff --git a/src/main/java/com/deepgram/resources/speak/v1/types/SpeakV1Clear.java b/src/main/java/com/deepgram/resources/speak/v1/types/SpeakV1Clear.java index 1c170c3..7f75df4 100644 --- a/src/main/java/com/deepgram/resources/speak/v1/types/SpeakV1Clear.java +++ b/src/main/java/com/deepgram/resources/speak/v1/types/SpeakV1Clear.java @@ -98,7 +98,6 @@ public Builder from(SpeakV1Clear other) { } /** - *

    Message type identifier

    *

    Message type identifier

    * @return Reference to {@code this} so that method calls can be chained together. */ diff --git a/src/main/java/com/deepgram/resources/speak/v1/types/SpeakV1Cleared.java b/src/main/java/com/deepgram/resources/speak/v1/types/SpeakV1Cleared.java index b494af8..a279df1 100644 --- a/src/main/java/com/deepgram/resources/speak/v1/types/SpeakV1Cleared.java +++ b/src/main/java/com/deepgram/resources/speak/v1/types/SpeakV1Cleared.java @@ -119,7 +119,6 @@ public Builder from(SpeakV1Cleared other) { } /** - *

    Message type identifier

    *

    Message type identifier

    * @return Reference to {@code this} so that method calls can be chained together. */ @@ -131,7 +130,6 @@ public SequenceIdStage type(@NotNull SpeakV1ClearedType type) { } /** - *

    The sequence ID of the response

    *

    The sequence ID of the response

    * @return Reference to {@code this} so that method calls can be chained together. */ diff --git a/src/main/java/com/deepgram/resources/speak/v1/types/SpeakV1Close.java b/src/main/java/com/deepgram/resources/speak/v1/types/SpeakV1Close.java index 81cc593..3aa0904 100644 --- a/src/main/java/com/deepgram/resources/speak/v1/types/SpeakV1Close.java +++ b/src/main/java/com/deepgram/resources/speak/v1/types/SpeakV1Close.java @@ -98,7 +98,6 @@ public Builder from(SpeakV1Close other) { } /** - *

    Message type identifier

    *

    Message type identifier

    * @return Reference to {@code this} so that method calls can be chained together. */ diff --git a/src/main/java/com/deepgram/resources/speak/v1/types/SpeakV1Flush.java b/src/main/java/com/deepgram/resources/speak/v1/types/SpeakV1Flush.java index 848dd1d..7355672 100644 --- a/src/main/java/com/deepgram/resources/speak/v1/types/SpeakV1Flush.java +++ b/src/main/java/com/deepgram/resources/speak/v1/types/SpeakV1Flush.java @@ -98,7 +98,6 @@ public Builder from(SpeakV1Flush other) { } /** - *

    Message type identifier

    *

    Message type identifier

    * @return Reference to {@code this} so that method calls can be chained together. */ diff --git a/src/main/java/com/deepgram/resources/speak/v1/types/SpeakV1Flushed.java b/src/main/java/com/deepgram/resources/speak/v1/types/SpeakV1Flushed.java index 442d656..db49f03 100644 --- a/src/main/java/com/deepgram/resources/speak/v1/types/SpeakV1Flushed.java +++ b/src/main/java/com/deepgram/resources/speak/v1/types/SpeakV1Flushed.java @@ -119,7 +119,6 @@ public Builder from(SpeakV1Flushed other) { } /** - *

    Message type identifier

    *

    Message type identifier

    * @return Reference to {@code this} so that method calls can be chained together. */ @@ -131,7 +130,6 @@ public SequenceIdStage type(@NotNull SpeakV1FlushedType type) { } /** - *

    The sequence ID of the response

    *

    The sequence ID of the response

    * @return Reference to {@code this} so that method calls can be chained together. */ diff --git a/src/main/java/com/deepgram/resources/speak/v1/types/SpeakV1Metadata.java b/src/main/java/com/deepgram/resources/speak/v1/types/SpeakV1Metadata.java index 44f77b6..1c89152 100644 --- a/src/main/java/com/deepgram/resources/speak/v1/types/SpeakV1Metadata.java +++ b/src/main/java/com/deepgram/resources/speak/v1/types/SpeakV1Metadata.java @@ -205,7 +205,6 @@ public Builder from(SpeakV1Metadata other) { } /** - *

    Unique identifier for the request

    *

    Unique identifier for the request

    * @return Reference to {@code this} so that method calls can be chained together. */ @@ -217,7 +216,6 @@ public ModelNameStage requestId(@NotNull String requestId) { } /** - *

    Name of the model being used

    *

    Name of the model being used

    * @return Reference to {@code this} so that method calls can be chained together. */ @@ -229,7 +227,6 @@ public ModelVersionStage modelName(@NotNull String modelName) { } /** - *

    Version of the primary model being used

    *

    Version of the primary model being used

    * @return Reference to {@code this} so that method calls can be chained together. */ @@ -241,7 +238,6 @@ public ModelUuidStage modelVersion(@NotNull String modelVersion) { } /** - *

    Unique identifier for the primary model used

    *

    Unique identifier for the primary model used

    * @return Reference to {@code this} so that method calls can be chained together. */ diff --git a/src/main/java/com/deepgram/resources/speak/v1/types/SpeakV1Text.java b/src/main/java/com/deepgram/resources/speak/v1/types/SpeakV1Text.java index 0f5704f..003857b 100644 --- a/src/main/java/com/deepgram/resources/speak/v1/types/SpeakV1Text.java +++ b/src/main/java/com/deepgram/resources/speak/v1/types/SpeakV1Text.java @@ -106,7 +106,6 @@ public Builder from(SpeakV1Text other) { } /** - *

    The input text to be converted to speech

    *

    The input text to be converted to speech

    * @return Reference to {@code this} so that method calls can be chained together. */ diff --git a/src/main/java/com/deepgram/resources/speak/v1/types/SpeakV1Warning.java b/src/main/java/com/deepgram/resources/speak/v1/types/SpeakV1Warning.java index f8ba1d2..00a5ffb 100644 --- a/src/main/java/com/deepgram/resources/speak/v1/types/SpeakV1Warning.java +++ b/src/main/java/com/deepgram/resources/speak/v1/types/SpeakV1Warning.java @@ -127,7 +127,6 @@ public Builder from(SpeakV1Warning other) { } /** - *

    A description of what went wrong

    *

    A description of what went wrong

    * @return Reference to {@code this} so that method calls can be chained together. */ @@ -139,7 +138,6 @@ public CodeStage description(@NotNull String description) { } /** - *

    Error code identifying the type of error

    *

    Error code identifying the type of error

    * @return Reference to {@code this} so that method calls can be chained together. */ 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 8033a55..8af4275 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,17 +122,6 @@ public CompletableFuture connect(V1ConnectOptions options) { urlBuilder.addQueryParameter( "speed", String.valueOf(options.getSpeed().get())); } - // Escape hatch: emit caller-supplied additionalProperties (e.g. no_delay) as query params. - // The generated template only serializes the typed options and drops these otherwise. - // ConnectOptions is request-only (never deserialized), so this map holds only what the - // caller set via the builder. Routed through QueryStringMapper to match the REST path. - 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/audio/AsyncRawAudioClient.java b/src/main/java/com/deepgram/resources/speak/v2/audio/AsyncRawAudioClient.java index 05baadc..a840a77 100644 --- a/src/main/java/com/deepgram/resources/speak/v2/audio/AsyncRawAudioClient.java +++ b/src/main/java/com/deepgram/resources/speak/v2/audio/AsyncRawAudioClient.java @@ -12,6 +12,7 @@ import com.deepgram.core.QueryStringMapper; import com.deepgram.core.RequestOptions; import com.deepgram.core.ResponseBodyInputStream; +import com.deepgram.core.RetryInterceptor; import com.deepgram.errors.BadRequestError; import com.deepgram.resources.speak.v2.audio.requests.SpeakV2Request; import com.fasterxml.jackson.core.JsonProcessingException; @@ -75,11 +76,19 @@ public CompletableFuture> generate( QueryStringMapper.addQueryParameter( httpUrl, "encoding", request.getEncoding().get(), false); } + if (request.getExpressivity().isPresent()) { + QueryStringMapper.addQueryParameter( + httpUrl, "expressivity", request.getExpressivity().get(), false); + } QueryStringMapper.addQueryParameter(httpUrl, "model", request.getModel(), false); if (request.getSampleRate().isPresent()) { QueryStringMapper.addQueryParameter( httpUrl, "sample_rate", request.getSampleRate().get(), false); } + if (request.getSpeed().isPresent()) { + QueryStringMapper.addQueryParameter( + httpUrl, "speed", request.getSpeed().get(), false); + } if (request.getPriority().isPresent()) { QueryStringMapper.addQueryParameter( httpUrl, "priority", request.getPriority().get(), false); @@ -110,6 +119,15 @@ public CompletableFuture> generate( if (requestOptions != null && requestOptions.getTimeout().isPresent()) { client = clientOptions.httpClientWithTimeout(requestOptions); } + if (requestOptions != null && requestOptions.getMaxRetries().isPresent()) { + okhttpRequest = okhttpRequest + .newBuilder() + .tag( + RetryInterceptor.MaxRetriesOverride.class, + new RetryInterceptor.MaxRetriesOverride( + requestOptions.getMaxRetries().get())) + .build(); + } CompletableFuture> future = new CompletableFuture<>(); client.newCall(okhttpRequest).enqueue(new Callback() { @Override @@ -134,6 +152,9 @@ public void onResponse(@NotNull Call call, @NotNull Response response) throws IO future.completeExceptionally(new DeepgramHttpException( "Error with status code " + response.code(), response.code(), errorBody, response)); return; + } catch (JsonProcessingException e) { + future.completeExceptionally( + new DeepgramApiException("Failed to deserialize response: " + e.getMessage(), e)); } catch (IOException e) { future.completeExceptionally(new DeepgramApiException("Network error executing HTTP request", e)); } diff --git a/src/main/java/com/deepgram/resources/speak/v2/audio/RawAudioClient.java b/src/main/java/com/deepgram/resources/speak/v2/audio/RawAudioClient.java index 08b3bd0..6fc9ae1 100644 --- a/src/main/java/com/deepgram/resources/speak/v2/audio/RawAudioClient.java +++ b/src/main/java/com/deepgram/resources/speak/v2/audio/RawAudioClient.java @@ -12,6 +12,7 @@ import com.deepgram.core.QueryStringMapper; import com.deepgram.core.RequestOptions; import com.deepgram.core.ResponseBodyInputStream; +import com.deepgram.core.RetryInterceptor; import com.deepgram.errors.BadRequestError; import com.deepgram.resources.speak.v2.audio.requests.SpeakV2Request; import com.fasterxml.jackson.core.JsonProcessingException; @@ -70,11 +71,19 @@ public DeepgramApiHttpResponse generate(SpeakV2Request request, Req QueryStringMapper.addQueryParameter( httpUrl, "encoding", request.getEncoding().get(), false); } + if (request.getExpressivity().isPresent()) { + QueryStringMapper.addQueryParameter( + httpUrl, "expressivity", request.getExpressivity().get(), false); + } QueryStringMapper.addQueryParameter(httpUrl, "model", request.getModel(), false); if (request.getSampleRate().isPresent()) { QueryStringMapper.addQueryParameter( httpUrl, "sample_rate", request.getSampleRate().get(), false); } + if (request.getSpeed().isPresent()) { + QueryStringMapper.addQueryParameter( + httpUrl, "speed", request.getSpeed().get(), false); + } if (request.getPriority().isPresent()) { QueryStringMapper.addQueryParameter( httpUrl, "priority", request.getPriority().get(), false); @@ -105,6 +114,15 @@ public DeepgramApiHttpResponse generate(SpeakV2Request request, Req if (requestOptions != null && requestOptions.getTimeout().isPresent()) { client = clientOptions.httpClientWithTimeout(requestOptions); } + if (requestOptions != null && requestOptions.getMaxRetries().isPresent()) { + okhttpRequest = okhttpRequest + .newBuilder() + .tag( + RetryInterceptor.MaxRetriesOverride.class, + new RetryInterceptor.MaxRetriesOverride( + requestOptions.getMaxRetries().get())) + .build(); + } try { Response response = client.newCall(okhttpRequest).execute(); ResponseBody responseBody = response.body(); @@ -123,6 +141,8 @@ public DeepgramApiHttpResponse generate(SpeakV2Request request, Req Object errorBody = ObjectMappers.parseErrorBody(responseBodyString); throw new DeepgramHttpException( "Error with status code " + response.code(), response.code(), errorBody, response); + } catch (JsonProcessingException e) { + throw new DeepgramApiException("Failed to deserialize response: " + e.getMessage(), e); } catch (IOException e) { throw new DeepgramApiException("Network error executing HTTP request", e); } diff --git a/src/main/java/com/deepgram/resources/speak/v2/audio/requests/SpeakV2Request.java b/src/main/java/com/deepgram/resources/speak/v2/audio/requests/SpeakV2Request.java index bf7a08c..7dfc30d 100644 --- a/src/main/java/com/deepgram/resources/speak/v2/audio/requests/SpeakV2Request.java +++ b/src/main/java/com/deepgram/resources/speak/v2/audio/requests/SpeakV2Request.java @@ -41,10 +41,14 @@ public final class SpeakV2Request { private final Optional encoding; + private final Optional expressivity; + private final String model; private final Optional sampleRate; + private final Optional speed; + private final Optional priority; private final String text; @@ -59,8 +63,10 @@ private SpeakV2Request( Optional bitRate, Optional container, Optional encoding, + Optional expressivity, String model, Optional sampleRate, + Optional speed, Optional priority, String text, Map additionalProperties) { @@ -71,8 +77,10 @@ private SpeakV2Request( this.bitRate = bitRate; this.container = container; this.encoding = encoding; + this.expressivity = expressivity; this.model = model; this.sampleRate = sampleRate; + this.speed = speed; this.priority = priority; this.text = text; this.additionalProperties = additionalProperties; @@ -134,6 +142,14 @@ public Optional getEncoding() { return encoding; } + /** + * @return Expressive range of the generated speech. 0 is the voice's nominal delivery; negative values are flatter and more restrained, positive values more animated. + */ + @JsonIgnore + public Optional getExpressivity() { + return expressivity; + } + /** * @return Flux TTS model used to synthesize the submitted text, in the form flux-{voice}-{language} (for example, flux-alexis-en). Required; unlike the v1 (Aura) endpoint there is no default and only flux models are accepted. English-only at launch. */ @@ -150,6 +166,14 @@ public Optional getSampleRate() { return sampleRate; } + /** + * @return Speaking rate multiplier that adjusts the pace of generated speech while preserving natural prosody and voice quality. Only the multipliers listed here are accepted — the range is 0.85 to 1.15 in 0.05 increments. Not yet supported in all languages. + */ + @JsonIgnore + public Optional getSpeed() { + return speed; + } + /** * @return Processing priority for asynchronous (callback) requests. The only supported value is low. */ @@ -159,7 +183,7 @@ public Optional getPriority() { } /** - * @return The text content to be converted to speech. The server normalizes and preprocesses the text (e.g. stripping inline controls) before synthesis. + * @return The text content to be converted to speech. The server normalizes and preprocesses the text before synthesis. May carry inline pause and pronunciation controls; see the TTS Voice Controls guide for their syntax and limits. Unlike the streaming transport, which drops an unusable pause control and warns, batch rejects the whole request with a 400. */ @JsonProperty("text") public String getText() { @@ -185,8 +209,10 @@ private boolean equalTo(SpeakV2Request other) { && bitRate.equals(other.bitRate) && container.equals(other.container) && encoding.equals(other.encoding) + && expressivity.equals(other.expressivity) && model.equals(other.model) && sampleRate.equals(other.sampleRate) + && speed.equals(other.speed) && priority.equals(other.priority) && text.equals(other.text); } @@ -201,8 +227,10 @@ public int hashCode() { this.bitRate, this.container, this.encoding, + this.expressivity, this.model, this.sampleRate, + this.speed, this.priority, this.text); } @@ -227,7 +255,7 @@ public interface ModelStage { public interface TextStage { /** - *

    The text content to be converted to speech. The server normalizes and preprocesses the text (e.g. stripping inline controls) before synthesis.

    + *

    The text content to be converted to speech. The server normalizes and preprocesses the text before synthesis. May carry inline pause and pronunciation controls; see the TTS Voice Controls guide for their syntax and limits. Unlike the streaming transport, which drops an unusable pause control and warns, batch rejects the whole request with a 400.

    */ _FinalStage text(@NotNull String text); } @@ -290,6 +318,13 @@ public interface _FinalStage { _FinalStage encoding(AudioGenerateRequestEncoding encoding); + /** + *

    Expressive range of the generated speech. 0 is the voice's nominal delivery; negative values are flatter and more restrained, positive values more animated.

    + */ + _FinalStage expressivity(Optional expressivity); + + _FinalStage expressivity(Integer expressivity); + /** *

    Sample Rate specifies the sample rate for the output audio. Based on the encoding, different sample rates are supported. For some encodings, the sample rate is not configurable

    */ @@ -297,6 +332,13 @@ public interface _FinalStage { _FinalStage sampleRate(Integer sampleRate); + /** + *

    Speaking rate multiplier that adjusts the pace of generated speech while preserving natural prosody and voice quality. Only the multipliers listed here are accepted — the range is 0.85 to 1.15 in 0.05 increments. Not yet supported in all languages.

    + */ + _FinalStage speed(Optional speed); + + _FinalStage speed(Double speed); + /** *

    Processing priority for asynchronous (callback) requests. The only supported value is low.

    */ @@ -313,8 +355,12 @@ public static final class Builder implements ModelStage, TextStage, _FinalStage private Optional priority = Optional.empty(); + private Optional speed = Optional.empty(); + private Optional sampleRate = Optional.empty(); + private Optional expressivity = Optional.empty(); + private Optional encoding = Optional.empty(); private Optional container = Optional.empty(); @@ -343,15 +389,16 @@ public Builder from(SpeakV2Request other) { bitRate(other.getBitRate()); container(other.getContainer()); encoding(other.getEncoding()); + expressivity(other.getExpressivity()); model(other.getModel()); sampleRate(other.getSampleRate()); + speed(other.getSpeed()); priority(other.getPriority()); text(other.getText()); return this; } /** - *

    Flux TTS model used to synthesize the submitted text, in the form flux-{voice}-{language} (for example, flux-alexis-en). Required; unlike the v1 (Aura) endpoint there is no default and only flux models are accepted. English-only at launch.

    *

    Flux TTS model used to synthesize the submitted text, in the form flux-{voice}-{language} (for example, flux-alexis-en). Required; unlike the v1 (Aura) endpoint there is no default and only flux models are accepted. English-only at launch.

    * @return Reference to {@code this} so that method calls can be chained together. */ @@ -363,8 +410,7 @@ public TextStage model(@NotNull String model) { } /** - *

    The text content to be converted to speech. The server normalizes and preprocesses the text (e.g. stripping inline controls) before synthesis.

    - *

    The text content to be converted to speech. The server normalizes and preprocesses the text (e.g. stripping inline controls) before synthesis.

    + *

    The text content to be converted to speech. The server normalizes and preprocesses the text before synthesis. May carry inline pause and pronunciation controls; see the TTS Voice Controls guide for their syntax and limits. Unlike the streaming transport, which drops an unusable pause control and warns, batch rejects the whole request with a 400.

    * @return Reference to {@code this} so that method calls can be chained together. */ @java.lang.Override @@ -394,6 +440,26 @@ public _FinalStage priority(Optional priority) { return this; } + /** + *

    Speaking rate multiplier that adjusts the pace of generated speech while preserving natural prosody and voice quality. Only the multipliers listed here are accepted — the range is 0.85 to 1.15 in 0.05 increments. Not yet supported in all languages.

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

    Speaking rate multiplier that adjusts the pace of generated speech while preserving natural prosody and voice quality. Only the multipliers listed here are accepted — the range is 0.85 to 1.15 in 0.05 increments. Not yet supported in all languages.

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

    Sample Rate specifies the sample rate for the output audio. Based on the encoding, different sample rates are supported. For some encodings, the sample rate is not configurable

    * @return Reference to {@code this} so that method calls can be chained together. @@ -414,6 +480,26 @@ public _FinalStage sampleRate(Optional sampleRate) { return this; } + /** + *

    Expressive range of the generated speech. 0 is the voice's nominal delivery; negative values are flatter and more restrained, positive values more animated.

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

    Expressive range of the generated speech. 0 is the voice's nominal delivery; negative values are flatter and more restrained, positive values more animated.

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

    Encoding allows you to specify the expected encoding of your audio output

    * @return Reference to {@code this} so that method calls can be chained together. @@ -570,8 +656,10 @@ public SpeakV2Request build() { bitRate, container, encoding, + expressivity, model, sampleRate, + speed, priority, text, additionalProperties); 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 51fef19..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,14 +36,6 @@ public boolean equals(Object other) { return other instanceof SpeakV2Close; } - @java.lang.Override - public int hashCode() { - // Manual patch: Fern generates equals() (all instances of this fields-less message - // are equal) but no hashCode(), violating the Object contract. Mirror equals() with a - // type-based constant hash. Remove once Fern emits a consistent equals/hashCode pair. - return getClass().hashCode(); - } - @JsonAnyGetter public Map getAdditionalProperties() { return this.additionalProperties; diff --git a/src/main/java/com/deepgram/resources/speak/v2/types/SpeakV2Configure.java b/src/main/java/com/deepgram/resources/speak/v2/types/SpeakV2Configure.java new file mode 100644 index 0000000..7e7e008 --- /dev/null +++ b/src/main/java/com/deepgram/resources/speak/v2/types/SpeakV2Configure.java @@ -0,0 +1,113 @@ +/** + * 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.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; + +@JsonInclude(JsonInclude.Include.NON_ABSENT) +@JsonDeserialize(builder = SpeakV2Configure.Builder.class) +public final class SpeakV2Configure { + private final Optional speed; + + private final Map additionalProperties; + + private SpeakV2Configure(Optional speed, Map additionalProperties) { + this.speed = speed; + this.additionalProperties = additionalProperties; + } + + /** + * @return Message type identifier + */ + @JsonProperty("type") + public String getType() { + return "Configure"; + } + + @JsonProperty("speed") + public Optional getSpeed() { + return speed; + } + + @java.lang.Override + public boolean equals(Object other) { + if (this == other) return true; + return other instanceof SpeakV2Configure && equalTo((SpeakV2Configure) other); + } + + @JsonAnyGetter + public Map getAdditionalProperties() { + return this.additionalProperties; + } + + private boolean equalTo(SpeakV2Configure other) { + return speed.equals(other.speed); + } + + @java.lang.Override + public int hashCode() { + return Objects.hash(this.speed); + } + + @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 speed = Optional.empty(); + + @JsonAnySetter + private Map additionalProperties = new HashMap<>(); + + private Builder() {} + + public Builder from(SpeakV2Configure other) { + speed(other.getSpeed()); + return this; + } + + @JsonSetter(value = "speed", nulls = Nulls.SKIP) + public Builder speed(Optional speed) { + this.speed = speed; + return this; + } + + public Builder speed(Double speed) { + this.speed = Optional.ofNullable(speed); + return this; + } + + public SpeakV2Configure build() { + return new SpeakV2Configure(speed, 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/SpeakV2ConfigureFailure.java b/src/main/java/com/deepgram/resources/speak/v2/types/SpeakV2ConfigureFailure.java new file mode 100644 index 0000000..fc27cc0 --- /dev/null +++ b/src/main/java/com/deepgram/resources/speak/v2/types/SpeakV2ConfigureFailure.java @@ -0,0 +1,260 @@ +/** + * 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.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 = SpeakV2ConfigureFailure.Builder.class) +public final class SpeakV2ConfigureFailure { + private final SpeakV2ConfigureFailureCode code; + + private final Optional field; + + private final Optional value; + + private final String description; + + private final Map additionalProperties; + + private SpeakV2ConfigureFailure( + SpeakV2ConfigureFailureCode code, + Optional field, + Optional value, + String description, + Map additionalProperties) { + this.code = code; + this.field = field; + this.value = value; + this.description = description; + this.additionalProperties = additionalProperties; + } + + /** + * @return Message type identifier + */ + @JsonProperty("type") + public String getType() { + return "ConfigureFailure"; + } + + /** + * @return Failure code, in SCREAMING_SNAKE_CASE. SPEED_OUT_OF_RANGE: outside the multipliers the model publishes. SPEED_INCREMENT_INVALID: inside the published range but not one of the multipliers. SPEED_NOT_SUPPORTED: this model or language has no runtime speed control at all. INTERNAL_ERROR: the configuration was acceptable but the server could not apply it — unlike the others, a server-side failure rather than a statement about the request. + */ + @JsonProperty("code") + public SpeakV2ConfigureFailureCode getCode() { + return code; + } + + /** + * @return The configuration field the failure is about. Absent when the failure is not tied to one field. + */ + @JsonProperty("field") + public Optional getField() { + return field; + } + + /** + * @return The rejected value for field. Absent when there is no offending value to echo — SPEED_NOT_SUPPORTED names the field but carries no value, because the rejection is a property of the model. + */ + @JsonProperty("value") + public Optional getValue() { + return value; + } + + /** + * @return A human-readable description of the failure + */ + @JsonProperty("description") + public String getDescription() { + return description; + } + + @java.lang.Override + public boolean equals(Object other) { + if (this == other) return true; + return other instanceof SpeakV2ConfigureFailure && equalTo((SpeakV2ConfigureFailure) other); + } + + @JsonAnyGetter + public Map getAdditionalProperties() { + return this.additionalProperties; + } + + private boolean equalTo(SpeakV2ConfigureFailure other) { + return code.equals(other.code) + && field.equals(other.field) + && value.equals(other.value) + && description.equals(other.description); + } + + @java.lang.Override + public int hashCode() { + return Objects.hash(this.code, this.field, this.value, this.description); + } + + @java.lang.Override + public String toString() { + return ObjectMappers.stringify(this); + } + + public static CodeStage builder() { + return new Builder(); + } + + public interface CodeStage { + /** + *

    Failure code, in SCREAMING_SNAKE_CASE. SPEED_OUT_OF_RANGE: outside the multipliers the model publishes. SPEED_INCREMENT_INVALID: inside the published range but not one of the multipliers. SPEED_NOT_SUPPORTED: this model or language has no runtime speed control at all. INTERNAL_ERROR: the configuration was acceptable but the server could not apply it — unlike the others, a server-side failure rather than a statement about the request.

    + */ + DescriptionStage code(@NotNull SpeakV2ConfigureFailureCode code); + + Builder from(SpeakV2ConfigureFailure other); + } + + public interface DescriptionStage { + /** + *

    A human-readable description of the failure

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

    The configuration field the failure is about. Absent when the failure is not tied to one field.

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

    The rejected value for field. Absent when there is no offending value to echo — SPEED_NOT_SUPPORTED names the field but carries no value, because the rejection is a property of the model.

    + */ + _FinalStage value(Optional value); + + _FinalStage value(Double value); + } + + @JsonIgnoreProperties(ignoreUnknown = true) + public static final class Builder implements CodeStage, DescriptionStage, _FinalStage { + private SpeakV2ConfigureFailureCode code; + + private String description; + + private Optional value = Optional.empty(); + + private Optional field = Optional.empty(); + + @JsonAnySetter + private Map additionalProperties = new HashMap<>(); + + private Builder() {} + + @java.lang.Override + public Builder from(SpeakV2ConfigureFailure other) { + code(other.getCode()); + field(other.getField()); + value(other.getValue()); + description(other.getDescription()); + return this; + } + + /** + *

    Failure code, in SCREAMING_SNAKE_CASE. SPEED_OUT_OF_RANGE: outside the multipliers the model publishes. SPEED_INCREMENT_INVALID: inside the published range but not one of the multipliers. SPEED_NOT_SUPPORTED: this model or language has no runtime speed control at all. INTERNAL_ERROR: the configuration was acceptable but the server could not apply it — unlike the others, a server-side failure rather than a statement about the request.

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

    A human-readable description of the failure

    + * @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; + } + + /** + *

    The rejected value for field. Absent when there is no offending value to echo — SPEED_NOT_SUPPORTED names the field but carries no value, because the rejection is a property of the model.

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

    The rejected value for field. Absent when there is no offending value to echo — SPEED_NOT_SUPPORTED names the field but carries no value, because the rejection is a property of the model.

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

    The configuration field the failure is about. Absent when the failure is not tied to one field.

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

    The configuration field the failure is about. Absent when the failure is not tied to one field.

    + */ + @java.lang.Override + @JsonSetter(value = "field", nulls = Nulls.SKIP) + public _FinalStage field(Optional field) { + this.field = field; + return this; + } + + @java.lang.Override + public SpeakV2ConfigureFailure build() { + return new SpeakV2ConfigureFailure(code, field, value, description, 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/speak/v2/types/SpeakV2ConfigureFailureCode.java b/src/main/java/com/deepgram/resources/speak/v2/types/SpeakV2ConfigureFailureCode.java new file mode 100644 index 0000000..7d02bce --- /dev/null +++ b/src/main/java/com/deepgram/resources/speak/v2/types/SpeakV2ConfigureFailureCode.java @@ -0,0 +1,108 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ +package com.deepgram.resources.speak.v2.types; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonValue; + +public final class SpeakV2ConfigureFailureCode { + public static final SpeakV2ConfigureFailureCode SPEED_OUT_OF_RANGE = + new SpeakV2ConfigureFailureCode(Value.SPEED_OUT_OF_RANGE, "SPEED_OUT_OF_RANGE"); + + public static final SpeakV2ConfigureFailureCode SPEED_INCREMENT_INVALID = + new SpeakV2ConfigureFailureCode(Value.SPEED_INCREMENT_INVALID, "SPEED_INCREMENT_INVALID"); + + public static final SpeakV2ConfigureFailureCode INTERNAL_ERROR = + new SpeakV2ConfigureFailureCode(Value.INTERNAL_ERROR, "INTERNAL_ERROR"); + + public static final SpeakV2ConfigureFailureCode SPEED_NOT_SUPPORTED = + new SpeakV2ConfigureFailureCode(Value.SPEED_NOT_SUPPORTED, "SPEED_NOT_SUPPORTED"); + + private final Value value; + + private final String string; + + SpeakV2ConfigureFailureCode(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 SpeakV2ConfigureFailureCode + && this.string.equals(((SpeakV2ConfigureFailureCode) other).string)); + } + + @java.lang.Override + public int hashCode() { + return this.string.hashCode(); + } + + public T visit(Visitor visitor) { + switch (value) { + case SPEED_OUT_OF_RANGE: + return visitor.visitSpeedOutOfRange(); + case SPEED_INCREMENT_INVALID: + return visitor.visitSpeedIncrementInvalid(); + case INTERNAL_ERROR: + return visitor.visitInternalError(); + case SPEED_NOT_SUPPORTED: + return visitor.visitSpeedNotSupported(); + case UNKNOWN: + default: + return visitor.visitUnknown(string); + } + } + + @JsonCreator(mode = JsonCreator.Mode.DELEGATING) + public static SpeakV2ConfigureFailureCode valueOf(String value) { + switch (value) { + case "SPEED_OUT_OF_RANGE": + return SPEED_OUT_OF_RANGE; + case "SPEED_INCREMENT_INVALID": + return SPEED_INCREMENT_INVALID; + case "INTERNAL_ERROR": + return INTERNAL_ERROR; + case "SPEED_NOT_SUPPORTED": + return SPEED_NOT_SUPPORTED; + default: + return new SpeakV2ConfigureFailureCode(Value.UNKNOWN, value); + } + } + + public enum Value { + SPEED_OUT_OF_RANGE, + + SPEED_INCREMENT_INVALID, + + SPEED_NOT_SUPPORTED, + + INTERNAL_ERROR, + + UNKNOWN + } + + public interface Visitor { + T visitSpeedOutOfRange(); + + T visitSpeedIncrementInvalid(); + + T visitSpeedNotSupported(); + + T visitInternalError(); + + T visitUnknown(String unknownType); + } +} diff --git a/src/main/java/com/deepgram/resources/speak/v2/types/SpeakV2ConfigureSuccess.java b/src/main/java/com/deepgram/resources/speak/v2/types/SpeakV2ConfigureSuccess.java new file mode 100644 index 0000000..6d657d3 --- /dev/null +++ b/src/main/java/com/deepgram/resources/speak/v2/types/SpeakV2ConfigureSuccess.java @@ -0,0 +1,136 @@ +/** + * 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.annotation.JsonSetter; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import java.util.HashMap; +import java.util.Map; +import java.util.Objects; +import org.jetbrains.annotations.NotNull; + +@JsonInclude(JsonInclude.Include.NON_ABSENT) +@JsonDeserialize(builder = SpeakV2ConfigureSuccess.Builder.class) +public final class SpeakV2ConfigureSuccess { + private final SpeakV2ConfigureSuccessApplied applied; + + private final Map additionalProperties; + + private SpeakV2ConfigureSuccess(SpeakV2ConfigureSuccessApplied applied, Map additionalProperties) { + this.applied = applied; + this.additionalProperties = additionalProperties; + } + + /** + * @return Message type identifier + */ + @JsonProperty("type") + public String getType() { + return "ConfigureSuccess"; + } + + /** + * @return Synthesis configuration. A field is present only when it has been set on this session. + */ + @JsonProperty("applied") + public SpeakV2ConfigureSuccessApplied getApplied() { + return applied; + } + + @java.lang.Override + public boolean equals(Object other) { + if (this == other) return true; + return other instanceof SpeakV2ConfigureSuccess && equalTo((SpeakV2ConfigureSuccess) other); + } + + @JsonAnyGetter + public Map getAdditionalProperties() { + return this.additionalProperties; + } + + private boolean equalTo(SpeakV2ConfigureSuccess other) { + return applied.equals(other.applied); + } + + @java.lang.Override + public int hashCode() { + return Objects.hash(this.applied); + } + + @java.lang.Override + public String toString() { + return ObjectMappers.stringify(this); + } + + public static AppliedStage builder() { + return new Builder(); + } + + public interface AppliedStage { + /** + *

    Synthesis configuration. A field is present only when it has been set on this session.

    + */ + _FinalStage applied(@NotNull SpeakV2ConfigureSuccessApplied applied); + + Builder from(SpeakV2ConfigureSuccess other); + } + + public interface _FinalStage { + SpeakV2ConfigureSuccess build(); + + _FinalStage additionalProperty(String key, Object value); + + _FinalStage additionalProperties(Map additionalProperties); + } + + @JsonIgnoreProperties(ignoreUnknown = true) + public static final class Builder implements AppliedStage, _FinalStage { + private SpeakV2ConfigureSuccessApplied applied; + + @JsonAnySetter + private Map additionalProperties = new HashMap<>(); + + private Builder() {} + + @java.lang.Override + public Builder from(SpeakV2ConfigureSuccess other) { + applied(other.getApplied()); + return this; + } + + /** + *

    Synthesis configuration. A field is present only when it has been set on this session.

    + * @return Reference to {@code this} so that method calls can be chained together. + */ + @java.lang.Override + @JsonSetter("applied") + public _FinalStage applied(@NotNull SpeakV2ConfigureSuccessApplied applied) { + this.applied = Objects.requireNonNull(applied, "applied must not be null"); + return this; + } + + @java.lang.Override + public SpeakV2ConfigureSuccess build() { + return new SpeakV2ConfigureSuccess(applied, 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/speak/v2/types/SpeakV2ConfigureSuccessApplied.java b/src/main/java/com/deepgram/resources/speak/v2/types/SpeakV2ConfigureSuccessApplied.java new file mode 100644 index 0000000..ca8d8fd --- /dev/null +++ b/src/main/java/com/deepgram/resources/speak/v2/types/SpeakV2ConfigureSuccessApplied.java @@ -0,0 +1,105 @@ +/** + * 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.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; + +@JsonInclude(JsonInclude.Include.NON_ABSENT) +@JsonDeserialize(builder = SpeakV2ConfigureSuccessApplied.Builder.class) +public final class SpeakV2ConfigureSuccessApplied { + private final Optional speed; + + private final Map additionalProperties; + + private SpeakV2ConfigureSuccessApplied(Optional speed, Map additionalProperties) { + this.speed = speed; + this.additionalProperties = additionalProperties; + } + + @JsonProperty("speed") + public Optional getSpeed() { + return speed; + } + + @java.lang.Override + public boolean equals(Object other) { + if (this == other) return true; + return other instanceof SpeakV2ConfigureSuccessApplied && equalTo((SpeakV2ConfigureSuccessApplied) other); + } + + @JsonAnyGetter + public Map getAdditionalProperties() { + return this.additionalProperties; + } + + private boolean equalTo(SpeakV2ConfigureSuccessApplied other) { + return speed.equals(other.speed); + } + + @java.lang.Override + public int hashCode() { + return Objects.hash(this.speed); + } + + @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 speed = Optional.empty(); + + @JsonAnySetter + private Map additionalProperties = new HashMap<>(); + + private Builder() {} + + public Builder from(SpeakV2ConfigureSuccessApplied other) { + speed(other.getSpeed()); + return this; + } + + @JsonSetter(value = "speed", nulls = Nulls.SKIP) + public Builder speed(Optional speed) { + this.speed = speed; + return this; + } + + public Builder speed(Double speed) { + this.speed = Optional.ofNullable(speed); + return this; + } + + public SpeakV2ConfigureSuccessApplied build() { + return new SpeakV2ConfigureSuccessApplied(speed, 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/SpeakV2Connected.java b/src/main/java/com/deepgram/resources/speak/v2/types/SpeakV2Connected.java index 64301ae..4b6046d 100644 --- a/src/main/java/com/deepgram/resources/speak/v2/types/SpeakV2Connected.java +++ b/src/main/java/com/deepgram/resources/speak/v2/types/SpeakV2Connected.java @@ -182,7 +182,6 @@ public Builder from(SpeakV2Connected other) { } /** - *

    The unique identifier of the /v2/speak request

    *

    The unique identifier of the /v2/speak request

    * @return Reference to {@code this} so that method calls can be chained together. */ @@ -194,7 +193,6 @@ public ModelNameStage requestId(@NotNull String requestId) { } /** - *

    Resolved model name

    *

    Resolved model name

    * @return Reference to {@code this} so that method calls can be chained together. */ @@ -206,7 +204,6 @@ public ModelVersionStage modelName(@NotNull String modelName) { } /** - *

    Resolved model version

    *

    Resolved model version

    * @return Reference to {@code this} so that method calls can be chained together. */ diff --git a/src/main/java/com/deepgram/resources/speak/v2/types/SpeakV2Error.java b/src/main/java/com/deepgram/resources/speak/v2/types/SpeakV2Error.java index 288d11c..343cfa7 100644 --- a/src/main/java/com/deepgram/resources/speak/v2/types/SpeakV2Error.java +++ b/src/main/java/com/deepgram/resources/speak/v2/types/SpeakV2Error.java @@ -127,7 +127,6 @@ public Builder from(SpeakV2Error other) { } /** - *

    A code identifying the error, e.g. MESSAGE-0000 or NET-0000.

    *

    A code identifying the error, e.g. MESSAGE-0000 or NET-0000.

    * @return Reference to {@code this} so that method calls can be chained together. */ @@ -139,7 +138,6 @@ public DescriptionStage code(@NotNull SpeakV2ErrorCode code) { } /** - *

    Prose description of the error

    *

    Prose description of the error

    * @return Reference to {@code this} so that method calls can be chained together. */ diff --git a/src/main/java/com/deepgram/resources/speak/v2/types/SpeakV2ErrorCode.java b/src/main/java/com/deepgram/resources/speak/v2/types/SpeakV2ErrorCode.java index d84171d..82c91cc 100644 --- a/src/main/java/com/deepgram/resources/speak/v2/types/SpeakV2ErrorCode.java +++ b/src/main/java/com/deepgram/resources/speak/v2/types/SpeakV2ErrorCode.java @@ -7,6 +7,8 @@ import com.fasterxml.jackson.annotation.JsonValue; public final class SpeakV2ErrorCode { + public static final SpeakV2ErrorCode DATA0002 = new SpeakV2ErrorCode(Value.DATA0002, "DATA-0002"); + public static final SpeakV2ErrorCode NET0002 = new SpeakV2ErrorCode(Value.NET0002, "NET-0002"); public static final SpeakV2ErrorCode NET0003 = new SpeakV2ErrorCode(Value.NET0003, "NET-0003"); @@ -55,6 +57,8 @@ public int hashCode() { public T visit(Visitor visitor) { switch (value) { + case DATA0002: + return visitor.visitData0002(); case NET0002: return visitor.visitNet0002(); case NET0003: @@ -80,6 +84,8 @@ public T visit(Visitor visitor) { @JsonCreator(mode = JsonCreator.Mode.DELEGATING) public static SpeakV2ErrorCode valueOf(String value) { switch (value) { + case "DATA-0002": + return DATA0002; case "NET-0002": return NET0002; case "NET-0003": @@ -106,6 +112,8 @@ public enum Value { DATA0000, + DATA0002, + BIG0000, NET0000, @@ -126,6 +134,8 @@ public interface Visitor { T visitData0000(); + T visitData0002(); + T visitBig0000(); T visitNet0000(); 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 e122400..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,14 +36,6 @@ public boolean equals(Object other) { return other instanceof SpeakV2Flush; } - @java.lang.Override - public int hashCode() { - // Manual patch: Fern generates equals() (all instances of this fields-less message - // are equal) but no hashCode(), violating the Object contract. Mirror equals() with a - // type-based constant hash. Remove once Fern emits a consistent equals/hashCode pair. - return getClass().hashCode(); - } - @JsonAnyGetter public Map getAdditionalProperties() { return this.additionalProperties; diff --git a/src/main/java/com/deepgram/resources/speak/v2/types/SpeakV2Flushed.java b/src/main/java/com/deepgram/resources/speak/v2/types/SpeakV2Flushed.java index 76aa4f9..74629bd 100644 --- a/src/main/java/com/deepgram/resources/speak/v2/types/SpeakV2Flushed.java +++ b/src/main/java/com/deepgram/resources/speak/v2/types/SpeakV2Flushed.java @@ -106,7 +106,6 @@ public Builder from(SpeakV2Flushed other) { } /** - *

    Server-assigned turn identifier

    *

    Server-assigned turn identifier

    * @return Reference to {@code this} so that method calls can be chained together. */ diff --git a/src/main/java/com/deepgram/resources/speak/v2/types/SpeakV2Interrupt.java b/src/main/java/com/deepgram/resources/speak/v2/types/SpeakV2Interrupt.java new file mode 100644 index 0000000..ea33b57 --- /dev/null +++ b/src/main/java/com/deepgram/resources/speak/v2/types/SpeakV2Interrupt.java @@ -0,0 +1,122 @@ +/** + * 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.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; + +@JsonInclude(JsonInclude.Include.NON_ABSENT) +@JsonDeserialize(builder = SpeakV2Interrupt.Builder.class) +public final class SpeakV2Interrupt { + private final Optional playbackOffset; + + private final Map additionalProperties; + + private SpeakV2Interrupt( + Optional playbackOffset, Map additionalProperties) { + this.playbackOffset = playbackOffset; + this.additionalProperties = additionalProperties; + } + + /** + * @return Message type identifier + */ + @JsonProperty("type") + public String getType() { + return "Interrupt"; + } + + /** + * @return How much audio the client had played when the user barged in. Optional: without it the server cannot split the turn's text, so SpeechInterrupted omits text_spoken and text_remaining. + *

    The offset is cumulative from the start of the session, not from the start of the current turn. Each Interrupt must advance past the position the previous one established.

    + */ + @JsonProperty("playback_offset") + public Optional getPlaybackOffset() { + return playbackOffset; + } + + @java.lang.Override + public boolean equals(Object other) { + if (this == other) return true; + return other instanceof SpeakV2Interrupt && equalTo((SpeakV2Interrupt) other); + } + + @JsonAnyGetter + public Map getAdditionalProperties() { + return this.additionalProperties; + } + + private boolean equalTo(SpeakV2Interrupt other) { + return playbackOffset.equals(other.playbackOffset); + } + + @java.lang.Override + public int hashCode() { + return Objects.hash(this.playbackOffset); + } + + @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 playbackOffset = Optional.empty(); + + @JsonAnySetter + private Map additionalProperties = new HashMap<>(); + + private Builder() {} + + public Builder from(SpeakV2Interrupt other) { + playbackOffset(other.getPlaybackOffset()); + return this; + } + + /** + *

    How much audio the client had played when the user barged in. Optional: without it the server cannot split the turn's text, so SpeechInterrupted omits text_spoken and text_remaining.

    + *

    The offset is cumulative from the start of the session, not from the start of the current turn. Each Interrupt must advance past the position the previous one established.

    + */ + @JsonSetter(value = "playback_offset", nulls = Nulls.SKIP) + public Builder playbackOffset(Optional playbackOffset) { + this.playbackOffset = playbackOffset; + return this; + } + + public Builder playbackOffset(SpeakV2InterruptPlaybackOffset playbackOffset) { + this.playbackOffset = Optional.ofNullable(playbackOffset); + return this; + } + + public SpeakV2Interrupt build() { + return new SpeakV2Interrupt(playbackOffset, 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/SpeakV2InterruptPlaybackOffset.java b/src/main/java/com/deepgram/resources/speak/v2/types/SpeakV2InterruptPlaybackOffset.java new file mode 100644 index 0000000..0cef7cb --- /dev/null +++ b/src/main/java/com/deepgram/resources/speak/v2/types/SpeakV2InterruptPlaybackOffset.java @@ -0,0 +1,135 @@ +/** + * 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.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 = SpeakV2InterruptPlaybackOffset.Builder.class) +public final class SpeakV2InterruptPlaybackOffset { + private final int value; + + private final Map additionalProperties; + + private SpeakV2InterruptPlaybackOffset(int value, Map additionalProperties) { + this.value = value; + this.additionalProperties = additionalProperties; + } + + /** + * @return Offset unit. time_ms is the only supported form. + */ + @JsonProperty("type") + public String getType() { + return "time_ms"; + } + + /** + * @return Milliseconds of session audio the client played before barging in. + */ + @JsonProperty("value") + public int getValue() { + return value; + } + + @java.lang.Override + public boolean equals(Object other) { + if (this == other) return true; + return other instanceof SpeakV2InterruptPlaybackOffset && equalTo((SpeakV2InterruptPlaybackOffset) other); + } + + @JsonAnyGetter + public Map getAdditionalProperties() { + return this.additionalProperties; + } + + private boolean equalTo(SpeakV2InterruptPlaybackOffset other) { + return value == other.value; + } + + @java.lang.Override + public int hashCode() { + return Objects.hash(this.value); + } + + @java.lang.Override + public String toString() { + return ObjectMappers.stringify(this); + } + + public static ValueStage builder() { + return new Builder(); + } + + public interface ValueStage { + /** + *

    Milliseconds of session audio the client played before barging in.

    + */ + _FinalStage value(int value); + + Builder from(SpeakV2InterruptPlaybackOffset other); + } + + public interface _FinalStage { + SpeakV2InterruptPlaybackOffset build(); + + _FinalStage additionalProperty(String key, Object value); + + _FinalStage additionalProperties(Map additionalProperties); + } + + @JsonIgnoreProperties(ignoreUnknown = true) + public static final class Builder implements ValueStage, _FinalStage { + private int value; + + @JsonAnySetter + private Map additionalProperties = new HashMap<>(); + + private Builder() {} + + @java.lang.Override + public Builder from(SpeakV2InterruptPlaybackOffset other) { + value(other.getValue()); + return this; + } + + /** + *

    Milliseconds of session audio the client played before barging in.

    + * @return Reference to {@code this} so that method calls can be chained together. + */ + @java.lang.Override + @JsonSetter("value") + public _FinalStage value(int value) { + this.value = value; + return this; + } + + @java.lang.Override + public SpeakV2InterruptPlaybackOffset build() { + return new SpeakV2InterruptPlaybackOffset(value, 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/speak/v2/types/SpeakV2SessionMetadata.java b/src/main/java/com/deepgram/resources/speak/v2/types/SpeakV2SessionMetadata.java index a0d38ee..726537b 100644 --- a/src/main/java/com/deepgram/resources/speak/v2/types/SpeakV2SessionMetadata.java +++ b/src/main/java/com/deepgram/resources/speak/v2/types/SpeakV2SessionMetadata.java @@ -46,7 +46,7 @@ public String getType() { } /** - * @return Cumulative audio duration produced across the session, in milliseconds + * @return Cumulative audio duration produced across the session, in milliseconds. An Interrupt rebases this onto the audio the client actually played. */ @JsonProperty("total_audio_duration_ms") public int getTotalAudioDurationMs() { @@ -102,7 +102,7 @@ public static TotalAudioDurationMsStage builder() { public interface TotalAudioDurationMsStage { /** - *

    Cumulative audio duration produced across the session, in milliseconds

    + *

    Cumulative audio duration produced across the session, in milliseconds. An Interrupt rebases this onto the audio the client actually played.

    */ TotalInputCharacterCountStage totalAudioDurationMs(int totalAudioDurationMs); @@ -157,8 +157,7 @@ public Builder from(SpeakV2SessionMetadata other) { } /** - *

    Cumulative audio duration produced across the session, in milliseconds

    - *

    Cumulative audio duration produced across the session, in milliseconds

    + *

    Cumulative audio duration produced across the session, in milliseconds. An Interrupt rebases this onto the audio the client actually played.

    * @return Reference to {@code this} so that method calls can be chained together. */ @java.lang.Override @@ -169,7 +168,6 @@ public TotalInputCharacterCountStage totalAudioDurationMs(int totalAudioDuration } /** - *

    Cumulative raw input character count across the session

    *

    Cumulative raw input character count across the session

    * @return Reference to {@code this} so that method calls can be chained together. */ @@ -181,7 +179,6 @@ public TotalBillableCharacterCountStage totalInputCharacterCount(int totalInputC } /** - *

    Cumulative billable character count across the session

    *

    Cumulative billable character count across the session

    * @return Reference to {@code this} so that method calls can be chained together. */ diff --git a/src/main/java/com/deepgram/resources/speak/v2/types/SpeakV2Speak.java b/src/main/java/com/deepgram/resources/speak/v2/types/SpeakV2Speak.java index 56476cc..2d4e5bd 100644 --- a/src/main/java/com/deepgram/resources/speak/v2/types/SpeakV2Speak.java +++ b/src/main/java/com/deepgram/resources/speak/v2/types/SpeakV2Speak.java @@ -37,7 +37,7 @@ public String getType() { } /** - * @return The input text to synthesize + * @return The input text to synthesize. May carry inline pause and pronunciation controls; see the TTS Voice Controls guide for their syntax and limits. A control that is rejected or ignored raises a Warning, while a malformed one is fatal (DATA-0002). */ @JsonProperty("text") public String getText() { @@ -75,7 +75,7 @@ public static TextStage builder() { public interface TextStage { /** - *

    The input text to synthesize

    + *

    The input text to synthesize. May carry inline pause and pronunciation controls; see the TTS Voice Controls guide for their syntax and limits. A control that is rejected or ignored raises a Warning, while a malformed one is fatal (DATA-0002).

    */ _FinalStage text(@NotNull String text); @@ -106,8 +106,7 @@ public Builder from(SpeakV2Speak other) { } /** - *

    The input text to synthesize

    - *

    The input text to synthesize

    + *

    The input text to synthesize. May carry inline pause and pronunciation controls; see the TTS Voice Controls guide for their syntax and limits. A control that is rejected or ignored raises a Warning, while a malformed one is fatal (DATA-0002).

    * @return Reference to {@code this} so that method calls can be chained together. */ @java.lang.Override diff --git a/src/main/java/com/deepgram/resources/speak/v2/types/SpeakV2SpeechInterrupted.java b/src/main/java/com/deepgram/resources/speak/v2/types/SpeakV2SpeechInterrupted.java new file mode 100644 index 0000000..98ed500 --- /dev/null +++ b/src/main/java/com/deepgram/resources/speak/v2/types/SpeakV2SpeechInterrupted.java @@ -0,0 +1,261 @@ +/** + * 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.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 = SpeakV2SpeechInterrupted.Builder.class) +public final class SpeakV2SpeechInterrupted { + private final int audioPlayedMs; + + private final Optional textSpoken; + + private final Optional textRemaining; + + private final SpeakV2SpeechInterruptedMetadata metadata; + + private final Map additionalProperties; + + private SpeakV2SpeechInterrupted( + int audioPlayedMs, + Optional textSpoken, + Optional textRemaining, + SpeakV2SpeechInterruptedMetadata metadata, + Map additionalProperties) { + this.audioPlayedMs = audioPlayedMs; + this.textSpoken = textSpoken; + this.textRemaining = textRemaining; + this.metadata = metadata; + this.additionalProperties = additionalProperties; + } + + /** + * @return Message type identifier + */ + @JsonProperty("type") + public String getType() { + return "SpeechInterrupted"; + } + + /** + * @return How much audio the client had played when the interrupt landed, in milliseconds from the start of the session. Echoes the Interrupt's playback_offset when one was supplied. Otherwise it is the server's own total, representing the audio that has been generated so far. A client that sends its first Interrupt without an offset can use this value as the baseline the next one must advance past. + */ + @JsonProperty("audio_played_ms") + public int getAudioPlayedMs() { + return audioPlayedMs; + } + + /** + * @return The portion of the turn's text the user heard. Omitted when the Interrupt carried no playback_offset. + */ + @JsonProperty("text_spoken") + public Optional getTextSpoken() { + return textSpoken; + } + + /** + * @return The portion of the turn's text the user did not hear. Omitted when the Interrupt carried no playback_offset. + */ + @JsonProperty("text_remaining") + public Optional getTextRemaining() { + return textRemaining; + } + + /** + * @return Billing and timing for a single turn. + */ + @JsonProperty("metadata") + public SpeakV2SpeechInterruptedMetadata getMetadata() { + return metadata; + } + + @java.lang.Override + public boolean equals(Object other) { + if (this == other) return true; + return other instanceof SpeakV2SpeechInterrupted && equalTo((SpeakV2SpeechInterrupted) other); + } + + @JsonAnyGetter + public Map getAdditionalProperties() { + return this.additionalProperties; + } + + private boolean equalTo(SpeakV2SpeechInterrupted other) { + return audioPlayedMs == other.audioPlayedMs + && textSpoken.equals(other.textSpoken) + && textRemaining.equals(other.textRemaining) + && metadata.equals(other.metadata); + } + + @java.lang.Override + public int hashCode() { + return Objects.hash(this.audioPlayedMs, this.textSpoken, this.textRemaining, this.metadata); + } + + @java.lang.Override + public String toString() { + return ObjectMappers.stringify(this); + } + + public static AudioPlayedMsStage builder() { + return new Builder(); + } + + public interface AudioPlayedMsStage { + /** + *

    How much audio the client had played when the interrupt landed, in milliseconds from the start of the session. Echoes the Interrupt's playback_offset when one was supplied. Otherwise it is the server's own total, representing the audio that has been generated so far. A client that sends its first Interrupt without an offset can use this value as the baseline the next one must advance past.

    + */ + MetadataStage audioPlayedMs(int audioPlayedMs); + + Builder from(SpeakV2SpeechInterrupted other); + } + + public interface MetadataStage { + /** + *

    Billing and timing for a single turn.

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

    The portion of the turn's text the user heard. Omitted when the Interrupt carried no playback_offset.

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

    The portion of the turn's text the user did not hear. Omitted when the Interrupt carried no playback_offset.

    + */ + _FinalStage textRemaining(Optional textRemaining); + + _FinalStage textRemaining(String textRemaining); + } + + @JsonIgnoreProperties(ignoreUnknown = true) + public static final class Builder implements AudioPlayedMsStage, MetadataStage, _FinalStage { + private int audioPlayedMs; + + private SpeakV2SpeechInterruptedMetadata metadata; + + private Optional textRemaining = Optional.empty(); + + private Optional textSpoken = Optional.empty(); + + @JsonAnySetter + private Map additionalProperties = new HashMap<>(); + + private Builder() {} + + @java.lang.Override + public Builder from(SpeakV2SpeechInterrupted other) { + audioPlayedMs(other.getAudioPlayedMs()); + textSpoken(other.getTextSpoken()); + textRemaining(other.getTextRemaining()); + metadata(other.getMetadata()); + return this; + } + + /** + *

    How much audio the client had played when the interrupt landed, in milliseconds from the start of the session. Echoes the Interrupt's playback_offset when one was supplied. Otherwise it is the server's own total, representing the audio that has been generated so far. A client that sends its first Interrupt without an offset can use this value as the baseline the next one must advance past.

    + * @return Reference to {@code this} so that method calls can be chained together. + */ + @java.lang.Override + @JsonSetter("audio_played_ms") + public MetadataStage audioPlayedMs(int audioPlayedMs) { + this.audioPlayedMs = audioPlayedMs; + return this; + } + + /** + *

    Billing and timing for a single turn.

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

    The portion of the turn's text the user did not hear. Omitted when the Interrupt carried no playback_offset.

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

    The portion of the turn's text the user did not hear. Omitted when the Interrupt carried no playback_offset.

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

    The portion of the turn's text the user heard. Omitted when the Interrupt carried no playback_offset.

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

    The portion of the turn's text the user heard. Omitted when the Interrupt carried no playback_offset.

    + */ + @java.lang.Override + @JsonSetter(value = "text_spoken", nulls = Nulls.SKIP) + public _FinalStage textSpoken(Optional textSpoken) { + this.textSpoken = textSpoken; + return this; + } + + @java.lang.Override + public SpeakV2SpeechInterrupted build() { + return new SpeakV2SpeechInterrupted( + audioPlayedMs, textSpoken, textRemaining, metadata, 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/speak/v2/types/SpeakV2SpeechInterruptedMetadata.java b/src/main/java/com/deepgram/resources/speak/v2/types/SpeakV2SpeechInterruptedMetadata.java new file mode 100644 index 0000000..b16d5f8 --- /dev/null +++ b/src/main/java/com/deepgram/resources/speak/v2/types/SpeakV2SpeechInterruptedMetadata.java @@ -0,0 +1,283 @@ +/** + * 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.annotation.JsonSetter; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import java.util.HashMap; +import java.util.Map; +import java.util.Objects; +import org.jetbrains.annotations.NotNull; + +@JsonInclude(JsonInclude.Include.NON_ABSENT) +@JsonDeserialize(builder = SpeakV2SpeechInterruptedMetadata.Builder.class) +public final class SpeakV2SpeechInterruptedMetadata { + private final String speechId; + + private final int audioDurationMs; + + private final int inputCharacterCount; + + private final int billableCharacterCount; + + private final SpeakV2SpeechInterruptedMetadataControlsApplied controlsApplied; + + private final Map additionalProperties; + + private SpeakV2SpeechInterruptedMetadata( + String speechId, + int audioDurationMs, + int inputCharacterCount, + int billableCharacterCount, + SpeakV2SpeechInterruptedMetadataControlsApplied controlsApplied, + Map additionalProperties) { + this.speechId = speechId; + this.audioDurationMs = audioDurationMs; + this.inputCharacterCount = inputCharacterCount; + this.billableCharacterCount = billableCharacterCount; + this.controlsApplied = controlsApplied; + this.additionalProperties = additionalProperties; + } + + /** + * @return Server-assigned turn identifier + */ + @JsonProperty("speech_id") + public String getSpeechId() { + return speechId; + } + + /** + * @return Audio duration produced for this turn, in milliseconds + */ + @JsonProperty("audio_duration_ms") + public int getAudioDurationMs() { + return audioDurationMs; + } + + /** + * @return Raw input character count for this turn, before text normalization + */ + @JsonProperty("input_character_count") + public int getInputCharacterCount() { + return inputCharacterCount; + } + + /** + * @return Billable character count for this turn — the input character count with stripped control characters removed. Always less than or equal to input_character_count. + */ + @JsonProperty("billable_character_count") + public int getBillableCharacterCount() { + return billableCharacterCount; + } + + /** + * @return Counts of the inline controls the server acted on during the turn. A control that was rejected or ignored is reported through a Warning and does not count here. + */ + @JsonProperty("controls_applied") + public SpeakV2SpeechInterruptedMetadataControlsApplied getControlsApplied() { + return controlsApplied; + } + + @java.lang.Override + public boolean equals(Object other) { + if (this == other) return true; + return other instanceof SpeakV2SpeechInterruptedMetadata && equalTo((SpeakV2SpeechInterruptedMetadata) other); + } + + @JsonAnyGetter + public Map getAdditionalProperties() { + return this.additionalProperties; + } + + private boolean equalTo(SpeakV2SpeechInterruptedMetadata other) { + return speechId.equals(other.speechId) + && audioDurationMs == other.audioDurationMs + && inputCharacterCount == other.inputCharacterCount + && billableCharacterCount == other.billableCharacterCount + && controlsApplied.equals(other.controlsApplied); + } + + @java.lang.Override + public int hashCode() { + return Objects.hash( + this.speechId, + this.audioDurationMs, + this.inputCharacterCount, + this.billableCharacterCount, + this.controlsApplied); + } + + @java.lang.Override + public String toString() { + return ObjectMappers.stringify(this); + } + + public static SpeechIdStage builder() { + return new Builder(); + } + + public interface SpeechIdStage { + /** + *

    Server-assigned turn identifier

    + */ + AudioDurationMsStage speechId(@NotNull String speechId); + + Builder from(SpeakV2SpeechInterruptedMetadata other); + } + + public interface AudioDurationMsStage { + /** + *

    Audio duration produced for this turn, in milliseconds

    + */ + InputCharacterCountStage audioDurationMs(int audioDurationMs); + } + + public interface InputCharacterCountStage { + /** + *

    Raw input character count for this turn, before text normalization

    + */ + BillableCharacterCountStage inputCharacterCount(int inputCharacterCount); + } + + public interface BillableCharacterCountStage { + /** + *

    Billable character count for this turn — the input character count with stripped control characters removed. Always less than or equal to input_character_count.

    + */ + ControlsAppliedStage billableCharacterCount(int billableCharacterCount); + } + + public interface ControlsAppliedStage { + /** + *

    Counts of the inline controls the server acted on during the turn. A control that was rejected or ignored is reported through a Warning and does not count here.

    + */ + _FinalStage controlsApplied(@NotNull SpeakV2SpeechInterruptedMetadataControlsApplied controlsApplied); + } + + public interface _FinalStage { + SpeakV2SpeechInterruptedMetadata build(); + + _FinalStage additionalProperty(String key, Object value); + + _FinalStage additionalProperties(Map additionalProperties); + } + + @JsonIgnoreProperties(ignoreUnknown = true) + public static final class Builder + implements SpeechIdStage, + AudioDurationMsStage, + InputCharacterCountStage, + BillableCharacterCountStage, + ControlsAppliedStage, + _FinalStage { + private String speechId; + + private int audioDurationMs; + + private int inputCharacterCount; + + private int billableCharacterCount; + + private SpeakV2SpeechInterruptedMetadataControlsApplied controlsApplied; + + @JsonAnySetter + private Map additionalProperties = new HashMap<>(); + + private Builder() {} + + @java.lang.Override + public Builder from(SpeakV2SpeechInterruptedMetadata other) { + speechId(other.getSpeechId()); + audioDurationMs(other.getAudioDurationMs()); + inputCharacterCount(other.getInputCharacterCount()); + billableCharacterCount(other.getBillableCharacterCount()); + controlsApplied(other.getControlsApplied()); + return this; + } + + /** + *

    Server-assigned turn identifier

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

    Audio duration produced for this turn, in milliseconds

    + * @return Reference to {@code this} so that method calls can be chained together. + */ + @java.lang.Override + @JsonSetter("audio_duration_ms") + public InputCharacterCountStage audioDurationMs(int audioDurationMs) { + this.audioDurationMs = audioDurationMs; + return this; + } + + /** + *

    Raw input character count for this turn, before text normalization

    + * @return Reference to {@code this} so that method calls can be chained together. + */ + @java.lang.Override + @JsonSetter("input_character_count") + public BillableCharacterCountStage inputCharacterCount(int inputCharacterCount) { + this.inputCharacterCount = inputCharacterCount; + return this; + } + + /** + *

    Billable character count for this turn — the input character count with stripped control characters removed. Always less than or equal to input_character_count.

    + * @return Reference to {@code this} so that method calls can be chained together. + */ + @java.lang.Override + @JsonSetter("billable_character_count") + public ControlsAppliedStage billableCharacterCount(int billableCharacterCount) { + this.billableCharacterCount = billableCharacterCount; + return this; + } + + /** + *

    Counts of the inline controls the server acted on during the turn. A control that was rejected or ignored is reported through a Warning and does not count here.

    + * @return Reference to {@code this} so that method calls can be chained together. + */ + @java.lang.Override + @JsonSetter("controls_applied") + public _FinalStage controlsApplied(@NotNull SpeakV2SpeechInterruptedMetadataControlsApplied controlsApplied) { + this.controlsApplied = Objects.requireNonNull(controlsApplied, "controlsApplied must not be null"); + return this; + } + + @java.lang.Override + public SpeakV2SpeechInterruptedMetadata build() { + return new SpeakV2SpeechInterruptedMetadata( + speechId, + audioDurationMs, + inputCharacterCount, + billableCharacterCount, + controlsApplied, + 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/speak/v2/types/SpeakV2SpeechInterruptedMetadataControlsApplied.java b/src/main/java/com/deepgram/resources/speak/v2/types/SpeakV2SpeechInterruptedMetadataControlsApplied.java new file mode 100644 index 0000000..850b47b --- /dev/null +++ b/src/main/java/com/deepgram/resources/speak/v2/types/SpeakV2SpeechInterruptedMetadataControlsApplied.java @@ -0,0 +1,200 @@ +/** + * 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.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 = SpeakV2SpeechInterruptedMetadataControlsApplied.Builder.class) +public final class SpeakV2SpeechInterruptedMetadataControlsApplied { + private final int pronunciationsApplied; + + private final int breaksApplied; + + private final int pronunciationWarnings; + + private final Map additionalProperties; + + private SpeakV2SpeechInterruptedMetadataControlsApplied( + int pronunciationsApplied, + int breaksApplied, + int pronunciationWarnings, + Map additionalProperties) { + this.pronunciationsApplied = pronunciationsApplied; + this.breaksApplied = breaksApplied; + this.pronunciationWarnings = pronunciationWarnings; + this.additionalProperties = additionalProperties; + } + + /** + * @return Pronunciation overrides successfully applied. Mirrors the Aura-2 dg-pronunciations-applied REST header. + */ + @JsonProperty("pronunciations_applied") + public int getPronunciationsApplied() { + return pronunciationsApplied; + } + + /** + * @return Pause (break) controls successfully applied. Mirrors the Aura-2 dg-breaks-applied REST header. A pause whose duration was out of range or off the supported increment is stripped rather than applied, so it is excluded from this count. + */ + @JsonProperty("breaks_applied") + public int getBreaksApplied() { + return breaksApplied; + } + + /** + * @return Pronunciation entries that triggered a warning (invalid IPA, word too long). Mirrors the Aura-2 dg-pronunciation-warnings REST header. + */ + @JsonProperty("pronunciation_warnings") + public int getPronunciationWarnings() { + return pronunciationWarnings; + } + + @java.lang.Override + public boolean equals(Object other) { + if (this == other) return true; + return other instanceof SpeakV2SpeechInterruptedMetadataControlsApplied + && equalTo((SpeakV2SpeechInterruptedMetadataControlsApplied) other); + } + + @JsonAnyGetter + public Map getAdditionalProperties() { + return this.additionalProperties; + } + + private boolean equalTo(SpeakV2SpeechInterruptedMetadataControlsApplied other) { + return pronunciationsApplied == other.pronunciationsApplied + && breaksApplied == other.breaksApplied + && pronunciationWarnings == other.pronunciationWarnings; + } + + @java.lang.Override + public int hashCode() { + return Objects.hash(this.pronunciationsApplied, this.breaksApplied, this.pronunciationWarnings); + } + + @java.lang.Override + public String toString() { + return ObjectMappers.stringify(this); + } + + public static PronunciationsAppliedStage builder() { + return new Builder(); + } + + public interface PronunciationsAppliedStage { + /** + *

    Pronunciation overrides successfully applied. Mirrors the Aura-2 dg-pronunciations-applied REST header.

    + */ + BreaksAppliedStage pronunciationsApplied(int pronunciationsApplied); + + Builder from(SpeakV2SpeechInterruptedMetadataControlsApplied other); + } + + public interface BreaksAppliedStage { + /** + *

    Pause (break) controls successfully applied. Mirrors the Aura-2 dg-breaks-applied REST header. A pause whose duration was out of range or off the supported increment is stripped rather than applied, so it is excluded from this count.

    + */ + PronunciationWarningsStage breaksApplied(int breaksApplied); + } + + public interface PronunciationWarningsStage { + /** + *

    Pronunciation entries that triggered a warning (invalid IPA, word too long). Mirrors the Aura-2 dg-pronunciation-warnings REST header.

    + */ + _FinalStage pronunciationWarnings(int pronunciationWarnings); + } + + public interface _FinalStage { + SpeakV2SpeechInterruptedMetadataControlsApplied build(); + + _FinalStage additionalProperty(String key, Object value); + + _FinalStage additionalProperties(Map additionalProperties); + } + + @JsonIgnoreProperties(ignoreUnknown = true) + public static final class Builder + implements PronunciationsAppliedStage, BreaksAppliedStage, PronunciationWarningsStage, _FinalStage { + private int pronunciationsApplied; + + private int breaksApplied; + + private int pronunciationWarnings; + + @JsonAnySetter + private Map additionalProperties = new HashMap<>(); + + private Builder() {} + + @java.lang.Override + public Builder from(SpeakV2SpeechInterruptedMetadataControlsApplied other) { + pronunciationsApplied(other.getPronunciationsApplied()); + breaksApplied(other.getBreaksApplied()); + pronunciationWarnings(other.getPronunciationWarnings()); + return this; + } + + /** + *

    Pronunciation overrides successfully applied. Mirrors the Aura-2 dg-pronunciations-applied REST header.

    + * @return Reference to {@code this} so that method calls can be chained together. + */ + @java.lang.Override + @JsonSetter("pronunciations_applied") + public BreaksAppliedStage pronunciationsApplied(int pronunciationsApplied) { + this.pronunciationsApplied = pronunciationsApplied; + return this; + } + + /** + *

    Pause (break) controls successfully applied. Mirrors the Aura-2 dg-breaks-applied REST header. A pause whose duration was out of range or off the supported increment is stripped rather than applied, so it is excluded from this count.

    + * @return Reference to {@code this} so that method calls can be chained together. + */ + @java.lang.Override + @JsonSetter("breaks_applied") + public PronunciationWarningsStage breaksApplied(int breaksApplied) { + this.breaksApplied = breaksApplied; + return this; + } + + /** + *

    Pronunciation entries that triggered a warning (invalid IPA, word too long). Mirrors the Aura-2 dg-pronunciation-warnings REST header.

    + * @return Reference to {@code this} so that method calls can be chained together. + */ + @java.lang.Override + @JsonSetter("pronunciation_warnings") + public _FinalStage pronunciationWarnings(int pronunciationWarnings) { + this.pronunciationWarnings = pronunciationWarnings; + return this; + } + + @java.lang.Override + public SpeakV2SpeechInterruptedMetadataControlsApplied build() { + return new SpeakV2SpeechInterruptedMetadataControlsApplied( + pronunciationsApplied, breaksApplied, pronunciationWarnings, 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/speak/v2/types/SpeakV2SpeechMetadata.java b/src/main/java/com/deepgram/resources/speak/v2/types/SpeakV2SpeechMetadata.java index e1c0ece..a3e3393 100644 --- a/src/main/java/com/deepgram/resources/speak/v2/types/SpeakV2SpeechMetadata.java +++ b/src/main/java/com/deepgram/resources/speak/v2/types/SpeakV2SpeechMetadata.java @@ -87,7 +87,7 @@ public int getBillableCharacterCount() { } /** - * @return Controls applied during the turn. Inline pronunciation and pause controls are not available during Early Access, so every count is currently 0. + * @return Counts of the inline controls the server acted on during the turn. A control that was rejected or ignored is reported through a Warning and does not count here. */ @JsonProperty("controls_applied") public SpeakV2SpeechMetadataControlsApplied getControlsApplied() { @@ -164,7 +164,7 @@ public interface BillableCharacterCountStage { public interface ControlsAppliedStage { /** - *

    Controls applied during the turn. Inline pronunciation and pause controls are not available during Early Access, so every count is currently 0.

    + *

    Counts of the inline controls the server acted on during the turn. A control that was rejected or ignored is reported through a Warning and does not count here.

    */ _FinalStage controlsApplied(@NotNull SpeakV2SpeechMetadataControlsApplied controlsApplied); } @@ -211,7 +211,6 @@ public Builder from(SpeakV2SpeechMetadata other) { } /** - *

    Server-assigned turn identifier

    *

    Server-assigned turn identifier

    * @return Reference to {@code this} so that method calls can be chained together. */ @@ -223,7 +222,6 @@ public AudioDurationMsStage speechId(@NotNull String speechId) { } /** - *

    Total audio duration produced for this turn, in milliseconds

    *

    Total audio duration produced for this turn, in milliseconds

    * @return Reference to {@code this} so that method calls can be chained together. */ @@ -235,7 +233,6 @@ public InputCharacterCountStage audioDurationMs(int audioDurationMs) { } /** - *

    Raw input character count for this turn, before text normalization

    *

    Raw input character count for this turn, before text normalization

    * @return Reference to {@code this} so that method calls can be chained together. */ @@ -247,7 +244,6 @@ public BillableCharacterCountStage inputCharacterCount(int inputCharacterCount) } /** - *

    Billable character count for this turn — the input character count with stripped control characters removed. Always less than or equal to input_character_count.

    *

    Billable character count for this turn — the input character count with stripped control characters removed. Always less than or equal to input_character_count.

    * @return Reference to {@code this} so that method calls can be chained together. */ @@ -259,8 +255,7 @@ public ControlsAppliedStage billableCharacterCount(int billableCharacterCount) { } /** - *

    Controls applied during the turn. Inline pronunciation and pause controls are not available during Early Access, so every count is currently 0.

    - *

    Controls applied during the turn. Inline pronunciation and pause controls are not available during Early Access, so every count is currently 0.

    + *

    Counts of the inline controls the server acted on during the turn. A control that was rejected or ignored is reported through a Warning and does not count here.

    * @return Reference to {@code this} so that method calls can be chained together. */ @java.lang.Override diff --git a/src/main/java/com/deepgram/resources/speak/v2/types/SpeakV2SpeechMetadataControlsApplied.java b/src/main/java/com/deepgram/resources/speak/v2/types/SpeakV2SpeechMetadataControlsApplied.java index 8225d04..fd50e5d 100644 --- a/src/main/java/com/deepgram/resources/speak/v2/types/SpeakV2SpeechMetadataControlsApplied.java +++ b/src/main/java/com/deepgram/resources/speak/v2/types/SpeakV2SpeechMetadataControlsApplied.java @@ -20,19 +20,25 @@ public final class SpeakV2SpeechMetadataControlsApplied { private final int pronunciationsApplied; + private final int breaksApplied; + private final int pronunciationWarnings; private final Map additionalProperties; private SpeakV2SpeechMetadataControlsApplied( - int pronunciationsApplied, int pronunciationWarnings, Map additionalProperties) { + int pronunciationsApplied, + int breaksApplied, + int pronunciationWarnings, + Map additionalProperties) { this.pronunciationsApplied = pronunciationsApplied; + this.breaksApplied = breaksApplied; this.pronunciationWarnings = pronunciationWarnings; this.additionalProperties = additionalProperties; } /** - * @return Pronunciation overrides successfully applied. Mirrors the Aura-2 dg-pronunciations-applied REST header. Always 0 during Early Access. + * @return Pronunciation overrides successfully applied. Mirrors the Aura-2 dg-pronunciations-applied REST header. */ @JsonProperty("pronunciations_applied") public int getPronunciationsApplied() { @@ -40,7 +46,15 @@ public int getPronunciationsApplied() { } /** - * @return Pronunciation entries that triggered a warning (invalid IPA, word too long). Mirrors the Aura-2 dg-pronunciation-warnings REST header. Always 0 during Early Access. + * @return Pause (break) controls successfully applied. Mirrors the Aura-2 dg-breaks-applied REST header. A pause whose duration was out of range or off the supported increment is stripped rather than applied, so it is excluded from this count. + */ + @JsonProperty("breaks_applied") + public int getBreaksApplied() { + return breaksApplied; + } + + /** + * @return Pronunciation entries that triggered a warning (invalid IPA, word too long). Mirrors the Aura-2 dg-pronunciation-warnings REST header. */ @JsonProperty("pronunciation_warnings") public int getPronunciationWarnings() { @@ -61,12 +75,13 @@ public Map getAdditionalProperties() { private boolean equalTo(SpeakV2SpeechMetadataControlsApplied other) { return pronunciationsApplied == other.pronunciationsApplied + && breaksApplied == other.breaksApplied && pronunciationWarnings == other.pronunciationWarnings; } @java.lang.Override public int hashCode() { - return Objects.hash(this.pronunciationsApplied, this.pronunciationWarnings); + return Objects.hash(this.pronunciationsApplied, this.breaksApplied, this.pronunciationWarnings); } @java.lang.Override @@ -80,16 +95,23 @@ public static PronunciationsAppliedStage builder() { public interface PronunciationsAppliedStage { /** - *

    Pronunciation overrides successfully applied. Mirrors the Aura-2 dg-pronunciations-applied REST header. Always 0 during Early Access.

    + *

    Pronunciation overrides successfully applied. Mirrors the Aura-2 dg-pronunciations-applied REST header.

    */ - PronunciationWarningsStage pronunciationsApplied(int pronunciationsApplied); + BreaksAppliedStage pronunciationsApplied(int pronunciationsApplied); Builder from(SpeakV2SpeechMetadataControlsApplied other); } + public interface BreaksAppliedStage { + /** + *

    Pause (break) controls successfully applied. Mirrors the Aura-2 dg-breaks-applied REST header. A pause whose duration was out of range or off the supported increment is stripped rather than applied, so it is excluded from this count.

    + */ + PronunciationWarningsStage breaksApplied(int breaksApplied); + } + public interface PronunciationWarningsStage { /** - *

    Pronunciation entries that triggered a warning (invalid IPA, word too long). Mirrors the Aura-2 dg-pronunciation-warnings REST header. Always 0 during Early Access.

    + *

    Pronunciation entries that triggered a warning (invalid IPA, word too long). Mirrors the Aura-2 dg-pronunciation-warnings REST header.

    */ _FinalStage pronunciationWarnings(int pronunciationWarnings); } @@ -103,9 +125,12 @@ public interface _FinalStage { } @JsonIgnoreProperties(ignoreUnknown = true) - public static final class Builder implements PronunciationsAppliedStage, PronunciationWarningsStage, _FinalStage { + public static final class Builder + implements PronunciationsAppliedStage, BreaksAppliedStage, PronunciationWarningsStage, _FinalStage { private int pronunciationsApplied; + private int breaksApplied; + private int pronunciationWarnings; @JsonAnySetter @@ -116,25 +141,35 @@ private Builder() {} @java.lang.Override public Builder from(SpeakV2SpeechMetadataControlsApplied other) { pronunciationsApplied(other.getPronunciationsApplied()); + breaksApplied(other.getBreaksApplied()); pronunciationWarnings(other.getPronunciationWarnings()); return this; } /** - *

    Pronunciation overrides successfully applied. Mirrors the Aura-2 dg-pronunciations-applied REST header. Always 0 during Early Access.

    - *

    Pronunciation overrides successfully applied. Mirrors the Aura-2 dg-pronunciations-applied REST header. Always 0 during Early Access.

    + *

    Pronunciation overrides successfully applied. Mirrors the Aura-2 dg-pronunciations-applied REST header.

    * @return Reference to {@code this} so that method calls can be chained together. */ @java.lang.Override @JsonSetter("pronunciations_applied") - public PronunciationWarningsStage pronunciationsApplied(int pronunciationsApplied) { + public BreaksAppliedStage pronunciationsApplied(int pronunciationsApplied) { this.pronunciationsApplied = pronunciationsApplied; return this; } /** - *

    Pronunciation entries that triggered a warning (invalid IPA, word too long). Mirrors the Aura-2 dg-pronunciation-warnings REST header. Always 0 during Early Access.

    - *

    Pronunciation entries that triggered a warning (invalid IPA, word too long). Mirrors the Aura-2 dg-pronunciation-warnings REST header. Always 0 during Early Access.

    + *

    Pause (break) controls successfully applied. Mirrors the Aura-2 dg-breaks-applied REST header. A pause whose duration was out of range or off the supported increment is stripped rather than applied, so it is excluded from this count.

    + * @return Reference to {@code this} so that method calls can be chained together. + */ + @java.lang.Override + @JsonSetter("breaks_applied") + public PronunciationWarningsStage breaksApplied(int breaksApplied) { + this.breaksApplied = breaksApplied; + return this; + } + + /** + *

    Pronunciation entries that triggered a warning (invalid IPA, word too long). Mirrors the Aura-2 dg-pronunciation-warnings REST header.

    * @return Reference to {@code this} so that method calls can be chained together. */ @java.lang.Override @@ -147,7 +182,7 @@ public _FinalStage pronunciationWarnings(int pronunciationWarnings) { @java.lang.Override public SpeakV2SpeechMetadataControlsApplied build() { return new SpeakV2SpeechMetadataControlsApplied( - pronunciationsApplied, pronunciationWarnings, additionalProperties); + pronunciationsApplied, breaksApplied, pronunciationWarnings, additionalProperties); } @java.lang.Override diff --git a/src/main/java/com/deepgram/resources/speak/v2/types/SpeakV2SpeechStarted.java b/src/main/java/com/deepgram/resources/speak/v2/types/SpeakV2SpeechStarted.java index b83be2f..033f5af 100644 --- a/src/main/java/com/deepgram/resources/speak/v2/types/SpeakV2SpeechStarted.java +++ b/src/main/java/com/deepgram/resources/speak/v2/types/SpeakV2SpeechStarted.java @@ -106,7 +106,6 @@ public Builder from(SpeakV2SpeechStarted other) { } /** - *

    Server-minted identifier for this turn, of the form dg_sp_<12 hex digits>. Informational.

    *

    Server-minted identifier for this turn, of the form dg_sp_<12 hex digits>. Informational.

    * @return Reference to {@code this} so that method calls can be chained together. */ diff --git a/src/main/java/com/deepgram/resources/speak/v2/types/SpeakV2Warning.java b/src/main/java/com/deepgram/resources/speak/v2/types/SpeakV2Warning.java index 1542479..4597a5c 100644 --- a/src/main/java/com/deepgram/resources/speak/v2/types/SpeakV2Warning.java +++ b/src/main/java/com/deepgram/resources/speak/v2/types/SpeakV2Warning.java @@ -40,7 +40,10 @@ public String getType() { } /** - * @return Warning code identifying the condition, in SCREAMING_SNAKE_CASE. Early Access codes are NO_ACTIVE_SPEECH (a speech-scoped message arrived with no active turn) and SYNTHESIS_RETRYING (a synthesis request failed and is being retried). + * @return Warning code identifying the condition, in SCREAMING_SNAKE_CASE. + *

    Turn-scoped codes: NO_ACTIVE_SPEECH (a speech-scoped message arrived with no active turn), NO_SYNTHESIZABLE_TEXT (the turn's text was entirely whitespace or punctuation, so it produced no audio and is completed with a zero-duration SpeechMetadata).SYNTHESIS_RETRYING (a synthesis request failed and is being retried).

    + *

    Inline-control codes. The offending control is dropped and the rest of the turn is synthesized: PRONUNCIATION_WARNINGS (one or more pronunciation overrides contained invalid IPA; the server falls back to a best-effort pronunciation and the description lists each offending sequence), PRONUNCIATION_TOO_LONG (an IPA string exceeded the length limit), PRONUNCIATIONS_LIMIT_EXCEEDED (too many pronunciation controls in one turn), BREAKS_LIMIT_EXCEEDED (too many pause controls, or two pauses with no intervening text), BREAK_TOKENS_OUT_OF_RANGE (pause durations outside the range the model supports), BREAK_TOKENS_WITH_INVALID_INCREMENTS (pause durations off the model's supported increment).

    + *

    Interrupt-scoped codes, each meaning the Interrupt was ignored: NO_AUDIO_GENERATED (the session has produced no audio yet, so there is nothing to interrupt), INTERRUPT_IN_PROGRESS (an earlier Interrupt is still being processed — at most one is handled at a time), INVALID_INTERRUPT_OFFSET (the playback_offset did not advance past the position a prior interrupt established).

    */ @JsonProperty("code") public String getCode() { @@ -86,7 +89,10 @@ public static CodeStage builder() { public interface CodeStage { /** - *

    Warning code identifying the condition, in SCREAMING_SNAKE_CASE. Early Access codes are NO_ACTIVE_SPEECH (a speech-scoped message arrived with no active turn) and SYNTHESIS_RETRYING (a synthesis request failed and is being retried).

    + *

    Warning code identifying the condition, in SCREAMING_SNAKE_CASE.

    + *

    Turn-scoped codes: NO_ACTIVE_SPEECH (a speech-scoped message arrived with no active turn), NO_SYNTHESIZABLE_TEXT (the turn's text was entirely whitespace or punctuation, so it produced no audio and is completed with a zero-duration SpeechMetadata).SYNTHESIS_RETRYING (a synthesis request failed and is being retried).

    + *

    Inline-control codes. The offending control is dropped and the rest of the turn is synthesized: PRONUNCIATION_WARNINGS (one or more pronunciation overrides contained invalid IPA; the server falls back to a best-effort pronunciation and the description lists each offending sequence), PRONUNCIATION_TOO_LONG (an IPA string exceeded the length limit), PRONUNCIATIONS_LIMIT_EXCEEDED (too many pronunciation controls in one turn), BREAKS_LIMIT_EXCEEDED (too many pause controls, or two pauses with no intervening text), BREAK_TOKENS_OUT_OF_RANGE (pause durations outside the range the model supports), BREAK_TOKENS_WITH_INVALID_INCREMENTS (pause durations off the model's supported increment).

    + *

    Interrupt-scoped codes, each meaning the Interrupt was ignored: NO_AUDIO_GENERATED (the session has produced no audio yet, so there is nothing to interrupt), INTERRUPT_IN_PROGRESS (an earlier Interrupt is still being processed — at most one is handled at a time), INVALID_INTERRUPT_OFFSET (the playback_offset did not advance past the position a prior interrupt established).

    */ DescriptionStage code(@NotNull String code); @@ -127,8 +133,10 @@ public Builder from(SpeakV2Warning other) { } /** - *

    Warning code identifying the condition, in SCREAMING_SNAKE_CASE. Early Access codes are NO_ACTIVE_SPEECH (a speech-scoped message arrived with no active turn) and SYNTHESIS_RETRYING (a synthesis request failed and is being retried).

    - *

    Warning code identifying the condition, in SCREAMING_SNAKE_CASE. Early Access codes are NO_ACTIVE_SPEECH (a speech-scoped message arrived with no active turn) and SYNTHESIS_RETRYING (a synthesis request failed and is being retried).

    + *

    Warning code identifying the condition, in SCREAMING_SNAKE_CASE.

    + *

    Turn-scoped codes: NO_ACTIVE_SPEECH (a speech-scoped message arrived with no active turn), NO_SYNTHESIZABLE_TEXT (the turn's text was entirely whitespace or punctuation, so it produced no audio and is completed with a zero-duration SpeechMetadata).SYNTHESIS_RETRYING (a synthesis request failed and is being retried).

    + *

    Inline-control codes. The offending control is dropped and the rest of the turn is synthesized: PRONUNCIATION_WARNINGS (one or more pronunciation overrides contained invalid IPA; the server falls back to a best-effort pronunciation and the description lists each offending sequence), PRONUNCIATION_TOO_LONG (an IPA string exceeded the length limit), PRONUNCIATIONS_LIMIT_EXCEEDED (too many pronunciation controls in one turn), BREAKS_LIMIT_EXCEEDED (too many pause controls, or two pauses with no intervening text), BREAK_TOKENS_OUT_OF_RANGE (pause durations outside the range the model supports), BREAK_TOKENS_WITH_INVALID_INCREMENTS (pause durations off the model's supported increment).

    + *

    Interrupt-scoped codes, each meaning the Interrupt was ignored: NO_AUDIO_GENERATED (the session has produced no audio yet, so there is nothing to interrupt), INTERRUPT_IN_PROGRESS (an earlier Interrupt is still being processed — at most one is handled at a time), INVALID_INTERRUPT_OFFSET (the playback_offset did not advance past the position a prior interrupt established).

    * @return Reference to {@code this} so that method calls can be chained together. */ @java.lang.Override @@ -139,7 +147,6 @@ public DescriptionStage code(@NotNull String code) { } /** - *

    A human-readable description of the warning

    *

    A human-readable description of the warning

    * @return Reference to {@code this} so that method calls can be chained together. */ diff --git a/src/main/java/com/deepgram/resources/speak/v2/websocket/V2ConnectOptions.java b/src/main/java/com/deepgram/resources/speak/v2/websocket/V2ConnectOptions.java index d99498f..1173c8a 100644 --- a/src/main/java/com/deepgram/resources/speak/v2/websocket/V2ConnectOptions.java +++ b/src/main/java/com/deepgram/resources/speak/v2/websocket/V2ConnectOptions.java @@ -31,6 +31,10 @@ public final class V2ConnectOptions { private final Optional sampleRate; + private final Optional speed; + + private final Optional expressivity; + private final Optional mipOptOut; private final Optional tag; @@ -41,12 +45,16 @@ private V2ConnectOptions( String model, Optional encoding, Optional sampleRate, + Optional speed, + Optional expressivity, Optional mipOptOut, Optional tag, Map additionalProperties) { this.model = model; this.encoding = encoding; this.sampleRate = sampleRate; + this.speed = speed; + this.expressivity = expressivity; this.mipOptOut = mipOptOut; this.tag = tag; this.additionalProperties = additionalProperties; @@ -67,6 +75,16 @@ public Optional getSampleRate() { return sampleRate; } + @JsonProperty("speed") + public Optional getSpeed() { + return speed; + } + + @JsonProperty("expressivity") + public Optional getExpressivity() { + return expressivity; + } + @JsonProperty("mip_opt_out") public Optional getMipOptOut() { return mipOptOut; @@ -92,13 +110,16 @@ private boolean equalTo(V2ConnectOptions other) { return model.equals(other.model) && encoding.equals(other.encoding) && sampleRate.equals(other.sampleRate) + && speed.equals(other.speed) + && expressivity.equals(other.expressivity) && mipOptOut.equals(other.mipOptOut) && tag.equals(other.tag); } @java.lang.Override public int hashCode() { - return Objects.hash(this.model, this.encoding, this.sampleRate, this.mipOptOut, this.tag); + return Objects.hash( + this.model, this.encoding, this.sampleRate, this.speed, this.expressivity, this.mipOptOut, this.tag); } @java.lang.Override @@ -131,6 +152,14 @@ public interface _FinalStage { _FinalStage sampleRate(SpeakV2SampleRate sampleRate); + _FinalStage speed(Optional speed); + + _FinalStage speed(Double speed); + + _FinalStage expressivity(Optional expressivity); + + _FinalStage expressivity(Integer expressivity); + _FinalStage mipOptOut(Optional mipOptOut); _FinalStage mipOptOut(SpeakV2MipOptOut mipOptOut); @@ -148,6 +177,10 @@ public static final class Builder implements ModelStage, _FinalStage { private Optional mipOptOut = Optional.empty(); + private Optional expressivity = Optional.empty(); + + private Optional speed = Optional.empty(); + private Optional sampleRate = Optional.empty(); private Optional encoding = Optional.empty(); @@ -162,6 +195,8 @@ public Builder from(V2ConnectOptions other) { model(other.getModel()); encoding(other.getEncoding()); sampleRate(other.getSampleRate()); + speed(other.getSpeed()); + expressivity(other.getExpressivity()); mipOptOut(other.getMipOptOut()); tag(other.getTag()); return this; @@ -200,6 +235,32 @@ public _FinalStage mipOptOut(Optional mipOptOut) { return this; } + @java.lang.Override + public _FinalStage expressivity(Integer expressivity) { + this.expressivity = Optional.ofNullable(expressivity); + return this; + } + + @java.lang.Override + @JsonSetter(value = "expressivity", nulls = Nulls.SKIP) + public _FinalStage expressivity(Optional expressivity) { + this.expressivity = expressivity; + return this; + } + + @java.lang.Override + public _FinalStage speed(Double speed) { + this.speed = Optional.ofNullable(speed); + return this; + } + + @java.lang.Override + @JsonSetter(value = "speed", nulls = Nulls.SKIP) + public _FinalStage speed(Optional speed) { + this.speed = speed; + return this; + } + @java.lang.Override public _FinalStage sampleRate(SpeakV2SampleRate sampleRate) { this.sampleRate = Optional.ofNullable(sampleRate); @@ -228,7 +289,8 @@ public _FinalStage encoding(Optional encoding) { @java.lang.Override public V2ConnectOptions build() { - return new V2ConnectOptions(model, encoding, sampleRate, mipOptOut, tag, additionalProperties); + return new V2ConnectOptions( + model, encoding, sampleRate, speed, expressivity, mipOptOut, tag, additionalProperties); } @java.lang.Override 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 44426c4..3e032b1 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,17 +6,21 @@ 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; @@ -69,10 +73,16 @@ public class V2WebSocketClient implements AutoCloseable { 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; @@ -120,28 +130,20 @@ public CompletableFuture connect(V2ConnectOptions options) { 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()) { - // Array-valued query params (String | List unions) must serialize as repeated - // params (tag=a&tag=b), not a stringified list. The generated streaming template uses - // String.valueOf(...), which mangles a List into "[a, b]"; route through - // QueryStringMapper (arraysAsRepeats=true) so the wire format matches the REST path. - QueryStringMapper.addQueryParameter( - urlBuilder, "tag", options.getTag().get().get(), true); - } - // Escape hatch: emit caller-supplied additionalProperties (e.g. no_delay) as query params. - // The generated template only serializes the typed options and drops these otherwise. - // ConnectOptions is request-only (never deserialized), so this map holds only what the - // caller set via the builder. Routed through QueryStringMapper to match the REST path. - 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); @@ -240,6 +242,24 @@ 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 @@ -281,6 +301,14 @@ 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 @@ -297,6 +325,22 @@ 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 @@ -453,6 +497,36 @@ private void handleIncomingMessage(String json) { 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())) { @@ -510,11 +584,25 @@ private void handleIncomingMessage(String json) { return; } } - // Unrecognized message type: forward-compatible no-op. The raw frame was - // already delivered to onMessage(String) above, so a newer server adding a - // benign control frame (e.g. a GA addition to this endpoint) must not surface - // as a fatal error to deployed clients. Routing it to onError would make a - // harmless frame look fatal to a voice agent; mirrors the JS/Python SDKs. + 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; + } + } + 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/voiceagent/configurations/AsyncRawConfigurationsClient.java b/src/main/java/com/deepgram/resources/voiceagent/configurations/AsyncRawConfigurationsClient.java index 9f2dc29..3f0d53f 100644 --- a/src/main/java/com/deepgram/resources/voiceagent/configurations/AsyncRawConfigurationsClient.java +++ b/src/main/java/com/deepgram/resources/voiceagent/configurations/AsyncRawConfigurationsClient.java @@ -10,6 +10,7 @@ import com.deepgram.core.MediaTypes; import com.deepgram.core.ObjectMappers; import com.deepgram.core.RequestOptions; +import com.deepgram.core.RetryInterceptor; import com.deepgram.errors.BadRequestError; import com.deepgram.resources.voiceagent.configurations.requests.CreateAgentConfigurationV1Request; import com.deepgram.resources.voiceagent.configurations.requests.UpdateAgentMetadataV1Request; @@ -71,6 +72,15 @@ public CompletableFuture> future = new CompletableFuture<>(); client.newCall(okhttpRequest).enqueue(new Callback() { @@ -98,6 +108,9 @@ public void onResponse(@NotNull Call call, @NotNull Response response) throws IO future.completeExceptionally(new DeepgramHttpException( "Error with status code " + response.code(), response.code(), errorBody, response)); return; + } catch (JsonProcessingException e) { + future.completeExceptionally( + new DeepgramApiException("Failed to deserialize response: " + e.getMessage(), e)); } catch (IOException e) { future.completeExceptionally(new DeepgramApiException("Network error executing HTTP request", e)); } @@ -152,6 +165,15 @@ public CompletableFuture> future = new CompletableFuture<>(); client.newCall(okhttpRequest).enqueue(new Callback() { @@ -179,6 +201,9 @@ public void onResponse(@NotNull Call call, @NotNull Response response) throws IO future.completeExceptionally(new DeepgramHttpException( "Error with status code " + response.code(), response.code(), errorBody, response)); return; + } catch (JsonProcessingException e) { + future.completeExceptionally( + new DeepgramApiException("Failed to deserialize response: " + e.getMessage(), e)); } catch (IOException e) { future.completeExceptionally(new DeepgramApiException("Network error executing HTTP request", e)); } @@ -225,6 +250,15 @@ public CompletableFuture> get( if (requestOptions != null && requestOptions.getTimeout().isPresent()) { client = clientOptions.httpClientWithTimeout(requestOptions); } + if (requestOptions != null && requestOptions.getMaxRetries().isPresent()) { + okhttpRequest = okhttpRequest + .newBuilder() + .tag( + RetryInterceptor.MaxRetriesOverride.class, + new RetryInterceptor.MaxRetriesOverride( + requestOptions.getMaxRetries().get())) + .build(); + } CompletableFuture> future = new CompletableFuture<>(); client.newCall(okhttpRequest).enqueue(new Callback() { @Override @@ -250,6 +284,9 @@ public void onResponse(@NotNull Call call, @NotNull Response response) throws IO future.completeExceptionally(new DeepgramHttpException( "Error with status code " + response.code(), response.code(), errorBody, response)); return; + } catch (JsonProcessingException e) { + future.completeExceptionally( + new DeepgramApiException("Failed to deserialize response: " + e.getMessage(), e)); } catch (IOException e) { future.completeExceptionally(new DeepgramApiException("Network error executing HTTP request", e)); } @@ -305,6 +342,15 @@ public CompletableFuture> update( if (requestOptions != null && requestOptions.getTimeout().isPresent()) { client = clientOptions.httpClientWithTimeout(requestOptions); } + if (requestOptions != null && requestOptions.getMaxRetries().isPresent()) { + okhttpRequest = okhttpRequest + .newBuilder() + .tag( + RetryInterceptor.MaxRetriesOverride.class, + new RetryInterceptor.MaxRetriesOverride( + requestOptions.getMaxRetries().get())) + .build(); + } CompletableFuture> future = new CompletableFuture<>(); client.newCall(okhttpRequest).enqueue(new Callback() { @Override @@ -330,6 +376,9 @@ public void onResponse(@NotNull Call call, @NotNull Response response) throws IO future.completeExceptionally(new DeepgramHttpException( "Error with status code " + response.code(), response.code(), errorBody, response)); return; + } catch (JsonProcessingException e) { + future.completeExceptionally( + new DeepgramApiException("Failed to deserialize response: " + e.getMessage(), e)); } catch (IOException e) { future.completeExceptionally(new DeepgramApiException("Network error executing HTTP request", e)); } @@ -376,6 +425,15 @@ public CompletableFuture>> delete( if (requestOptions != null && requestOptions.getTimeout().isPresent()) { client = clientOptions.httpClientWithTimeout(requestOptions); } + if (requestOptions != null && requestOptions.getMaxRetries().isPresent()) { + okhttpRequest = okhttpRequest + .newBuilder() + .tag( + RetryInterceptor.MaxRetriesOverride.class, + new RetryInterceptor.MaxRetriesOverride( + requestOptions.getMaxRetries().get())) + .build(); + } CompletableFuture>> future = new CompletableFuture<>(); client.newCall(okhttpRequest).enqueue(new Callback() { @Override @@ -402,6 +460,9 @@ public void onResponse(@NotNull Call call, @NotNull Response response) throws IO future.completeExceptionally(new DeepgramHttpException( "Error with status code " + response.code(), response.code(), errorBody, response)); return; + } catch (JsonProcessingException e) { + future.completeExceptionally( + new DeepgramApiException("Failed to deserialize response: " + e.getMessage(), e)); } catch (IOException e) { future.completeExceptionally(new DeepgramApiException("Network error executing HTTP request", e)); } diff --git a/src/main/java/com/deepgram/resources/voiceagent/configurations/RawConfigurationsClient.java b/src/main/java/com/deepgram/resources/voiceagent/configurations/RawConfigurationsClient.java index d15fe0f..2c78edb 100644 --- a/src/main/java/com/deepgram/resources/voiceagent/configurations/RawConfigurationsClient.java +++ b/src/main/java/com/deepgram/resources/voiceagent/configurations/RawConfigurationsClient.java @@ -10,6 +10,7 @@ import com.deepgram.core.MediaTypes; import com.deepgram.core.ObjectMappers; import com.deepgram.core.RequestOptions; +import com.deepgram.core.RetryInterceptor; import com.deepgram.errors.BadRequestError; import com.deepgram.resources.voiceagent.configurations.requests.CreateAgentConfigurationV1Request; import com.deepgram.resources.voiceagent.configurations.requests.UpdateAgentMetadataV1Request; @@ -67,6 +68,15 @@ public DeepgramApiHttpResponse list( if (requestOptions != null && requestOptions.getTimeout().isPresent()) { client = clientOptions.httpClientWithTimeout(requestOptions); } + if (requestOptions != null && requestOptions.getMaxRetries().isPresent()) { + okhttpRequest = okhttpRequest + .newBuilder() + .tag( + RetryInterceptor.MaxRetriesOverride.class, + new RetryInterceptor.MaxRetriesOverride( + requestOptions.getMaxRetries().get())) + .build(); + } try (Response response = client.newCall(okhttpRequest).execute()) { ResponseBody responseBody = response.body(); String responseBodyString = responseBody != null ? responseBody.string() : "{}"; @@ -87,6 +97,8 @@ public DeepgramApiHttpResponse list( Object errorBody = ObjectMappers.parseErrorBody(responseBodyString); throw new DeepgramHttpException( "Error with status code " + response.code(), response.code(), errorBody, response); + } catch (JsonProcessingException e) { + throw new DeepgramApiException("Failed to deserialize response: " + e.getMessage(), e); } catch (IOException e) { throw new DeepgramApiException("Network error executing HTTP request", e); } @@ -133,6 +145,15 @@ public DeepgramApiHttpResponse create( if (requestOptions != null && requestOptions.getTimeout().isPresent()) { client = clientOptions.httpClientWithTimeout(requestOptions); } + if (requestOptions != null && requestOptions.getMaxRetries().isPresent()) { + okhttpRequest = okhttpRequest + .newBuilder() + .tag( + RetryInterceptor.MaxRetriesOverride.class, + new RetryInterceptor.MaxRetriesOverride( + requestOptions.getMaxRetries().get())) + .build(); + } try (Response response = client.newCall(okhttpRequest).execute()) { ResponseBody responseBody = response.body(); String responseBodyString = responseBody != null ? responseBody.string() : "{}"; @@ -153,6 +174,8 @@ public DeepgramApiHttpResponse create( Object errorBody = ObjectMappers.parseErrorBody(responseBodyString); throw new DeepgramHttpException( "Error with status code " + response.code(), response.code(), errorBody, response); + } catch (JsonProcessingException e) { + throw new DeepgramApiException("Failed to deserialize response: " + e.getMessage(), e); } catch (IOException e) { throw new DeepgramApiException("Network error executing HTTP request", e); } @@ -191,6 +214,15 @@ public DeepgramApiHttpResponse get( if (requestOptions != null && requestOptions.getTimeout().isPresent()) { client = clientOptions.httpClientWithTimeout(requestOptions); } + if (requestOptions != null && requestOptions.getMaxRetries().isPresent()) { + okhttpRequest = okhttpRequest + .newBuilder() + .tag( + RetryInterceptor.MaxRetriesOverride.class, + new RetryInterceptor.MaxRetriesOverride( + requestOptions.getMaxRetries().get())) + .build(); + } try (Response response = client.newCall(okhttpRequest).execute()) { ResponseBody responseBody = response.body(); String responseBodyString = responseBody != null ? responseBody.string() : "{}"; @@ -209,6 +241,8 @@ public DeepgramApiHttpResponse get( Object errorBody = ObjectMappers.parseErrorBody(responseBodyString); throw new DeepgramHttpException( "Error with status code " + response.code(), response.code(), errorBody, response); + } catch (JsonProcessingException e) { + throw new DeepgramApiException("Failed to deserialize response: " + e.getMessage(), e); } catch (IOException e) { throw new DeepgramApiException("Network error executing HTTP request", e); } @@ -256,6 +290,15 @@ public DeepgramApiHttpResponse update( if (requestOptions != null && requestOptions.getTimeout().isPresent()) { client = clientOptions.httpClientWithTimeout(requestOptions); } + if (requestOptions != null && requestOptions.getMaxRetries().isPresent()) { + okhttpRequest = okhttpRequest + .newBuilder() + .tag( + RetryInterceptor.MaxRetriesOverride.class, + new RetryInterceptor.MaxRetriesOverride( + requestOptions.getMaxRetries().get())) + .build(); + } try (Response response = client.newCall(okhttpRequest).execute()) { ResponseBody responseBody = response.body(); String responseBodyString = responseBody != null ? responseBody.string() : "{}"; @@ -274,6 +317,8 @@ public DeepgramApiHttpResponse update( Object errorBody = ObjectMappers.parseErrorBody(responseBodyString); throw new DeepgramHttpException( "Error with status code " + response.code(), response.code(), errorBody, response); + } catch (JsonProcessingException e) { + throw new DeepgramApiException("Failed to deserialize response: " + e.getMessage(), e); } catch (IOException e) { throw new DeepgramApiException("Network error executing HTTP request", e); } @@ -312,6 +357,15 @@ public DeepgramApiHttpResponse> delete( if (requestOptions != null && requestOptions.getTimeout().isPresent()) { client = clientOptions.httpClientWithTimeout(requestOptions); } + if (requestOptions != null && requestOptions.getMaxRetries().isPresent()) { + okhttpRequest = okhttpRequest + .newBuilder() + .tag( + RetryInterceptor.MaxRetriesOverride.class, + new RetryInterceptor.MaxRetriesOverride( + requestOptions.getMaxRetries().get())) + .build(); + } try (Response response = client.newCall(okhttpRequest).execute()) { ResponseBody responseBody = response.body(); String responseBodyString = responseBody != null ? responseBody.string() : "{}"; @@ -332,6 +386,8 @@ public DeepgramApiHttpResponse> delete( Object errorBody = ObjectMappers.parseErrorBody(responseBodyString); throw new DeepgramHttpException( "Error with status code " + response.code(), response.code(), errorBody, response); + } catch (JsonProcessingException e) { + throw new DeepgramApiException("Failed to deserialize response: " + e.getMessage(), e); } catch (IOException e) { throw new DeepgramApiException("Network error executing HTTP request", e); } diff --git a/src/main/java/com/deepgram/resources/voiceagent/configurations/requests/CreateAgentConfigurationV1Request.java b/src/main/java/com/deepgram/resources/voiceagent/configurations/requests/CreateAgentConfigurationV1Request.java index 8c6f444..5eceba9 100644 --- a/src/main/java/com/deepgram/resources/voiceagent/configurations/requests/CreateAgentConfigurationV1Request.java +++ b/src/main/java/com/deepgram/resources/voiceagent/configurations/requests/CreateAgentConfigurationV1Request.java @@ -146,7 +146,6 @@ public Builder from(CreateAgentConfigurationV1Request other) { } /** - *

    A valid JSON string representing the agent block of a Settings message

    *

    A valid JSON string representing the agent block of a Settings message

    * @return Reference to {@code this} so that method calls can be chained together. */ diff --git a/src/main/java/com/deepgram/resources/voiceagent/variables/AsyncRawVariablesClient.java b/src/main/java/com/deepgram/resources/voiceagent/variables/AsyncRawVariablesClient.java index 6ae084d..8073f3b 100644 --- a/src/main/java/com/deepgram/resources/voiceagent/variables/AsyncRawVariablesClient.java +++ b/src/main/java/com/deepgram/resources/voiceagent/variables/AsyncRawVariablesClient.java @@ -10,6 +10,7 @@ import com.deepgram.core.MediaTypes; import com.deepgram.core.ObjectMappers; import com.deepgram.core.RequestOptions; +import com.deepgram.core.RetryInterceptor; import com.deepgram.errors.BadRequestError; import com.deepgram.resources.voiceagent.variables.requests.CreateAgentVariableV1Request; import com.deepgram.resources.voiceagent.variables.requests.UpdateAgentVariableV1Request; @@ -70,6 +71,15 @@ public CompletableFuture> if (requestOptions != null && requestOptions.getTimeout().isPresent()) { client = clientOptions.httpClientWithTimeout(requestOptions); } + if (requestOptions != null && requestOptions.getMaxRetries().isPresent()) { + okhttpRequest = okhttpRequest + .newBuilder() + .tag( + RetryInterceptor.MaxRetriesOverride.class, + new RetryInterceptor.MaxRetriesOverride( + requestOptions.getMaxRetries().get())) + .build(); + } CompletableFuture> future = new CompletableFuture<>(); client.newCall(okhttpRequest).enqueue(new Callback() { @Override @@ -96,6 +106,9 @@ public void onResponse(@NotNull Call call, @NotNull Response response) throws IO future.completeExceptionally(new DeepgramHttpException( "Error with status code " + response.code(), response.code(), errorBody, response)); return; + } catch (JsonProcessingException e) { + future.completeExceptionally( + new DeepgramApiException("Failed to deserialize response: " + e.getMessage(), e)); } catch (IOException e) { future.completeExceptionally(new DeepgramApiException("Network error executing HTTP request", e)); } @@ -150,6 +163,15 @@ public CompletableFuture> create( if (requestOptions != null && requestOptions.getTimeout().isPresent()) { client = clientOptions.httpClientWithTimeout(requestOptions); } + if (requestOptions != null && requestOptions.getMaxRetries().isPresent()) { + okhttpRequest = okhttpRequest + .newBuilder() + .tag( + RetryInterceptor.MaxRetriesOverride.class, + new RetryInterceptor.MaxRetriesOverride( + requestOptions.getMaxRetries().get())) + .build(); + } CompletableFuture> future = new CompletableFuture<>(); client.newCall(okhttpRequest).enqueue(new Callback() { @Override @@ -175,6 +197,9 @@ public void onResponse(@NotNull Call call, @NotNull Response response) throws IO future.completeExceptionally(new DeepgramHttpException( "Error with status code " + response.code(), response.code(), errorBody, response)); return; + } catch (JsonProcessingException e) { + future.completeExceptionally( + new DeepgramApiException("Failed to deserialize response: " + e.getMessage(), e)); } catch (IOException e) { future.completeExceptionally(new DeepgramApiException("Network error executing HTTP request", e)); } @@ -221,6 +246,15 @@ public CompletableFuture> get( if (requestOptions != null && requestOptions.getTimeout().isPresent()) { client = clientOptions.httpClientWithTimeout(requestOptions); } + if (requestOptions != null && requestOptions.getMaxRetries().isPresent()) { + okhttpRequest = okhttpRequest + .newBuilder() + .tag( + RetryInterceptor.MaxRetriesOverride.class, + new RetryInterceptor.MaxRetriesOverride( + requestOptions.getMaxRetries().get())) + .build(); + } CompletableFuture> future = new CompletableFuture<>(); client.newCall(okhttpRequest).enqueue(new Callback() { @Override @@ -246,6 +280,9 @@ public void onResponse(@NotNull Call call, @NotNull Response response) throws IO future.completeExceptionally(new DeepgramHttpException( "Error with status code " + response.code(), response.code(), errorBody, response)); return; + } catch (JsonProcessingException e) { + future.completeExceptionally( + new DeepgramApiException("Failed to deserialize response: " + e.getMessage(), e)); } catch (IOException e) { future.completeExceptionally(new DeepgramApiException("Network error executing HTTP request", e)); } @@ -292,6 +329,15 @@ public CompletableFuture>> delete( if (requestOptions != null && requestOptions.getTimeout().isPresent()) { client = clientOptions.httpClientWithTimeout(requestOptions); } + if (requestOptions != null && requestOptions.getMaxRetries().isPresent()) { + okhttpRequest = okhttpRequest + .newBuilder() + .tag( + RetryInterceptor.MaxRetriesOverride.class, + new RetryInterceptor.MaxRetriesOverride( + requestOptions.getMaxRetries().get())) + .build(); + } CompletableFuture>> future = new CompletableFuture<>(); client.newCall(okhttpRequest).enqueue(new Callback() { @Override @@ -318,6 +364,9 @@ public void onResponse(@NotNull Call call, @NotNull Response response) throws IO future.completeExceptionally(new DeepgramHttpException( "Error with status code " + response.code(), response.code(), errorBody, response)); return; + } catch (JsonProcessingException e) { + future.completeExceptionally( + new DeepgramApiException("Failed to deserialize response: " + e.getMessage(), e)); } catch (IOException e) { future.completeExceptionally(new DeepgramApiException("Network error executing HTTP request", e)); } @@ -373,6 +422,15 @@ public CompletableFuture> update( if (requestOptions != null && requestOptions.getTimeout().isPresent()) { client = clientOptions.httpClientWithTimeout(requestOptions); } + if (requestOptions != null && requestOptions.getMaxRetries().isPresent()) { + okhttpRequest = okhttpRequest + .newBuilder() + .tag( + RetryInterceptor.MaxRetriesOverride.class, + new RetryInterceptor.MaxRetriesOverride( + requestOptions.getMaxRetries().get())) + .build(); + } CompletableFuture> future = new CompletableFuture<>(); client.newCall(okhttpRequest).enqueue(new Callback() { @Override @@ -398,6 +456,9 @@ public void onResponse(@NotNull Call call, @NotNull Response response) throws IO future.completeExceptionally(new DeepgramHttpException( "Error with status code " + response.code(), response.code(), errorBody, response)); return; + } catch (JsonProcessingException e) { + future.completeExceptionally( + new DeepgramApiException("Failed to deserialize response: " + e.getMessage(), e)); } catch (IOException e) { future.completeExceptionally(new DeepgramApiException("Network error executing HTTP request", e)); } diff --git a/src/main/java/com/deepgram/resources/voiceagent/variables/RawVariablesClient.java b/src/main/java/com/deepgram/resources/voiceagent/variables/RawVariablesClient.java index 745e3d5..49bd2d2 100644 --- a/src/main/java/com/deepgram/resources/voiceagent/variables/RawVariablesClient.java +++ b/src/main/java/com/deepgram/resources/voiceagent/variables/RawVariablesClient.java @@ -10,6 +10,7 @@ import com.deepgram.core.MediaTypes; import com.deepgram.core.ObjectMappers; import com.deepgram.core.RequestOptions; +import com.deepgram.core.RetryInterceptor; import com.deepgram.errors.BadRequestError; import com.deepgram.resources.voiceagent.variables.requests.CreateAgentVariableV1Request; import com.deepgram.resources.voiceagent.variables.requests.UpdateAgentVariableV1Request; @@ -65,6 +66,15 @@ public DeepgramApiHttpResponse list(String project if (requestOptions != null && requestOptions.getTimeout().isPresent()) { client = clientOptions.httpClientWithTimeout(requestOptions); } + if (requestOptions != null && requestOptions.getMaxRetries().isPresent()) { + okhttpRequest = okhttpRequest + .newBuilder() + .tag( + RetryInterceptor.MaxRetriesOverride.class, + new RetryInterceptor.MaxRetriesOverride( + requestOptions.getMaxRetries().get())) + .build(); + } try (Response response = client.newCall(okhttpRequest).execute()) { ResponseBody responseBody = response.body(); String responseBodyString = responseBody != null ? responseBody.string() : "{}"; @@ -84,6 +94,8 @@ public DeepgramApiHttpResponse list(String project Object errorBody = ObjectMappers.parseErrorBody(responseBodyString); throw new DeepgramHttpException( "Error with status code " + response.code(), response.code(), errorBody, response); + } catch (JsonProcessingException e) { + throw new DeepgramApiException("Failed to deserialize response: " + e.getMessage(), e); } catch (IOException e) { throw new DeepgramApiException("Network error executing HTTP request", e); } @@ -129,6 +141,15 @@ public DeepgramApiHttpResponse create( if (requestOptions != null && requestOptions.getTimeout().isPresent()) { client = clientOptions.httpClientWithTimeout(requestOptions); } + if (requestOptions != null && requestOptions.getMaxRetries().isPresent()) { + okhttpRequest = okhttpRequest + .newBuilder() + .tag( + RetryInterceptor.MaxRetriesOverride.class, + new RetryInterceptor.MaxRetriesOverride( + requestOptions.getMaxRetries().get())) + .build(); + } try (Response response = client.newCall(okhttpRequest).execute()) { ResponseBody responseBody = response.body(); String responseBodyString = responseBody != null ? responseBody.string() : "{}"; @@ -147,6 +168,8 @@ public DeepgramApiHttpResponse create( Object errorBody = ObjectMappers.parseErrorBody(responseBodyString); throw new DeepgramHttpException( "Error with status code " + response.code(), response.code(), errorBody, response); + } catch (JsonProcessingException e) { + throw new DeepgramApiException("Failed to deserialize response: " + e.getMessage(), e); } catch (IOException e) { throw new DeepgramApiException("Network error executing HTTP request", e); } @@ -185,6 +208,15 @@ public DeepgramApiHttpResponse get( if (requestOptions != null && requestOptions.getTimeout().isPresent()) { client = clientOptions.httpClientWithTimeout(requestOptions); } + if (requestOptions != null && requestOptions.getMaxRetries().isPresent()) { + okhttpRequest = okhttpRequest + .newBuilder() + .tag( + RetryInterceptor.MaxRetriesOverride.class, + new RetryInterceptor.MaxRetriesOverride( + requestOptions.getMaxRetries().get())) + .build(); + } try (Response response = client.newCall(okhttpRequest).execute()) { ResponseBody responseBody = response.body(); String responseBodyString = responseBody != null ? responseBody.string() : "{}"; @@ -203,6 +235,8 @@ public DeepgramApiHttpResponse get( Object errorBody = ObjectMappers.parseErrorBody(responseBodyString); throw new DeepgramHttpException( "Error with status code " + response.code(), response.code(), errorBody, response); + } catch (JsonProcessingException e) { + throw new DeepgramApiException("Failed to deserialize response: " + e.getMessage(), e); } catch (IOException e) { throw new DeepgramApiException("Network error executing HTTP request", e); } @@ -241,6 +275,15 @@ public DeepgramApiHttpResponse> delete( if (requestOptions != null && requestOptions.getTimeout().isPresent()) { client = clientOptions.httpClientWithTimeout(requestOptions); } + if (requestOptions != null && requestOptions.getMaxRetries().isPresent()) { + okhttpRequest = okhttpRequest + .newBuilder() + .tag( + RetryInterceptor.MaxRetriesOverride.class, + new RetryInterceptor.MaxRetriesOverride( + requestOptions.getMaxRetries().get())) + .build(); + } try (Response response = client.newCall(okhttpRequest).execute()) { ResponseBody responseBody = response.body(); String responseBodyString = responseBody != null ? responseBody.string() : "{}"; @@ -261,6 +304,8 @@ public DeepgramApiHttpResponse> delete( Object errorBody = ObjectMappers.parseErrorBody(responseBodyString); throw new DeepgramHttpException( "Error with status code " + response.code(), response.code(), errorBody, response); + } catch (JsonProcessingException e) { + throw new DeepgramApiException("Failed to deserialize response: " + e.getMessage(), e); } catch (IOException e) { throw new DeepgramApiException("Network error executing HTTP request", e); } @@ -308,6 +353,15 @@ public DeepgramApiHttpResponse update( if (requestOptions != null && requestOptions.getTimeout().isPresent()) { client = clientOptions.httpClientWithTimeout(requestOptions); } + if (requestOptions != null && requestOptions.getMaxRetries().isPresent()) { + okhttpRequest = okhttpRequest + .newBuilder() + .tag( + RetryInterceptor.MaxRetriesOverride.class, + new RetryInterceptor.MaxRetriesOverride( + requestOptions.getMaxRetries().get())) + .build(); + } try (Response response = client.newCall(okhttpRequest).execute()) { ResponseBody responseBody = response.body(); String responseBodyString = responseBody != null ? responseBody.string() : "{}"; @@ -326,6 +380,8 @@ public DeepgramApiHttpResponse update( Object errorBody = ObjectMappers.parseErrorBody(responseBodyString); throw new DeepgramHttpException( "Error with status code " + response.code(), response.code(), errorBody, response); + } catch (JsonProcessingException e) { + throw new DeepgramApiException("Failed to deserialize response: " + e.getMessage(), e); } catch (IOException e) { throw new DeepgramApiException("Network error executing HTTP request", e); } diff --git a/src/main/java/com/deepgram/resources/voiceagent/variables/requests/CreateAgentVariableV1Request.java b/src/main/java/com/deepgram/resources/voiceagent/variables/requests/CreateAgentVariableV1Request.java index e8d8ece..dc92662 100644 --- a/src/main/java/com/deepgram/resources/voiceagent/variables/requests/CreateAgentVariableV1Request.java +++ b/src/main/java/com/deepgram/resources/voiceagent/variables/requests/CreateAgentVariableV1Request.java @@ -137,7 +137,6 @@ public Builder from(CreateAgentVariableV1Request other) { } /** - *

    The variable name, following the DG_<VARIABLE_NAME> format

    *

    The variable name, following the DG_<VARIABLE_NAME> format

    * @return Reference to {@code this} so that method calls can be chained together. */ diff --git a/src/main/java/com/deepgram/types/AgentConfigurationV1.java b/src/main/java/com/deepgram/types/AgentConfigurationV1.java index 610baba..d18d264 100644 --- a/src/main/java/com/deepgram/types/AgentConfigurationV1.java +++ b/src/main/java/com/deepgram/types/AgentConfigurationV1.java @@ -198,7 +198,6 @@ public Builder from(AgentConfigurationV1 other) { } /** - *

    The unique identifier of the agent configuration

    *

    The unique identifier of the agent configuration

    * @return Reference to {@code this} so that method calls can be chained together. */ diff --git a/src/main/java/com/deepgram/types/AgentThinkModelsV1ResponseModelsItemId.java b/src/main/java/com/deepgram/types/AgentThinkModelsV1ResponseModelsItemId.java index f09bc0b..7e82e4f 100644 --- a/src/main/java/com/deepgram/types/AgentThinkModelsV1ResponseModelsItemId.java +++ b/src/main/java/com/deepgram/types/AgentThinkModelsV1ResponseModelsItemId.java @@ -128,7 +128,6 @@ public Builder from(AgentThinkModelsV1ResponseModelsItemId other) { } /** - *

    The unique identifier of the AWS Bedrock model (any model string accepted for BYO LLMs)

    *

    The unique identifier of the AWS Bedrock model (any model string accepted for BYO LLMs)

    * @return Reference to {@code this} so that method calls can be chained together. */ @@ -140,7 +139,6 @@ public NameStage id(@NotNull String id) { } /** - *

    The display name of the model

    *

    The display name of the model

    * @return Reference to {@code this} so that method calls can be chained together. */ diff --git a/src/main/java/com/deepgram/types/AgentThinkModelsV1ResponseModelsItemOne.java b/src/main/java/com/deepgram/types/AgentThinkModelsV1ResponseModelsItemOne.java index b756dd7..55d1b1c 100644 --- a/src/main/java/com/deepgram/types/AgentThinkModelsV1ResponseModelsItemOne.java +++ b/src/main/java/com/deepgram/types/AgentThinkModelsV1ResponseModelsItemOne.java @@ -129,7 +129,6 @@ public Builder from(AgentThinkModelsV1ResponseModelsItemOne other) { } /** - *

    The unique identifier of the Anthropic model

    *

    The unique identifier of the Anthropic model

    * @return Reference to {@code this} so that method calls can be chained together. */ @@ -141,7 +140,6 @@ public NameStage id(@NotNull AgentThinkModelsV1ResponseModelsItemOneId id) { } /** - *

    The display name of the model

    *

    The display name of the model

    * @return Reference to {@code this} so that method calls can be chained together. */ diff --git a/src/main/java/com/deepgram/types/AgentThinkModelsV1ResponseModelsItemThree.java b/src/main/java/com/deepgram/types/AgentThinkModelsV1ResponseModelsItemThree.java index 257e433..3fb1d9e 100644 --- a/src/main/java/com/deepgram/types/AgentThinkModelsV1ResponseModelsItemThree.java +++ b/src/main/java/com/deepgram/types/AgentThinkModelsV1ResponseModelsItemThree.java @@ -115,7 +115,6 @@ public Builder from(AgentThinkModelsV1ResponseModelsItemThree other) { } /** - *

    The display name of the model

    *

    The display name of the model

    * @return Reference to {@code this} so that method calls can be chained together. */ diff --git a/src/main/java/com/deepgram/types/AgentThinkModelsV1ResponseModelsItemTwo.java b/src/main/java/com/deepgram/types/AgentThinkModelsV1ResponseModelsItemTwo.java index b58d537..0587907 100644 --- a/src/main/java/com/deepgram/types/AgentThinkModelsV1ResponseModelsItemTwo.java +++ b/src/main/java/com/deepgram/types/AgentThinkModelsV1ResponseModelsItemTwo.java @@ -129,7 +129,6 @@ public Builder from(AgentThinkModelsV1ResponseModelsItemTwo other) { } /** - *

    The unique identifier of the Google model

    *

    The unique identifier of the Google model

    * @return Reference to {@code this} so that method calls can be chained together. */ @@ -141,7 +140,6 @@ public NameStage id(@NotNull AgentThinkModelsV1ResponseModelsItemTwoId id) { } /** - *

    The display name of the model

    *

    The display name of the model

    * @return Reference to {@code this} so that method calls can be chained together. */ diff --git a/src/main/java/com/deepgram/types/AgentThinkModelsV1ResponseModelsItemZero.java b/src/main/java/com/deepgram/types/AgentThinkModelsV1ResponseModelsItemZero.java index e41781c..351975b 100644 --- a/src/main/java/com/deepgram/types/AgentThinkModelsV1ResponseModelsItemZero.java +++ b/src/main/java/com/deepgram/types/AgentThinkModelsV1ResponseModelsItemZero.java @@ -129,7 +129,6 @@ public Builder from(AgentThinkModelsV1ResponseModelsItemZero other) { } /** - *

    The unique identifier of the OpenAI model

    *

    The unique identifier of the OpenAI model

    * @return Reference to {@code this} so that method calls can be chained together. */ @@ -141,7 +140,6 @@ public NameStage id(@NotNull AgentThinkModelsV1ResponseModelsItemZeroId id) { } /** - *

    The display name of the model

    *

    The display name of the model

    * @return Reference to {@code this} so that method calls can be chained together. */ diff --git a/src/main/java/com/deepgram/types/AgentVariableV1.java b/src/main/java/com/deepgram/types/AgentVariableV1.java index 6f09f2f..0fa7f88 100644 --- a/src/main/java/com/deepgram/types/AgentVariableV1.java +++ b/src/main/java/com/deepgram/types/AgentVariableV1.java @@ -189,7 +189,6 @@ public Builder from(AgentVariableV1 other) { } /** - *

    The unique identifier of the variable

    *

    The unique identifier of the variable

    * @return Reference to {@code this} so that method calls can be chained together. */ @@ -201,7 +200,6 @@ public KeyStage variableId(@NotNull String variableId) { } /** - *

    The variable name, following the DG_<VARIABLE_NAME> format

    *

    The variable name, following the DG_<VARIABLE_NAME> format

    * @return Reference to {@code this} so that method calls can be chained together. */ diff --git a/src/main/java/com/deepgram/types/Anthropic.java b/src/main/java/com/deepgram/types/Anthropic.java index 0221486..0c4b5d6 100644 --- a/src/main/java/com/deepgram/types/Anthropic.java +++ b/src/main/java/com/deepgram/types/Anthropic.java @@ -151,7 +151,6 @@ public Builder from(Anthropic other) { } /** - *

    Anthropic model to use

    *

    Anthropic model to use

    * @return Reference to {@code this} so that method calls can be chained together. */ diff --git a/src/main/java/com/deepgram/types/AwsBedrockThinkProvider.java b/src/main/java/com/deepgram/types/AwsBedrockThinkProvider.java index 3cc3fae..5f2840d 100644 --- a/src/main/java/com/deepgram/types/AwsBedrockThinkProvider.java +++ b/src/main/java/com/deepgram/types/AwsBedrockThinkProvider.java @@ -153,7 +153,6 @@ public Builder from(AwsBedrockThinkProvider other) { } /** - *

    AWS Bedrock model to use

    *

    AWS Bedrock model to use

    * @return Reference to {@code this} so that method calls can be chained together. */ diff --git a/src/main/java/com/deepgram/types/AwsPollySpeakProvider.java b/src/main/java/com/deepgram/types/AwsPollySpeakProvider.java index 0e5f4ef..4b292ab 100644 --- a/src/main/java/com/deepgram/types/AwsPollySpeakProvider.java +++ b/src/main/java/com/deepgram/types/AwsPollySpeakProvider.java @@ -187,7 +187,6 @@ public Builder from(AwsPollySpeakProvider other) { } /** - *

    AWS Polly voice name

    *

    AWS Polly voice name

    * @return Reference to {@code this} so that method calls can be chained together. */ @@ -199,7 +198,6 @@ public LanguageStage voice(@NotNull AwsPollySpeakProviderVoice voice) { } /** - *

    Language code to use, e.g. 'en-US'. Corresponds to the language_code parameter in the AWS Polly API

    *

    Language code to use, e.g. 'en-US'. Corresponds to the language_code parameter in the AWS Polly API

    * @return Reference to {@code this} so that method calls can be chained together. */ diff --git a/src/main/java/com/deepgram/types/BillingBreakdownV1Response.java b/src/main/java/com/deepgram/types/BillingBreakdownV1Response.java index ecb9982..3c4f9f7 100644 --- a/src/main/java/com/deepgram/types/BillingBreakdownV1Response.java +++ b/src/main/java/com/deepgram/types/BillingBreakdownV1Response.java @@ -162,7 +162,6 @@ public Builder from(BillingBreakdownV1Response other) { } /** - *

    Start date of the billing summmary period

    *

    Start date of the billing summmary period

    * @return Reference to {@code this} so that method calls can be chained together. */ @@ -174,7 +173,6 @@ public EndStage start(@NotNull String start) { } /** - *

    End date of the billing summary period

    *

    End date of the billing summary period

    * @return Reference to {@code this} so that method calls can be chained together. */ diff --git a/src/main/java/com/deepgram/types/BillingBreakdownV1ResponseResolution.java b/src/main/java/com/deepgram/types/BillingBreakdownV1ResponseResolution.java index 5527d1c..15de5de 100644 --- a/src/main/java/com/deepgram/types/BillingBreakdownV1ResponseResolution.java +++ b/src/main/java/com/deepgram/types/BillingBreakdownV1ResponseResolution.java @@ -121,7 +121,6 @@ public Builder from(BillingBreakdownV1ResponseResolution other) { } /** - *

    Time unit for the resolution

    *

    Time unit for the resolution

    * @return Reference to {@code this} so that method calls can be chained together. */ @@ -133,7 +132,6 @@ public AmountStage units(@NotNull String units) { } /** - *

    Amount of units

    *

    Amount of units

    * @return Reference to {@code this} so that method calls can be chained together. */ diff --git a/src/main/java/com/deepgram/types/BillingBreakdownV1ResponseResultsItem.java b/src/main/java/com/deepgram/types/BillingBreakdownV1ResponseResultsItem.java index 7927fe8..ef690ec 100644 --- a/src/main/java/com/deepgram/types/BillingBreakdownV1ResponseResultsItem.java +++ b/src/main/java/com/deepgram/types/BillingBreakdownV1ResponseResultsItem.java @@ -117,7 +117,6 @@ public Builder from(BillingBreakdownV1ResponseResultsItem other) { } /** - *

    USD cost of the billing for this grouping

    *

    USD cost of the billing for this grouping

    * @return Reference to {@code this} so that method calls can be chained together. */ diff --git a/src/main/java/com/deepgram/types/Cartesia.java b/src/main/java/com/deepgram/types/Cartesia.java index 8ca8847..0a7bfae 100644 --- a/src/main/java/com/deepgram/types/Cartesia.java +++ b/src/main/java/com/deepgram/types/Cartesia.java @@ -193,7 +193,6 @@ public Builder from(Cartesia other) { } /** - *

    Cartesia model ID

    *

    Cartesia model ID

    * @return Reference to {@code this} so that method calls can be chained together. */ diff --git a/src/main/java/com/deepgram/types/CartesiaSpeakProviderVoice.java b/src/main/java/com/deepgram/types/CartesiaSpeakProviderVoice.java index 9d7168f..b788675 100644 --- a/src/main/java/com/deepgram/types/CartesiaSpeakProviderVoice.java +++ b/src/main/java/com/deepgram/types/CartesiaSpeakProviderVoice.java @@ -119,7 +119,6 @@ public Builder from(CartesiaSpeakProviderVoice other) { } /** - *

    Cartesia voice mode

    *

    Cartesia voice mode

    * @return Reference to {@code this} so that method calls can be chained together. */ @@ -131,7 +130,6 @@ public IdStage mode(@NotNull String mode) { } /** - *

    Cartesia voice ID

    *

    Cartesia voice ID

    * @return Reference to {@code this} so that method calls can be chained together. */ diff --git a/src/main/java/com/deepgram/types/CreateAgentConfigurationV1Response.java b/src/main/java/com/deepgram/types/CreateAgentConfigurationV1Response.java index aed9a45..3807090 100644 --- a/src/main/java/com/deepgram/types/CreateAgentConfigurationV1Response.java +++ b/src/main/java/com/deepgram/types/CreateAgentConfigurationV1Response.java @@ -150,7 +150,6 @@ public Builder from(CreateAgentConfigurationV1Response other) { } /** - *

    The unique identifier of the newly created agent configuration

    *

    The unique identifier of the newly created agent configuration

    * @return Reference to {@code this} so that method calls can be chained together. */ diff --git a/src/main/java/com/deepgram/types/CreateProjectDistributionCredentialsV1ResponseDistributionCredentials.java b/src/main/java/com/deepgram/types/CreateProjectDistributionCredentialsV1ResponseDistributionCredentials.java index a8fe5af..0f6d43a 100644 --- a/src/main/java/com/deepgram/types/CreateProjectDistributionCredentialsV1ResponseDistributionCredentials.java +++ b/src/main/java/com/deepgram/types/CreateProjectDistributionCredentialsV1ResponseDistributionCredentials.java @@ -201,7 +201,6 @@ public Builder from(CreateProjectDistributionCredentialsV1ResponseDistributionCr } /** - *

    Unique identifier for the distribution credentials

    *

    Unique identifier for the distribution credentials

    * @return Reference to {@code this} so that method calls can be chained together. */ @@ -214,7 +213,6 @@ public ProviderStage distributionCredentialsId(@NotNull String distributionCrede } /** - *

    The provider of the distribution service

    *

    The provider of the distribution service

    * @return Reference to {@code this} so that method calls can be chained together. */ @@ -226,7 +224,6 @@ public CreatedStage provider(@NotNull String provider) { } /** - *

    Timestamp when the credentials were created

    *

    Timestamp when the credentials were created

    * @return Reference to {@code this} so that method calls can be chained together. */ diff --git a/src/main/java/com/deepgram/types/CreateProjectDistributionCredentialsV1ResponseMember.java b/src/main/java/com/deepgram/types/CreateProjectDistributionCredentialsV1ResponseMember.java index 4c7468c..968290d 100644 --- a/src/main/java/com/deepgram/types/CreateProjectDistributionCredentialsV1ResponseMember.java +++ b/src/main/java/com/deepgram/types/CreateProjectDistributionCredentialsV1ResponseMember.java @@ -121,7 +121,6 @@ public Builder from(CreateProjectDistributionCredentialsV1ResponseMember other) } /** - *

    Unique identifier for the member

    *

    Unique identifier for the member

    * @return Reference to {@code this} so that method calls can be chained together. */ @@ -133,7 +132,6 @@ public EmailStage memberId(@NotNull String memberId) { } /** - *

    Email address of the member

    *

    Email address of the member

    * @return Reference to {@code this} so that method calls can be chained together. */ diff --git a/src/main/java/com/deepgram/types/Deepgram.java b/src/main/java/com/deepgram/types/Deepgram.java index 28ba235..e502c19 100644 --- a/src/main/java/com/deepgram/types/Deepgram.java +++ b/src/main/java/com/deepgram/types/Deepgram.java @@ -46,7 +46,7 @@ public String getType() { } /** - * @return The REST API version for the Deepgram text-to-speech API + * @return The Deepgram text-to-speech model family. Accepted values: v1 (Aura, the default) and v2 (Flux TTS, Early Access). Use v1 with an aura-* model and v2 with a flux-* model. Defaults to v1 when omitted. */ @JsonProperty("version") public Optional getVersion() { @@ -54,7 +54,7 @@ public Optional getVersion() { } /** - * @return Deepgram TTS model + * @return Deepgram TTS model. Aura models (version v1) use the aura-* voices; Flux TTS (version v2, Early Access) uses the flux-{voice}-{language} voices (e.g. flux-alexis-en). */ @JsonProperty("model") public DeepgramSpeakProviderModel getModel() { @@ -100,7 +100,7 @@ public static ModelStage builder() { public interface ModelStage { /** - *

    Deepgram TTS model

    + *

    Deepgram TTS model. Aura models (version v1) use the aura-* voices; Flux TTS (version v2, Early Access) uses the flux-{voice}-{language} voices (e.g. flux-alexis-en).

    */ _FinalStage model(@NotNull DeepgramSpeakProviderModel model); @@ -115,7 +115,7 @@ public interface _FinalStage { _FinalStage additionalProperties(Map additionalProperties); /** - *

    The REST API version for the Deepgram text-to-speech API

    + *

    The Deepgram text-to-speech model family. Accepted values: v1 (Aura, the default) and v2 (Flux TTS, Early Access). Use v1 with an aura-* model and v2 with a flux-* model. Defaults to v1 when omitted.

    */ _FinalStage version(Optional version); @@ -151,8 +151,7 @@ public Builder from(Deepgram other) { } /** - *

    Deepgram TTS model

    - *

    Deepgram TTS model

    + *

    Deepgram TTS model. Aura models (version v1) use the aura-* voices; Flux TTS (version v2, Early Access) uses the flux-{voice}-{language} voices (e.g. flux-alexis-en).

    * @return Reference to {@code this} so that method calls can be chained together. */ @java.lang.Override @@ -183,7 +182,7 @@ public _FinalStage speed(Optional speed) { } /** - *

    The REST API version for the Deepgram text-to-speech API

    + *

    The Deepgram text-to-speech model family. Accepted values: v1 (Aura, the default) and v2 (Flux TTS, Early Access). Use v1 with an aura-* model and v2 with a flux-* model. Defaults to v1 when omitted.

    * @return Reference to {@code this} so that method calls can be chained together. */ @java.lang.Override @@ -193,7 +192,7 @@ public _FinalStage version(String version) { } /** - *

    The REST API version for the Deepgram text-to-speech API

    + *

    The Deepgram text-to-speech model family. Accepted values: v1 (Aura, the default) and v2 (Flux TTS, Early Access). Use v1 with an aura-* model and v2 with a flux-* model. Defaults to v1 when omitted.

    */ @java.lang.Override @JsonSetter(value = "version", nulls = Nulls.SKIP) diff --git a/src/main/java/com/deepgram/types/DeepgramListenProviderV2.java b/src/main/java/com/deepgram/types/DeepgramListenProviderV2.java index 560f010..79cdd1e 100644 --- a/src/main/java/com/deepgram/types/DeepgramListenProviderV2.java +++ b/src/main/java/com/deepgram/types/DeepgramListenProviderV2.java @@ -256,7 +256,6 @@ public Builder from(DeepgramListenProviderV2 other) { } /** - *

    Model to use for speech to text using the V2 API (e.g. flux-general-en, flux-general-multi)

    *

    Model to use for speech to text using the V2 API (e.g. flux-general-en, flux-general-multi)

    * @return Reference to {@code this} so that method calls can be chained together. */ diff --git a/src/main/java/com/deepgram/types/DeepgramSpeakProviderModel.java b/src/main/java/com/deepgram/types/DeepgramSpeakProviderModel.java index 897ac16..0fe7645 100644 --- a/src/main/java/com/deepgram/types/DeepgramSpeakProviderModel.java +++ b/src/main/java/com/deepgram/types/DeepgramSpeakProviderModel.java @@ -34,6 +34,9 @@ public final class DeepgramSpeakProviderModel { public static final DeepgramSpeakProviderModel AURA2NEPTUNE_EN = new DeepgramSpeakProviderModel(Value.AURA2NEPTUNE_EN, "aura-2-neptune-en"); + public static final DeepgramSpeakProviderModel FLUX_RUFUS_EN = + new DeepgramSpeakProviderModel(Value.FLUX_RUFUS_EN, "flux-rufus-en"); + public static final DeepgramSpeakProviderModel AURA2CALLISTA_EN = new DeepgramSpeakProviderModel(Value.AURA2CALLISTA_EN, "aura-2-callista-en"); @@ -43,9 +46,15 @@ public final class DeepgramSpeakProviderModel { public static final DeepgramSpeakProviderModel AURA2OPHELIA_EN = new DeepgramSpeakProviderModel(Value.AURA2OPHELIA_EN, "aura-2-ophelia-en"); + public static final DeepgramSpeakProviderModel FLUX_BRUCE_EN = + new DeepgramSpeakProviderModel(Value.FLUX_BRUCE_EN, "flux-bruce-en"); + public static final DeepgramSpeakProviderModel AURA2APOLLO_EN = new DeepgramSpeakProviderModel(Value.AURA2APOLLO_EN, "aura-2-apollo-en"); + public static final DeepgramSpeakProviderModel FLUX_ALEXIS_EN = + new DeepgramSpeakProviderModel(Value.FLUX_ALEXIS_EN, "flux-alexis-en"); + public static final DeepgramSpeakProviderModel AURA_LUNA_EN = new DeepgramSpeakProviderModel(Value.AURA_LUNA_EN, "aura-luna-en"); @@ -67,6 +76,12 @@ public final class DeepgramSpeakProviderModel { public static final DeepgramSpeakProviderModel AURA2SELENE_EN = new DeepgramSpeakProviderModel(Value.AURA2SELENE_EN, "aura-2-selene-en"); + public static final DeepgramSpeakProviderModel FLUX_MARCUS_EN = + new DeepgramSpeakProviderModel(Value.FLUX_MARCUS_EN, "flux-marcus-en"); + + public static final DeepgramSpeakProviderModel FLUX_SHARON_EN = + new DeepgramSpeakProviderModel(Value.FLUX_SHARON_EN, "flux-sharon-en"); + public static final DeepgramSpeakProviderModel AURA2ARIES_EN = new DeepgramSpeakProviderModel(Value.AURA2ARIES_EN, "aura-2-aries-en"); @@ -76,6 +91,9 @@ public final class DeepgramSpeakProviderModel { public static final DeepgramSpeakProviderModel AURA2LUNA_EN = new DeepgramSpeakProviderModel(Value.AURA2LUNA_EN, "aura-2-luna-en"); + public static final DeepgramSpeakProviderModel FLUX_DREW_EN = + new DeepgramSpeakProviderModel(Value.FLUX_DREW_EN, "flux-drew-en"); + public static final DeepgramSpeakProviderModel AURA2JAVIER_ES = new DeepgramSpeakProviderModel(Value.AURA2JAVIER_ES, "aura-2-javier-es"); @@ -109,6 +127,9 @@ public final class DeepgramSpeakProviderModel { public static final DeepgramSpeakProviderModel AURA_ANGUS_EN = new DeepgramSpeakProviderModel(Value.AURA_ANGUS_EN, "aura-angus-en"); + public static final DeepgramSpeakProviderModel FLUX_HEATHER_EN = + new DeepgramSpeakProviderModel(Value.FLUX_HEATHER_EN, "flux-heather-en"); + public static final DeepgramSpeakProviderModel AURA2JUPITER_EN = new DeepgramSpeakProviderModel(Value.AURA2JUPITER_EN, "aura-2-jupiter-en"); @@ -127,9 +148,15 @@ public final class DeepgramSpeakProviderModel { public static final DeepgramSpeakProviderModel AURA2HELENA_EN = new DeepgramSpeakProviderModel(Value.AURA2HELENA_EN, "aura-2-helena-en"); + public static final DeepgramSpeakProviderModel FLUX_HALEY_EN = + new DeepgramSpeakProviderModel(Value.FLUX_HALEY_EN, "flux-haley-en"); + public static final DeepgramSpeakProviderModel AURA_STELLA_EN = new DeepgramSpeakProviderModel(Value.AURA_STELLA_EN, "aura-stella-en"); + public static final DeepgramSpeakProviderModel FLUX_JACK_EN = + new DeepgramSpeakProviderModel(Value.FLUX_JACK_EN, "flux-jack-en"); + public static final DeepgramSpeakProviderModel AURA2DRACO_EN = new DeepgramSpeakProviderModel(Value.AURA2DRACO_EN, "aura-2-draco-en"); @@ -139,6 +166,9 @@ public final class DeepgramSpeakProviderModel { public static final DeepgramSpeakProviderModel AURA2CELESTE_ES = new DeepgramSpeakProviderModel(Value.AURA2CELESTE_ES, "aura-2-celeste-es"); + public static final DeepgramSpeakProviderModel FLUX_PRIYA_EN = + new DeepgramSpeakProviderModel(Value.FLUX_PRIYA_EN, "flux-priya-en"); + public static final DeepgramSpeakProviderModel AURA_HELIOS_EN = new DeepgramSpeakProviderModel(Value.AURA_HELIOS_EN, "aura-helios-en"); @@ -157,6 +187,9 @@ public final class DeepgramSpeakProviderModel { public static final DeepgramSpeakProviderModel AURA2ORION_EN = new DeepgramSpeakProviderModel(Value.AURA2ORION_EN, "aura-2-orion-en"); + public static final DeepgramSpeakProviderModel FLUX_RENEE_EN = + new DeepgramSpeakProviderModel(Value.FLUX_RENEE_EN, "flux-renee-en"); + public static final DeepgramSpeakProviderModel AURA_ATHENA_EN = new DeepgramSpeakProviderModel(Value.AURA_ATHENA_EN, "aura-athena-en"); @@ -178,6 +211,9 @@ public final class DeepgramSpeakProviderModel { public static final DeepgramSpeakProviderModel AURA2VESTA_EN = new DeepgramSpeakProviderModel(Value.AURA2VESTA_EN, "aura-2-vesta-en"); + public static final DeepgramSpeakProviderModel FLUX_COLE_EN = + new DeepgramSpeakProviderModel(Value.FLUX_COLE_EN, "flux-cole-en"); + public static final DeepgramSpeakProviderModel AURA_ASTERIA_EN = new DeepgramSpeakProviderModel(Value.AURA_ASTERIA_EN, "aura-asteria-en"); @@ -247,14 +283,20 @@ public T visit(Visitor visitor) { return visitor.visitAura2SelenaEs(); case AURA2NEPTUNE_EN: return visitor.visitAura2NeptuneEn(); + case FLUX_RUFUS_EN: + return visitor.visitFluxRufusEn(); 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 FLUX_ALEXIS_EN: + return visitor.visitFluxAlexisEn(); case AURA_LUNA_EN: return visitor.visitAuraLunaEn(); case AURA_ORPHEUS_EN: @@ -269,12 +311,18 @@ public T visit(Visitor visitor) { return visitor.visitAuraHeraEn(); case AURA2SELENE_EN: return visitor.visitAura2SeleneEn(); + case FLUX_MARCUS_EN: + return visitor.visitFluxMarcusEn(); + case FLUX_SHARON_EN: + return visitor.visitFluxSharonEn(); case AURA2ARIES_EN: return visitor.visitAura2AriesEn(); case AURA2JUNO_EN: return visitor.visitAura2JunoEn(); case AURA2LUNA_EN: return visitor.visitAura2LunaEn(); + case FLUX_DREW_EN: + return visitor.visitFluxDrewEn(); case AURA2JAVIER_ES: return visitor.visitAura2JavierEs(); case AURA2PHOEBE_EN: @@ -297,6 +345,8 @@ public T visit(Visitor visitor) { return visitor.visitAura2IrisEn(); case AURA_ANGUS_EN: return visitor.visitAuraAngusEn(); + case FLUX_HEATHER_EN: + return visitor.visitFluxHeatherEn(); case AURA2JUPITER_EN: return visitor.visitAura2JupiterEn(); case AURA2AQUILA_ES: @@ -309,14 +359,20 @@ public T visit(Visitor visitor) { return visitor.visitAura2AtlasEn(); 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 AURA2HYPERION_EN: return visitor.visitAura2HyperionEn(); case AURA2CELESTE_ES: return visitor.visitAura2CelesteEs(); + case FLUX_PRIYA_EN: + return visitor.visitFluxPriyaEn(); case AURA_HELIOS_EN: return visitor.visitAuraHeliosEn(); case AURA2PLUTO_EN: @@ -329,6 +385,8 @@ public T visit(Visitor visitor) { return visitor.visitAura2ArcasEn(); case AURA2ORION_EN: return visitor.visitAura2OrionEn(); + case FLUX_RENEE_EN: + return visitor.visitFluxReneeEn(); case AURA_ATHENA_EN: return visitor.visitAuraAthenaEn(); case AURA2ODYSSEUS_EN: @@ -343,6 +401,8 @@ public T visit(Visitor visitor) { return visitor.visitAuraPerseusEn(); case AURA2VESTA_EN: return visitor.visitAura2VestaEn(); + case FLUX_COLE_EN: + return visitor.visitFluxColeEn(); case AURA_ASTERIA_EN: return visitor.visitAuraAsteriaEn(); case AURA2ZEUS_EN: @@ -382,14 +442,20 @@ public static DeepgramSpeakProviderModel valueOf(String value) { return AURA2SELENA_ES; case "aura-2-neptune-en": return AURA2NEPTUNE_EN; + case "flux-rufus-en": + return FLUX_RUFUS_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 "flux-alexis-en": + return FLUX_ALEXIS_EN; case "aura-luna-en": return AURA_LUNA_EN; case "aura-orpheus-en": @@ -404,12 +470,18 @@ public static DeepgramSpeakProviderModel valueOf(String value) { return AURA_HERA_EN; case "aura-2-selene-en": return AURA2SELENE_EN; + case "flux-marcus-en": + return FLUX_MARCUS_EN; + case "flux-sharon-en": + return FLUX_SHARON_EN; case "aura-2-aries-en": return AURA2ARIES_EN; case "aura-2-juno-en": return AURA2JUNO_EN; case "aura-2-luna-en": return AURA2LUNA_EN; + case "flux-drew-en": + return FLUX_DREW_EN; case "aura-2-javier-es": return AURA2JAVIER_ES; case "aura-2-phoebe-en": @@ -432,6 +504,8 @@ public static DeepgramSpeakProviderModel valueOf(String value) { return AURA2IRIS_EN; case "aura-angus-en": return AURA_ANGUS_EN; + case "flux-heather-en": + return FLUX_HEATHER_EN; case "aura-2-jupiter-en": return AURA2JUPITER_EN; case "aura-2-aquila-es": @@ -444,14 +518,20 @@ public static DeepgramSpeakProviderModel valueOf(String value) { return AURA2ATLAS_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 "aura-2-hyperion-en": return AURA2HYPERION_EN; case "aura-2-celeste-es": return AURA2CELESTE_ES; + case "flux-priya-en": + return FLUX_PRIYA_EN; case "aura-helios-en": return AURA_HELIOS_EN; case "aura-2-pluto-en": @@ -464,6 +544,8 @@ public static DeepgramSpeakProviderModel valueOf(String value) { return AURA2ARCAS_EN; case "aura-2-orion-en": return AURA2ORION_EN; + case "flux-renee-en": + return FLUX_RENEE_EN; case "aura-athena-en": return AURA_ATHENA_EN; case "aura-2-odysseus-en": @@ -478,6 +560,8 @@ public static DeepgramSpeakProviderModel valueOf(String value) { return AURA_PERSEUS_EN; case "aura-2-vesta-en": return AURA2VESTA_EN; + case "flux-cole-en": + return FLUX_COLE_EN; case "aura-asteria-en": return AURA_ASTERIA_EN; case "aura-2-zeus-en": @@ -622,6 +706,30 @@ public enum Value { AURA2JAVIER_ES, + FLUX_HALEY_EN, + + FLUX_HEATHER_EN, + + FLUX_COLE_EN, + + FLUX_ALEXIS_EN, + + FLUX_PRIYA_EN, + + FLUX_JACK_EN, + + FLUX_BRUCE_EN, + + FLUX_RUFUS_EN, + + FLUX_DREW_EN, + + FLUX_RENEE_EN, + + FLUX_MARCUS_EN, + + FLUX_SHARON_EN, + UNKNOWN } @@ -752,6 +860,30 @@ public interface Visitor { T visitAura2JavierEs(); + T visitFluxHaleyEn(); + + T visitFluxHeatherEn(); + + T visitFluxColeEn(); + + T visitFluxAlexisEn(); + + T visitFluxPriyaEn(); + + T visitFluxJackEn(); + + T visitFluxBruceEn(); + + T visitFluxRufusEn(); + + T visitFluxDrewEn(); + + T visitFluxReneeEn(); + + T visitFluxMarcusEn(); + + T visitFluxSharonEn(); + T visitUnknown(String unknownType); } } diff --git a/src/main/java/com/deepgram/types/ElevenLabsSpeakProvider.java b/src/main/java/com/deepgram/types/ElevenLabsSpeakProvider.java index b530f1f..9379f94 100644 --- a/src/main/java/com/deepgram/types/ElevenLabsSpeakProvider.java +++ b/src/main/java/com/deepgram/types/ElevenLabsSpeakProvider.java @@ -176,7 +176,6 @@ public Builder from(ElevenLabsSpeakProvider other) { } /** - *

    Eleven Labs model ID

    *

    Eleven Labs model ID

    * @return Reference to {@code this} so that method calls can be chained together. */ diff --git a/src/main/java/com/deepgram/types/GetProjectDistributionCredentialsV1ResponseDistributionCredentials.java b/src/main/java/com/deepgram/types/GetProjectDistributionCredentialsV1ResponseDistributionCredentials.java index 08bd56e..735d903 100644 --- a/src/main/java/com/deepgram/types/GetProjectDistributionCredentialsV1ResponseDistributionCredentials.java +++ b/src/main/java/com/deepgram/types/GetProjectDistributionCredentialsV1ResponseDistributionCredentials.java @@ -201,7 +201,6 @@ public Builder from(GetProjectDistributionCredentialsV1ResponseDistributionCrede } /** - *

    Unique identifier for the distribution credentials

    *

    Unique identifier for the distribution credentials

    * @return Reference to {@code this} so that method calls can be chained together. */ @@ -214,7 +213,6 @@ public ProviderStage distributionCredentialsId(@NotNull String distributionCrede } /** - *

    The provider of the distribution service

    *

    The provider of the distribution service

    * @return Reference to {@code this} so that method calls can be chained together. */ @@ -226,7 +224,6 @@ public CreatedStage provider(@NotNull String provider) { } /** - *

    Timestamp when the credentials were created

    *

    Timestamp when the credentials were created

    * @return Reference to {@code this} so that method calls can be chained together. */ diff --git a/src/main/java/com/deepgram/types/GetProjectDistributionCredentialsV1ResponseMember.java b/src/main/java/com/deepgram/types/GetProjectDistributionCredentialsV1ResponseMember.java index 1732032..d39bda7 100644 --- a/src/main/java/com/deepgram/types/GetProjectDistributionCredentialsV1ResponseMember.java +++ b/src/main/java/com/deepgram/types/GetProjectDistributionCredentialsV1ResponseMember.java @@ -121,7 +121,6 @@ public Builder from(GetProjectDistributionCredentialsV1ResponseMember other) { } /** - *

    Unique identifier for the member

    *

    Unique identifier for the member

    * @return Reference to {@code this} so that method calls can be chained together. */ @@ -133,7 +132,6 @@ public EmailStage memberId(@NotNull String memberId) { } /** - *

    Email address of the member

    *

    Email address of the member

    * @return Reference to {@code this} so that method calls can be chained together. */ diff --git a/src/main/java/com/deepgram/types/Google.java b/src/main/java/com/deepgram/types/Google.java index fb6b2ef..541b1e6 100644 --- a/src/main/java/com/deepgram/types/Google.java +++ b/src/main/java/com/deepgram/types/Google.java @@ -21,7 +21,7 @@ @JsonInclude(JsonInclude.Include.NON_ABSENT) @JsonDeserialize(builder = Google.Builder.class) public final class Google { - private final Optional version; + private final Optional version; private final GoogleThinkProviderModel model; @@ -30,7 +30,7 @@ public final class Google { private final Map additionalProperties; private Google( - Optional version, + Optional version, GoogleThinkProviderModel model, Optional temperature, Map additionalProperties) { @@ -46,10 +46,10 @@ public String getType() { } /** - * @return The REST API version for the Google generative language API + * @return The Google API used for the request: ai-studio-v1beta for the AI Studio API, or gemini-enterprise-agent-v1 for the Gemini Enterprise Agent (GEA) API. v1beta is accepted as an alias for ai-studio-v1beta. Defaults based on the Deepgram Voice Agent endpoint you connect to. */ @JsonProperty("version") - public Optional getVersion() { + public Optional getVersion() { return version; } @@ -115,11 +115,11 @@ public interface _FinalStage { _FinalStage additionalProperties(Map additionalProperties); /** - *

    The REST API version for the Google generative language API

    + *

    The Google API used for the request: ai-studio-v1beta for the AI Studio API, or gemini-enterprise-agent-v1 for the Gemini Enterprise Agent (GEA) API. v1beta is accepted as an alias for ai-studio-v1beta. Defaults based on the Deepgram Voice Agent endpoint you connect to.

    */ - _FinalStage version(Optional version); + _FinalStage version(Optional version); - _FinalStage version(String version); + _FinalStage version(GoogleThinkProviderVersion version); /** *

    Google temperature (0-2)

    @@ -135,7 +135,7 @@ public static final class Builder implements ModelStage, _FinalStage { private Optional temperature = Optional.empty(); - private Optional version = Optional.empty(); + private Optional version = Optional.empty(); @JsonAnySetter private Map additionalProperties = new HashMap<>(); @@ -151,7 +151,6 @@ public Builder from(Google other) { } /** - *

    Google model to use

    *

    Google model to use

    * @return Reference to {@code this} so that method calls can be chained together. */ @@ -183,21 +182,21 @@ public _FinalStage temperature(Optional temperature) { } /** - *

    The REST API version for the Google generative language API

    + *

    The Google API used for the request: ai-studio-v1beta for the AI Studio API, or gemini-enterprise-agent-v1 for the Gemini Enterprise Agent (GEA) API. v1beta is accepted as an alias for ai-studio-v1beta. Defaults based on the Deepgram Voice Agent endpoint you connect to.

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

    The REST API version for the Google generative language API

    + *

    The Google API used for the request: ai-studio-v1beta for the AI Studio API, or gemini-enterprise-agent-v1 for the Gemini Enterprise Agent (GEA) API. v1beta is accepted as an alias for ai-studio-v1beta. Defaults based on the Deepgram Voice Agent endpoint you connect to.

    */ @java.lang.Override @JsonSetter(value = "version", nulls = Nulls.SKIP) - public _FinalStage version(Optional version) { + public _FinalStage version(Optional version) { this.version = version; return this; } diff --git a/src/main/java/com/deepgram/types/GoogleThinkProviderVersion.java b/src/main/java/com/deepgram/types/GoogleThinkProviderVersion.java new file mode 100644 index 0000000..358ad3a --- /dev/null +++ b/src/main/java/com/deepgram/types/GoogleThinkProviderVersion.java @@ -0,0 +1,96 @@ +/** + * 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 GoogleThinkProviderVersion { + public static final GoogleThinkProviderVersion AI_STUDIO_V1BETA = + new GoogleThinkProviderVersion(Value.AI_STUDIO_V1BETA, "ai-studio-v1beta"); + + public static final GoogleThinkProviderVersion V1BETA = new GoogleThinkProviderVersion(Value.V1BETA, "v1beta"); + + public static final GoogleThinkProviderVersion GEMINI_ENTERPRISE_AGENT_V1 = + new GoogleThinkProviderVersion(Value.GEMINI_ENTERPRISE_AGENT_V1, "gemini-enterprise-agent-v1"); + + private final Value value; + + private final String string; + + GoogleThinkProviderVersion(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 GoogleThinkProviderVersion + && this.string.equals(((GoogleThinkProviderVersion) other).string)); + } + + @java.lang.Override + public int hashCode() { + return this.string.hashCode(); + } + + public T visit(Visitor visitor) { + switch (value) { + case AI_STUDIO_V1BETA: + return visitor.visitAiStudioV1Beta(); + case V1BETA: + return visitor.visitV1Beta(); + case GEMINI_ENTERPRISE_AGENT_V1: + return visitor.visitGeminiEnterpriseAgentV1(); + case UNKNOWN: + default: + return visitor.visitUnknown(string); + } + } + + @JsonCreator(mode = JsonCreator.Mode.DELEGATING) + public static GoogleThinkProviderVersion valueOf(String value) { + switch (value) { + case "ai-studio-v1beta": + return AI_STUDIO_V1BETA; + case "v1beta": + return V1BETA; + case "gemini-enterprise-agent-v1": + return GEMINI_ENTERPRISE_AGENT_V1; + default: + return new GoogleThinkProviderVersion(Value.UNKNOWN, value); + } + } + + public enum Value { + AI_STUDIO_V1BETA, + + GEMINI_ENTERPRISE_AGENT_V1, + + V1BETA, + + UNKNOWN + } + + public interface Visitor { + T visitAiStudioV1Beta(); + + T visitGeminiEnterpriseAgentV1(); + + T visitV1Beta(); + + T visitUnknown(String unknownType); + } +} diff --git a/src/main/java/com/deepgram/types/GrantV1Response.java b/src/main/java/com/deepgram/types/GrantV1Response.java index f4c886c..968d7b4 100644 --- a/src/main/java/com/deepgram/types/GrantV1Response.java +++ b/src/main/java/com/deepgram/types/GrantV1Response.java @@ -121,7 +121,6 @@ public Builder from(GrantV1Response other) { } /** - *

    JSON Web Token (JWT)

    *

    JSON Web Token (JWT)

    * @return Reference to {@code this} so that method calls can be chained together. */ diff --git a/src/main/java/com/deepgram/types/ListProjectDistributionCredentialsV1ResponseDistributionCredentialsItemDistributionCredentials.java b/src/main/java/com/deepgram/types/ListProjectDistributionCredentialsV1ResponseDistributionCredentialsItemDistributionCredentials.java index 060f413..b12610a 100644 --- a/src/main/java/com/deepgram/types/ListProjectDistributionCredentialsV1ResponseDistributionCredentialsItemDistributionCredentials.java +++ b/src/main/java/com/deepgram/types/ListProjectDistributionCredentialsV1ResponseDistributionCredentialsItemDistributionCredentials.java @@ -211,7 +211,6 @@ public Builder from( } /** - *

    Unique identifier for the distribution credentials

    *

    Unique identifier for the distribution credentials

    * @return Reference to {@code this} so that method calls can be chained together. */ @@ -224,7 +223,6 @@ public ProviderStage distributionCredentialsId(@NotNull String distributionCrede } /** - *

    The provider of the distribution service

    *

    The provider of the distribution service

    * @return Reference to {@code this} so that method calls can be chained together. */ @@ -236,7 +234,6 @@ public CreatedStage provider(@NotNull String provider) { } /** - *

    Timestamp when the credentials were created

    *

    Timestamp when the credentials were created

    * @return Reference to {@code this} so that method calls can be chained together. */ diff --git a/src/main/java/com/deepgram/types/ListProjectDistributionCredentialsV1ResponseDistributionCredentialsItemMember.java b/src/main/java/com/deepgram/types/ListProjectDistributionCredentialsV1ResponseDistributionCredentialsItemMember.java index 9931e31..88f7d86 100644 --- a/src/main/java/com/deepgram/types/ListProjectDistributionCredentialsV1ResponseDistributionCredentialsItemMember.java +++ b/src/main/java/com/deepgram/types/ListProjectDistributionCredentialsV1ResponseDistributionCredentialsItemMember.java @@ -121,7 +121,6 @@ public Builder from(ListProjectDistributionCredentialsV1ResponseDistributionCred } /** - *

    Unique identifier for the member

    *

    Unique identifier for the member

    * @return Reference to {@code this} so that method calls can be chained together. */ @@ -133,7 +132,6 @@ public EmailStage memberId(@NotNull String memberId) { } /** - *

    Email address of the member

    *

    Email address of the member

    * @return Reference to {@code this} so that method calls can be chained together. */ diff --git a/src/main/java/com/deepgram/types/ListenV1AcceptedResponse.java b/src/main/java/com/deepgram/types/ListenV1AcceptedResponse.java index 5d58d50..94bc4fc 100644 --- a/src/main/java/com/deepgram/types/ListenV1AcceptedResponse.java +++ b/src/main/java/com/deepgram/types/ListenV1AcceptedResponse.java @@ -98,7 +98,6 @@ public Builder from(ListenV1AcceptedResponse other) { } /** - *

    Unique identifier for tracking the asynchronous request

    *

    Unique identifier for tracking the asynchronous request

    * @return Reference to {@code this} so that method calls can be chained together. */ diff --git a/src/main/java/com/deepgram/types/ListenV2Redact.java b/src/main/java/com/deepgram/types/ListenV2Redact.java new file mode 100644 index 0000000..ceee795 --- /dev/null +++ b/src/main/java/com/deepgram/types/ListenV2Redact.java @@ -0,0 +1,84 @@ +/** + * 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 ListenV2Redact { + public static final ListenV2Redact NUMBERS = new ListenV2Redact(Value.NUMBERS, "numbers"); + + public static final ListenV2Redact AGGRESSIVE_NUMBERS = + new ListenV2Redact(Value.AGGRESSIVE_NUMBERS, "aggressive_numbers"); + + private final Value value; + + private final String string; + + ListenV2Redact(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 ListenV2Redact && this.string.equals(((ListenV2Redact) other).string)); + } + + @java.lang.Override + public int hashCode() { + return this.string.hashCode(); + } + + public T visit(Visitor visitor) { + switch (value) { + case NUMBERS: + return visitor.visitNumbers(); + case AGGRESSIVE_NUMBERS: + return visitor.visitAggressiveNumbers(); + case UNKNOWN: + default: + return visitor.visitUnknown(string); + } + } + + @JsonCreator(mode = JsonCreator.Mode.DELEGATING) + public static ListenV2Redact valueOf(String value) { + switch (value) { + case "numbers": + return NUMBERS; + case "aggressive_numbers": + return AGGRESSIVE_NUMBERS; + default: + return new ListenV2Redact(Value.UNKNOWN, value); + } + } + + public enum Value { + NUMBERS, + + AGGRESSIVE_NUMBERS, + + UNKNOWN + } + + public interface Visitor { + T visitNumbers(); + + T visitAggressiveNumbers(); + + T visitUnknown(String unknownType); + } +} diff --git a/src/main/java/com/deepgram/types/OpenAiSpeakProvider.java b/src/main/java/com/deepgram/types/OpenAiSpeakProvider.java index 7f6c48f..880da92 100644 --- a/src/main/java/com/deepgram/types/OpenAiSpeakProvider.java +++ b/src/main/java/com/deepgram/types/OpenAiSpeakProvider.java @@ -151,7 +151,6 @@ public Builder from(OpenAiSpeakProvider other) { } /** - *

    OpenAI TTS model

    *

    OpenAI TTS model

    * @return Reference to {@code this} so that method calls can be chained together. */ @@ -163,7 +162,6 @@ public VoiceStage model(@NotNull OpenAiSpeakProviderModel model) { } /** - *

    OpenAI voice

    *

    OpenAI voice

    * @return Reference to {@code this} so that method calls can be chained together. */ diff --git a/src/main/java/com/deepgram/types/OpenAiThinkProvider.java b/src/main/java/com/deepgram/types/OpenAiThinkProvider.java index 18f3122..01e69a4 100644 --- a/src/main/java/com/deepgram/types/OpenAiThinkProvider.java +++ b/src/main/java/com/deepgram/types/OpenAiThinkProvider.java @@ -176,7 +176,6 @@ public Builder from(OpenAiThinkProvider other) { } /** - *

    OpenAI model to use

    *

    OpenAI model to use

    * @return Reference to {@code this} so that method calls can be chained together. */ diff --git a/src/main/java/com/deepgram/types/ReadV1RequestText.java b/src/main/java/com/deepgram/types/ReadV1RequestText.java index 326f080..e9ce4e0 100644 --- a/src/main/java/com/deepgram/types/ReadV1RequestText.java +++ b/src/main/java/com/deepgram/types/ReadV1RequestText.java @@ -98,7 +98,6 @@ public Builder from(ReadV1RequestText other) { } /** - *

    The plain text to analyze

    *

    The plain text to analyze

    * @return Reference to {@code this} so that method calls can be chained together. */ diff --git a/src/main/java/com/deepgram/types/ReadV1RequestUrl.java b/src/main/java/com/deepgram/types/ReadV1RequestUrl.java index c2825b3..1125494 100644 --- a/src/main/java/com/deepgram/types/ReadV1RequestUrl.java +++ b/src/main/java/com/deepgram/types/ReadV1RequestUrl.java @@ -98,7 +98,6 @@ public Builder from(ReadV1RequestUrl other) { } /** - *

    A URL pointing to the text source

    *

    A URL pointing to the text source

    * @return Reference to {@code this} so that method calls can be chained together. */ diff --git a/src/main/java/com/deepgram/types/SpeakV2AcceptedResponse.java b/src/main/java/com/deepgram/types/SpeakV2AcceptedResponse.java index cb5f4bc..c448c83 100644 --- a/src/main/java/com/deepgram/types/SpeakV2AcceptedResponse.java +++ b/src/main/java/com/deepgram/types/SpeakV2AcceptedResponse.java @@ -98,7 +98,6 @@ public Builder from(SpeakV2AcceptedResponse other) { } /** - *

    Unique identifier for tracking the asynchronous request

    *

    Unique identifier for tracking the asynchronous request

    * @return Reference to {@code this} so that method calls can be chained together. */ diff --git a/src/main/java/com/deepgram/types/UsageBreakdownV1Response.java b/src/main/java/com/deepgram/types/UsageBreakdownV1Response.java index 2dec51d..162530a 100644 --- a/src/main/java/com/deepgram/types/UsageBreakdownV1Response.java +++ b/src/main/java/com/deepgram/types/UsageBreakdownV1Response.java @@ -162,7 +162,6 @@ public Builder from(UsageBreakdownV1Response other) { } /** - *

    Start date of the usage period

    *

    Start date of the usage period

    * @return Reference to {@code this} so that method calls can be chained together. */ @@ -174,7 +173,6 @@ public EndStage start(@NotNull String start) { } /** - *

    End date of the usage period

    *

    End date of the usage period

    * @return Reference to {@code this} so that method calls can be chained together. */ diff --git a/src/main/java/com/deepgram/types/UsageBreakdownV1ResponseResolution.java b/src/main/java/com/deepgram/types/UsageBreakdownV1ResponseResolution.java index 5372f22..b0b2de6 100644 --- a/src/main/java/com/deepgram/types/UsageBreakdownV1ResponseResolution.java +++ b/src/main/java/com/deepgram/types/UsageBreakdownV1ResponseResolution.java @@ -120,7 +120,6 @@ public Builder from(UsageBreakdownV1ResponseResolution other) { } /** - *

    Time unit for the resolution

    *

    Time unit for the resolution

    * @return Reference to {@code this} so that method calls can be chained together. */ @@ -132,7 +131,6 @@ public AmountStage units(@NotNull String units) { } /** - *

    Amount of units

    *

    Amount of units

    * @return Reference to {@code this} so that method calls can be chained together. */ diff --git a/src/main/java/com/deepgram/types/UsageBreakdownV1ResponseResultsItem.java b/src/main/java/com/deepgram/types/UsageBreakdownV1ResponseResultsItem.java index e9e9495..3b018a5 100644 --- a/src/main/java/com/deepgram/types/UsageBreakdownV1ResponseResultsItem.java +++ b/src/main/java/com/deepgram/types/UsageBreakdownV1ResponseResultsItem.java @@ -273,7 +273,6 @@ public Builder from(UsageBreakdownV1ResponseResultsItem other) { } /** - *

    Audio hours processed

    *

    Audio hours processed

    * @return Reference to {@code this} so that method calls can be chained together. */ @@ -285,7 +284,6 @@ public TotalHoursStage hours(float hours) { } /** - *

    Total hours including all processing

    *

    Total hours including all processing

    * @return Reference to {@code this} so that method calls can be chained together. */ @@ -297,7 +295,6 @@ public AgentHoursStage totalHours(float totalHours) { } /** - *

    Agent hours used

    *

    Agent hours used

    * @return Reference to {@code this} so that method calls can be chained together. */ @@ -309,7 +306,6 @@ public TokensInStage agentHours(float agentHours) { } /** - *

    Number of input tokens

    *

    Number of input tokens

    * @return Reference to {@code this} so that method calls can be chained together. */ @@ -321,7 +317,6 @@ public TokensOutStage tokensIn(double tokensIn) { } /** - *

    Number of output tokens

    *

    Number of output tokens

    * @return Reference to {@code this} so that method calls can be chained together. */ @@ -333,7 +328,6 @@ public TtsCharactersStage tokensOut(double tokensOut) { } /** - *

    Number of text-to-speech characters processed

    *

    Number of text-to-speech characters processed

    * @return Reference to {@code this} so that method calls can be chained together. */ @@ -345,7 +339,6 @@ public RequestsStage ttsCharacters(double ttsCharacters) { } /** - *

    Number of requests

    *

    Number of requests

    * @return Reference to {@code this} so that method calls can be chained together. */ From ad5e57cb4d74b2f8ea6b03ce433bf8de8c2122b7 Mon Sep 17 00:00:00 2001 From: Greg Holmes Date: Tue, 11 Aug 2026 14:26:20 +0100 Subject: [PATCH 04/14] chore: re-apply manual patches after regen --- .fernignore | 34 +- .../java/com/deepgram/core/ClientOptions.java | 9 +- .../com/deepgram/core/ClientOptions.java.bak | 241 -------- .../core/ReconnectingWebSocketListener.java | 93 ++- .../ReconnectingWebSocketListener.java.bak | 545 ----------------- .../agent/v1/types/AgentV1AgentAudioDone.java | 8 + .../v1/types/AgentV1AgentAudioDone.java.bak | 86 --- .../agent/v1/types/AgentV1KeepAlive.java | 8 + .../agent/v1/types/AgentV1KeepAlive.java.bak | 86 --- .../agent/v1/types/AgentV1ListenUpdated.java | 8 + .../v1/types/AgentV1ListenUpdated.java.bak | 86 --- .../agent/v1/types/AgentV1PromptUpdated.java | 8 + .../v1/types/AgentV1PromptUpdated.java.bak | 86 --- .../v1/types/AgentV1SettingsApplied.java | 8 + .../v1/types/AgentV1SettingsApplied.java.bak | 86 --- .../agent/v1/types/AgentV1SpeakUpdated.java | 8 + .../v1/types/AgentV1SpeakUpdated.java.bak | 86 --- .../agent/v1/types/AgentV1ThinkUpdated.java | 8 + .../v1/types/AgentV1ThinkUpdated.java.bak | 86 --- .../v1/types/AgentV1UserStartedSpeaking.java | 8 + .../types/AgentV1UserStartedSpeaking.java.bak | 86 --- .../v1/websocket/V1WebSocketClient.java | 39 +- .../v1/websocket/V1WebSocketClient.java.bak | 555 ------------------ .../listen/v2/types/ListenV2CloseStream.java | 8 + .../v2/types/ListenV2CloseStream.java.bak | 86 --- .../v2/websocket/V2WebSocketClient.java | 37 +- .../v2/websocket/V2WebSocketClient.java.bak | 504 ---------------- .../speak/v1/websocket/V1WebSocketClient.java | 12 + .../v1/websocket/V1WebSocketClient.java.bak | 458 --------------- .../speak/v2/types/SpeakV2Close.java | 8 + .../speak/v2/types/SpeakV2Close.java.bak | 86 --- .../speak/v2/types/SpeakV2Flush.java | 8 + .../speak/v2/types/SpeakV2Flush.java.bak | 86 --- .../speak/v2/websocket/V2WebSocketClient.java | 29 +- .../v2/websocket/V2WebSocketClient.java.bak | 524 ----------------- 35 files changed, 269 insertions(+), 3845 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/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/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/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/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 diff --git a/.fernignore b/.fernignore index a6fd951..ce1762c 100644 --- a/.fernignore +++ b/.fernignore @@ -14,7 +14,7 @@ src/main/java/com/deepgram/AsyncDeepgramClientBuilder.java # Contains User-Agent, X-Fern-SDK-Name, and X-Fern-SDK-Version headers # with // x-release-please-version comments for automated version bumps. # Fern regen overwrites these with incorrect SDK names and strips the markers. -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/ @@ -22,7 +22,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 @@ -32,8 +32,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, @@ -47,24 +47,24 @@ 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 # 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/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/listen/v2/types/ListenV2CloseStream.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 # 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 e2a76ff..c6e3383 100644 --- a/src/main/java/com/deepgram/core/ClientOptions.java +++ b/src/main/java/com/deepgram/core/ClientOptions.java @@ -50,10 +50,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.7.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.7.1"); // x-release-please-version } }); this.headerSuppliers = headerSuppliers; @@ -82,11 +82,6 @@ public Map headers(RequestOptions requestOptions) { return values; } - private static String getSdkVersion() { - String version = ClientOptions.class.getPackage().getImplementationVersion(); - return version != null ? version : "0.7.2"; - } - 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 021caba..0000000 --- a/src/main/java/com/deepgram/core/ClientOptions.java.bak +++ /dev/null @@ -1,241 +0,0 @@ -/** - * This file was auto-generated by Fern from our API Definition. - */ -package com.deepgram.core; - -import java.util.HashMap; -import java.util.Map; -import java.util.Optional; -import java.util.concurrent.TimeUnit; -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 int timeout; - - private final int maxRetries; - - private final Optional webSocketFactory; - - private final Optional logging; - - private ClientOptions( - Environment environment, - Map headers, - Map> headerSuppliers, - OkHttpClient httpClient, - int timeout, - int maxRetries, - 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.7.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", "0.7.1"); // x-release-please-version - } - }); - this.headerSuppliers = headerSuppliers; - this.httpClient = httpClient; - this.timeout = timeout; - this.maxRetries = maxRetries; - 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 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 timeout = Optional.empty(); - - private OkHttpClient httpClient = null; - - 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) { - 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; - } - - public Builder httpClient(OkHttpClient httpClient) { - this.httpClient = httpClient; - 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)); - } - - Logger logger = Logger.from(this.logging); - httpClientBuilder.addInterceptor(new LoggingInterceptor(logger)); - - this.httpClient = httpClientBuilder.build(); - this.timeout = Optional.of(httpClient.callTimeoutMillis() / 1000); - - return new ClientOptions( - environment, - headers, - headerSuppliers, - httpClient, - this.timeout.get(), - this.maxRetries, - 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.headers.putAll(clientOptions.headers); - builder.headerSuppliers.putAll(clientOptions.headerSuppliers); - builder.maxRetries = clientOptions.maxRetries(); - 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 0ca455a..a533068 100644 --- a/src/main/java/com/deepgram/core/ReconnectingWebSocketListener.java +++ b/src/main/java/com/deepgram/core/ReconnectingWebSocketListener.java @@ -25,14 +25,15 @@ * 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; - + // Overridable options are held behind a single volatile reference (not five separate volatile + // fields) so {@link #applyOptionsOverride} swaps them atomically — a reader that snapshots the + // reference once sees a mutually-consistent set (e.g. getNextDelay() cannot observe a new min + // paired with an old max). Rewiring is used by {@code TransportWebSocketFactory} to honour + // {@code DeepgramTransportFactory.reconnectOptions()} without editing the generated WS clients. + private volatile ReconnectOptions activeOptions; + + // maxEnqueuedMessages is fixed at construction (the queue is sized once) and intentionally not + // overridable, so it stays a separate final field rather than being read from activeOptions. private final int maxEnqueuedMessages; private final AtomicInteger retryCount = new AtomicInteger(0); @@ -61,21 +62,42 @@ public abstract class ReconnectingWebSocketListener extends WebSocketListener { */ public ReconnectingWebSocketListener( ReconnectingWebSocketListener.ReconnectOptions options, Supplier connectionSupplier) { - 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; } + /** + * Replaces the option-derived parameters on this listener at runtime. Used by + * {@code TransportWebSocketFactory} to apply {@code DeepgramTransportFactory.reconnectOptions()} + * without requiring edits to the generated per-resource WebSocket clients. {@code maxEnqueuedMessages} + * is intentionally not overridden — the message queue is sized at construction. + * + *

    Thread-safety: the override is a single atomic write to a {@code volatile} reference, so a + * reader that snapshots {@link #activeOptions} once sees a mutually-consistent set of values + * (never a new min paired with an old max). The initial connect() call may have already started + * before the override lands, so for the very first attempt the original options apply; the + * override takes effect from the next attempt onwards. For the SageMaker storm-suppression case + * ({@code maxRetries(0)}) this is fine because the initial attempt's gate + * ({@code retryCount > maxRetries} with {@code retryCount=0}) always passes regardless. + * + * @param options replacement options; {@code null} is a no-op. + */ + public void applyOptionsOverride(ReconnectOptions options) { + if (options == null) { + return; + } + 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 — the initial attempt always proceeds. + * {@code maxRetries(0)} means "connect once, don't retry" (not "refuse to connect"). * * Error handling: * - TimeoutException: Includes retry attempt context @@ -86,20 +108,26 @@ public void connect() { if (!connectLock.compareAndSet(false, true)) { return; } - if (retryCount.get() >= maxRetries) { + // Snapshot the overridable options once so the gate and timeout below use a consistent set. + ReconnectOptions opts = this.activeOptions; + // retryCount is incremented inside scheduleReconnect() before re-entering connect(), + // so on the initial call retryCount == 0 and we always proceed. The cap applies to + // retries only — maxRetries(0) blocks retries but allows the initial attempt. + if (retryCount.get() > opts.maxRetries) { connectLock.set(false); return; } try { CompletableFuture connectionFuture = CompletableFuture.supplyAsync(connectionSupplier); try { - webSocket = connectionFuture.get(4000, MILLISECONDS); + webSocket = connectionFuture.get(opts.connectionTimeoutMs, MILLISECONDS); } catch (TimeoutException e) { connectionFuture.cancel(true); TimeoutException timeoutError = - new TimeoutException("WebSocket connection timeout after " + 4000 + " milliseconds" + new TimeoutException("WebSocket connection timeout after " + opts.connectionTimeoutMs + + " milliseconds" + (retryCount.get() > 0 - ? " (retry attempt #" + retryCount.get() + ? " (retry attempt #" + retryCount.get() + ")" : " (initial connection attempt)")); onWebSocketFailure(null, timeoutError, null); if (shouldReconnect.get()) { @@ -314,11 +342,14 @@ public void onClosed(WebSocket webSocket, int code, String reason) { * - 2+ = exponential backoff up to maxReconnectionDelayMs */ private long getNextDelay() { + // Single volatile read → a consistent (min, growFactor, max) snapshot even if + // applyOptionsOverride swaps the options concurrently. + ReconnectOptions opts = this.activeOptions; if (retryCount.get() == 1) { - return minReconnectionDelayMs; + return opts.minReconnectionDelayMs; } - long delay = (long) (minReconnectionDelayMs * Math.pow(reconnectionDelayGrowFactor, retryCount.get() - 1)); - return Math.min(delay, maxReconnectionDelayMs); + long delay = (long) (opts.minReconnectionDelayMs * Math.pow(opts.reconnectionDelayGrowFactor, retryCount.get() - 1)); + return Math.min(delay, opts.maxReconnectionDelayMs); } /** @@ -399,12 +430,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() { @@ -422,12 +456,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) { @@ -455,6 +492,16 @@ public Builder maxEnqueuedMessages(int maxEnqueuedMessages) { return this; } + /** + * Sets the per-attempt connection timeout in milliseconds. Defaults to {@code 4000}. + * Each call to {@link ReconnectingWebSocketListener#connect()} will wait at most + * this long for the underlying WebSocket factory to produce a connected socket. + */ + public Builder connectionTimeoutMs(long connectionTimeoutMs) { + this.connectionTimeoutMs = connectionTimeoutMs; + return this; + } + /** * Builds the ReconnectOptions with validation. * @@ -463,6 +510,7 @@ public Builder maxEnqueuedMessages(int maxEnqueuedMessages) { * - minReconnectionDelayMs <= maxReconnectionDelayMs * - reconnectionDelayGrowFactor >= 1.0 * - maxRetries and maxEnqueuedMessages are non-negative + * - connectionTimeoutMs is positive * * @return The validated ReconnectOptions instance * @throws IllegalArgumentException if configuration is invalid @@ -487,6 +535,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 a533068..0000000 --- a/src/main/java/com/deepgram/core/ReconnectingWebSocketListener.java.bak +++ /dev/null @@ -1,545 +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.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 { - // Overridable options are held behind a single volatile reference (not five separate volatile - // fields) so {@link #applyOptionsOverride} swaps them atomically — a reader that snapshots the - // reference once sees a mutually-consistent set (e.g. getNextDelay() cannot observe a new min - // paired with an old max). Rewiring is used by {@code TransportWebSocketFactory} to honour - // {@code DeepgramTransportFactory.reconnectOptions()} without editing the generated WS clients. - private volatile ReconnectOptions activeOptions; - - // maxEnqueuedMessages is fixed at construction (the queue is sized once) and intentionally not - // overridable, so it stays a separate final field rather than being read from 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; - - /** - * 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.activeOptions = options; - this.maxEnqueuedMessages = options.maxEnqueuedMessages; - this.connectionSupplier = connectionSupplier; - } - - /** - * Replaces the option-derived parameters on this listener at runtime. Used by - * {@code TransportWebSocketFactory} to apply {@code DeepgramTransportFactory.reconnectOptions()} - * without requiring edits to the generated per-resource WebSocket clients. {@code maxEnqueuedMessages} - * is intentionally not overridden — the message queue is sized at construction. - * - *

    Thread-safety: the override is a single atomic write to a {@code volatile} reference, so a - * reader that snapshots {@link #activeOptions} once sees a mutually-consistent set of values - * (never a new min paired with an old max). The initial connect() call may have already started - * before the override lands, so for the very first attempt the original options apply; the - * override takes effect from the next attempt onwards. For the SageMaker storm-suppression case - * ({@code maxRetries(0)}) this is fine because the initial attempt's gate - * ({@code retryCount > maxRetries} with {@code retryCount=0}) always passes regardless. - * - * @param options replacement options; {@code null} is a no-op. - */ - public void applyOptionsOverride(ReconnectOptions options) { - if (options == null) { - return; - } - 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 — the initial attempt always proceeds. - * {@code maxRetries(0)} means "connect once, don't retry" (not "refuse to connect"). - * - * Error handling: - * - TimeoutException: Includes retry attempt context - * - InterruptedException: Preserves thread interruption status - * - ExecutionException: Extracts actual cause and adds context - */ - public void connect() { - if (!connectLock.compareAndSet(false, true)) { - return; - } - // Snapshot the overridable options once so the gate and timeout below use a consistent set. - ReconnectOptions opts = this.activeOptions; - // retryCount is incremented inside scheduleReconnect() before re-entering connect(), - // so on the initial call retryCount == 0 and we always proceed. The cap applies to - // retries only — maxRetries(0) blocks retries but allows the initial attempt. - if (retryCount.get() > opts.maxRetries) { - connectLock.set(false); - return; - } - try { - CompletableFuture connectionFuture = CompletableFuture.supplyAsync(connectionSupplier); - try { - webSocket = connectionFuture.get(opts.connectionTimeoutMs, MILLISECONDS); - } catch (TimeoutException e) { - connectionFuture.cancel(true); - TimeoutException timeoutError = - new TimeoutException("WebSocket connection timeout after " + opts.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) { - WebSocket ws = webSocket; - if (ws != null) { - boolean sent = ws.send(message); - 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) { - WebSocket ws = webSocket; - if (ws != null) { - boolean sent = ws.send(data); - 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(); - } - } - - /** - * @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 (code != 1000 && shouldReconnect.get()) { - scheduleReconnect(); - } - } - - /** - * 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() { - // Single volatile read → a consistent (min, growFactor, max) snapshot even if - // applyOptionsOverride swaps the options concurrently. - ReconnectOptions opts = this.activeOptions; - if (retryCount.get() == 1) { - return opts.minReconnectionDelayMs; - } - long delay = (long) (opts.minReconnectionDelayMs * Math.pow(opts.reconnectionDelayGrowFactor, retryCount.get() - 1)); - return Math.min(delay, opts.maxReconnectionDelayMs); - } - - /** - * Schedules a reconnection attempt with appropriate delay. - * Increments retry count and uses exponential backoff. - */ - private void scheduleReconnect() { - 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}. - * Each call to {@link ReconnectingWebSocketListener#connect()} will wait at most - * this long for the underlying WebSocket factory to produce a connected socket. - */ - 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 - * - connectionTimeoutMs is positive - * - * @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..41af522 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,14 @@ public boolean equals(Object other) { return other instanceof AgentV1AgentAudioDone; } + @java.lang.Override + public int hashCode() { + // Manual patch: Fern generates equals() (all instances of this fields-less message + // are equal) but no hashCode(), violating the Object contract. Mirror equals() with a + // type-based constant hash. Remove once Fern emits a consistent equals/hashCode pair. + 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 41af522..0000000 --- a/src/main/java/com/deepgram/resources/agent/v1/types/AgentV1AgentAudioDone.java.bak +++ /dev/null @@ -1,86 +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() { - // Manual patch: Fern generates equals() (all instances of this fields-less message - // are equal) but no hashCode(), violating the Object contract. Mirror equals() with a - // type-based constant hash. Remove once Fern emits a consistent equals/hashCode pair. - 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/AgentV1KeepAlive.java b/src/main/java/com/deepgram/resources/agent/v1/types/AgentV1KeepAlive.java index 7842266..083578b 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,14 @@ public boolean equals(Object other) { return other instanceof AgentV1KeepAlive; } + @java.lang.Override + public int hashCode() { + // Manual patch: Fern generates equals() (all instances of this fields-less message + // are equal) but no hashCode(), violating the Object contract. Mirror equals() with a + // type-based constant hash. Remove once Fern emits a consistent equals/hashCode pair. + 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 083578b..0000000 --- a/src/main/java/com/deepgram/resources/agent/v1/types/AgentV1KeepAlive.java.bak +++ /dev/null @@ -1,86 +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() { - // Manual patch: Fern generates equals() (all instances of this fields-less message - // are equal) but no hashCode(), violating the Object contract. Mirror equals() with a - // type-based constant hash. Remove once Fern emits a consistent equals/hashCode pair. - 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..6018473 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,14 @@ public boolean equals(Object other) { return other instanceof AgentV1ListenUpdated; } + @java.lang.Override + public int hashCode() { + // Manual patch: Fern generates equals() (all instances of this fields-less message + // are equal) but no hashCode(), violating the Object contract. Mirror equals() with a + // type-based constant hash. Remove once Fern emits a consistent equals/hashCode pair. + 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 6018473..0000000 --- a/src/main/java/com/deepgram/resources/agent/v1/types/AgentV1ListenUpdated.java.bak +++ /dev/null @@ -1,86 +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() { - // Manual patch: Fern generates equals() (all instances of this fields-less message - // are equal) but no hashCode(), violating the Object contract. Mirror equals() with a - // type-based constant hash. Remove once Fern emits a consistent equals/hashCode pair. - 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..6edec37 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,14 @@ public boolean equals(Object other) { return other instanceof AgentV1PromptUpdated; } + @java.lang.Override + public int hashCode() { + // Manual patch: Fern generates equals() (all instances of this fields-less message + // are equal) but no hashCode(), violating the Object contract. Mirror equals() with a + // type-based constant hash. Remove once Fern emits a consistent equals/hashCode pair. + 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 6edec37..0000000 --- a/src/main/java/com/deepgram/resources/agent/v1/types/AgentV1PromptUpdated.java.bak +++ /dev/null @@ -1,86 +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() { - // Manual patch: Fern generates equals() (all instances of this fields-less message - // are equal) but no hashCode(), violating the Object contract. Mirror equals() with a - // type-based constant hash. Remove once Fern emits a consistent equals/hashCode pair. - 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/AgentV1SettingsApplied.java b/src/main/java/com/deepgram/resources/agent/v1/types/AgentV1SettingsApplied.java index 0d60b72..fb23c60 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,14 @@ public boolean equals(Object other) { return other instanceof AgentV1SettingsApplied; } + @java.lang.Override + public int hashCode() { + // Manual patch: Fern generates equals() (all instances of this fields-less message + // are equal) but no hashCode(), violating the Object contract. Mirror equals() with a + // type-based constant hash. Remove once Fern emits a consistent equals/hashCode pair. + 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 fb23c60..0000000 --- a/src/main/java/com/deepgram/resources/agent/v1/types/AgentV1SettingsApplied.java.bak +++ /dev/null @@ -1,86 +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() { - // Manual patch: Fern generates equals() (all instances of this fields-less message - // are equal) but no hashCode(), violating the Object contract. Mirror equals() with a - // type-based constant hash. Remove once Fern emits a consistent equals/hashCode pair. - 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..7619157 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,14 @@ public boolean equals(Object other) { return other instanceof AgentV1SpeakUpdated; } + @java.lang.Override + public int hashCode() { + // Manual patch: Fern generates equals() (all instances of this fields-less message + // are equal) but no hashCode(), violating the Object contract. Mirror equals() with a + // type-based constant hash. Remove once Fern emits a consistent equals/hashCode pair. + 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 7619157..0000000 --- a/src/main/java/com/deepgram/resources/agent/v1/types/AgentV1SpeakUpdated.java.bak +++ /dev/null @@ -1,86 +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() { - // Manual patch: Fern generates equals() (all instances of this fields-less message - // are equal) but no hashCode(), violating the Object contract. Mirror equals() with a - // type-based constant hash. Remove once Fern emits a consistent equals/hashCode pair. - 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..2f2a981 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,14 @@ public boolean equals(Object other) { return other instanceof AgentV1ThinkUpdated; } + @java.lang.Override + public int hashCode() { + // Manual patch: Fern generates equals() (all instances of this fields-less message + // are equal) but no hashCode(), violating the Object contract. Mirror equals() with a + // type-based constant hash. Remove once Fern emits a consistent equals/hashCode pair. + 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 2f2a981..0000000 --- a/src/main/java/com/deepgram/resources/agent/v1/types/AgentV1ThinkUpdated.java.bak +++ /dev/null @@ -1,86 +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() { - // Manual patch: Fern generates equals() (all instances of this fields-less message - // are equal) but no hashCode(), violating the Object contract. Mirror equals() with a - // type-based constant hash. Remove once Fern emits a consistent equals/hashCode pair. - 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/AgentV1UserStartedSpeaking.java b/src/main/java/com/deepgram/resources/agent/v1/types/AgentV1UserStartedSpeaking.java index edb80f8..7c3fc38 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,14 @@ public boolean equals(Object other) { return other instanceof AgentV1UserStartedSpeaking; } + @java.lang.Override + public int hashCode() { + // Manual patch: Fern generates equals() (all instances of this fields-less message + // are equal) but no hashCode(), violating the Object contract. Mirror equals() with a + // type-based constant hash. Remove once Fern emits a consistent equals/hashCode pair. + 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 7c3fc38..0000000 --- a/src/main/java/com/deepgram/resources/agent/v1/types/AgentV1UserStartedSpeaking.java.bak +++ /dev/null @@ -1,86 +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() { - // Manual patch: Fern generates equals() (all instances of this fields-less message - // are equal) but no hashCode(), violating the Object contract. Mirror equals() with a - // type-based constant hash. Remove once Fern emits a consistent equals/hashCode pair. - 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 4952808..ffa4953 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; @@ -138,8 +139,12 @@ 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())); + // Array-valued query params (String | List unions) must serialize as repeated + // params (extra=a&extra=b), not a stringified list. The generated streaming template + // uses String.valueOf(...), which mangles a List into "[a, b]"; route these through + // QueryStringMapper (arraysAsRepeats=true) so the wire format matches the REST path. + QueryStringMapper.addQueryParameter( + urlBuilder, "extra", options.getExtra().get().get(), true); } if (options.getInterimResults() != null && options.getInterimResults().isPresent()) { urlBuilder.addQueryParameter( @@ -147,12 +152,12 @@ 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( @@ -185,23 +190,24 @@ 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( @@ -216,6 +222,17 @@ public CompletableFuture connect(V1ConnectOptions options) { urlBuilder.addQueryParameter( "version", String.valueOf(options.getVersion().get())); } + // Escape hatch: emit caller-supplied additionalProperties (e.g. no_delay) as query params. + // The generated template only serializes the typed options and drops these otherwise. + // ConnectOptions is request-only (never deserialized), so this map holds only what the + // caller set via the builder. Routed through QueryStringMapper to match the REST path. + 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 ffa4953..0000000 --- a/src/main/java/com/deepgram/resources/listen/v1/websocket/V1WebSocketClient.java.bak +++ /dev/null @@ -1,555 +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()) { - // Array-valued query params (String | List unions) must serialize as repeated - // params (extra=a&extra=b), not a stringified list. The generated streaming template - // uses String.valueOf(...), which mangles a List into "[a, b]"; route these through - // QueryStringMapper (arraysAsRepeats=true) so the wire format matches the REST path. - 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())); - } - // Escape hatch: emit caller-supplied additionalProperties (e.g. no_delay) as query params. - // The generated template only serializes the typed options and drops these otherwise. - // ConnectOptions is request-only (never deserialized), so this map holds only what the - // caller set via the builder. Routed through QueryStringMapper to match the REST path. - 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.webSocketFactory().isPresent()) { - return clientOptions.webSocketFactory().get().create(request, this.reconnectingListener); - } else { - return okHttpClient.newWebSocket(request, this.reconnectingListener); - } - }) { - @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)); - } - } - }; - reconnectingListener.connect(); - return connectionFuture; - } - - /** - * Disconnects the WebSocket connection and releases resources. - */ - public void disconnect() { - 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..e508681 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,14 @@ public boolean equals(Object other) { return other instanceof ListenV2CloseStream; } + @java.lang.Override + public int hashCode() { + // Manual patch: Fern generates equals() (all instances of this fields-less message + // are equal) but no hashCode(), violating the Object contract. Mirror equals() with a + // type-based constant hash. Remove once Fern emits a consistent equals/hashCode pair. + 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 e508681..0000000 --- a/src/main/java/com/deepgram/resources/listen/v2/types/ListenV2CloseStream.java.bak +++ /dev/null @@ -1,86 +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() { - // Manual patch: Fern generates equals() (all instances of this fields-less message - // are equal) but no hashCode(), violating the Object contract. Mirror equals() with a - // type-based constant hash. Remove once Fern emits a consistent equals/hashCode pair. - 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/websocket/V2WebSocketClient.java b/src/main/java/com/deepgram/resources/listen/v2/websocket/V2WebSocketClient.java index 0d07ce5..63f44e4 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; @@ -125,12 +126,16 @@ 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())); + // Array-valued query params (String | List unions) must serialize as repeated + // params (keyterm=a&keyterm=b), not a stringified list. The generated streaming template + // uses String.valueOf(...), which mangles a List into "[a, b]"; route these through + // QueryStringMapper (arraysAsRepeats=true) so the wire format matches the REST path. + 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( @@ -150,7 +155,19 @@ 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); + } + // Escape hatch: emit caller-supplied additionalProperties (e.g. no_delay) as query params. + // The generated template only serializes the typed options and drops these otherwise. + // ConnectOptions is request-only (never deserialized), so this map holds only what the + // caller set via the builder. Routed through QueryStringMapper to match the REST path. + 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); @@ -477,11 +494,11 @@ 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.")); - } + // Unrecognized message type: forward-compatible no-op. The raw frame was + // already delivered to onMessage(String) above, so a newer server adding a + // benign control frame (e.g. a GA addition to this endpoint) must not surface + // as a fatal error to deployed clients. Routing it to onError would make a + // harmless frame look fatal; mirrors the speak v2 client and the JS/Python SDKs. } 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 f47ee22..0000000 --- a/src/main/java/com/deepgram/resources/listen/v2/websocket/V2WebSocketClient.java.bak +++ /dev/null @@ -1,504 +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.ListenV2TurnInfo; -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 connectedHandler; - - private volatile Consumer turnInfoHandler; - - private volatile Consumer configureSuccessHandler; - - private volatile Consumer configureFailureHandler; - - 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/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()) { - // Array-valued query params (String | List unions) must serialize as repeated - // params (keyterm=a&keyterm=b), not a stringified list. The generated streaming template - // uses String.valueOf(...), which mangles a List into "[a, b]"; route these through - // QueryStringMapper (arraysAsRepeats=true) so the wire format matches the REST path. - 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.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); - } - // Escape hatch: emit caller-supplied additionalProperties (e.g. no_delay) as query params. - // The generated template only serializes the typed options and drops these otherwise. - // ConnectOptions is request-only (never deserialized), so this map holds only what the - // caller set via the builder. Routed through QueryStringMapper to match the REST path. - 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.webSocketFactory().isPresent()) { - return clientOptions.webSocketFactory().get().create(request, this.reconnectingListener); - } else { - return okHttpClient.newWebSocket(request, this.reconnectingListener); - } - }) { - @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)); - } - } - }; - reconnectingListener.connect(); - return connectionFuture; - } - - /** - * Disconnects the WebSocket connection and releases resources. - */ - public void disconnect() { - 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. - * @param message the message to send - * @return a CompletableFuture that completes when the message is sent - */ - public CompletableFuture sendCloseStream(ListenV2CloseStream 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 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) { - 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("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("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; - } - } - // Unrecognized message type: forward-compatible no-op. The raw frame was - // already delivered to onMessage(String) above, so a newer server adding a - // benign control frame (e.g. a GA addition to this endpoint) must not surface - // as a fatal error to deployed clients. Routing it to onError would make a - // harmless frame look fatal; mirrors the speak v2 client and the JS/Python SDKs. - } 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 8af4275..8033a55 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,17 @@ public CompletableFuture connect(V1ConnectOptions options) { urlBuilder.addQueryParameter( "speed", String.valueOf(options.getSpeed().get())); } + // Escape hatch: emit caller-supplied additionalProperties (e.g. no_delay) as query params. + // The generated template only serializes the typed options and drops these otherwise. + // ConnectOptions is request-only (never deserialized), so this map holds only what the + // caller set via the builder. Routed through QueryStringMapper to match the REST path. + 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 8033a55..0000000 --- a/src/main/java/com/deepgram/resources/speak/v1/websocket/V1WebSocketClient.java.bak +++ /dev/null @@ -1,458 +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())); - } - // Escape hatch: emit caller-supplied additionalProperties (e.g. no_delay) as query params. - // The generated template only serializes the typed options and drops these otherwise. - // ConnectOptions is request-only (never deserialized), so this map holds only what the - // caller set via the builder. Routed through QueryStringMapper to match the REST path. - 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.webSocketFactory().isPresent()) { - return clientOptions.webSocketFactory().get().create(request, this.reconnectingListener); - } else { - return okHttpClient.newWebSocket(request, this.reconnectingListener); - } - }) { - @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)); - } - } - }; - 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() { - 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..51fef19 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,14 @@ public boolean equals(Object other) { return other instanceof SpeakV2Close; } + @java.lang.Override + public int hashCode() { + // Manual patch: Fern generates equals() (all instances of this fields-less message + // are equal) but no hashCode(), violating the Object contract. Mirror equals() with a + // type-based constant hash. Remove once Fern emits a consistent equals/hashCode pair. + 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 51fef19..0000000 --- a/src/main/java/com/deepgram/resources/speak/v2/types/SpeakV2Close.java.bak +++ /dev/null @@ -1,86 +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() { - // Manual patch: Fern generates equals() (all instances of this fields-less message - // are equal) but no hashCode(), violating the Object contract. Mirror equals() with a - // type-based constant hash. Remove once Fern emits a consistent equals/hashCode pair. - 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..e122400 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,14 @@ public boolean equals(Object other) { return other instanceof SpeakV2Flush; } + @java.lang.Override + public int hashCode() { + // Manual patch: Fern generates equals() (all instances of this fields-less message + // are equal) but no hashCode(), violating the Object contract. Mirror equals() with a + // type-based constant hash. Remove once Fern emits a consistent equals/hashCode pair. + 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 e122400..0000000 --- a/src/main/java/com/deepgram/resources/speak/v2/types/SpeakV2Flush.java.bak +++ /dev/null @@ -1,86 +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() { - // Manual patch: Fern generates equals() (all instances of this fields-less message - // are equal) but no hashCode(), violating the Object contract. Mirror equals() with a - // type-based constant hash. Remove once Fern emits a consistent equals/hashCode pair. - 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 3e032b1..2a8dc12 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,23 @@ 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())); + // Array-valued query params (String | List unions) must serialize as repeated + // params (tag=a&tag=b), not a stringified list. The generated streaming template uses + // String.valueOf(...), which mangles a List into "[a, b]"; route through + // QueryStringMapper (arraysAsRepeats=true) so the wire format matches the REST path. + QueryStringMapper.addQueryParameter( + urlBuilder, "tag", options.getTag().get().get(), true); + } + // Escape hatch: emit caller-supplied additionalProperties (e.g. no_delay) as query params. + // The generated template only serializes the typed options and drops these otherwise. + // ConnectOptions is request-only (never deserialized), so this map holds only what the + // caller set via the builder. Routed through QueryStringMapper to match the REST path. + 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); @@ -598,11 +615,11 @@ 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.")); - } + // Unrecognized message type: forward-compatible no-op. The raw frame was + // already delivered to onMessage(String) above, so a newer server adding a + // benign control frame (e.g. a GA addition to this endpoint) must not surface + // as a fatal error to deployed clients. Routing it to onError would make a + // harmless frame look fatal to a voice agent; mirrors the JS/Python SDKs. } 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 44426c4..0000000 --- a/src/main/java/com/deepgram/resources/speak/v2/websocket/V2WebSocketClient.java.bak +++ /dev/null @@ -1,524 +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.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.SpeakV2SessionMetadata; -import com.deepgram.resources.speak.v2.types.SpeakV2Speak; -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 flushedHandler; - - private volatile Consumer sessionMetadataHandler; - - 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.getMipOptOut() != null && options.getMipOptOut().isPresent()) { - urlBuilder.addQueryParameter( - "mip_opt_out", String.valueOf(options.getMipOptOut().get())); - } - if (options.getTag() != null && options.getTag().isPresent()) { - // Array-valued query params (String | List unions) must serialize as repeated - // params (tag=a&tag=b), not a stringified list. The generated streaming template uses - // String.valueOf(...), which mangles a List into "[a, b]"; route through - // QueryStringMapper (arraysAsRepeats=true) so the wire format matches the REST path. - QueryStringMapper.addQueryParameter( - urlBuilder, "tag", options.getTag().get().get(), true); - } - // Escape hatch: emit caller-supplied additionalProperties (e.g. no_delay) as query params. - // The generated template only serializes the typed options and drops these otherwise. - // ConnectOptions is request-only (never deserialized), so this map holds only what the - // caller set via the builder. Routed through QueryStringMapper to match the REST path. - 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.webSocketFactory().isPresent()) { - return clientOptions.webSocketFactory().get().create(request, this.reconnectingListener); - } else { - return okHttpClient.newWebSocket(request, this.reconnectingListener); - } - }) { - @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)); - } - } - }; - reconnectingListener.connect(); - return connectionFuture; - } - - /** - * Disconnects the WebSocket connection and releases resources. - */ - public void disconnect() { - 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 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 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 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("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; - } - } - // Unrecognized message type: forward-compatible no-op. The raw frame was - // already delivered to onMessage(String) above, so a newer server adding a - // benign control frame (e.g. a GA addition to this endpoint) must not surface - // as a fatal error to deployed clients. Routing it to onError would make a - // harmless frame look fatal to a voice agent; mirrors the JS/Python SDKs. - } catch (Exception e) { - if (onErrorHandler != null) { - onErrorHandler.accept(e); - } - } - } -} From 00658c1e89dfe70f1e9e76faaa055b5de95b3c1b Mon Sep 17 00:00:00 2001 From: Greg Holmes Date: Tue, 11 Aug 2026 14:48:45 +0100 Subject: [PATCH 05/14] docs: add v0.7 to v0.8 migration guide for breaking regen changes --- README.md | 3 +- docs/Migrating-v0.7-to-v0.8.md | 226 +++++++++++++++++++++++++++++++++ 2 files changed, 228 insertions(+), 1 deletion(-) create mode 100644 docs/Migrating-v0.7-to-v0.8.md diff --git a/README.md b/README.md index b140c8c..8ea4c38 100644 --- a/README.md +++ b/README.md @@ -16,7 +16,8 @@ You can learn more about the Deepgram API at [developers.deepgram.com](https://d ### Migrating Between Versions -- [v0.6 to v0.7](./docs/Migrating-v0.6-to-v0.7.md) (current) +- [v0.7 to v0.8](./docs/Migrating-v0.7-to-v0.8.md) (current) +- [v0.6 to v0.7](./docs/Migrating-v0.6-to-v0.7.md) - [v0.5 to v0.6](./docs/Migrating-v0.5-to-v0.6.md) - [v0.3 to v0.4](./docs/Migrating-v0.3-to-v0.4.md) - [v0.2 to v0.3](./docs/Migrating-v0.2-to-v0.3.md) diff --git a/docs/Migrating-v0.7-to-v0.8.md b/docs/Migrating-v0.7-to-v0.8.md new file mode 100644 index 0000000..e1f1a19 --- /dev/null +++ b/docs/Migrating-v0.7-to-v0.8.md @@ -0,0 +1,226 @@ +# v0.7 to v0.8 Migration Guide + +This guide helps you migrate from Deepgram Java SDK v0.7.x to v0.8.0. The `0.8.0` release is still pre-`1.0`, and it ships three breaking source changes from the latest SDK regeneration along with a set of additive features (Speak V2 interrupt/configure, Listen V2 redaction, client retry tuning, and automatic response decompression). + +All three breaking changes are **source/compile-time** only — they follow the API definition, which promoted two loosely-typed fields to typed values and added one required field to a server-emitted message. On-the-wire payloads for existing requests are unchanged. + +The three breaking changes are: + +1. **`AgentV1UpdateListenListen.getProvider()` retyped** from `DeepgramListenProviderV2` to the new `AgentV1UpdateListenListenProvider` V1/V2 union — the API now models the `provider` field as a versioned discriminated union. +2. **`Google.getVersion()` retyped** from `Optional` to `Optional` — the Google think-provider `version` field is now an enum. +3. **`SpeakV2SpeechMetadataControlsApplied` gained a required `breaksApplied` field** — the builder chain now includes a `breaksApplied(int)` step between `pronunciationsApplied(...)` and `pronunciationWarnings(...)`. + +## Table of Contents + +- [Installation](#installation) +- [Configuration Changes](#configuration-changes) +- [Authentication Changes](#authentication-changes) +- [API Method Changes](#api-method-changes) + - [Agent V1 (WebSocket)](#agent-v1-websocket) + - [Listen V2 (WebSocket)](#listen-v2-websocket) + - [Speak V2 (WebSocket)](#speak-v2-websocket) +- [Type Changes](#type-changes) + - [Agent Update-Listen Provider Union](#agent-update-listen-provider-union) + - [Google Think-Provider Version Enum](#google-think-provider-version-enum) + - [Speak V2 Controls-Applied breaksApplied](#speak-v2-controls-applied-breaksapplied) + - [Other Additive Types](#other-additive-types) +- [Breaking Changes Summary](#breaking-changes-summary) + +## Installation + +Upgrade to `0.8.0` with Gradle or Maven. + +**Gradle** + +```groovy +dependencies { + implementation 'com.deepgram:deepgram-java-sdk:0.8.0' +} +``` + +**Maven** + +```xml + + com.deepgram + deepgram-java-sdk + 0.8.0 + +``` + +## Configuration Changes + +No required client-construction changes. Existing `DeepgramClient.builder()` usage still works. + +`0.8.0` adds optional client-level retry tuning on `ClientOptions.Builder` — all additive and defaulted: + +```java +DeepgramClient.builder() + .apiKey("YOUR_API_KEY") + // all optional; defaults preserve prior behavior + .initialRetryDelayMillis(1000) + .maxRetryDelayMillis(60000) + .retryJitterFactor(0.2) + .build(); +``` + +`0.8.0` also installs a response-decompression interceptor by default, so gzip/deflate-encoded HTTP responses are transparently decoded. No action required. + +## Authentication Changes + +No changes. API key, access token, and session ID configuration all work the same as in `0.7.x`. + +## API Method Changes + +### Agent V1 (WebSocket) + +No breaking client-method changes. The breaking change is on the `AgentV1UpdateListenListen` **type** used when you send an `UpdateListen` message (see [Type Changes](#agent-update-listen-provider-union)). + +### Listen V2 (WebSocket) + +No breaking client-method changes. `0.8.0` adds an optional `redact` query parameter to the V2 WebSocket connection via `V2ConnectOptions.redact(...)` (`com.deepgram.types.ListenV2Redact`). It is additive. + +### Speak V2 (WebSocket) + +No breaking client-method changes. `0.8.0` adds new Speak V2 send methods and server-event handlers, all additive: + +- `sendInterrupt(SpeakV2Interrupt)` and `sendConfigure(SpeakV2Configure)` +- `onSpeechInterrupted(...)`, `onConfigureSuccess(...)`, `onConfigureFailure(...)` + +## Type Changes + +### Agent Update-Listen Provider Union + +The `provider` field on `AgentV1UpdateListenListen` changed from the bare `DeepgramListenProviderV2` to the new `AgentV1UpdateListenListenProvider` discriminated union (variants `v1` / `v2`, discriminated on `version`). Wrap your existing provider in the matching variant when building, and read it back through `getV2()` / `getV1()` (or `visit(...)`). + +**v0.7.x** + +```java +import com.deepgram.types.DeepgramListenProviderV2; + +AgentV1UpdateListenListen listen = AgentV1UpdateListenListen.builder() + .provider(DeepgramListenProviderV2.builder() + // ...provider fields... + .build()) + .build(); + +// reading +DeepgramListenProviderV2 provider = listen.getProvider(); +``` + +**v0.8.0** + +```java +import com.deepgram.types.DeepgramListenProviderV2; +import com.deepgram.resources.agent.v1.types.AgentV1UpdateListenListenProvider; + +AgentV1UpdateListenListen listen = AgentV1UpdateListenListen.builder() + .provider(AgentV1UpdateListenListenProvider.v2( + DeepgramListenProviderV2.builder() + // ...provider fields... + .build())) + .build(); + +// reading +listen.getProvider().getV2().ifPresent(v2 -> { + // handle DeepgramListenProviderV2 +}); +``` + +### Google Think-Provider Version Enum + +`Google.getVersion()` changed from `Optional` to `Optional`, and the `version(...)` builder methods now take a `GoogleThinkProviderVersion` instead of a `String`. Replace string literals with the corresponding constant. + +Available constants (with wire values): `GoogleThinkProviderVersion.V1BETA` (`v1beta`), `AI_STUDIO_V1BETA` (`ai-studio-v1beta`), `GEMINI_ENTERPRISE_AGENT_V1` (`gemini-enterprise-agent-v1`). It is a forward-compatible enum, so unrecognized server values are preserved rather than rejected. + +**v0.7.x** + +```java +Google google = Google.builder() + .version("v1beta") + .build(); + +Optional version = google.getVersion(); +``` + +**v0.8.0** + +```java +import com.deepgram.types.GoogleThinkProviderVersion; + +Google google = Google.builder() + .version(GoogleThinkProviderVersion.V1BETA) + .build(); + +Optional version = google.getVersion(); +``` + +### Speak V2 Controls-Applied breaksApplied + +`SpeakV2SpeechMetadataControlsApplied` gained a required `breaksApplied` (`int`) field, reflecting a new `breaks_applied` field in the server payload. `SpeakV2SpeechMetadataControlsApplied` is a **server-emitted (read-only)** message, so most applications only read it — a new `getBreaksApplied()` getter is now available and no migration is needed for read paths. + +If you construct this type directly (uncommon — e.g. in tests), the staged builder now requires a `breaksApplied(int)` step between `pronunciationsApplied(...)` and `pronunciationWarnings(...)`. + +**v0.7.x** + +```java +SpeakV2SpeechMetadataControlsApplied.builder() + .pronunciationsApplied(2) + .pronunciationWarnings(0) + .build(); +``` + +**v0.8.0** + +```java +SpeakV2SpeechMetadataControlsApplied.builder() + .pronunciationsApplied(2) + .breaksApplied(1) + .pronunciationWarnings(0) + .build(); + +// reading +int breaks = controlsApplied.getBreaksApplied(); +``` + +### Other Additive Types + +`0.8.0` also adds new generated types and constants that do not require migration unless you want to use them: + +- **Speak V2 interrupt & configure**: `SpeakV2Interrupt`, `SpeakV2InterruptPlaybackOffset`, `SpeakV2Configure`, `SpeakV2ConfigureSuccess`, `SpeakV2ConfigureFailure` (+ `...Code`), and `SpeakV2SpeechInterrupted` (+ `...Metadata`, `...MetadataControlsApplied`), wired to the new client send methods and handlers above. +- **Listen V2 redaction**: `ListenV2Redact` and the `redact` V2 WebSocket query parameter (`V2ConnectOptions.redact(...)`). +- **Listen V1 diarization metadata & word speaker confidence**: `ListenV1ResponseMetadataDiarizeInfo` (+ `...Arch`) on the response metadata, plus per-word speaker-confidence fields on the words items. +- **New Deepgram Flux TTS voices**: `FLUX_*` constants added to `DeepgramSpeakProviderModel` (for example `FLUX_RUFUS_EN`). Purely additive — existing voice constants are unchanged. +- **Client retry tuning**: `ClientOptions.Builder.initialRetryDelayMillis(...)`, `maxRetryDelayMillis(...)`, `retryJitterFactor(...)`. + +## Breaking Changes Summary + +### Major Changes + +1. **Agent update-listen provider union**: `AgentV1UpdateListenListen.getProvider()` / `provider(...)` now use `AgentV1UpdateListenListenProvider` (V1/V2 union) instead of `DeepgramListenProviderV2`. +2. **Google think-provider version enum**: `Google.getVersion()` / `version(...)` now use `GoogleThinkProviderVersion` instead of `String`. +3. **Speak V2 controls-applied field**: `SpeakV2SpeechMetadataControlsApplied` adds a required `breaksApplied` field (new builder step; new `getBreaksApplied()` getter). + +### Changed Signatures + +- `AgentV1UpdateListenListen.getProvider()`: `DeepgramListenProviderV2` → `AgentV1UpdateListenListenProvider`; builder `provider(DeepgramListenProviderV2)` → `provider(AgentV1UpdateListenListenProvider)` +- `Google.getVersion()`: `Optional` → `Optional`; builder `version(String)` / `version(Optional)` → `version(GoogleThinkProviderVersion)` / `version(Optional)` +- `SpeakV2SpeechMetadataControlsApplied.builder()`: `pronunciationsApplied(int)` now returns a `BreaksAppliedStage` requiring `breaksApplied(int)` before `pronunciationWarnings(int)` + +### New Features in v0.8.0 + +- **Speak V2 interrupt/configure**: send methods (`sendInterrupt`, `sendConfigure`) and handlers (`onSpeechInterrupted`, `onConfigureSuccess`, `onConfigureFailure`) plus their message types +- **Listen V2 redaction**: `ListenV2Redact` and `V2ConnectOptions.redact(...)` +- **Listen V1 diarize info & word speaker confidence** on response metadata/words +- **New Deepgram Flux TTS voices** (`DeepgramSpeakProviderModel.FLUX_*`) +- **Client retry tuning** (`initialRetryDelayMillis`, `maxRetryDelayMillis`, `retryJitterFactor`) and automatic response decompression + +### Migration Checklist + +- [ ] Upgrade to `com.deepgram:deepgram-java-sdk:0.8.0` +- [ ] Wrap `AgentV1UpdateListenListen` providers in `AgentV1UpdateListenListenProvider.v2(...)` (or `.v1(...)`) and read them via `getV2()` / `getV1()` +- [ ] Replace `Google` `version` string literals with `GoogleThinkProviderVersion` constants and update any `Optional getVersion()` reads +- [ ] Add a `breaksApplied(...)` step to any hand-built `SpeakV2SpeechMetadataControlsApplied` (read paths need no change) +- [ ] Rebuild your project and fix any remaining references to the changed signatures +- [ ] (Optional) Adopt Speak V2 interrupt/configure, Listen V2 `redact`, client retry tuning, and the new Flux voices +``` \ No newline at end of file From a1dfd8386baa52c0a1c3ef22651b4fcc71365a30 Mon Sep 17 00:00:00 2001 From: Greg Holmes Date: Tue, 11 Aug 2026 15:04:22 +0100 Subject: [PATCH 06/14] test: cover 2026-08-11 regen surface (redact connect param, provider union, google version enum) --- .../com/deepgram/ListenV2ConnectWireTest.java | 24 +++++++++ .../java/com/deepgram/RegenTypesTest.java | 50 +++++++++++++++++++ 2 files changed, 74 insertions(+) diff --git a/src/test/java/com/deepgram/ListenV2ConnectWireTest.java b/src/test/java/com/deepgram/ListenV2ConnectWireTest.java index 8b0e3a7..19f8a22 100644 --- a/src/test/java/com/deepgram/ListenV2ConnectWireTest.java +++ b/src/test/java/com/deepgram/ListenV2ConnectWireTest.java @@ -8,6 +8,7 @@ import com.deepgram.types.ListenV2LanguageHint; import com.deepgram.types.ListenV2Model; import com.deepgram.types.ListenV2Numerals; +import com.deepgram.types.ListenV2Redact; import com.deepgram.types.ListenV2Tag; import java.util.List; import java.util.concurrent.TimeUnit; @@ -148,4 +149,27 @@ void languageHintStringSentAsOneParam() throws Exception { assertThat(url.queryParameterValues("language_hint")).containsExactly("en"); } + + @Test + @DisplayName("redact is sent on the connect URL as its wire value when set") + void redactPresentWhenSet() throws Exception { + // redact is a new (2026-08-11 regen) single-value Flux STT connect param backed by the + // ListenV2Redact enum, whose @JsonValue toString() is the raw wire string. Pin that it + // serializes as "numbers" (not the enum constant name) and lands on the connect URL. + HttpUrl url = connectAndCaptureUrl(V2ConnectOptions.builder() + .model(ListenV2Model.FLUX_GENERAL_EN) + .redact(ListenV2Redact.NUMBERS) + .build()); + + assertThat(url.queryParameter("redact")).isEqualTo("numbers"); + } + + @Test + @DisplayName("redact is omitted from the connect URL when not set") + void redactOmittedWhenAbsent() throws Exception { + HttpUrl url = connectAndCaptureUrl( + V2ConnectOptions.builder().model(ListenV2Model.FLUX_GENERAL_EN).build()); + + assertThat(url.queryParameterNames()).doesNotContain("redact"); + } } diff --git a/src/test/java/com/deepgram/RegenTypesTest.java b/src/test/java/com/deepgram/RegenTypesTest.java index 9b8d76c..8b5c328 100644 --- a/src/test/java/com/deepgram/RegenTypesTest.java +++ b/src/test/java/com/deepgram/RegenTypesTest.java @@ -10,12 +10,17 @@ import com.deepgram.resources.agent.v1.types.AgentV1SettingsApplied; import com.deepgram.resources.agent.v1.types.AgentV1SpeakUpdated; import com.deepgram.resources.agent.v1.types.AgentV1ThinkUpdated; +import com.deepgram.resources.agent.v1.types.AgentV1UpdateListenListenProvider; import com.deepgram.resources.agent.v1.types.AgentV1UserStartedSpeaking; import com.deepgram.resources.listen.v2.types.ListenV2CloseStream; import com.deepgram.resources.listen.v2.types.ListenV2TurnInfoWordsItem; import com.deepgram.resources.speak.v2.types.SpeakV2Close; import com.deepgram.resources.speak.v2.types.SpeakV2Flush; import com.deepgram.types.DeepgramListenProviderV2; +import com.deepgram.types.Google; +import com.deepgram.types.GoogleThinkProviderModel; +import com.deepgram.types.GoogleThinkProviderVersion; +import com.deepgram.types.ListenV2Redact; import com.fasterxml.jackson.databind.ObjectMapper; import java.util.Arrays; import org.junit.jupiter.api.DisplayName; @@ -147,4 +152,49 @@ void languageHintsRoundTrip() throws Exception { assertThat(parsed.getLanguageHints()).contains(Arrays.asList("en", "es")); } } + + /** + * Coverage for public-surface changes introduced by the 2026-08-11 regeneration. These are + * breaking (documented in {@code docs/Migrating-v0.7-to-v0.8.md}); the tests pin the new typed + * API shapes so a future regen can't silently reshape them again without a failing guard. + */ + @Nested + @DisplayName("2026-08-11 regen type shapes") + class Regen20260811 { + + @Test + @DisplayName("AgentV1UpdateListenListen provider is a V1/V2 union: v2 variant round-trips") + void updateListenProviderUnionV2() throws Exception { + // provider was retyped from a bare DeepgramListenProviderV2 to this discriminated union. + // Guard the v2 factory + accessors (the migration path) and that the payload survives + // serialization (the nested provider model must appear on the wire). + DeepgramListenProviderV2 v2 = + DeepgramListenProviderV2.builder().model("flux-general-en").build(); + AgentV1UpdateListenListenProvider provider = AgentV1UpdateListenListenProvider.v2(v2); + + assertThat(provider.isV2()).isTrue(); + assertThat(provider.isV1()).isFalse(); + assertThat(provider.getV2()).contains(v2); + assertThat(MAPPER.writeValueAsString(provider)).contains("flux-general-en"); + } + + @Test + @DisplayName("Google.version is a GoogleThinkProviderVersion enum serializing to its wire value") + void googleVersionEnum() throws Exception { + Google google = Google.builder() + .model(GoogleThinkProviderModel.GEMINI25FLASH) + .version(GoogleThinkProviderVersion.V1BETA) + .build(); + + assertThat(google.getVersion()).contains(GoogleThinkProviderVersion.V1BETA); + assertThat(MAPPER.writeValueAsString(google)).contains("\"version\":\"v1beta\""); + } + + @Test + @DisplayName("ListenV2Redact enum serializes to its raw wire value") + void listenV2RedactWireValue() { + assertThat(ListenV2Redact.NUMBERS.toString()).isEqualTo("numbers"); + assertThat(ListenV2Redact.AGGRESSIVE_NUMBERS.toString()).isEqualTo("aggressive_numbers"); + } + } } From c5753c927745f7ac959143763dde4281933a388d Mon Sep 17 00:00:00 2001 From: Greg Holmes Date: Tue, 11 Aug 2026 15:21:51 +0100 Subject: [PATCH 07/14] fix: default agent listen-provider unions to V2 so version-less payloads aren't dropped Fern points the union @JsonTypeInfo defaultImpl at the empty-bodied _UnknownValue, so a provider payload omitting the optional "version" discriminator (what 0.7.x emits) deserialized to null and re-serialized as {"provider":null}, silently dropping the provider. Patch defaultImpl = V2Value on the three agent listen-provider unions, freeze them in .fernignore, and guard with AgentSettingsProviderDefaultTest. Re-applies the fix the #89 revert forfeited (issue #93). --- .fernignore | 11 +++ AGENTS.md | 1 + ...tV1SettingsAgentContextListenProvider.java | 7 +- .../AgentV1SettingsAgentListenProvider.java | 7 +- .../AgentV1UpdateListenListenProvider.java | 7 +- .../AgentSettingsProviderDefaultTest.java | 80 +++++++++++++++++++ 6 files changed, 110 insertions(+), 3 deletions(-) create mode 100644 src/test/java/com/deepgram/AgentSettingsProviderDefaultTest.java diff --git a/.fernignore b/.fernignore index ce1762c..70ccf7a 100644 --- a/.fernignore +++ b/.fernignore @@ -66,6 +66,17 @@ 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 +# 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 +# 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 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 + # Build and project configuration build.gradle settings.gradle diff --git a/AGENTS.md b/AGENTS.md index a4b559b..8b43260 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -52,6 +52,7 @@ Current temporarily frozen files: - `src/main/java/com/deepgram/resources/speak/v2/websocket/V2WebSocketClient.java` and `src/main/java/com/deepgram/resources/listen/v2/websocket/V2WebSocketClient.java` - forward-compat patch (both clients). Fern's generated `handleIncomingMessage` dispatcher routes any unrecognized message type to `onError` with "Update your SDK version...", which makes a benign new server control frame look fatal to a deployed client. Patched so the unrecognized-type branch is a no-op — the raw frame is already delivered via `onMessage(String)` earlier in the method, so consumers still see it. Mirrors the JS/Python SDKs' forward-compat behavior and is regression-guarded by `src/test/java/com/deepgram/SpeakV2ForwardCompatTest.java` and `src/test/java/com/deepgram/ListenV2ForwardCompatTest.java`. These two clients also carry the streaming query-param patches described in the next entry. Use the standard `.bak` swap/restore workflow during regen review; re-apply the no-op to both after regen, and unfreeze once the generator stops treating unknown frames as errors. - `src/main/java/com/deepgram/resources/listen/v1/websocket/V1WebSocketClient.java` and `src/main/java/com/deepgram/resources/speak/v1/websocket/V1WebSocketClient.java` (and the v2 clients above) - streaming query-param patches on the generated `connect()` builders. Two fixes: (1) multi-value serialization — array-valued params (listen: `keyterm`, `keywords`, `replace`, `search`, `tag`, `extra`, `language_hint`; speak: `tag`) were serialized with `String.valueOf(union.get())`, collapsing a `List` into one param (`keyterm=[a, b]`) instead of repeats (`keyterm=a&keyterm=b`); (2) an `additionalProperties` escape hatch — the builder exposes `additionalProperty(key, value)` for unmodeled params (e.g. `no_delay`) but `connect()` never emitted them to the URL. Both patched to route through `QueryStringMapper(arraysAsRepeats=true)`, matching the REST path. 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/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}.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). +- 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). ### Prepare repo for regeneration 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..c4ee1de 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,12 @@ public interface Visitor { T _visitUnknown(Object unknownType); } - @JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "version", visible = true, defaultImpl = _UnknownValue.class) + // Manual patch (STOPGAP): Fern defaults this union to _UnknownValue, whose @JsonCreator has an + // empty body, so a provider payload omitting the optional "version" discriminator (exactly what + // 0.7.x emits) is silently dropped — it deserializes to an empty unknown variant and re-serializes + // as null. Default to V2Value so version-less payloads resolve to V2. Frozen in .fernignore; drop + // once the generator stops defaulting unions to the empty _UnknownValue (upstream Fern request). + @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 b/src/main/java/com/deepgram/resources/agent/v1/types/AgentV1SettingsAgentListenProvider.java index 4309ec0..3bf0333 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,12 @@ public interface Visitor { T _visitUnknown(Object unknownType); } - @JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "version", visible = true, defaultImpl = _UnknownValue.class) + // Manual patch (STOPGAP): Fern defaults this union to _UnknownValue, whose @JsonCreator has an + // empty body, so a provider payload omitting the optional "version" discriminator (exactly what + // 0.7.x emits) is silently dropped — it deserializes to an empty unknown variant and re-serializes + // as null. Default to V2Value so version-less payloads resolve to V2. Frozen in .fernignore; drop + // once the generator stops defaulting unions to the empty _UnknownValue (upstream Fern request). + @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 b/src/main/java/com/deepgram/resources/agent/v1/types/AgentV1UpdateListenListenProvider.java index e693a04..a0b84d9 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,12 @@ public interface Visitor { T _visitUnknown(Object unknownType); } - @JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property = "version", visible = true, defaultImpl = _UnknownValue.class) + // Manual patch (STOPGAP): Fern defaults this union to _UnknownValue, whose @JsonCreator has an + // empty body, so a provider payload omitting the optional "version" discriminator (exactly what + // 0.7.x emits) is silently dropped — it deserializes to an empty unknown variant and re-serializes + // as null. Default to V2Value so version-less payloads resolve to V2. Frozen in .fernignore; drop + // once the generator stops defaulting unions to the empty _UnknownValue (upstream Fern request). + @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/test/java/com/deepgram/AgentSettingsProviderDefaultTest.java b/src/test/java/com/deepgram/AgentSettingsProviderDefaultTest.java new file mode 100644 index 0000000..332a0b7 --- /dev/null +++ b/src/test/java/com/deepgram/AgentSettingsProviderDefaultTest.java @@ -0,0 +1,80 @@ +package com.deepgram; + +import static org.assertj.core.api.Assertions.assertThat; + +import com.deepgram.core.ObjectMappers; +import com.deepgram.resources.agent.v1.types.AgentV1SettingsAgentContextListen; +import com.deepgram.resources.agent.v1.types.AgentV1SettingsAgentListen; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +/** + * Guards the union default-variant patch on the agent listen-provider unions (see .fernignore): + * {@code AgentV1SettingsAgentListenProvider}, {@code AgentV1SettingsAgentContextListenProvider}, and + * {@code AgentV1UpdateListenListenProvider}. + * + *

    {@code version} is optional on both {@code DeepgramListenProviderV1} and + * {@code DeepgramListenProviderV2}, so a provider object without it is a valid payload. Fern points + * the union's {@code @JsonTypeInfo} {@code defaultImpl} at {@code _UnknownValue}, whose + * {@code @JsonCreator} has an empty body, so such a payload deserializes to an unknown variant + * carrying {@code null}: {@code getV2()} comes back empty and re-serializing emits {@code null}, + * silently dropping the caller's provider. All three are patched to {@code defaultImpl = V2Value}, + * which matches the server's default. + * + *

    The two Settings unions shipped in 0.7.1 with the defect, so this is a pre-existing bug fix + * rather than a regression guard — but it is the same trade-off, so it is pinned the same way. + */ +public class AgentSettingsProviderDefaultTest { + + private static final String VERSIONLESS = "{\"provider\":{\"type\":\"deepgram\",\"model\":\"nova-3\"}}"; + + @Test + @DisplayName("settings agent.listen: a provider with no \"version\" key parses as V2, not an unknown variant") + void agentListenVersionlessProviderIsV2() throws Exception { + AgentV1SettingsAgentListen listen = + ObjectMappers.JSON_MAPPER.readValue(VERSIONLESS, AgentV1SettingsAgentListen.class); + + assertThat(listen.getProvider()).isPresent(); + assertThat(listen.getProvider().get().isV2()).isTrue(); + assertThat(listen.getProvider().get()._isUnknown()).isFalse(); + assertThat(listen.getProvider().get().getV2()).isPresent(); + assertThat(listen.getProvider().get().getV2().get().getModel()).isEqualTo("nova-3"); + + // and it must survive re-serialization rather than becoming {"provider":null} + String again = ObjectMappers.JSON_MAPPER.writeValueAsString(listen); + assertThat(again).doesNotContain("\"provider\":null"); + assertThat(again).contains("\"model\":\"nova-3\""); + } + + @Test + @DisplayName("settings agent.context.listen: a provider with no \"version\" key parses as V2") + void agentContextListenVersionlessProviderIsV2() throws Exception { + AgentV1SettingsAgentContextListen listen = + ObjectMappers.JSON_MAPPER.readValue(VERSIONLESS, AgentV1SettingsAgentContextListen.class); + + assertThat(listen.getProvider()).isPresent(); + assertThat(listen.getProvider().get().isV2()).isTrue(); + assertThat(listen.getProvider().get()._isUnknown()).isFalse(); + assertThat(listen.getProvider().get().getV2().get().getModel()).isEqualTo("nova-3"); + + String again = ObjectMappers.JSON_MAPPER.writeValueAsString(listen); + assertThat(again).doesNotContain("\"provider\":null"); + assertThat(again).contains("\"model\":\"nova-3\""); + } + + @Test + @DisplayName("an explicit \"version\":\"v1\" still selects the V1 variant on both unions") + void explicitV1StillWins() throws Exception { + String v1 = "{\"provider\":{\"version\":\"v1\",\"type\":\"deepgram\"," + + "\"model\":\"nova-2\",\"language\":\"en\"}}"; + + AgentV1SettingsAgentListen listen = ObjectMappers.JSON_MAPPER.readValue(v1, AgentV1SettingsAgentListen.class); + assertThat(listen.getProvider().get().isV1()).isTrue(); + // note: model is Optional on V1 (required on V2), hence contains() rather than isEqualTo() + assertThat(listen.getProvider().get().getV1().get().getModel()).contains("nova-2"); + + AgentV1SettingsAgentContextListen context = + ObjectMappers.JSON_MAPPER.readValue(v1, AgentV1SettingsAgentContextListen.class); + assertThat(context.getProvider().get().isV1()).isTrue(); + } +} From cfa12646e5b718e2d016066a27e5c3ce28471571 Mon Sep 17 00:00:00 2001 From: Greg Holmes Date: Tue, 11 Aug 2026 16:10:20 +0100 Subject: [PATCH 08/14] =?UTF-8?q?docs:=20correct=20v0.7-to-v0.8=20guide=20?= =?UTF-8?q?=E2=80=94=20drop=20diarize-info/word-speaker-confidence=20(not?= =?UTF-8?q?=20in=20this=20regen)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/Migrating-v0.7-to-v0.8.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/Migrating-v0.7-to-v0.8.md b/docs/Migrating-v0.7-to-v0.8.md index e1f1a19..460b5c2 100644 --- a/docs/Migrating-v0.7-to-v0.8.md +++ b/docs/Migrating-v0.7-to-v0.8.md @@ -189,7 +189,7 @@ int breaks = controlsApplied.getBreaksApplied(); - **Speak V2 interrupt & configure**: `SpeakV2Interrupt`, `SpeakV2InterruptPlaybackOffset`, `SpeakV2Configure`, `SpeakV2ConfigureSuccess`, `SpeakV2ConfigureFailure` (+ `...Code`), and `SpeakV2SpeechInterrupted` (+ `...Metadata`, `...MetadataControlsApplied`), wired to the new client send methods and handlers above. - **Listen V2 redaction**: `ListenV2Redact` and the `redact` V2 WebSocket query parameter (`V2ConnectOptions.redact(...)`). -- **Listen V1 diarization metadata & word speaker confidence**: `ListenV1ResponseMetadataDiarizeInfo` (+ `...Arch`) on the response metadata, plus per-word speaker-confidence fields on the words items. +- **Speak V2 `speed` / `expressivity`**: optional connect params on `V2ConnectOptions` and on the REST `SpeakV2Request`. - **New Deepgram Flux TTS voices**: `FLUX_*` constants added to `DeepgramSpeakProviderModel` (for example `FLUX_RUFUS_EN`). Purely additive — existing voice constants are unchanged. - **Client retry tuning**: `ClientOptions.Builder.initialRetryDelayMillis(...)`, `maxRetryDelayMillis(...)`, `retryJitterFactor(...)`. @@ -211,7 +211,7 @@ int breaks = controlsApplied.getBreaksApplied(); - **Speak V2 interrupt/configure**: send methods (`sendInterrupt`, `sendConfigure`) and handlers (`onSpeechInterrupted`, `onConfigureSuccess`, `onConfigureFailure`) plus their message types - **Listen V2 redaction**: `ListenV2Redact` and `V2ConnectOptions.redact(...)` -- **Listen V1 diarize info & word speaker confidence** on response metadata/words +- **Speak V2 `speed` / `expressivity`** connect params (`V2ConnectOptions`, `SpeakV2Request`) - **New Deepgram Flux TTS voices** (`DeepgramSpeakProviderModel.FLUX_*`) - **Client retry tuning** (`initialRetryDelayMillis`, `maxRetryDelayMillis`, `retryJitterFactor`) and automatic response decompression From b64748692e7156175291ccfc521171e27c7cb242 Mon Sep 17 00:00:00 2001 From: Greg Holmes Date: Tue, 11 Aug 2026 16:20:56 +0100 Subject: [PATCH 09/14] docs: drop Flux TTS Early Access wording from Deepgram speak provider (matches deepgram-docs#1094) --- src/main/java/com/deepgram/types/Deepgram.java | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/main/java/com/deepgram/types/Deepgram.java b/src/main/java/com/deepgram/types/Deepgram.java index e502c19..849edda 100644 --- a/src/main/java/com/deepgram/types/Deepgram.java +++ b/src/main/java/com/deepgram/types/Deepgram.java @@ -46,7 +46,7 @@ public String getType() { } /** - * @return The Deepgram text-to-speech model family. Accepted values: v1 (Aura, the default) and v2 (Flux TTS, Early Access). Use v1 with an aura-* model and v2 with a flux-* model. Defaults to v1 when omitted. + * @return The Deepgram text-to-speech model family. Accepted values: v1 (Aura, the default) and v2 (Flux TTS). Use v1 with an aura-* model and v2 with a flux-* model. Defaults to v1 when omitted. */ @JsonProperty("version") public Optional getVersion() { @@ -54,7 +54,7 @@ public Optional getVersion() { } /** - * @return Deepgram TTS model. Aura models (version v1) use the aura-* voices; Flux TTS (version v2, Early Access) uses the flux-{voice}-{language} voices (e.g. flux-alexis-en). + * @return Deepgram TTS model. Aura models (version v1) use the aura-* voices; Flux TTS (version v2) uses the flux-{voice}-{language} voices (e.g. flux-alexis-en). */ @JsonProperty("model") public DeepgramSpeakProviderModel getModel() { @@ -100,7 +100,7 @@ public static ModelStage builder() { public interface ModelStage { /** - *

    Deepgram TTS model. Aura models (version v1) use the aura-* voices; Flux TTS (version v2, Early Access) uses the flux-{voice}-{language} voices (e.g. flux-alexis-en).

    + *

    Deepgram TTS model. Aura models (version v1) use the aura-* voices; Flux TTS (version v2) uses the flux-{voice}-{language} voices (e.g. flux-alexis-en).

    */ _FinalStage model(@NotNull DeepgramSpeakProviderModel model); @@ -115,7 +115,7 @@ public interface _FinalStage { _FinalStage additionalProperties(Map additionalProperties); /** - *

    The Deepgram text-to-speech model family. Accepted values: v1 (Aura, the default) and v2 (Flux TTS, Early Access). Use v1 with an aura-* model and v2 with a flux-* model. Defaults to v1 when omitted.

    + *

    The Deepgram text-to-speech model family. Accepted values: v1 (Aura, the default) and v2 (Flux TTS). Use v1 with an aura-* model and v2 with a flux-* model. Defaults to v1 when omitted.

    */ _FinalStage version(Optional version); @@ -151,7 +151,7 @@ public Builder from(Deepgram other) { } /** - *

    Deepgram TTS model. Aura models (version v1) use the aura-* voices; Flux TTS (version v2, Early Access) uses the flux-{voice}-{language} voices (e.g. flux-alexis-en).

    + *

    Deepgram TTS model. Aura models (version v1) use the aura-* voices; Flux TTS (version v2) uses the flux-{voice}-{language} voices (e.g. flux-alexis-en).

    * @return Reference to {@code this} so that method calls can be chained together. */ @java.lang.Override @@ -182,7 +182,7 @@ public _FinalStage speed(Optional speed) { } /** - *

    The Deepgram text-to-speech model family. Accepted values: v1 (Aura, the default) and v2 (Flux TTS, Early Access). Use v1 with an aura-* model and v2 with a flux-* model. Defaults to v1 when omitted.

    + *

    The Deepgram text-to-speech model family. Accepted values: v1 (Aura, the default) and v2 (Flux TTS). Use v1 with an aura-* model and v2 with a flux-* model. Defaults to v1 when omitted.

    * @return Reference to {@code this} so that method calls can be chained together. */ @java.lang.Override @@ -192,7 +192,7 @@ public _FinalStage version(String version) { } /** - *

    The Deepgram text-to-speech model family. Accepted values: v1 (Aura, the default) and v2 (Flux TTS, Early Access). Use v1 with an aura-* model and v2 with a flux-* model. Defaults to v1 when omitted.

    + *

    The Deepgram text-to-speech model family. Accepted values: v1 (Aura, the default) and v2 (Flux TTS). Use v1 with an aura-* model and v2 with a flux-* model. Defaults to v1 when omitted.

    */ @java.lang.Override @JsonSetter(value = "version", nulls = Nulls.SKIP) From 5882abe4369db30fe23445c694e8630776d6e437 Mon Sep 17 00:00:00 2001 From: Greg Holmes Date: Tue, 11 Aug 2026 16:25:17 +0100 Subject: [PATCH 10/14] docs: state speed/expressivity accepted values on SpeakV2Request (matches deepgram-docs#1094 intent) --- .../speak/v2/audio/requests/SpeakV2Request.java | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/main/java/com/deepgram/resources/speak/v2/audio/requests/SpeakV2Request.java b/src/main/java/com/deepgram/resources/speak/v2/audio/requests/SpeakV2Request.java index 7dfc30d..13fcf00 100644 --- a/src/main/java/com/deepgram/resources/speak/v2/audio/requests/SpeakV2Request.java +++ b/src/main/java/com/deepgram/resources/speak/v2/audio/requests/SpeakV2Request.java @@ -143,7 +143,7 @@ public Optional getEncoding() { } /** - * @return Expressive range of the generated speech. 0 is the voice's nominal delivery; negative values are flatter and more restrained, positive values more animated. + * @return Expressive range of the generated speech. Accepted values: -2, -1, 0, 1, 2. 0 is the voice's nominal delivery; negative values are flatter and more restrained, positive values more animated. */ @JsonIgnore public Optional getExpressivity() { @@ -167,7 +167,7 @@ public Optional getSampleRate() { } /** - * @return Speaking rate multiplier that adjusts the pace of generated speech while preserving natural prosody and voice quality. Only the multipliers listed here are accepted — the range is 0.85 to 1.15 in 0.05 increments. Not yet supported in all languages. + * @return Speaking rate multiplier that adjusts the pace of generated speech while preserving natural prosody and voice quality. Accepted values: 0.85, 0.90, 0.95, 1.00, 1.05, 1.10, 1.15. Any other value is rejected with SPEED_OUT_OF_RANGE, and a value off the 0.05 increment with SPEED_INCREMENT_INVALID. Not yet supported in all languages. */ @JsonIgnore public Optional getSpeed() { @@ -319,7 +319,7 @@ public interface _FinalStage { _FinalStage encoding(AudioGenerateRequestEncoding encoding); /** - *

    Expressive range of the generated speech. 0 is the voice's nominal delivery; negative values are flatter and more restrained, positive values more animated.

    + *

    Expressive range of the generated speech. Accepted values: -2, -1, 0, 1, 2. 0 is the voice's nominal delivery; negative values are flatter and more restrained, positive values more animated.

    */ _FinalStage expressivity(Optional expressivity); @@ -333,7 +333,7 @@ public interface _FinalStage { _FinalStage sampleRate(Integer sampleRate); /** - *

    Speaking rate multiplier that adjusts the pace of generated speech while preserving natural prosody and voice quality. Only the multipliers listed here are accepted — the range is 0.85 to 1.15 in 0.05 increments. Not yet supported in all languages.

    + *

    Speaking rate multiplier that adjusts the pace of generated speech while preserving natural prosody and voice quality. Accepted values: 0.85, 0.90, 0.95, 1.00, 1.05, 1.10, 1.15. Any other value is rejected with SPEED_OUT_OF_RANGE, and a value off the 0.05 increment with SPEED_INCREMENT_INVALID. Not yet supported in all languages.

    */ _FinalStage speed(Optional speed); @@ -441,7 +441,7 @@ public _FinalStage priority(Optional priority) { } /** - *

    Speaking rate multiplier that adjusts the pace of generated speech while preserving natural prosody and voice quality. Only the multipliers listed here are accepted — the range is 0.85 to 1.15 in 0.05 increments. Not yet supported in all languages.

    + *

    Speaking rate multiplier that adjusts the pace of generated speech while preserving natural prosody and voice quality. Accepted values: 0.85, 0.90, 0.95, 1.00, 1.05, 1.10, 1.15. Any other value is rejected with SPEED_OUT_OF_RANGE, and a value off the 0.05 increment with SPEED_INCREMENT_INVALID. Not yet supported in all languages.

    * @return Reference to {@code this} so that method calls can be chained together. */ @java.lang.Override @@ -451,7 +451,7 @@ public _FinalStage speed(Double speed) { } /** - *

    Speaking rate multiplier that adjusts the pace of generated speech while preserving natural prosody and voice quality. Only the multipliers listed here are accepted — the range is 0.85 to 1.15 in 0.05 increments. Not yet supported in all languages.

    + *

    Speaking rate multiplier that adjusts the pace of generated speech while preserving natural prosody and voice quality. Accepted values: 0.85, 0.90, 0.95, 1.00, 1.05, 1.10, 1.15. Any other value is rejected with SPEED_OUT_OF_RANGE, and a value off the 0.05 increment with SPEED_INCREMENT_INVALID. Not yet supported in all languages.

    */ @java.lang.Override @JsonSetter(value = "speed", nulls = Nulls.SKIP) @@ -481,7 +481,7 @@ public _FinalStage sampleRate(Optional sampleRate) { } /** - *

    Expressive range of the generated speech. 0 is the voice's nominal delivery; negative values are flatter and more restrained, positive values more animated.

    + *

    Expressive range of the generated speech. Accepted values: -2, -1, 0, 1, 2. 0 is the voice's nominal delivery; negative values are flatter and more restrained, positive values more animated.

    * @return Reference to {@code this} so that method calls can be chained together. */ @java.lang.Override @@ -491,7 +491,7 @@ public _FinalStage expressivity(Integer expressivity) { } /** - *

    Expressive range of the generated speech. 0 is the voice's nominal delivery; negative values are flatter and more restrained, positive values more animated.

    + *

    Expressive range of the generated speech. Accepted values: -2, -1, 0, 1, 2. 0 is the voice's nominal delivery; negative values are flatter and more restrained, positive values more animated.

    */ @java.lang.Override @JsonSetter(value = "expressivity", nulls = Nulls.SKIP) From 9cf105c9868848d0334668ef47dfe41e97c045ef Mon Sep 17 00:00:00 2001 From: Greg Holmes Date: Tue, 11 Aug 2026 16:31:15 +0100 Subject: [PATCH 11/14] docs(examples): add Speak V2 barge-in/configure example + README section --- README.md | 43 +++++++++++++++++ examples/speak/StreamingTtsV2.java | 77 +++++++++++++++++++++++++----- 2 files changed, 108 insertions(+), 12 deletions(-) diff --git a/README.md b/README.md index 8ea4c38..43a0dad 100644 --- a/README.md +++ b/README.md @@ -333,6 +333,49 @@ ttsWs.sendClose(SpeakV1Close.builder() ttsWs.close(); ``` +### Flux TTS Barge-in (Speak V2 WebSocket) + +The Speak V2 WebSocket adds Flux TTS barge-in and mid-stream controls. Open the connection with `V2ConnectOptions` (model required; `speed` and `expressivity` are optional connect params), then: + +- **`sendConfigure(...)`** adjusts the speech-rate multiplier mid-stream. Accepted speeds are `0.85`–`1.15` in `0.05` steps; the server replies via `onConfigureSuccess` or a typed `onConfigureFailure` (e.g. `SPEED_OUT_OF_RANGE`). +- **`sendInterrupt(...)`** stops playback (barge-in). Pass a `SpeakV2InterruptPlaybackOffset` with the audio milliseconds played so the `onSpeechInterrupted` event can report `getTextSpoken()` / `getTextRemaining()`. The offset is cumulative from session start, and each interrupt must advance past the previous one. + +```java +import com.deepgram.resources.speak.v2.types.SpeakV2Configure; +import com.deepgram.resources.speak.v2.types.SpeakV2Interrupt; +import com.deepgram.resources.speak.v2.types.SpeakV2InterruptPlaybackOffset; +import com.deepgram.resources.speak.v2.types.SpeakV2Speak; +import com.deepgram.resources.speak.v2.websocket.V2ConnectOptions; +import com.deepgram.resources.speak.v2.websocket.V2WebSocketClient; + +V2WebSocketClient ttsWs = client.speak().v2().v2WebSocket(); + +// Mid-stream configure acknowledgements +ttsWs.onConfigureSuccess(success -> System.out.println("configured: " + success.getApplied())); +ttsWs.onConfigureFailure(failure -> + System.out.println("rejected [" + failure.getCode() + "]: " + failure.getDescription())); + +// Barge-in: reports where playback was cut off when the interrupt carried a playback offset +ttsWs.onSpeechInterrupted(interrupted -> { + interrupted.getTextSpoken().ifPresent(spoken -> System.out.println("spoken: " + spoken)); + interrupted.getTextRemaining().ifPresent(remaining -> System.out.println("remaining: " + remaining)); +}); + +ttsWs.connect(V2ConnectOptions.builder().model("flux-alexis-en").build()).get(10, TimeUnit.SECONDS); + +ttsWs.sendConfigure(SpeakV2Configure.builder().speed(1.05).build()); +ttsWs.sendSpeak(SpeakV2Speak.builder().text("This is a longer sentence we can barge in on.").build()); + +// Stop playback after ~1.2s of audio has played +ttsWs.sendInterrupt(SpeakV2Interrupt.builder() + .playbackOffset(SpeakV2InterruptPlaybackOffset.builder().value(1200).build()) + .build()); + +ttsWs.close(); +``` + +See [`examples/speak/StreamingTtsV2.java`](examples/speak/StreamingTtsV2.java) for a complete, runnable barge-in example. + ### Agent WebSocket Connect to Deepgram's voice agent for real-time conversational AI. diff --git a/examples/speak/StreamingTtsV2.java b/examples/speak/StreamingTtsV2.java index 0b97779..3956374 100644 --- a/examples/speak/StreamingTtsV2.java +++ b/examples/speak/StreamingTtsV2.java @@ -1,6 +1,9 @@ import com.deepgram.DeepgramClient; import com.deepgram.resources.speak.v2.types.SpeakV2Close; +import com.deepgram.resources.speak.v2.types.SpeakV2Configure; import com.deepgram.resources.speak.v2.types.SpeakV2Flush; +import com.deepgram.resources.speak.v2.types.SpeakV2Interrupt; +import com.deepgram.resources.speak.v2.types.SpeakV2InterruptPlaybackOffset; import com.deepgram.resources.speak.v2.types.SpeakV2Speak; import com.deepgram.resources.speak.v2.websocket.V2ConnectOptions; import com.deepgram.resources.speak.v2.websocket.V2WebSocketClient; @@ -12,15 +15,31 @@ import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.atomic.AtomicLong; /** * Streaming text-to-speech using the Speak V2 WebSocket. Sends text chunks and receives audio data in real time, saving * to a file. Unlike V1, the V2 connection is opened with {@link V2ConnectOptions} (model is required; encoding and * sample rate are optional). * + *

    This example also demonstrates the Flux TTS barge-in controls: + * + *

      + *
    • {@code sendConfigure(...)} — adjust the speech-rate multiplier mid-stream; the server acknowledges with a + * {@code ConfigureSuccess} or a typed {@code ConfigureFailure} (e.g. {@code SPEED_OUT_OF_RANGE}). + *
    • {@code sendInterrupt(...)} — stop playback (barge-in). Pass a {@link SpeakV2InterruptPlaybackOffset} carrying + * the number of audio milliseconds the client has actually played so the server can report {@code text_spoken} + * and {@code text_remaining} in the {@code SpeechInterrupted} event. The offset is cumulative from the start of + * the session, and each interrupt must advance past the previous one. Omit the offset and + * {@code SpeechInterrupted} comes back without the spoken/remaining split. + *
    + * *

    Usage: java StreamingTtsV2 [output-file] */ public class StreamingTtsV2 { + // LINEAR16 mono @ 16 kHz = 2 bytes/sample * 16000 samples/sec = 32000 bytes/sec. + private static final long BYTES_PER_SECOND = 32000; + public static void main(String[] args) { // Get API key from environment String apiKey = System.getenv("DEEPGRAM_API_KEY"); @@ -46,6 +65,8 @@ public static void main(String[] args) { CountDownLatch closeLatch = new CountDownLatch(1); AtomicInteger audioChunks = new AtomicInteger(0); + // Total audio bytes received so far — used to estimate the playback offset for barge-in. + AtomicLong bytesReceived = new AtomicLong(0); try (OutputStream audioOutput = new FileOutputStream(outputFile)) { final String outputPath = outputFile; @@ -60,6 +81,7 @@ public static void main(String[] args) { // Audio data arrives as ByteString byte[] bytes = audioData.toByteArray(); audioOutput.write(bytes); + bytesReceived.addAndGet(bytes.length); int count = audioChunks.incrementAndGet(); System.out.printf("Received audio chunk #%d (%d bytes)%n", count, bytes.length); } catch (Exception e) { @@ -75,6 +97,25 @@ public static void main(String[] args) { System.out.println("Audio flushed - all queued text has been converted"); }); + // Barge-in: the server acknowledges sendInterrupt(...) with SpeechInterrupted. When the interrupt + // carried a playback offset, text_spoken / text_remaining describe where playback was cut off. + wsClient.onSpeechInterrupted(interrupted -> { + System.out.printf("Speech interrupted at %d ms played%n", interrupted.getAudioPlayedMs()); + interrupted.getTextSpoken().ifPresent(spoken -> System.out.println(" text spoken: " + spoken)); + interrupted + .getTextRemaining() + .ifPresent(remaining -> System.out.println(" text remaining: " + remaining)); + }); + + // Mid-stream configure acknowledgements. + wsClient.onConfigureSuccess(success -> { + System.out.println("Configure applied: " + success.getApplied()); + }); + + wsClient.onConfigureFailure(failure -> { + System.out.printf("Configure rejected [%s]: %s%n", failure.getCode(), failure.getDescription()); + }); + wsClient.onWarning(warning -> { System.out.println("Warning: " + warning); }); @@ -103,23 +144,35 @@ public static void main(String[] args) { CompletableFuture connectFuture = wsClient.connect(connectOptions); connectFuture.get(10, TimeUnit.SECONDS); - // Send text chunks for TTS conversion - String[] sentences = { - "Hello, this is a streaming text-to-speech demo.", - "Each sentence is sent as a separate message.", - "The audio is generated and streamed back in real time." - }; + // Adjust the speech rate mid-stream. Accepted values are 0.85–1.15 in 0.05 increments; anything + // else comes back as a ConfigureFailure (SPEED_OUT_OF_RANGE / SPEED_INCREMENT_INVALID). + System.out.println("Configuring speed = 1.05"); + wsClient.sendConfigure(SpeakV2Configure.builder().speed(1.05).build()); - for (String sentence : sentences) { - System.out.println("Sending: \"" + sentence + "\""); - wsClient.sendSpeak(SpeakV2Speak.builder().text(sentence).build()); - } + // Send a longer utterance we can barge in on. + String longUtterance = "This is a longer sentence that we will interrupt partway through " + + "to demonstrate barge-in, where the caller starts speaking before playback finishes."; + System.out.println("Sending: \"" + longUtterance + "\""); + wsClient.sendSpeak(SpeakV2Speak.builder().text(longUtterance).build()); + wsClient.sendFlush(SpeakV2Flush.builder().build()); - // Flush to ensure all text is processed + // Let some audio arrive, then barge in. In a real app you'd trigger this when the user starts + // speaking; here we interrupt after a fixed delay and report how much audio had played. + Thread.sleep(1500); + long playedMs = bytesReceived.get() * 1000 / BYTES_PER_SECOND; + System.out.printf("%nBarging in at ~%d ms of played audio%n", playedMs); + wsClient.sendInterrupt(SpeakV2Interrupt.builder() + .playbackOffset(SpeakV2InterruptPlaybackOffset.builder() + .value((int) playedMs) + .build()) + .build()); + + // Send a short follow-up so there is something to hear after the interrupt. + wsClient.sendSpeak(SpeakV2Speak.builder().text("Sure, go ahead.").build()); wsClient.sendFlush(SpeakV2Flush.builder().build()); // Give time for audio to arrive - Thread.sleep(5000); + Thread.sleep(3000); // Close the connection System.out.println("\nClosing connection..."); From 21db0509ac329f465d3d1029df4ebf5eb36b0aca Mon Sep 17 00:00:00 2001 From: Greg Holmes Date: Tue, 11 Aug 2026 21:15:21 +0100 Subject: [PATCH 12/14] =?UTF-8?q?fix:=20address=20Corey's=20#92=20review?= =?UTF-8?q?=20=E2=80=94=20retry-knob=20builder=20overrides,=20guide/exampl?= =?UTF-8?q?e/pom/wire-test=20fixes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - B3: add covariant initialRetryDelayMillis/maxRetryDelayMillis/retryJitterFactor overrides to DeepgramClientBuilder + AsyncDeepgramClientBuilder so the fluent chain returns DeepgramClient. - B4: fix migration-guide snippets — add required .model(...) to the Google and provider-union builders. - S1: restore addDefaultImplementationEntries in pom.xml maven-jar-plugin. - S2: add SpeakV2 wire coverage — speed/expressivity connect params + sendInterrupt/sendConfigure frames. - S3+nit: derive the example's bytes/sec from a shared SpeakV2SampleRate; restore the multi-chunk send loop. - nit: .fern/metadata.json sdkVersion 0.7.2 -> 0.7.1 to match the other markers. --- .fern/metadata.json | 2 +- docs/Migrating-v0.7-to-v0.8.md | 9 +- examples/speak/StreamingTtsV2.java | 23 +++-- pom.xml | 7 ++ .../deepgram/AsyncDeepgramClientBuilder.java | 18 ++++ .../com/deepgram/DeepgramClientBuilder.java | 18 ++++ .../com/deepgram/SpeakV2ConnectWireTest.java | 84 +++++++++++++++++++ 7 files changed, 150 insertions(+), 11 deletions(-) diff --git a/.fern/metadata.json b/.fern/metadata.json index 72d7e7c..14ccba7 100644 --- a/.fern/metadata.json +++ b/.fern/metadata.json @@ -15,5 +15,5 @@ "originGitCommit": "03f06776bbb692c49f8d76c4230bca7f4bc4bca7", "originGitCommitIsDirty": true, "invokedBy": "manual", - "sdkVersion": "0.7.2" + "sdkVersion": "0.7.1" } \ No newline at end of file diff --git a/docs/Migrating-v0.7-to-v0.8.md b/docs/Migrating-v0.7-to-v0.8.md index 460b5c2..4bf275d 100644 --- a/docs/Migrating-v0.7-to-v0.8.md +++ b/docs/Migrating-v0.7-to-v0.8.md @@ -100,7 +100,7 @@ import com.deepgram.types.DeepgramListenProviderV2; AgentV1UpdateListenListen listen = AgentV1UpdateListenListen.builder() .provider(DeepgramListenProviderV2.builder() - // ...provider fields... + .model("flux-general-en") .build()) .build(); @@ -117,7 +117,7 @@ import com.deepgram.resources.agent.v1.types.AgentV1UpdateListenListenProvider; AgentV1UpdateListenListen listen = AgentV1UpdateListenListen.builder() .provider(AgentV1UpdateListenListenProvider.v2( DeepgramListenProviderV2.builder() - // ...provider fields... + .model("flux-general-en") .build())) .build(); @@ -136,7 +136,10 @@ Available constants (with wire values): `GoogleThinkProviderVersion.V1BETA` (`v1 **v0.7.x** ```java +import com.deepgram.types.GoogleThinkProviderModel; + Google google = Google.builder() + .model(GoogleThinkProviderModel.GEMINI25FLASH) .version("v1beta") .build(); @@ -146,9 +149,11 @@ Optional version = google.getVersion(); **v0.8.0** ```java +import com.deepgram.types.GoogleThinkProviderModel; import com.deepgram.types.GoogleThinkProviderVersion; Google google = Google.builder() + .model(GoogleThinkProviderModel.GEMINI25FLASH) .version(GoogleThinkProviderVersion.V1BETA) .build(); diff --git a/examples/speak/StreamingTtsV2.java b/examples/speak/StreamingTtsV2.java index 3956374..28f7172 100644 --- a/examples/speak/StreamingTtsV2.java +++ b/examples/speak/StreamingTtsV2.java @@ -37,8 +37,11 @@ *

    Usage: java StreamingTtsV2 [output-file] */ public class StreamingTtsV2 { - // LINEAR16 mono @ 16 kHz = 2 bytes/sample * 16000 samples/sec = 32000 bytes/sec. - private static final long BYTES_PER_SECOND = 32000; + // Single source of truth for the audio format, shared by the connect() call and the + // playback-offset math below so the two can't drift. + private static final SpeakV2SampleRate SAMPLE_RATE = SpeakV2SampleRate.SIXTEEN_THOUSAND; + private static final int BYTES_PER_SAMPLE = 2; // LINEAR16 mono + private static final long BYTES_PER_SECOND = Long.parseLong(SAMPLE_RATE.toString()) * BYTES_PER_SAMPLE; public static void main(String[] args) { // Get API key from environment @@ -139,7 +142,7 @@ public static void main(String[] args) { V2ConnectOptions connectOptions = V2ConnectOptions.builder() .model("flux-alexis-en") .encoding(SpeakV2Encoding.LINEAR16) - .sampleRate(SpeakV2SampleRate.SIXTEEN_THOUSAND) + .sampleRate(SAMPLE_RATE) .build(); CompletableFuture connectFuture = wsClient.connect(connectOptions); connectFuture.get(10, TimeUnit.SECONDS); @@ -149,11 +152,15 @@ public static void main(String[] args) { System.out.println("Configuring speed = 1.05"); wsClient.sendConfigure(SpeakV2Configure.builder().speed(1.05).build()); - // Send a longer utterance we can barge in on. - String longUtterance = "This is a longer sentence that we will interrupt partway through " - + "to demonstrate barge-in, where the caller starts speaking before playback finishes."; - System.out.println("Sending: \"" + longUtterance + "\""); - wsClient.sendSpeak(SpeakV2Speak.builder().text(longUtterance).build()); + // Send a longer utterance, split across chunks, that we can barge in on. + String[] chunks = { + "This is a longer sentence that we will interrupt partway through ", + "to demonstrate barge-in, where the caller starts speaking before playback finishes." + }; + for (String chunk : chunks) { + System.out.println("Sending: \"" + chunk + "\""); + wsClient.sendSpeak(SpeakV2Speak.builder().text(chunk).build()); + } wsClient.sendFlush(SpeakV2Flush.builder().build()); // Let some audio arrive, then barge in. In a real app you'd trigger this when the user starts diff --git a/pom.xml b/pom.xml index cbc9ce3..93dca74 100644 --- a/pom.xml +++ b/pom.xml @@ -169,6 +169,13 @@ org.apache.maven.plugins maven-jar-plugin 3.4.1 + + + + true + + + diff --git a/src/main/java/com/deepgram/AsyncDeepgramClientBuilder.java b/src/main/java/com/deepgram/AsyncDeepgramClientBuilder.java index c147d26..074f04c 100644 --- a/src/main/java/com/deepgram/AsyncDeepgramClientBuilder.java +++ b/src/main/java/com/deepgram/AsyncDeepgramClientBuilder.java @@ -103,6 +103,24 @@ public AsyncDeepgramClientBuilder addHeader(String name, String value) { return this; } + @Override + public AsyncDeepgramClientBuilder initialRetryDelayMillis(long initialRetryDelayMillis) { + super.initialRetryDelayMillis(initialRetryDelayMillis); + return this; + } + + @Override + public AsyncDeepgramClientBuilder maxRetryDelayMillis(long maxRetryDelayMillis) { + super.maxRetryDelayMillis(maxRetryDelayMillis); + return this; + } + + @Override + public AsyncDeepgramClientBuilder retryJitterFactor(double retryJitterFactor) { + super.retryJitterFactor(retryJitterFactor); + return this; + } + @Override protected void setAuthentication(ClientOptions.Builder builder) { if (accessToken != null) { diff --git a/src/main/java/com/deepgram/DeepgramClientBuilder.java b/src/main/java/com/deepgram/DeepgramClientBuilder.java index eb3a166..614a23e 100644 --- a/src/main/java/com/deepgram/DeepgramClientBuilder.java +++ b/src/main/java/com/deepgram/DeepgramClientBuilder.java @@ -102,6 +102,24 @@ public DeepgramClientBuilder addHeader(String name, String value) { return this; } + @Override + public DeepgramClientBuilder initialRetryDelayMillis(long initialRetryDelayMillis) { + super.initialRetryDelayMillis(initialRetryDelayMillis); + return this; + } + + @Override + public DeepgramClientBuilder maxRetryDelayMillis(long maxRetryDelayMillis) { + super.maxRetryDelayMillis(maxRetryDelayMillis); + return this; + } + + @Override + public DeepgramClientBuilder retryJitterFactor(double retryJitterFactor) { + super.retryJitterFactor(retryJitterFactor); + return this; + } + @Override protected void setAuthentication(ClientOptions.Builder builder) { if (accessToken != null) { diff --git a/src/test/java/com/deepgram/SpeakV2ConnectWireTest.java b/src/test/java/com/deepgram/SpeakV2ConnectWireTest.java index d61f520..5d899ef 100644 --- a/src/test/java/com/deepgram/SpeakV2ConnectWireTest.java +++ b/src/test/java/com/deepgram/SpeakV2ConnectWireTest.java @@ -3,9 +3,15 @@ import static org.assertj.core.api.Assertions.assertThat; import com.deepgram.core.Environment; +import com.deepgram.resources.speak.v2.types.SpeakV2Configure; +import com.deepgram.resources.speak.v2.types.SpeakV2Interrupt; +import com.deepgram.resources.speak.v2.types.SpeakV2InterruptPlaybackOffset; import com.deepgram.resources.speak.v2.websocket.V2ConnectOptions; +import com.deepgram.resources.speak.v2.websocket.V2WebSocketClient; import com.deepgram.types.SpeakV2Tag; import java.util.List; +import java.util.concurrent.BlockingQueue; +import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.TimeUnit; import okhttp3.HttpUrl; import okhttp3.WebSocket; @@ -86,4 +92,82 @@ void tagStringSentAsOneParam() throws Exception { assertThat(url.queryParameterValues("tag")).containsExactly("a"); } + + @Test + @DisplayName("speed is sent on the connect URL as its raw numeric value when set") + void speedPresentWhenSet() throws Exception { + HttpUrl url = connectAndCaptureUrl(V2ConnectOptions.builder() + .model("flux-alexis-en") + .speed(1.05) + .build()); + + assertThat(url.queryParameter("speed")).isEqualTo("1.05"); + } + + @Test + @DisplayName("expressivity is sent on the connect URL as its raw integer value when set") + void expressivityPresentWhenSet() throws Exception { + HttpUrl url = connectAndCaptureUrl(V2ConnectOptions.builder() + .model("flux-alexis-en") + .expressivity(2) + .build()); + + assertThat(url.queryParameter("expressivity")).isEqualTo("2"); + } + + @Test + @DisplayName("speed/expressivity are omitted from the connect URL when not set") + void speedExpressivityOmittedWhenAbsent() throws Exception { + HttpUrl url = connectAndCaptureUrl( + V2ConnectOptions.builder().model("flux-alexis-en").build()); + + assertThat(url.queryParameterNames()).doesNotContain("speed", "expressivity"); + } + + /** Connects, keeps the socket open, runs {@code action}, and returns the first frame the server received. */ + private String connectAndCaptureSentFrame(java.util.function.Consumer action) throws Exception { + BlockingQueue received = new LinkedBlockingQueue<>(); + server.enqueue(new MockResponse().withWebSocketUpgrade(new WebSocketListener() { + @Override + public void onMessage(WebSocket webSocket, String text) { + received.add(text); + // Close server-side once we've captured a frame so MockWebServer can shut down cleanly. + webSocket.close(1000, null); + } + })); + V2WebSocketClient ws = client.speak().v2().v2WebSocket(); + try { + ws.connect(V2ConnectOptions.builder().model("flux-alexis-en").build()).get(5, TimeUnit.SECONDS); + action.accept(ws); + return received.poll(5, TimeUnit.SECONDS); + } finally { + ws.disconnect(); + } + } + + @Test + @DisplayName("sendInterrupt serializes an Interrupt frame carrying its playback_offset") + void sendInterruptFrame() throws Exception { + String frame = connectAndCaptureSentFrame(ws -> ws.sendInterrupt(SpeakV2Interrupt.builder() + .playbackOffset(SpeakV2InterruptPlaybackOffset.builder() + .value(1200) + .build()) + .build())); + + assertThat(frame).as("Interrupt frame reached the server").isNotNull(); + assertThat(frame).contains("\"type\":\"Interrupt\""); + assertThat(frame).contains("\"playback_offset\""); + assertThat(frame).contains("\"value\":1200"); + } + + @Test + @DisplayName("sendConfigure serializes a Configure frame carrying the speed") + void sendConfigureFrame() throws Exception { + String frame = connectAndCaptureSentFrame( + ws -> ws.sendConfigure(SpeakV2Configure.builder().speed(1.05).build())); + + assertThat(frame).as("Configure frame reached the server").isNotNull(); + assertThat(frame).contains("\"type\":\"Configure\""); + assertThat(frame).contains("\"speed\":1.05"); + } } From f354ef627beb2bdd94d6ae5d432a64aec2d4b807 Mon Sep 17 00:00:00 2001 From: Greg Holmes Date: Wed, 12 Aug 2026 07:42:38 +0100 Subject: [PATCH 13/14] docs: defer inline pause/pronunciation controls in Speak V2 descriptions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Transcribed from the approved deepgram-docs#1090 spec (4170ce53), which defers both inline controls at GA. Two descriptions were not merely stale but inverted: SpeakV2Request said batch "rejects the whole request with a 400" (the server now strips the control instead), and SpeakV2Speak said a malformed control is fatal via DATA-0002 (nothing is fatal). Warning codes are now described as reserved and not currently emitted, and the controls-applied counts as always 0 at launch. Also corrects the v0.7-to-v0.8 guide: breaksApplied is always 0 until pause controls ship, and the builder example no longer shows an impossible non-zero value. These docstrings are a bridge, not a patch to preserve — once #1090 is on main a regen reproduces them from the spec and should overwrite freely, so no .fernignore entries were added. Do not regen before #1090 lands or the old wording returns. --- docs/Migrating-v0.7-to-v0.8.md | 10 ++++++---- .../v2/audio/requests/SpeakV2Request.java | 6 +++--- .../resources/speak/v2/types/SpeakV2Speak.java | 6 +++--- .../SpeakV2SpeechInterruptedMetadata.java | 6 +++--- ...eechInterruptedMetadataControlsApplied.java | 18 +++++++++--------- .../speak/v2/types/SpeakV2SpeechMetadata.java | 6 +++--- .../SpeakV2SpeechMetadataControlsApplied.java | 18 +++++++++--------- .../speak/v2/types/SpeakV2Warning.java | 6 +++--- 8 files changed, 39 insertions(+), 37 deletions(-) diff --git a/docs/Migrating-v0.7-to-v0.8.md b/docs/Migrating-v0.7-to-v0.8.md index 4bf275d..da7c8e2 100644 --- a/docs/Migrating-v0.7-to-v0.8.md +++ b/docs/Migrating-v0.7-to-v0.8.md @@ -8,7 +8,7 @@ The three breaking changes are: 1. **`AgentV1UpdateListenListen.getProvider()` retyped** from `DeepgramListenProviderV2` to the new `AgentV1UpdateListenListenProvider` V1/V2 union — the API now models the `provider` field as a versioned discriminated union. 2. **`Google.getVersion()` retyped** from `Optional` to `Optional` — the Google think-provider `version` field is now an enum. -3. **`SpeakV2SpeechMetadataControlsApplied` gained a required `breaksApplied` field** — the builder chain now includes a `breaksApplied(int)` step between `pronunciationsApplied(...)` and `pronunciationWarnings(...)`. +3. **`SpeakV2SpeechMetadataControlsApplied` gained a required `breaksApplied` field** — the builder chain now includes a `breaksApplied(int)` step between `pronunciationsApplied(...)` and `pronunciationWarnings(...)`. The server always sends `0` at launch, because inline pause controls are not yet applied. ## Table of Contents @@ -164,6 +164,8 @@ Optional version = google.getVersion(); `SpeakV2SpeechMetadataControlsApplied` gained a required `breaksApplied` (`int`) field, reflecting a new `breaks_applied` field in the server payload. `SpeakV2SpeechMetadataControlsApplied` is a **server-emitted (read-only)** message, so most applications only read it — a new `getBreaksApplied()` getter is now available and no migration is needed for read paths. +> **Inline pause controls are not applied at launch** — support is coming soon. The server sends `breaks_applied` on every turn, but the value is always `0` until pause controls ship. The same is true of `pronunciationsApplied` and `pronunciationWarnings`. Read the field if you like, but do not branch on a non-zero value yet. + If you construct this type directly (uncommon — e.g. in tests), the staged builder now requires a `breaksApplied(int)` step between `pronunciationsApplied(...)` and `pronunciationWarnings(...)`. **v0.7.x** @@ -180,7 +182,7 @@ SpeakV2SpeechMetadataControlsApplied.builder() ```java SpeakV2SpeechMetadataControlsApplied.builder() .pronunciationsApplied(2) - .breaksApplied(1) + .breaksApplied(0) // always 0 at launch — pause controls are not yet applied .pronunciationWarnings(0) .build(); @@ -204,7 +206,7 @@ int breaks = controlsApplied.getBreaksApplied(); 1. **Agent update-listen provider union**: `AgentV1UpdateListenListen.getProvider()` / `provider(...)` now use `AgentV1UpdateListenListenProvider` (V1/V2 union) instead of `DeepgramListenProviderV2`. 2. **Google think-provider version enum**: `Google.getVersion()` / `version(...)` now use `GoogleThinkProviderVersion` instead of `String`. -3. **Speak V2 controls-applied field**: `SpeakV2SpeechMetadataControlsApplied` adds a required `breaksApplied` field (new builder step; new `getBreaksApplied()` getter). +3. **Speak V2 controls-applied field**: `SpeakV2SpeechMetadataControlsApplied` adds a required `breaksApplied` field (new builder step; new `getBreaksApplied()` getter). Always `0` at launch — inline pause controls are not yet applied. ### Changed Signatures @@ -225,7 +227,7 @@ int breaks = controlsApplied.getBreaksApplied(); - [ ] Upgrade to `com.deepgram:deepgram-java-sdk:0.8.0` - [ ] Wrap `AgentV1UpdateListenListen` providers in `AgentV1UpdateListenListenProvider.v2(...)` (or `.v1(...)`) and read them via `getV2()` / `getV1()` - [ ] Replace `Google` `version` string literals with `GoogleThinkProviderVersion` constants and update any `Optional getVersion()` reads -- [ ] Add a `breaksApplied(...)` step to any hand-built `SpeakV2SpeechMetadataControlsApplied` (read paths need no change) +- [ ] Add a `breaksApplied(...)` step to any hand-built `SpeakV2SpeechMetadataControlsApplied` — pass `0`, since inline pause controls are not applied at launch (read paths need no change) - [ ] Rebuild your project and fix any remaining references to the changed signatures - [ ] (Optional) Adopt Speak V2 interrupt/configure, Listen V2 `redact`, client retry tuning, and the new Flux voices ``` \ No newline at end of file diff --git a/src/main/java/com/deepgram/resources/speak/v2/audio/requests/SpeakV2Request.java b/src/main/java/com/deepgram/resources/speak/v2/audio/requests/SpeakV2Request.java index 13fcf00..2f7a5e8 100644 --- a/src/main/java/com/deepgram/resources/speak/v2/audio/requests/SpeakV2Request.java +++ b/src/main/java/com/deepgram/resources/speak/v2/audio/requests/SpeakV2Request.java @@ -183,7 +183,7 @@ public Optional getPriority() { } /** - * @return The text content to be converted to speech. The server normalizes and preprocesses the text before synthesis. May carry inline pause and pronunciation controls; see the TTS Voice Controls guide for their syntax and limits. Unlike the streaming transport, which drops an unusable pause control and warns, batch rejects the whole request with a 400. + * @return The text content to be converted to speech. The server normalizes and preprocesses the text before synthesis. Inline pause and pronunciation controls are not yet applied; they are stripped from the text before synthesis. */ @JsonProperty("text") public String getText() { @@ -255,7 +255,7 @@ public interface ModelStage { public interface TextStage { /** - *

    The text content to be converted to speech. The server normalizes and preprocesses the text before synthesis. May carry inline pause and pronunciation controls; see the TTS Voice Controls guide for their syntax and limits. Unlike the streaming transport, which drops an unusable pause control and warns, batch rejects the whole request with a 400.

    + *

    The text content to be converted to speech. The server normalizes and preprocesses the text before synthesis. Inline pause and pronunciation controls are not yet applied; they are stripped from the text before synthesis.

    */ _FinalStage text(@NotNull String text); } @@ -410,7 +410,7 @@ public TextStage model(@NotNull String model) { } /** - *

    The text content to be converted to speech. The server normalizes and preprocesses the text before synthesis. May carry inline pause and pronunciation controls; see the TTS Voice Controls guide for their syntax and limits. Unlike the streaming transport, which drops an unusable pause control and warns, batch rejects the whole request with a 400.

    + *

    The text content to be converted to speech. The server normalizes and preprocesses the text before synthesis. Inline pause and pronunciation controls are not yet applied; they are stripped from the text before synthesis.

    * @return Reference to {@code this} so that method calls can be chained together. */ @java.lang.Override diff --git a/src/main/java/com/deepgram/resources/speak/v2/types/SpeakV2Speak.java b/src/main/java/com/deepgram/resources/speak/v2/types/SpeakV2Speak.java index 2d4e5bd..16150bf 100644 --- a/src/main/java/com/deepgram/resources/speak/v2/types/SpeakV2Speak.java +++ b/src/main/java/com/deepgram/resources/speak/v2/types/SpeakV2Speak.java @@ -37,7 +37,7 @@ public String getType() { } /** - * @return The input text to synthesize. May carry inline pause and pronunciation controls; see the TTS Voice Controls guide for their syntax and limits. A control that is rejected or ignored raises a Warning, while a malformed one is fatal (DATA-0002). + * @return The input text to synthesize. Inline pause and pronunciation controls are not yet applied; they are stripped from the text before synthesis. */ @JsonProperty("text") public String getText() { @@ -75,7 +75,7 @@ public static TextStage builder() { public interface TextStage { /** - *

    The input text to synthesize. May carry inline pause and pronunciation controls; see the TTS Voice Controls guide for their syntax and limits. A control that is rejected or ignored raises a Warning, while a malformed one is fatal (DATA-0002).

    + *

    The input text to synthesize. Inline pause and pronunciation controls are not yet applied; they are stripped from the text before synthesis.

    */ _FinalStage text(@NotNull String text); @@ -106,7 +106,7 @@ public Builder from(SpeakV2Speak other) { } /** - *

    The input text to synthesize. May carry inline pause and pronunciation controls; see the TTS Voice Controls guide for their syntax and limits. A control that is rejected or ignored raises a Warning, while a malformed one is fatal (DATA-0002).

    + *

    The input text to synthesize. Inline pause and pronunciation controls are not yet applied; they are stripped from the text before synthesis.

    * @return Reference to {@code this} so that method calls can be chained together. */ @java.lang.Override diff --git a/src/main/java/com/deepgram/resources/speak/v2/types/SpeakV2SpeechInterruptedMetadata.java b/src/main/java/com/deepgram/resources/speak/v2/types/SpeakV2SpeechInterruptedMetadata.java index b16d5f8..da24995 100644 --- a/src/main/java/com/deepgram/resources/speak/v2/types/SpeakV2SpeechInterruptedMetadata.java +++ b/src/main/java/com/deepgram/resources/speak/v2/types/SpeakV2SpeechInterruptedMetadata.java @@ -79,7 +79,7 @@ public int getBillableCharacterCount() { } /** - * @return Counts of the inline controls the server acted on during the turn. A control that was rejected or ignored is reported through a Warning and does not count here. + * @return Counts of the inline controls the server acted on during the turn. Inline pause and pronunciation controls are not applied at launch — support is coming soon — so every count is currently 0. */ @JsonProperty("controls_applied") public SpeakV2SpeechInterruptedMetadataControlsApplied getControlsApplied() { @@ -156,7 +156,7 @@ public interface BillableCharacterCountStage { public interface ControlsAppliedStage { /** - *

    Counts of the inline controls the server acted on during the turn. A control that was rejected or ignored is reported through a Warning and does not count here.

    + *

    Counts of the inline controls the server acted on during the turn. Inline pause and pronunciation controls are not applied at launch — support is coming soon — so every count is currently 0.

    */ _FinalStage controlsApplied(@NotNull SpeakV2SpeechInterruptedMetadataControlsApplied controlsApplied); } @@ -247,7 +247,7 @@ public ControlsAppliedStage billableCharacterCount(int billableCharacterCount) { } /** - *

    Counts of the inline controls the server acted on during the turn. A control that was rejected or ignored is reported through a Warning and does not count here.

    + *

    Counts of the inline controls the server acted on during the turn. Inline pause and pronunciation controls are not applied at launch — support is coming soon — so every count is currently 0.

    * @return Reference to {@code this} so that method calls can be chained together. */ @java.lang.Override diff --git a/src/main/java/com/deepgram/resources/speak/v2/types/SpeakV2SpeechInterruptedMetadataControlsApplied.java b/src/main/java/com/deepgram/resources/speak/v2/types/SpeakV2SpeechInterruptedMetadataControlsApplied.java index 850b47b..85000d9 100644 --- a/src/main/java/com/deepgram/resources/speak/v2/types/SpeakV2SpeechInterruptedMetadataControlsApplied.java +++ b/src/main/java/com/deepgram/resources/speak/v2/types/SpeakV2SpeechInterruptedMetadataControlsApplied.java @@ -38,7 +38,7 @@ private SpeakV2SpeechInterruptedMetadataControlsApplied( } /** - * @return Pronunciation overrides successfully applied. Mirrors the Aura-2 dg-pronunciations-applied REST header. + * @return Pronunciation overrides successfully applied. Mirrors the Aura-2 dg-pronunciations-applied REST header. Currently always 0. */ @JsonProperty("pronunciations_applied") public int getPronunciationsApplied() { @@ -46,7 +46,7 @@ public int getPronunciationsApplied() { } /** - * @return Pause (break) controls successfully applied. Mirrors the Aura-2 dg-breaks-applied REST header. A pause whose duration was out of range or off the supported increment is stripped rather than applied, so it is excluded from this count. + * @return Pause (break) controls successfully applied. Mirrors the Aura-2 dg-breaks-applied REST header. Currently always 0. */ @JsonProperty("breaks_applied") public int getBreaksApplied() { @@ -54,7 +54,7 @@ public int getBreaksApplied() { } /** - * @return Pronunciation entries that triggered a warning (invalid IPA, word too long). Mirrors the Aura-2 dg-pronunciation-warnings REST header. + * @return Pronunciation entries that triggered a warning (invalid IPA, word too long). Mirrors the Aura-2 dg-pronunciation-warnings REST header. Currently always 0. */ @JsonProperty("pronunciation_warnings") public int getPronunciationWarnings() { @@ -95,7 +95,7 @@ public static PronunciationsAppliedStage builder() { public interface PronunciationsAppliedStage { /** - *

    Pronunciation overrides successfully applied. Mirrors the Aura-2 dg-pronunciations-applied REST header.

    + *

    Pronunciation overrides successfully applied. Mirrors the Aura-2 dg-pronunciations-applied REST header. Currently always 0.

    */ BreaksAppliedStage pronunciationsApplied(int pronunciationsApplied); @@ -104,14 +104,14 @@ public interface PronunciationsAppliedStage { public interface BreaksAppliedStage { /** - *

    Pause (break) controls successfully applied. Mirrors the Aura-2 dg-breaks-applied REST header. A pause whose duration was out of range or off the supported increment is stripped rather than applied, so it is excluded from this count.

    + *

    Pause (break) controls successfully applied. Mirrors the Aura-2 dg-breaks-applied REST header. Currently always 0.

    */ PronunciationWarningsStage breaksApplied(int breaksApplied); } public interface PronunciationWarningsStage { /** - *

    Pronunciation entries that triggered a warning (invalid IPA, word too long). Mirrors the Aura-2 dg-pronunciation-warnings REST header.

    + *

    Pronunciation entries that triggered a warning (invalid IPA, word too long). Mirrors the Aura-2 dg-pronunciation-warnings REST header. Currently always 0.

    */ _FinalStage pronunciationWarnings(int pronunciationWarnings); } @@ -147,7 +147,7 @@ public Builder from(SpeakV2SpeechInterruptedMetadataControlsApplied other) { } /** - *

    Pronunciation overrides successfully applied. Mirrors the Aura-2 dg-pronunciations-applied REST header.

    + *

    Pronunciation overrides successfully applied. Mirrors the Aura-2 dg-pronunciations-applied REST header. Currently always 0.

    * @return Reference to {@code this} so that method calls can be chained together. */ @java.lang.Override @@ -158,7 +158,7 @@ public BreaksAppliedStage pronunciationsApplied(int pronunciationsApplied) { } /** - *

    Pause (break) controls successfully applied. Mirrors the Aura-2 dg-breaks-applied REST header. A pause whose duration was out of range or off the supported increment is stripped rather than applied, so it is excluded from this count.

    + *

    Pause (break) controls successfully applied. Mirrors the Aura-2 dg-breaks-applied REST header. Currently always 0.

    * @return Reference to {@code this} so that method calls can be chained together. */ @java.lang.Override @@ -169,7 +169,7 @@ public PronunciationWarningsStage breaksApplied(int breaksApplied) { } /** - *

    Pronunciation entries that triggered a warning (invalid IPA, word too long). Mirrors the Aura-2 dg-pronunciation-warnings REST header.

    + *

    Pronunciation entries that triggered a warning (invalid IPA, word too long). Mirrors the Aura-2 dg-pronunciation-warnings REST header. Currently always 0.

    * @return Reference to {@code this} so that method calls can be chained together. */ @java.lang.Override diff --git a/src/main/java/com/deepgram/resources/speak/v2/types/SpeakV2SpeechMetadata.java b/src/main/java/com/deepgram/resources/speak/v2/types/SpeakV2SpeechMetadata.java index a3e3393..d982b95 100644 --- a/src/main/java/com/deepgram/resources/speak/v2/types/SpeakV2SpeechMetadata.java +++ b/src/main/java/com/deepgram/resources/speak/v2/types/SpeakV2SpeechMetadata.java @@ -87,7 +87,7 @@ public int getBillableCharacterCount() { } /** - * @return Counts of the inline controls the server acted on during the turn. A control that was rejected or ignored is reported through a Warning and does not count here. + * @return Counts of the inline controls the server acted on during the turn. Inline pause and pronunciation controls are not applied at launch — support is coming soon — so every count is currently 0. */ @JsonProperty("controls_applied") public SpeakV2SpeechMetadataControlsApplied getControlsApplied() { @@ -164,7 +164,7 @@ public interface BillableCharacterCountStage { public interface ControlsAppliedStage { /** - *

    Counts of the inline controls the server acted on during the turn. A control that was rejected or ignored is reported through a Warning and does not count here.

    + *

    Counts of the inline controls the server acted on during the turn. Inline pause and pronunciation controls are not applied at launch — support is coming soon — so every count is currently 0.

    */ _FinalStage controlsApplied(@NotNull SpeakV2SpeechMetadataControlsApplied controlsApplied); } @@ -255,7 +255,7 @@ public ControlsAppliedStage billableCharacterCount(int billableCharacterCount) { } /** - *

    Counts of the inline controls the server acted on during the turn. A control that was rejected or ignored is reported through a Warning and does not count here.

    + *

    Counts of the inline controls the server acted on during the turn. Inline pause and pronunciation controls are not applied at launch — support is coming soon — so every count is currently 0.

    * @return Reference to {@code this} so that method calls can be chained together. */ @java.lang.Override diff --git a/src/main/java/com/deepgram/resources/speak/v2/types/SpeakV2SpeechMetadataControlsApplied.java b/src/main/java/com/deepgram/resources/speak/v2/types/SpeakV2SpeechMetadataControlsApplied.java index fd50e5d..b258f91 100644 --- a/src/main/java/com/deepgram/resources/speak/v2/types/SpeakV2SpeechMetadataControlsApplied.java +++ b/src/main/java/com/deepgram/resources/speak/v2/types/SpeakV2SpeechMetadataControlsApplied.java @@ -38,7 +38,7 @@ private SpeakV2SpeechMetadataControlsApplied( } /** - * @return Pronunciation overrides successfully applied. Mirrors the Aura-2 dg-pronunciations-applied REST header. + * @return Pronunciation overrides successfully applied. Mirrors the Aura-2 dg-pronunciations-applied REST header. Currently always 0. */ @JsonProperty("pronunciations_applied") public int getPronunciationsApplied() { @@ -46,7 +46,7 @@ public int getPronunciationsApplied() { } /** - * @return Pause (break) controls successfully applied. Mirrors the Aura-2 dg-breaks-applied REST header. A pause whose duration was out of range or off the supported increment is stripped rather than applied, so it is excluded from this count. + * @return Pause (break) controls successfully applied. Mirrors the Aura-2 dg-breaks-applied REST header. Currently always 0. */ @JsonProperty("breaks_applied") public int getBreaksApplied() { @@ -54,7 +54,7 @@ public int getBreaksApplied() { } /** - * @return Pronunciation entries that triggered a warning (invalid IPA, word too long). Mirrors the Aura-2 dg-pronunciation-warnings REST header. + * @return Pronunciation entries that triggered a warning (invalid IPA, word too long). Mirrors the Aura-2 dg-pronunciation-warnings REST header. Currently always 0. */ @JsonProperty("pronunciation_warnings") public int getPronunciationWarnings() { @@ -95,7 +95,7 @@ public static PronunciationsAppliedStage builder() { public interface PronunciationsAppliedStage { /** - *

    Pronunciation overrides successfully applied. Mirrors the Aura-2 dg-pronunciations-applied REST header.

    + *

    Pronunciation overrides successfully applied. Mirrors the Aura-2 dg-pronunciations-applied REST header. Currently always 0.

    */ BreaksAppliedStage pronunciationsApplied(int pronunciationsApplied); @@ -104,14 +104,14 @@ public interface PronunciationsAppliedStage { public interface BreaksAppliedStage { /** - *

    Pause (break) controls successfully applied. Mirrors the Aura-2 dg-breaks-applied REST header. A pause whose duration was out of range or off the supported increment is stripped rather than applied, so it is excluded from this count.

    + *

    Pause (break) controls successfully applied. Mirrors the Aura-2 dg-breaks-applied REST header. Currently always 0.

    */ PronunciationWarningsStage breaksApplied(int breaksApplied); } public interface PronunciationWarningsStage { /** - *

    Pronunciation entries that triggered a warning (invalid IPA, word too long). Mirrors the Aura-2 dg-pronunciation-warnings REST header.

    + *

    Pronunciation entries that triggered a warning (invalid IPA, word too long). Mirrors the Aura-2 dg-pronunciation-warnings REST header. Currently always 0.

    */ _FinalStage pronunciationWarnings(int pronunciationWarnings); } @@ -147,7 +147,7 @@ public Builder from(SpeakV2SpeechMetadataControlsApplied other) { } /** - *

    Pronunciation overrides successfully applied. Mirrors the Aura-2 dg-pronunciations-applied REST header.

    + *

    Pronunciation overrides successfully applied. Mirrors the Aura-2 dg-pronunciations-applied REST header. Currently always 0.

    * @return Reference to {@code this} so that method calls can be chained together. */ @java.lang.Override @@ -158,7 +158,7 @@ public BreaksAppliedStage pronunciationsApplied(int pronunciationsApplied) { } /** - *

    Pause (break) controls successfully applied. Mirrors the Aura-2 dg-breaks-applied REST header. A pause whose duration was out of range or off the supported increment is stripped rather than applied, so it is excluded from this count.

    + *

    Pause (break) controls successfully applied. Mirrors the Aura-2 dg-breaks-applied REST header. Currently always 0.

    * @return Reference to {@code this} so that method calls can be chained together. */ @java.lang.Override @@ -169,7 +169,7 @@ public PronunciationWarningsStage breaksApplied(int breaksApplied) { } /** - *

    Pronunciation entries that triggered a warning (invalid IPA, word too long). Mirrors the Aura-2 dg-pronunciation-warnings REST header.

    + *

    Pronunciation entries that triggered a warning (invalid IPA, word too long). Mirrors the Aura-2 dg-pronunciation-warnings REST header. Currently always 0.

    * @return Reference to {@code this} so that method calls can be chained together. */ @java.lang.Override diff --git a/src/main/java/com/deepgram/resources/speak/v2/types/SpeakV2Warning.java b/src/main/java/com/deepgram/resources/speak/v2/types/SpeakV2Warning.java index 4597a5c..0e44840 100644 --- a/src/main/java/com/deepgram/resources/speak/v2/types/SpeakV2Warning.java +++ b/src/main/java/com/deepgram/resources/speak/v2/types/SpeakV2Warning.java @@ -42,7 +42,7 @@ public String getType() { /** * @return Warning code identifying the condition, in SCREAMING_SNAKE_CASE. *

    Turn-scoped codes: NO_ACTIVE_SPEECH (a speech-scoped message arrived with no active turn), NO_SYNTHESIZABLE_TEXT (the turn's text was entirely whitespace or punctuation, so it produced no audio and is completed with a zero-duration SpeechMetadata).SYNTHESIS_RETRYING (a synthesis request failed and is being retried).

    - *

    Inline-control codes. The offending control is dropped and the rest of the turn is synthesized: PRONUNCIATION_WARNINGS (one or more pronunciation overrides contained invalid IPA; the server falls back to a best-effort pronunciation and the description lists each offending sequence), PRONUNCIATION_TOO_LONG (an IPA string exceeded the length limit), PRONUNCIATIONS_LIMIT_EXCEEDED (too many pronunciation controls in one turn), BREAKS_LIMIT_EXCEEDED (too many pause controls, or two pauses with no intervening text), BREAK_TOKENS_OUT_OF_RANGE (pause durations outside the range the model supports), BREAK_TOKENS_WITH_INVALID_INCREMENTS (pause durations off the model's supported increment).

    + *

    Inline-control codes are reserved and not currently emitted, because inline pause and pronunciation controls are not yet applied: BREAKS_LIMIT_EXCEEDED (too many pause controls, or two pauses with no intervening text), BREAK_TOKENS_OUT_OF_RANGE (pause durations outside the range the model supports), BREAK_TOKENS_WITH_INVALID_INCREMENTS (pause durations off the model's supported increment), PRONUNCIATION_WARNINGS (a pronunciation override contained invalid IPA), PRONUNCIATION_TOO_LONG (an IPA string exceeded the length limit), PRONUNCIATIONS_LIMIT_EXCEEDED (too many pronunciation controls in one turn).

    *

    Interrupt-scoped codes, each meaning the Interrupt was ignored: NO_AUDIO_GENERATED (the session has produced no audio yet, so there is nothing to interrupt), INTERRUPT_IN_PROGRESS (an earlier Interrupt is still being processed — at most one is handled at a time), INVALID_INTERRUPT_OFFSET (the playback_offset did not advance past the position a prior interrupt established).

    */ @JsonProperty("code") @@ -91,7 +91,7 @@ public interface CodeStage { /** *

    Warning code identifying the condition, in SCREAMING_SNAKE_CASE.

    *

    Turn-scoped codes: NO_ACTIVE_SPEECH (a speech-scoped message arrived with no active turn), NO_SYNTHESIZABLE_TEXT (the turn's text was entirely whitespace or punctuation, so it produced no audio and is completed with a zero-duration SpeechMetadata).SYNTHESIS_RETRYING (a synthesis request failed and is being retried).

    - *

    Inline-control codes. The offending control is dropped and the rest of the turn is synthesized: PRONUNCIATION_WARNINGS (one or more pronunciation overrides contained invalid IPA; the server falls back to a best-effort pronunciation and the description lists each offending sequence), PRONUNCIATION_TOO_LONG (an IPA string exceeded the length limit), PRONUNCIATIONS_LIMIT_EXCEEDED (too many pronunciation controls in one turn), BREAKS_LIMIT_EXCEEDED (too many pause controls, or two pauses with no intervening text), BREAK_TOKENS_OUT_OF_RANGE (pause durations outside the range the model supports), BREAK_TOKENS_WITH_INVALID_INCREMENTS (pause durations off the model's supported increment).

    + *

    Inline-control codes are reserved and not currently emitted, because inline pause and pronunciation controls are not yet applied: BREAKS_LIMIT_EXCEEDED (too many pause controls, or two pauses with no intervening text), BREAK_TOKENS_OUT_OF_RANGE (pause durations outside the range the model supports), BREAK_TOKENS_WITH_INVALID_INCREMENTS (pause durations off the model's supported increment), PRONUNCIATION_WARNINGS (a pronunciation override contained invalid IPA), PRONUNCIATION_TOO_LONG (an IPA string exceeded the length limit), PRONUNCIATIONS_LIMIT_EXCEEDED (too many pronunciation controls in one turn).

    *

    Interrupt-scoped codes, each meaning the Interrupt was ignored: NO_AUDIO_GENERATED (the session has produced no audio yet, so there is nothing to interrupt), INTERRUPT_IN_PROGRESS (an earlier Interrupt is still being processed — at most one is handled at a time), INVALID_INTERRUPT_OFFSET (the playback_offset did not advance past the position a prior interrupt established).

    */ DescriptionStage code(@NotNull String code); @@ -135,7 +135,7 @@ public Builder from(SpeakV2Warning other) { /** *

    Warning code identifying the condition, in SCREAMING_SNAKE_CASE.

    *

    Turn-scoped codes: NO_ACTIVE_SPEECH (a speech-scoped message arrived with no active turn), NO_SYNTHESIZABLE_TEXT (the turn's text was entirely whitespace or punctuation, so it produced no audio and is completed with a zero-duration SpeechMetadata).SYNTHESIS_RETRYING (a synthesis request failed and is being retried).

    - *

    Inline-control codes. The offending control is dropped and the rest of the turn is synthesized: PRONUNCIATION_WARNINGS (one or more pronunciation overrides contained invalid IPA; the server falls back to a best-effort pronunciation and the description lists each offending sequence), PRONUNCIATION_TOO_LONG (an IPA string exceeded the length limit), PRONUNCIATIONS_LIMIT_EXCEEDED (too many pronunciation controls in one turn), BREAKS_LIMIT_EXCEEDED (too many pause controls, or two pauses with no intervening text), BREAK_TOKENS_OUT_OF_RANGE (pause durations outside the range the model supports), BREAK_TOKENS_WITH_INVALID_INCREMENTS (pause durations off the model's supported increment).

    + *

    Inline-control codes are reserved and not currently emitted, because inline pause and pronunciation controls are not yet applied: BREAKS_LIMIT_EXCEEDED (too many pause controls, or two pauses with no intervening text), BREAK_TOKENS_OUT_OF_RANGE (pause durations outside the range the model supports), BREAK_TOKENS_WITH_INVALID_INCREMENTS (pause durations off the model's supported increment), PRONUNCIATION_WARNINGS (a pronunciation override contained invalid IPA), PRONUNCIATION_TOO_LONG (an IPA string exceeded the length limit), PRONUNCIATIONS_LIMIT_EXCEEDED (too many pronunciation controls in one turn).

    *

    Interrupt-scoped codes, each meaning the Interrupt was ignored: NO_AUDIO_GENERATED (the session has produced no audio yet, so there is nothing to interrupt), INTERRUPT_IN_PROGRESS (an earlier Interrupt is still being processed — at most one is handled at a time), INVALID_INTERRUPT_OFFSET (the playback_offset did not advance past the position a prior interrupt established).

    * @return Reference to {@code this} so that method calls can be chained together. */ From cc219b986c6cbded355af5e9d8d09a24000afc93 Mon Sep 17 00:00:00 2001 From: Greg Holmes Date: Wed, 12 Aug 2026 12:04:23 +0100 Subject: [PATCH 14/14] docs: scope speed error codes to the WebSocket path (SpeakV2Request) SPEED_OUT_OF_RANGE and SPEED_INCREMENT_INVALID are members of SpeakV2ConfigureFailureCode, a WebSocket ConfigureFailure message. The REST batch path returns err_code "Bad Request" instead, so naming them on SpeakV2Request was wrong on that path. The spec agrees: parameters.speak.v2.yml (deepgram-docs#1090 @ 4170ce53) describes the REST speed query param without error codes, while the asyncapi SpeakV2Speed schema carries them. Description now matches the REST spec text verbatim. README.md:340 already attributes the codes to the WebSocket and is left alone. --- .../resources/speak/v2/audio/requests/SpeakV2Request.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/main/java/com/deepgram/resources/speak/v2/audio/requests/SpeakV2Request.java b/src/main/java/com/deepgram/resources/speak/v2/audio/requests/SpeakV2Request.java index 2f7a5e8..2c7dc8b 100644 --- a/src/main/java/com/deepgram/resources/speak/v2/audio/requests/SpeakV2Request.java +++ b/src/main/java/com/deepgram/resources/speak/v2/audio/requests/SpeakV2Request.java @@ -167,7 +167,7 @@ public Optional getSampleRate() { } /** - * @return Speaking rate multiplier that adjusts the pace of generated speech while preserving natural prosody and voice quality. Accepted values: 0.85, 0.90, 0.95, 1.00, 1.05, 1.10, 1.15. Any other value is rejected with SPEED_OUT_OF_RANGE, and a value off the 0.05 increment with SPEED_INCREMENT_INVALID. Not yet supported in all languages. + * @return Speaking rate multiplier that adjusts the pace of generated speech while preserving natural prosody and voice quality. Accepted values run 0.85 to 1.15 in 0.05 increments. Not yet supported in all languages. */ @JsonIgnore public Optional getSpeed() { @@ -333,7 +333,7 @@ public interface _FinalStage { _FinalStage sampleRate(Integer sampleRate); /** - *

    Speaking rate multiplier that adjusts the pace of generated speech while preserving natural prosody and voice quality. Accepted values: 0.85, 0.90, 0.95, 1.00, 1.05, 1.10, 1.15. Any other value is rejected with SPEED_OUT_OF_RANGE, and a value off the 0.05 increment with SPEED_INCREMENT_INVALID. Not yet supported in all languages.

    + *

    Speaking rate multiplier that adjusts the pace of generated speech while preserving natural prosody and voice quality. Accepted values run 0.85 to 1.15 in 0.05 increments. Not yet supported in all languages.

    */ _FinalStage speed(Optional speed); @@ -441,7 +441,7 @@ public _FinalStage priority(Optional priority) { } /** - *

    Speaking rate multiplier that adjusts the pace of generated speech while preserving natural prosody and voice quality. Accepted values: 0.85, 0.90, 0.95, 1.00, 1.05, 1.10, 1.15. Any other value is rejected with SPEED_OUT_OF_RANGE, and a value off the 0.05 increment with SPEED_INCREMENT_INVALID. Not yet supported in all languages.

    + *

    Speaking rate multiplier that adjusts the pace of generated speech while preserving natural prosody and voice quality. Accepted values run 0.85 to 1.15 in 0.05 increments. Not yet supported in all languages.

    * @return Reference to {@code this} so that method calls can be chained together. */ @java.lang.Override @@ -451,7 +451,7 @@ public _FinalStage speed(Double speed) { } /** - *

    Speaking rate multiplier that adjusts the pace of generated speech while preserving natural prosody and voice quality. Accepted values: 0.85, 0.90, 0.95, 1.00, 1.05, 1.10, 1.15. Any other value is rejected with SPEED_OUT_OF_RANGE, and a value off the 0.05 increment with SPEED_INCREMENT_INVALID. Not yet supported in all languages.

    + *

    Speaking rate multiplier that adjusts the pace of generated speech while preserving natural prosody and voice quality. Accepted values run 0.85 to 1.15 in 0.05 increments. Not yet supported in all languages.

    */ @java.lang.Override @JsonSetter(value = "speed", nulls = Nulls.SKIP)