diff --git a/.fern/metadata.json b/.fern/metadata.json index 0ebdc43..df860bb 100644 --- a/.fern/metadata.json +++ b/.fern/metadata.json @@ -1,5 +1,5 @@ { - "cliVersion": "5.50.1", + "cliVersion": "5.50.4", "generatorName": "fernapi/fern-java-sdk", "generatorVersion": "4.9.2", "generatorConfig": { @@ -10,10 +10,10 @@ "enable-wire-tests": true, "publish-to": "central" }, - "originGitCommit": "fbaef4c66e97232edfaacb20d23d476aa286ecdd", + "originGitCommit": "e2c477c4583f8f9e7d0d1a5b5591c1aa8cabef10", "originGitCommitIsDirty": true, "invokedBy": "ci", "requestedVersion": "AUTO", "ciProvider": "unknown", - "sdkVersion": "17.6.0" -} \ No newline at end of file + "sdkVersion": "17.7.0" +} diff --git a/build.gradle b/build.gradle index 842382d..c80eee6 100644 --- a/build.gradle +++ b/build.gradle @@ -48,7 +48,7 @@ java { group = 'com.phenoml.maven' -version = '17.6.0' +version = '17.7.0' jar { dependsOn(":generatePomFileForMavenPublication") @@ -79,7 +79,7 @@ publishing { maven(MavenPublication) { groupId = 'com.phenoml.maven' artifactId = 'phenoml-java-sdk' - version = '17.6.0' + version = '17.7.0' from components.java pom { name = 'phenoml' diff --git a/changelog.md b/changelog.md index 18c89a5..f4c3a7f 100644 --- a/changelog.md +++ b/changelog.md @@ -1,3 +1,11 @@ +## [17.7.0] - 2026-06-23 +### Added +- **`PhenomlClient.voice().voice().transcribe(...)`** — new sync method that uploads raw audio bytes (WAV, FLAC, MP3, or OGG/WebM Opus) to `POST /transcribe` and returns a `TranscribeResponse`, supporting up to ~5 minutes of audio per request. +- **`AsyncPhenomlClient.voice().voice().transcribe(...)`** — new async method for the same voice transcription endpoint. +- **`TranscribeRequest`** — new request type carrying raw audio bytes plus an optional BCP-47 `language` list. +- **`TranscribeResponse.getTranscript()`** — new response accessor exposing the full transcript returned by the voice service. +- **Voice service errors** — new typed exceptions (`BadRequestError`, `UnauthorizedError`, `PaymentRequiredError`, `ContentTooLargeError`, `BadGatewayError`, `ServiceUnavailableError`, `GatewayTimeoutError`) thrown by the voice service. + ## [17.6.0] - 2026-06-18 ### Added - **`ConflictError`** — new typed exception thrown by `RawChatClient.send(...)`, `RawChatClient.stream(...)`, `AsyncRawChatClient.send(...)`, and `AsyncRawChatClient.stream(...)` for HTTP 409 responses when a session already has an active turn. diff --git a/code-examples.json b/code-examples.json index 00fb9e1..3606eac 100644 --- a/code-examples.json +++ b/code-examples.json @@ -2,8 +2,8 @@ "metadata": { "language": "java", "packageName": "com.phenoml.maven:phenoml-java-sdk", - "sdkVersion": "17.6.0", - "specCommit": "fbaef4c66e97232edfaacb20d23d476aa286ecdd", + "sdkVersion": "17.7.0", + "specCommit": "e2c477c4583f8f9e7d0d1a5b5591c1aa8cabef10", "generatorName": "fernapi/fern-java-sdk" }, "renderRules": { @@ -4553,6 +4553,37 @@ ] } }, + "POST /transcribe": { + "httpMethod": "POST", + "httpPath": "/transcribe", + "request": { + "body": null + }, + "response": { + "body": null + }, + "render": { + "callTemplate": "client.voice().voice().transcribe(TranscribeRequest.builder(){{__body__}}.build())", + "params": [], + "body": { + "fieldSeparator": "", + "fields": [ + { + "jsonKey": "language", + "fieldTemplate": ".language({{value}})", + "kind": "list", + "required": false, + "items": { + "jsonKey": "", + "fieldTemplate": "{{value}}", + "kind": "string", + "required": true + } + } + ] + } + } + }, "GET /workflows": { "httpMethod": "GET", "httpPath": "/workflows", diff --git a/reference.md b/reference.md index 6731113..22d9d24 100644 --- a/reference.md +++ b/reference.md @@ -5490,6 +5490,7 @@ client.tools().mcpTools().delete("mcp_server_tool_id"); +## Voice ## Workflows
client.workflows.list() -> ListWorkflowsResponse
diff --git a/src/main/java/com/phenoml/api/AsyncPhenomlClient.java b/src/main/java/com/phenoml/api/AsyncPhenomlClient.java index e308c3f..d03501c 100644 --- a/src/main/java/com/phenoml/api/AsyncPhenomlClient.java +++ b/src/main/java/com/phenoml/api/AsyncPhenomlClient.java @@ -15,6 +15,7 @@ import com.phenoml.api.resources.lang2fhir.AsyncLang2FhirClient; import com.phenoml.api.resources.summary.AsyncSummaryClient; import com.phenoml.api.resources.tools.AsyncToolsClient; +import com.phenoml.api.resources.voice.AsyncVoiceClient; import com.phenoml.api.resources.workflows.AsyncWorkflowsClient; import java.util.function.Supplier; @@ -41,6 +42,8 @@ public class AsyncPhenomlClient { protected final Supplier toolsClient; + protected final Supplier voiceClient; + protected final Supplier workflowsClient; public AsyncPhenomlClient(ClientOptions clientOptions) { @@ -55,6 +58,7 @@ public AsyncPhenomlClient(ClientOptions clientOptions) { this.lang2FhirClient = Suppliers.memoize(() -> new AsyncLang2FhirClient(clientOptions)); this.summaryClient = Suppliers.memoize(() -> new AsyncSummaryClient(clientOptions)); this.toolsClient = Suppliers.memoize(() -> new AsyncToolsClient(clientOptions)); + this.voiceClient = Suppliers.memoize(() -> new AsyncVoiceClient(clientOptions)); this.workflowsClient = Suppliers.memoize(() -> new AsyncWorkflowsClient(clientOptions)); } @@ -98,6 +102,10 @@ public AsyncToolsClient tools() { return this.toolsClient.get(); } + public AsyncVoiceClient voice() { + return this.voiceClient.get(); + } + public AsyncWorkflowsClient workflows() { return this.workflowsClient.get(); } diff --git a/src/main/java/com/phenoml/api/PhenomlClient.java b/src/main/java/com/phenoml/api/PhenomlClient.java index 9bd4743..ff4e24e 100644 --- a/src/main/java/com/phenoml/api/PhenomlClient.java +++ b/src/main/java/com/phenoml/api/PhenomlClient.java @@ -15,6 +15,7 @@ import com.phenoml.api.resources.lang2fhir.Lang2FhirClient; import com.phenoml.api.resources.summary.SummaryClient; import com.phenoml.api.resources.tools.ToolsClient; +import com.phenoml.api.resources.voice.VoiceClient; import com.phenoml.api.resources.workflows.WorkflowsClient; import java.util.function.Supplier; @@ -41,6 +42,8 @@ public class PhenomlClient { protected final Supplier toolsClient; + protected final Supplier voiceClient; + protected final Supplier workflowsClient; public PhenomlClient(ClientOptions clientOptions) { @@ -55,6 +58,7 @@ public PhenomlClient(ClientOptions clientOptions) { this.lang2FhirClient = Suppliers.memoize(() -> new Lang2FhirClient(clientOptions)); this.summaryClient = Suppliers.memoize(() -> new SummaryClient(clientOptions)); this.toolsClient = Suppliers.memoize(() -> new ToolsClient(clientOptions)); + this.voiceClient = Suppliers.memoize(() -> new VoiceClient(clientOptions)); this.workflowsClient = Suppliers.memoize(() -> new WorkflowsClient(clientOptions)); } @@ -98,6 +102,10 @@ public ToolsClient tools() { return this.toolsClient.get(); } + public VoiceClient voice() { + return this.voiceClient.get(); + } + public WorkflowsClient workflows() { return this.workflowsClient.get(); } diff --git a/src/main/java/com/phenoml/api/core/ClientOptions.java b/src/main/java/com/phenoml/api/core/ClientOptions.java index af95a7f..882d3b8 100644 --- a/src/main/java/com/phenoml/api/core/ClientOptions.java +++ b/src/main/java/com/phenoml/api/core/ClientOptions.java @@ -38,10 +38,10 @@ private ClientOptions( this.headers.putAll(headers); this.headers.putAll(new HashMap() { { - put("User-Agent", "com.phenoml.maven:phenoml-java-sdk/17.6.0"); + put("User-Agent", "com.phenoml.maven:phenoml-java-sdk/17.7.0"); put("X-Fern-Language", "JAVA"); put("X-Fern-SDK-Name", "com.phenoml.fern:api-sdk"); - put("X-Fern-SDK-Version", "17.6.0"); + put("X-Fern-SDK-Version", "17.7.0"); } }); this.headerSuppliers = headerSuppliers; diff --git a/src/main/java/com/phenoml/api/resources/voice/AsyncVoiceClient.java b/src/main/java/com/phenoml/api/resources/voice/AsyncVoiceClient.java new file mode 100644 index 0000000..7ee4e32 --- /dev/null +++ b/src/main/java/com/phenoml/api/resources/voice/AsyncVoiceClient.java @@ -0,0 +1,24 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ +package com.phenoml.api.resources.voice; + +import com.phenoml.api.core.ClientOptions; +import com.phenoml.api.core.Suppliers; +import java.util.function.Supplier; + +public class AsyncVoiceClient { + protected final ClientOptions clientOptions; + + protected final Supplier voiceClient; + + public AsyncVoiceClient(ClientOptions clientOptions) { + this.clientOptions = clientOptions; + this.voiceClient = + Suppliers.memoize(() -> new com.phenoml.api.resources.voice.voice.AsyncVoiceClient(clientOptions)); + } + + public com.phenoml.api.resources.voice.voice.AsyncVoiceClient voice() { + return this.voiceClient.get(); + } +} diff --git a/src/main/java/com/phenoml/api/resources/voice/VoiceClient.java b/src/main/java/com/phenoml/api/resources/voice/VoiceClient.java new file mode 100644 index 0000000..91d847d --- /dev/null +++ b/src/main/java/com/phenoml/api/resources/voice/VoiceClient.java @@ -0,0 +1,24 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ +package com.phenoml.api.resources.voice; + +import com.phenoml.api.core.ClientOptions; +import com.phenoml.api.core.Suppliers; +import java.util.function.Supplier; + +public class VoiceClient { + protected final ClientOptions clientOptions; + + protected final Supplier voiceClient; + + public VoiceClient(ClientOptions clientOptions) { + this.clientOptions = clientOptions; + this.voiceClient = + Suppliers.memoize(() -> new com.phenoml.api.resources.voice.voice.VoiceClient(clientOptions)); + } + + public com.phenoml.api.resources.voice.voice.VoiceClient voice() { + return this.voiceClient.get(); + } +} diff --git a/src/main/java/com/phenoml/api/resources/voice/errors/BadGatewayError.java b/src/main/java/com/phenoml/api/resources/voice/errors/BadGatewayError.java new file mode 100644 index 0000000..d3619a9 --- /dev/null +++ b/src/main/java/com/phenoml/api/resources/voice/errors/BadGatewayError.java @@ -0,0 +1,32 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ +package com.phenoml.api.resources.voice.errors; + +import com.phenoml.api.core.PhenomlClientApiException; +import okhttp3.Response; + +public final class BadGatewayError extends PhenomlClientApiException { + /** + * The body of the response that triggered the exception. + */ + private final Object body; + + public BadGatewayError(Object body) { + super("BadGatewayError", 502, body); + this.body = body; + } + + public BadGatewayError(Object body, Response rawResponse) { + super("BadGatewayError", 502, body, rawResponse); + this.body = body; + } + + /** + * @return the body + */ + @java.lang.Override + public Object body() { + return this.body; + } +} diff --git a/src/main/java/com/phenoml/api/resources/voice/errors/BadRequestError.java b/src/main/java/com/phenoml/api/resources/voice/errors/BadRequestError.java new file mode 100644 index 0000000..9fdf894 --- /dev/null +++ b/src/main/java/com/phenoml/api/resources/voice/errors/BadRequestError.java @@ -0,0 +1,32 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ +package com.phenoml.api.resources.voice.errors; + +import com.phenoml.api.core.PhenomlClientApiException; +import okhttp3.Response; + +public final class BadRequestError extends PhenomlClientApiException { + /** + * The body of the response that triggered the exception. + */ + private final Object body; + + public BadRequestError(Object body) { + super("BadRequestError", 400, body); + this.body = body; + } + + public BadRequestError(Object body, Response rawResponse) { + super("BadRequestError", 400, body, rawResponse); + this.body = body; + } + + /** + * @return the body + */ + @java.lang.Override + public Object body() { + return this.body; + } +} diff --git a/src/main/java/com/phenoml/api/resources/voice/errors/ContentTooLargeError.java b/src/main/java/com/phenoml/api/resources/voice/errors/ContentTooLargeError.java new file mode 100644 index 0000000..806588a --- /dev/null +++ b/src/main/java/com/phenoml/api/resources/voice/errors/ContentTooLargeError.java @@ -0,0 +1,32 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ +package com.phenoml.api.resources.voice.errors; + +import com.phenoml.api.core.PhenomlClientApiException; +import okhttp3.Response; + +public final class ContentTooLargeError extends PhenomlClientApiException { + /** + * The body of the response that triggered the exception. + */ + private final Object body; + + public ContentTooLargeError(Object body) { + super("ContentTooLargeError", 413, body); + this.body = body; + } + + public ContentTooLargeError(Object body, Response rawResponse) { + super("ContentTooLargeError", 413, body, rawResponse); + this.body = body; + } + + /** + * @return the body + */ + @java.lang.Override + public Object body() { + return this.body; + } +} diff --git a/src/main/java/com/phenoml/api/resources/voice/errors/GatewayTimeoutError.java b/src/main/java/com/phenoml/api/resources/voice/errors/GatewayTimeoutError.java new file mode 100644 index 0000000..41d4bbf --- /dev/null +++ b/src/main/java/com/phenoml/api/resources/voice/errors/GatewayTimeoutError.java @@ -0,0 +1,32 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ +package com.phenoml.api.resources.voice.errors; + +import com.phenoml.api.core.PhenomlClientApiException; +import okhttp3.Response; + +public final class GatewayTimeoutError extends PhenomlClientApiException { + /** + * The body of the response that triggered the exception. + */ + private final Object body; + + public GatewayTimeoutError(Object body) { + super("GatewayTimeoutError", 504, body); + this.body = body; + } + + public GatewayTimeoutError(Object body, Response rawResponse) { + super("GatewayTimeoutError", 504, body, rawResponse); + this.body = body; + } + + /** + * @return the body + */ + @java.lang.Override + public Object body() { + return this.body; + } +} diff --git a/src/main/java/com/phenoml/api/resources/voice/errors/PaymentRequiredError.java b/src/main/java/com/phenoml/api/resources/voice/errors/PaymentRequiredError.java new file mode 100644 index 0000000..c0a0314 --- /dev/null +++ b/src/main/java/com/phenoml/api/resources/voice/errors/PaymentRequiredError.java @@ -0,0 +1,32 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ +package com.phenoml.api.resources.voice.errors; + +import com.phenoml.api.core.PhenomlClientApiException; +import okhttp3.Response; + +public final class PaymentRequiredError extends PhenomlClientApiException { + /** + * The body of the response that triggered the exception. + */ + private final Object body; + + public PaymentRequiredError(Object body) { + super("PaymentRequiredError", 402, body); + this.body = body; + } + + public PaymentRequiredError(Object body, Response rawResponse) { + super("PaymentRequiredError", 402, body, rawResponse); + this.body = body; + } + + /** + * @return the body + */ + @java.lang.Override + public Object body() { + return this.body; + } +} diff --git a/src/main/java/com/phenoml/api/resources/voice/errors/ServiceUnavailableError.java b/src/main/java/com/phenoml/api/resources/voice/errors/ServiceUnavailableError.java new file mode 100644 index 0000000..ff612e2 --- /dev/null +++ b/src/main/java/com/phenoml/api/resources/voice/errors/ServiceUnavailableError.java @@ -0,0 +1,32 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ +package com.phenoml.api.resources.voice.errors; + +import com.phenoml.api.core.PhenomlClientApiException; +import okhttp3.Response; + +public final class ServiceUnavailableError extends PhenomlClientApiException { + /** + * The body of the response that triggered the exception. + */ + private final Object body; + + public ServiceUnavailableError(Object body) { + super("ServiceUnavailableError", 503, body); + this.body = body; + } + + public ServiceUnavailableError(Object body, Response rawResponse) { + super("ServiceUnavailableError", 503, body, rawResponse); + this.body = body; + } + + /** + * @return the body + */ + @java.lang.Override + public Object body() { + return this.body; + } +} diff --git a/src/main/java/com/phenoml/api/resources/voice/errors/UnauthorizedError.java b/src/main/java/com/phenoml/api/resources/voice/errors/UnauthorizedError.java new file mode 100644 index 0000000..464b538 --- /dev/null +++ b/src/main/java/com/phenoml/api/resources/voice/errors/UnauthorizedError.java @@ -0,0 +1,32 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ +package com.phenoml.api.resources.voice.errors; + +import com.phenoml.api.core.PhenomlClientApiException; +import okhttp3.Response; + +public final class UnauthorizedError extends PhenomlClientApiException { + /** + * The body of the response that triggered the exception. + */ + private final Object body; + + public UnauthorizedError(Object body) { + super("UnauthorizedError", 401, body); + this.body = body; + } + + public UnauthorizedError(Object body, Response rawResponse) { + super("UnauthorizedError", 401, body, rawResponse); + this.body = body; + } + + /** + * @return the body + */ + @java.lang.Override + public Object body() { + return this.body; + } +} diff --git a/src/main/java/com/phenoml/api/resources/voice/types/TranscribeResponse.java b/src/main/java/com/phenoml/api/resources/voice/types/TranscribeResponse.java new file mode 100644 index 0000000..73641af --- /dev/null +++ b/src/main/java/com/phenoml/api/resources/voice/types/TranscribeResponse.java @@ -0,0 +1,129 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ +package com.phenoml.api.resources.voice.types; + +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 com.phenoml.api.core.ObjectMappers; +import java.util.HashMap; +import java.util.Map; +import java.util.Objects; +import org.jetbrains.annotations.NotNull; + +@JsonInclude(JsonInclude.Include.NON_ABSENT) +@JsonDeserialize(builder = TranscribeResponse.Builder.class) +public final class TranscribeResponse { + private final String transcript; + + private final Map additionalProperties; + + private TranscribeResponse(String transcript, Map additionalProperties) { + this.transcript = transcript; + this.additionalProperties = additionalProperties; + } + + /** + * @return The full transcript of the audio. + */ + @JsonProperty("transcript") + public String getTranscript() { + return transcript; + } + + @java.lang.Override + public boolean equals(Object other) { + if (this == other) return true; + return other instanceof TranscribeResponse && equalTo((TranscribeResponse) other); + } + + @JsonAnyGetter + public Map getAdditionalProperties() { + return this.additionalProperties; + } + + private boolean equalTo(TranscribeResponse other) { + return transcript.equals(other.transcript); + } + + @java.lang.Override + public int hashCode() { + return Objects.hash(this.transcript); + } + + @java.lang.Override + public String toString() { + return ObjectMappers.stringify(this); + } + + public static TranscriptStage builder() { + return new Builder(); + } + + public interface TranscriptStage { + /** + *

The full transcript of the audio.

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

The full transcript of the audio.

+ *

The full transcript of the audio.

+ * @return Reference to {@code this} so that method calls can be chained together. + */ + @java.lang.Override + @JsonSetter("transcript") + public _FinalStage transcript(@NotNull String transcript) { + this.transcript = Objects.requireNonNull(transcript, "transcript must not be null"); + return this; + } + + @java.lang.Override + public TranscribeResponse build() { + return new TranscribeResponse(transcript, 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/phenoml/api/resources/voice/voice/AsyncRawVoiceClient.java b/src/main/java/com/phenoml/api/resources/voice/voice/AsyncRawVoiceClient.java new file mode 100644 index 0000000..a0c379e --- /dev/null +++ b/src/main/java/com/phenoml/api/resources/voice/voice/AsyncRawVoiceClient.java @@ -0,0 +1,195 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ +package com.phenoml.api.resources.voice.voice; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.phenoml.api.core.ClientOptions; +import com.phenoml.api.core.InputStreamRequestBody; +import com.phenoml.api.core.ObjectMappers; +import com.phenoml.api.core.PhenomlClientApiException; +import com.phenoml.api.core.PhenomlClientException; +import com.phenoml.api.core.PhenomlClientHttpResponse; +import com.phenoml.api.core.QueryStringMapper; +import com.phenoml.api.core.RequestOptions; +import com.phenoml.api.resources.voice.errors.BadGatewayError; +import com.phenoml.api.resources.voice.errors.BadRequestError; +import com.phenoml.api.resources.voice.errors.ContentTooLargeError; +import com.phenoml.api.resources.voice.errors.GatewayTimeoutError; +import com.phenoml.api.resources.voice.errors.PaymentRequiredError; +import com.phenoml.api.resources.voice.errors.ServiceUnavailableError; +import com.phenoml.api.resources.voice.errors.UnauthorizedError; +import com.phenoml.api.resources.voice.types.TranscribeResponse; +import com.phenoml.api.resources.voice.voice.requests.TranscribeRequest; +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.util.concurrent.CompletableFuture; +import okhttp3.Call; +import okhttp3.Callback; +import okhttp3.Headers; +import okhttp3.HttpUrl; +import okhttp3.MediaType; +import okhttp3.OkHttpClient; +import okhttp3.Request; +import okhttp3.RequestBody; +import okhttp3.Response; +import okhttp3.ResponseBody; +import org.jetbrains.annotations.NotNull; + +public class AsyncRawVoiceClient { + protected final ClientOptions clientOptions; + + public AsyncRawVoiceClient(ClientOptions clientOptions) { + this.clientOptions = clientOptions; + } + + /** + * Transcribes an uploaded audio recording and returns the transcript. + * Send the raw audio bytes as the request body; the audio format is + * detected automatically (WAV, FLAC, MP3, OGG/WebM Opus). + *

Supports up to ~5 minutes of audio per request. This limit is on audio + * duration regardless of file size or format, so a compressed recording + * within the size limit can still be rejected for being too long. Pair the + * transcript with a downstream text step (e.g. POST /lang2fhir/create) + * to turn it into a FHIR resource.

+ */ + public CompletableFuture> transcribe(byte[] body) { + return transcribe(TranscribeRequest.builder().body(body).build()); + } + + /** + * Transcribes an uploaded audio recording and returns the transcript. + * Send the raw audio bytes as the request body; the audio format is + * detected automatically (WAV, FLAC, MP3, OGG/WebM Opus). + *

Supports up to ~5 minutes of audio per request. This limit is on audio + * duration regardless of file size or format, so a compressed recording + * within the size limit can still be rejected for being too long. Pair the + * transcript with a downstream text step (e.g. POST /lang2fhir/create) + * to turn it into a FHIR resource.

+ */ + public CompletableFuture> transcribe( + byte[] body, RequestOptions requestOptions) { + return transcribe(TranscribeRequest.builder().body(body).build(), requestOptions); + } + + /** + * Transcribes an uploaded audio recording and returns the transcript. + * Send the raw audio bytes as the request body; the audio format is + * detected automatically (WAV, FLAC, MP3, OGG/WebM Opus). + *

Supports up to ~5 minutes of audio per request. This limit is on audio + * duration regardless of file size or format, so a compressed recording + * within the size limit can still be rejected for being too long. Pair the + * transcript with a downstream text step (e.g. POST /lang2fhir/create) + * to turn it into a FHIR resource.

+ */ + public CompletableFuture> transcribe(TranscribeRequest request) { + return transcribe(request, null); + } + + /** + * Transcribes an uploaded audio recording and returns the transcript. + * Send the raw audio bytes as the request body; the audio format is + * detected automatically (WAV, FLAC, MP3, OGG/WebM Opus). + *

Supports up to ~5 minutes of audio per request. This limit is on audio + * duration regardless of file size or format, so a compressed recording + * within the size limit can still be rejected for being too long. Pair the + * transcript with a downstream text step (e.g. POST /lang2fhir/create) + * to turn it into a FHIR resource.

+ */ + public CompletableFuture> transcribe( + TranscribeRequest request, RequestOptions requestOptions) { + HttpUrl.Builder httpUrl = HttpUrl.parse(this.clientOptions.environment().getUrl()) + .newBuilder() + .addPathSegments("transcribe"); + if (request.getLanguage().isPresent()) { + QueryStringMapper.addQueryParameter( + httpUrl, "language", request.getLanguage().get(), true); + } + if (requestOptions != null) { + requestOptions.getQueryParameters().forEach((_key, _value) -> { + httpUrl.addQueryParameter(_key, _value); + }); + } + RequestBody body = new InputStreamRequestBody( + MediaType.parse("application/octet-stream"), new ByteArrayInputStream(request.getBody())); + Request.Builder _requestBuilder = new Request.Builder() + .url(httpUrl.build()) + .method("POST", body) + .headers(Headers.of(clientOptions.headers(requestOptions))) + .addHeader("Content-Type", "application/octet-stream") + .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()) { + String responseBodyString = responseBody != null ? responseBody.string() : "{}"; + if (response.isSuccessful()) { + future.complete(new PhenomlClientHttpResponse<>( + ObjectMappers.JSON_MAPPER.readValue(responseBodyString, TranscribeResponse.class), + response)); + return; + } + try { + switch (response.code()) { + case 400: + future.completeExceptionally(new BadRequestError( + ObjectMappers.JSON_MAPPER.readValue(responseBodyString, Object.class), + response)); + return; + case 401: + future.completeExceptionally(new UnauthorizedError( + ObjectMappers.JSON_MAPPER.readValue(responseBodyString, Object.class), + response)); + return; + case 402: + future.completeExceptionally(new PaymentRequiredError( + ObjectMappers.JSON_MAPPER.readValue(responseBodyString, Object.class), + response)); + return; + case 413: + future.completeExceptionally(new ContentTooLargeError( + ObjectMappers.JSON_MAPPER.readValue(responseBodyString, Object.class), + response)); + return; + case 502: + future.completeExceptionally(new BadGatewayError( + ObjectMappers.JSON_MAPPER.readValue(responseBodyString, Object.class), + response)); + return; + case 503: + future.completeExceptionally(new ServiceUnavailableError( + ObjectMappers.JSON_MAPPER.readValue(responseBodyString, Object.class), + response)); + return; + case 504: + future.completeExceptionally(new GatewayTimeoutError( + 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 PhenomlClientApiException( + "Error with status code " + response.code(), response.code(), errorBody, response)); + return; + } catch (IOException e) { + future.completeExceptionally(new PhenomlClientException("Network error executing HTTP request", e)); + } + } + + @Override + public void onFailure(@NotNull Call call, @NotNull IOException e) { + future.completeExceptionally(new PhenomlClientException("Network error executing HTTP request", e)); + } + }); + return future; + } +} diff --git a/src/main/java/com/phenoml/api/resources/voice/voice/AsyncVoiceClient.java b/src/main/java/com/phenoml/api/resources/voice/voice/AsyncVoiceClient.java new file mode 100644 index 0000000..00dce6c --- /dev/null +++ b/src/main/java/com/phenoml/api/resources/voice/voice/AsyncVoiceClient.java @@ -0,0 +1,84 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ +package com.phenoml.api.resources.voice.voice; + +import com.phenoml.api.core.ClientOptions; +import com.phenoml.api.core.RequestOptions; +import com.phenoml.api.resources.voice.types.TranscribeResponse; +import com.phenoml.api.resources.voice.voice.requests.TranscribeRequest; +import java.util.concurrent.CompletableFuture; + +public class AsyncVoiceClient { + protected final ClientOptions clientOptions; + + private final AsyncRawVoiceClient rawClient; + + public AsyncVoiceClient(ClientOptions clientOptions) { + this.clientOptions = clientOptions; + this.rawClient = new AsyncRawVoiceClient(clientOptions); + } + + /** + * Get responses with HTTP metadata like headers + */ + public AsyncRawVoiceClient withRawResponse() { + return this.rawClient; + } + + /** + * Transcribes an uploaded audio recording and returns the transcript. + * Send the raw audio bytes as the request body; the audio format is + * detected automatically (WAV, FLAC, MP3, OGG/WebM Opus). + *

Supports up to ~5 minutes of audio per request. This limit is on audio + * duration regardless of file size or format, so a compressed recording + * within the size limit can still be rejected for being too long. Pair the + * transcript with a downstream text step (e.g. POST /lang2fhir/create) + * to turn it into a FHIR resource.

+ */ + public CompletableFuture transcribe(byte[] body) { + return this.rawClient.transcribe(body).thenApply(response -> response.body()); + } + + /** + * Transcribes an uploaded audio recording and returns the transcript. + * Send the raw audio bytes as the request body; the audio format is + * detected automatically (WAV, FLAC, MP3, OGG/WebM Opus). + *

Supports up to ~5 minutes of audio per request. This limit is on audio + * duration regardless of file size or format, so a compressed recording + * within the size limit can still be rejected for being too long. Pair the + * transcript with a downstream text step (e.g. POST /lang2fhir/create) + * to turn it into a FHIR resource.

+ */ + public CompletableFuture transcribe(byte[] body, RequestOptions requestOptions) { + return this.rawClient.transcribe(body, requestOptions).thenApply(response -> response.body()); + } + + /** + * Transcribes an uploaded audio recording and returns the transcript. + * Send the raw audio bytes as the request body; the audio format is + * detected automatically (WAV, FLAC, MP3, OGG/WebM Opus). + *

Supports up to ~5 minutes of audio per request. This limit is on audio + * duration regardless of file size or format, so a compressed recording + * within the size limit can still be rejected for being too long. Pair the + * transcript with a downstream text step (e.g. POST /lang2fhir/create) + * to turn it into a FHIR resource.

+ */ + public CompletableFuture transcribe(TranscribeRequest request) { + return this.rawClient.transcribe(request).thenApply(response -> response.body()); + } + + /** + * Transcribes an uploaded audio recording and returns the transcript. + * Send the raw audio bytes as the request body; the audio format is + * detected automatically (WAV, FLAC, MP3, OGG/WebM Opus). + *

Supports up to ~5 minutes of audio per request. This limit is on audio + * duration regardless of file size or format, so a compressed recording + * within the size limit can still be rejected for being too long. Pair the + * transcript with a downstream text step (e.g. POST /lang2fhir/create) + * to turn it into a FHIR resource.

+ */ + public CompletableFuture transcribe(TranscribeRequest request, RequestOptions requestOptions) { + return this.rawClient.transcribe(request, requestOptions).thenApply(response -> response.body()); + } +} diff --git a/src/main/java/com/phenoml/api/resources/voice/voice/RawVoiceClient.java b/src/main/java/com/phenoml/api/resources/voice/voice/RawVoiceClient.java new file mode 100644 index 0000000..57d76ac --- /dev/null +++ b/src/main/java/com/phenoml/api/resources/voice/voice/RawVoiceClient.java @@ -0,0 +1,162 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ +package com.phenoml.api.resources.voice.voice; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.phenoml.api.core.ClientOptions; +import com.phenoml.api.core.InputStreamRequestBody; +import com.phenoml.api.core.ObjectMappers; +import com.phenoml.api.core.PhenomlClientApiException; +import com.phenoml.api.core.PhenomlClientException; +import com.phenoml.api.core.PhenomlClientHttpResponse; +import com.phenoml.api.core.QueryStringMapper; +import com.phenoml.api.core.RequestOptions; +import com.phenoml.api.resources.voice.errors.BadGatewayError; +import com.phenoml.api.resources.voice.errors.BadRequestError; +import com.phenoml.api.resources.voice.errors.ContentTooLargeError; +import com.phenoml.api.resources.voice.errors.GatewayTimeoutError; +import com.phenoml.api.resources.voice.errors.PaymentRequiredError; +import com.phenoml.api.resources.voice.errors.ServiceUnavailableError; +import com.phenoml.api.resources.voice.errors.UnauthorizedError; +import com.phenoml.api.resources.voice.types.TranscribeResponse; +import com.phenoml.api.resources.voice.voice.requests.TranscribeRequest; +import java.io.ByteArrayInputStream; +import java.io.IOException; +import okhttp3.Headers; +import okhttp3.HttpUrl; +import okhttp3.MediaType; +import okhttp3.OkHttpClient; +import okhttp3.Request; +import okhttp3.RequestBody; +import okhttp3.Response; +import okhttp3.ResponseBody; + +public class RawVoiceClient { + protected final ClientOptions clientOptions; + + public RawVoiceClient(ClientOptions clientOptions) { + this.clientOptions = clientOptions; + } + + /** + * Transcribes an uploaded audio recording and returns the transcript. + * Send the raw audio bytes as the request body; the audio format is + * detected automatically (WAV, FLAC, MP3, OGG/WebM Opus). + *

Supports up to ~5 minutes of audio per request. This limit is on audio + * duration regardless of file size or format, so a compressed recording + * within the size limit can still be rejected for being too long. Pair the + * transcript with a downstream text step (e.g. POST /lang2fhir/create) + * to turn it into a FHIR resource.

+ */ + public PhenomlClientHttpResponse transcribe(byte[] body) { + return transcribe(TranscribeRequest.builder().body(body).build()); + } + + /** + * Transcribes an uploaded audio recording and returns the transcript. + * Send the raw audio bytes as the request body; the audio format is + * detected automatically (WAV, FLAC, MP3, OGG/WebM Opus). + *

Supports up to ~5 minutes of audio per request. This limit is on audio + * duration regardless of file size or format, so a compressed recording + * within the size limit can still be rejected for being too long. Pair the + * transcript with a downstream text step (e.g. POST /lang2fhir/create) + * to turn it into a FHIR resource.

+ */ + public PhenomlClientHttpResponse transcribe(byte[] body, RequestOptions requestOptions) { + return transcribe(TranscribeRequest.builder().body(body).build(), requestOptions); + } + + /** + * Transcribes an uploaded audio recording and returns the transcript. + * Send the raw audio bytes as the request body; the audio format is + * detected automatically (WAV, FLAC, MP3, OGG/WebM Opus). + *

Supports up to ~5 minutes of audio per request. This limit is on audio + * duration regardless of file size or format, so a compressed recording + * within the size limit can still be rejected for being too long. Pair the + * transcript with a downstream text step (e.g. POST /lang2fhir/create) + * to turn it into a FHIR resource.

+ */ + public PhenomlClientHttpResponse transcribe(TranscribeRequest request) { + return transcribe(request, null); + } + + /** + * Transcribes an uploaded audio recording and returns the transcript. + * Send the raw audio bytes as the request body; the audio format is + * detected automatically (WAV, FLAC, MP3, OGG/WebM Opus). + *

Supports up to ~5 minutes of audio per request. This limit is on audio + * duration regardless of file size or format, so a compressed recording + * within the size limit can still be rejected for being too long. Pair the + * transcript with a downstream text step (e.g. POST /lang2fhir/create) + * to turn it into a FHIR resource.

+ */ + public PhenomlClientHttpResponse transcribe( + TranscribeRequest request, RequestOptions requestOptions) { + HttpUrl.Builder httpUrl = HttpUrl.parse(this.clientOptions.environment().getUrl()) + .newBuilder() + .addPathSegments("transcribe"); + if (request.getLanguage().isPresent()) { + QueryStringMapper.addQueryParameter( + httpUrl, "language", request.getLanguage().get(), true); + } + if (requestOptions != null) { + requestOptions.getQueryParameters().forEach((_key, _value) -> { + httpUrl.addQueryParameter(_key, _value); + }); + } + RequestBody body = new InputStreamRequestBody( + MediaType.parse("application/octet-stream"), new ByteArrayInputStream(request.getBody())); + Request.Builder _requestBuilder = new Request.Builder() + .url(httpUrl.build()) + .method("POST", body) + .headers(Headers.of(clientOptions.headers(requestOptions))) + .addHeader("Content-Type", "application/octet-stream") + .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(); + String responseBodyString = responseBody != null ? responseBody.string() : "{}"; + if (response.isSuccessful()) { + return new PhenomlClientHttpResponse<>( + ObjectMappers.JSON_MAPPER.readValue(responseBodyString, TranscribeResponse.class), response); + } + try { + switch (response.code()) { + case 400: + throw new BadRequestError( + ObjectMappers.JSON_MAPPER.readValue(responseBodyString, Object.class), response); + case 401: + throw new UnauthorizedError( + ObjectMappers.JSON_MAPPER.readValue(responseBodyString, Object.class), response); + case 402: + throw new PaymentRequiredError( + ObjectMappers.JSON_MAPPER.readValue(responseBodyString, Object.class), response); + case 413: + throw new ContentTooLargeError( + ObjectMappers.JSON_MAPPER.readValue(responseBodyString, Object.class), response); + case 502: + throw new BadGatewayError( + ObjectMappers.JSON_MAPPER.readValue(responseBodyString, Object.class), response); + case 503: + throw new ServiceUnavailableError( + ObjectMappers.JSON_MAPPER.readValue(responseBodyString, Object.class), response); + case 504: + throw new GatewayTimeoutError( + 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 PhenomlClientApiException( + "Error with status code " + response.code(), response.code(), errorBody, response); + } catch (IOException e) { + throw new PhenomlClientException("Network error executing HTTP request", e); + } + } +} diff --git a/src/main/java/com/phenoml/api/resources/voice/voice/VoiceClient.java b/src/main/java/com/phenoml/api/resources/voice/voice/VoiceClient.java new file mode 100644 index 0000000..387b698 --- /dev/null +++ b/src/main/java/com/phenoml/api/resources/voice/voice/VoiceClient.java @@ -0,0 +1,83 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ +package com.phenoml.api.resources.voice.voice; + +import com.phenoml.api.core.ClientOptions; +import com.phenoml.api.core.RequestOptions; +import com.phenoml.api.resources.voice.types.TranscribeResponse; +import com.phenoml.api.resources.voice.voice.requests.TranscribeRequest; + +public class VoiceClient { + protected final ClientOptions clientOptions; + + private final RawVoiceClient rawClient; + + public VoiceClient(ClientOptions clientOptions) { + this.clientOptions = clientOptions; + this.rawClient = new RawVoiceClient(clientOptions); + } + + /** + * Get responses with HTTP metadata like headers + */ + public RawVoiceClient withRawResponse() { + return this.rawClient; + } + + /** + * Transcribes an uploaded audio recording and returns the transcript. + * Send the raw audio bytes as the request body; the audio format is + * detected automatically (WAV, FLAC, MP3, OGG/WebM Opus). + *

Supports up to ~5 minutes of audio per request. This limit is on audio + * duration regardless of file size or format, so a compressed recording + * within the size limit can still be rejected for being too long. Pair the + * transcript with a downstream text step (e.g. POST /lang2fhir/create) + * to turn it into a FHIR resource.

+ */ + public TranscribeResponse transcribe(byte[] body) { + return this.rawClient.transcribe(body).body(); + } + + /** + * Transcribes an uploaded audio recording and returns the transcript. + * Send the raw audio bytes as the request body; the audio format is + * detected automatically (WAV, FLAC, MP3, OGG/WebM Opus). + *

Supports up to ~5 minutes of audio per request. This limit is on audio + * duration regardless of file size or format, so a compressed recording + * within the size limit can still be rejected for being too long. Pair the + * transcript with a downstream text step (e.g. POST /lang2fhir/create) + * to turn it into a FHIR resource.

+ */ + public TranscribeResponse transcribe(byte[] body, RequestOptions requestOptions) { + return this.rawClient.transcribe(body, requestOptions).body(); + } + + /** + * Transcribes an uploaded audio recording and returns the transcript. + * Send the raw audio bytes as the request body; the audio format is + * detected automatically (WAV, FLAC, MP3, OGG/WebM Opus). + *

Supports up to ~5 minutes of audio per request. This limit is on audio + * duration regardless of file size or format, so a compressed recording + * within the size limit can still be rejected for being too long. Pair the + * transcript with a downstream text step (e.g. POST /lang2fhir/create) + * to turn it into a FHIR resource.

+ */ + public TranscribeResponse transcribe(TranscribeRequest request) { + return this.rawClient.transcribe(request).body(); + } + + /** + * Transcribes an uploaded audio recording and returns the transcript. + * Send the raw audio bytes as the request body; the audio format is + * detected automatically (WAV, FLAC, MP3, OGG/WebM Opus). + *

Supports up to ~5 minutes of audio per request. This limit is on audio + * duration regardless of file size or format, so a compressed recording + * within the size limit can still be rejected for being too long. Pair the + * transcript with a downstream text step (e.g. POST /lang2fhir/create) + * to turn it into a FHIR resource.

+ */ + public TranscribeResponse transcribe(TranscribeRequest request, RequestOptions requestOptions) { + return this.rawClient.transcribe(request, requestOptions).body(); + } +} diff --git a/src/main/java/com/phenoml/api/resources/voice/voice/requests/TranscribeRequest.java b/src/main/java/com/phenoml/api/resources/voice/voice/requests/TranscribeRequest.java new file mode 100644 index 0000000..8301781 --- /dev/null +++ b/src/main/java/com/phenoml/api/resources/voice/voice/requests/TranscribeRequest.java @@ -0,0 +1,171 @@ +/** + * This file was auto-generated by Fern from our API Definition. + */ +package com.phenoml.api.resources.voice.voice.requests; + +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 com.phenoml.api.core.ObjectMappers; +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 = TranscribeRequest.Builder.class) +public final class TranscribeRequest { + private final Optional> language; + + private final byte[] body; + + private final Map additionalProperties; + + private TranscribeRequest(Optional> language, byte[] body, Map additionalProperties) { + this.language = language; + this.body = body; + this.additionalProperties = additionalProperties; + } + + /** + * @return BCP-47 language tag, repeatable for up to 4 candidate languages. Defaults to en-US. + */ + @JsonProperty("language") + public Optional> getLanguage() { + return language; + } + + @JsonProperty("body") + public byte[] getBody() { + return body; + } + + @java.lang.Override + public boolean equals(Object other) { + if (this == other) return true; + return other instanceof TranscribeRequest && equalTo((TranscribeRequest) other); + } + + @JsonAnyGetter + public Map getAdditionalProperties() { + return this.additionalProperties; + } + + private boolean equalTo(TranscribeRequest other) { + return language.equals(other.language) && body.equals(other.body); + } + + @java.lang.Override + public int hashCode() { + return Objects.hash(this.language, this.body); + } + + @java.lang.Override + public String toString() { + return ObjectMappers.stringify(this); + } + + public static BodyStage builder() { + return new Builder(); + } + + public interface BodyStage { + _FinalStage body(@NotNull byte[] body); + + Builder from(TranscribeRequest other); + } + + public interface _FinalStage { + TranscribeRequest build(); + + _FinalStage additionalProperty(String key, Object value); + + _FinalStage additionalProperties(Map additionalProperties); + + /** + *

BCP-47 language tag, repeatable for up to 4 candidate languages. Defaults to en-US.

+ */ + _FinalStage language(Optional> language); + + _FinalStage language(List language); + + _FinalStage language(String language); + } + + @JsonIgnoreProperties(ignoreUnknown = true) + public static final class Builder implements BodyStage, _FinalStage { + private byte[] body; + + private Optional> language = Optional.empty(); + + @JsonAnySetter + private Map additionalProperties = new HashMap<>(); + + private Builder() {} + + @java.lang.Override + public Builder from(TranscribeRequest other) { + language(other.getLanguage()); + body(other.getBody()); + return this; + } + + @java.lang.Override + @JsonSetter("body") + public _FinalStage body(@NotNull byte[] body) { + this.body = Objects.requireNonNull(body, "body must not be null"); + return this; + } + + @java.lang.Override + public _FinalStage language(String language) { + this.language = Optional.of(Collections.singletonList(language)); + return this; + } + + /** + *

BCP-47 language tag, repeatable for up to 4 candidate languages. Defaults to en-US.

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

BCP-47 language tag, repeatable for up to 4 candidate languages. Defaults to en-US.

+ */ + @java.lang.Override + @JsonSetter(value = "language", nulls = Nulls.SKIP) + public _FinalStage language(Optional> language) { + this.language = language; + return this; + } + + @java.lang.Override + public TranscribeRequest build() { + return new TranscribeRequest(language, body, 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/resources/openapi/openapi.json b/src/main/resources/openapi/openapi.json index 20e6e8a..979abe8 100644 --- a/src/main/resources/openapi/openapi.json +++ b/src/main/resources/openapi/openapi.json @@ -2,7 +2,7 @@ "openapi": "3.0.3", "info": { "title": "Phenoml API", - "version": "fbaef4c66e97232edfaacb20d23d476aa286ecdd" + "version": "e2c477c4583f8f9e7d0d1a5b5591c1aa8cabef10" }, "x-services": [ { @@ -67,6 +67,13 @@ "description": "FHIR server operations including resource CRUD, search, and batch operations.", "iconHint": "proxy", "status": "ga" + }, + { + "id": "voice", + "name": "Voice", + "description": "Transcribe audio recordings into text with speech-to-text. Pair the transcript with Lang2FHIR to turn spoken clinical notes into structured FHIR resources.", + "iconHint": "voice", + "status": "alpha" } ], "paths": { @@ -6409,6 +6416,80 @@ "x-service": "tools" } }, + "/transcribe": { + "post": { + "tags": [ + "Voice / Voice" + ], + "operationId": "voice_transcribe", + "summary": "Transcribe audio", + "description": "Transcribes an uploaded audio recording and returns the transcript.\nSend the raw audio bytes as the request body; the audio format is\ndetected automatically (WAV, FLAC, MP3, OGG/WebM Opus).\n\nSupports up to ~5 minutes of audio per request. This limit is on audio\nduration regardless of file size or format, so a compressed recording\nwithin the size limit can still be rejected for being too long. Pair the\ntranscript with a downstream text step (e.g. `POST /lang2fhir/create`)\nto turn it into a FHIR resource.\n", + "parameters": [ + { + "name": "language", + "in": "query", + "description": "BCP-47 language tag, repeatable for up to 4 candidate languages. Defaults to `en-US`.", + "schema": { + "type": "array", + "items": { + "type": "string" + } + } + } + ], + "requestBody": { + "required": true, + "description": "Raw audio bytes (WAV, FLAC, MP3, or OGG/WebM Opus).", + "content": { + "application/octet-stream": { + "schema": { + "type": "string", + "format": "binary" + } + } + } + }, + "responses": { + "200": { + "description": "Transcription succeeded.", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/voice_TranscribeResponse" + } + } + } + }, + "400": { + "description": "Invalid request (empty body, too many languages, no transcript produced, or audio that is too long or in an unsupported format)" + }, + "401": { + "description": "Unauthorized" + }, + "402": { + "description": "Payment required (credits exhausted or subscription inactive)" + }, + "413": { + "description": "Audio exceeds the maximum size" + }, + "502": { + "description": "Speech recognition failed" + }, + "503": { + "description": "Transcription temporarily unavailable" + }, + "504": { + "description": "Transcription timed out" + } + }, + "security": [ + { + "bearerAuth": [] + } + ], + "x-service": "voice" + } + }, "/workflows": { "get": { "operationId": "workflows_list", @@ -11044,6 +11125,18 @@ } } }, + "voice_TranscribeResponse": { + "type": "object", + "required": [ + "transcript" + ], + "properties": { + "transcript": { + "type": "string", + "description": "The full transcript of the audio." + } + } + }, "workflows_CreateWorkflowRequest": { "type": "object", "required": [ @@ -11832,6 +11925,10 @@ { "name": "Tools / MCP Tools", "description": "List and manage individual tools exposed by an MCP server." + }, + { + "name": "Voice / Voice", + "description": "Speech-to-text transcription of audio recordings." } ] }