diff --git a/.fern/metadata.json b/.fern/metadata.json index 6620043..5e8f480 100644 --- a/.fern/metadata.json +++ b/.fern/metadata.json @@ -11,8 +11,8 @@ }, "enable-wire-tests": true }, - "originGitCommit": "335af96251466afae7a9f71badb65c630a48626e", + "originGitCommit": "14c0ca94fcd0279d99570389d7b688dc3a18ea41", "originGitCommitIsDirty": true, "invokedBy": "manual", - "sdkVersion": "0.6.0" + "sdkVersion": "0.6.1" } \ No newline at end of file diff --git a/src/main/java/com/deepgram/resources/agent/v1/types/AgentV1InjectAgentMessage.java b/src/main/java/com/deepgram/resources/agent/v1/types/AgentV1InjectAgentMessage.java index cb3a41c..88be5ed 100644 --- a/src/main/java/com/deepgram/resources/agent/v1/types/AgentV1InjectAgentMessage.java +++ b/src/main/java/com/deepgram/resources/agent/v1/types/AgentV1InjectAgentMessage.java @@ -57,6 +57,7 @@ public String getMessage() { * */ @JsonProperty("behavior") @@ -114,6 +115,7 @@ public interface _FinalStage { * */ _FinalStage behavior(Optional behavior); @@ -156,6 +158,7 @@ public _FinalStage message(@NotNull String message) { * * @return Reference to {@code this} so that method calls can be chained together. */ @@ -170,6 +173,7 @@ public _FinalStage behavior(AgentV1InjectAgentMessageBehavior behavior) { * */ @java.lang.Override diff --git a/src/main/java/com/deepgram/resources/agent/v1/types/AgentV1InjectAgentMessageBehavior.java b/src/main/java/com/deepgram/resources/agent/v1/types/AgentV1InjectAgentMessageBehavior.java index d308988..44af927 100644 --- a/src/main/java/com/deepgram/resources/agent/v1/types/AgentV1InjectAgentMessageBehavior.java +++ b/src/main/java/com/deepgram/resources/agent/v1/types/AgentV1InjectAgentMessageBehavior.java @@ -10,6 +10,9 @@ public final class AgentV1InjectAgentMessageBehavior { public static final AgentV1InjectAgentMessageBehavior DEFAULT = new AgentV1InjectAgentMessageBehavior(Value.DEFAULT, "default"); + public static final AgentV1InjectAgentMessageBehavior INTERRUPT = + new AgentV1InjectAgentMessageBehavior(Value.INTERRUPT, "interrupt"); + public static final AgentV1InjectAgentMessageBehavior QUEUE = new AgentV1InjectAgentMessageBehavior(Value.QUEUE, "queue"); @@ -48,6 +51,8 @@ public T visit(Visitor visitor) { switch (value) { case DEFAULT: return visitor.visitDefault(); + case INTERRUPT: + return visitor.visitInterrupt(); case QUEUE: return visitor.visitQueue(); case UNKNOWN: @@ -61,6 +66,8 @@ public static AgentV1InjectAgentMessageBehavior valueOf(String value) { switch (value) { case "default": return DEFAULT; + case "interrupt": + return INTERRUPT; case "queue": return QUEUE; default: @@ -73,6 +80,8 @@ public enum Value { QUEUE, + INTERRUPT, + UNKNOWN } @@ -81,6 +90,8 @@ public interface Visitor { T visitQueue(); + T visitInterrupt(); + T visitUnknown(String unknownType); } } diff --git a/src/main/java/com/deepgram/resources/agent/v1/types/AgentV1LatencyReport.java b/src/main/java/com/deepgram/resources/agent/v1/types/AgentV1LatencyReport.java new file mode 100644 index 0000000..39aecfb --- /dev/null +++ b/src/main/java/com/deepgram/resources/agent/v1/types/AgentV1LatencyReport.java @@ -0,0 +1,316 @@ +/** + * 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.annotation.Nulls; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import java.util.HashMap; +import java.util.Map; +import java.util.Objects; +import java.util.Optional; + +@JsonInclude(JsonInclude.Include.NON_ABSENT) +@JsonDeserialize(builder = AgentV1LatencyReport.Builder.class) +public final class AgentV1LatencyReport { + private final Optional sttLatency; + + private final Optional tttTokenLatency; + + private final Optional tttTextLatency; + + private final Optional tttToolLatency; + + private final Optional tttThinkingLatency; + + private final Optional ttsLatency; + + private final Optional totalLatency; + + private final Map additionalProperties; + + private AgentV1LatencyReport( + Optional sttLatency, + Optional tttTokenLatency, + Optional tttTextLatency, + Optional tttToolLatency, + Optional tttThinkingLatency, + Optional ttsLatency, + Optional totalLatency, + Map additionalProperties) { + this.sttLatency = sttLatency; + this.tttTokenLatency = tttTokenLatency; + this.tttTextLatency = tttTextLatency; + this.tttToolLatency = tttToolLatency; + this.tttThinkingLatency = tttThinkingLatency; + this.ttsLatency = ttsLatency; + this.totalLatency = totalLatency; + this.additionalProperties = additionalProperties; + } + + /** + * @return Message type identifier for the latency report + */ + @JsonProperty("type") + public String getType() { + return "LatencyReport"; + } + + /** + * @return Speech-to-text: time from audio received to transcript produced, in seconds + */ + @JsonProperty("stt_latency") + public Optional getSttLatency() { + return sttLatency; + } + + /** + * @return Time to first token of any type (text, tool call, or thinking), in seconds + */ + @JsonProperty("ttt_token_latency") + public Optional getTttTokenLatency() { + return tttTokenLatency; + } + + /** + * @return Time to first text token from the LLM, in seconds + */ + @JsonProperty("ttt_text_latency") + public Optional getTttTextLatency() { + return tttTextLatency; + } + + /** + * @return Time to first tool-call token from the LLM, in seconds + */ + @JsonProperty("ttt_tool_latency") + public Optional getTttToolLatency() { + return tttToolLatency; + } + + /** + * @return Time to first thinking token from the LLM, in seconds + */ + @JsonProperty("ttt_thinking_latency") + public Optional getTttThinkingLatency() { + return tttThinkingLatency; + } + + /** + * @return Text-to-speech: time from first text token to first audio byte, in seconds + */ + @JsonProperty("tts_latency") + public Optional getTtsLatency() { + return ttsLatency; + } + + /** + * @return End-to-end: time from user utterance end to first audio byte, in seconds + */ + @JsonProperty("total_latency") + public Optional getTotalLatency() { + return totalLatency; + } + + @java.lang.Override + public boolean equals(Object other) { + if (this == other) return true; + return other instanceof AgentV1LatencyReport && equalTo((AgentV1LatencyReport) other); + } + + @JsonAnyGetter + public Map getAdditionalProperties() { + return this.additionalProperties; + } + + private boolean equalTo(AgentV1LatencyReport other) { + return sttLatency.equals(other.sttLatency) + && tttTokenLatency.equals(other.tttTokenLatency) + && tttTextLatency.equals(other.tttTextLatency) + && tttToolLatency.equals(other.tttToolLatency) + && tttThinkingLatency.equals(other.tttThinkingLatency) + && ttsLatency.equals(other.ttsLatency) + && totalLatency.equals(other.totalLatency); + } + + @java.lang.Override + public int hashCode() { + return Objects.hash( + this.sttLatency, + this.tttTokenLatency, + this.tttTextLatency, + this.tttToolLatency, + this.tttThinkingLatency, + this.ttsLatency, + this.totalLatency); + } + + @java.lang.Override + public String toString() { + return ObjectMappers.stringify(this); + } + + public static Builder builder() { + return new Builder(); + } + + @JsonIgnoreProperties(ignoreUnknown = true) + public static final class Builder { + private Optional sttLatency = Optional.empty(); + + private Optional tttTokenLatency = Optional.empty(); + + private Optional tttTextLatency = Optional.empty(); + + private Optional tttToolLatency = Optional.empty(); + + private Optional tttThinkingLatency = Optional.empty(); + + private Optional ttsLatency = Optional.empty(); + + private Optional totalLatency = Optional.empty(); + + @JsonAnySetter + private Map additionalProperties = new HashMap<>(); + + private Builder() {} + + public Builder from(AgentV1LatencyReport other) { + sttLatency(other.getSttLatency()); + tttTokenLatency(other.getTttTokenLatency()); + tttTextLatency(other.getTttTextLatency()); + tttToolLatency(other.getTttToolLatency()); + tttThinkingLatency(other.getTttThinkingLatency()); + ttsLatency(other.getTtsLatency()); + totalLatency(other.getTotalLatency()); + return this; + } + + /** + *

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

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

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

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

Time to first text token from the LLM, in seconds

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

Time to first tool-call token from the LLM, in seconds

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

Time to first thinking token from the LLM, in seconds

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

Text-to-speech: time from first text token to first audio byte, in seconds

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

End-to-end: time from user utterance end to first audio byte, in seconds

+ */ + @JsonSetter(value = "total_latency", nulls = Nulls.SKIP) + public Builder totalLatency(Optional totalLatency) { + this.totalLatency = totalLatency; + return this; + } + + public Builder totalLatency(Float totalLatency) { + this.totalLatency = Optional.ofNullable(totalLatency); + return this; + } + + public AgentV1LatencyReport build() { + return new AgentV1LatencyReport( + sttLatency, + tttTokenLatency, + tttTextLatency, + tttToolLatency, + tttThinkingLatency, + ttsLatency, + totalLatency, + 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/websocket/V1WebSocketClient.java b/src/main/java/com/deepgram/resources/agent/v1/websocket/V1WebSocketClient.java index d333064..1a86ab3 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.AgentV1LatencyReport; import com.deepgram.resources.agent.v1.types.AgentV1ListenUpdated; import com.deepgram.resources.agent.v1.types.AgentV1PromptUpdated; import com.deepgram.resources.agent.v1.types.AgentV1ReceiveFunctionCallResponse; @@ -98,6 +99,8 @@ public class V1WebSocketClient implements AutoCloseable { private volatile Consumer agentThinkingHandler; + private volatile Consumer latencyReportHandler; + private volatile Consumer functionCallRequestHandler; private volatile Consumer agentStartedSpeakingHandler; @@ -411,6 +414,14 @@ public void onAgentThinking(Consumer handler) { this.agentThinkingHandler = handler; } + /** + * Registers a handler for AgentV1LatencyReport messages from the server. + * @param handler the handler to invoke when a message is received + */ + public void onLatencyReport(Consumer handler) { + this.latencyReportHandler = handler; + } + /** * Registers a handler for AgentV1FunctionCallRequest messages from the server. * @param handler the handler to invoke when a message is received @@ -766,6 +777,19 @@ private void handleIncomingMessage(String json) { return; } } + if ("LatencyReport".equals(node.path("type").asText())) { + AgentV1LatencyReport latencyReportHandlerEvent = null; + try { + latencyReportHandlerEvent = objectMapper.treeToValue(node, AgentV1LatencyReport.class); + } catch (Exception e) { + } + if (latencyReportHandlerEvent != null) { + if (latencyReportHandler != null) { + latencyReportHandler.accept(latencyReportHandlerEvent); + } + return; + } + } if ("AgentAudioDone".equals(node.path("type").asText())) { AgentV1AgentAudioDone agentAudioDoneHandlerEvent = null; try { diff --git a/src/main/java/com/deepgram/resources/speak/v2/AsyncV2Client.java b/src/main/java/com/deepgram/resources/speak/v2/AsyncV2Client.java index f54fdb1..354acdc 100644 --- a/src/main/java/com/deepgram/resources/speak/v2/AsyncV2Client.java +++ b/src/main/java/com/deepgram/resources/speak/v2/AsyncV2Client.java @@ -4,13 +4,19 @@ package com.deepgram.resources.speak.v2; import com.deepgram.core.ClientOptions; +import com.deepgram.core.Suppliers; +import com.deepgram.resources.speak.v2.audio.AsyncAudioClient; import com.deepgram.resources.speak.v2.websocket.V2WebSocketClient; +import java.util.function.Supplier; public class AsyncV2Client { protected final ClientOptions clientOptions; + protected final Supplier audioClient; + public AsyncV2Client(ClientOptions clientOptions) { this.clientOptions = clientOptions; + this.audioClient = Suppliers.memoize(() -> new AsyncAudioClient(clientOptions)); } /** @@ -19,4 +25,8 @@ public AsyncV2Client(ClientOptions clientOptions) { public V2WebSocketClient v2WebSocket() { return new V2WebSocketClient(clientOptions); } + + public AsyncAudioClient audio() { + return this.audioClient.get(); + } } diff --git a/src/main/java/com/deepgram/resources/speak/v2/V2Client.java b/src/main/java/com/deepgram/resources/speak/v2/V2Client.java index 87095e4..3993165 100644 --- a/src/main/java/com/deepgram/resources/speak/v2/V2Client.java +++ b/src/main/java/com/deepgram/resources/speak/v2/V2Client.java @@ -4,13 +4,19 @@ package com.deepgram.resources.speak.v2; import com.deepgram.core.ClientOptions; +import com.deepgram.core.Suppliers; +import com.deepgram.resources.speak.v2.audio.AudioClient; import com.deepgram.resources.speak.v2.websocket.V2WebSocketClient; +import java.util.function.Supplier; public class V2Client { protected final ClientOptions clientOptions; + protected final Supplier audioClient; + public V2Client(ClientOptions clientOptions) { this.clientOptions = clientOptions; + this.audioClient = Suppliers.memoize(() -> new AudioClient(clientOptions)); } /** @@ -19,4 +25,8 @@ public V2Client(ClientOptions clientOptions) { public V2WebSocketClient v2WebSocket() { return new V2WebSocketClient(clientOptions); } + + public AudioClient audio() { + return this.audioClient.get(); + } } diff --git a/src/main/java/com/deepgram/resources/speak/v2/audio/AsyncAudioClient.java b/src/main/java/com/deepgram/resources/speak/v2/audio/AsyncAudioClient.java new file mode 100644 index 0000000..f1dca14 --- /dev/null +++ b/src/main/java/com/deepgram/resources/speak/v2/audio/AsyncAudioClient.java @@ -0,0 +1,42 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ +package com.deepgram.resources.speak.v2.audio; + +import com.deepgram.core.ClientOptions; +import com.deepgram.core.RequestOptions; +import com.deepgram.resources.speak.v2.audio.requests.SpeakV2Request; +import java.io.InputStream; +import java.util.concurrent.CompletableFuture; + +public class AsyncAudioClient { + protected final ClientOptions clientOptions; + + private final AsyncRawAudioClient rawClient; + + public AsyncAudioClient(ClientOptions clientOptions) { + this.clientOptions = clientOptions; + this.rawClient = new AsyncRawAudioClient(clientOptions); + } + + /** + * Get responses with HTTP metadata like headers + */ + public AsyncRawAudioClient withRawResponse() { + return this.rawClient; + } + + /** + * Synthesize a complete block of text into a single audio response using Deepgram's Flux TTS batch (REST) API. Use this for pre-rendering fixed audio (IVR prompts, notifications, narration) where the whole text is known up front and you don't need incremental playback or interruption. + */ + public CompletableFuture generate(SpeakV2Request request) { + return this.rawClient.generate(request).thenApply(response -> response.body()); + } + + /** + * Synthesize a complete block of text into a single audio response using Deepgram's Flux TTS batch (REST) API. Use this for pre-rendering fixed audio (IVR prompts, notifications, narration) where the whole text is known up front and you don't need incremental playback or interruption. + */ + public CompletableFuture generate(SpeakV2Request request, RequestOptions requestOptions) { + return this.rawClient.generate(request, requestOptions).thenApply(response -> response.body()); + } +} diff --git a/src/main/java/com/deepgram/resources/speak/v2/audio/AsyncRawAudioClient.java b/src/main/java/com/deepgram/resources/speak/v2/audio/AsyncRawAudioClient.java new file mode 100644 index 0000000..05baadc --- /dev/null +++ b/src/main/java/com/deepgram/resources/speak/v2/audio/AsyncRawAudioClient.java @@ -0,0 +1,149 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ +package com.deepgram.resources.speak.v2.audio; + +import com.deepgram.core.ClientOptions; +import com.deepgram.core.DeepgramApiException; +import com.deepgram.core.DeepgramApiHttpResponse; +import com.deepgram.core.DeepgramHttpException; +import com.deepgram.core.MediaTypes; +import com.deepgram.core.ObjectMappers; +import com.deepgram.core.QueryStringMapper; +import com.deepgram.core.RequestOptions; +import com.deepgram.core.ResponseBodyInputStream; +import com.deepgram.errors.BadRequestError; +import com.deepgram.resources.speak.v2.audio.requests.SpeakV2Request; +import com.fasterxml.jackson.core.JsonProcessingException; +import java.io.IOException; +import java.io.InputStream; +import java.util.concurrent.CompletableFuture; +import okhttp3.Call; +import okhttp3.Callback; +import okhttp3.Headers; +import okhttp3.HttpUrl; +import okhttp3.OkHttpClient; +import okhttp3.Request; +import okhttp3.RequestBody; +import okhttp3.Response; +import okhttp3.ResponseBody; +import org.jetbrains.annotations.NotNull; + +public class AsyncRawAudioClient { + protected final ClientOptions clientOptions; + + public AsyncRawAudioClient(ClientOptions clientOptions) { + this.clientOptions = clientOptions; + } + + /** + * Synthesize a complete block of text into a single audio response using Deepgram's Flux TTS batch (REST) API. Use this for pre-rendering fixed audio (IVR prompts, notifications, narration) where the whole text is known up front and you don't need incremental playback or interruption. + */ + public CompletableFuture> generate(SpeakV2Request request) { + return generate(request, null); + } + + /** + * Synthesize a complete block of text into a single audio response using Deepgram's Flux TTS batch (REST) API. Use this for pre-rendering fixed audio (IVR prompts, notifications, narration) where the whole text is known up front and you don't need incremental playback or interruption. + */ + public CompletableFuture> generate( + SpeakV2Request request, RequestOptions requestOptions) { + HttpUrl.Builder httpUrl = HttpUrl.parse(this.clientOptions.environment().getBaseURL()) + .newBuilder() + .addPathSegments("v2/speak"); + if (request.getCallback().isPresent()) { + QueryStringMapper.addQueryParameter( + httpUrl, "callback", request.getCallback().get(), false); + } + if (request.getCallbackMethod().isPresent()) { + QueryStringMapper.addQueryParameter( + httpUrl, "callback_method", request.getCallbackMethod().get(), false); + } + if (request.getMipOptOut().isPresent()) { + QueryStringMapper.addQueryParameter( + httpUrl, "mip_opt_out", request.getMipOptOut().get(), false); + } + if (request.getBitRate().isPresent()) { + QueryStringMapper.addQueryParameter( + httpUrl, "bit_rate", request.getBitRate().get(), false); + } + if (request.getContainer().isPresent()) { + QueryStringMapper.addQueryParameter( + httpUrl, "container", request.getContainer().get(), false); + } + if (request.getEncoding().isPresent()) { + QueryStringMapper.addQueryParameter( + httpUrl, "encoding", request.getEncoding().get(), false); + } + QueryStringMapper.addQueryParameter(httpUrl, "model", request.getModel(), false); + if (request.getSampleRate().isPresent()) { + QueryStringMapper.addQueryParameter( + httpUrl, "sample_rate", request.getSampleRate().get(), false); + } + if (request.getPriority().isPresent()) { + QueryStringMapper.addQueryParameter( + httpUrl, "priority", request.getPriority().get(), false); + } + if (request.getTag().isPresent()) { + QueryStringMapper.addQueryParameter(httpUrl, "tag", request.getTag().get(), true); + } + if (requestOptions != null) { + requestOptions.getQueryParameters().forEach((_key, _value) -> { + httpUrl.addQueryParameter(_key, _value); + }); + } + RequestBody body; + try { + body = RequestBody.create( + ObjectMappers.JSON_MAPPER.writeValueAsBytes(request), MediaTypes.APPLICATION_JSON); + } catch (Exception e) { + throw new RuntimeException(e); + } + Request.Builder _requestBuilder = new Request.Builder() + .url(httpUrl.build()) + .method("POST", body) + .headers(Headers.of(clientOptions.headers(requestOptions))) + .addHeader("Content-Type", "application/json") + .addHeader("Accept", "application/json"); + Request okhttpRequest = _requestBuilder.build(); + OkHttpClient client = clientOptions.httpClient(); + if (requestOptions != null && requestOptions.getTimeout().isPresent()) { + client = clientOptions.httpClientWithTimeout(requestOptions); + } + CompletableFuture> future = new CompletableFuture<>(); + client.newCall(okhttpRequest).enqueue(new Callback() { + @Override + public void onResponse(@NotNull Call call, @NotNull Response response) throws IOException { + try { + ResponseBody responseBody = response.body(); + if (response.isSuccessful()) { + future.complete(new DeepgramApiHttpResponse<>(new ResponseBodyInputStream(response), response)); + return; + } + String responseBodyString = responseBody != null ? responseBody.string() : "{}"; + try { + if (response.code() == 400) { + future.completeExceptionally(new BadRequestError( + ObjectMappers.JSON_MAPPER.readValue(responseBodyString, Object.class), response)); + return; + } + } catch (JsonProcessingException ignored) { + // unable to map error response, throwing generic error + } + Object errorBody = ObjectMappers.parseErrorBody(responseBodyString); + future.completeExceptionally(new DeepgramHttpException( + "Error with status code " + response.code(), response.code(), errorBody, response)); + return; + } catch (IOException e) { + future.completeExceptionally(new DeepgramApiException("Network error executing HTTP request", e)); + } + } + + @Override + public void onFailure(@NotNull Call call, @NotNull IOException e) { + future.completeExceptionally(new DeepgramApiException("Network error executing HTTP request", e)); + } + }); + return future; + } +} diff --git a/src/main/java/com/deepgram/resources/speak/v2/audio/AudioClient.java b/src/main/java/com/deepgram/resources/speak/v2/audio/AudioClient.java new file mode 100644 index 0000000..5a38201 --- /dev/null +++ b/src/main/java/com/deepgram/resources/speak/v2/audio/AudioClient.java @@ -0,0 +1,41 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ +package com.deepgram.resources.speak.v2.audio; + +import com.deepgram.core.ClientOptions; +import com.deepgram.core.RequestOptions; +import com.deepgram.resources.speak.v2.audio.requests.SpeakV2Request; +import java.io.InputStream; + +public class AudioClient { + protected final ClientOptions clientOptions; + + private final RawAudioClient rawClient; + + public AudioClient(ClientOptions clientOptions) { + this.clientOptions = clientOptions; + this.rawClient = new RawAudioClient(clientOptions); + } + + /** + * Get responses with HTTP metadata like headers + */ + public RawAudioClient withRawResponse() { + return this.rawClient; + } + + /** + * Synthesize a complete block of text into a single audio response using Deepgram's Flux TTS batch (REST) API. Use this for pre-rendering fixed audio (IVR prompts, notifications, narration) where the whole text is known up front and you don't need incremental playback or interruption. + */ + public InputStream generate(SpeakV2Request request) { + return this.rawClient.generate(request).body(); + } + + /** + * Synthesize a complete block of text into a single audio response using Deepgram's Flux TTS batch (REST) API. Use this for pre-rendering fixed audio (IVR prompts, notifications, narration) where the whole text is known up front and you don't need incremental playback or interruption. + */ + public InputStream generate(SpeakV2Request request, RequestOptions requestOptions) { + return this.rawClient.generate(request, requestOptions).body(); + } +} diff --git a/src/main/java/com/deepgram/resources/speak/v2/audio/RawAudioClient.java b/src/main/java/com/deepgram/resources/speak/v2/audio/RawAudioClient.java new file mode 100644 index 0000000..08b3bd0 --- /dev/null +++ b/src/main/java/com/deepgram/resources/speak/v2/audio/RawAudioClient.java @@ -0,0 +1,130 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ +package com.deepgram.resources.speak.v2.audio; + +import com.deepgram.core.ClientOptions; +import com.deepgram.core.DeepgramApiException; +import com.deepgram.core.DeepgramApiHttpResponse; +import com.deepgram.core.DeepgramHttpException; +import com.deepgram.core.MediaTypes; +import com.deepgram.core.ObjectMappers; +import com.deepgram.core.QueryStringMapper; +import com.deepgram.core.RequestOptions; +import com.deepgram.core.ResponseBodyInputStream; +import com.deepgram.errors.BadRequestError; +import com.deepgram.resources.speak.v2.audio.requests.SpeakV2Request; +import com.fasterxml.jackson.core.JsonProcessingException; +import java.io.IOException; +import java.io.InputStream; +import okhttp3.Headers; +import okhttp3.HttpUrl; +import okhttp3.OkHttpClient; +import okhttp3.Request; +import okhttp3.RequestBody; +import okhttp3.Response; +import okhttp3.ResponseBody; + +public class RawAudioClient { + protected final ClientOptions clientOptions; + + public RawAudioClient(ClientOptions clientOptions) { + this.clientOptions = clientOptions; + } + + /** + * Synthesize a complete block of text into a single audio response using Deepgram's Flux TTS batch (REST) API. Use this for pre-rendering fixed audio (IVR prompts, notifications, narration) where the whole text is known up front and you don't need incremental playback or interruption. + */ + public DeepgramApiHttpResponse generate(SpeakV2Request request) { + return generate(request, null); + } + + /** + * Synthesize a complete block of text into a single audio response using Deepgram's Flux TTS batch (REST) API. Use this for pre-rendering fixed audio (IVR prompts, notifications, narration) where the whole text is known up front and you don't need incremental playback or interruption. + */ + public DeepgramApiHttpResponse generate(SpeakV2Request request, RequestOptions requestOptions) { + HttpUrl.Builder httpUrl = HttpUrl.parse(this.clientOptions.environment().getBaseURL()) + .newBuilder() + .addPathSegments("v2/speak"); + if (request.getCallback().isPresent()) { + QueryStringMapper.addQueryParameter( + httpUrl, "callback", request.getCallback().get(), false); + } + if (request.getCallbackMethod().isPresent()) { + QueryStringMapper.addQueryParameter( + httpUrl, "callback_method", request.getCallbackMethod().get(), false); + } + if (request.getMipOptOut().isPresent()) { + QueryStringMapper.addQueryParameter( + httpUrl, "mip_opt_out", request.getMipOptOut().get(), false); + } + if (request.getBitRate().isPresent()) { + QueryStringMapper.addQueryParameter( + httpUrl, "bit_rate", request.getBitRate().get(), false); + } + if (request.getContainer().isPresent()) { + QueryStringMapper.addQueryParameter( + httpUrl, "container", request.getContainer().get(), false); + } + if (request.getEncoding().isPresent()) { + QueryStringMapper.addQueryParameter( + httpUrl, "encoding", request.getEncoding().get(), false); + } + QueryStringMapper.addQueryParameter(httpUrl, "model", request.getModel(), false); + if (request.getSampleRate().isPresent()) { + QueryStringMapper.addQueryParameter( + httpUrl, "sample_rate", request.getSampleRate().get(), false); + } + if (request.getPriority().isPresent()) { + QueryStringMapper.addQueryParameter( + httpUrl, "priority", request.getPriority().get(), false); + } + if (request.getTag().isPresent()) { + QueryStringMapper.addQueryParameter(httpUrl, "tag", request.getTag().get(), true); + } + if (requestOptions != null) { + requestOptions.getQueryParameters().forEach((_key, _value) -> { + httpUrl.addQueryParameter(_key, _value); + }); + } + RequestBody body; + try { + body = RequestBody.create( + ObjectMappers.JSON_MAPPER.writeValueAsBytes(request), MediaTypes.APPLICATION_JSON); + } catch (Exception e) { + throw new RuntimeException(e); + } + Request.Builder _requestBuilder = new Request.Builder() + .url(httpUrl.build()) + .method("POST", body) + .headers(Headers.of(clientOptions.headers(requestOptions))) + .addHeader("Content-Type", "application/json") + .addHeader("Accept", "application/json"); + Request okhttpRequest = _requestBuilder.build(); + OkHttpClient client = clientOptions.httpClient(); + if (requestOptions != null && requestOptions.getTimeout().isPresent()) { + client = clientOptions.httpClientWithTimeout(requestOptions); + } + try { + Response response = client.newCall(okhttpRequest).execute(); + ResponseBody responseBody = response.body(); + if (response.isSuccessful()) { + return new DeepgramApiHttpResponse<>(new ResponseBodyInputStream(response), response); + } + String responseBodyString = responseBody != null ? responseBody.string() : "{}"; + try { + if (response.code() == 400) { + throw new BadRequestError( + ObjectMappers.JSON_MAPPER.readValue(responseBodyString, Object.class), response); + } + } catch (JsonProcessingException ignored) { + // unable to map error response, throwing generic error + } + Object errorBody = ObjectMappers.parseErrorBody(responseBodyString); + throw new DeepgramHttpException( + "Error with status code " + response.code(), response.code(), errorBody, response); + } catch (IOException e) { + throw new DeepgramApiException("Network error executing HTTP request", e); + } + } +} diff --git a/src/main/java/com/deepgram/resources/speak/v2/audio/requests/SpeakV2Request.java b/src/main/java/com/deepgram/resources/speak/v2/audio/requests/SpeakV2Request.java new file mode 100644 index 0000000..dcf5b22 --- /dev/null +++ b/src/main/java/com/deepgram/resources/speak/v2/audio/requests/SpeakV2Request.java @@ -0,0 +1,592 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ +package com.deepgram.resources.speak.v2.audio.requests; + +import com.deepgram.core.ObjectMappers; +import com.deepgram.resources.speak.v2.audio.types.AudioGenerateRequestCallbackMethod; +import com.deepgram.resources.speak.v2.audio.types.AudioGenerateRequestContainer; +import com.deepgram.resources.speak.v2.audio.types.AudioGenerateRequestEncoding; +import com.fasterxml.jackson.annotation.JsonAnyGetter; +import com.fasterxml.jackson.annotation.JsonAnySetter; +import com.fasterxml.jackson.annotation.JsonIgnore; +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.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Optional; +import org.jetbrains.annotations.NotNull; + +@JsonInclude(JsonInclude.Include.NON_ABSENT) +@JsonDeserialize(builder = SpeakV2Request.Builder.class) +public final class SpeakV2Request { + private final Optional> tag; + + private final Optional callback; + + private final Optional callbackMethod; + + private final Optional mipOptOut; + + private final Optional bitRate; + + private final Optional container; + + private final Optional encoding; + + private final String model; + + private final Optional sampleRate; + + private final Optional priority; + + private final String text; + + private final Map additionalProperties; + + private SpeakV2Request( + Optional> tag, + Optional callback, + Optional callbackMethod, + Optional mipOptOut, + Optional bitRate, + Optional container, + Optional encoding, + String model, + Optional sampleRate, + Optional priority, + String text, + Map additionalProperties) { + this.tag = tag; + this.callback = callback; + this.callbackMethod = callbackMethod; + this.mipOptOut = mipOptOut; + this.bitRate = bitRate; + this.container = container; + this.encoding = encoding; + this.model = model; + this.sampleRate = sampleRate; + this.priority = priority; + this.text = text; + this.additionalProperties = additionalProperties; + } + + /** + * @return Label your requests for the purpose of identification during usage reporting + */ + @JsonIgnore + public Optional> getTag() { + return tag; + } + + /** + * @return URL to which we'll make the callback request + */ + @JsonIgnore + public Optional getCallback() { + return callback; + } + + /** + * @return HTTP method by which the callback request will be made + */ + @JsonIgnore + public Optional getCallbackMethod() { + return callbackMethod; + } + + /** + * @return Opts out requests from the Deepgram Model Improvement Program. Refer to our Docs for pricing impacts before setting this to true. https://dpgr.am/deepgram-mip + */ + @JsonIgnore + public Optional getMipOptOut() { + return mipOptOut; + } + + /** + * @return The bitrate of the audio in bits per second. Choose from predefined ranges or specific values based on the encoding type. + */ + @JsonIgnore + public Optional getBitRate() { + return bitRate; + } + + /** + * @return Container specifies the file format wrapper for the output audio. The available options depend on the encoding type. + */ + @JsonIgnore + public Optional getContainer() { + return container; + } + + /** + * @return Encoding allows you to specify the expected encoding of your audio output + */ + @JsonIgnore + public Optional getEncoding() { + return encoding; + } + + /** + * @return Flux TTS model used to synthesize the submitted text, in the form flux-{voice}-{language} (for example, flux-alexis-en). Required; unlike the v1 (Aura) endpoint there is no default and only flux models are accepted. English-only at launch. The voice catalog is not yet finalized, so this is typed as a free-form string rather than a closed enum; it can be locked to an enum (matching STT Flux ListenV2Model) once the GA catalog is confirmed. + */ + @JsonIgnore + public String getModel() { + return model; + } + + /** + * @return Sample Rate specifies the sample rate for the output audio. Based on the encoding, different sample rates are supported. For some encodings, the sample rate is not configurable + */ + @JsonIgnore + public Optional getSampleRate() { + return sampleRate; + } + + /** + * @return Processing priority for asynchronous (callback) requests. The only supported value is low. + */ + @JsonIgnore + public Optional getPriority() { + return priority; + } + + /** + * @return The text content to be converted to speech. The server normalizes and preprocesses the text (e.g. stripping inline controls) before synthesis. + */ + @JsonProperty("text") + public String getText() { + return text; + } + + @java.lang.Override + public boolean equals(Object other) { + if (this == other) return true; + return other instanceof SpeakV2Request && equalTo((SpeakV2Request) other); + } + + @JsonAnyGetter + public Map getAdditionalProperties() { + return this.additionalProperties; + } + + private boolean equalTo(SpeakV2Request other) { + return tag.equals(other.tag) + && callback.equals(other.callback) + && callbackMethod.equals(other.callbackMethod) + && mipOptOut.equals(other.mipOptOut) + && bitRate.equals(other.bitRate) + && container.equals(other.container) + && encoding.equals(other.encoding) + && model.equals(other.model) + && sampleRate.equals(other.sampleRate) + && priority.equals(other.priority) + && text.equals(other.text); + } + + @java.lang.Override + public int hashCode() { + return Objects.hash( + this.tag, + this.callback, + this.callbackMethod, + this.mipOptOut, + this.bitRate, + this.container, + this.encoding, + this.model, + this.sampleRate, + this.priority, + this.text); + } + + @java.lang.Override + public String toString() { + return ObjectMappers.stringify(this); + } + + public static ModelStage builder() { + return new Builder(); + } + + public interface ModelStage { + /** + *

Flux TTS model used to synthesize the submitted text, in the form flux-{voice}-{language} (for example, flux-alexis-en). Required; unlike the v1 (Aura) endpoint there is no default and only flux models are accepted. English-only at launch. The voice catalog is not yet finalized, so this is typed as a free-form string rather than a closed enum; it can be locked to an enum (matching STT Flux ListenV2Model) once the GA catalog is confirmed.

+ */ + TextStage model(@NotNull String model); + + Builder from(SpeakV2Request other); + } + + public interface TextStage { + /** + *

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

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

Label your requests for the purpose of identification during usage reporting

+ */ + _FinalStage tag(Optional> tag); + + _FinalStage tag(List tag); + + _FinalStage tag(String tag); + + /** + *

URL to which we'll make the callback request

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

HTTP method by which the callback request will be made

+ */ + _FinalStage callbackMethod(Optional callbackMethod); + + _FinalStage callbackMethod(AudioGenerateRequestCallbackMethod callbackMethod); + + /** + *

Opts out requests from the Deepgram Model Improvement Program. Refer to our Docs for pricing impacts before setting this to true. https://dpgr.am/deepgram-mip

+ */ + _FinalStage mipOptOut(Optional mipOptOut); + + _FinalStage mipOptOut(Boolean mipOptOut); + + /** + *

The bitrate of the audio in bits per second. Choose from predefined ranges or specific values based on the encoding type.

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

Container specifies the file format wrapper for the output audio. The available options depend on the encoding type.

+ */ + _FinalStage container(Optional container); + + _FinalStage container(AudioGenerateRequestContainer container); + + /** + *

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

+ */ + _FinalStage encoding(Optional encoding); + + _FinalStage encoding(AudioGenerateRequestEncoding encoding); + + /** + *

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

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

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

+ */ + _FinalStage priority(Optional priority); + + _FinalStage priority(String priority); + } + + @JsonIgnoreProperties(ignoreUnknown = true) + public static final class Builder implements ModelStage, TextStage, _FinalStage { + private String model; + + private String text; + + private Optional priority = Optional.empty(); + + private Optional sampleRate = Optional.empty(); + + private Optional encoding = Optional.empty(); + + private Optional container = Optional.empty(); + + private Optional bitRate = Optional.empty(); + + private Optional mipOptOut = Optional.empty(); + + private Optional callbackMethod = Optional.empty(); + + private Optional callback = Optional.empty(); + + private Optional> tag = Optional.empty(); + + @JsonAnySetter + private Map additionalProperties = new HashMap<>(); + + private Builder() {} + + @java.lang.Override + public Builder from(SpeakV2Request other) { + tag(other.getTag()); + callback(other.getCallback()); + callbackMethod(other.getCallbackMethod()); + mipOptOut(other.getMipOptOut()); + bitRate(other.getBitRate()); + container(other.getContainer()); + encoding(other.getEncoding()); + model(other.getModel()); + sampleRate(other.getSampleRate()); + priority(other.getPriority()); + text(other.getText()); + return this; + } + + /** + *

Flux TTS model used to synthesize the submitted text, in the form flux-{voice}-{language} (for example, flux-alexis-en). Required; unlike the v1 (Aura) endpoint there is no default and only flux models are accepted. English-only at launch. The voice catalog is not yet finalized, so this is typed as a free-form string rather than a closed enum; it can be locked to an enum (matching STT Flux ListenV2Model) once the GA catalog is confirmed.

+ *

Flux TTS model used to synthesize the submitted text, in the form flux-{voice}-{language} (for example, flux-alexis-en). Required; unlike the v1 (Aura) endpoint there is no default and only flux models are accepted. English-only at launch. The voice catalog is not yet finalized, so this is typed as a free-form string rather than a closed enum; it can be locked to an enum (matching STT Flux ListenV2Model) once the GA catalog is confirmed.

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

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

+ *

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Container specifies the file format wrapper for the output audio. The available options depend on the encoding type.

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

Container specifies the file format wrapper for the output audio. The available options depend on the encoding type.

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

The bitrate of the audio in bits per second. Choose from predefined ranges or specific values based on the encoding type.

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

The bitrate of the audio in bits per second. Choose from predefined ranges or specific values based on the encoding type.

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

Opts out requests from the Deepgram Model Improvement Program. Refer to our Docs for pricing impacts before setting this to true. https://dpgr.am/deepgram-mip

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

Opts out requests from the Deepgram Model Improvement Program. Refer to our Docs for pricing impacts before setting this to true. https://dpgr.am/deepgram-mip

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

HTTP method by which the callback request will be made

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

HTTP method by which the callback request will be made

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

URL to which we'll make the callback request

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

URL to which we'll make the callback request

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

Label your requests for the purpose of identification during usage reporting

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

Label your requests for the purpose of identification during usage reporting

+ */ + @java.lang.Override + @JsonSetter(value = "tag", nulls = Nulls.SKIP) + public _FinalStage tag(Optional> tag) { + this.tag = tag; + return this; + } + + @java.lang.Override + public SpeakV2Request build() { + return new SpeakV2Request( + tag, + callback, + callbackMethod, + mipOptOut, + bitRate, + container, + encoding, + model, + sampleRate, + priority, + 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/audio/types/AudioGenerateRequestCallbackMethod.java b/src/main/java/com/deepgram/resources/speak/v2/audio/types/AudioGenerateRequestCallbackMethod.java new file mode 100644 index 0000000..2959cf6 --- /dev/null +++ b/src/main/java/com/deepgram/resources/speak/v2/audio/types/AudioGenerateRequestCallbackMethod.java @@ -0,0 +1,86 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ +package com.deepgram.resources.speak.v2.audio.types; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonValue; + +public final class AudioGenerateRequestCallbackMethod { + public static final AudioGenerateRequestCallbackMethod PUT = + new AudioGenerateRequestCallbackMethod(Value.PUT, "PUT"); + + public static final AudioGenerateRequestCallbackMethod POST = + new AudioGenerateRequestCallbackMethod(Value.POST, "POST"); + + private final Value value; + + private final String string; + + AudioGenerateRequestCallbackMethod(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 AudioGenerateRequestCallbackMethod + && this.string.equals(((AudioGenerateRequestCallbackMethod) other).string)); + } + + @java.lang.Override + public int hashCode() { + return this.string.hashCode(); + } + + public T visit(Visitor visitor) { + switch (value) { + case PUT: + return visitor.visitPut(); + case POST: + return visitor.visitPost(); + case UNKNOWN: + default: + return visitor.visitUnknown(string); + } + } + + @JsonCreator(mode = JsonCreator.Mode.DELEGATING) + public static AudioGenerateRequestCallbackMethod valueOf(String value) { + switch (value) { + case "PUT": + return PUT; + case "POST": + return POST; + default: + return new AudioGenerateRequestCallbackMethod(Value.UNKNOWN, value); + } + } + + public enum Value { + POST, + + PUT, + + UNKNOWN + } + + public interface Visitor { + T visitPost(); + + T visitPut(); + + T visitUnknown(String unknownType); + } +} diff --git a/src/main/java/com/deepgram/resources/speak/v2/audio/types/AudioGenerateRequestContainer.java b/src/main/java/com/deepgram/resources/speak/v2/audio/types/AudioGenerateRequestContainer.java new file mode 100644 index 0000000..4622ec6 --- /dev/null +++ b/src/main/java/com/deepgram/resources/speak/v2/audio/types/AudioGenerateRequestContainer.java @@ -0,0 +1,94 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ +package com.deepgram.resources.speak.v2.audio.types; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonValue; + +public final class AudioGenerateRequestContainer { + public static final AudioGenerateRequestContainer OGG = new AudioGenerateRequestContainer(Value.OGG, "ogg"); + + public static final AudioGenerateRequestContainer WAV = new AudioGenerateRequestContainer(Value.WAV, "wav"); + + public static final AudioGenerateRequestContainer NONE = new AudioGenerateRequestContainer(Value.NONE, "none"); + + private final Value value; + + private final String string; + + AudioGenerateRequestContainer(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 AudioGenerateRequestContainer + && this.string.equals(((AudioGenerateRequestContainer) other).string)); + } + + @java.lang.Override + public int hashCode() { + return this.string.hashCode(); + } + + public T visit(Visitor visitor) { + switch (value) { + case OGG: + return visitor.visitOgg(); + case WAV: + return visitor.visitWav(); + case NONE: + return visitor.visitNone(); + case UNKNOWN: + default: + return visitor.visitUnknown(string); + } + } + + @JsonCreator(mode = JsonCreator.Mode.DELEGATING) + public static AudioGenerateRequestContainer valueOf(String value) { + switch (value) { + case "ogg": + return OGG; + case "wav": + return WAV; + case "none": + return NONE; + default: + return new AudioGenerateRequestContainer(Value.UNKNOWN, value); + } + } + + public enum Value { + NONE, + + WAV, + + OGG, + + UNKNOWN + } + + public interface Visitor { + T visitNone(); + + T visitWav(); + + T visitOgg(); + + T visitUnknown(String unknownType); + } +} diff --git a/src/main/java/com/deepgram/resources/speak/v2/audio/types/AudioGenerateRequestEncoding.java b/src/main/java/com/deepgram/resources/speak/v2/audio/types/AudioGenerateRequestEncoding.java new file mode 100644 index 0000000..3f0fef4 --- /dev/null +++ b/src/main/java/com/deepgram/resources/speak/v2/audio/types/AudioGenerateRequestEncoding.java @@ -0,0 +1,135 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ +package com.deepgram.resources.speak.v2.audio.types; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonValue; + +public final class AudioGenerateRequestEncoding { + public static final AudioGenerateRequestEncoding AAC = new AudioGenerateRequestEncoding(Value.AAC, "aac"); + + public static final AudioGenerateRequestEncoding MULAW = new AudioGenerateRequestEncoding(Value.MULAW, "mulaw"); + + public static final AudioGenerateRequestEncoding FLAC = new AudioGenerateRequestEncoding(Value.FLAC, "flac"); + + public static final AudioGenerateRequestEncoding MP3 = new AudioGenerateRequestEncoding(Value.MP3, "mp3"); + + public static final AudioGenerateRequestEncoding LINEAR16 = + new AudioGenerateRequestEncoding(Value.LINEAR16, "linear16"); + + public static final AudioGenerateRequestEncoding OPUS = new AudioGenerateRequestEncoding(Value.OPUS, "opus"); + + public static final AudioGenerateRequestEncoding ALAW = new AudioGenerateRequestEncoding(Value.ALAW, "alaw"); + + private final Value value; + + private final String string; + + AudioGenerateRequestEncoding(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 AudioGenerateRequestEncoding + && this.string.equals(((AudioGenerateRequestEncoding) other).string)); + } + + @java.lang.Override + public int hashCode() { + return this.string.hashCode(); + } + + public T visit(Visitor visitor) { + switch (value) { + case AAC: + return visitor.visitAac(); + case MULAW: + return visitor.visitMulaw(); + case FLAC: + return visitor.visitFlac(); + case MP3: + return visitor.visitMp3(); + case LINEAR16: + return visitor.visitLinear16(); + case OPUS: + return visitor.visitOpus(); + case ALAW: + return visitor.visitAlaw(); + case UNKNOWN: + default: + return visitor.visitUnknown(string); + } + } + + @JsonCreator(mode = JsonCreator.Mode.DELEGATING) + public static AudioGenerateRequestEncoding valueOf(String value) { + switch (value) { + case "aac": + return AAC; + case "mulaw": + return MULAW; + case "flac": + return FLAC; + case "mp3": + return MP3; + case "linear16": + return LINEAR16; + case "opus": + return OPUS; + case "alaw": + return ALAW; + default: + return new AudioGenerateRequestEncoding(Value.UNKNOWN, value); + } + } + + public enum Value { + LINEAR16, + + FLAC, + + MULAW, + + ALAW, + + MP3, + + OPUS, + + AAC, + + UNKNOWN + } + + public interface Visitor { + T visitLinear16(); + + T visitFlac(); + + T visitMulaw(); + + T visitAlaw(); + + T visitMp3(); + + T visitOpus(); + + T visitAac(); + + T visitUnknown(String unknownType); + } +} diff --git a/src/main/java/com/deepgram/types/SpeakV2AcceptedResponse.java b/src/main/java/com/deepgram/types/SpeakV2AcceptedResponse.java new file mode 100644 index 0000000..cb5f4bc --- /dev/null +++ b/src/main/java/com/deepgram/types/SpeakV2AcceptedResponse.java @@ -0,0 +1,129 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ +package com.deepgram.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 = SpeakV2AcceptedResponse.Builder.class) +public final class SpeakV2AcceptedResponse { + private final String requestId; + + private final Map additionalProperties; + + private SpeakV2AcceptedResponse(String requestId, Map additionalProperties) { + this.requestId = requestId; + this.additionalProperties = additionalProperties; + } + + /** + * @return Unique identifier for tracking the asynchronous request + */ + @JsonProperty("request_id") + public String getRequestId() { + return requestId; + } + + @java.lang.Override + public boolean equals(Object other) { + if (this == other) return true; + return other instanceof SpeakV2AcceptedResponse && equalTo((SpeakV2AcceptedResponse) other); + } + + @JsonAnyGetter + public Map getAdditionalProperties() { + return this.additionalProperties; + } + + private boolean equalTo(SpeakV2AcceptedResponse other) { + return requestId.equals(other.requestId); + } + + @java.lang.Override + public int hashCode() { + return Objects.hash(this.requestId); + } + + @java.lang.Override + public String toString() { + return ObjectMappers.stringify(this); + } + + public static RequestIdStage builder() { + return new Builder(); + } + + public interface RequestIdStage { + /** + *

Unique identifier for tracking the asynchronous request

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

Unique identifier for tracking the asynchronous request

+ *

Unique identifier for tracking the asynchronous request

+ * @return Reference to {@code this} so that method calls can be chained together. + */ + @java.lang.Override + @JsonSetter("request_id") + public _FinalStage requestId(@NotNull String requestId) { + this.requestId = Objects.requireNonNull(requestId, "requestId must not be null"); + return this; + } + + @java.lang.Override + public SpeakV2AcceptedResponse build() { + return new SpeakV2AcceptedResponse(requestId, 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/test/java/com/deepgram/SpeakV2BatchTest.java b/src/test/java/com/deepgram/SpeakV2BatchTest.java new file mode 100644 index 0000000..0c26ba1 --- /dev/null +++ b/src/test/java/com/deepgram/SpeakV2BatchTest.java @@ -0,0 +1,106 @@ +package com.deepgram; + +import static org.assertj.core.api.Assertions.assertThat; + +import com.deepgram.core.Environment; +import com.deepgram.resources.speak.v2.audio.requests.SpeakV2Request; +import com.deepgram.resources.speak.v2.audio.types.AudioGenerateRequestEncoding; +import java.io.InputStream; +import okhttp3.HttpUrl; +import okhttp3.mockwebserver.MockResponse; +import okhttp3.mockwebserver.MockWebServer; +import okhttp3.mockwebserver.RecordedRequest; +import okio.Buffer; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +/** + * Hand-written coverage for the Flux TTS batch (REST) endpoint: {@code speak().v2().audio().generate()} + * (POST /v2/speak). + * + *

The Fern generator did not emit a wire test for this endpoint, so this fills the gap. It asserts + * the outgoing request shape (POST /v2/speak, {@code model}/{@code encoding} in the query, {@code text} + * in the JSON body), that the binary audio response is returned as an {@link InputStream}, and that + * integer {@code sample_rate}/{@code bit_rate} serialize WITHOUT a decimal — stem parses these as a + * nonzero u32 and rejects {@code "24000.0"}, so this guards the spec's {@code type: integer} typing + * against a future regen. Frozen via {@code src/test/} in .fernignore. + */ +class SpeakV2BatchTest { + private static final byte[] AUDIO = new byte[] {(byte) 0xFF, (byte) 0xFB, (byte) 0x90, 0x00, 0x11, 0x22, 0x33, 0x44}; + + private MockWebServer server; + private DeepgramClient client; + + @BeforeEach + void setUp() throws Exception { + server = new MockWebServer(); + server.start(); + String base = server.url("/").toString().replaceAll("/$", ""); + Environment env = Environment.custom().base(base).production(base).agent(base).agentRest(base).build(); + client = DeepgramClient.builder().apiKey("test").environment(env).build(); + } + + @AfterEach + void tearDown() throws Exception { + server.shutdown(); + } + + private void enqueueAudio() { + server.enqueue(new MockResponse() + .setResponseCode(200) + .addHeader("Content-Type", "audio/mpeg") + .setBody(new Buffer().write(AUDIO))); + } + + @Test + @DisplayName("issues POST /v2/speak with model in query + text in body, and returns the audio bytes") + void batchRequestShape() throws Exception { + enqueueAudio(); + + InputStream response = client.speak() + .v2() + .audio() + .generate(SpeakV2Request.builder() + .model("flux-alexis-en") + .text("Hello from the batch endpoint.") + .encoding(AudioGenerateRequestEncoding.MP3) + .build()); + byte[] audio = response.readAllBytes(); + + RecordedRequest request = server.takeRequest(); + assertThat(request.getMethod()).isEqualTo("POST"); + HttpUrl url = request.getRequestUrl(); + assertThat(url.encodedPath()).isEqualTo("/v2/speak"); + assertThat(url.queryParameter("model")).isEqualTo("flux-alexis-en"); + assertThat(url.queryParameter("encoding")).isEqualTo("mp3"); + assertThat(request.getBody().readUtf8()).contains("\"text\":\"Hello from the batch endpoint.\""); + assertThat(audio).isEqualTo(AUDIO); + } + + @Test + @DisplayName("serializes integer sample_rate/bit_rate without a decimal (guards the type: integer fix)") + void integerSampleRateAndBitRate() throws Exception { + enqueueAudio(); + + client.speak() + .v2() + .audio() + .generate(SpeakV2Request.builder() + .model("flux-alexis-en") + .text("hi") + .encoding(AudioGenerateRequestEncoding.LINEAR16) + .sampleRate(24000) + .bitRate(48000) + .build()) + .readAllBytes(); + + HttpUrl url = server.takeRequest().getRequestUrl(); + assertThat(url.queryParameter("sample_rate")).isEqualTo("24000"); + assertThat(url.queryParameter("bit_rate")).isEqualTo("48000"); + // Must NOT be "24000.0" — stem rejects a non-integer with "expected a nonzero u32". + assertThat(url.queryParameter("sample_rate")).doesNotContain("."); + assertThat(url.queryParameter("bit_rate")).doesNotContain("."); + } +}