diff --git a/.fern/metadata.json b/.fern/metadata.json index 70012a8..165ec67 100644 --- a/.fern/metadata.json +++ b/.fern/metadata.json @@ -11,8 +11,8 @@ }, "enable-wire-tests": true }, - "originGitCommit": "8dd6f48a06eee8c3985894f68ba3b554e5564d21", + "originGitCommit": "335af96251466afae7a9f71badb65c630a48626e", "originGitCommitIsDirty": true, "invokedBy": "manual", - "sdkVersion": "0.6.0" + "sdkVersion": "0.6.1" } \ No newline at end of file diff --git a/.fernignore b/.fernignore index 0ce5ae6..f359721 100644 --- a/.fernignore +++ b/.fernignore @@ -24,10 +24,21 @@ src/main/java/com/deepgram/core/transport/ # Pull this back out once the fixes are upstreamed into the Fern generator. src/main/java/com/deepgram/core/ReconnectingWebSocketListener.java -# Manual equals/hashCode contract fix: Fern generates equals() but no hashCode() for this -# fields-less message type. We add a consistent hashCode(). Unfreeze and drop the patch once -# the generator emits a matching equals/hashCode pair. +# 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 # Build and project configuration build.gradle diff --git a/AGENTS.md b/AGENTS.md index e1af8d2..751ced0 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -49,10 +49,11 @@ 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. +- 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 -1. Create a new branch off `main` named `lo/sdk-gen-`. +1. Create a new branch off `main` named `/sdk-gen-` (e.g. `gh/sdk-gen-2026-07-09`). Use your own initials as the prefix. 2. Push the branch and create a PR titled `chore: SDK regeneration ` (empty commit if needed). 3. Read `.fernignore` and classify each entry using the rules above. 4. For each temporarily frozen file only: diff --git a/examples/speak/StreamingTtsV2.java b/examples/speak/StreamingTtsV2.java new file mode 100644 index 0000000..168fde3 --- /dev/null +++ b/examples/speak/StreamingTtsV2.java @@ -0,0 +1,140 @@ +import com.deepgram.DeepgramClient; +import com.deepgram.resources.speak.v2.types.SpeakV2Close; +import com.deepgram.resources.speak.v2.types.SpeakV2Flush; +import com.deepgram.resources.speak.v2.types.SpeakV2Speak; +import com.deepgram.resources.speak.v2.websocket.V2ConnectOptions; +import com.deepgram.resources.speak.v2.websocket.V2WebSocketClient; +import com.deepgram.types.SpeakV2Encoding; +import com.deepgram.types.SpeakV2SampleRate; +import java.io.FileOutputStream; +import java.io.OutputStream; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicInteger; + +/** + * 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). + * + *

Usage: java StreamingTtsV2 [output-file] + */ +public class StreamingTtsV2 { + public static void main(String[] args) { + // Get API key from environment + String apiKey = System.getenv("DEEPGRAM_API_KEY"); + if (apiKey == null || apiKey.isEmpty()) { + System.err.println("DEEPGRAM_API_KEY environment variable is required"); + System.exit(1); + } + + String outputFile = "output_streaming_v2.wav"; + if (args.length > 0) { + outputFile = args[0]; + } + + System.out.println("Streaming Text-to-Speech (Speak V2 WebSocket)"); + System.out.println("Output: " + outputFile); + System.out.println(); + + // Create client + DeepgramClient client = DeepgramClient.builder().apiKey(apiKey).build(); + + // Get the Speak V2 WebSocket client + V2WebSocketClient wsClient = client.speak().v2().v2WebSocket(); + + CountDownLatch closeLatch = new CountDownLatch(1); + AtomicInteger audioChunks = new AtomicInteger(0); + + try (OutputStream audioOutput = new FileOutputStream(outputFile)) { + final String outputPath = outputFile; + + // Register event handlers before connecting + wsClient.onConnected(() -> { + System.out.println("Connected to Deepgram TTS WebSocket (V2)"); + }); + + wsClient.onSpeakV2Audio(audioData -> { + try { + // Audio data arrives as ByteString + byte[] bytes = audioData.toByteArray(); + audioOutput.write(bytes); + int count = audioChunks.incrementAndGet(); + System.out.printf("Received audio chunk #%d (%d bytes)%n", count, bytes.length); + } catch (Exception e) { + System.err.println("Error writing audio: " + e.getMessage()); + } + }); + + wsClient.onSpeechStarted(started -> { + System.out.println("Speech started: " + started); + }); + + wsClient.onFlushed(flushed -> { + System.out.println("Audio flushed - all queued text has been converted"); + }); + + wsClient.onWarning(warning -> { + System.out.println("Warning: " + warning); + }); + + wsClient.onErrorMessage(error -> { + System.err.println("Server error: " + error); + }); + + wsClient.onError(error -> { + System.err.println("Error: " + error.getMessage()); + }); + + wsClient.onDisconnected(reason -> { + // audioOutput is closed by try-with-resources when the block exits. + System.out.println( + "\nConnection closed (code: " + reason.getCode() + ", reason: " + reason.getReason() + ")"); + closeLatch.countDown(); + }); + + // Connect to the WebSocket. Model is required; encoding and sample rate are optional. + V2ConnectOptions connectOptions = V2ConnectOptions.builder() + .model("aura-2-thalia-en") + .encoding(SpeakV2Encoding.LINEAR16) + .sampleRate(SpeakV2SampleRate.SIXTEEN_THOUSAND) + .build(); + 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." + }; + + for (String sentence : sentences) { + System.out.println("Sending: \"" + sentence + "\""); + wsClient.sendSpeak(SpeakV2Speak.builder().text(sentence).build()); + } + + // Flush to ensure all text is processed + wsClient.sendFlush(SpeakV2Flush.builder().build()); + + // Give time for audio to arrive + Thread.sleep(5000); + + // Close the connection + System.out.println("\nClosing connection..."); + wsClient.sendClose(SpeakV2Close.builder().build()); + + closeLatch.await(10, TimeUnit.SECONDS); + + System.out.printf("%nTotal audio chunks received: %d%n", audioChunks.get()); + System.out.printf("Audio saved to %s%n", outputPath); + + } catch (Exception e) { + System.err.println("Error: " + e.getMessage()); + e.printStackTrace(); + } finally { + wsClient.disconnect(); + } + } +} diff --git a/src/main/java/com/deepgram/core/ReconnectingWebSocketListener.java b/src/main/java/com/deepgram/core/ReconnectingWebSocketListener.java index e10e5af..a533068 100644 --- a/src/main/java/com/deepgram/core/ReconnectingWebSocketListener.java +++ b/src/main/java/com/deepgram/core/ReconnectingWebSocketListener.java @@ -25,21 +25,17 @@ * Provides production-ready resilience for WebSocket connections. */ public abstract class ReconnectingWebSocketListener extends WebSocketListener { - // Option-derived fields are volatile (not final) so {@link #applyOptionsOverride} can rewire them - // after construction — used by {@code TransportWebSocketFactory} to honour + // 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 long minReconnectionDelayMs; - - private volatile long maxReconnectionDelayMs; - - private volatile double reconnectionDelayGrowFactor; - - private volatile int maxRetries; + 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 volatile long connectionTimeoutMs; - private final AtomicInteger retryCount = new AtomicInteger(0); private final AtomicBoolean connectLock = new AtomicBoolean(false); @@ -66,12 +62,8 @@ 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.connectionTimeoutMs = options.connectionTimeoutMs; this.connectionSupplier = connectionSupplier; } @@ -81,12 +73,13 @@ public ReconnectingWebSocketListener( * 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: option-derived fields are volatile; reads observe the latest write. 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. + *

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. */ @@ -94,11 +87,7 @@ public void applyOptionsOverride(ReconnectOptions options) { if (options == null) { return; } - this.minReconnectionDelayMs = options.minReconnectionDelayMs; - this.maxReconnectionDelayMs = options.maxReconnectionDelayMs; - this.reconnectionDelayGrowFactor = options.reconnectionDelayGrowFactor; - this.maxRetries = options.maxRetries; - this.connectionTimeoutMs = options.connectionTimeoutMs; + this.activeOptions = options; } /** @@ -119,23 +108,26 @@ 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() > maxRetries) { + if (retryCount.get() > opts.maxRetries) { connectLock.set(false); return; } try { CompletableFuture connectionFuture = CompletableFuture.supplyAsync(connectionSupplier); try { - webSocket = connectionFuture.get(connectionTimeoutMs, MILLISECONDS); + webSocket = connectionFuture.get(opts.connectionTimeoutMs, MILLISECONDS); } catch (TimeoutException e) { connectionFuture.cancel(true); TimeoutException timeoutError = - new TimeoutException("WebSocket connection timeout after " + connectionTimeoutMs + " 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()) { @@ -350,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); } /** 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/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/AgentV1ListenUpdated.java b/src/main/java/com/deepgram/resources/agent/v1/types/AgentV1ListenUpdated.java new file mode 100644 index 0000000..6018473 --- /dev/null +++ b/src/main/java/com/deepgram/resources/agent/v1/types/AgentV1ListenUpdated.java @@ -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 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/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/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/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/AgentV1UpdateListen.java b/src/main/java/com/deepgram/resources/agent/v1/types/AgentV1UpdateListen.java new file mode 100644 index 0000000..432d72b --- /dev/null +++ b/src/main/java/com/deepgram/resources/agent/v1/types/AgentV1UpdateListen.java @@ -0,0 +1,137 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ +package com.deepgram.resources.agent.v1.types; + +import com.deepgram.core.ObjectMappers; +import com.fasterxml.jackson.annotation.JsonAnyGetter; +import com.fasterxml.jackson.annotation.JsonAnySetter; +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonSetter; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import java.util.HashMap; +import java.util.Map; +import java.util.Objects; +import org.jetbrains.annotations.NotNull; + +@JsonInclude(JsonInclude.Include.NON_ABSENT) +@JsonDeserialize(builder = AgentV1UpdateListen.Builder.class) +public final class AgentV1UpdateListen { + private final AgentV1UpdateListenListen listen; + + private final Map additionalProperties; + + private AgentV1UpdateListen(AgentV1UpdateListenListen listen, Map additionalProperties) { + this.listen = listen; + this.additionalProperties = additionalProperties; + } + + /** + * @return Message type identifier for updating the listen configuration + */ + @JsonProperty("type") + public String getType() { + return "UpdateListen"; + } + + /** + * @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. + */ + @JsonProperty("listen") + public AgentV1UpdateListenListen getListen() { + return listen; + } + + @java.lang.Override + public boolean equals(Object other) { + if (this == other) return true; + return other instanceof AgentV1UpdateListen && equalTo((AgentV1UpdateListen) other); + } + + @JsonAnyGetter + public Map getAdditionalProperties() { + return this.additionalProperties; + } + + private boolean equalTo(AgentV1UpdateListen other) { + return listen.equals(other.listen); + } + + @java.lang.Override + public int hashCode() { + return Objects.hash(this.listen); + } + + @java.lang.Override + public String toString() { + return ObjectMappers.stringify(this); + } + + public static ListenStage builder() { + return new 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.

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

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.

+ * @return Reference to {@code this} so that method calls can be chained together. + */ + @java.lang.Override + @JsonSetter("listen") + public _FinalStage listen(@NotNull AgentV1UpdateListenListen listen) { + this.listen = Objects.requireNonNull(listen, "listen must not be null"); + return this; + } + + @java.lang.Override + public AgentV1UpdateListen build() { + return new AgentV1UpdateListen(listen, additionalProperties); + } + + @java.lang.Override + public Builder additionalProperty(String key, Object value) { + this.additionalProperties.put(key, value); + return this; + } + + @java.lang.Override + public Builder additionalProperties(Map additionalProperties) { + this.additionalProperties.putAll(additionalProperties); + return this; + } + } +} diff --git a/src/main/java/com/deepgram/resources/agent/v1/types/AgentV1UpdateListenListen.java b/src/main/java/com/deepgram/resources/agent/v1/types/AgentV1UpdateListenListen.java new file mode 100644 index 0000000..c65548a --- /dev/null +++ b/src/main/java/com/deepgram/resources/agent/v1/types/AgentV1UpdateListenListen.java @@ -0,0 +1,119 @@ +/** + * 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.deepgram.types.DeepgramListenProviderV2; +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 = AgentV1UpdateListenListen.Builder.class) +public final class AgentV1UpdateListenListen { + private final DeepgramListenProviderV2 provider; + + private final Map additionalProperties; + + private AgentV1UpdateListenListen(DeepgramListenProviderV2 provider, Map additionalProperties) { + this.provider = provider; + this.additionalProperties = additionalProperties; + } + + @JsonProperty("provider") + public DeepgramListenProviderV2 getProvider() { + return provider; + } + + @java.lang.Override + public boolean equals(Object other) { + if (this == other) return true; + return other instanceof AgentV1UpdateListenListen && equalTo((AgentV1UpdateListenListen) other); + } + + @JsonAnyGetter + public Map getAdditionalProperties() { + return this.additionalProperties; + } + + private boolean equalTo(AgentV1UpdateListenListen other) { + return provider.equals(other.provider); + } + + @java.lang.Override + public int hashCode() { + return Objects.hash(this.provider); + } + + @java.lang.Override + public String toString() { + return ObjectMappers.stringify(this); + } + + public static ProviderStage builder() { + return new Builder(); + } + + public interface ProviderStage { + _FinalStage provider(@NotNull DeepgramListenProviderV2 provider); + + Builder from(AgentV1UpdateListenListen other); + } + + public interface _FinalStage { + AgentV1UpdateListenListen build(); + + _FinalStage additionalProperty(String key, Object value); + + _FinalStage additionalProperties(Map additionalProperties); + } + + @JsonIgnoreProperties(ignoreUnknown = true) + public static final class Builder implements ProviderStage, _FinalStage { + private DeepgramListenProviderV2 provider; + + @JsonAnySetter + private Map additionalProperties = new HashMap<>(); + + private Builder() {} + + @java.lang.Override + public Builder from(AgentV1UpdateListenListen other) { + provider(other.getProvider()); + return this; + } + + @java.lang.Override + @JsonSetter("provider") + public _FinalStage provider(@NotNull DeepgramListenProviderV2 provider) { + this.provider = Objects.requireNonNull(provider, "provider must not be null"); + return this; + } + + @java.lang.Override + public AgentV1UpdateListenListen build() { + return new AgentV1UpdateListenListen(provider, additionalProperties); + } + + @java.lang.Override + public Builder additionalProperty(String key, Object value) { + this.additionalProperties.put(key, value); + return this; + } + + @java.lang.Override + public Builder additionalProperties(Map additionalProperties) { + this.additionalProperties.putAll(additionalProperties); + return this; + } + } +} diff --git a/src/main/java/com/deepgram/resources/agent/v1/types/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/websocket/V1WebSocketClient.java b/src/main/java/com/deepgram/resources/agent/v1/websocket/V1WebSocketClient.java index 567290d..d333064 100644 --- a/src/main/java/com/deepgram/resources/agent/v1/websocket/V1WebSocketClient.java +++ b/src/main/java/com/deepgram/resources/agent/v1/websocket/V1WebSocketClient.java @@ -20,6 +20,7 @@ import com.deepgram.resources.agent.v1.types.AgentV1InjectUserMessage; import com.deepgram.resources.agent.v1.types.AgentV1InjectionRefused; import com.deepgram.resources.agent.v1.types.AgentV1KeepAlive; +import com.deepgram.resources.agent.v1.types.AgentV1ListenUpdated; import com.deepgram.resources.agent.v1.types.AgentV1PromptUpdated; import com.deepgram.resources.agent.v1.types.AgentV1ReceiveFunctionCallResponse; import com.deepgram.resources.agent.v1.types.AgentV1SendFunctionCallResponse; @@ -27,6 +28,7 @@ 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.AgentV1UpdateListen; import com.deepgram.resources.agent.v1.types.AgentV1UpdatePrompt; import com.deepgram.resources.agent.v1.types.AgentV1UpdateSpeak; import com.deepgram.resources.agent.v1.types.AgentV1UpdateThink; @@ -74,14 +76,16 @@ public class V1WebSocketClient implements AutoCloseable { private ReconnectingWebSocketListener reconnectingListener; + private volatile Consumer listenUpdatedHandler; + + private volatile Consumer thinkUpdatedHandler; + private volatile Consumer onFunctionCallResponseHandler; private volatile Consumer promptUpdatedHandler; private volatile Consumer speakUpdatedHandler; - private volatile Consumer thinkUpdatedHandler; - private volatile Consumer injectionRefusedHandler; private volatile Consumer welcomeHandler; @@ -229,6 +233,24 @@ public CompletableFuture sendSettings(AgentV1Settings message) { return sendMessage(message); } + /** + * Sends an AgentV1UpdateListen message to the server asynchronously. + * @param message the message to send + * @return a CompletableFuture that completes when the message is sent + */ + public CompletableFuture sendUpdateListen(AgentV1UpdateListen message) { + return sendMessage(message); + } + + /** + * Sends an AgentV1UpdateThink message to the server asynchronously. + * @param message the message to send + * @return a CompletableFuture that completes when the message is sent + */ + public CompletableFuture sendUpdateThink(AgentV1UpdateThink message) { + return sendMessage(message); + } + /** * Sends an AgentV1UpdateSpeak message to the server asynchronously. * @param message the message to send @@ -283,15 +305,6 @@ public CompletableFuture sendUpdatePrompt(AgentV1UpdatePrompt message) { return sendMessage(message); } - /** - * Sends an AgentV1UpdateThink message to the server asynchronously. - * @param message the message to send - * @return a CompletableFuture that completes when the message is sent - */ - public CompletableFuture sendUpdateThink(AgentV1UpdateThink message) { - return sendMessage(message); - } - /** * Sends an AgentV1Media message to the server asynchronously. * @param message the message to send @@ -310,6 +323,22 @@ public CompletableFuture sendMedia(ByteString message) { return future; } + /** + * Registers a handler for AgentV1ListenUpdated messages from the server. + * @param handler the handler to invoke when a message is received + */ + public void onListenUpdated(Consumer handler) { + this.listenUpdatedHandler = handler; + } + + /** + * Registers a handler for AgentV1ThinkUpdated messages from the server. + * @param handler the handler to invoke when a message is received + */ + public void onThinkUpdated(Consumer handler) { + this.thinkUpdatedHandler = handler; + } + /** * Registers a handler for AgentV1ReceiveFunctionCallResponse messages from the server. * @param handler the handler to invoke when a message is received @@ -334,14 +363,6 @@ public void onSpeakUpdated(Consumer handler) { this.speakUpdatedHandler = handler; } - /** - * Registers a handler for AgentV1ThinkUpdated messages from the server. - * @param handler the handler to invoke when a message is received - */ - public void onThinkUpdated(Consumer handler) { - this.thinkUpdatedHandler = handler; - } - /** * Registers a handler for AgentV1InjectionRefused messages from the server. * @param handler the handler to invoke when a message is received @@ -667,6 +688,32 @@ private void handleIncomingMessage(String json) { return; } } + if ("ListenUpdated".equals(node.path("type").asText())) { + AgentV1ListenUpdated listenUpdatedHandlerEvent = null; + try { + listenUpdatedHandlerEvent = objectMapper.treeToValue(node, AgentV1ListenUpdated.class); + } catch (Exception e) { + } + if (listenUpdatedHandlerEvent != null) { + if (listenUpdatedHandler != null) { + listenUpdatedHandler.accept(listenUpdatedHandlerEvent); + } + return; + } + } + if ("ThinkUpdated".equals(node.path("type").asText())) { + AgentV1ThinkUpdated thinkUpdatedHandlerEvent = null; + try { + thinkUpdatedHandlerEvent = objectMapper.treeToValue(node, AgentV1ThinkUpdated.class); + } catch (Exception e) { + } + if (thinkUpdatedHandlerEvent != null) { + if (thinkUpdatedHandler != null) { + thinkUpdatedHandler.accept(thinkUpdatedHandlerEvent); + } + return; + } + } if ("PromptUpdated".equals(node.path("type").asText())) { AgentV1PromptUpdated promptUpdatedHandlerEvent = null; try { @@ -693,19 +740,6 @@ private void handleIncomingMessage(String json) { return; } } - if ("ThinkUpdated".equals(node.path("type").asText())) { - AgentV1ThinkUpdated thinkUpdatedHandlerEvent = null; - try { - thinkUpdatedHandlerEvent = objectMapper.treeToValue(node, AgentV1ThinkUpdated.class); - } catch (Exception e) { - } - if (thinkUpdatedHandlerEvent != null) { - if (thinkUpdatedHandler != null) { - thinkUpdatedHandler.accept(thinkUpdatedHandlerEvent); - } - return; - } - } if ("SettingsApplied".equals(node.path("type").asText())) { AgentV1SettingsApplied settingsAppliedHandlerEvent = null; try { diff --git a/src/main/java/com/deepgram/resources/speak/AsyncSpeakClient.java b/src/main/java/com/deepgram/resources/speak/AsyncSpeakClient.java index 4a9da9e..6222cd7 100644 --- a/src/main/java/com/deepgram/resources/speak/AsyncSpeakClient.java +++ b/src/main/java/com/deepgram/resources/speak/AsyncSpeakClient.java @@ -6,6 +6,7 @@ import com.deepgram.core.ClientOptions; import com.deepgram.core.Suppliers; import com.deepgram.resources.speak.v1.AsyncV1Client; +import com.deepgram.resources.speak.v2.AsyncV2Client; import java.util.function.Supplier; public class AsyncSpeakClient { @@ -13,12 +14,19 @@ public class AsyncSpeakClient { protected final Supplier v1Client; + protected final Supplier v2Client; + public AsyncSpeakClient(ClientOptions clientOptions) { this.clientOptions = clientOptions; this.v1Client = Suppliers.memoize(() -> new AsyncV1Client(clientOptions)); + this.v2Client = Suppliers.memoize(() -> new AsyncV2Client(clientOptions)); } public AsyncV1Client v1() { return this.v1Client.get(); } + + public AsyncV2Client v2() { + return this.v2Client.get(); + } } diff --git a/src/main/java/com/deepgram/resources/speak/SpeakClient.java b/src/main/java/com/deepgram/resources/speak/SpeakClient.java index c341119..5779e0b 100644 --- a/src/main/java/com/deepgram/resources/speak/SpeakClient.java +++ b/src/main/java/com/deepgram/resources/speak/SpeakClient.java @@ -6,6 +6,7 @@ import com.deepgram.core.ClientOptions; import com.deepgram.core.Suppliers; import com.deepgram.resources.speak.v1.V1Client; +import com.deepgram.resources.speak.v2.V2Client; import java.util.function.Supplier; public class SpeakClient { @@ -13,12 +14,19 @@ public class SpeakClient { protected final Supplier v1Client; + protected final Supplier v2Client; + public SpeakClient(ClientOptions clientOptions) { this.clientOptions = clientOptions; this.v1Client = Suppliers.memoize(() -> new V1Client(clientOptions)); + this.v2Client = Suppliers.memoize(() -> new V2Client(clientOptions)); } public V1Client v1() { return this.v1Client.get(); } + + public V2Client v2() { + return this.v2Client.get(); + } } diff --git a/src/main/java/com/deepgram/resources/speak/v2/AsyncV2Client.java b/src/main/java/com/deepgram/resources/speak/v2/AsyncV2Client.java new file mode 100644 index 0000000..f54fdb1 --- /dev/null +++ b/src/main/java/com/deepgram/resources/speak/v2/AsyncV2Client.java @@ -0,0 +1,22 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ +package com.deepgram.resources.speak.v2; + +import com.deepgram.core.ClientOptions; +import com.deepgram.resources.speak.v2.websocket.V2WebSocketClient; + +public class AsyncV2Client { + protected final ClientOptions clientOptions; + + public AsyncV2Client(ClientOptions clientOptions) { + this.clientOptions = clientOptions; + } + + /** + * Creates a new WebSocket client for the v2 channel. + */ + public V2WebSocketClient v2WebSocket() { + return new V2WebSocketClient(clientOptions); + } +} diff --git a/src/main/java/com/deepgram/resources/speak/v2/V2Client.java b/src/main/java/com/deepgram/resources/speak/v2/V2Client.java new file mode 100644 index 0000000..87095e4 --- /dev/null +++ b/src/main/java/com/deepgram/resources/speak/v2/V2Client.java @@ -0,0 +1,22 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ +package com.deepgram.resources.speak.v2; + +import com.deepgram.core.ClientOptions; +import com.deepgram.resources.speak.v2.websocket.V2WebSocketClient; + +public class V2Client { + protected final ClientOptions clientOptions; + + public V2Client(ClientOptions clientOptions) { + this.clientOptions = clientOptions; + } + + /** + * Creates a new WebSocket client for the v2 channel. + */ + public V2WebSocketClient v2WebSocket() { + return new V2WebSocketClient(clientOptions); + } +} 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 new file mode 100644 index 0000000..51fef19 --- /dev/null +++ b/src/main/java/com/deepgram/resources/speak/v2/types/SpeakV2Close.java @@ -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/SpeakV2Connected.java b/src/main/java/com/deepgram/resources/speak/v2/types/SpeakV2Connected.java new file mode 100644 index 0000000..64301ae --- /dev/null +++ b/src/main/java/com/deepgram/resources/speak/v2/types/SpeakV2Connected.java @@ -0,0 +1,272 @@ +/** + * 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.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import org.jetbrains.annotations.NotNull; + +@JsonInclude(JsonInclude.Include.NON_ABSENT) +@JsonDeserialize(builder = SpeakV2Connected.Builder.class) +public final class SpeakV2Connected { + private final String requestId; + + private final String modelName; + + private final String modelVersion; + + private final List modelUuids; + + private final Map additionalProperties; + + private SpeakV2Connected( + String requestId, + String modelName, + String modelVersion, + List modelUuids, + Map additionalProperties) { + this.requestId = requestId; + this.modelName = modelName; + this.modelVersion = modelVersion; + this.modelUuids = modelUuids; + this.additionalProperties = additionalProperties; + } + + /** + * @return Message type identifier + */ + @JsonProperty("type") + public String getType() { + return "Connected"; + } + + /** + * @return The unique identifier of the /v2/speak request + */ + @JsonProperty("request_id") + public String getRequestId() { + return requestId; + } + + /** + * @return Resolved model name + */ + @JsonProperty("model_name") + public String getModelName() { + return modelName; + } + + /** + * @return Resolved model version + */ + @JsonProperty("model_version") + public String getModelVersion() { + return modelVersion; + } + + /** + * @return Resolved model UUIDs. A list, because a resolved model may be backed by more than one underlying model. + */ + @JsonProperty("model_uuids") + public List getModelUuids() { + return modelUuids; + } + + @java.lang.Override + public boolean equals(Object other) { + if (this == other) return true; + return other instanceof SpeakV2Connected && equalTo((SpeakV2Connected) other); + } + + @JsonAnyGetter + public Map getAdditionalProperties() { + return this.additionalProperties; + } + + private boolean equalTo(SpeakV2Connected other) { + return requestId.equals(other.requestId) + && modelName.equals(other.modelName) + && modelVersion.equals(other.modelVersion) + && modelUuids.equals(other.modelUuids); + } + + @java.lang.Override + public int hashCode() { + return Objects.hash(this.requestId, this.modelName, this.modelVersion, this.modelUuids); + } + + @java.lang.Override + public String toString() { + return ObjectMappers.stringify(this); + } + + public static RequestIdStage builder() { + return new Builder(); + } + + public interface RequestIdStage { + /** + *

The unique identifier of the /v2/speak request

+ */ + ModelNameStage requestId(@NotNull String requestId); + + Builder from(SpeakV2Connected other); + } + + public interface ModelNameStage { + /** + *

Resolved model name

+ */ + ModelVersionStage modelName(@NotNull String modelName); + } + + public interface ModelVersionStage { + /** + *

Resolved model version

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

Resolved model UUIDs. A list, because a resolved model may be backed by more than one underlying model.

+ */ + _FinalStage modelUuids(List modelUuids); + + _FinalStage addModelUuids(String modelUuids); + + _FinalStage addAllModelUuids(List modelUuids); + } + + @JsonIgnoreProperties(ignoreUnknown = true) + public static final class Builder implements RequestIdStage, ModelNameStage, ModelVersionStage, _FinalStage { + private String requestId; + + private String modelName; + + private String modelVersion; + + private List modelUuids = new ArrayList<>(); + + @JsonAnySetter + private Map additionalProperties = new HashMap<>(); + + private Builder() {} + + @java.lang.Override + public Builder from(SpeakV2Connected other) { + requestId(other.getRequestId()); + modelName(other.getModelName()); + modelVersion(other.getModelVersion()); + modelUuids(other.getModelUuids()); + return this; + } + + /** + *

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. + */ + @java.lang.Override + @JsonSetter("request_id") + public ModelNameStage requestId(@NotNull String requestId) { + this.requestId = Objects.requireNonNull(requestId, "requestId must not be null"); + return this; + } + + /** + *

Resolved model name

+ *

Resolved model name

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

Resolved model version

+ *

Resolved model version

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

Resolved model UUIDs. A list, because a resolved model may be backed by more than one underlying model.

+ * @return Reference to {@code this} so that method calls can be chained together. + */ + @java.lang.Override + public _FinalStage addAllModelUuids(List modelUuids) { + if (modelUuids != null) { + this.modelUuids.addAll(modelUuids); + } + return this; + } + + /** + *

Resolved model UUIDs. A list, because a resolved model may be backed by more than one underlying model.

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

Resolved model UUIDs. A list, because a resolved model may be backed by more than one underlying model.

+ */ + @java.lang.Override + @JsonSetter(value = "model_uuids", nulls = Nulls.SKIP) + public _FinalStage modelUuids(List modelUuids) { + this.modelUuids.clear(); + if (modelUuids != null) { + this.modelUuids.addAll(modelUuids); + } + return this; + } + + @java.lang.Override + public SpeakV2Connected build() { + return new SpeakV2Connected(requestId, modelName, modelVersion, modelUuids, 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/SpeakV2Error.java b/src/main/java/com/deepgram/resources/speak/v2/types/SpeakV2Error.java new file mode 100644 index 0000000..288d11c --- /dev/null +++ b/src/main/java/com/deepgram/resources/speak/v2/types/SpeakV2Error.java @@ -0,0 +1,170 @@ +/** + * 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 = SpeakV2Error.Builder.class) +public final class SpeakV2Error { + private final SpeakV2ErrorCode code; + + private final String description; + + private final Map additionalProperties; + + private SpeakV2Error(SpeakV2ErrorCode code, String description, Map additionalProperties) { + this.code = code; + this.description = description; + this.additionalProperties = additionalProperties; + } + + /** + * @return Message type identifier + */ + @JsonProperty("type") + public String getType() { + return "Error"; + } + + /** + * @return A code identifying the error, e.g. MESSAGE-0000 or NET-0000. + */ + @JsonProperty("code") + public SpeakV2ErrorCode getCode() { + return code; + } + + /** + * @return Prose description of the error + */ + @JsonProperty("description") + public String getDescription() { + return description; + } + + @java.lang.Override + public boolean equals(Object other) { + if (this == other) return true; + return other instanceof SpeakV2Error && equalTo((SpeakV2Error) other); + } + + @JsonAnyGetter + public Map getAdditionalProperties() { + return this.additionalProperties; + } + + private boolean equalTo(SpeakV2Error other) { + return code.equals(other.code) && description.equals(other.description); + } + + @java.lang.Override + public int hashCode() { + return Objects.hash(this.code, this.description); + } + + @java.lang.Override + public String toString() { + return ObjectMappers.stringify(this); + } + + public static CodeStage builder() { + return new Builder(); + } + + public interface CodeStage { + /** + *

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

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

Prose description of the error

+ */ + _FinalStage description(@NotNull String description); + } + + public interface _FinalStage { + SpeakV2Error build(); + + _FinalStage additionalProperty(String key, Object value); + + _FinalStage additionalProperties(Map additionalProperties); + } + + @JsonIgnoreProperties(ignoreUnknown = true) + public static final class Builder implements CodeStage, DescriptionStage, _FinalStage { + private SpeakV2ErrorCode code; + + private String description; + + @JsonAnySetter + private Map additionalProperties = new HashMap<>(); + + private Builder() {} + + @java.lang.Override + public Builder from(SpeakV2Error other) { + code(other.getCode()); + description(other.getDescription()); + return this; + } + + /** + *

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. + */ + @java.lang.Override + @JsonSetter("code") + public DescriptionStage code(@NotNull SpeakV2ErrorCode code) { + this.code = Objects.requireNonNull(code, "code must not be null"); + return this; + } + + /** + *

Prose description of the error

+ *

Prose description of the error

+ * @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; + } + + @java.lang.Override + public SpeakV2Error build() { + return new SpeakV2Error(code, 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/SpeakV2ErrorCode.java b/src/main/java/com/deepgram/resources/speak/v2/types/SpeakV2ErrorCode.java new file mode 100644 index 0000000..d84171d --- /dev/null +++ b/src/main/java/com/deepgram/resources/speak/v2/types/SpeakV2ErrorCode.java @@ -0,0 +1,143 @@ +/** + * 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 SpeakV2ErrorCode { + public static final SpeakV2ErrorCode NET0002 = new SpeakV2ErrorCode(Value.NET0002, "NET-0002"); + + public static final SpeakV2ErrorCode NET0003 = new SpeakV2ErrorCode(Value.NET0003, "NET-0003"); + + public static final SpeakV2ErrorCode NET0001 = new SpeakV2ErrorCode(Value.NET0001, "NET-0001"); + + public static final SpeakV2ErrorCode NET0000 = new SpeakV2ErrorCode(Value.NET0000, "NET-0000"); + + public static final SpeakV2ErrorCode MESSAGE0000 = new SpeakV2ErrorCode(Value.MESSAGE0000, "MESSAGE-0000"); + + public static final SpeakV2ErrorCode DATA0000 = new SpeakV2ErrorCode(Value.DATA0000, "DATA-0000"); + + public static final SpeakV2ErrorCode BIG0000 = new SpeakV2ErrorCode(Value.BIG0000, "BIG-0000"); + + public static final SpeakV2ErrorCode NET0004 = new SpeakV2ErrorCode(Value.NET0004, "NET-0004"); + + private final Value value; + + private final String string; + + SpeakV2ErrorCode(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 SpeakV2ErrorCode && this.string.equals(((SpeakV2ErrorCode) other).string)); + } + + @java.lang.Override + public int hashCode() { + return this.string.hashCode(); + } + + public T visit(Visitor visitor) { + switch (value) { + case NET0002: + return visitor.visitNet0002(); + case NET0003: + return visitor.visitNet0003(); + case NET0001: + return visitor.visitNet0001(); + case NET0000: + return visitor.visitNet0000(); + case MESSAGE0000: + return visitor.visitMessage0000(); + case DATA0000: + return visitor.visitData0000(); + case BIG0000: + return visitor.visitBig0000(); + case NET0004: + return visitor.visitNet0004(); + case UNKNOWN: + default: + return visitor.visitUnknown(string); + } + } + + @JsonCreator(mode = JsonCreator.Mode.DELEGATING) + public static SpeakV2ErrorCode valueOf(String value) { + switch (value) { + case "NET-0002": + return NET0002; + case "NET-0003": + return NET0003; + case "NET-0001": + return NET0001; + case "NET-0000": + return NET0000; + case "MESSAGE-0000": + return MESSAGE0000; + case "DATA-0000": + return DATA0000; + case "BIG-0000": + return BIG0000; + case "NET-0004": + return NET0004; + default: + return new SpeakV2ErrorCode(Value.UNKNOWN, value); + } + } + + public enum Value { + MESSAGE0000, + + DATA0000, + + BIG0000, + + NET0000, + + NET0001, + + NET0002, + + NET0003, + + NET0004, + + UNKNOWN + } + + public interface Visitor { + T visitMessage0000(); + + T visitData0000(); + + T visitBig0000(); + + T visitNet0000(); + + T visitNet0001(); + + T visitNet0002(); + + T visitNet0003(); + + T visitNet0004(); + + T visitUnknown(String unknownType); + } +} 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 new file mode 100644 index 0000000..e122400 --- /dev/null +++ b/src/main/java/com/deepgram/resources/speak/v2/types/SpeakV2Flush.java @@ -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/types/SpeakV2Flushed.java b/src/main/java/com/deepgram/resources/speak/v2/types/SpeakV2Flushed.java new file mode 100644 index 0000000..76aa4f9 --- /dev/null +++ b/src/main/java/com/deepgram/resources/speak/v2/types/SpeakV2Flushed.java @@ -0,0 +1,137 @@ +/** + * 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 = SpeakV2Flushed.Builder.class) +public final class SpeakV2Flushed { + private final String speechId; + + private final Map additionalProperties; + + private SpeakV2Flushed(String speechId, Map additionalProperties) { + this.speechId = speechId; + this.additionalProperties = additionalProperties; + } + + /** + * @return Message type identifier + */ + @JsonProperty("type") + public String getType() { + return "Flushed"; + } + + /** + * @return Server-assigned turn identifier + */ + @JsonProperty("speech_id") + public String getSpeechId() { + return speechId; + } + + @java.lang.Override + public boolean equals(Object other) { + if (this == other) return true; + return other instanceof SpeakV2Flushed && equalTo((SpeakV2Flushed) other); + } + + @JsonAnyGetter + public Map getAdditionalProperties() { + return this.additionalProperties; + } + + private boolean equalTo(SpeakV2Flushed other) { + return speechId.equals(other.speechId); + } + + @java.lang.Override + public int hashCode() { + return Objects.hash(this.speechId); + } + + @java.lang.Override + public String toString() { + return ObjectMappers.stringify(this); + } + + public static SpeechIdStage builder() { + return new Builder(); + } + + public interface SpeechIdStage { + /** + *

Server-assigned turn identifier

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

Server-assigned turn identifier

+ *

Server-assigned turn identifier

+ * @return Reference to {@code this} so that method calls can be chained together. + */ + @java.lang.Override + @JsonSetter("speech_id") + public _FinalStage speechId(@NotNull String speechId) { + this.speechId = Objects.requireNonNull(speechId, "speechId must not be null"); + return this; + } + + @java.lang.Override + public SpeakV2Flushed build() { + return new SpeakV2Flushed(speechId, 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 new file mode 100644 index 0000000..a0d38ee --- /dev/null +++ b/src/main/java/com/deepgram/resources/speak/v2/types/SpeakV2SessionMetadata.java @@ -0,0 +1,213 @@ +/** + * 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 = SpeakV2SessionMetadata.Builder.class) +public final class SpeakV2SessionMetadata { + private final int totalAudioDurationMs; + + private final int totalInputCharacterCount; + + private final int totalBillableCharacterCount; + + private final Map additionalProperties; + + private SpeakV2SessionMetadata( + int totalAudioDurationMs, + int totalInputCharacterCount, + int totalBillableCharacterCount, + Map additionalProperties) { + this.totalAudioDurationMs = totalAudioDurationMs; + this.totalInputCharacterCount = totalInputCharacterCount; + this.totalBillableCharacterCount = totalBillableCharacterCount; + this.additionalProperties = additionalProperties; + } + + /** + * @return Message type identifier + */ + @JsonProperty("type") + public String getType() { + return "SessionMetadata"; + } + + /** + * @return Cumulative audio duration produced across the session, in milliseconds + */ + @JsonProperty("total_audio_duration_ms") + public int getTotalAudioDurationMs() { + return totalAudioDurationMs; + } + + /** + * @return Cumulative raw input character count across the session + */ + @JsonProperty("total_input_character_count") + public int getTotalInputCharacterCount() { + return totalInputCharacterCount; + } + + /** + * @return Cumulative billable character count across the session + */ + @JsonProperty("total_billable_character_count") + public int getTotalBillableCharacterCount() { + return totalBillableCharacterCount; + } + + @java.lang.Override + public boolean equals(Object other) { + if (this == other) return true; + return other instanceof SpeakV2SessionMetadata && equalTo((SpeakV2SessionMetadata) other); + } + + @JsonAnyGetter + public Map getAdditionalProperties() { + return this.additionalProperties; + } + + private boolean equalTo(SpeakV2SessionMetadata other) { + return totalAudioDurationMs == other.totalAudioDurationMs + && totalInputCharacterCount == other.totalInputCharacterCount + && totalBillableCharacterCount == other.totalBillableCharacterCount; + } + + @java.lang.Override + public int hashCode() { + return Objects.hash(this.totalAudioDurationMs, this.totalInputCharacterCount, this.totalBillableCharacterCount); + } + + @java.lang.Override + public String toString() { + return ObjectMappers.stringify(this); + } + + public static TotalAudioDurationMsStage builder() { + return new Builder(); + } + + public interface TotalAudioDurationMsStage { + /** + *

Cumulative audio duration produced across the session, in milliseconds

+ */ + TotalInputCharacterCountStage totalAudioDurationMs(int totalAudioDurationMs); + + Builder from(SpeakV2SessionMetadata other); + } + + public interface TotalInputCharacterCountStage { + /** + *

Cumulative raw input character count across the session

+ */ + TotalBillableCharacterCountStage totalInputCharacterCount(int totalInputCharacterCount); + } + + public interface TotalBillableCharacterCountStage { + /** + *

Cumulative billable character count across the session

+ */ + _FinalStage totalBillableCharacterCount(int totalBillableCharacterCount); + } + + public interface _FinalStage { + SpeakV2SessionMetadata build(); + + _FinalStage additionalProperty(String key, Object value); + + _FinalStage additionalProperties(Map additionalProperties); + } + + @JsonIgnoreProperties(ignoreUnknown = true) + public static final class Builder + implements TotalAudioDurationMsStage, + TotalInputCharacterCountStage, + TotalBillableCharacterCountStage, + _FinalStage { + private int totalAudioDurationMs; + + private int totalInputCharacterCount; + + private int totalBillableCharacterCount; + + @JsonAnySetter + private Map additionalProperties = new HashMap<>(); + + private Builder() {} + + @java.lang.Override + public Builder from(SpeakV2SessionMetadata other) { + totalAudioDurationMs(other.getTotalAudioDurationMs()); + totalInputCharacterCount(other.getTotalInputCharacterCount()); + totalBillableCharacterCount(other.getTotalBillableCharacterCount()); + return this; + } + + /** + *

Cumulative audio duration produced across the session, in milliseconds

+ *

Cumulative audio duration produced across the session, in milliseconds

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

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. + */ + @java.lang.Override + @JsonSetter("total_input_character_count") + public TotalBillableCharacterCountStage totalInputCharacterCount(int totalInputCharacterCount) { + this.totalInputCharacterCount = totalInputCharacterCount; + return this; + } + + /** + *

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. + */ + @java.lang.Override + @JsonSetter("total_billable_character_count") + public _FinalStage totalBillableCharacterCount(int totalBillableCharacterCount) { + this.totalBillableCharacterCount = totalBillableCharacterCount; + return this; + } + + @java.lang.Override + public SpeakV2SessionMetadata build() { + return new SpeakV2SessionMetadata( + totalAudioDurationMs, totalInputCharacterCount, totalBillableCharacterCount, 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/SpeakV2Speak.java b/src/main/java/com/deepgram/resources/speak/v2/types/SpeakV2Speak.java new file mode 100644 index 0000000..56476cc --- /dev/null +++ b/src/main/java/com/deepgram/resources/speak/v2/types/SpeakV2Speak.java @@ -0,0 +1,137 @@ +/** + * 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 = SpeakV2Speak.Builder.class) +public final class SpeakV2Speak { + private final String text; + + private final Map additionalProperties; + + private SpeakV2Speak(String text, Map additionalProperties) { + this.text = text; + this.additionalProperties = additionalProperties; + } + + /** + * @return Message type identifier + */ + @JsonProperty("type") + public String getType() { + return "Speak"; + } + + /** + * @return The input text to synthesize + */ + @JsonProperty("text") + public String getText() { + return text; + } + + @java.lang.Override + public boolean equals(Object other) { + if (this == other) return true; + return other instanceof SpeakV2Speak && equalTo((SpeakV2Speak) other); + } + + @JsonAnyGetter + public Map getAdditionalProperties() { + return this.additionalProperties; + } + + private boolean equalTo(SpeakV2Speak other) { + return text.equals(other.text); + } + + @java.lang.Override + public int hashCode() { + return Objects.hash(this.text); + } + + @java.lang.Override + public String toString() { + return ObjectMappers.stringify(this); + } + + public static TextStage builder() { + return new Builder(); + } + + public interface TextStage { + /** + *

The input text to synthesize

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

The input text to synthesize

+ *

The input text to synthesize

+ * @return Reference to {@code this} so that method calls can be chained together. + */ + @java.lang.Override + @JsonSetter("text") + public _FinalStage text(@NotNull String text) { + this.text = Objects.requireNonNull(text, "text must not be null"); + return this; + } + + @java.lang.Override + public SpeakV2Speak build() { + return new SpeakV2Speak(text, 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 new file mode 100644 index 0000000..e1c0ece --- /dev/null +++ b/src/main/java/com/deepgram/resources/speak/v2/types/SpeakV2SpeechMetadata.java @@ -0,0 +1,296 @@ +/** + * 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 = SpeakV2SpeechMetadata.Builder.class) +public final class SpeakV2SpeechMetadata { + private final String speechId; + + private final int audioDurationMs; + + private final int inputCharacterCount; + + private final int billableCharacterCount; + + private final SpeakV2SpeechMetadataControlsApplied controlsApplied; + + private final Map additionalProperties; + + private SpeakV2SpeechMetadata( + String speechId, + int audioDurationMs, + int inputCharacterCount, + int billableCharacterCount, + SpeakV2SpeechMetadataControlsApplied controlsApplied, + Map additionalProperties) { + this.speechId = speechId; + this.audioDurationMs = audioDurationMs; + this.inputCharacterCount = inputCharacterCount; + this.billableCharacterCount = billableCharacterCount; + this.controlsApplied = controlsApplied; + this.additionalProperties = additionalProperties; + } + + /** + * @return Message type identifier + */ + @JsonProperty("type") + public String getType() { + return "SpeechMetadata"; + } + + /** + * @return Server-assigned turn identifier + */ + @JsonProperty("speech_id") + public String getSpeechId() { + return speechId; + } + + /** + * @return Total 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 Controls applied during the turn. Inline pronunciation and pause controls are not available during Early Access, so every count is currently 0. + */ + @JsonProperty("controls_applied") + public SpeakV2SpeechMetadataControlsApplied getControlsApplied() { + return controlsApplied; + } + + @java.lang.Override + public boolean equals(Object other) { + if (this == other) return true; + return other instanceof SpeakV2SpeechMetadata && equalTo((SpeakV2SpeechMetadata) other); + } + + @JsonAnyGetter + public Map getAdditionalProperties() { + return this.additionalProperties; + } + + private boolean equalTo(SpeakV2SpeechMetadata 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(SpeakV2SpeechMetadata other); + } + + public interface AudioDurationMsStage { + /** + *

Total 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 { + /** + *

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

+ */ + _FinalStage controlsApplied(@NotNull SpeakV2SpeechMetadataControlsApplied controlsApplied); + } + + public interface _FinalStage { + SpeakV2SpeechMetadata 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 SpeakV2SpeechMetadataControlsApplied controlsApplied; + + @JsonAnySetter + private Map additionalProperties = new HashMap<>(); + + private Builder() {} + + @java.lang.Override + public Builder from(SpeakV2SpeechMetadata other) { + speechId(other.getSpeechId()); + audioDurationMs(other.getAudioDurationMs()); + inputCharacterCount(other.getInputCharacterCount()); + billableCharacterCount(other.getBillableCharacterCount()); + controlsApplied(other.getControlsApplied()); + return this; + } + + /** + *

Server-assigned turn identifier

+ *

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

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

+ *

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.

+ *

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

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.

+ * @return Reference to {@code this} so that method calls can be chained together. + */ + @java.lang.Override + @JsonSetter("controls_applied") + public _FinalStage controlsApplied(@NotNull SpeakV2SpeechMetadataControlsApplied controlsApplied) { + this.controlsApplied = Objects.requireNonNull(controlsApplied, "controlsApplied must not be null"); + return this; + } + + @java.lang.Override + public SpeakV2SpeechMetadata build() { + return new SpeakV2SpeechMetadata( + 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/SpeakV2SpeechMetadataControlsApplied.java b/src/main/java/com/deepgram/resources/speak/v2/types/SpeakV2SpeechMetadataControlsApplied.java new file mode 100644 index 0000000..8225d04 --- /dev/null +++ b/src/main/java/com/deepgram/resources/speak/v2/types/SpeakV2SpeechMetadataControlsApplied.java @@ -0,0 +1,165 @@ +/** + * 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 = SpeakV2SpeechMetadataControlsApplied.Builder.class) +public final class SpeakV2SpeechMetadataControlsApplied { + private final int pronunciationsApplied; + + private final int pronunciationWarnings; + + private final Map additionalProperties; + + private SpeakV2SpeechMetadataControlsApplied( + int pronunciationsApplied, int pronunciationWarnings, Map additionalProperties) { + this.pronunciationsApplied = pronunciationsApplied; + 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. + */ + @JsonProperty("pronunciations_applied") + public int getPronunciationsApplied() { + return pronunciationsApplied; + } + + /** + * @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. + */ + @JsonProperty("pronunciation_warnings") + public int getPronunciationWarnings() { + return pronunciationWarnings; + } + + @java.lang.Override + public boolean equals(Object other) { + if (this == other) return true; + return other instanceof SpeakV2SpeechMetadataControlsApplied + && equalTo((SpeakV2SpeechMetadataControlsApplied) other); + } + + @JsonAnyGetter + public Map getAdditionalProperties() { + return this.additionalProperties; + } + + private boolean equalTo(SpeakV2SpeechMetadataControlsApplied other) { + return pronunciationsApplied == other.pronunciationsApplied + && pronunciationWarnings == other.pronunciationWarnings; + } + + @java.lang.Override + public int hashCode() { + return Objects.hash(this.pronunciationsApplied, 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. Always 0 during Early Access.

+ */ + PronunciationWarningsStage pronunciationsApplied(int pronunciationsApplied); + + Builder from(SpeakV2SpeechMetadataControlsApplied other); + } + + 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.

+ */ + _FinalStage pronunciationWarnings(int pronunciationWarnings); + } + + public interface _FinalStage { + SpeakV2SpeechMetadataControlsApplied build(); + + _FinalStage additionalProperty(String key, Object value); + + _FinalStage additionalProperties(Map additionalProperties); + } + + @JsonIgnoreProperties(ignoreUnknown = true) + public static final class Builder implements PronunciationsAppliedStage, PronunciationWarningsStage, _FinalStage { + private int pronunciationsApplied; + + private int pronunciationWarnings; + + @JsonAnySetter + private Map additionalProperties = new HashMap<>(); + + private Builder() {} + + @java.lang.Override + public Builder from(SpeakV2SpeechMetadataControlsApplied other) { + pronunciationsApplied(other.getPronunciationsApplied()); + 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.

+ * @return Reference to {@code this} so that method calls can be chained together. + */ + @java.lang.Override + @JsonSetter("pronunciations_applied") + public PronunciationWarningsStage 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.

+ * @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 SpeakV2SpeechMetadataControlsApplied build() { + return new SpeakV2SpeechMetadataControlsApplied( + pronunciationsApplied, 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/SpeakV2SpeechStarted.java b/src/main/java/com/deepgram/resources/speak/v2/types/SpeakV2SpeechStarted.java new file mode 100644 index 0000000..b83be2f --- /dev/null +++ b/src/main/java/com/deepgram/resources/speak/v2/types/SpeakV2SpeechStarted.java @@ -0,0 +1,137 @@ +/** + * 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 = SpeakV2SpeechStarted.Builder.class) +public final class SpeakV2SpeechStarted { + private final String speechId; + + private final Map additionalProperties; + + private SpeakV2SpeechStarted(String speechId, Map additionalProperties) { + this.speechId = speechId; + this.additionalProperties = additionalProperties; + } + + /** + * @return Message type identifier + */ + @JsonProperty("type") + public String getType() { + return "SpeechStarted"; + } + + /** + * @return Server-minted identifier for this turn, of the form dg_sp_<12 hex digits>. Informational. + */ + @JsonProperty("speech_id") + public String getSpeechId() { + return speechId; + } + + @java.lang.Override + public boolean equals(Object other) { + if (this == other) return true; + return other instanceof SpeakV2SpeechStarted && equalTo((SpeakV2SpeechStarted) other); + } + + @JsonAnyGetter + public Map getAdditionalProperties() { + return this.additionalProperties; + } + + private boolean equalTo(SpeakV2SpeechStarted other) { + return speechId.equals(other.speechId); + } + + @java.lang.Override + public int hashCode() { + return Objects.hash(this.speechId); + } + + @java.lang.Override + public String toString() { + return ObjectMappers.stringify(this); + } + + public static SpeechIdStage builder() { + return new Builder(); + } + + public interface SpeechIdStage { + /** + *

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

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

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. + */ + @java.lang.Override + @JsonSetter("speech_id") + public _FinalStage speechId(@NotNull String speechId) { + this.speechId = Objects.requireNonNull(speechId, "speechId must not be null"); + return this; + } + + @java.lang.Override + public SpeakV2SpeechStarted build() { + return new SpeakV2SpeechStarted(speechId, 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/SpeakV2Warning.java b/src/main/java/com/deepgram/resources/speak/v2/types/SpeakV2Warning.java new file mode 100644 index 0000000..1542479 --- /dev/null +++ b/src/main/java/com/deepgram/resources/speak/v2/types/SpeakV2Warning.java @@ -0,0 +1,170 @@ +/** + * 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 = SpeakV2Warning.Builder.class) +public final class SpeakV2Warning { + private final String code; + + private final String description; + + private final Map additionalProperties; + + private SpeakV2Warning(String code, String description, Map additionalProperties) { + this.code = code; + this.description = description; + this.additionalProperties = additionalProperties; + } + + /** + * @return Message type identifier + */ + @JsonProperty("type") + public String getType() { + return "Warning"; + } + + /** + * @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). + */ + @JsonProperty("code") + public String getCode() { + return code; + } + + /** + * @return A human-readable description of the warning + */ + @JsonProperty("description") + public String getDescription() { + return description; + } + + @java.lang.Override + public boolean equals(Object other) { + if (this == other) return true; + return other instanceof SpeakV2Warning && equalTo((SpeakV2Warning) other); + } + + @JsonAnyGetter + public Map getAdditionalProperties() { + return this.additionalProperties; + } + + private boolean equalTo(SpeakV2Warning other) { + return code.equals(other.code) && description.equals(other.description); + } + + @java.lang.Override + public int hashCode() { + return Objects.hash(this.code, this.description); + } + + @java.lang.Override + public String toString() { + return ObjectMappers.stringify(this); + } + + public static CodeStage builder() { + return new 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).

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

A human-readable description of the warning

+ */ + _FinalStage description(@NotNull String description); + } + + public interface _FinalStage { + SpeakV2Warning build(); + + _FinalStage additionalProperty(String key, Object value); + + _FinalStage additionalProperties(Map additionalProperties); + } + + @JsonIgnoreProperties(ignoreUnknown = true) + public static final class Builder implements CodeStage, DescriptionStage, _FinalStage { + private String code; + + private String description; + + @JsonAnySetter + private Map additionalProperties = new HashMap<>(); + + private Builder() {} + + @java.lang.Override + public Builder from(SpeakV2Warning other) { + code(other.getCode()); + description(other.getDescription()); + return this; + } + + /** + *

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

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

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. + */ + @java.lang.Override + @JsonSetter("description") + public _FinalStage description(@NotNull String description) { + this.description = Objects.requireNonNull(description, "description must not be null"); + return this; + } + + @java.lang.Override + public SpeakV2Warning build() { + return new SpeakV2Warning(code, 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/websocket/V2ConnectOptions.java b/src/main/java/com/deepgram/resources/speak/v2/websocket/V2ConnectOptions.java new file mode 100644 index 0000000..d99498f --- /dev/null +++ b/src/main/java/com/deepgram/resources/speak/v2/websocket/V2ConnectOptions.java @@ -0,0 +1,246 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ +package com.deepgram.resources.speak.v2.websocket; + +import com.deepgram.core.ObjectMappers; +import com.deepgram.types.SpeakV2Encoding; +import com.deepgram.types.SpeakV2MipOptOut; +import com.deepgram.types.SpeakV2SampleRate; +import com.deepgram.types.SpeakV2Tag; +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 = V2ConnectOptions.Builder.class) +public final class V2ConnectOptions { + private final String model; + + private final Optional encoding; + + private final Optional sampleRate; + + private final Optional mipOptOut; + + private final Optional tag; + + private final Map additionalProperties; + + private V2ConnectOptions( + String model, + Optional encoding, + Optional sampleRate, + Optional mipOptOut, + Optional tag, + Map additionalProperties) { + this.model = model; + this.encoding = encoding; + this.sampleRate = sampleRate; + this.mipOptOut = mipOptOut; + this.tag = tag; + this.additionalProperties = additionalProperties; + } + + @JsonProperty("model") + public String getModel() { + return model; + } + + @JsonProperty("encoding") + public Optional getEncoding() { + return encoding; + } + + @JsonProperty("sample_rate") + public Optional getSampleRate() { + return sampleRate; + } + + @JsonProperty("mip_opt_out") + public Optional getMipOptOut() { + return mipOptOut; + } + + @JsonProperty("tag") + public Optional getTag() { + return tag; + } + + @java.lang.Override + public boolean equals(Object other) { + if (this == other) return true; + return other instanceof V2ConnectOptions && equalTo((V2ConnectOptions) other); + } + + @JsonAnyGetter + public Map getAdditionalProperties() { + return this.additionalProperties; + } + + private boolean equalTo(V2ConnectOptions other) { + return model.equals(other.model) + && encoding.equals(other.encoding) + && sampleRate.equals(other.sampleRate) + && 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); + } + + @java.lang.Override + public String toString() { + return ObjectMappers.stringify(this); + } + + public static ModelStage builder() { + return new Builder(); + } + + public interface ModelStage { + _FinalStage model(@NotNull String model); + + Builder from(V2ConnectOptions other); + } + + public interface _FinalStage { + V2ConnectOptions build(); + + _FinalStage additionalProperty(String key, Object value); + + _FinalStage additionalProperties(Map additionalProperties); + + _FinalStage encoding(Optional encoding); + + _FinalStage encoding(SpeakV2Encoding encoding); + + _FinalStage sampleRate(Optional sampleRate); + + _FinalStage sampleRate(SpeakV2SampleRate sampleRate); + + _FinalStage mipOptOut(Optional mipOptOut); + + _FinalStage mipOptOut(SpeakV2MipOptOut mipOptOut); + + _FinalStage tag(Optional tag); + + _FinalStage tag(SpeakV2Tag tag); + } + + @JsonIgnoreProperties(ignoreUnknown = true) + public static final class Builder implements ModelStage, _FinalStage { + private String model; + + private Optional tag = Optional.empty(); + + private Optional mipOptOut = Optional.empty(); + + private Optional sampleRate = Optional.empty(); + + private Optional encoding = Optional.empty(); + + @JsonAnySetter + private Map additionalProperties = new HashMap<>(); + + private Builder() {} + + @java.lang.Override + public Builder from(V2ConnectOptions other) { + model(other.getModel()); + encoding(other.getEncoding()); + sampleRate(other.getSampleRate()); + mipOptOut(other.getMipOptOut()); + tag(other.getTag()); + return this; + } + + @java.lang.Override + @JsonSetter("model") + public _FinalStage model(@NotNull String model) { + this.model = Objects.requireNonNull(model, "model must not be null"); + return this; + } + + @java.lang.Override + public _FinalStage tag(SpeakV2Tag tag) { + this.tag = Optional.ofNullable(tag); + return this; + } + + @java.lang.Override + @JsonSetter(value = "tag", nulls = Nulls.SKIP) + public _FinalStage tag(Optional tag) { + this.tag = tag; + return this; + } + + @java.lang.Override + public _FinalStage mipOptOut(SpeakV2MipOptOut mipOptOut) { + this.mipOptOut = Optional.ofNullable(mipOptOut); + return this; + } + + @java.lang.Override + @JsonSetter(value = "mip_opt_out", nulls = Nulls.SKIP) + public _FinalStage mipOptOut(Optional mipOptOut) { + this.mipOptOut = mipOptOut; + return this; + } + + @java.lang.Override + public _FinalStage sampleRate(SpeakV2SampleRate sampleRate) { + this.sampleRate = Optional.ofNullable(sampleRate); + return this; + } + + @java.lang.Override + @JsonSetter(value = "sample_rate", nulls = Nulls.SKIP) + public _FinalStage sampleRate(Optional sampleRate) { + this.sampleRate = sampleRate; + return this; + } + + @java.lang.Override + public _FinalStage encoding(SpeakV2Encoding encoding) { + this.encoding = Optional.ofNullable(encoding); + return this; + } + + @java.lang.Override + @JsonSetter(value = "encoding", nulls = Nulls.SKIP) + public _FinalStage encoding(Optional encoding) { + this.encoding = encoding; + return this; + } + + @java.lang.Override + public V2ConnectOptions build() { + return new V2ConnectOptions(model, encoding, sampleRate, mipOptOut, tag, 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/websocket/V2WebSocketClient.java b/src/main/java/com/deepgram/resources/speak/v2/websocket/V2WebSocketClient.java new file mode 100644 index 0000000..cf23bcf --- /dev/null +++ b/src/main/java/com/deepgram/resources/speak/v2/websocket/V2WebSocketClient.java @@ -0,0 +1,507 @@ +/** + * 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.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()) { + urlBuilder.addQueryParameter("tag", String.valueOf(options.getTag().get())); + } + 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; + } + } + if (onErrorHandler != null) { + onErrorHandler.accept(new RuntimeException( + "Unrecognized WebSocket message: " + json.substring(0, Math.min(200, json.length())) + + "... Update your SDK version to support new message types.")); + } + } catch (Exception e) { + if (onErrorHandler != null) { + onErrorHandler.accept(e); + } + } + } +} diff --git a/src/main/java/com/deepgram/types/DeepgramListenProviderV2.java b/src/main/java/com/deepgram/types/DeepgramListenProviderV2.java index 4c0af19..560f010 100644 --- a/src/main/java/com/deepgram/types/DeepgramListenProviderV2.java +++ b/src/main/java/com/deepgram/types/DeepgramListenProviderV2.java @@ -28,6 +28,12 @@ public final class DeepgramListenProviderV2 { private final Optional> languageHints; + private final Optional eotThreshold; + + private final Optional eagerEotThreshold; + + private final Optional eotTimeoutMs; + private final Optional> keyterms; private final Map additionalProperties; @@ -36,11 +42,17 @@ private DeepgramListenProviderV2( Optional version, String model, Optional> languageHints, + Optional eotThreshold, + Optional eagerEotThreshold, + Optional eotTimeoutMs, Optional> keyterms, Map additionalProperties) { this.version = version; this.model = model; this.languageHints = languageHints; + this.eotThreshold = eotThreshold; + this.eagerEotThreshold = eagerEotThreshold; + this.eotTimeoutMs = eotTimeoutMs; this.keyterms = keyterms; this.additionalProperties = additionalProperties; } @@ -77,6 +89,30 @@ public Optional> getLanguageHints() { return languageHints; } + /** + * @return End-of-turn confidence required to finish a turn. Valid range: 0.5 - 0.9. Defaults to 0.7. + */ + @JsonProperty("eot_threshold") + public Optional getEotThreshold() { + return eotThreshold; + } + + /** + * @return End-of-turn confidence required to fire an eager end-of-turn event. When set, enables EagerEndOfTurn and TurnResumed events. Valid range: 0.3 - 0.9. + */ + @JsonProperty("eager_eot_threshold") + public Optional getEagerEotThreshold() { + return eagerEotThreshold; + } + + /** + * @return A turn will be finished when this much time in milliseconds has passed after speech, regardless of EOT confidence. Defaults to 5000. + */ + @JsonProperty("eot_timeout_ms") + public Optional getEotTimeoutMs() { + return eotTimeoutMs; + } + /** * @return Prompt keyterm recognition to improve Keyword Recall Rate */ @@ -100,12 +136,22 @@ private boolean equalTo(DeepgramListenProviderV2 other) { return version.equals(other.version) && model.equals(other.model) && languageHints.equals(other.languageHints) + && eotThreshold.equals(other.eotThreshold) + && eagerEotThreshold.equals(other.eagerEotThreshold) + && eotTimeoutMs.equals(other.eotTimeoutMs) && keyterms.equals(other.keyterms); } @java.lang.Override public int hashCode() { - return Objects.hash(this.version, this.model, this.languageHints, this.keyterms); + return Objects.hash( + this.version, + this.model, + this.languageHints, + this.eotThreshold, + this.eagerEotThreshold, + this.eotTimeoutMs, + this.keyterms); } @java.lang.Override @@ -147,6 +193,27 @@ public interface _FinalStage { _FinalStage languageHints(List languageHints); + /** + *

End-of-turn confidence required to finish a turn. Valid range: 0.5 - 0.9. Defaults to 0.7.

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

End-of-turn confidence required to fire an eager end-of-turn event. When set, enables EagerEndOfTurn and TurnResumed events. Valid range: 0.3 - 0.9.

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

A turn will be finished when this much time in milliseconds has passed after speech, regardless of EOT confidence. Defaults to 5000.

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

Prompt keyterm recognition to improve Keyword Recall Rate

*/ @@ -161,6 +228,12 @@ public static final class Builder implements ModelStage, _FinalStage { private Optional> keyterms = Optional.empty(); + private Optional eotTimeoutMs = Optional.empty(); + + private Optional eagerEotThreshold = Optional.empty(); + + private Optional eotThreshold = Optional.empty(); + private Optional> languageHints = Optional.empty(); private Optional version = Optional.empty(); @@ -175,6 +248,9 @@ public Builder from(DeepgramListenProviderV2 other) { version(other.getVersion()); model(other.getModel()); languageHints(other.getLanguageHints()); + eotThreshold(other.getEotThreshold()); + eagerEotThreshold(other.getEagerEotThreshold()); + eotTimeoutMs(other.getEotTimeoutMs()); keyterms(other.getKeyterms()); return this; } @@ -211,6 +287,66 @@ public _FinalStage keyterms(Optional> keyterms) { return this; } + /** + *

A turn will be finished when this much time in milliseconds has passed after speech, regardless of EOT confidence. Defaults to 5000.

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

A turn will be finished when this much time in milliseconds has passed after speech, regardless of EOT confidence. Defaults to 5000.

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

End-of-turn confidence required to fire an eager end-of-turn event. When set, enables EagerEndOfTurn and TurnResumed events. Valid range: 0.3 - 0.9.

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

End-of-turn confidence required to fire an eager end-of-turn event. When set, enables EagerEndOfTurn and TurnResumed events. Valid range: 0.3 - 0.9.

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

End-of-turn confidence required to finish a turn. Valid range: 0.5 - 0.9. Defaults to 0.7.

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

End-of-turn confidence required to finish a turn. Valid range: 0.5 - 0.9. Defaults to 0.7.

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

An array of one or more BCP-47 language codes to bias the model toward specific languages. Only supported when model is flux-general-multi. Without hints, the model auto-detects the spoken language. See the Language Prompting guide for details.

* @return Reference to {@code this} so that method calls can be chained together. @@ -253,7 +389,15 @@ public _FinalStage version(Optional version) { @java.lang.Override public DeepgramListenProviderV2 build() { - return new DeepgramListenProviderV2(version, model, languageHints, keyterms, additionalProperties); + return new DeepgramListenProviderV2( + version, + model, + languageHints, + eotThreshold, + eagerEotThreshold, + eotTimeoutMs, + keyterms, + additionalProperties); } @java.lang.Override diff --git a/src/main/java/com/deepgram/types/SpeakV2Encoding.java b/src/main/java/com/deepgram/types/SpeakV2Encoding.java new file mode 100644 index 0000000..456868f --- /dev/null +++ b/src/main/java/com/deepgram/types/SpeakV2Encoding.java @@ -0,0 +1,93 @@ +/** + * 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 SpeakV2Encoding { + public static final SpeakV2Encoding MULAW = new SpeakV2Encoding(Value.MULAW, "mulaw"); + + public static final SpeakV2Encoding LINEAR16 = new SpeakV2Encoding(Value.LINEAR16, "linear16"); + + public static final SpeakV2Encoding ALAW = new SpeakV2Encoding(Value.ALAW, "alaw"); + + private final Value value; + + private final String string; + + SpeakV2Encoding(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 SpeakV2Encoding && this.string.equals(((SpeakV2Encoding) other).string)); + } + + @java.lang.Override + public int hashCode() { + return this.string.hashCode(); + } + + public T visit(Visitor visitor) { + switch (value) { + case MULAW: + return visitor.visitMulaw(); + case LINEAR16: + return visitor.visitLinear16(); + case ALAW: + return visitor.visitAlaw(); + case UNKNOWN: + default: + return visitor.visitUnknown(string); + } + } + + @JsonCreator(mode = JsonCreator.Mode.DELEGATING) + public static SpeakV2Encoding valueOf(String value) { + switch (value) { + case "mulaw": + return MULAW; + case "linear16": + return LINEAR16; + case "alaw": + return ALAW; + default: + return new SpeakV2Encoding(Value.UNKNOWN, value); + } + } + + public enum Value { + LINEAR16, + + MULAW, + + ALAW, + + UNKNOWN + } + + public interface Visitor { + T visitLinear16(); + + T visitMulaw(); + + T visitAlaw(); + + T visitUnknown(String unknownType); + } +} diff --git a/src/main/java/com/deepgram/types/SpeakV2MipOptOut.java b/src/main/java/com/deepgram/types/SpeakV2MipOptOut.java new file mode 100644 index 0000000..2a495ea --- /dev/null +++ b/src/main/java/com/deepgram/types/SpeakV2MipOptOut.java @@ -0,0 +1,42 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ +package com.deepgram.types; + +import com.deepgram.core.WrappedAlias; +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonValue; + +public final class SpeakV2MipOptOut implements WrappedAlias { + private final Object value; + + private SpeakV2MipOptOut(Object value) { + this.value = value; + } + + @JsonValue + public Object get() { + return this.value; + } + + @java.lang.Override + public boolean equals(Object other) { + return this == other + || (other instanceof SpeakV2MipOptOut && this.value.equals(((SpeakV2MipOptOut) other).value)); + } + + @java.lang.Override + public int hashCode() { + return value.hashCode(); + } + + @java.lang.Override + public String toString() { + return value.toString(); + } + + @JsonCreator(mode = JsonCreator.Mode.DELEGATING) + public static SpeakV2MipOptOut of(Object value) { + return new SpeakV2MipOptOut(value); + } +} diff --git a/src/main/java/com/deepgram/types/SpeakV2SampleRate.java b/src/main/java/com/deepgram/types/SpeakV2SampleRate.java new file mode 100644 index 0000000..a82fa5d --- /dev/null +++ b/src/main/java/com/deepgram/types/SpeakV2SampleRate.java @@ -0,0 +1,127 @@ +/** + * 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 SpeakV2SampleRate { + public static final SpeakV2SampleRate SIXTEEN_THOUSAND = new SpeakV2SampleRate(Value.SIXTEEN_THOUSAND, "16000"); + + public static final SpeakV2SampleRate EIGHT_THOUSAND = new SpeakV2SampleRate(Value.EIGHT_THOUSAND, "8000"); + + public static final SpeakV2SampleRate FORTY_EIGHT_THOUSAND = + new SpeakV2SampleRate(Value.FORTY_EIGHT_THOUSAND, "48000"); + + public static final SpeakV2SampleRate TWENTY_FOUR_THOUSAND = + new SpeakV2SampleRate(Value.TWENTY_FOUR_THOUSAND, "24000"); + + public static final SpeakV2SampleRate FORTY_FOUR_THOUSAND_ONE_HUNDRED = + new SpeakV2SampleRate(Value.FORTY_FOUR_THOUSAND_ONE_HUNDRED, "44100"); + + public static final SpeakV2SampleRate THIRTY_TWO_THOUSAND = + new SpeakV2SampleRate(Value.THIRTY_TWO_THOUSAND, "32000"); + + private final Value value; + + private final String string; + + SpeakV2SampleRate(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 SpeakV2SampleRate && this.string.equals(((SpeakV2SampleRate) other).string)); + } + + @java.lang.Override + public int hashCode() { + return this.string.hashCode(); + } + + public T visit(Visitor visitor) { + switch (value) { + case SIXTEEN_THOUSAND: + return visitor.visitSixteenThousand(); + case EIGHT_THOUSAND: + return visitor.visitEightThousand(); + case FORTY_EIGHT_THOUSAND: + return visitor.visitFortyEightThousand(); + case TWENTY_FOUR_THOUSAND: + return visitor.visitTwentyFourThousand(); + case FORTY_FOUR_THOUSAND_ONE_HUNDRED: + return visitor.visitFortyFourThousandOneHundred(); + case THIRTY_TWO_THOUSAND: + return visitor.visitThirtyTwoThousand(); + case UNKNOWN: + default: + return visitor.visitUnknown(string); + } + } + + @JsonCreator(mode = JsonCreator.Mode.DELEGATING) + public static SpeakV2SampleRate valueOf(String value) { + switch (value) { + case "16000": + return SIXTEEN_THOUSAND; + case "8000": + return EIGHT_THOUSAND; + case "48000": + return FORTY_EIGHT_THOUSAND; + case "24000": + return TWENTY_FOUR_THOUSAND; + case "44100": + return FORTY_FOUR_THOUSAND_ONE_HUNDRED; + case "32000": + return THIRTY_TWO_THOUSAND; + default: + return new SpeakV2SampleRate(Value.UNKNOWN, value); + } + } + + public enum Value { + EIGHT_THOUSAND, + + SIXTEEN_THOUSAND, + + TWENTY_FOUR_THOUSAND, + + THIRTY_TWO_THOUSAND, + + FORTY_FOUR_THOUSAND_ONE_HUNDRED, + + FORTY_EIGHT_THOUSAND, + + UNKNOWN + } + + public interface Visitor { + T visitEightThousand(); + + T visitSixteenThousand(); + + T visitTwentyFourThousand(); + + T visitThirtyTwoThousand(); + + T visitFortyFourThousandOneHundred(); + + T visitFortyEightThousand(); + + T visitUnknown(String unknownType); + } +} diff --git a/src/main/java/com/deepgram/types/SpeakV2Tag.java b/src/main/java/com/deepgram/types/SpeakV2Tag.java new file mode 100644 index 0000000..69d03ef --- /dev/null +++ b/src/main/java/com/deepgram/types/SpeakV2Tag.java @@ -0,0 +1,41 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ +package com.deepgram.types; + +import com.deepgram.core.WrappedAlias; +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonValue; + +public final class SpeakV2Tag implements WrappedAlias { + private final Object value; + + private SpeakV2Tag(Object value) { + this.value = value; + } + + @JsonValue + public Object get() { + return this.value; + } + + @java.lang.Override + public boolean equals(Object other) { + return this == other || (other instanceof SpeakV2Tag && this.value.equals(((SpeakV2Tag) other).value)); + } + + @java.lang.Override + public int hashCode() { + return value.hashCode(); + } + + @java.lang.Override + public String toString() { + return value.toString(); + } + + @JsonCreator(mode = JsonCreator.Mode.DELEGATING) + public static SpeakV2Tag of(Object value) { + return new SpeakV2Tag(value); + } +} diff --git a/src/test/java/com/deepgram/IntegrationTest.java b/src/test/java/com/deepgram/IntegrationTest.java index 551f8c4..2fe794f 100644 --- a/src/test/java/com/deepgram/IntegrationTest.java +++ b/src/test/java/com/deepgram/IntegrationTest.java @@ -9,6 +9,13 @@ import com.deepgram.resources.listen.v1.media.types.MediaTranscribeResponse; import com.deepgram.resources.read.v1.text.requests.TextAnalyzeRequest; import com.deepgram.resources.speak.v1.audio.requests.SpeakV1Request; +import com.deepgram.resources.speak.v2.types.SpeakV2Close; +import com.deepgram.resources.speak.v2.types.SpeakV2Flush; +import com.deepgram.resources.speak.v2.types.SpeakV2Speak; +import com.deepgram.resources.speak.v2.websocket.V2ConnectOptions; +import com.deepgram.resources.speak.v2.websocket.V2WebSocketClient; +import com.deepgram.types.SpeakV2Encoding; +import com.deepgram.types.SpeakV2SampleRate; import com.deepgram.types.ListProjectsV1Response; import com.deepgram.types.ListProjectsV1ResponseProjectsItem; import com.deepgram.types.ListenV1AcceptedResponse; @@ -23,6 +30,10 @@ import java.net.URL; import java.util.List; import java.util.Optional; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicLong; +import java.util.concurrent.atomic.AtomicReference; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Nested; @@ -221,5 +232,57 @@ void testIntegration_ManageProjects() { assertThat(projects).as("expected at least one project").isNotEmpty(); System.out.println("Found " + projects.size() + " projects"); } + + @Test + @DisplayName("SpeakV2WebSocket - stream TTS audio over the Speak v2 WebSocket") + void testIntegration_SpeakV2WebSocket() throws Exception { + // The Speak v2 WebSocket endpoint is not yet generally available on the production URL + // this key targets, so this test is opt-in: set DEEPGRAM_SPEAK_V2_WS=1 in an environment + // with access (e.g. staging). Otherwise it is skipped rather than failing the build. + String enabled = System.getenv("DEEPGRAM_SPEAK_V2_WS"); + org.junit.jupiter.api.Assumptions.assumeTrue( + enabled != null && !enabled.isEmpty(), + "DEEPGRAM_SPEAK_V2_WS not set, skipping Speak v2 WebSocket integration test"); + + V2WebSocketClient wsClient = client.speak().v2().v2WebSocket(); + + CountDownLatch flushedLatch = new CountDownLatch(1); + AtomicLong totalAudioBytes = new AtomicLong(0); + AtomicReference serverError = new AtomicReference<>(); + + wsClient.onSpeakV2Audio(audio -> totalAudioBytes.addAndGet(audio.size())); + wsClient.onFlushed(flushed -> flushedLatch.countDown()); + wsClient.onErrorMessage(error -> serverError.set(String.valueOf(error))); + wsClient.onError(error -> serverError.set(error.getMessage())); + + try { + V2ConnectOptions options = V2ConnectOptions.builder() + .model("aura-2-thalia-en") + .encoding(SpeakV2Encoding.LINEAR16) + .sampleRate(SpeakV2SampleRate.SIXTEEN_THOUSAND) + .build(); + wsClient.connect(options).get(15, TimeUnit.SECONDS); + + wsClient.sendSpeak(SpeakV2Speak.builder() + .text("Hello from the Deepgram Java SDK integration test.") + .build()); + wsClient.sendFlush(SpeakV2Flush.builder().build()); + + boolean flushed = flushedLatch.await(20, TimeUnit.SECONDS); + + wsClient.sendClose(SpeakV2Close.builder().build()); + + assertThat(serverError.get()) + .as("no server/transport error during streaming") + .isNull(); + assertThat(flushed).as("received a Flushed message").isTrue(); + assertThat(totalAudioBytes.get()) + .as("expected streamed audio bytes") + .isGreaterThan(0); + System.out.println("Speak v2 WS returned " + totalAudioBytes.get() + " bytes of audio"); + } finally { + wsClient.disconnect(); + } + } } } diff --git a/src/test/java/com/deepgram/RegenTypesTest.java b/src/test/java/com/deepgram/RegenTypesTest.java index d89119c..9b8d76c 100644 --- a/src/test/java/com/deepgram/RegenTypesTest.java +++ b/src/test/java/com/deepgram/RegenTypesTest.java @@ -3,8 +3,18 @@ import static org.assertj.core.api.Assertions.assertThat; import com.deepgram.core.ObjectMappers; +import com.deepgram.resources.agent.v1.types.AgentV1AgentAudioDone; +import com.deepgram.resources.agent.v1.types.AgentV1KeepAlive; +import com.deepgram.resources.agent.v1.types.AgentV1ListenUpdated; +import com.deepgram.resources.agent.v1.types.AgentV1PromptUpdated; +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.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.fasterxml.jackson.databind.ObjectMapper; import java.util.Arrays; @@ -21,6 +31,9 @@ *
  • {@link ListenV2CloseStream} {@code type} is now a fixed {@code "CloseStream"} constant, and its manually * patched {@code equals}/{@code hashCode} pair must honour the {@link Object} contract. *
  • {@link DeepgramListenProviderV2} {@code language_hint} was renamed to {@code language_hints} (a list). + *
  • Fields-less message types generate {@code equals()} but no {@code hashCode()}; we patch a consistent + * {@code hashCode()} onto each (frozen in {@code .fernignore}). This test guards those patches against a + * future regen silently dropping them again — see {@code FieldsLessMessageContract}. * */ public class RegenTypesTest { @@ -86,6 +99,35 @@ void equalsHashCodeContract() { } } + @Nested + @DisplayName("Fields-less message types: manual hashCode() patch honours the Object contract") + class FieldsLessMessageContract { + + @Test + @DisplayName("equal instances share a hash code across all patched fields-less types") + void equalsHashCodeContract() { + // Each type generates equals() (all instances equal) but no hashCode(); we patch a + // consistent hashCode(). Two freshly-built instances must be equal AND share a hash. + assertContract(SpeakV2Close.builder().build(), SpeakV2Close.builder().build()); + assertContract(SpeakV2Flush.builder().build(), SpeakV2Flush.builder().build()); + assertContract(AgentV1ListenUpdated.builder().build(), AgentV1ListenUpdated.builder().build()); + assertContract(AgentV1SpeakUpdated.builder().build(), AgentV1SpeakUpdated.builder().build()); + assertContract(AgentV1AgentAudioDone.builder().build(), AgentV1AgentAudioDone.builder().build()); + assertContract(AgentV1SettingsApplied.builder().build(), AgentV1SettingsApplied.builder().build()); + assertContract( + AgentV1UserStartedSpeaking.builder().build(), + AgentV1UserStartedSpeaking.builder().build()); + assertContract(AgentV1KeepAlive.builder().build(), AgentV1KeepAlive.builder().build()); + assertContract(AgentV1ThinkUpdated.builder().build(), AgentV1ThinkUpdated.builder().build()); + assertContract(AgentV1PromptUpdated.builder().build(), AgentV1PromptUpdated.builder().build()); + } + + private void assertContract(Object a, Object b) { + assertThat(a).isEqualTo(b); + assertThat(a.hashCode()).isEqualTo(b.hashCode()); + } + } + @Nested @DisplayName("DeepgramListenProviderV2 languageHints") class ListenProviderV2 {